Black Lives Matter. Support the Equal Justice Initiative.

Source file src/crypto/x509/root_darwin.go

Documentation: crypto/x509

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build !ios
     6  // +build !ios
     7  
     8  package x509
     9  
    10  import (
    11  	"bytes"
    12  	macOS "crypto/x509/internal/macos"
    13  	"fmt"
    14  	"os"
    15  	"strings"
    16  )
    17  
    18  var debugDarwinRoots = strings.Contains(os.Getenv("GODEBUG"), "x509roots=1")
    19  
    20  func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
    21  	return nil, nil
    22  }
    23  
    24  func loadSystemRoots() (*CertPool, error) {
    25  	var trustedRoots []*Certificate
    26  	untrustedRoots := make(map[string]bool)
    27  
    28  	// macOS has three trust domains: one for CAs added by users to their
    29  	// "login" keychain, one for CAs added by Admins to the "System" keychain,
    30  	// and one for the CAs that ship with the OS.
    31  	for _, domain := range []macOS.SecTrustSettingsDomain{
    32  		macOS.SecTrustSettingsDomainUser,
    33  		macOS.SecTrustSettingsDomainAdmin,
    34  		macOS.SecTrustSettingsDomainSystem,
    35  	} {
    36  		certs, err := macOS.SecTrustSettingsCopyCertificates(domain)
    37  		if err == macOS.ErrNoTrustSettings {
    38  			continue
    39  		} else if err != nil {
    40  			return nil, err
    41  		}
    42  		defer macOS.CFRelease(certs)
    43  
    44  		for i := 0; i < macOS.CFArrayGetCount(certs); i++ {
    45  			c := macOS.CFArrayGetValueAtIndex(certs, i)
    46  			cert, err := exportCertificate(c)
    47  			if err != nil {
    48  				if debugDarwinRoots {
    49  					fmt.Fprintf(os.Stderr, "crypto/x509: domain %d, certificate #%d: %v\n", domain, i, err)
    50  				}
    51  				continue
    52  			}
    53  
    54  			var result macOS.SecTrustSettingsResult
    55  			if domain == macOS.SecTrustSettingsDomainSystem {
    56  				// Certs found in the system domain are always trusted. If the user
    57  				// configures "Never Trust" on such a cert, it will also be found in the
    58  				// admin or user domain, causing it to be added to untrustedRoots.
    59  				result = macOS.SecTrustSettingsResultTrustRoot
    60  			} else {
    61  				result, err = sslTrustSettingsResult(c)
    62  				if err != nil {
    63  					if debugDarwinRoots {
    64  						fmt.Fprintf(os.Stderr, "crypto/x509: trust settings for %v: %v\n", cert.Subject, err)
    65  					}
    66  					continue
    67  				}
    68  				if debugDarwinRoots {
    69  					fmt.Fprintf(os.Stderr, "crypto/x509: trust settings for %v: %d\n", cert.Subject, result)
    70  				}
    71  			}
    72  
    73  			switch result {
    74  			// "Note the distinction between the results kSecTrustSettingsResultTrustRoot
    75  			// and kSecTrustSettingsResultTrustAsRoot: The former can only be applied to
    76  			// root (self-signed) certificates; the latter can only be applied to
    77  			// non-root certificates."
    78  			case macOS.SecTrustSettingsResultTrustRoot:
    79  				if isRootCertificate(cert) {
    80  					trustedRoots = append(trustedRoots, cert)
    81  				}
    82  			case macOS.SecTrustSettingsResultTrustAsRoot:
    83  				if !isRootCertificate(cert) {
    84  					trustedRoots = append(trustedRoots, cert)
    85  				}
    86  
    87  			case macOS.SecTrustSettingsResultDeny:
    88  				// Add this certificate to untrustedRoots, which are subtracted
    89  				// from trustedRoots, so that we don't have to evaluate policies
    90  				// for every root in the system domain, but still apply user and
    91  				// admin policies that override system roots.
    92  				untrustedRoots[string(cert.Raw)] = true
    93  
    94  			case macOS.SecTrustSettingsResultUnspecified:
    95  				// Certificates with unspecified trust should be added to a pool
    96  				// of intermediates for chain building, but we don't support it
    97  				// at the moment. This is Issue 35631.
    98  
    99  			default:
   100  				if debugDarwinRoots {
   101  					fmt.Fprintf(os.Stderr, "crypto/x509: unknown trust setting for %v: %d\n", cert.Subject, result)
   102  				}
   103  			}
   104  		}
   105  	}
   106  
   107  	pool := NewCertPool()
   108  	for _, cert := range trustedRoots {
   109  		if !untrustedRoots[string(cert.Raw)] {
   110  			pool.AddCert(cert)
   111  		}
   112  	}
   113  	return pool, nil
   114  }
   115  
   116  // exportCertificate returns a *Certificate for a SecCertificateRef.
   117  func exportCertificate(cert macOS.CFRef) (*Certificate, error) {
   118  	data, err := macOS.SecItemExport(cert)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  	defer macOS.CFRelease(data)
   123  	der := macOS.CFDataToSlice(data)
   124  
   125  	return ParseCertificate(der)
   126  }
   127  
   128  // isRootCertificate reports whether Subject and Issuer match.
   129  func isRootCertificate(cert *Certificate) bool {
   130  	return bytes.Equal(cert.RawSubject, cert.RawIssuer)
   131  }
   132  
   133  // sslTrustSettingsResult obtains the final kSecTrustSettingsResult value for a
   134  // certificate in the user or admin domain, combining usage constraints for the
   135  // SSL SecTrustSettingsPolicy,
   136  //
   137  // It ignores SecTrustSettingsKeyUsage and kSecTrustSettingsAllowedError, and
   138  // doesn't support kSecTrustSettingsDefaultRootCertSetting.
   139  //
   140  // https://developer.apple.com/documentation/security/1400261-sectrustsettingscopytrustsetting
   141  func sslTrustSettingsResult(cert macOS.CFRef) (macOS.SecTrustSettingsResult, error) {
   142  	// In Apple's implementation user trust settings override admin trust settings
   143  	// (which themselves override system trust settings). If SecTrustSettingsCopyTrustSettings
   144  	// fails, or returns a NULL trust settings, when looking for the user trust
   145  	// settings then fallback to checking the admin trust settings.
   146  	//
   147  	// See Security-59306.41.2/trust/headers/SecTrustSettings.h for a description of
   148  	// the trust settings overrides, and SecLegacyAnchorSourceCopyUsageConstraints in
   149  	// Security-59306.41.2/trust/trustd/SecCertificateSource.c for a concrete example
   150  	// of how Apple applies the override in the case of NULL trust settings, or non
   151  	// success errors.
   152  	trustSettings, err := macOS.SecTrustSettingsCopyTrustSettings(cert, macOS.SecTrustSettingsDomainUser)
   153  	if err != nil || trustSettings == 0 {
   154  		if debugDarwinRoots && err != macOS.ErrNoTrustSettings {
   155  			fmt.Fprintf(os.Stderr, "crypto/x509: SecTrustSettingsCopyTrustSettings for SecTrustSettingsDomainUser failed: %s\n", err)
   156  		}
   157  		trustSettings, err = macOS.SecTrustSettingsCopyTrustSettings(cert, macOS.SecTrustSettingsDomainAdmin)
   158  	}
   159  	if err != nil || trustSettings == 0 {
   160  		// If there are neither user nor admin trust settings for a certificate returned
   161  		// from SecTrustSettingsCopyCertificates Apple returns kSecTrustSettingsResultInvalid,
   162  		// as this method is intended to return certificates _which have trust settings_.
   163  		// The most likely case for this being triggered is that the existing trust settings
   164  		// are invalid and cannot be properly parsed. In this case SecTrustSettingsCopyTrustSettings
   165  		// returns errSecInvalidTrustSettings. The existing cgo implementation returns
   166  		// kSecTrustSettingsResultUnspecified in this case, which mostly matches the Apple
   167  		// implementation because we don't do anything with certificates marked with this
   168  		// result.
   169  		//
   170  		// See SecPVCGetTrustSettingsResult in Security-59306.41.2/trust/trustd/SecPolicyServer.c
   171  		if debugDarwinRoots && err != macOS.ErrNoTrustSettings {
   172  			fmt.Fprintf(os.Stderr, "crypto/x509: SecTrustSettingsCopyTrustSettings for SecTrustSettingsDomainAdmin failed: %s\n", err)
   173  		}
   174  		return macOS.SecTrustSettingsResultUnspecified, nil
   175  	}
   176  	defer macOS.CFRelease(trustSettings)
   177  
   178  	// "An empty trust settings array means 'always trust this certificate' with an
   179  	// overall trust setting for the certificate of kSecTrustSettingsResultTrustRoot."
   180  	if macOS.CFArrayGetCount(trustSettings) == 0 {
   181  		return macOS.SecTrustSettingsResultTrustRoot, nil
   182  	}
   183  
   184  	isSSLPolicy := func(policyRef macOS.CFRef) bool {
   185  		properties := macOS.SecPolicyCopyProperties(policyRef)
   186  		defer macOS.CFRelease(properties)
   187  		if v, ok := macOS.CFDictionaryGetValueIfPresent(properties, macOS.SecPolicyOid); ok {
   188  			return macOS.CFEqual(v, macOS.CFRef(macOS.SecPolicyAppleSSL))
   189  		}
   190  		return false
   191  	}
   192  
   193  	for i := 0; i < macOS.CFArrayGetCount(trustSettings); i++ {
   194  		tSetting := macOS.CFArrayGetValueAtIndex(trustSettings, i)
   195  
   196  		// First, check if this trust setting is constrained to a non-SSL policy.
   197  		if policyRef, ok := macOS.CFDictionaryGetValueIfPresent(tSetting, macOS.SecTrustSettingsPolicy); ok {
   198  			if !isSSLPolicy(policyRef) {
   199  				continue
   200  			}
   201  		}
   202  
   203  		// Then check if it is restricted to a hostname, so not a root.
   204  		if _, ok := macOS.CFDictionaryGetValueIfPresent(tSetting, macOS.SecTrustSettingsPolicyString); ok {
   205  			continue
   206  		}
   207  
   208  		cfNum, ok := macOS.CFDictionaryGetValueIfPresent(tSetting, macOS.SecTrustSettingsResultKey)
   209  		// "If this key is not present, a default value of kSecTrustSettingsResultTrustRoot is assumed."
   210  		if !ok {
   211  			return macOS.SecTrustSettingsResultTrustRoot, nil
   212  		}
   213  		result, err := macOS.CFNumberGetValue(cfNum)
   214  		if err != nil {
   215  			return 0, err
   216  		}
   217  
   218  		// If multiple dictionaries match, we are supposed to "OR" them,
   219  		// the semantics of which are not clear. Since TrustRoot and TrustAsRoot
   220  		// are mutually exclusive, Deny should probably override, and Invalid and
   221  		// Unspecified be overridden, approximate this by stopping at the first
   222  		// TrustRoot, TrustAsRoot or Deny.
   223  		switch r := macOS.SecTrustSettingsResult(result); r {
   224  		case macOS.SecTrustSettingsResultTrustRoot,
   225  			macOS.SecTrustSettingsResultTrustAsRoot,
   226  			macOS.SecTrustSettingsResultDeny:
   227  			return r, nil
   228  		}
   229  	}
   230  
   231  	// If trust settings are present, but none of them match the policy...
   232  	// the docs don't tell us what to do.
   233  	//
   234  	// "Trust settings for a given use apply if any of the dictionaries in the
   235  	// certificate’s trust settings array satisfies the specified use." suggests
   236  	// that it's as if there were no trust settings at all, so we should maybe
   237  	// fallback to the admin trust settings? TODO(golang.org/issue/38888).
   238  
   239  	return macOS.SecTrustSettingsResultUnspecified, nil
   240  }
   241  

View as plain text