| // Copyright 2020 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // +build !ios |
| |
| package x509 |
| |
| import ( |
| "bytes" |
| "crypto/x509/internal/macOS" |
| "fmt" |
| "os" |
| "strings" |
| ) |
| |
| var debugDarwinRoots = strings.Contains(os.Getenv("GODEBUG"), "x509roots=1") |
| |
| func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { |
| return nil, nil |
| } |
| |
| // loadSystemRootsWithCgo is set in root_cgo_darwin_amd64.go when cgo is |
| // available, and is only used for testing. |
| var loadSystemRootsWithCgo func() (*CertPool, error) |
| |
| func loadSystemRoots() (*CertPool, error) { |
| var trustedRoots []*Certificate |
| untrustedRoots := make(map[string]bool) |
| |
| // macOS has three trust domains: one for CAs added by users to their |
| // "login" keychain, one for CAs added by Admins to the "System" keychain, |
| // and one for the CAs that ship with the OS. |
| for _, domain := range []macOS.SecTrustSettingsDomain{ |
| macOS.SecTrustSettingsDomainUser, |
| macOS.SecTrustSettingsDomainAdmin, |
| macOS.SecTrustSettingsDomainSystem, |
| } { |
| certs, err := macOS.SecTrustSettingsCopyCertificates(domain) |
| if err == macOS.ErrNoTrustSettings { |
| continue |
| } else if err != nil { |
| return nil, err |
| } |
| defer macOS.CFRelease(certs) |
| |
| for i := 0; i < macOS.CFArrayGetCount(certs); i++ { |
| c := macOS.CFArrayGetValueAtIndex(certs, i) |
| cert, err := exportCertificate(c) |
| if err != nil { |
| if debugDarwinRoots { |
| fmt.Fprintf(os.Stderr, "crypto/x509: domain %d, certificate #%d: %v\n", domain, i, err) |
| } |
| continue |
| } |
| |
| var result macOS.SecTrustSettingsResult |
| if domain == macOS.SecTrustSettingsDomainSystem { |
| // Certs found in the system domain are always trusted. If the user |
| // configures "Never Trust" on such a cert, it will also be found in the |
| // admin or user domain, causing it to be added to untrustedRoots. |
| result = macOS.SecTrustSettingsResultTrustRoot |
| } else { |
| result, err = sslTrustSettingsResult(c) |
| if err != nil { |
| if debugDarwinRoots { |
| fmt.Fprintf(os.Stderr, "crypto/x509: trust settings for %v: %v\n", cert.Subject, err) |
| } |
| continue |
| } |
| if debugDarwinRoots { |
| fmt.Fprintf(os.Stderr, "crypto/x509: trust settings for %v: %d\n", cert.Subject, result) |
| } |
| } |
| |
| switch result { |
| // "Note the distinction between the results kSecTrustSettingsResultTrustRoot |
| // and kSecTrustSettingsResultTrustAsRoot: The former can only be applied to |
| // root (self-signed) certificates; the latter can only be applied to |
| // non-root certificates." |
| case macOS.SecTrustSettingsResultTrustRoot: |
| if isRootCertificate(cert) { |
| trustedRoots = append(trustedRoots, cert) |
| } |
| case macOS.SecTrustSettingsResultTrustAsRoot: |
| if !isRootCertificate(cert) { |
| trustedRoots = append(trustedRoots, cert) |
| } |
| |
| case macOS.SecTrustSettingsResultDeny: |
| // Add this certificate to untrustedRoots, which are subtracted |
| // from trustedRoots, so that we don't have to evaluate policies |
| // for every root in the system domain, but still apply user and |
| // admin policies that override system roots. |
| untrustedRoots[string(cert.Raw)] = true |
| |
| case macOS.SecTrustSettingsResultUnspecified: |
| // Certificates with unspecified trust should be added to a pool |
| // of intermediates for chain building, but we don't support it |
| // at the moment. This is Issue 35631. |
| |
| default: |
| if debugDarwinRoots { |
| fmt.Fprintf(os.Stderr, "crypto/x509: unknown trust setting for %v: %d\n", cert.Subject, result) |
| } |
| } |
| } |
| } |
| |
| pool := NewCertPool() |
| for _, cert := range trustedRoots { |
| if !untrustedRoots[string(cert.Raw)] { |
| pool.AddCert(cert) |
| } |
| } |
| return pool, nil |
| } |
| |
| // exportCertificate returns a *Certificate for a SecCertificateRef. |
| func exportCertificate(cert macOS.CFRef) (*Certificate, error) { |
| data, err := macOS.SecItemExport(cert) |
| if err != nil { |
| return nil, err |
| } |
| defer macOS.CFRelease(data) |
| der := macOS.CFDataToSlice(data) |
| |
| return ParseCertificate(der) |
| } |
| |
| // isRootCertificate reports whether Subject and Issuer match. |
| func isRootCertificate(cert *Certificate) bool { |
| return bytes.Equal(cert.RawSubject, cert.RawIssuer) |
| } |
| |
| // sslTrustSettingsResult obtains the final kSecTrustSettingsResult value for a |
| // certificate in the user or admin domain, combining usage constraints for the |
| // SSL SecTrustSettingsPolicy, |
| // |
| // It ignores SecTrustSettingsKeyUsage and kSecTrustSettingsAllowedError, and |
| // doesn't support kSecTrustSettingsDefaultRootCertSetting. |
| // |
| // https://developer.apple.com/documentation/security/1400261-sectrustsettingscopytrustsetting |
| func sslTrustSettingsResult(cert macOS.CFRef) (macOS.SecTrustSettingsResult, error) { |
| // In Apple's implementation user trust settings override admin trust settings |
| // (which themselves override system trust settings). If SecTrustSettingsCopyTrustSettings |
| // fails, or returns a NULL trust settings, when looking for the user trust |
| // settings then fallback to checking the admin trust settings. |
| // |
| // See Security-59306.41.2/trust/headers/SecTrustSettings.h for a description of |
| // the trust settings overrides, and SecLegacyAnchorSourceCopyUsageConstraints in |
| // Security-59306.41.2/trust/trustd/SecCertificateSource.c for a concrete example |
| // of how Apple applies the override in the case of NULL trust settings, or non |
| // success errors. |
| trustSettings, err := macOS.SecTrustSettingsCopyTrustSettings(cert, macOS.SecTrustSettingsDomainUser) |
| if err != nil || trustSettings == 0 { |
| if debugDarwinRoots && err != macOS.ErrNoTrustSettings { |
| fmt.Fprintf(os.Stderr, "crypto/x509: SecTrustSettingsCopyTrustSettings for SecTrustSettingsDomainUser failed: %s\n", err) |
| } |
| trustSettings, err = macOS.SecTrustSettingsCopyTrustSettings(cert, macOS.SecTrustSettingsDomainAdmin) |
| } |
| if err != nil || trustSettings == 0 { |
| // If there are neither user nor admin trust settings for a certificate returned |
| // from SecTrustSettingsCopyCertificates Apple returns kSecTrustSettingsResultInvalid, |
| // as this method is intended to return certificates _which have trust settings_. |
| // The most likely case for this being triggered is that the existing trust settings |
| // are invalid and cannot be properly parsed. In this case SecTrustSettingsCopyTrustSettings |
| // returns errSecInvalidTrustSettings. The existing cgo implementation returns |
| // kSecTrustSettingsResultUnspecified in this case, which mostly matches the Apple |
| // implementation because we don't do anything with certificates marked with this |
| // result. |
| // |
| // See SecPVCGetTrustSettingsResult in Security-59306.41.2/trust/trustd/SecPolicyServer.c |
| if debugDarwinRoots && err != macOS.ErrNoTrustSettings { |
| fmt.Fprintf(os.Stderr, "crypto/x509: SecTrustSettingsCopyTrustSettings for SecTrustSettingsDomainAdmin failed: %s\n", err) |
| } |
| return macOS.SecTrustSettingsResultUnspecified, nil |
| } |
| defer macOS.CFRelease(trustSettings) |
| |
| // "An empty trust settings array means 'always trust this certificate' with an |
| // overall trust setting for the certificate of kSecTrustSettingsResultTrustRoot." |
| if macOS.CFArrayGetCount(trustSettings) == 0 { |
| return macOS.SecTrustSettingsResultTrustRoot, nil |
| } |
| |
| isSSLPolicy := func(policyRef macOS.CFRef) bool { |
| properties := macOS.SecPolicyCopyProperties(policyRef) |
| defer macOS.CFRelease(properties) |
| if v, ok := macOS.CFDictionaryGetValueIfPresent(properties, macOS.SecPolicyOid); ok { |
| return macOS.CFEqual(v, macOS.CFRef(macOS.SecPolicyAppleSSL)) |
| } |
| return false |
| } |
| |
| for i := 0; i < macOS.CFArrayGetCount(trustSettings); i++ { |
| tSetting := macOS.CFArrayGetValueAtIndex(trustSettings, i) |
| |
| // First, check if this trust setting is constrained to a non-SSL policy. |
| if policyRef, ok := macOS.CFDictionaryGetValueIfPresent(tSetting, macOS.SecTrustSettingsPolicy); ok { |
| if !isSSLPolicy(policyRef) { |
| continue |
| } |
| } |
| |
| // Then check if it is restricted to a hostname, so not a root. |
| if _, ok := macOS.CFDictionaryGetValueIfPresent(tSetting, macOS.SecTrustSettingsPolicyString); ok { |
| continue |
| } |
| |
| cfNum, ok := macOS.CFDictionaryGetValueIfPresent(tSetting, macOS.SecTrustSettingsResultKey) |
| // "If this key is not present, a default value of kSecTrustSettingsResultTrustRoot is assumed." |
| if !ok { |
| return macOS.SecTrustSettingsResultTrustRoot, nil |
| } |
| result, err := macOS.CFNumberGetValue(cfNum) |
| if err != nil { |
| return 0, err |
| } |
| |
| // If multiple dictionaries match, we are supposed to "OR" them, |
| // the semantics of which are not clear. Since TrustRoot and TrustAsRoot |
| // are mutually exclusive, Deny should probably override, and Invalid and |
| // Unspecified be overridden, approximate this by stopping at the first |
| // TrustRoot, TrustAsRoot or Deny. |
| switch r := macOS.SecTrustSettingsResult(result); r { |
| case macOS.SecTrustSettingsResultTrustRoot, |
| macOS.SecTrustSettingsResultTrustAsRoot, |
| macOS.SecTrustSettingsResultDeny: |
| return r, nil |
| } |
| } |
| |
| // If trust settings are present, but none of them match the policy... |
| // the docs don't tell us what to do. |
| // |
| // "Trust settings for a given use apply if any of the dictionaries in the |
| // certificate’s trust settings array satisfies the specified use." suggests |
| // that it's as if there were no trust settings at all, so we should maybe |
| // fallback to the admin trust settings? TODO(golang.org/issue/38888). |
| |
| return macOS.SecTrustSettingsResultUnspecified, nil |
| } |