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