gcc/libgo/go/crypto/x509/root_darwin_amd64.go
2020-08-01 11:21:40 -07:00

244 lines
9.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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"
macOS "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
// certificates 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
}