]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
866f8763 | 2 | * Copyright (c) 2002-2017 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
d87e1158 | 5 | * |
b1ab9ed8 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
d87e1158 | 12 | * |
b1ab9ed8 A |
13 | * The Original Code and all software distributed under the License are |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
d87e1158 | 20 | * |
b1ab9ed8 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
ecaf5866 A |
24 | #include <libDER/oids.h> |
25 | #include <Security/oidscert.h> | |
26 | ||
b54c578e A |
27 | #include <Security/SecTrust.h> |
28 | #include <Security/SecTrustPriv.h> | |
b1ab9ed8 | 29 | #include "Trust.h" |
b54c578e | 30 | #include <Security/SecBase.h> |
b1ab9ed8 | 31 | #include "SecBridge.h" |
b54c578e A |
32 | #include <Security/SecInternal.h> |
33 | #include <Security/SecTrustSettings.h> | |
34 | #include <Security/SecTrustSettingsPriv.h> | |
35 | #include <Security/SecTrustStatusCodes.h> | |
36 | #include <Security/SecCertificatePriv.h> | |
37 | #include <Security/SecPolicyPriv.h> | |
b1ab9ed8 | 38 | #include <security_utilities/cfutilities.h> |
427c49bc | 39 | #include <security_utilities/cfmunge.h> |
b1ab9ed8 | 40 | #include <CoreFoundation/CoreFoundation.h> |
5c19dc3a | 41 | #include <syslog.h> |
b1ab9ed8 | 42 | |
427c49bc | 43 | // forward declarations |
fa7225c8 A |
44 | CFArrayRef SecTrustCopyInputCertificates(SecTrustRef trust); |
45 | CFArrayRef SecTrustCopyInputAnchors(SecTrustRef trust); | |
46 | CFArrayRef SecTrustCopyConstructedChain(SecTrustRef trust); | |
47 | static CSSM_TP_APPLE_EVIDENCE_INFO * SecTrustGetEvidenceInfo(SecTrustRef trust); | |
427c49bc A |
48 | |
49 | typedef struct SecTrustCheckExceptionContext { | |
50 | CFDictionaryRef exception; | |
51 | bool exceptionNotFound; | |
52 | } SecTrustCheckExceptionContext; | |
53 | ||
b1ab9ed8 A |
54 | // |
55 | // Sec* API bridge functions | |
56 | // | |
5c19dc3a | 57 | /* OS X only: __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_2, __MAC_10_7, __IPHONE_NA, __IPHONE_NA) */ |
b1ab9ed8 A |
58 | OSStatus SecTrustSetParameters( |
59 | SecTrustRef trustRef, | |
60 | CSSM_TP_ACTION action, | |
61 | CFDataRef actionData) | |
62 | { | |
5c19dc3a A |
63 | /* bridge to support API functionality for legacy callers */ |
64 | OSStatus status; | |
65 | CSSM_APPLE_TP_ACTION_FLAGS actionFlags = 0; | |
66 | if (actionData) { | |
67 | CSSM_APPLE_TP_ACTION_DATA *actionDataPtr = (CSSM_APPLE_TP_ACTION_DATA *) CFDataGetBytePtr(actionData); | |
68 | if (actionDataPtr) { | |
69 | actionFlags = actionDataPtr->ActionFlags; | |
70 | } | |
71 | } | |
72 | // note that SecTrustOptionFlags == CSSM_APPLE_TP_ACTION_FLAGS; | |
73 | // both are sizeof(uint32) and the flag values have identical meanings | |
74 | status = SecTrustSetOptions(trustRef, (SecTrustOptionFlags)actionFlags); | |
75 | ||
76 | #if SECTRUST_DEPRECATION_WARNINGS | |
77 | syslog(LOG_ERR, "WARNING: SecTrustSetParameters was deprecated in 10.7. Use SecTrustSetOptions instead."); | |
78 | #endif | |
b1ab9ed8 | 79 | |
5c19dc3a | 80 | return status; |
5c19dc3a A |
81 | } |
82 | ||
5c19dc3a | 83 | /* OS X only: __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA) */ |
b1ab9ed8 A |
84 | OSStatus SecTrustSetKeychains(SecTrustRef trust, CFTypeRef keychainOrArray) |
85 | { | |
5c19dc3a A |
86 | /* this function is currently unsupported in unified SecTrust */ |
87 | // TODO: pull all certs out of the specified keychains for the evaluation? | |
88 | #if SECTRUST_DEPRECATION_WARNINGS | |
89 | syslog(LOG_ERR, "WARNING: SecTrustSetKeychains does nothing in 10.11. Use SecTrustSetAnchorCertificates{Only} to provide anchors."); | |
90 | #endif | |
91 | return errSecSuccess; | |
b1ab9ed8 | 92 | } |
b1ab9ed8 A |
93 | |
94 | // | |
95 | // Construct the "official" result evidence and return it | |
96 | // | |
5c19dc3a | 97 | /* OS X only: __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_2, __MAC_10_7, __IPHONE_NA, __IPHONE_NA) */ |
b1ab9ed8 A |
98 | OSStatus SecTrustGetResult( |
99 | SecTrustRef trustRef, | |
100 | SecTrustResultType *result, | |
101 | CFArrayRef *certChain, CSSM_TP_APPLE_EVIDENCE_INFO **statusChain) | |
102 | { | |
5c19dc3a A |
103 | /* bridge to support old functionality */ |
104 | #if SECTRUST_DEPRECATION_WARNINGS | |
fa7225c8 | 105 | syslog(LOG_ERR, "WARNING: SecTrustGetResult has been deprecated since 10.7. Please use SecTrustGetTrustResult instead."); |
5c19dc3a | 106 | #endif |
fa7225c8 A |
107 | SecTrustResultType trustResult; |
108 | OSStatus status = SecTrustGetTrustResult(trustRef, &trustResult); | |
109 | if (status != errSecSuccess) { | |
110 | return status; | |
111 | } | |
5c19dc3a A |
112 | if (result) { |
113 | *result = trustResult; | |
114 | } | |
fa7225c8 A |
115 | if (certChain) { |
116 | *certChain = SecTrustCopyConstructedChain(trustRef); | |
117 | } | |
118 | if (statusChain) { | |
119 | *statusChain = SecTrustGetEvidenceInfo(trustRef); | |
5c19dc3a A |
120 | } |
121 | return status; | |
b1ab9ed8 A |
122 | } |
123 | ||
124 | // | |
125 | // Retrieve extended validation trust results | |
126 | // | |
5c19dc3a | 127 | /* OS X only: __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA) */ |
b1ab9ed8 A |
128 | OSStatus SecTrustCopyExtendedResult(SecTrustRef trust, CFDictionaryRef *result) |
129 | { | |
5c19dc3a A |
130 | /* bridge to support old functionality */ |
131 | #if SECTRUST_DEPRECATION_WARNINGS | |
132 | syslog(LOG_ERR, "WARNING: SecTrustCopyExtendedResult will be deprecated in an upcoming release. Please use SecTrustCopyResult instead."); | |
133 | #endif | |
134 | CFDictionaryRef resultDict = SecTrustCopyResult(trust); | |
135 | if (result == nil) { | |
6b200bc3 | 136 | CFReleaseNull(resultDict); |
5c19dc3a A |
137 | return errSecParam; |
138 | } | |
139 | *result = resultDict; | |
140 | return errSecSuccess; | |
b1ab9ed8 A |
141 | } |
142 | ||
143 | // | |
144 | // Retrieve CSSM-level information for those who want to dig down | |
145 | // | |
5c19dc3a | 146 | /* OS X only: __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_2, __MAC_10_7, __IPHONE_NA, __IPHONE_NA) */ |
b1ab9ed8 A |
147 | OSStatus SecTrustGetCssmResult(SecTrustRef trust, CSSM_TP_VERIFY_CONTEXT_RESULT_PTR *result) |
148 | { | |
5c19dc3a A |
149 | /* this function is unsupported in unified SecTrust */ |
150 | #if SECTRUST_DEPRECATION_WARNINGS | |
151 | syslog(LOG_ERR, "WARNING: SecTrustGetCssmResult has been deprecated since 10.7, and has no functional equivalent in 10.11. Please use SecTrustCopyResult instead."); | |
152 | #endif | |
153 | if (result) { | |
154 | *result = NULL; | |
155 | } | |
156 | return errSecServiceNotAvailable; | |
b1ab9ed8 A |
157 | } |
158 | ||
fa7225c8 A |
159 | static uint8_t convertCssmResultToPriority(CSSM_RETURN resultCode) { |
160 | switch (resultCode) { | |
161 | /* explicitly not trusted */ | |
162 | case CSSMERR_TP_CERT_REVOKED: | |
163 | case CSSMERR_APPLETP_TRUST_SETTING_DENY: | |
164 | return 1; | |
165 | /* failure to comply with X.509 */ | |
166 | case CSSMERR_APPLETP_NO_BASIC_CONSTRAINTS: | |
167 | case CSSMERR_APPLETP_UNKNOWN_QUAL_CERT_STATEMENT: | |
168 | case CSSMERR_APPLETP_INVALID_EMPTY_SUBJECT: | |
169 | case CSSMERR_APPLETP_INVALID_AUTHORITY_ID: | |
170 | case CSSMERR_TP_INVALID_CERTIFICATE: | |
171 | case CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN: | |
172 | return 2; | |
173 | case CSSMERR_TP_CERT_EXPIRED: | |
174 | return 3; | |
175 | /* doesn't chain to trusted root */ | |
176 | case CSSMERR_TP_NOT_TRUSTED: | |
177 | case CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH: | |
178 | return 4; | |
179 | /* all others are policy-specific failures */ | |
180 | default: | |
181 | return 5; | |
182 | } | |
183 | } | |
184 | ||
b1ab9ed8 A |
185 | // |
186 | // Retrieve CSSM_LEVEL TP return code | |
187 | // | |
5c19dc3a | 188 | /* OS X only: __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_2, __MAC_10_7, __IPHONE_NA, __IPHONE_NA) */ |
b1ab9ed8 A |
189 | OSStatus SecTrustGetCssmResultCode(SecTrustRef trustRef, OSStatus *result) |
190 | { | |
b54c578e | 191 | /* bridge to support old functionality */ |
5c19dc3a A |
192 | #if SECTRUST_DEPRECATION_WARNINGS |
193 | syslog(LOG_ERR, "WARNING: SecTrustGetCssmResultCode has been deprecated since 10.7, and will be removed in a future release. Please use SecTrustCopyProperties instead."); | |
194 | #endif | |
b54c578e A |
195 | if (!trustRef || !result) { |
196 | return errSecParam; | |
197 | } | |
fa7225c8 A |
198 | |
199 | SecTrustResultType trustResult = kSecTrustResultInvalid; | |
200 | (void) SecTrustGetTrustResult(trustRef, &trustResult); | |
201 | if (trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified) { | |
202 | if (result) { *result = 0; } | |
203 | return errSecSuccess; | |
204 | } | |
205 | ||
fa7225c8 A |
206 | OSStatus cssmResultCode = errSecSuccess; |
207 | uint8_t resultCodePriority = 0xFF; | |
208 | CFIndex ix, count = SecTrustGetCertificateCount(trustRef); | |
209 | for (ix = 0; ix < count; ix++) { | |
866f8763 | 210 | CFIndex numStatusCodes; |
fa7225c8 | 211 | CSSM_RETURN *statusCodes = NULL; |
866f8763 | 212 | statusCodes = (CSSM_RETURN*)SecTrustCopyStatusCodes(trustRef, ix, &numStatusCodes); |
fa7225c8 A |
213 | if (statusCodes && numStatusCodes > 0) { |
214 | unsigned int statusIX; | |
215 | for (statusIX = 0; statusIX < numStatusCodes; statusIX++) { | |
216 | CSSM_RETURN currStatus = statusCodes[statusIX]; | |
6b200bc3 A |
217 | uint8_t currPriority = convertCssmResultToPriority(currStatus); |
218 | if (resultCodePriority > currPriority) { | |
fa7225c8 | 219 | cssmResultCode = currStatus; |
6b200bc3 | 220 | resultCodePriority = currPriority; |
fa7225c8 A |
221 | } |
222 | } | |
223 | } | |
224 | if (statusCodes) { free(statusCodes); } | |
225 | if (resultCodePriority == 1) { break; } | |
226 | } | |
227 | ||
b54c578e A |
228 | if (result) { |
229 | *result = cssmResultCode; | |
230 | } | |
231 | return errSecSuccess; | |
b1ab9ed8 A |
232 | } |
233 | ||
5c19dc3a | 234 | /* OS X only: __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_2, __MAC_10_7, __IPHONE_NA, __IPHONE_NA) */ |
b1ab9ed8 A |
235 | OSStatus SecTrustGetTPHandle(SecTrustRef trust, CSSM_TP_HANDLE *handle) |
236 | { | |
5c19dc3a A |
237 | /* this function is unsupported in unified SecTrust */ |
238 | #if SECTRUST_DEPRECATION_WARNINGS | |
239 | syslog(LOG_ERR, "WARNING: SecTrustGetTPHandle has been deprecated since 10.7, and cannot return CSSM objects in 10.11. Please stop using it."); | |
240 | #endif | |
241 | if (handle) { | |
242 | *handle = NULL; | |
243 | } | |
244 | return errSecServiceNotAvailable; | |
427c49bc | 245 | } |
b1ab9ed8 A |
246 | |
247 | // | |
248 | // Get the user's default anchor certificate set | |
249 | // | |
5c19dc3a | 250 | /* OS X only */ |
b1ab9ed8 A |
251 | OSStatus SecTrustCopyAnchorCertificates(CFArrayRef *anchorCertificates) |
252 | { | |
427c49bc | 253 | BEGIN_SECAPI |
866f8763 A |
254 | CFArrayRef outArray; |
255 | OSStatus status = SecTrustSettingsCopyUnrestrictedRoots( | |
427c49bc | 256 | true, true, true, /* all domains */ |
866f8763 A |
257 | &outArray); |
258 | if (status != errSecSuccess) { | |
259 | return status; | |
260 | } | |
261 | CFIndex count = outArray ? CFArrayGetCount(outArray) : 0; | |
262 | if(count == 0) { | |
263 | return errSecNoTrustSettings; | |
264 | } | |
265 | ||
266 | /* Go through outArray and do a SecTrustEvaluate */ | |
267 | CFIndex i; | |
268 | SecPolicyRef policy = SecPolicyCreateBasicX509(); | |
79b9da22 | 269 | SecTrustRef trust = NULL; |
866f8763 A |
270 | CFMutableArrayRef trustedCertArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); |
271 | for (i = 0; i < count ; i++) { | |
866f8763 A |
272 | SecTrustResultType result; |
273 | SecCertificateRef certificate = (SecCertificateRef) CFArrayGetValueAtIndex(outArray, i); | |
274 | status = SecTrustCreateWithCertificates(certificate, policy, &trust); | |
275 | if (status != errSecSuccess) { | |
276 | CFReleaseSafe(trustedCertArray); | |
277 | goto out; | |
278 | } | |
279 | status = SecTrustEvaluate(trust, &result); | |
280 | if (status != errSecSuccess) { | |
281 | CFReleaseSafe(trustedCertArray); | |
282 | goto out; | |
283 | } | |
284 | if (result != kSecTrustResultFatalTrustFailure) { | |
285 | CFArrayAppendValue(trustedCertArray, certificate); | |
286 | } | |
79b9da22 | 287 | CFReleaseNull(trust); |
866f8763 A |
288 | } |
289 | if (CFArrayGetCount(trustedCertArray) == 0) { | |
290 | status = errSecNoTrustSettings; | |
291 | CFReleaseSafe(trustedCertArray); | |
292 | goto out; | |
293 | } | |
294 | *anchorCertificates = trustedCertArray; | |
295 | out: | |
296 | CFReleaseSafe(outArray); | |
297 | CFReleaseSafe(policy); | |
79b9da22 | 298 | CFReleaseSafe(trust); |
866f8763 | 299 | return status; |
427c49bc | 300 | END_SECAPI |
b1ab9ed8 A |
301 | } |
302 | ||
b54c578e A |
303 | /* |
304 | * We have an iOS-style SecTrustRef, but we need to return a CDSA-based SecKeyRef. | |
305 | * | |
306 | * If you need a SecKeyRef based of the iOS based SecKey, check certificate chain | |
307 | * length, get certificate with SecTrustGetCertificateAtIndex(0), use | |
308 | * SecCertificateCopyKey() to get a iOS based key. | |
5c19dc3a A |
309 | */ |
310 | SecKeyRef SecTrustCopyPublicKey(SecTrustRef trust) | |
311 | { | |
312 | SecKeyRef pubKey = NULL; | |
313 | SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, 0); | |
79b9da22 A |
314 | #pragma clang diagnostic push |
315 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
5c19dc3a | 316 | (void) SecCertificateCopyPublicKey(certificate, &pubKey); |
79b9da22 | 317 | #pragma clang diagnostic pop |
5c19dc3a A |
318 | return pubKey; |
319 | } | |
b1ab9ed8 | 320 | |
fa7225c8 A |
321 | // cannot link against the new iOS SecTrust from this implementation, |
322 | // so there are no possible accessors for the fields of this struct | |
323 | typedef struct __TSecTrust { | |
324 | CFRuntimeBase _base; | |
325 | CFArrayRef _certificates; | |
326 | CFArrayRef _anchors; | |
327 | CFTypeRef _policies; | |
328 | CFArrayRef _responses; | |
329 | CFArrayRef _SCTs; | |
330 | CFArrayRef _trustedLogs; | |
331 | CFDateRef _verifyDate; | |
332 | CFTypeRef _chain; | |
333 | SecKeyRef _publicKey; | |
334 | CFArrayRef _details; | |
335 | CFDictionaryRef _info; | |
336 | CFArrayRef _exceptions; | |
337 | SecTrustResultType _trustResult; | |
338 | bool _anchorsOnly; | |
339 | bool _keychainsAllowed; | |
340 | void* _legacy_info_array; | |
341 | void* _legacy_status_array; | |
6b200bc3 | 342 | dispatch_queue_t _trustQueue; |
fa7225c8 A |
343 | } TSecTrust; |
344 | ||
fa7225c8 A |
345 | CFArrayRef SecTrustCopyInputCertificates(SecTrustRef trust) |
346 | { | |
347 | if (!trust) { return NULL; }; | |
348 | TSecTrust *secTrust = (TSecTrust *)trust; | |
349 | if (secTrust->_certificates) { | |
350 | CFRetain(secTrust->_certificates); | |
351 | } | |
352 | return secTrust->_certificates; | |
353 | } | |
fa7225c8 | 354 | |
fa7225c8 A |
355 | CFArrayRef SecTrustCopyInputAnchors(SecTrustRef trust) |
356 | { | |
357 | if (!trust) { return NULL; }; | |
358 | TSecTrust *secTrust = (TSecTrust *)trust; | |
359 | if (secTrust->_anchors) { | |
360 | CFRetain(secTrust->_anchors); | |
361 | } | |
362 | return secTrust->_anchors; | |
363 | } | |
fa7225c8 | 364 | |
fa7225c8 A |
365 | // Return the constructed certificate chain for this trust reference, |
366 | // making output certificates pointer-equivalent to any provided input | |
367 | // certificates (where possible) for legacy behavioral compatibility. | |
368 | // Caller must release this array. | |
369 | // | |
370 | CFArrayRef SecTrustCopyConstructedChain(SecTrustRef trust) | |
371 | { | |
372 | CFMutableArrayRef certChain = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
373 | CFIndex idx, count = SecTrustGetCertificateCount(trust); | |
374 | for (idx=0; idx < count; idx++) { | |
375 | SecCertificateRef certificate = SecTrustGetCertificateAtIndex(trust, idx); | |
376 | if (certificate) { | |
377 | CFArrayAppendValue(certChain, certificate); | |
378 | } | |
379 | } | |
380 | // <rdar://24393060> | |
381 | // Some callers make the assumption that the certificates in | |
382 | // this chain are pointer-equivalent to ones they passed to the | |
383 | // SecTrustCreateWithCertificates function. We'll maintain that | |
384 | // behavior here for compatibility. | |
385 | // | |
386 | CFArrayRef inputCertArray = SecTrustCopyInputCertificates(trust); | |
387 | CFArrayRef inputAnchorArray = SecTrustCopyInputAnchors(trust); | |
388 | CFIndex inputCertIdx, inputCertCount = (inputCertArray) ? CFArrayGetCount(inputCertArray) : 0; | |
389 | CFIndex inputAnchorIdx, inputAnchorCount = (inputAnchorArray) ? CFArrayGetCount(inputAnchorArray) : 0; | |
390 | for (idx=0; idx < count; idx++) { | |
391 | SecCertificateRef tmpCert = (SecCertificateRef) CFArrayGetValueAtIndex(certChain, idx); | |
392 | if (tmpCert) { | |
393 | SecCertificateRef matchCert = NULL; | |
394 | for (inputCertIdx=0; inputCertIdx < inputCertCount && !matchCert; inputCertIdx++) { | |
395 | SecCertificateRef inputCert = (SecCertificateRef) CFArrayGetValueAtIndex(inputCertArray, inputCertIdx); | |
396 | if (inputCert && CFEqual(inputCert, tmpCert)) { | |
397 | matchCert = inputCert; | |
398 | } | |
399 | } | |
400 | for (inputAnchorIdx=0; inputAnchorIdx < inputAnchorCount && !matchCert; inputAnchorIdx++) { | |
401 | SecCertificateRef inputAnchor = (SecCertificateRef) CFArrayGetValueAtIndex(inputAnchorArray, inputAnchorIdx); | |
402 | if (inputAnchor && CFEqual(inputAnchor, tmpCert)) { | |
403 | matchCert = inputAnchor; | |
404 | } | |
405 | } | |
406 | if (matchCert) { | |
407 | CFArraySetValueAtIndex(certChain, idx, matchCert); | |
408 | } | |
409 | } | |
410 | } | |
411 | if (inputCertArray) { | |
412 | CFRelease(inputCertArray); | |
413 | } | |
414 | if (inputAnchorArray) { | |
415 | CFRelease(inputAnchorArray); | |
416 | } | |
417 | return certChain; | |
418 | } | |
fa7225c8 | 419 | |
fa7225c8 A |
420 | // |
421 | // Here is where backward compatibility gets ugly. CSSM_TP_APPLE_EVIDENCE_INFO does not exist | |
422 | // in the unified SecTrust world. Unfortunately, some clients are still calling legacy APIs | |
423 | // (e.g. SecTrustGetResult) and grubbing through the info for StatusBits and StatusCodes. | |
424 | // SecTrustGetEvidenceInfo builds the legacy evidence info structure as needed, and returns | |
425 | // a pointer to it. The evidence data is allocated here and set in the _legacy_* fields | |
426 | // of the TSecTrust; the trust object subsequently owns it. The returned pointer is expected | |
427 | // to be valid for the lifetime of the SecTrustRef, or until the trust parameters are changed, | |
428 | // which would force re-evaluation. | |
429 | // | |
430 | static CSSM_TP_APPLE_EVIDENCE_INFO * | |
431 | SecTrustGetEvidenceInfo(SecTrustRef trust) | |
432 | { | |
433 | TSecTrust *secTrust = (TSecTrust *)trust; | |
434 | if (!secTrust) { | |
435 | return NULL; | |
436 | } | |
6b200bc3 | 437 | if (secTrust->_trustResult != kSecTrustResultInvalid && |
fa7225c8 A |
438 | secTrust->_legacy_info_array) { |
439 | // we've already got valid evidence info, return it now. | |
440 | return (CSSM_TP_APPLE_EVIDENCE_INFO *)secTrust->_legacy_info_array; | |
441 | } | |
442 | ||
443 | // Getting the count implicitly evaluates the chain if necessary. | |
444 | CFIndex idx, count = SecTrustGetCertificateCount(trust); | |
445 | CFArrayRef inputCertArray = SecTrustCopyInputCertificates(trust); | |
446 | CFArrayRef inputAnchorArray = SecTrustCopyInputAnchors(trust); | |
447 | CFIndex inputCertIdx, inputCertCount = (inputCertArray) ? CFArrayGetCount(inputCertArray) : 0; | |
448 | CFIndex inputAnchorIdx, inputAnchorCount = (inputAnchorArray) ? CFArrayGetCount(inputAnchorArray) : 0; | |
449 | ||
450 | CSSM_TP_APPLE_EVIDENCE_INFO *infoArray = (CSSM_TP_APPLE_EVIDENCE_INFO *)calloc(count, sizeof(CSSM_TP_APPLE_EVIDENCE_INFO)); | |
451 | CSSM_RETURN *statusArray = NULL; | |
866f8763 | 452 | CFIndex numStatusCodes = 0; |
fa7225c8 A |
453 | |
454 | // Set status codes for each certificate in the constructed chain | |
455 | for (idx=0; idx < count; idx++) { | |
456 | SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, idx); | |
457 | if (!cert) { | |
458 | continue; | |
459 | } | |
460 | CSSM_TP_APPLE_EVIDENCE_INFO *evInfo = &infoArray[idx]; | |
461 | ||
462 | /* first the booleans (StatusBits flags) */ | |
463 | CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); | |
464 | if (secTrust->_verifyDate) { | |
465 | now = CFDateGetAbsoluteTime(secTrust->_verifyDate); | |
466 | } | |
467 | CFAbsoluteTime na = SecCertificateNotValidAfter(cert); | |
468 | if (na < now) { | |
469 | evInfo->StatusBits |= CSSM_CERT_STATUS_EXPIRED; | |
470 | } | |
471 | CFAbsoluteTime nb = SecCertificateNotValidBefore(cert); | |
472 | if (nb > now) { | |
473 | evInfo->StatusBits |= CSSM_CERT_STATUS_NOT_VALID_YET; | |
474 | } | |
475 | for (inputAnchorIdx=0; inputAnchorIdx < inputAnchorCount; inputAnchorIdx++) { | |
476 | SecCertificateRef inputAnchor = (SecCertificateRef) CFArrayGetValueAtIndex(inputAnchorArray, inputAnchorIdx); | |
477 | if (inputAnchor && CFEqual(inputAnchor, cert)) { | |
478 | evInfo->StatusBits |= CSSM_CERT_STATUS_IS_IN_ANCHORS; | |
479 | break; | |
480 | } | |
481 | } | |
482 | for (inputCertIdx=0; inputCertIdx < inputCertCount; inputCertIdx++) { | |
483 | SecCertificateRef inputCert = (SecCertificateRef) CFArrayGetValueAtIndex(inputCertArray, inputCertIdx); | |
484 | if (inputCert && CFEqual(inputCert, cert)) { | |
485 | evInfo->StatusBits |= CSSM_CERT_STATUS_IS_IN_INPUT_CERTS; | |
486 | break; | |
487 | } | |
488 | } | |
489 | ||
490 | /* See if there are trust settings for this certificate. */ | |
491 | CFStringRef hashStr = SecTrustSettingsCertHashStrFromCert(cert); | |
492 | bool foundMatch = false; | |
493 | bool foundAny = false; | |
494 | CSSM_RETURN *errors = NULL; | |
495 | uint32 errorCount = 0; | |
496 | OSStatus status = 0; | |
6b200bc3 | 497 | SecTrustSettingsDomain foundDomain = kSecTrustSettingsDomainUser; |
fa7225c8 A |
498 | SecTrustSettingsResult foundResult = kSecTrustSettingsResultInvalid; |
499 | bool isSelfSigned = false; | |
500 | if ((count - 1) == idx) { | |
501 | // Only the last cert in the chain needs to be considered | |
502 | Boolean selfSigned; | |
503 | status = SecCertificateIsSelfSigned(cert, &selfSigned); | |
504 | isSelfSigned = (status) ? false : ((selfSigned) ? true : false); | |
505 | if (isSelfSigned) { | |
506 | evInfo->StatusBits |= CSSM_CERT_STATUS_IS_ROOT; | |
507 | } | |
508 | } | |
509 | // STU: rdar://25554967 | |
510 | // %%% need to get policyOID, policyString, and keyUsage here! | |
511 | ||
512 | status = SecTrustSettingsEvaluateCert( | |
513 | hashStr, /* certHashStr */ | |
514 | NULL, /* policyOID (optional) */ | |
515 | NULL, /* policyString (optional) */ | |
516 | 0, /* policyStringLen */ | |
517 | 0, /* keyUsage */ | |
518 | isSelfSigned, /* isRootCert */ | |
519 | &foundDomain, /* foundDomain */ | |
520 | &errors, /* allowedErrors -- MUST FREE */ | |
521 | &errorCount, /* numAllowedErrors */ | |
522 | &foundResult, /* resultType */ | |
523 | &foundMatch, /* foundMatchingEntry */ | |
524 | &foundAny); /* foundAnyEntry */ | |
525 | ||
526 | if (status == errSecSuccess) { | |
527 | if (foundMatch) { | |
528 | switch (foundResult) { | |
529 | case kSecTrustSettingsResultTrustRoot: | |
530 | case kSecTrustSettingsResultTrustAsRoot: | |
531 | /* these two can be disambiguated by IS_ROOT */ | |
532 | evInfo->StatusBits |= CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST; | |
533 | break; | |
534 | case kSecTrustSettingsResultDeny: | |
535 | evInfo->StatusBits |= CSSM_CERT_STATUS_TRUST_SETTINGS_DENY; | |
536 | break; | |
537 | case kSecTrustSettingsResultUnspecified: | |
538 | case kSecTrustSettingsResultInvalid: | |
539 | default: | |
540 | break; | |
541 | } | |
542 | } | |
543 | } | |
544 | if (errors) { | |
545 | free(errors); | |
546 | } | |
547 | if (hashStr) { | |
548 | CFRelease(hashStr); | |
549 | } | |
550 | ||
866f8763 A |
551 | CFIndex numCodes=0; |
552 | CSSM_RETURN *statusCodes = (CSSM_RETURN*)SecTrustCopyStatusCodes(trust, idx, &numCodes); | |
fa7225c8 A |
553 | if (statusCodes) { |
554 | // Realloc space for these status codes at end of our status codes block. | |
555 | // Two important things to note: | |
866f8763 | 556 | // 1. the actual length is numCodes+1 because SecTrustCopyStatusCodes |
fa7225c8 A |
557 | // allocates one more element at the end for the CrlReason value. |
558 | // 2. realloc may cause the pointer to move, which means we will | |
559 | // need to fix up the StatusCodes fields after we're done with this loop. | |
866f8763 | 560 | CFIndex totalStatusCodes = numStatusCodes + numCodes + 1; |
fa7225c8 A |
561 | statusArray = (CSSM_RETURN *)realloc(statusArray, totalStatusCodes * sizeof(CSSM_RETURN)); |
562 | evInfo->StatusCodes = &statusArray[numStatusCodes]; | |
866f8763 | 563 | evInfo->NumStatusCodes = (uint32)numCodes; |
fa7225c8 A |
564 | // Copy the new codes (plus one) into place |
565 | for (unsigned int cpix = 0; cpix <= numCodes; cpix++) { | |
566 | evInfo->StatusCodes[cpix] = statusCodes[cpix]; | |
567 | } | |
568 | numStatusCodes = totalStatusCodes; | |
569 | free(statusCodes); | |
570 | } | |
571 | ||
572 | if(evInfo->StatusBits & (CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST | | |
573 | CSSM_CERT_STATUS_TRUST_SETTINGS_DENY | | |
574 | CSSM_CERT_STATUS_TRUST_SETTINGS_IGNORED_ERROR)) { | |
575 | /* Something noteworthy happened involving TrustSettings */ | |
576 | uint32 whichDomain = 0; | |
577 | switch(foundDomain) { | |
578 | case kSecTrustSettingsDomainUser: | |
579 | whichDomain = CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_USER; | |
580 | break; | |
581 | case kSecTrustSettingsDomainAdmin: | |
582 | whichDomain = CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_ADMIN; | |
583 | break; | |
584 | case kSecTrustSettingsDomainSystem: | |
585 | whichDomain = CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_SYSTEM; | |
586 | break; | |
587 | } | |
588 | evInfo->StatusBits |= whichDomain; | |
589 | } | |
590 | ||
591 | /* index into raw cert group or AnchorCerts depending on IS_IN_ANCHORS */ | |
592 | //evInfo->Index = certInfo->index(); | |
593 | /* nonzero if cert came from a DLDB */ | |
594 | //evInfo->DlDbHandle = certInfo->dlDbHandle(); | |
595 | //evInfo->UniqueRecord = certInfo->uniqueRecord(); | |
596 | } | |
597 | ||
598 | // Now that all the status codes have been allocated in a contiguous block, | |
599 | // refresh the StatusCodes pointer in each array element. | |
600 | numStatusCodes = 0; | |
601 | for (idx=0; idx < count; idx++) { | |
602 | CSSM_TP_APPLE_EVIDENCE_INFO *evInfo = &infoArray[idx]; | |
603 | evInfo->StatusCodes = &statusArray[numStatusCodes]; | |
604 | numStatusCodes += evInfo->NumStatusCodes + 1; | |
605 | } | |
606 | ||
607 | secTrust->_legacy_info_array = infoArray; | |
608 | secTrust->_legacy_status_array = statusArray; | |
609 | ||
610 | if (inputCertArray) { | |
611 | CFRelease(inputCertArray); | |
612 | } | |
613 | if (inputAnchorArray) { | |
614 | CFRelease(inputAnchorArray); | |
615 | } | |
616 | ||
617 | return (CSSM_TP_APPLE_EVIDENCE_INFO *)secTrust->_legacy_info_array; | |
618 | } | |
427c49bc | 619 | |
fa7225c8 A |
620 | CFArrayRef SecTrustCopyProperties(SecTrustRef trust) { |
621 | /* OS X creates a completely different structure with one dictionary for each certificate */ | |
622 | CFIndex ix, count = SecTrustGetCertificateCount(trust); | |
623 | ||
624 | CFMutableArrayRef properties = CFArrayCreateMutable(kCFAllocatorDefault, count, | |
625 | &kCFTypeArrayCallBacks); | |
626 | ||
627 | for (ix = 0; ix < count; ix++) { | |
628 | CFMutableDictionaryRef certDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, | |
629 | &kCFTypeDictionaryValueCallBacks); | |
630 | /* Populate the certificate title */ | |
631 | SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, ix); | |
632 | if (cert) { | |
633 | CFStringRef subjectSummary = SecCertificateCopySubjectSummary(cert); | |
634 | if (subjectSummary) { | |
635 | CFDictionaryAddValue(certDict, kSecPropertyTypeTitle, subjectSummary); | |
636 | CFRelease(subjectSummary); | |
637 | } | |
638 | } | |
639 | ||
640 | /* Populate a revocation reason if the cert was revoked */ | |
866f8763 | 641 | CFIndex numStatusCodes; |
fa7225c8 | 642 | CSSM_RETURN *statusCodes = NULL; |
866f8763 | 643 | statusCodes = (CSSM_RETURN*)SecTrustCopyStatusCodes(trust, ix, &numStatusCodes); |
fa7225c8 | 644 | if (statusCodes) { |
866f8763 | 645 | SInt32 reason = statusCodes[numStatusCodes]; // stored at end of status codes array |
fa7225c8 A |
646 | if (reason > 0) { |
647 | CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason); | |
648 | if (cfreason) { | |
649 | CFDictionarySetValue(certDict, kSecTrustRevocationReason, cfreason); | |
650 | CFRelease(cfreason); | |
651 | } | |
652 | } | |
653 | free(statusCodes); | |
654 | } | |
655 | ||
656 | /* Populate the error in the leaf dictionary */ | |
657 | if (ix == 0) { | |
658 | OSStatus error = errSecSuccess; | |
659 | (void)SecTrustGetCssmResultCode(trust, &error); | |
660 | CFStringRef errorStr = SecCopyErrorMessageString(error, NULL); | |
661 | if (errorStr) { | |
662 | CFDictionarySetValue(certDict, kSecPropertyTypeError, errorStr); | |
663 | CFRelease(errorStr); | |
664 | } | |
665 | } | |
666 | ||
667 | CFArrayAppendValue(properties, certDict); | |
6b200bc3 | 668 | CFRelease(certDict); |
fa7225c8 A |
669 | } |
670 | ||
671 | return properties; | |
672 | } | |
b1ab9ed8 A |
673 | |
674 | /* deprecated in 10.5 */ | |
675 | OSStatus SecTrustGetCSSMAnchorCertificates(const CSSM_DATA **cssmAnchors, | |
676 | uint32 *cssmAnchorCount) | |
677 | { | |
5c19dc3a A |
678 | /* this function is unsupported in unified SecTrust */ |
679 | #if SECTRUST_DEPRECATION_WARNINGS | |
680 | syslog(LOG_ERR, "WARNING: SecTrustGetCSSMAnchorCertificates has been deprecated since 10.5, and cannot return CSSM objects in 10.11. Please stop using it."); | |
681 | #endif | |
682 | if (cssmAnchors) { | |
683 | *cssmAnchors = NULL; | |
684 | } | |
685 | if (cssmAnchorCount) { | |
686 | *cssmAnchorCount = 0; | |
687 | } | |
688 | return errSecServiceNotAvailable; | |
b1ab9ed8 A |
689 | } |
690 | ||
691 | ||
692 | // | |
693 | // Get and set user trust settings. Deprecated in 10.5. | |
694 | // User Trust getter, deprecated, works as it always has. | |
695 | // | |
696 | OSStatus SecTrustGetUserTrust(SecCertificateRef certificate, | |
697 | SecPolicyRef policy, SecTrustUserSetting *trustSetting) | |
698 | { | |
5c19dc3a A |
699 | /* this function is unsupported in unified SecTrust */ |
700 | #if SECTRUST_DEPRECATION_WARNINGS | |
701 | syslog(LOG_ERR, "WARNING: SecTrustGetUserTrust has been deprecated since 10.5, and does nothing in 10.11. Please stop using it."); | |
702 | #endif | |
703 | return errSecServiceNotAvailable; | |
b1ab9ed8 A |
704 | } |
705 | ||
706 | // | |
707 | // The public setter, also deprecated; it maps to the appropriate | |
427c49bc | 708 | // Trust Settings call if possible, else throws errSecUnimplemented. |
b1ab9ed8 A |
709 | // |
710 | OSStatus SecTrustSetUserTrust(SecCertificateRef certificate, | |
711 | SecPolicyRef policy, SecTrustUserSetting trustSetting) | |
712 | { | |
5c19dc3a A |
713 | /* this function is unsupported in unified SecTrust */ |
714 | #if SECTRUST_DEPRECATION_WARNINGS | |
715 | syslog(LOG_ERR, "WARNING: SecTrustSetUserTrust has been deprecated since 10.5, and does nothing in 10.11. Please stop using it."); | |
716 | #endif | |
717 | return errSecServiceNotAvailable; | |
b1ab9ed8 A |
718 | } |
719 | ||
720 | // | |
721 | // This one is the now-private version of what SecTrustSetUserTrust() used to | |
722 | // be. The public API can no longer manipulate User Trust settings, only | |
723 | // view them. | |
724 | // | |
725 | OSStatus SecTrustSetUserTrustLegacy(SecCertificateRef certificate, | |
726 | SecPolicyRef policy, SecTrustUserSetting trustSetting) | |
727 | { | |
5c19dc3a A |
728 | /* this function is unsupported in unified SecTrust */ |
729 | #if SECTRUST_DEPRECATION_WARNINGS | |
730 | syslog(LOG_ERR, "WARNING: SecTrustSetUserTrustLegacy does nothing in 10.11. Please stop using it."); | |
731 | #endif | |
732 | return errSecServiceNotAvailable; | |
b1ab9ed8 | 733 | } |