@ -1,326 +0,0 @@
// Copyright 2011 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
// This cgo implementation exists only to support side-by-side testing by
// TestSystemRoots. It can be removed once we are confident in the no-cgo
// implementation.
/ *
# cgo CFLAGS : - mmacosx - version - min = 10.11
# cgo LDFLAGS : - framework CoreFoundation - framework Security
# include < errno . h >
# include < sys / sysctl . h >
# include < CoreFoundation / CoreFoundation . h >
# include < Security / Security . h >
static Boolean isSSLPolicy ( SecPolicyRef policyRef ) {
if ( ! policyRef ) {
return false ;
}
CFDictionaryRef properties = SecPolicyCopyProperties ( policyRef ) ;
if ( properties == NULL ) {
return false ;
}
Boolean isSSL = false ;
CFTypeRef value = NULL ;
if ( CFDictionaryGetValueIfPresent ( properties , kSecPolicyOid , ( const void * * ) & value ) ) {
isSSL = CFEqual ( value , kSecPolicyAppleSSL ) ;
}
CFRelease ( properties ) ;
return isSSL ;
}
// sslTrustSettingsResult obtains the final kSecTrustSettingsResult value
// for a certificate in the user or admin domain, combining usage constraints
// for the SSL SecTrustSettingsPolicy, ignoring SecTrustSettingsKeyUsage and
// kSecTrustSettingsAllowedError.
// https://developer.apple.com/documentation/security/1400261-sectrustsettingscopytrustsetting
static SInt32 sslTrustSettingsResult ( SecCertificateRef cert ) {
CFArrayRef trustSettings = NULL ;
OSStatus err = SecTrustSettingsCopyTrustSettings ( cert , kSecTrustSettingsDomainUser , & trustSettings ) ;
// 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 user trust settings fail to load or are NULL.
if ( err != errSecSuccess || trustSettings == NULL ) {
if ( trustSettings != NULL ) CFRelease ( trustSettings ) ;
err = SecTrustSettingsCopyTrustSettings ( cert , kSecTrustSettingsDomainAdmin , & trustSettings ) ;
}
// > no trust settings [...] means "this certificate must be verified to a known trusted certificate”
// (Should this cause a fallback from user to admin domain? It's unclear.)
if ( err != errSecSuccess || trustSettings == NULL ) {
if ( trustSettings != NULL ) CFRelease ( trustSettings ) ;
return kSecTrustSettingsResultUnspecified ;
}
// > An empty trust settings array means "always trust this certificate” with an
// > overall trust setting for the certificate of kSecTrustSettingsResultTrustRoot.
if ( CFArrayGetCount ( trustSettings ) == 0 ) {
CFRelease ( trustSettings ) ;
return kSecTrustSettingsResultTrustRoot ;
}
// kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"),
// but the Go linker's internal linking mode can't handle CFSTR relocations.
// Create our own dynamic string instead and release it below.
CFStringRef _kSecTrustSettingsResult = CFStringCreateWithCString (
NULL , "kSecTrustSettingsResult" , kCFStringEncodingUTF8 ) ;
CFStringRef _kSecTrustSettingsPolicy = CFStringCreateWithCString (
NULL , "kSecTrustSettingsPolicy" , kCFStringEncodingUTF8 ) ;
CFStringRef _kSecTrustSettingsPolicyString = CFStringCreateWithCString (
NULL , "kSecTrustSettingsPolicyString" , kCFStringEncodingUTF8 ) ;
CFIndex m ; SInt32 result = 0 ;
for ( m = 0 ; m < CFArrayGetCount ( trustSettings ) ; m ++ ) {
CFDictionaryRef tSetting = ( CFDictionaryRef ) CFArrayGetValueAtIndex ( trustSettings , m ) ;
// First, check if this trust setting is constrained to a non-SSL policy.
SecPolicyRef policyRef ;
if ( CFDictionaryGetValueIfPresent ( tSetting , _kSecTrustSettingsPolicy , ( const void * * ) & policyRef ) ) {
if ( ! isSSLPolicy ( policyRef ) ) {
continue ;
}
}
if ( CFDictionaryContainsKey ( tSetting , _kSecTrustSettingsPolicyString ) ) {
// Restricted to a hostname, not a root.
continue ;
}
CFNumberRef cfNum ;
if ( CFDictionaryGetValueIfPresent ( tSetting , _kSecTrustSettingsResult , ( const void * * ) & cfNum ) ) {
CFNumberGetValue ( cfNum , kCFNumberSInt32Type , & result ) ;
} else {
// > If this key is not present, a default value of
// > kSecTrustSettingsResultTrustRoot is assumed.
result = kSecTrustSettingsResultTrustRoot ;
}
// 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.
if ( result == kSecTrustSettingsResultTrustRoot ) {
break ;
} else if ( result == kSecTrustSettingsResultTrustAsRoot ) {
break ;
} else if ( result == kSecTrustSettingsResultDeny ) {
break ;
}
}
// 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 probably
// fallback to the admin trust settings. TODO.
if ( result == 0 ) {
result = kSecTrustSettingsResultUnspecified ;
}
CFRelease ( _kSecTrustSettingsPolicy ) ;
CFRelease ( _kSecTrustSettingsPolicyString ) ;
CFRelease ( _kSecTrustSettingsResult ) ;
CFRelease ( trustSettings ) ;
return result ;
}
// isRootCertificate reports whether Subject and Issuer match.
static Boolean isRootCertificate ( SecCertificateRef cert , CFErrorRef * errRef ) {
CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent ( cert , errRef ) ;
if ( * errRef != NULL ) {
return false ;
}
CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent ( cert , errRef ) ;
if ( * errRef != NULL ) {
CFRelease ( subjectName ) ;
return false ;
}
Boolean equal = CFEqual ( subjectName , issuerName ) ;
CFRelease ( subjectName ) ;
CFRelease ( issuerName ) ;
return equal ;
}
// CopyPEMRoots fetches the system's list of trusted X.509 root certificates
// for the kSecTrustSettingsPolicy SSL.
//
// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
// certificates of the system. On failure, the function returns -1.
// Additionally, it fills untrustedPemRoots with certs that must be removed from pemRoots.
//
// Note: The CFDataRef returned in pemRoots and untrustedPemRoots must
// be released (using CFRelease) after we've consumed its content.
static int CopyPEMRoots ( CFDataRef * pemRoots , CFDataRef * untrustedPemRoots , bool debugDarwinRoots ) {
int i ;
if ( debugDarwinRoots ) {
fprintf ( stderr , "crypto/x509: kSecTrustSettingsResultInvalid = %d\n" , kSecTrustSettingsResultInvalid ) ;
fprintf ( stderr , "crypto/x509: kSecTrustSettingsResultTrustRoot = %d\n" , kSecTrustSettingsResultTrustRoot ) ;
fprintf ( stderr , "crypto/x509: kSecTrustSettingsResultTrustAsRoot = %d\n" , kSecTrustSettingsResultTrustAsRoot ) ;
fprintf ( stderr , "crypto/x509: kSecTrustSettingsResultDeny = %d\n" , kSecTrustSettingsResultDeny ) ;
fprintf ( stderr , "crypto/x509: kSecTrustSettingsResultUnspecified = %d\n" , kSecTrustSettingsResultUnspecified ) ;
}
// Get certificates from all domains, not just System, this lets
// the user add CAs to their "login" keychain, and Admins to add
// to the "System" keychain
SecTrustSettingsDomain domains [ ] = { kSecTrustSettingsDomainSystem ,
kSecTrustSettingsDomainAdmin , kSecTrustSettingsDomainUser } ;
int numDomains = sizeof ( domains ) / sizeof ( SecTrustSettingsDomain ) ;
if ( pemRoots == NULL || untrustedPemRoots == NULL ) {
return - 1 ;
}
CFMutableDataRef combinedData = CFDataCreateMutable ( kCFAllocatorDefault , 0 ) ;
CFMutableDataRef combinedUntrustedData = CFDataCreateMutable ( kCFAllocatorDefault , 0 ) ;
for ( i = 0 ; i < numDomains ; i ++ ) {
int j ;
CFArrayRef certs = NULL ;
OSStatus err = SecTrustSettingsCopyCertificates ( domains [ i ] , & certs ) ;
if ( err != noErr ) {
continue ;
}
CFIndex numCerts = CFArrayGetCount ( certs ) ;
for ( j = 0 ; j < numCerts ; j ++ ) {
SecCertificateRef cert = ( SecCertificateRef ) CFArrayGetValueAtIndex ( certs , j ) ;
if ( cert == NULL ) {
continue ;
}
SInt32 result ;
if ( domains [ i ] == kSecTrustSettingsDomainSystem ) {
// 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 untrustedPemRoots. The
// Go code will then clean this up.
result = kSecTrustSettingsResultTrustRoot ;
} else {
result = sslTrustSettingsResult ( cert ) ;
if ( debugDarwinRoots ) {
CFErrorRef errRef = NULL ;
CFStringRef summary = SecCertificateCopyShortDescription ( NULL , cert , & errRef ) ;
if ( errRef != NULL ) {
fprintf ( stderr , "crypto/x509: SecCertificateCopyShortDescription failed\n" ) ;
CFRelease ( errRef ) ;
continue ;
}
CFIndex length = CFStringGetLength ( summary ) ;
CFIndex maxSize = CFStringGetMaximumSizeForEncoding ( length , kCFStringEncodingUTF8 ) + 1 ;
char * buffer = malloc ( maxSize ) ;
if ( CFStringGetCString ( summary , buffer , maxSize , kCFStringEncodingUTF8 ) ) {
fprintf ( stderr , "crypto/x509: %s returned %d\n" , buffer , ( int ) result ) ;
}
free ( buffer ) ;
CFRelease ( summary ) ;
}
}
CFMutableDataRef appendTo ;
// > 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.
if ( result == kSecTrustSettingsResultTrustRoot ) {
CFErrorRef errRef = NULL ;
if ( ! isRootCertificate ( cert , & errRef ) || errRef != NULL ) {
if ( errRef != NULL ) CFRelease ( errRef ) ;
continue ;
}
appendTo = combinedData ;
} else if ( result == kSecTrustSettingsResultTrustAsRoot ) {
CFErrorRef errRef = NULL ;
if ( isRootCertificate ( cert , & errRef ) || errRef != NULL ) {
if ( errRef != NULL ) CFRelease ( errRef ) ;
continue ;
}
appendTo = combinedData ;
} else if ( result == kSecTrustSettingsResultDeny ) {
appendTo = combinedUntrustedData ;
} else if ( result == kSecTrustSettingsResultUnspecified ) {
// Certificates with unspecified trust should probably be added to a pool of
// intermediates for chain building, or checked for transitive trust and
// added to the root pool (which is an imprecise approximation because it
// cuts chains short) but we don't support either at the moment. TODO.
continue ;
} else {
continue ;
}
CFDataRef data = NULL ;
err = SecItemExport ( cert , kSecFormatX509Cert , kSecItemPemArmour , NULL , & data ) ;
if ( err != noErr ) {
continue ;
}
if ( data != NULL ) {
CFDataAppendBytes ( appendTo , CFDataGetBytePtr ( data ) , CFDataGetLength ( data ) ) ;
CFRelease ( data ) ;
}
}
CFRelease ( certs ) ;
}
* pemRoots = combinedData ;
* untrustedPemRoots = combinedUntrustedData ;
return 0 ;
}
* /
import "C"
import (
"errors"
"unsafe"
)
func init ( ) {
loadSystemRootsWithCgo = _loadSystemRootsWithCgo
}
func _loadSystemRootsWithCgo ( ) ( * CertPool , error ) {
var data , untrustedData C . CFDataRef
err := C . CopyPEMRoots ( & data , & untrustedData , C . bool ( debugDarwinRoots ) )
if err == - 1 {
return nil , errors . New ( "crypto/x509: failed to load darwin system roots with cgo" )
}
defer C . CFRelease ( C . CFTypeRef ( data ) )
defer C . CFRelease ( C . CFTypeRef ( untrustedData ) )
buf := C . GoBytes ( unsafe . Pointer ( C . CFDataGetBytePtr ( data ) ) , C . int ( C . CFDataGetLength ( data ) ) )
roots := NewCertPool ( )
roots . AppendCertsFromPEM ( buf )
if C . CFDataGetLength ( untrustedData ) == 0 {
return roots , nil
}
buf = C . GoBytes ( unsafe . Pointer ( C . CFDataGetBytePtr ( untrustedData ) ) , C . int ( C . CFDataGetLength ( untrustedData ) ) )
untrustedRoots := NewCertPool ( )
untrustedRoots . AppendCertsFromPEM ( buf )
trustedRoots := NewCertPool ( )
for _ , lc := range roots . lazyCerts {
c , err := lc . getCert ( )
if err != nil {
return nil , err
}
if ! untrustedRoots . contains ( c ) {
trustedRoots . AddCert ( c )
}
}
return trustedRoots , nil
}