blob: a1ef04aead88a0da2a67ce56a67ea90e749c1f1f [file] [log] [blame]
// 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.
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:
// Roots in untrustedRoots and removed from the pool below, 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.CFDataCopyGoBytes(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) {
trustSettings, err := macOS.SecTrustSettingsCopyTrustSettings(cert, macOS.SecTrustSettingsDomainUser)
// According to Apple's SecTrustServer.c, "user trust settings overrule
// admin trust settings", but the rules of the override are unclear. Let's
// assume admin trust settings are applicable if and only if there are no
// user trust settings.
if err == macOS.ErrNoTrustSettings {
trustSettings, err = macOS.SecTrustSettingsCopyTrustSettings(cert, macOS.SecTrustSettingsDomainAdmin)
// > no trust settings [...] means "this certificate must be verified to a known trusted certificate”
if err == macOS.ErrNoTrustSettings {
return macOS.SecTrustSettingsResultUnspecified, nil
}
}
if err != nil {
return 0, err
}
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.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.
return macOS.SecTrustSettingsResultUnspecified, nil
}