]> git.saurik.com Git - apple/security.git/blob - libsecurity_keychain/lib/Trust.cpp
Security-55178.0.1.tar.gz
[apple/security.git] / libsecurity_keychain / lib / Trust.cpp
1 /*
2 * Copyright (c) 2002-2010 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 //
25 // Trust.cpp
26 //
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
38
39 using namespace Security;
40 using namespace KeychainCore;
41
42 //
43 // Translate CFDataRef to CssmData. The output shares the input's buffer.
44 //
45 static inline CssmData cfData(CFDataRef data)
46 {
47 return CssmData(const_cast<UInt8 *>(CFDataGetBytePtr(data)),
48 CFDataGetLength(data));
49 }
50
51 //
52 // Convert a SecPointer to a CF object.
53 //
54 static SecCertificateRef
55 convert(const SecPointer<Certificate> &certificate)
56 {
57 return *certificate;
58 }
59
60 //
61 // For now, we use a global TrustStore
62 //
63 ModuleNexus<TrustStore> Trust::gStore;
64
65 #pragma mark -- TrustKeychains --
66
67 static const CSSM_DL_DB_HANDLE nullCSSMDLDBHandle = {0,};
68 //
69 // TrustKeychains maintains a global reference to standard system keychains,
70 // to avoid having them be opened anew for each Trust instance.
71 //
72 class TrustKeychains
73 {
74 public:
75 TrustKeychains();
76 ~TrustKeychains() {}
77 CSSM_DL_DB_HANDLE rootStoreHandle() { return mRootStore ? mRootStore->database()->handle() : nullCSSMDLDBHandle; }
78 CSSM_DL_DB_HANDLE systemKcHandle() { return mSystem ? mSystem->database()->handle() : nullCSSMDLDBHandle; }
79 Keychain &rootStore() { return mRootStore; }
80 Keychain &systemKc() { return mSystem; }
81 private:
82 Keychain mRootStore;
83 Keychain mSystem;
84 };
85
86 //
87 // Singleton maintaining open references to standard system keychains,
88 // to avoid having them be opened anew every time SecTrust is used.
89 //
90
91 static ModuleNexus<TrustKeychains> trustKeychains;
92 static ModuleNexus<RecursiveMutex> trustKeychainsMutex;
93
94 TrustKeychains::TrustKeychains() :
95 mRootStore(globals().storageManager.make(SYSTEM_ROOT_STORE_PATH, false)),
96 mSystem(globals().storageManager.make(ADMIN_CERT_STORE_PATH, false))
97 {
98 }
99
100 RecursiveMutex& SecTrustKeychainsGetMutex()
101 {
102 return trustKeychainsMutex();
103 }
104
105 #pragma mark -- Trust --
106 //
107 // Construct a Trust object with suitable defaults.
108 // Use setters for additional arguments before calling evaluate().
109 //
110 Trust::Trust(CFTypeRef certificates, CFTypeRef policies)
111 : mTP(gGuidAppleX509TP), mAction(CSSM_TP_ACTION_DEFAULT),
112 mCerts(cfArrayize(certificates)), mPolicies(cfArrayize(policies)),
113 mResult(kSecTrustResultInvalid), mUsingTrustSettings(false),
114 mAnchorPolicy(useAnchorsDefault), mSearchLibsSet(false),
115 mSearchLibs(NULL), mMutex(Mutex::recursive)
116 {
117 }
118
119
120 //
121 // Clean up a Trust object
122 //
123 Trust::~Trust()
124 {
125 clearResults();
126 if (mSearchLibs) {
127 delete mSearchLibs;
128 }
129 }
130
131
132 //
133 // Get searchLibs (a vector of Keychain objects);
134 // normally initialized to default search list
135 //
136 StorageManager::KeychainList& Trust::searchLibs(bool init)
137 {
138 if (!mSearchLibs) {
139 mSearchLibs = new StorageManager::KeychainList;
140 if (init) {
141 globals().storageManager.getSearchList(*mSearchLibs);
142 }
143 }
144 return *mSearchLibs;
145 }
146
147
148 //
149 // Set searchLibs to provided vector of Keychain objects
150 //
151 void Trust::searchLibs(StorageManager::KeychainList &libs)
152 {
153 searchLibs(false) = libs;
154 mSearchLibsSet = true;
155 }
156
157
158 //
159 // Retrieve the last TP evaluation result, if any
160 //
161 CSSM_TP_VERIFY_CONTEXT_RESULT_PTR Trust::cssmResult()
162 {
163 if (mResult == kSecTrustResultInvalid)
164 MacOSError::throwMe(errSecTrustNotAvailable);
165 return &mTpResult;
166 }
167
168
169 // SecCertificateRef -> CssmData
170 CssmData cfCertificateData(SecCertificateRef certificate)
171 {
172 return Certificate::required(certificate)->data();
173 }
174
175 // SecPolicyRef -> CssmField (CFDataRef/NULL or oid/value of a SecPolicy)
176 CssmField cfField(SecPolicyRef item)
177 {
178 SecPointer<Policy> policy = Policy::required(SecPolicyRef(item));
179 return CssmField(policy->oid(), policy->value());
180 }
181
182 // SecKeychain -> CssmDlDbHandle
183 CSSM_DL_DB_HANDLE cfKeychain(SecKeychainRef ref)
184 {
185 Keychain keychain = KeychainImpl::required(ref);
186 return keychain->database()->handle();
187 }
188
189 #if !defined(NDEBUG)
190 void showCertSKID(const void *value, void *context);
191 #endif
192
193 //
194 // Here's the big "E" - evaluation.
195 // We build most of the CSSM-layer input structures dynamically right here;
196 // they will auto-destruct when we're done. The output structures are kept
197 // around (in our data members) for later analysis.
198 // Note that evaluate() can be called repeatedly, so we must be careful to
199 // dispose of prior results.
200 //
201 void Trust::evaluate(bool disableEV)
202 {
203 bool isEVCandidate=false;
204 // begin evaluation block with stack-based mutex
205 {
206 StLock<Mutex>_(mMutex);
207 // if we have evaluated before, release prior result
208 clearResults();
209
210 // determine whether the leaf certificate is an EV candidate
211 CFArrayRef allowedAnchors = allowedEVRootsForLeafCertificate(mCerts);
212 CFArrayRef filteredCerts = NULL;
213 isEVCandidate = (allowedAnchors && !disableEV) ? true : false;
214 if (isEVCandidate) {
215 secdebug("evTrust", "Trust::evaluate() certificate is EV candidate");
216 filteredCerts = potentialEVChainWithCertificates(mCerts);
217 mCerts = filteredCerts;
218 } else {
219 secdebug("evTrust", "Trust::evaluate() performing standard evaluation");
220 if (mCerts) {
221 filteredCerts = CFArrayCreateMutableCopy(NULL, 0, mCerts);
222 }
223 if (mAnchors) {
224 allowedAnchors = CFArrayCreateMutableCopy(NULL, 0, mAnchors);
225 }
226 }
227 // retain these certs as long as we potentially could have results involving them
228 // (note that assignment to a CFRef type performs an implicit retain)
229 mAllowedAnchors = allowedAnchors;
230 mFilteredCerts = filteredCerts;
231
232 if (allowedAnchors)
233 CFRelease(allowedAnchors);
234 if (filteredCerts)
235 CFRelease(filteredCerts);
236
237 if (mAllowedAnchors)
238 {
239 secdebug("trusteval", "Trust::evaluate: anchors: %ld", CFArrayGetCount(mAllowedAnchors));
240 #if !defined(NDEBUG)
241 CFArrayApplyFunction(mAllowedAnchors, CFRangeMake(0, CFArrayGetCount(mAllowedAnchors)), showCertSKID, NULL);
242 #endif
243 }
244
245 // set default search list from user's default, if caller did not explicitly supply it
246 if(!mSearchLibsSet) {
247 globals().storageManager.getSearchList(searchLibs());
248 mSearchLibsSet = true;
249 }
250
251 // build the target cert group
252 CFToVector<CssmData, SecCertificateRef, cfCertificateData> subjects(mFilteredCerts);
253 CertGroup subjectCertGroup(CSSM_CERT_X_509v3,
254 CSSM_CERT_ENCODING_BER, CSSM_CERTGROUP_DATA);
255 subjectCertGroup.count() = subjects;
256 subjectCertGroup.blobCerts() = subjects;
257
258 // build a TP_VERIFY_CONTEXT, a veritable nightmare of a data structure
259 TPBuildVerifyContext context(mAction);
260
261 /*
262 * Guarantee *some* action data...
263 * NOTE this only works with the local X509 TP. When this module can deal
264 * with other TPs, this must be revisited.
265 */
266 CSSM_APPLE_TP_ACTION_DATA localActionData;
267 memset(&localActionData, 0, sizeof(localActionData));
268 CssmData localActionCData((uint8 *)&localActionData, sizeof(localActionData));
269 CSSM_APPLE_TP_ACTION_DATA *actionDataP = &localActionData;
270 if (mActionData) {
271 context.actionData() = cfData(mActionData);
272 actionDataP = (CSSM_APPLE_TP_ACTION_DATA *)context.actionData().data();
273 }
274 else {
275 context.actionData() = localActionCData;
276 }
277
278 if (!mAnchors) {
279 // always check trust settings if caller did not provide explicit trust anchors
280 actionDataP->ActionFlags |= CSSM_TP_ACTION_TRUST_SETTINGS;
281 }
282
283 if (policySpecified(mPolicies, CSSMOID_APPLE_TP_SSL)) {
284 // enable network cert fetch for SSL only: <rdar://7422356>
285 actionDataP->ActionFlags |= CSSM_TP_ACTION_FETCH_CERT_FROM_NET;
286 }
287
288 /*
289 * Policies (one at least, please).
290 * For revocation policies, see if any have been explicitly specified...
291 */
292 CFMutableArrayRef allPolicies = NULL;
293 uint32 numSpecAdded = 0;
294 uint32 numPrefAdded = 0;
295 bool requirePerCert = (actionDataP->ActionFlags & CSSM_TP_ACTION_REQUIRE_REV_PER_CERT);
296 if (isEVCandidate || requirePerCert) {
297 // force revocation checking for this evaluation
298 secdebug("evTrust", "Trust::evaluate() forcing OCSP/CRL revocation checking");
299 allPolicies = forceRevocationPolicies(numPrefAdded, context.allocator, requirePerCert);
300 }
301 else if (mAnchors && (CFArrayGetCount(mAnchors)==0) && (searchLibs().size()==0)) {
302 // caller explicitly provided empty anchors and no keychain list;
303 // override global revocation check setting for this evaluation
304 allPolicies = NULL; // use only mPolicies
305 }
306 else if(!(revocationPolicySpecified(mPolicies))) {
307 /*
308 * None specified in mPolicies; see if any specified via SPI.
309 */
310 allPolicies = addSpecifiedRevocationPolicies(numSpecAdded, context.allocator);
311 if(allPolicies == NULL) {
312 /*
313 * None there; try preferences.
314 */
315 allPolicies = Trust::addPreferenceRevocationPolicies(numPrefAdded,
316 context.allocator);
317 }
318
319 }
320 if(allPolicies == NULL) {
321 allPolicies = CFMutableArrayRef(CFArrayRef(mPolicies));
322 }
323 orderRevocationPolicies(allPolicies);
324 CFToVector<CssmField, SecPolicyRef, cfField> policies(allPolicies);
325 if (policies.empty())
326 MacOSError::throwMe(CSSMERR_TP_INVALID_POLICY_IDENTIFIERS);
327 context.setPolicies(policies, policies);
328
329 // anchor certificates (if caller provides them, or if cert requires EV)
330 CFCopyRef<CFArrayRef> anchors(mAllowedAnchors);
331 CFToVector<CssmData, SecCertificateRef, cfCertificateData> roots(anchors);
332 if (!anchors) {
333 // no anchor certificates were provided;
334 // built-in anchors will be trusted unless explicitly disabled.
335 mUsingTrustSettings = (mAnchorPolicy < useAnchorsOnly);
336 secdebug("userTrust", "Trust::evaluate() %s",
337 (mUsingTrustSettings) ? "using UserTrust" : "has no trusted anchors!");
338 }
339 else {
340 // anchor certificates were provided;
341 // built-in anchors will NOT also be trusted unless explicitly enabled.
342 mUsingTrustSettings = (mAnchorPolicy == useAnchorsAndBuiltIns);
343 secdebug("userTrust", "Trust::evaluate() using %s %s anchors",
344 (mUsingTrustSettings) ? "UserTrust AND" : "only",
345 (isEVCandidate) ? "EV" : "caller");
346 context.anchors(roots, roots);
347 }
348
349 // dlDbList (keychain list)
350 vector<CSSM_DL_DB_HANDLE> dlDbList;
351 {
352 StLock<Mutex> _(SecTrustKeychainsGetMutex());
353 StorageManager::KeychainList& list = searchLibs();
354 for (StorageManager::KeychainList::const_iterator it = list.begin();
355 it != list.end(); it++)
356 {
357 try
358 {
359 // For the purpose of looking up intermediate certificates to establish trust,
360 // do not include the network-based LDAP or DotMac pseudo-keychains. (The only
361 // time the network should be consulted for certificates is if there is an AIA
362 // extension with a specific URL, which will be handled by the TP code.)
363 CSSM_DL_DB_HANDLE dldbHandle = (*it)->database()->handle();
364 if (dldbHandle.DLHandle) {
365 CSSM_GUID guid = {};
366 CSSM_RETURN crtn = CSSM_GetModuleGUIDFromHandle(dldbHandle.DLHandle, &guid);
367 if (crtn == CSSM_OK) {
368 if ((memcmp(&guid, &gGuidAppleLDAPDL, sizeof(CSSM_GUID))==0) ||
369 (memcmp(&guid, &gGuidAppleDotMacDL, sizeof(CSSM_GUID))==0)) {
370 continue; // don't add to dlDbList
371 }
372 }
373 }
374 // This DB is OK to search for intermediate certificates.
375 dlDbList.push_back(dldbHandle);
376 }
377 catch (...)
378 {
379 }
380 }
381 if(mUsingTrustSettings) {
382 /* Append system anchors for use with Trust Settings */
383 try {
384 CSSM_DL_DB_HANDLE rootStoreHandle = trustKeychains().rootStoreHandle();
385 if (rootStoreHandle.DBHandle)
386 dlDbList.push_back(rootStoreHandle);
387 actionDataP->ActionFlags |= CSSM_TP_ACTION_TRUST_SETTINGS;
388 }
389 catch (...) {
390 // no root store or system keychain; don't use trust settings but continue
391 mUsingTrustSettings = false;
392 }
393 try {
394 CSSM_DL_DB_HANDLE systemKcHandle = trustKeychains().systemKcHandle();
395 if (systemKcHandle.DBHandle)
396 dlDbList.push_back(systemKcHandle);
397 }
398 catch(...) {
399 /* Oh well, at least we got the root store DB */
400 }
401 }
402 context.setDlDbList(dlDbList.size(), &dlDbList[0]);
403 }
404
405 // verification time
406 char timeString[15];
407 if (mVerifyTime) {
408 CssmUniformDate(static_cast<CFDateRef>(mVerifyTime)).convertTo(
409 timeString, sizeof(timeString));
410 context.time(timeString);
411 }
412
413 // to avoid keychain open/close thrashing, hold a copy of the search list
414 StorageManager::KeychainList *holdSearchList = NULL;
415 if (searchLibs().size() > 0) {
416 holdSearchList = new StorageManager::KeychainList;
417 globals().storageManager.getSearchList(*holdSearchList);
418 }
419
420 // Go TP!
421 try {
422 mTP->certGroupVerify(subjectCertGroup, context, &mTpResult);
423 mTpReturn = noErr;
424 } catch (CommonError &err) {
425 mTpReturn = err.osStatus();
426 secdebug("trusteval", "certGroupVerify exception: %d", (int)mTpReturn);
427 }
428 mResult = diagnoseOutcome();
429
430 // see if we can use the evidence
431 if (mTpResult.count() > 0
432 && mTpResult[0].form() == CSSM_EVIDENCE_FORM_APPLE_HEADER
433 && mTpResult[0].as<CSSM_TP_APPLE_EVIDENCE_HEADER>()->Version == CSSM_TP_APPLE_EVIDENCE_VERSION
434 && mTpResult.count() == 3
435 && mTpResult[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP
436 && mTpResult[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO) {
437 evaluateUserTrust(*mTpResult[1].as<CertGroup>(),
438 mTpResult[2].as<CSSM_TP_APPLE_EVIDENCE_INFO>(), anchors);
439 } else {
440 // unexpected evidence information. Can't use it
441 secdebug("trusteval", "unexpected evidence ignored");
442 }
443
444 /* do post-processing for the evaluated certificate chain */
445 CFArrayRef fullChain = makeCFArray(convert, mCertChain);
446 CFDictionaryRef etResult = extendedTrustResults(fullChain, mResult, mTpReturn, isEVCandidate);
447 mExtendedResult = etResult; // assignment to CFRef type is an implicit retain
448 if (etResult) {
449 CFRelease(etResult);
450 }
451 if (fullChain) {
452 CFRelease(fullChain);
453 }
454
455 /* Clean up Policies we created implicitly */
456 if(numSpecAdded) {
457 freeSpecifiedRevocationPolicies(allPolicies, numSpecAdded, context.allocator);
458 }
459 if(numPrefAdded) {
460 Trust::freePreferenceRevocationPolicies(allPolicies, numPrefAdded, context.allocator);
461 }
462
463 if (holdSearchList) {
464 delete holdSearchList;
465 holdSearchList = NULL;
466 }
467 } // end evaluation block with mutex; releases all temporary allocations in this scope
468
469
470 if (isEVCandidate && mResult == kSecTrustResultRecoverableTrustFailure &&
471 isRevocationServerMetaError(mTpReturn)) {
472 // re-do the evaluation, this time disabling EV
473 evaluate(true);
474 }
475 }
476
477 // CSSM_RETURN values that map to kSecTrustResultRecoverableTrustFailure.
478 static const CSSM_RETURN recoverableErrors[] =
479 {
480 CSSMERR_TP_INVALID_ANCHOR_CERT,
481 CSSMERR_TP_NOT_TRUSTED,
482 CSSMERR_TP_VERIFICATION_FAILURE,
483 CSSMERR_TP_VERIFY_ACTION_FAILED,
484 CSSMERR_TP_INVALID_CERTIFICATE,
485 CSSMERR_TP_INVALID_REQUEST_INPUTS,
486 CSSMERR_TP_CERT_EXPIRED,
487 CSSMERR_TP_CERT_NOT_VALID_YET,
488 CSSMERR_TP_CERTIFICATE_CANT_OPERATE,
489 CSSMERR_TP_INVALID_CERT_AUTHORITY,
490 CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK,
491 CSSMERR_APPLETP_HOSTNAME_MISMATCH,
492 CSSMERR_TP_VERIFY_ACTION_FAILED,
493 CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND,
494 CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS,
495 CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE,
496 CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH,
497 CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS,
498 CSSMERR_APPLETP_CS_BAD_PATH_LENGTH,
499 CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE,
500 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE,
501 CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT,
502 CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH,
503 CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN,
504 CSSMERR_APPLETP_CRL_NOT_FOUND,
505 CSSMERR_APPLETP_CRL_SERVER_DOWN,
506 CSSMERR_APPLETP_CRL_NOT_VALID_YET,
507 CSSMERR_APPLETP_OCSP_UNAVAILABLE,
508 CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK,
509 CSSMERR_APPLETP_NETWORK_FAILURE,
510 CSSMERR_APPLETP_OCSP_RESP_TRY_LATER,
511 };
512 #define NUM_RECOVERABLE_ERRORS (sizeof(recoverableErrors) / sizeof(CSSM_RETURN))
513
514 //
515 // Classify the TP outcome in terms of a SecTrustResultType
516 //
517 SecTrustResultType Trust::diagnoseOutcome()
518 {
519 StLock<Mutex>_(mMutex);
520
521 uint32 chainLength = 0;
522 if (mTpResult.count() == 3 &&
523 mTpResult[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP &&
524 mTpResult[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO)
525 {
526 const CertGroup &chain = *mTpResult[1].as<CertGroup>();
527 chainLength = chain.count();
528 }
529
530 switch (mTpReturn) {
531 case noErr: // peachy
532 if (mUsingTrustSettings)
533 {
534 if (chainLength)
535 {
536 const CSSM_TP_APPLE_EVIDENCE_INFO *infoList = mTpResult[2].as<CSSM_TP_APPLE_EVIDENCE_INFO>();
537 const TPEvidenceInfo &info = TPEvidenceInfo::overlay(infoList[chainLength-1]);
538 const CSSM_TP_APPLE_CERT_STATUS resultCertStatus = info.status();
539 bool hasUserDomainTrust = ((resultCertStatus & CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST) &&
540 (resultCertStatus & CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_USER));
541 bool hasAdminDomainTrust = ((resultCertStatus & CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST) &&
542 (resultCertStatus & CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_ADMIN));
543 if (hasUserDomainTrust || hasAdminDomainTrust)
544 {
545 return kSecTrustResultProceed; // explicitly allowed
546 }
547 }
548 }
549 return kSecTrustResultUnspecified; // cert evaluates OK
550 case CSSMERR_TP_INVALID_CERTIFICATE: // bad certificate
551 return kSecTrustResultFatalTrustFailure;
552 case CSSMERR_APPLETP_TRUST_SETTING_DENY: // authoritative denial
553 return kSecTrustResultDeny;
554 default:
555 break;
556 }
557
558 // a known list of returns maps to kSecTrustResultRecoverableTrustFailure
559 const CSSM_RETURN *errp=recoverableErrors;
560 for(unsigned dex=0; dex<NUM_RECOVERABLE_ERRORS; dex++, errp++) {
561 if(*errp == mTpReturn) {
562 return kSecTrustResultRecoverableTrustFailure;
563 }
564 }
565 return kSecTrustResultOtherError; // unknown
566 }
567
568
569 //
570 // Assuming a good evidence chain, check user trust
571 // settings and set mResult accordingly.
572 //
573 void Trust::evaluateUserTrust(const CertGroup &chain,
574 const CSSM_TP_APPLE_EVIDENCE_INFO *infoList, CFCopyRef<CFArrayRef> anchors)
575 {
576 StLock<Mutex>_(mMutex);
577 // extract cert chain as Certificate objects
578 mCertChain.resize(chain.count());
579 for (uint32 n = 0; n < mCertChain.size(); n++) {
580 const TPEvidenceInfo &info = TPEvidenceInfo::overlay(infoList[n]);
581 if (info.recordId()) {
582 Keychain keychain = keychainByDLDb(info.DlDbHandle);
583 DbUniqueRecord uniqueId(keychain->database()->newDbUniqueRecord());
584 secdebug("trusteval", "evidence %lu from keychain \"%s\"", (unsigned long)n, keychain->name());
585 *static_cast<CSSM_DB_UNIQUE_RECORD_PTR *>(uniqueId) = info.UniqueRecord;
586 uniqueId->activate(); // transfers ownership
587 Item ii = keychain->item(CSSM_DL_DB_RECORD_X509_CERTIFICATE, uniqueId);
588 Certificate* cert = dynamic_cast<Certificate*>(ii.get());
589 if (cert == NULL) {
590 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
591 }
592 mCertChain[n] = cert;
593 } else if (info.status(CSSM_CERT_STATUS_IS_IN_INPUT_CERTS)) {
594 secdebug("trusteval", "evidence %lu from input cert %lu", (unsigned long)n, (unsigned long)info.index());
595 assert(info.index() < uint32(CFArrayGetCount(mCerts)));
596 SecCertificateRef cert = SecCertificateRef(CFArrayGetValueAtIndex(mCerts,
597 info.index()));
598 mCertChain[n] = Certificate::required(cert);
599 } else if (info.status(CSSM_CERT_STATUS_IS_IN_ANCHORS)) {
600 secdebug("trusteval", "evidence %lu from anchor cert %lu", (unsigned long)n, (unsigned long)info.index());
601 assert(info.index() < uint32(CFArrayGetCount(anchors)));
602 SecCertificateRef cert = SecCertificateRef(CFArrayGetValueAtIndex(anchors,
603 info.index()));
604 mCertChain[n] = Certificate::required(cert);
605 } else {
606 // unknown source; make a new Certificate for it
607 secdebug("trusteval", "evidence %lu from unknown source", (unsigned long)n);
608 mCertChain[n] =
609 new Certificate(chain.blobCerts()[n],
610 CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER);
611 }
612 }
613
614 // now walk the chain, leaf-to-root, checking for user settings
615 TrustStore &store = gStore();
616 SecPointer<Policy> policy =
617 Policy::required(SecPolicyRef(CFArrayGetValueAtIndex(mPolicies, 0)));
618 for (mResultIndex = 0;
619 mResult == kSecTrustResultUnspecified && mResultIndex < mCertChain.size();
620 mResultIndex++) {
621 if (!mCertChain[mResultIndex]) {
622 assert(false);
623 continue;
624 }
625 mResult = store.find(mCertChain[mResultIndex], policy, searchLibs());
626 secdebug("trusteval", "trustResult=%lu from cert %lu", mResult, (unsigned long)mResultIndex);
627 }
628 }
629
630
631 //
632 // Release TP evidence information.
633 // This information is severely under-defined by CSSM, so we proceed
634 // as follows:
635 // (a) If the evidence matches an Apple-defined pattern, use specific
636 // knowledge of that format.
637 // (b) Otherwise, assume that the void * are flat blocks of memory.
638 //
639 void Trust::releaseTPEvidence(TPVerifyResult &result, Allocator &allocator)
640 {
641 if (result.count() > 0) { // something to do
642 if (result[0].form() == CSSM_EVIDENCE_FORM_APPLE_HEADER) {
643 // Apple defined evidence form -- use intimate knowledge
644 if (result[0].as<CSSM_TP_APPLE_EVIDENCE_HEADER>()->Version == CSSM_TP_APPLE_EVIDENCE_VERSION
645 && result.count() == 3
646 && result[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP
647 && result[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO) {
648 // proper format
649 CertGroup& certs = *result[1].as<CertGroup>();
650 CSSM_TP_APPLE_EVIDENCE_INFO *evidence = result[2].as<CSSM_TP_APPLE_EVIDENCE_INFO>();
651 uint32 count = certs.count();
652 allocator.free(result[0].data()); // just a struct
653 certs.destroy(allocator); // certgroup contents
654 allocator.free(result[1].data()); // the CertGroup itself
655 for (uint32 n = 0; n < count; n++)
656 allocator.free(evidence[n].StatusCodes);
657 allocator.free(result[2].data()); // array of (flat) info structs
658 } else {
659 secdebug("trusteval", "unrecognized Apple TP evidence format");
660 // drop it -- better leak than kill
661 }
662 } else {
663 // unknown format -- blindly assume flat blobs
664 secdebug("trusteval", "destroying unknown TP evidence format");
665 for (uint32 n = 0; n < result.count(); n++)
666 {
667 allocator.free(result[n].data());
668 }
669 }
670
671 allocator.free (result.Evidence);
672 }
673 }
674
675
676 //
677 // Clear evaluation results unless state is initial (invalid)
678 //
679 void Trust::clearResults()
680 {
681 StLock<Mutex>_(mMutex);
682 if (mResult != kSecTrustResultInvalid) {
683 releaseTPEvidence(mTpResult, mTP.allocator());
684 mResult = kSecTrustResultInvalid;
685 }
686 }
687
688
689 //
690 // Build evidence information
691 //
692 void Trust::buildEvidence(CFArrayRef &certChain, TPEvidenceInfo * &statusChain)
693 {
694 StLock<Mutex>_(mMutex);
695 if (mResult == kSecTrustResultInvalid)
696 MacOSError::throwMe(errSecTrustNotAvailable);
697 certChain = mEvidenceReturned =
698 makeCFArray(convert, mCertChain);
699 if(mTpResult.count() >= 3) {
700 statusChain = mTpResult[2].as<TPEvidenceInfo>();
701 }
702 else {
703 statusChain = NULL;
704 }
705 }
706
707
708 //
709 // Return extended result dictionary
710 //
711 void Trust::extendedResult(CFDictionaryRef &result)
712 {
713 if (mResult == kSecTrustResultInvalid)
714 MacOSError::throwMe(errSecTrustNotAvailable);
715 if (mExtendedResult)
716 CFRetain(mExtendedResult); // retain before handing out to caller
717 result = mExtendedResult;
718 }
719
720
721 //
722 // Return properties array (a CFDictionaryRef for each certificate in chain)
723 //
724 CFArrayRef Trust::properties()
725 {
726 // Builds and returns an array which the caller must release.
727 StLock<Mutex>_(mMutex);
728 CFMutableArrayRef properties = CFArrayCreateMutable(NULL, 0,
729 &kCFTypeArrayCallBacks);
730 if (mResult == kSecTrustResultInvalid) // chain not built or evaluated
731 return properties;
732
733 // Walk the chain from leaf to anchor, building properties dictionaries
734 for (uint32 idx=0; idx < mCertChain.size(); idx++) {
735 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0,
736 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
737 if (dict) {
738 CFStringRef title = NULL;
739 mCertChain[idx]->inferLabel(false, &title);
740 if (title) {
741 CFDictionarySetValue(dict, (const void *)kSecPropertyTypeTitle, (const void *)title);
742 CFRelease(title);
743 }
744 if (idx == 0 && mTpReturn != noErr) {
745 CFStringRef error = SecCopyErrorMessageString(mTpReturn, NULL);
746 if (error) {
747 CFDictionarySetValue(dict, (const void *)kSecPropertyTypeError, (const void *)error);
748 CFRelease(error);
749 }
750 }
751 CFArrayAppendValue(properties, (const void *)dict);
752 CFRelease(dict);
753 }
754 }
755
756 return properties;
757 }
758
759
760
761 //* ===========================================================================
762 //* We need a way to compare two CSSM_DL_DB_HANDLEs WITHOUT using a operator
763 //* overload
764 //* ===========================================================================
765 bool Compare_CSSM_DL_DB_HANDLE(const CSSM_DL_DB_HANDLE &h1, const CSSM_DL_DB_HANDLE &h2)
766 {
767 return (h1.DLHandle == h2.DLHandle && h1.DBHandle == h2.DBHandle);
768 }
769
770
771
772 //
773 // Given a DL_DB_HANDLE, locate the Keychain object (from the search list)
774 //
775 Keychain Trust::keychainByDLDb(const CSSM_DL_DB_HANDLE &handle)
776 {
777 StLock<Mutex>_(mMutex);
778 StorageManager::KeychainList& list = searchLibs();
779 for (StorageManager::KeychainList::const_iterator it = list.begin();
780 it != list.end(); it++)
781 {
782 try
783 {
784
785 if (Compare_CSSM_DL_DB_HANDLE((*it)->database()->handle(), handle))
786 return *it;
787 }
788 catch (...)
789 {
790 }
791 }
792 if(mUsingTrustSettings) {
793 try {
794 if(Compare_CSSM_DL_DB_HANDLE(trustKeychains().rootStoreHandle(), handle)) {
795 return trustKeychains().rootStore();
796 }
797 if(Compare_CSSM_DL_DB_HANDLE(trustKeychains().systemKcHandle(), handle)) {
798 return trustKeychains().systemKc();
799 }
800 }
801 catch(...) {
802 /* one of those is missing; proceed */
803 }
804 }
805
806 // could not find in search list - internal error
807
808 // we now throw an error here rather than assert and silently fail. That way our application won't crash...
809 MacOSError::throwMe(errSecInternal);
810 }