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