2 * Copyright (c) 2002-2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
27 #include <security_keychain/Trust.h>
28 #include <security_keychain/TrustSettingsSchema.h>
29 #include <security_cdsa_utilities/cssmdates.h>
30 #include <security_utilities/cfutilities.h>
31 #include <CoreFoundation/CoreFoundation.h>
32 #include <Security/SecCertificate.h>
33 #include <Security/SecTrust.h>
34 #include "SecBridge.h"
35 #include "TrustAdditions.h"
36 #include "TrustKeychains.h"
37 #include <security_cdsa_client/dlclient.h>
40 using namespace Security
;
41 using namespace KeychainCore
;
44 // Translate CFDataRef to CssmData. The output shares the input's buffer.
46 static inline CssmData
cfData(CFDataRef data
)
48 return CssmData(const_cast<UInt8
*>(CFDataGetBytePtr(data
)),
49 CFDataGetLength(data
));
53 // Convert a SecPointer to a CF object.
55 static SecCertificateRef
56 convert(const SecPointer
<Certificate
> &certificate
)
62 // For now, we use a global TrustStore
64 ModuleNexus
<TrustStore
> Trust::gStore
;
66 #pragma mark -- TrustKeychains --
68 static const CSSM_DL_DB_HANDLE nullCSSMDLDBHandle
= {0,};
70 // TrustKeychains maintains a global reference to standard system keychains,
71 // to avoid having them be opened anew for each Trust instance.
78 CSSM_DL_DB_HANDLE
rootStoreHandle() { return mRootStoreHandle
; }
79 CSSM_DL_DB_HANDLE
systemKcHandle() { return mSystem
? mSystem
->database()->handle() : nullCSSMDLDBHandle
; }
80 Keychain
&systemKc() { return mSystem
; }
81 Keychain
&rootStore() { return *mRootStore
; }
87 CSSM_DL_DB_HANDLE mRootStoreHandle
;
92 // Singleton maintaining open references to standard system keychains,
93 // to avoid having them be opened anew every time SecTrust is used.
96 static ModuleNexus
<TrustKeychains
> trustKeychains
;
97 static ModuleNexus
<RecursiveMutex
> trustKeychainsMutex
;
99 extern "C" bool GetServerMode();
101 TrustKeychains::TrustKeychains() :
102 mRootStoreHandle(nullCSSMDLDBHandle
),
103 mSystem(globals().storageManager
.make(ADMIN_CERT_STORE_PATH
, false))
105 if (GetServerMode()) // in server mode? Don't make a keychain for the root store
107 mRootStoreDL
= new DL(gGuidAppleFileDL
),
108 mRootStoreDb
= new Db(*mRootStoreDL
, SYSTEM_ROOT_STORE_PATH
),
109 (*mRootStoreDb
)->activate();
110 mRootStoreHandle
= (*mRootStoreDb
)->handle();
114 mRootStore
= new Keychain(globals().storageManager
.make(SYSTEM_ROOT_STORE_PATH
, false));
115 (*mRootStore
)->database()->activate();
116 mRootStoreHandle
= (*mRootStore
)->database()->handle();
120 RecursiveMutex
& SecTrustKeychainsGetMutex()
122 return trustKeychainsMutex();
125 #pragma mark -- Trust --
127 // Construct a Trust object with suitable defaults.
128 // Use setters for additional arguments before calling evaluate().
130 Trust::Trust(CFTypeRef certificates
, CFTypeRef policies
)
131 : mTP(gGuidAppleX509TP
), mAction(CSSM_TP_ACTION_DEFAULT
),
132 mCerts(cfArrayize(certificates
)), mPolicies(cfArrayize(policies
)),
133 mSearchLibs(NULL
), mSearchLibsSet(false), mResult(kSecTrustResultInvalid
),
134 mUsingTrustSettings(false), mAnchorPolicy(useAnchorsDefault
), mMutex(Mutex::recursive
)
137 mPolicies
.take(CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
));
143 // Clean up a Trust object
157 // Get searchLibs (a vector of Keychain objects);
158 // normally initialized to default search list
160 StorageManager::KeychainList
& Trust::searchLibs(bool init
)
163 mSearchLibs
= new StorageManager::KeychainList
;
165 globals().storageManager
.getSearchList(*mSearchLibs
);
173 // Set searchLibs to provided vector of Keychain objects
175 void Trust::searchLibs(StorageManager::KeychainList
&libs
)
177 searchLibs(false) = libs
;
178 mSearchLibsSet
= true;
183 // Retrieve the last TP evaluation result, if any
185 CSSM_TP_VERIFY_CONTEXT_RESULT_PTR
Trust::cssmResult()
187 if (mResult
== kSecTrustResultInvalid
)
188 MacOSError::throwMe(errSecTrustNotAvailable
);
193 // SecCertificateRef -> CssmData
195 CssmData
cfCertificateData(SecCertificateRef certificate
)
197 return Certificate::required(certificate
)->data();
200 // SecPolicyRef -> CssmField (CFDataRef/NULL or oid/value of a SecPolicy)
202 CssmField
cfField(SecPolicyRef item
)
204 SecPointer
<Policy
> policy
= Policy::required(SecPolicyRef(item
));
205 return CssmField(policy
->oid(), policy
->value());
208 // SecKeychain -> CssmDlDbHandle
211 CSSM_DL_DB_HANDLE
cfKeychain(SecKeychainRef ref
)
213 Keychain keychain
= KeychainImpl::required(ref
);
214 return keychain
->database()->handle();
219 void showCertSKID(const void *value
, void *context
);
223 // Here's the big "E" - evaluation.
224 // We build most of the CSSM-layer input structures dynamically right here;
225 // they will auto-destruct when we're done. The output structures are kept
226 // around (in our data members) for later analysis.
227 // Note that evaluate() can be called repeatedly, so we must be careful to
228 // dispose of prior results.
230 void Trust::evaluate(bool disableEV
)
232 bool isEVCandidate
=false;
233 // begin evaluation block with stack-based mutex
235 StLock
<Mutex
>_(mMutex
);
236 // if we have evaluated before, release prior result
239 // determine whether the leaf certificate is an EV candidate
240 CFArrayRef allowedAnchors
= NULL
;
242 allowedAnchors
= allowedEVRootsForLeafCertificate(mCerts
);
243 isEVCandidate
= (allowedAnchors
!= NULL
);
245 CFArrayRef filteredCerts
= NULL
;
247 secdebug("evTrust", "Trust::evaluate() certificate is EV candidate");
248 filteredCerts
= potentialEVChainWithCertificates(mCerts
);
249 mCerts
= filteredCerts
;
251 secdebug("evTrust", "Trust::evaluate() performing standard evaluation");
253 filteredCerts
= CFArrayCreateMutableCopy(NULL
, 0, mCerts
);
256 allowedAnchors
= CFArrayCreateMutableCopy(NULL
, 0, mAnchors
);
259 // retain these certs as long as we potentially could have results involving them
260 // (note that assignment to a CFRef type performs an implicit retain)
261 mAllowedAnchors
= allowedAnchors
;
262 mFilteredCerts
= filteredCerts
;
265 CFRelease(allowedAnchors
);
267 CFRelease(filteredCerts
);
271 secdebug("trusteval", "Trust::evaluate: anchors: %ld", CFArrayGetCount(mAllowedAnchors
));
273 CFArrayApplyFunction(mAllowedAnchors
, CFRangeMake(0, CFArrayGetCount(mAllowedAnchors
)), showCertSKID
, NULL
);
277 // set default search list from user's default, if caller did not explicitly supply it
278 if(!mSearchLibsSet
) {
279 globals().storageManager
.getSearchList(searchLibs());
280 mSearchLibsSet
= true;
283 // build the target cert group
284 CFToVector
<CssmData
, SecCertificateRef
, cfCertificateData
> subjects(mFilteredCerts
);
285 CertGroup
subjectCertGroup(CSSM_CERT_X_509v3
,
286 CSSM_CERT_ENCODING_BER
, CSSM_CERTGROUP_DATA
);
287 subjectCertGroup
.count() = subjects
;
288 subjectCertGroup
.blobCerts() = subjects
;
290 // build a TP_VERIFY_CONTEXT, a veritable nightmare of a data structure
291 TPBuildVerifyContext
context(mAction
);
294 * Guarantee *some* action data...
295 * NOTE this only works with the local X509 TP. When this module can deal
296 * with other TPs, this must be revisited.
298 CSSM_APPLE_TP_ACTION_DATA localActionData
;
299 memset(&localActionData
, 0, sizeof(localActionData
));
300 CssmData
localActionCData((uint8
*)&localActionData
, sizeof(localActionData
));
301 CSSM_APPLE_TP_ACTION_DATA
*actionDataP
= &localActionData
;
303 context
.actionData() = cfData(mActionData
);
304 actionDataP
= (CSSM_APPLE_TP_ACTION_DATA
*)context
.actionData().data();
307 context
.actionData() = localActionCData
;
310 bool hasSSLPolicy
= policySpecified(mPolicies
, CSSMOID_APPLE_TP_SSL
);
311 bool hasEAPPolicy
= policySpecified(mPolicies
, CSSMOID_APPLE_TP_EAP
);
314 // always check trust settings if caller did not provide explicit trust anchors
315 actionDataP
->ActionFlags
|= CSSM_TP_ACTION_TRUST_SETTINGS
;
318 if (mNetworkPolicy
== useNetworkDefault
) {
320 // enable network cert fetch for SSL only: <rdar://7422356>
321 actionDataP
->ActionFlags
|= CSSM_TP_ACTION_FETCH_CERT_FROM_NET
;
324 else if (mNetworkPolicy
== useNetworkEnabled
)
325 actionDataP
->ActionFlags
|= CSSM_TP_ACTION_FETCH_CERT_FROM_NET
;
326 else if (mNetworkPolicy
== useNetworkDisabled
)
327 actionDataP
->ActionFlags
&= ~(CSSM_TP_ACTION_FETCH_CERT_FROM_NET
);
329 if (policySpecified(mPolicies
, CSSMOID_APPLE_TP_ESCROW_SERVICE
)) {
330 // ignore expiration dates, per rdar://21943474
331 actionDataP
->ActionFlags
|= (CSSM_TP_ACTION_ALLOW_EXPIRED
|
332 CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT
);
336 * Policies (one at least, please).
337 * For revocation policies, see if any have been explicitly specified...
339 CFMutableArrayRef allPolicies
= NULL
;
340 uint32 numRevocationAdded
= 0;
341 bool requirePerCert
= (actionDataP
->ActionFlags
& CSSM_TP_ACTION_REQUIRE_REV_PER_CERT
);
343 // If a new unified revocation policy was explicitly specified,
344 // convert into old-style individual OCSP and CRL policies.
345 // Note that the caller could configure revocation policy options
346 // to explicitly disable both methods, so 0 policies might be added,
347 // in which case we must no longer consider the cert an EV candidate.
349 allPolicies
= convertRevocationPolicy(numRevocationAdded
, context
.allocator
);
351 // caller has explicitly set the revocation policy they want to use
352 secdebug("evTrust", "Trust::evaluate() using explicit revocation policy (%d)",
354 if (numRevocationAdded
== 0)
355 isEVCandidate
= false;
357 else if (mAnchors
&& (CFArrayGetCount(mAnchors
)==0) && (searchLibs().size()==0)) {
358 // caller explicitly provided empty anchors and no keychain list,
359 // and did not explicitly specify the revocation policy;
360 // override global revocation check setting for this evaluation
361 secdebug("evTrust", "Trust::evaluate() has empty anchors and no keychains");
362 allPolicies
= NULL
; // use only mPolicies
363 isEVCandidate
= false;
365 else if (isEVCandidate
|| requirePerCert
) {
366 // force revocation checking for this evaluation
367 secdebug("evTrust", "Trust::evaluate() forcing OCSP/CRL revocation check");
368 allPolicies
= forceRevocationPolicies(true, requirePerCert
,
369 numRevocationAdded
, context
.allocator
, requirePerCert
);
371 else if(!(revocationPolicySpecified(mPolicies
))) {
372 // none specified in mPolicies; try preferences
373 allPolicies
= addPreferenceRevocationPolicies(!(hasSSLPolicy
|| hasEAPPolicy
),
374 !(hasSSLPolicy
|| hasEAPPolicy
), numRevocationAdded
, context
.allocator
);
376 if (allPolicies
== NULL
) {
377 // use mPolicies; no revocation checking will be performed
378 secdebug("evTrust", "Trust::evaluate() will not perform revocation check");
379 CFIndex numPolicies
= CFArrayGetCount(mPolicies
);
380 CFAllocatorRef allocator
= CFGetAllocator(mPolicies
);
381 allPolicies
= CFArrayCreateMutableCopy(allocator
, numPolicies
, mPolicies
);
383 orderRevocationPolicies(allPolicies
);
384 CFToVector
<CssmField
, SecPolicyRef
, cfField
> policies(allPolicies
);
386 // error exit here if empty policies are not supported
387 if (policies
.empty())
388 MacOSError::throwMe(CSSMERR_TP_INVALID_POLICY_IDENTIFIERS
);
390 context
.setPolicies(policies
, policies
);
392 // anchor certificates (if caller provides them, or if cert requires EV)
393 CFCopyRef
<CFArrayRef
> anchors(mAllowedAnchors
);
394 CFToVector
<CssmData
, SecCertificateRef
, cfCertificateData
> roots(anchors
);
396 // no anchor certificates were provided;
397 // built-in anchors will be trusted unless explicitly disabled.
398 mUsingTrustSettings
= (mAnchorPolicy
< useAnchorsOnly
);
399 secdebug("userTrust", "Trust::evaluate() %s",
400 (mUsingTrustSettings
) ? "using UserTrust" : "has no trusted anchors!");
403 // anchor certificates were provided;
404 // built-in anchors will NOT also be trusted unless explicitly enabled.
405 mUsingTrustSettings
= (mAnchorPolicy
== useAnchorsAndBuiltIns
);
406 secdebug("userTrust", "Trust::evaluate() using %s %s anchors",
407 (mUsingTrustSettings
) ? "UserTrust AND" : "only",
408 (isEVCandidate
) ? "EV" : "caller");
409 context
.anchors(roots
, roots
);
412 // dlDbList (keychain list)
413 vector
<CSSM_DL_DB_HANDLE
> dlDbList
;
415 StLock
<Mutex
> _(SecTrustKeychainsGetMutex());
416 StorageManager::KeychainList
& list
= searchLibs();
417 for (StorageManager::KeychainList::const_iterator it
= list
.begin();
418 it
!= list
.end(); it
++)
422 // For the purpose of looking up intermediate certificates to establish trust,
423 // do not include the network-based LDAP or DotMac pseudo-keychains. (The only
424 // time the network should be consulted for certificates is if there is an AIA
425 // extension with a specific URL, which will be handled by the TP code.)
426 CSSM_DL_DB_HANDLE dldbHandle
= (*it
)->database()->handle();
427 if (dldbHandle
.DLHandle
) {
429 CSSM_RETURN crtn
= CSSM_GetModuleGUIDFromHandle(dldbHandle
.DLHandle
, &guid
);
430 if (crtn
== CSSM_OK
) {
431 if ((memcmp(&guid
, &gGuidAppleLDAPDL
, sizeof(CSSM_GUID
))==0) ||
432 (memcmp(&guid
, &gGuidAppleDotMacDL
, sizeof(CSSM_GUID
))==0)) {
433 continue; // don't add to dlDbList
437 // This DB is OK to search for intermediate certificates.
438 dlDbList
.push_back(dldbHandle
);
444 if(mUsingTrustSettings
) {
445 /* Append system anchors for use with Trust Settings */
447 CSSM_DL_DB_HANDLE rootStoreHandle
= trustKeychains().rootStoreHandle();
448 if (rootStoreHandle
.DBHandle
)
449 dlDbList
.push_back(rootStoreHandle
);
450 actionDataP
->ActionFlags
|= CSSM_TP_ACTION_TRUST_SETTINGS
;
453 // no root store or system keychain; don't use trust settings but continue
454 mUsingTrustSettings
= false;
457 CSSM_DL_DB_HANDLE systemKcHandle
= trustKeychains().systemKcHandle();
458 if (systemKcHandle
.DBHandle
)
459 dlDbList
.push_back(systemKcHandle
);
462 /* Oh well, at least we got the root store DB */
465 context
.setDlDbList((uint32
)dlDbList
.size(), &dlDbList
[0]);
471 CssmUniformDate(static_cast<CFDateRef
>(mVerifyTime
)).convertTo(
472 timeString
, sizeof(timeString
));
473 context
.time(timeString
);
476 // to avoid keychain open/close thrashing, hold a copy of the search list
477 StorageManager::KeychainList
*holdSearchList
= NULL
;
478 if (searchLibs().size() > 0) {
479 holdSearchList
= new StorageManager::KeychainList
;
480 globals().storageManager
.getSearchList(*holdSearchList
);
485 mTP
->certGroupVerify(subjectCertGroup
, context
, &mTpResult
);
486 mTpReturn
= errSecSuccess
;
487 } catch (CommonError
&err
) {
488 mTpReturn
= err
.osStatus();
489 secdebug("trusteval", "certGroupVerify exception: %d", (int)mTpReturn
);
491 mResult
= diagnoseOutcome();
493 // see if we can use the evidence
494 if (mTpResult
.count() > 0
495 && mTpResult
[0].form() == CSSM_EVIDENCE_FORM_APPLE_HEADER
496 && mTpResult
[0].as
<CSSM_TP_APPLE_EVIDENCE_HEADER
>()->Version
== CSSM_TP_APPLE_EVIDENCE_VERSION
497 && mTpResult
.count() == 3
498 && mTpResult
[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP
499 && mTpResult
[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO
) {
500 evaluateUserTrust(*mTpResult
[1].as
<CertGroup
>(),
501 mTpResult
[2].as
<CSSM_TP_APPLE_EVIDENCE_INFO
>(), anchors
);
503 // unexpected evidence information. Can't use it
504 secdebug("trusteval", "unexpected evidence ignored");
507 /* do post-processing for the evaluated certificate chain */
508 CFArrayRef fullChain
= makeCFArray(convert
, mCertChain
);
509 CFDictionaryRef etResult
= extendedTrustResults(fullChain
, mResult
, mTpReturn
, isEVCandidate
);
510 mExtendedResult
= etResult
; // assignment to CFRef type is an implicit retain
515 CFRelease(fullChain
);
519 /* clean up revocation policies we created implicitly */
520 if(numRevocationAdded
) {
521 freeAddedRevocationPolicyData(allPolicies
, numRevocationAdded
, context
.allocator
);
523 CFRelease(allPolicies
);
526 if (holdSearchList
) {
527 delete holdSearchList
;
528 holdSearchList
= NULL
;
530 } // end evaluation block with mutex; releases all temporary allocations in this scope
533 if (isEVCandidate
&& mResult
== kSecTrustResultRecoverableTrustFailure
&&
534 (mTpReturn
== CSSMERR_TP_NOT_TRUSTED
|| isRevocationServerMetaError(mTpReturn
))) {
535 // re-do the evaluation, this time disabling EV
540 // CSSM_RETURN values that map to kSecTrustResultRecoverableTrustFailure.
541 static const CSSM_RETURN recoverableErrors
[] =
543 CSSMERR_TP_INVALID_ANCHOR_CERT
,
544 CSSMERR_TP_NOT_TRUSTED
,
545 CSSMERR_TP_VERIFICATION_FAILURE
,
546 CSSMERR_TP_VERIFY_ACTION_FAILED
,
547 CSSMERR_TP_INVALID_REQUEST_INPUTS
,
548 CSSMERR_TP_CERT_EXPIRED
,
549 CSSMERR_TP_CERT_NOT_VALID_YET
,
550 CSSMERR_TP_CERTIFICATE_CANT_OPERATE
,
551 CSSMERR_TP_INVALID_CERT_AUTHORITY
,
552 CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK
,
553 CSSMERR_APPLETP_HOSTNAME_MISMATCH
,
554 CSSMERR_TP_VERIFY_ACTION_FAILED
,
555 CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
,
556 CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS
,
557 CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE
,
558 CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
,
559 CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS
,
560 CSSMERR_APPLETP_CS_BAD_PATH_LENGTH
,
561 CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE
,
562 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
,
563 CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT
,
564 CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH
,
565 CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN
,
566 CSSMERR_APPLETP_CRL_NOT_FOUND
,
567 CSSMERR_APPLETP_CRL_SERVER_DOWN
,
568 CSSMERR_APPLETP_CRL_NOT_VALID_YET
,
569 CSSMERR_APPLETP_OCSP_UNAVAILABLE
,
570 CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK
,
571 CSSMERR_APPLETP_NETWORK_FAILURE
,
572 CSSMERR_APPLETP_OCSP_RESP_TRY_LATER
,
573 CSSMERR_APPLETP_IDENTIFIER_MISSING
,
575 #define NUM_RECOVERABLE_ERRORS (sizeof(recoverableErrors) / sizeof(CSSM_RETURN))
578 // Classify the TP outcome in terms of a SecTrustResultType
580 SecTrustResultType
Trust::diagnoseOutcome()
582 StLock
<Mutex
>_(mMutex
);
584 uint32 chainLength
= 0;
585 if (mTpResult
.count() == 3 &&
586 mTpResult
[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP
&&
587 mTpResult
[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO
)
589 const CertGroup
&chain
= *mTpResult
[1].as
<CertGroup
>();
590 chainLength
= chain
.count();
594 case errSecSuccess
: // peachy
595 if (mUsingTrustSettings
)
599 const CSSM_TP_APPLE_EVIDENCE_INFO
*infoList
= mTpResult
[2].as
<CSSM_TP_APPLE_EVIDENCE_INFO
>();
600 const TPEvidenceInfo
&info
= TPEvidenceInfo::overlay(infoList
[chainLength
-1]);
601 const CSSM_TP_APPLE_CERT_STATUS resultCertStatus
= info
.status();
602 bool hasUserDomainTrust
= ((resultCertStatus
& CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST
) &&
603 (resultCertStatus
& CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_USER
));
604 bool hasAdminDomainTrust
= ((resultCertStatus
& CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST
) &&
605 (resultCertStatus
& CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_ADMIN
));
606 if (hasUserDomainTrust
|| hasAdminDomainTrust
)
608 return kSecTrustResultProceed
; // explicitly allowed
612 return kSecTrustResultUnspecified
; // cert evaluates OK
613 case CSSMERR_TP_INVALID_CERTIFICATE
: // bad certificate
614 return kSecTrustResultFatalTrustFailure
;
615 case CSSMERR_APPLETP_TRUST_SETTING_DENY
: // authoritative denial
616 return kSecTrustResultDeny
;
621 // a known list of returns maps to kSecTrustResultRecoverableTrustFailure
622 const CSSM_RETURN
*errp
=recoverableErrors
;
623 for(unsigned dex
=0; dex
<NUM_RECOVERABLE_ERRORS
; dex
++, errp
++) {
624 if(*errp
== mTpReturn
) {
625 return kSecTrustResultRecoverableTrustFailure
;
628 return kSecTrustResultOtherError
; // unknown
633 // Assuming a good evidence chain, check user trust
634 // settings and set mResult accordingly.
636 void Trust::evaluateUserTrust(const CertGroup
&chain
,
637 const CSSM_TP_APPLE_EVIDENCE_INFO
*infoList
, CFCopyRef
<CFArrayRef
> anchors
)
639 StLock
<Mutex
>_(mMutex
);
640 // extract cert chain as Certificate objects
641 mCertChain
.resize(chain
.count());
642 for (uint32 n
= 0; n
< mCertChain
.size(); n
++) {
643 const TPEvidenceInfo
&info
= TPEvidenceInfo::overlay(infoList
[n
]);
644 if (info
.recordId()) {
645 Keychain keychain
= keychainByDLDb(info
.DlDbHandle
);
646 DbUniqueRecord
uniqueId(keychain
->database()->newDbUniqueRecord());
647 secdebug("trusteval", "evidence %lu from keychain \"%s\"", (unsigned long)n
, keychain
->name());
648 *static_cast<CSSM_DB_UNIQUE_RECORD_PTR
*>(uniqueId
) = info
.UniqueRecord
;
649 uniqueId
->activate(); // transfers ownership
650 Item ii
= keychain
->item(CSSM_DL_DB_RECORD_X509_CERTIFICATE
, uniqueId
);
651 Certificate
* cert
= dynamic_cast<Certificate
*>(ii
.get());
653 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
655 mCertChain
[n
] = cert
;
656 } else if (info
.status(CSSM_CERT_STATUS_IS_IN_INPUT_CERTS
)) {
657 secdebug("trusteval", "evidence %lu from input cert %lu", (unsigned long)n
, (unsigned long)info
.index());
658 assert(info
.index() < uint32(CFArrayGetCount(mCerts
)));
659 SecCertificateRef cert
= SecCertificateRef(CFArrayGetValueAtIndex(mCerts
,
661 mCertChain
[n
] = Certificate::required(cert
);
662 } else if (info
.status(CSSM_CERT_STATUS_IS_IN_ANCHORS
)) {
663 secdebug("trusteval", "evidence %lu from anchor cert %lu", (unsigned long)n
, (unsigned long)info
.index());
664 assert(info
.index() < uint32(CFArrayGetCount(anchors
)));
665 SecCertificateRef cert
= SecCertificateRef(CFArrayGetValueAtIndex(anchors
,
667 mCertChain
[n
] = Certificate::required(cert
);
669 // unknown source; make a new Certificate for it
670 secdebug("trusteval", "evidence %lu from unknown source", (unsigned long)n
);
672 new Certificate(chain
.blobCerts()[n
],
673 CSSM_CERT_X_509v3
, CSSM_CERT_ENCODING_BER
);
677 // now walk the chain, leaf-to-root, checking for user settings
678 TrustStore
&store
= gStore();
679 SecPointer
<Policy
> policy
= (CFArrayGetCount(mPolicies
)) ?
680 Policy::required(SecPolicyRef(CFArrayGetValueAtIndex(mPolicies
, 0))) : NULL
;
681 for (mResultIndex
= 0;
682 mResult
== kSecTrustResultUnspecified
&& mResultIndex
< mCertChain
.size() && policy
;
684 if (!mCertChain
[mResultIndex
]) {
688 mResult
= store
.find(mCertChain
[mResultIndex
], policy
, searchLibs());
689 secdebug("trusteval", "trustResult=%d from cert %d", (int)mResult
, (int)mResultIndex
);
695 // Release TP evidence information.
696 // This information is severely under-defined by CSSM, so we proceed
698 // (a) If the evidence matches an Apple-defined pattern, use specific
699 // knowledge of that format.
700 // (b) Otherwise, assume that the void * are flat blocks of memory.
702 void Trust::releaseTPEvidence(TPVerifyResult
&result
, Allocator
&allocator
)
704 if (result
.count() > 0) { // something to do
705 if (result
[0].form() == CSSM_EVIDENCE_FORM_APPLE_HEADER
) {
706 // Apple defined evidence form -- use intimate knowledge
707 if (result
[0].as
<CSSM_TP_APPLE_EVIDENCE_HEADER
>()->Version
== CSSM_TP_APPLE_EVIDENCE_VERSION
708 && result
.count() == 3
709 && result
[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP
710 && result
[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO
) {
712 CertGroup
& certs
= *result
[1].as
<CertGroup
>();
713 CSSM_TP_APPLE_EVIDENCE_INFO
*evidence
= result
[2].as
<CSSM_TP_APPLE_EVIDENCE_INFO
>();
714 uint32 count
= certs
.count();
715 allocator
.free(result
[0].data()); // just a struct
716 certs
.destroy(allocator
); // certgroup contents
717 allocator
.free(result
[1].data()); // the CertGroup itself
718 for (uint32 n
= 0; n
< count
; n
++)
719 allocator
.free(evidence
[n
].StatusCodes
);
720 allocator
.free(result
[2].data()); // array of (flat) info structs
722 secdebug("trusteval", "unrecognized Apple TP evidence format");
723 // drop it -- better leak than kill
726 // unknown format -- blindly assume flat blobs
727 secdebug("trusteval", "destroying unknown TP evidence format");
728 for (uint32 n
= 0; n
< result
.count(); n
++)
730 allocator
.free(result
[n
].data());
734 allocator
.free (result
.Evidence
);
740 // Clear evaluation results unless state is initial (invalid)
742 void Trust::clearResults()
744 StLock
<Mutex
>_(mMutex
);
745 if (mResult
!= kSecTrustResultInvalid
) {
746 releaseTPEvidence(mTpResult
, mTP
.allocator());
747 mResult
= kSecTrustResultInvalid
;
753 // Build evidence information
755 void Trust::buildEvidence(CFArrayRef
&certChain
, TPEvidenceInfo
* &statusChain
)
757 StLock
<Mutex
>_(mMutex
);
758 if (mResult
== kSecTrustResultInvalid
)
759 MacOSError::throwMe(errSecTrustNotAvailable
);
760 certChain
= mEvidenceReturned
=
761 makeCFArray(convert
, mCertChain
);
762 if(mTpResult
.count() >= 3) {
763 statusChain
= mTpResult
[2].as
<TPEvidenceInfo
>();
772 // Return extended result dictionary
774 void Trust::extendedResult(CFDictionaryRef
&result
)
776 if (mResult
== kSecTrustResultInvalid
)
777 MacOSError::throwMe(errSecTrustNotAvailable
);
779 CFRetain(mExtendedResult
); // retain before handing out to caller
780 result
= mExtendedResult
;
785 // Return properties array (a CFDictionaryRef for each certificate in chain)
787 CFArrayRef
Trust::properties()
789 // Builds and returns an array which the caller must release.
790 StLock
<Mutex
>_(mMutex
);
791 CFMutableArrayRef properties
= CFArrayCreateMutable(NULL
, 0,
792 &kCFTypeArrayCallBacks
);
793 if (mResult
== kSecTrustResultInvalid
) // chain not built or evaluated
796 /* Get evidence pointer */
797 CSSM_TP_APPLE_EVIDENCE_INFO
*evidence
= NULL
;
798 uint32 evidenceChainLen
= 0;
799 if (mTpResult
.count() > 0) {
800 if (mTpResult
[0].form() == CSSM_EVIDENCE_FORM_APPLE_HEADER
) {
801 if (mTpResult
[0].as
<CSSM_TP_APPLE_EVIDENCE_HEADER
>()->Version
== CSSM_TP_APPLE_EVIDENCE_VERSION
802 && mTpResult
.count() == 3
803 && mTpResult
[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP
804 && mTpResult
[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO
) {
806 CertGroup
& certs
= *mTpResult
[1].as
<CertGroup
>();
807 evidence
= mTpResult
[2].as
<CSSM_TP_APPLE_EVIDENCE_INFO
>();
808 evidenceChainLen
= certs
.count();
813 // Walk the chain from leaf to anchor, building properties dictionaries
814 for (uint32 idx
=0; idx
< mCertChain
.size(); idx
++) {
815 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(NULL
, 0,
816 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
818 CFStringRef title
= NULL
;
819 mCertChain
[idx
]->inferLabel(false, &title
);
821 CFDictionarySetValue(dict
, (const void *)kSecPropertyTypeTitle
, (const void *)title
);
824 if (idx
== 0 && mTpReturn
!= errSecSuccess
) {
825 CFStringRef error
= SecCopyErrorMessageString(mTpReturn
, NULL
);
827 CFDictionarySetValue(dict
, (const void *)kSecPropertyTypeError
, (const void *)error
);
831 if (idx
< evidenceChainLen
) {
832 uint32 numCodes
= evidence
[idx
].NumStatusCodes
;
833 sint32 reason
= evidence
[idx
].StatusCodes
[numCodes
]; // stored at end of status codes array
835 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
837 CFDictionarySetValue(dict
, (const void *)kSecTrustRevocationReason
, (const void *)cfreason
);
842 CFArrayAppendValue(properties
, (const void *)dict
);
851 // Return dictionary of evaluation results
853 CFDictionaryRef
Trust::results()
855 // Builds and returns a dictionary which the caller must release.
856 StLock
<Mutex
>_(mMutex
);
857 CFMutableDictionaryRef results
= CFDictionaryCreateMutable(NULL
, 0,
858 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
860 // kSecTrustResultValue
861 CFNumberRef numValue
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &mResult
);
863 CFDictionarySetValue(results
, (const void *)kSecTrustResultValue
, (const void *)numValue
);
866 if (mResult
== kSecTrustResultInvalid
|| !mExtendedResult
)
867 return results
; // we have nothing more to add
869 // kSecTrustEvaluationDate
870 CFTypeRef evaluationDate
;
871 if (CFDictionaryGetValueIfPresent(mExtendedResult
, kSecTrustEvaluationDate
, &evaluationDate
))
872 CFDictionarySetValue(results
, (const void *)kSecTrustEvaluationDate
, (const void *)evaluationDate
);
874 // kSecTrustExtendedValidation, kSecTrustOrganizationName
875 CFTypeRef organizationName
;
876 if (CFDictionaryGetValueIfPresent(mExtendedResult
, kSecEVOrganizationName
, &organizationName
)) {
877 CFDictionarySetValue(results
, (const void *)kSecTrustOrganizationName
, (const void *)organizationName
);
878 CFDictionarySetValue(results
, (const void *)kSecTrustExtendedValidation
, (const void *)kCFBooleanTrue
);
881 // kSecTrustRevocationChecked, kSecTrustRevocationValidUntilDate
882 CFTypeRef expirationDate
;
883 if (CFDictionaryGetValueIfPresent(mExtendedResult
, kSecTrustExpirationDate
, &expirationDate
)) {
884 CFDictionarySetValue(results
, (const void *)kSecTrustRevocationValidUntilDate
, (const void *)expirationDate
);
885 CFDictionarySetValue(results
, (const void *)kSecTrustRevocationChecked
, (const void *)kCFBooleanTrue
);
893 //* ===========================================================================
894 //* We need a way to compare two CSSM_DL_DB_HANDLEs WITHOUT using a operator
896 //* ===========================================================================
898 bool Compare_CSSM_DL_DB_HANDLE(const CSSM_DL_DB_HANDLE
&h1
, const CSSM_DL_DB_HANDLE
&h2
)
900 return (h1
.DLHandle
== h2
.DLHandle
&& h1
.DBHandle
== h2
.DBHandle
);
906 // Given a DL_DB_HANDLE, locate the Keychain object (from the search list)
908 Keychain
Trust::keychainByDLDb(const CSSM_DL_DB_HANDLE
&handle
)
910 StLock
<Mutex
>_(mMutex
);
911 StorageManager::KeychainList
& list
= searchLibs();
912 for (StorageManager::KeychainList::const_iterator it
= list
.begin();
913 it
!= list
.end(); it
++)
918 if (Compare_CSSM_DL_DB_HANDLE((*it
)->database()->handle(), handle
))
925 if(mUsingTrustSettings
) {
927 if(Compare_CSSM_DL_DB_HANDLE(trustKeychains().rootStoreHandle(), handle
)) {
928 return trustKeychains().rootStore();
930 if(Compare_CSSM_DL_DB_HANDLE(trustKeychains().systemKcHandle(), handle
)) {
931 return trustKeychains().systemKc();
935 /* one of those is missing; proceed */
939 // could not find in search list - internal error
941 // we now throw an error here rather than assert and silently fail. That way our application won't crash...
942 MacOSError::throwMe(errSecInternal
);