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