]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/Trust.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / Trust.cpp
1 /*
2 * Copyright (c) 2002-2015 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 #include <security_cdsa_client/dlclient.h>
38
39
40 using namespace Security;
41 using namespace KeychainCore;
42
43 //
44 // Translate CFDataRef to CssmData. The output shares the input's buffer.
45 //
46 static inline CssmData cfData(CFDataRef data)
47 {
48 return CssmData(const_cast<UInt8 *>(CFDataGetBytePtr(data)),
49 CFDataGetLength(data));
50 }
51
52 //
53 // Convert a SecPointer to a CF object.
54 //
55 static SecCertificateRef
56 convert(const SecPointer<Certificate> &certificate)
57 {
58 return *certificate;
59 }
60
61 //
62 // For now, we use a global TrustStore
63 //
64 ModuleNexus<TrustStore> Trust::gStore;
65
66 #pragma mark -- TrustKeychains --
67
68 static const CSSM_DL_DB_HANDLE nullCSSMDLDBHandle = {0,};
69 //
70 // TrustKeychains maintains a global reference to standard system keychains,
71 // to avoid having them be opened anew for each Trust instance.
72 //
73 class TrustKeychains
74 {
75 public:
76 TrustKeychains();
77 ~TrustKeychains() {}
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; }
82
83 private:
84 DL* mRootStoreDL;
85 Db* mRootStoreDb;
86 Keychain* mRootStore;
87 CSSM_DL_DB_HANDLE mRootStoreHandle;
88 Keychain mSystem;
89 };
90
91 //
92 // Singleton maintaining open references to standard system keychains,
93 // to avoid having them be opened anew every time SecTrust is used.
94 //
95
96 static ModuleNexus<TrustKeychains> trustKeychains;
97 static ModuleNexus<RecursiveMutex> trustKeychainsMutex;
98
99 extern "C" bool GetServerMode();
100
101 TrustKeychains::TrustKeychains() :
102 mRootStoreHandle(nullCSSMDLDBHandle),
103 mSystem(globals().storageManager.make(ADMIN_CERT_STORE_PATH, false))
104 {
105 if (GetServerMode()) // in server mode? Don't make a keychain for the root store
106 {
107 mRootStoreDL = new DL(gGuidAppleFileDL),
108 mRootStoreDb = new Db(*mRootStoreDL, SYSTEM_ROOT_STORE_PATH),
109 (*mRootStoreDb)->activate();
110 mRootStoreHandle = (*mRootStoreDb)->handle();
111 }
112 else
113 {
114 mRootStore = new Keychain(globals().storageManager.make(SYSTEM_ROOT_STORE_PATH, false));
115 (*mRootStore)->database()->activate();
116 mRootStoreHandle = (*mRootStore)->database()->handle();
117 }
118 }
119
120 RecursiveMutex& SecTrustKeychainsGetMutex()
121 {
122 return trustKeychainsMutex();
123 }
124
125 #pragma mark -- Trust --
126 //
127 // Construct a Trust object with suitable defaults.
128 // Use setters for additional arguments before calling evaluate().
129 //
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)
135 {
136 if (!mPolicies) {
137 mPolicies.take(CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks));
138 }
139 }
140
141
142 //
143 // Clean up a Trust object
144 //
145 Trust::~Trust()
146 {
147 clearResults();
148 if (mSearchLibs) {
149 delete mSearchLibs;
150 }
151
152 mPolicies = NULL;
153 }
154
155
156 //
157 // Get searchLibs (a vector of Keychain objects);
158 // normally initialized to default search list
159 //
160 StorageManager::KeychainList& Trust::searchLibs(bool init)
161 {
162 if (!mSearchLibs) {
163 mSearchLibs = new StorageManager::KeychainList;
164 if (init) {
165 globals().storageManager.getSearchList(*mSearchLibs);
166 }
167 }
168 return *mSearchLibs;
169 }
170
171
172 //
173 // Set searchLibs to provided vector of Keychain objects
174 //
175 void Trust::searchLibs(StorageManager::KeychainList &libs)
176 {
177 searchLibs(false) = libs;
178 mSearchLibsSet = true;
179 }
180
181
182 //
183 // Retrieve the last TP evaluation result, if any
184 //
185 CSSM_TP_VERIFY_CONTEXT_RESULT_PTR Trust::cssmResult()
186 {
187 if (mResult == kSecTrustResultInvalid)
188 MacOSError::throwMe(errSecTrustNotAvailable);
189 return &mTpResult;
190 }
191
192
193 // SecCertificateRef -> CssmData
194 static
195 CssmData cfCertificateData(SecCertificateRef certificate)
196 {
197 return Certificate::required(certificate)->data();
198 }
199
200 // SecPolicyRef -> CssmField (CFDataRef/NULL or oid/value of a SecPolicy)
201 static
202 CssmField cfField(SecPolicyRef item)
203 {
204 SecPointer<Policy> policy = Policy::required(SecPolicyRef(item));
205 return CssmField(policy->oid(), policy->value());
206 }
207
208 // SecKeychain -> CssmDlDbHandle
209 #if 0
210 static
211 CSSM_DL_DB_HANDLE cfKeychain(SecKeychainRef ref)
212 {
213 Keychain keychain = KeychainImpl::required(ref);
214 return keychain->database()->handle();
215 }
216 #endif
217
218 #if !defined(NDEBUG)
219 void showCertSKID(const void *value, void *context);
220 #endif
221
222 //
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.
229 //
230 void Trust::evaluate(bool disableEV)
231 {
232 bool isEVCandidate=false;
233 // begin evaluation block with stack-based mutex
234 {
235 StLock<Mutex>_(mMutex);
236 // if we have evaluated before, release prior result
237 clearResults();
238
239 // determine whether the leaf certificate is an EV candidate
240 CFArrayRef allowedAnchors = NULL;
241 if (!disableEV) {
242 allowedAnchors = allowedEVRootsForLeafCertificate(mCerts);
243 isEVCandidate = (allowedAnchors != NULL);
244 }
245 CFArrayRef filteredCerts = NULL;
246 if (isEVCandidate) {
247 secdebug("evTrust", "Trust::evaluate() certificate is EV candidate");
248 filteredCerts = potentialEVChainWithCertificates(mCerts);
249 mCerts = filteredCerts;
250 } else {
251 secdebug("evTrust", "Trust::evaluate() performing standard evaluation");
252 if (mCerts) {
253 filteredCerts = CFArrayCreateMutableCopy(NULL, 0, mCerts);
254 }
255 if (mAnchors) {
256 allowedAnchors = CFArrayCreateMutableCopy(NULL, 0, mAnchors);
257 }
258 }
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;
263
264 if (allowedAnchors)
265 CFRelease(allowedAnchors);
266 if (filteredCerts)
267 CFRelease(filteredCerts);
268
269 if (mAllowedAnchors)
270 {
271 secdebug("trusteval", "Trust::evaluate: anchors: %ld", CFArrayGetCount(mAllowedAnchors));
272 #if !defined(NDEBUG)
273 CFArrayApplyFunction(mAllowedAnchors, CFRangeMake(0, CFArrayGetCount(mAllowedAnchors)), showCertSKID, NULL);
274 #endif
275 }
276
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;
281 }
282
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;
289
290 // build a TP_VERIFY_CONTEXT, a veritable nightmare of a data structure
291 TPBuildVerifyContext context(mAction);
292
293 /*
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.
297 */
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;
302 if (mActionData) {
303 context.actionData() = cfData(mActionData);
304 actionDataP = (CSSM_APPLE_TP_ACTION_DATA *)context.actionData().data();
305 }
306 else {
307 context.actionData() = localActionCData;
308 }
309
310 bool hasSSLPolicy = policySpecified(mPolicies, CSSMOID_APPLE_TP_SSL);
311 bool hasEAPPolicy = policySpecified(mPolicies, CSSMOID_APPLE_TP_EAP);
312
313 if (!mAnchors) {
314 // always check trust settings if caller did not provide explicit trust anchors
315 actionDataP->ActionFlags |= CSSM_TP_ACTION_TRUST_SETTINGS;
316 }
317
318 if (mNetworkPolicy == useNetworkDefault) {
319 if (hasSSLPolicy) {
320 // enable network cert fetch for SSL only: <rdar://7422356>
321 actionDataP->ActionFlags |= CSSM_TP_ACTION_FETCH_CERT_FROM_NET;
322 }
323 }
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);
328
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);
333 }
334
335 /*
336 * Policies (one at least, please).
337 * For revocation policies, see if any have been explicitly specified...
338 */
339 CFMutableArrayRef allPolicies = NULL;
340 uint32 numRevocationAdded = 0;
341 bool requirePerCert = (actionDataP->ActionFlags & CSSM_TP_ACTION_REQUIRE_REV_PER_CERT);
342
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.
348
349 allPolicies = convertRevocationPolicy(numRevocationAdded, context.allocator);
350 if (allPolicies) {
351 // caller has explicitly set the revocation policy they want to use
352 secdebug("evTrust", "Trust::evaluate() using explicit revocation policy (%d)",
353 numRevocationAdded);
354 if (numRevocationAdded == 0)
355 isEVCandidate = false;
356 }
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;
364 }
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);
370 }
371 else if(!(revocationPolicySpecified(mPolicies))) {
372 // none specified in mPolicies; try preferences
373 allPolicies = addPreferenceRevocationPolicies(!(hasSSLPolicy || hasEAPPolicy),
374 !(hasSSLPolicy || hasEAPPolicy), numRevocationAdded, context.allocator);
375 }
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);
382 }
383 orderRevocationPolicies(allPolicies);
384 CFToVector<CssmField, SecPolicyRef, cfField> policies(allPolicies);
385 #if 0
386 // error exit here if empty policies are not supported
387 if (policies.empty())
388 MacOSError::throwMe(CSSMERR_TP_INVALID_POLICY_IDENTIFIERS);
389 #endif
390 context.setPolicies(policies, policies);
391
392 // anchor certificates (if caller provides them, or if cert requires EV)
393 CFCopyRef<CFArrayRef> anchors(mAllowedAnchors);
394 CFToVector<CssmData, SecCertificateRef, cfCertificateData> roots(anchors);
395 if (!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!");
401 }
402 else {
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);
410 }
411
412 // dlDbList (keychain list)
413 vector<CSSM_DL_DB_HANDLE> dlDbList;
414 {
415 StLock<Mutex> _(SecTrustKeychainsGetMutex());
416 StorageManager::KeychainList& list = searchLibs();
417 for (StorageManager::KeychainList::const_iterator it = list.begin();
418 it != list.end(); it++)
419 {
420 try
421 {
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) {
428 CSSM_GUID guid = {};
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
434 }
435 }
436 }
437 // This DB is OK to search for intermediate certificates.
438 dlDbList.push_back(dldbHandle);
439 }
440 catch (...)
441 {
442 }
443 }
444 if(mUsingTrustSettings) {
445 /* Append system anchors for use with Trust Settings */
446 try {
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;
451 }
452 catch (...) {
453 // no root store or system keychain; don't use trust settings but continue
454 mUsingTrustSettings = false;
455 }
456 try {
457 CSSM_DL_DB_HANDLE systemKcHandle = trustKeychains().systemKcHandle();
458 if (systemKcHandle.DBHandle)
459 dlDbList.push_back(systemKcHandle);
460 }
461 catch(...) {
462 /* Oh well, at least we got the root store DB */
463 }
464 }
465 context.setDlDbList((uint32)dlDbList.size(), &dlDbList[0]);
466 }
467
468 // verification time
469 char timeString[15];
470 if (mVerifyTime) {
471 CssmUniformDate(static_cast<CFDateRef>(mVerifyTime)).convertTo(
472 timeString, sizeof(timeString));
473 context.time(timeString);
474 }
475
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);
481 }
482
483 // Go TP!
484 try {
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);
490 }
491 mResult = diagnoseOutcome();
492
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);
502 } else {
503 // unexpected evidence information. Can't use it
504 secdebug("trusteval", "unexpected evidence ignored");
505 }
506
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
511 if (etResult) {
512 CFRelease(etResult);
513 }
514 if (fullChain) {
515 CFRelease(fullChain);
516 }
517
518 if (allPolicies) {
519 /* clean up revocation policies we created implicitly */
520 if(numRevocationAdded) {
521 freeAddedRevocationPolicyData(allPolicies, numRevocationAdded, context.allocator);
522 }
523 CFRelease(allPolicies);
524 }
525
526 if (holdSearchList) {
527 delete holdSearchList;
528 holdSearchList = NULL;
529 }
530 } // end evaluation block with mutex; releases all temporary allocations in this scope
531
532
533 if (isEVCandidate && mResult == kSecTrustResultRecoverableTrustFailure &&
534 (mTpReturn == CSSMERR_TP_NOT_TRUSTED || isRevocationServerMetaError(mTpReturn))) {
535 // re-do the evaluation, this time disabling EV
536 evaluate(true);
537 }
538 }
539
540 // CSSM_RETURN values that map to kSecTrustResultRecoverableTrustFailure.
541 static const CSSM_RETURN recoverableErrors[] =
542 {
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,
574 };
575 #define NUM_RECOVERABLE_ERRORS (sizeof(recoverableErrors) / sizeof(CSSM_RETURN))
576
577 //
578 // Classify the TP outcome in terms of a SecTrustResultType
579 //
580 SecTrustResultType Trust::diagnoseOutcome()
581 {
582 StLock<Mutex>_(mMutex);
583
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)
588 {
589 const CertGroup &chain = *mTpResult[1].as<CertGroup>();
590 chainLength = chain.count();
591 }
592
593 switch (mTpReturn) {
594 case errSecSuccess: // peachy
595 if (mUsingTrustSettings)
596 {
597 if (chainLength)
598 {
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)
607 {
608 return kSecTrustResultProceed; // explicitly allowed
609 }
610 }
611 }
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;
617 default:
618 break;
619 }
620
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;
626 }
627 }
628 return kSecTrustResultOtherError; // unknown
629 }
630
631
632 //
633 // Assuming a good evidence chain, check user trust
634 // settings and set mResult accordingly.
635 //
636 void Trust::evaluateUserTrust(const CertGroup &chain,
637 const CSSM_TP_APPLE_EVIDENCE_INFO *infoList, CFCopyRef<CFArrayRef> anchors)
638 {
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());
652 if (cert == NULL) {
653 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
654 }
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,
660 info.index()));
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,
666 info.index()));
667 mCertChain[n] = Certificate::required(cert);
668 } else {
669 // unknown source; make a new Certificate for it
670 secdebug("trusteval", "evidence %lu from unknown source", (unsigned long)n);
671 mCertChain[n] =
672 new Certificate(chain.blobCerts()[n],
673 CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER);
674 }
675 }
676
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;
683 mResultIndex++) {
684 if (!mCertChain[mResultIndex]) {
685 assert(false);
686 continue;
687 }
688 mResult = store.find(mCertChain[mResultIndex], policy, searchLibs());
689 secdebug("trusteval", "trustResult=%d from cert %d", (int)mResult, (int)mResultIndex);
690 }
691 }
692
693
694 //
695 // Release TP evidence information.
696 // This information is severely under-defined by CSSM, so we proceed
697 // as follows:
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.
701 //
702 void Trust::releaseTPEvidence(TPVerifyResult &result, Allocator &allocator)
703 {
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) {
711 // proper format
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
721 } else {
722 secdebug("trusteval", "unrecognized Apple TP evidence format");
723 // drop it -- better leak than kill
724 }
725 } else {
726 // unknown format -- blindly assume flat blobs
727 secdebug("trusteval", "destroying unknown TP evidence format");
728 for (uint32 n = 0; n < result.count(); n++)
729 {
730 allocator.free(result[n].data());
731 }
732 }
733
734 allocator.free (result.Evidence);
735 }
736 }
737
738
739 //
740 // Clear evaluation results unless state is initial (invalid)
741 //
742 void Trust::clearResults()
743 {
744 StLock<Mutex>_(mMutex);
745 if (mResult != kSecTrustResultInvalid) {
746 releaseTPEvidence(mTpResult, mTP.allocator());
747 mResult = kSecTrustResultInvalid;
748 }
749 }
750
751
752 //
753 // Build evidence information
754 //
755 void Trust::buildEvidence(CFArrayRef &certChain, TPEvidenceInfo * &statusChain)
756 {
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>();
764 }
765 else {
766 statusChain = NULL;
767 }
768 }
769
770
771 //
772 // Return extended result dictionary
773 //
774 void Trust::extendedResult(CFDictionaryRef &result)
775 {
776 if (mResult == kSecTrustResultInvalid)
777 MacOSError::throwMe(errSecTrustNotAvailable);
778 if (mExtendedResult)
779 CFRetain(mExtendedResult); // retain before handing out to caller
780 result = mExtendedResult;
781 }
782
783
784 //
785 // Return properties array (a CFDictionaryRef for each certificate in chain)
786 //
787 CFArrayRef Trust::properties()
788 {
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
794 return properties;
795
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) {
805 // proper format
806 CertGroup& certs = *mTpResult[1].as<CertGroup>();
807 evidence = mTpResult[2].as<CSSM_TP_APPLE_EVIDENCE_INFO>();
808 evidenceChainLen = certs.count();
809 }
810 }
811 }
812
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);
817 if (dict) {
818 CFStringRef title = NULL;
819 mCertChain[idx]->inferLabel(false, &title);
820 if (title) {
821 CFDictionarySetValue(dict, (const void *)kSecPropertyTypeTitle, (const void *)title);
822 CFRelease(title);
823 }
824 if (idx == 0 && mTpReturn != errSecSuccess) {
825 CFStringRef error = SecCopyErrorMessageString(mTpReturn, NULL);
826 if (error) {
827 CFDictionarySetValue(dict, (const void *)kSecPropertyTypeError, (const void *)error);
828 CFRelease(error);
829 }
830 }
831 if (idx < evidenceChainLen) {
832 uint32 numCodes = evidence[idx].NumStatusCodes;
833 sint32 reason = evidence[idx].StatusCodes[numCodes]; // stored at end of status codes array
834 if (reason > 0) {
835 CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason);
836 if (cfreason) {
837 CFDictionarySetValue(dict, (const void *)kSecTrustRevocationReason, (const void *)cfreason);
838 CFRelease(cfreason);
839 }
840 }
841 }
842 CFArrayAppendValue(properties, (const void *)dict);
843 CFRelease(dict);
844 }
845 }
846
847 return properties;
848 }
849
850 //
851 // Return dictionary of evaluation results
852 //
853 CFDictionaryRef Trust::results()
854 {
855 // Builds and returns a dictionary which the caller must release.
856 StLock<Mutex>_(mMutex);
857 CFMutableDictionaryRef results = CFDictionaryCreateMutable(NULL, 0,
858 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
859
860 // kSecTrustResultValue
861 CFNumberRef numValue = CFNumberCreate(NULL, kCFNumberSInt32Type, &mResult);
862 if (numValue) {
863 CFDictionarySetValue(results, (const void *)kSecTrustResultValue, (const void *)numValue);
864 CFRelease(numValue);
865 }
866 if (mResult == kSecTrustResultInvalid || !mExtendedResult)
867 return results; // we have nothing more to add
868
869 // kSecTrustEvaluationDate
870 CFTypeRef evaluationDate;
871 if (CFDictionaryGetValueIfPresent(mExtendedResult, kSecTrustEvaluationDate, &evaluationDate))
872 CFDictionarySetValue(results, (const void *)kSecTrustEvaluationDate, (const void *)evaluationDate);
873
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);
879 }
880
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);
886 }
887
888 return results;
889 }
890
891
892
893 //* ===========================================================================
894 //* We need a way to compare two CSSM_DL_DB_HANDLEs WITHOUT using a operator
895 //* overload
896 //* ===========================================================================
897 static
898 bool Compare_CSSM_DL_DB_HANDLE(const CSSM_DL_DB_HANDLE &h1, const CSSM_DL_DB_HANDLE &h2)
899 {
900 return (h1.DLHandle == h2.DLHandle && h1.DBHandle == h2.DBHandle);
901 }
902
903
904
905 //
906 // Given a DL_DB_HANDLE, locate the Keychain object (from the search list)
907 //
908 Keychain Trust::keychainByDLDb(const CSSM_DL_DB_HANDLE &handle)
909 {
910 StLock<Mutex>_(mMutex);
911 StorageManager::KeychainList& list = searchLibs();
912 for (StorageManager::KeychainList::const_iterator it = list.begin();
913 it != list.end(); it++)
914 {
915 try
916 {
917
918 if (Compare_CSSM_DL_DB_HANDLE((*it)->database()->handle(), handle))
919 return *it;
920 }
921 catch (...)
922 {
923 }
924 }
925 if(mUsingTrustSettings) {
926 try {
927 if(Compare_CSSM_DL_DB_HANDLE(trustKeychains().rootStoreHandle(), handle)) {
928 return trustKeychains().rootStore();
929 }
930 if(Compare_CSSM_DL_DB_HANDLE(trustKeychains().systemKcHandle(), handle)) {
931 return trustKeychains().systemKc();
932 }
933 }
934 catch(...) {
935 /* one of those is missing; proceed */
936 }
937 }
938
939 // could not find in search list - internal error
940
941 // we now throw an error here rather than assert and silently fail. That way our application won't crash...
942 MacOSError::throwMe(errSecInternal);
943 }