7
7
package x509
8
8
9
9
/*
10
- #cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1060
10
+ #cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1080
11
11
#cgo LDFLAGS: -framework CoreFoundation -framework Security
12
12
13
+ #include <errno.h>
14
+ #include <sys/sysctl.h>
15
+
13
16
#include <CoreFoundation/CoreFoundation.h>
14
17
#include <Security/Security.h>
15
18
16
- // FetchPEMRoots fetches the system's list of trusted X.509 root certificates.
17
- //
18
- // On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
19
- // certificates of the system. On failure, the function returns -1.
20
- //
21
- // Note: The CFDataRef returned in pemRoots must be released (using CFRelease) after
22
- // we've consumed its content.
23
- int FetchPEMRoots(CFDataRef *pemRoots) {
19
+ // FetchPEMRoots_MountainLion is the version of FetchPEMRoots from Go 1.6
20
+ // which still works on OS X 10.8 (Mountain Lion).
21
+ // It lacks support for admin & user cert domains.
22
+ // See golang.org/issue/16473
23
+ int FetchPEMRoots_MountainLion(CFDataRef *pemRoots) {
24
24
if (pemRoots == NULL) {
25
25
return -1;
26
26
}
27
-
28
27
CFArrayRef certs = NULL;
29
28
OSStatus err = SecTrustCopyAnchorCertificates(&certs);
30
29
if (err != noErr) {
31
30
return -1;
32
31
}
33
-
34
32
CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
35
33
int i, ncerts = CFArrayGetCount(certs);
36
34
for (i = 0; i < ncerts; i++) {
@@ -39,24 +37,164 @@ int FetchPEMRoots(CFDataRef *pemRoots) {
39
37
if (cert == NULL) {
40
38
continue;
41
39
}
42
-
43
40
// Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport.
44
41
// Once we support weak imports via cgo we should prefer that, and fall back to this
45
42
// for older systems.
46
43
err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
47
44
if (err != noErr) {
48
45
continue;
49
46
}
50
-
51
47
if (data != NULL) {
52
48
CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data));
53
49
CFRelease(data);
54
50
}
55
51
}
56
-
57
52
CFRelease(certs);
53
+ *pemRoots = combinedData;
54
+ return 0;
55
+ }
56
+
57
+ // useOldCode reports whether the running machine is OS X 10.8 Mountain Lion
58
+ // or older. We only support Mountain Lion and higher, but we'll at least try our
59
+ // best on older machines and continue to use the old code path.
60
+ //
61
+ // See golang.org/issue/16473
62
+ int useOldCode() {
63
+ char str[256];
64
+ size_t size = sizeof(str);
65
+ memset(str, 0, size);
66
+ sysctlbyname("kern.osrelease", str, &size, NULL, 0);
67
+ // OS X 10.8 is osrelease "12.*", 10.7 is 11.*, 10.6 is 10.*.
68
+ // We never supported things before that.
69
+ return memcmp(str, "12.", 3) == 0 || memcmp(str, "11.", 3) == 0 || memcmp(str, "10.", 3) == 0;
70
+ }
58
71
72
+ // FetchPEMRoots fetches the system's list of trusted X.509 root certificates.
73
+ //
74
+ // On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
75
+ // certificates of the system. On failure, the function returns -1.
76
+ // Additionally, it fills untrustedPemRoots with certs that must be removed from pemRoots.
77
+ //
78
+ // Note: The CFDataRef returned in pemRoots and untrustedPemRoots must
79
+ // be released (using CFRelease) after we've consumed its content.
80
+ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) {
81
+ if (useOldCode()) {
82
+ return FetchPEMRoots_MountainLion(pemRoots);
83
+ }
84
+
85
+ // Get certificates from all domains, not just System, this lets
86
+ // the user add CAs to their "login" keychain, and Admins to add
87
+ // to the "System" keychain
88
+ SecTrustSettingsDomain domains[] = { kSecTrustSettingsDomainSystem,
89
+ kSecTrustSettingsDomainAdmin,
90
+ kSecTrustSettingsDomainUser };
91
+
92
+ int numDomains = sizeof(domains)/sizeof(SecTrustSettingsDomain);
93
+ if (pemRoots == NULL) {
94
+ return -1;
95
+ }
96
+
97
+ // kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"),
98
+ // but the Go linker's internal linking mode can't handle CFSTR relocations.
99
+ // Create our own dynamic string instead and release it below.
100
+ CFStringRef policy = CFStringCreateWithCString(NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8);
101
+
102
+ CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
103
+ CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
104
+ for (int i = 0; i < numDomains; i++) {
105
+ CFArrayRef certs = NULL;
106
+ OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs);
107
+ if (err != noErr) {
108
+ continue;
109
+ }
110
+
111
+ CFIndex numCerts = CFArrayGetCount(certs);
112
+ for (int j = 0; j < numCerts; j++) {
113
+ CFDataRef data = NULL;
114
+ CFErrorRef errRef = NULL;
115
+ CFArrayRef trustSettings = NULL;
116
+ SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j);
117
+ if (cert == NULL) {
118
+ continue;
119
+ }
120
+ // We only want trusted certs.
121
+ int untrusted = 0;
122
+ if (i != 0) {
123
+ // Certs found in the system domain are always trusted. If the user
124
+ // configures "Never Trust" on such a cert, it will also be found in the
125
+ // admin or user domain, causing it to be added to untrustedPemRoots. The
126
+ // Go code will then clean this up.
127
+
128
+ // Trust may be stored in any of the domains. According to Apple's
129
+ // SecTrustServer.c, "user trust settings overrule admin trust settings",
130
+ // so take the last trust settings array we find.
131
+ // Skip the system domain since it is always trusted.
132
+ for (int k = 1; k < numDomains; k++) {
133
+ CFArrayRef domainTrustSettings = NULL;
134
+ err = SecTrustSettingsCopyTrustSettings(cert, domains[k], &domainTrustSettings);
135
+ if (err == errSecSuccess && domainTrustSettings != NULL) {
136
+ if (trustSettings) {
137
+ CFRelease(trustSettings);
138
+ }
139
+ trustSettings = domainTrustSettings;
140
+ }
141
+ }
142
+ if (trustSettings == NULL) {
143
+ // "this certificate must be verified to a known trusted certificate"; aka not a root.
144
+ continue;
145
+ }
146
+ for (CFIndex k = 0; k < CFArrayGetCount(trustSettings); k++) {
147
+ CFNumberRef cfNum;
148
+ CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, k);
149
+ if (CFDictionaryGetValueIfPresent(tSetting, policy, (const void**)&cfNum)){
150
+ SInt32 result = 0;
151
+ CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result);
152
+ // TODO: The rest of the dictionary specifies conditions for evaluation.
153
+ if (result == kSecTrustSettingsResultDeny) {
154
+ untrusted = 1;
155
+ }
156
+ }
157
+ }
158
+ CFRelease(trustSettings);
159
+ }
160
+ // We only want to add Root CAs, so make sure Subject and Issuer Name match
161
+ CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, &errRef);
162
+ if (errRef != NULL) {
163
+ CFRelease(errRef);
164
+ continue;
165
+ }
166
+ CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, &errRef);
167
+ if (errRef != NULL) {
168
+ CFRelease(subjectName);
169
+ CFRelease(errRef);
170
+ continue;
171
+ }
172
+ Boolean equal = CFEqual(subjectName, issuerName);
173
+ CFRelease(subjectName);
174
+ CFRelease(issuerName);
175
+ if (!equal) {
176
+ continue;
177
+ }
178
+
179
+ // Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport.
180
+ // Once we support weak imports via cgo we should prefer that, and fall back to this
181
+ // for older systems.
182
+ err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
183
+ if (err != noErr) {
184
+ continue;
185
+ }
186
+
187
+ if (data != NULL) {
188
+ CFMutableDataRef appendTo = untrusted ? combinedUntrustedData : combinedData;
189
+ CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data));
190
+ CFRelease(data);
191
+ }
192
+ }
193
+ CFRelease(certs);
194
+ }
195
+ CFRelease(policy);
59
196
*pemRoots = combinedData;
197
+ *untrustedPemRoots = combinedUntrustedData;
60
198
return 0;
61
199
}
62
200
*/
@@ -67,13 +205,29 @@ func initSystemRoots() {
67
205
roots := NewCertPool ()
68
206
69
207
var data C.CFDataRef = nil
70
- err := C .FetchPEMRoots (& data )
208
+ var untrustedData C.CFDataRef = nil
209
+ err := C .FetchPEMRoots (& data , & untrustedData )
71
210
if err == - 1 {
72
211
return
73
212
}
74
213
75
214
defer C .CFRelease (C .CFTypeRef (data ))
76
215
buf := C .GoBytes (unsafe .Pointer (C .CFDataGetBytePtr (data )), C .int (C .CFDataGetLength (data )))
77
216
roots .AppendCertsFromPEM (buf )
217
+ if untrustedData == nil {
218
+ systemRoots = roots
219
+ return
220
+ }
221
+ defer C .CFRelease (C .CFTypeRef (untrustedData ))
222
+ buf = C .GoBytes (unsafe .Pointer (C .CFDataGetBytePtr (untrustedData )), C .int (C .CFDataGetLength (untrustedData )))
223
+ untrustedRoots := NewCertPool ()
224
+ untrustedRoots .AppendCertsFromPEM (buf )
225
+
226
+ trustedRoots := NewCertPool ()
227
+ for _ , c := range roots .certs {
228
+ if ! untrustedRoots .contains (c ) {
229
+ trustedRoots .AddCert (c )
230
+ }
231
+ }
78
232
systemRoots = roots
79
233
}
0 commit comments