]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_x509_tp/lib/TPCertInfo.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_apple_x509_tp / lib / TPCertInfo.cpp
1 /*
2 * Copyright (c) 2000-2015 Apple Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19 /*
20 * TPCertInfo.cpp - TP's private certificate info classes
21 */
22
23 #include "TPCertInfo.h"
24 #include "tpdebugging.h"
25 #include "tpTime.h"
26 #include "certGroupUtils.h"
27 #include "TPDatabase.h"
28 #include "TPNetwork.h"
29 #include <Security/cssmapi.h>
30 #include <Security/x509defs.h>
31 #include <Security/oidscert.h>
32 #include <Security/oidsalg.h>
33 #include <string.h> /* for memcmp */
34 #include <security_utilities/threading.h> /* for Mutex */
35 #include <security_utilities/globalizer.h>
36 #include <security_utilities/debugging.h>
37 #include <security_cdsa_utilities/cssmerrors.h>
38 #include <Security/cssmapple.h>
39 #include <Security/SecCertificate.h>
40 #include <Security/SecImportExport.h>
41 #include <Security/SecTrustSettingsPriv.h>
42
43 #define tpTimeDbg(args...) secinfo("tpTime", ## args)
44 #define tpCertInfoDbg(args...) secinfo("tpCert", ## args)
45
46 static const TPClItemCalls tpCertClCalls =
47 {
48 CSSM_CL_CertGetFirstCachedFieldValue,
49 CSSM_CL_CertAbortQuery,
50 CSSM_CL_CertCache,
51 CSSM_CL_CertAbortCache,
52 CSSM_CL_CertVerify,
53 &CSSMOID_X509V1ValidityNotBefore,
54 &CSSMOID_X509V1ValidityNotAfter,
55 CSSMERR_TP_INVALID_CERT_POINTER,
56 CSSMERR_TP_CERT_EXPIRED,
57 CSSMERR_TP_CERT_NOT_VALID_YET
58 };
59
60 TPClItemInfo::TPClItemInfo(
61 CSSM_CL_HANDLE clHand,
62 CSSM_CSP_HANDLE cspHand,
63 const TPClItemCalls &clCalls,
64 const CSSM_DATA *itemData,
65 TPItemCopy copyItemData,
66 const char *verifyTime) // may be NULL
67 :
68 mClHand(clHand),
69 mCspHand(cspHand),
70 mClCalls(clCalls),
71 mWeOwnTheData(false),
72 mCacheHand(0),
73 mIssuerName(NULL),
74 mSubjectKeyID(NULL),
75 mAuthorityKeyID(NULL),
76 mItemData(NULL),
77 mSigAlg(CSSM_ALGID_NONE),
78 mNotBefore(NULL),
79 mNotAfter(NULL),
80 mIsExpired(false),
81 mIsNotValidYet(false),
82 mIndex(0)
83 {
84 try {
85 CSSM_RETURN crtn = cacheItem(itemData, copyItemData);
86 if(crtn) {
87 CssmError::throwMe(crtn);
88 }
89
90 /*
91 * Fetch standard fields...
92 * Issuer name assumes same OID for Certs and CRLs!
93 */
94 crtn = fetchField(&CSSMOID_X509V1IssuerName, &mIssuerName);
95 if(crtn) {
96 CssmError::throwMe(crtn);
97 }
98
99 /*
100 * Signing algorithm, infer from TBS algId
101 * Note this assumes that the OID for fetching this field is the
102 * same for CRLs and Certs.
103 */
104 CSSM_DATA_PTR algField;
105 crtn = fetchField(&CSSMOID_X509V1SignatureAlgorithmTBS, &algField);
106 if(crtn) {
107 releaseResources();
108 CssmError::throwMe(crtn);
109 }
110 if(algField->Length != sizeof(CSSM_X509_ALGORITHM_IDENTIFIER)) {
111 tpErrorLog("TPClItemInfo: bad CSSM_X509_ALGORITHM_IDENTIFIER\n");
112 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
113 }
114 CSSM_X509_ALGORITHM_IDENTIFIER *algId =
115 (CSSM_X509_ALGORITHM_IDENTIFIER *)algField->Data;
116 bool algFound = cssmOidToAlg(&algId->algorithm, &mSigAlg);
117 if(!algFound) {
118 tpErrorLog("TPClItemInfo: unknown signature algorithm\n");
119 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT);
120 }
121 if(mSigAlg == CSSM_ALGID_ECDSA_SPECIFIED) {
122 /* Further processing needed to get digest algorithm */
123 if(decodeECDSA_SigAlgParams(&algId->parameters, &mSigAlg)) {
124 tpErrorLog("TPClItemInfo: incomplete/unknown ECDSA signature algorithm\n");
125 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT);
126 }
127 }
128 freeField(&CSSMOID_X509V1SignatureAlgorithmTBS, algField);
129
130 /* Attempt to fetch authority key id and subject key id,
131 * ignore error if they do not exist.
132 */
133 fetchField(&CSSMOID_SubjectKeyIdentifier, &mSubjectKeyID);
134 fetchField(&CSSMOID_AuthorityKeyIdentifier, &mAuthorityKeyID);
135
136 fetchNotBeforeAfter();
137 calculateCurrent(verifyTime);
138 }
139 catch(...) {
140 releaseResources();
141 throw;
142 }
143 }
144
145 TPClItemInfo::~TPClItemInfo()
146 {
147 tpCertInfoDbg("TPClItemInfo destruct this %p", this);
148 releaseResources();
149 }
150
151 void TPClItemInfo::releaseResources()
152 {
153 if(mWeOwnTheData && (mItemData != NULL)) {
154 tpFreeCssmData(Allocator::standard(), mItemData, CSSM_TRUE);
155 mWeOwnTheData = false;
156 mItemData = NULL;
157 }
158 if(mIssuerName) {
159 freeField(&CSSMOID_X509V1IssuerName, mIssuerName);
160 mIssuerName = NULL;
161 }
162 if(mSubjectKeyID) {
163 freeField(&CSSMOID_SubjectKeyIdentifier, mSubjectKeyID);
164 mSubjectKeyID = NULL;
165 }
166 if(mAuthorityKeyID) {
167 freeField(&CSSMOID_AuthorityKeyIdentifier, mAuthorityKeyID);
168 mAuthorityKeyID = NULL;
169 }
170 if(mCacheHand != 0) {
171 mClCalls.abortCache(mClHand, mCacheHand);
172 mCacheHand = 0;
173 }
174 if(mNotBefore) {
175 CFRelease(mNotBefore);
176 mNotBefore = NULL;
177 }
178 if(mNotAfter) {
179 CFRelease(mNotAfter);
180 mNotAfter = NULL;
181 }
182 }
183
184 /* fetch arbitrary field from cached cert */
185 CSSM_RETURN TPClItemInfo::fetchField(
186 const CSSM_OID *fieldOid,
187 CSSM_DATA_PTR *fieldData) // mallocd by CL and RETURNED
188 {
189 CSSM_RETURN crtn;
190
191 uint32 NumberOfFields = 0;
192 CSSM_HANDLE resultHand = 0;
193 *fieldData = NULL;
194
195 assert(mClCalls.getField != NULL);
196 assert(mCacheHand != 0);
197 crtn = mClCalls.getField(
198 mClHand,
199 mCacheHand,
200 fieldOid,
201 &resultHand,
202 &NumberOfFields,
203 fieldData);
204 if(crtn) {
205 return crtn;
206 }
207 if(NumberOfFields != 1) {
208 tpCertInfoDbg("TPClItemInfo::fetchField: numFields %d, expected 1\n",
209 (int)NumberOfFields);
210 }
211 mClCalls.abortQuery(mClHand, resultHand);
212 return CSSM_OK;
213 }
214
215 /* free arbitrary field obtained from fetchField() */
216 CSSM_RETURN TPClItemInfo::freeField(
217 const CSSM_OID *fieldOid,
218 CSSM_DATA_PTR fieldData)
219 {
220 return CSSM_CL_FreeFieldValue(mClHand, fieldOid, fieldData);
221
222 }
223
224 /*
225 * Verify with an issuer cert - works on certs and CRLs.
226 * Issuer/subject name match already performed by caller.
227 * Optional paramCert is used to provide parameters when issuer
228 * has a partial public key.
229 */
230 CSSM_RETURN TPClItemInfo::verifyWithIssuer(
231 TPCertInfo *issuerCert,
232 TPCertInfo *paramCert /* = NULL */) const
233 {
234 CSSM_RETURN crtn;
235
236 assert(mClHand != 0);
237 assert(issuerCert->isIssuerOf(*this));
238 assert(mCspHand != 0);
239
240 /*
241 * Special case: detect partial public key right now; don't even
242 * bother trying the cert verify in that case.
243 */
244 if(issuerCert->hasPartialKey() && (paramCert == NULL)) {
245 /* caller deals with this later */
246 tpVfyDebug("verifyWithIssuer PUBLIC_KEY_INCOMPLETE");
247 return CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE;
248 }
249
250 CSSM_CC_HANDLE ccHand;
251 crtn = CSSM_CSP_CreateSignatureContext(mCspHand,
252 mSigAlg,
253 NULL, // Access Creds
254 issuerCert->pubKey(),
255 &ccHand);
256 if(crtn != CSSM_OK) {
257 tpErrorLog("verifyWithIssuer: CreateSignatureContext error\n");
258 CssmError::throwMe(crtn);
259 }
260 if(paramCert != NULL) {
261 assert(issuerCert->hasPartialKey());
262
263 /* add in parameter-bearing key */
264 CSSM_CONTEXT_ATTRIBUTE newAttr;
265
266 newAttr.AttributeType = CSSM_ATTRIBUTE_PARAM_KEY;
267 newAttr.AttributeLength = sizeof(CSSM_KEY);
268 newAttr.Attribute.Key = paramCert->pubKey();
269 crtn = CSSM_UpdateContextAttributes(ccHand, 1, &newAttr);
270 if(crtn) {
271 tpErrorLog("verifyWithIssuer: CSSM_UpdateContextAttributes error\n");
272 CssmError::throwMe(crtn);
273 }
274 }
275 crtn = mClCalls.itemVerify(mClHand,
276 ccHand,
277 mItemData,
278 NULL, // issuer cert
279 NULL, // VerifyScope
280 0); // ScopeSize
281
282 switch(crtn) {
283 case CSSM_OK: // success
284 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: // caller handles
285 tpVfyDebug("verifyWithIssuer GOOD");
286 break;
287 default:
288 /* all others appear here as general cert verify error */
289 crtn = CSSMERR_TP_VERIFICATION_FAILURE;
290 tpVfyDebug("verifyWithIssuer BAD");
291 break;
292 }
293 CSSM_DeleteContext(ccHand);
294 return crtn;
295 }
296
297 CSSM_RETURN TPClItemInfo::cacheItem(
298 const CSSM_DATA *itemData,
299 TPItemCopy copyItemData)
300 {
301 switch(copyItemData) {
302 case TIC_NoCopy:
303 mItemData = const_cast<CSSM_DATA *>(itemData);
304 break;
305 case TIC_CopyData:
306 mItemData = tpMallocCopyCssmData(Allocator::standard(), itemData);
307 mWeOwnTheData = true;
308 break;
309 default:
310 assert(0);
311 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
312 }
313
314 /* cache the cert/CRL in the CL */
315 return mClCalls.cacheItem(mClHand, mItemData, &mCacheHand);
316 }
317
318 /*
319 * Calculate not before/after times as struct tm. Only throws on
320 * gross error (CSSMERR_TP_INVALID_CERT_POINTER, etc.).
321 *
322 * Only differences between Cert and CRL flavors of this are the
323 * OIDs used to fetch the appropriate before/after times, both of
324 * which are expressed as CSSM_X509_TIME structs for both Certs
325 * and CRLS.
326 */
327 void TPClItemInfo::fetchNotBeforeAfter()
328 {
329 CSSM_DATA_PTR notBeforeField = NULL;
330 CSSM_DATA_PTR notAfterField = NULL;
331 CSSM_RETURN crtn = CSSM_OK;
332 CSSM_X509_TIME *xTime;
333
334 assert(cacheHand() != CSSM_INVALID_HANDLE);
335 crtn = fetchField(mClCalls.notBeforeOid, &notBeforeField);
336 if(crtn) {
337 tpErrorLog("fetchNotBeforeAfter: GetField error\n");
338 CssmError::throwMe(mClCalls.invalidItemRtn);
339 }
340
341 /* subsequent errors to errOut */
342 xTime = (CSSM_X509_TIME *)notBeforeField->Data;
343 if(timeStringToCfDate((char *)xTime->time.Data, (unsigned)xTime->time.Length, &mNotBefore)) {
344 tpErrorLog("fetchNotBeforeAfter: malformed notBefore time\n");
345 crtn = mClCalls.invalidItemRtn;
346 goto errOut;
347 }
348
349 crtn = fetchField(mClCalls.notAfterOid, &notAfterField);
350 if(crtn) {
351 /*
352 * Tolerate a missing NextUpdate in CRL only
353 */
354 if(mClCalls.notAfterOid == &CSSMOID_X509V1ValidityNotAfter) {
355 tpErrorLog("fetchNotBeforeAfter: GetField error\n");
356 crtn = mClCalls.invalidItemRtn;
357 goto errOut;
358 }
359 else {
360 /*
361 * Fake NextUpdate to be "at the end of time"
362 */
363 timeStringToCfDate(CSSM_APPLE_CRL_END_OF_TIME,
364 strlen(CSSM_APPLE_CRL_END_OF_TIME),
365 &mNotAfter);
366 }
367 }
368 else {
369 xTime = (CSSM_X509_TIME *)notAfterField->Data;
370 if(timeStringToCfDate((char *)xTime->time.Data, (unsigned)xTime->time.Length, &mNotAfter)) {
371 tpErrorLog("fetchNotBeforeAfter: malformed notAfter time\n");
372 crtn = mClCalls.invalidItemRtn;
373 goto errOut;
374 }
375 }
376 crtn = CSSM_OK;
377 errOut:
378 if(notAfterField) {
379 freeField(mClCalls.notAfterOid, notAfterField);
380 }
381 if(notBeforeField) {
382 freeField(mClCalls.notBeforeOid, notBeforeField);
383 }
384 if(crtn != CSSM_OK) {
385 CssmError::throwMe(crtn);
386 }
387 }
388
389 /*
390 * Verify validity (not before/after) by comparing the reference
391 * time (verifyString if present, or "now" if NULL) to the
392 * not before/after fields fetched from the item at construction.
393 *
394 * Called implicitly at construction; can be called again any time
395 * to re-establish validity (e.g. after fetching an item from a cache).
396 *
397 * We use some stdlib time calls over in tpTime.c; the stdlib function
398 * gmtime() is not thread-safe, so we do the protection here. Note that
399 * this makes *our* calls to gmtime() thread-safe, but if the app has
400 * other threads which are also calling gmtime, we're out of luck.
401 */
402 ModuleNexus<Mutex> tpTimeLock;
403
404 CSSM_RETURN TPClItemInfo::calculateCurrent(
405 const char *verifyString)
406 {
407 CFDateRef refTime = NULL;
408
409 if(verifyString != NULL) {
410 /* caller specifies verification time base */
411 if(timeStringToCfDate(verifyString, (unsigned)strlen(verifyString), &refTime)) {
412 tpErrorLog("calculateCurrent: timeStringToCfDate error\n");
413 return CSSMERR_TP_INVALID_TIMESTRING;
414 }
415 }
416 else {
417 /* time base = right now */
418 refTime = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
419 }
420 if(compareTimes(refTime, mNotBefore) < 0) {
421 mIsNotValidYet = true;
422 tpTimeDbg("\nTP_CERT_NOT_VALID_YET: now %g notBefore %g",
423 CFDateGetAbsoluteTime(refTime), CFDateGetAbsoluteTime(mNotBefore));
424 CFRelease(refTime);
425 return mClCalls.notValidYetRtn;
426 }
427 else {
428 mIsNotValidYet = false;
429 }
430
431 if(compareTimes(refTime, mNotAfter) > 0) {
432 mIsExpired = true;
433 tpTimeDbg("\nTP_CERT_EXPIRED: now %g notBefore %g",
434 CFDateGetAbsoluteTime(refTime), CFDateGetAbsoluteTime(mNotBefore));
435 CFRelease(refTime);
436 return mClCalls.expiredRtn;
437 }
438 else {
439 mIsExpired = false;
440 CFRelease(refTime);
441 return CSSM_OK;
442 }
443 }
444
445
446 /*
447 * No default constructor - this is the only way.
448 * This caches the cert and fetches subjectName, issuerName, and
449 * mPublicKey to ensure the incoming certData is well-constructed.
450 */
451 TPCertInfo::TPCertInfo(
452 CSSM_CL_HANDLE clHand,
453 CSSM_CSP_HANDLE cspHand,
454 const CSSM_DATA *certData,
455 TPItemCopy copyCertData, // true: we copy, we free
456 // false - caller owns
457 const char *verifyTime) // may be NULL
458 :
459 TPClItemInfo(clHand, cspHand, tpCertClCalls, certData,
460 copyCertData, verifyTime),
461 mSubjectName(NULL),
462 mPublicKeyData(NULL),
463 mPublicKey(NULL),
464 mIsAnchor(false),
465 mIsFromInputCerts(false),
466 mIsFromNet(false),
467 mNumStatusCodes(0),
468 mStatusCodes(NULL),
469 mCrlReason(0),
470 mUniqueRecord(NULL),
471 mUsed(false),
472 mIsLeaf(false),
473 mIsRoot(TRS_Unknown),
474 mRevCheckGood(false),
475 mRevCheckComplete(false),
476 mTrustSettingsEvaluated(false),
477 mTrustSettingsDomain(kSecTrustSettingsDomainSystem),
478 mTrustSettingsResult(kSecTrustSettingsResultInvalid),
479 mTrustSettingsFoundAnyEntry(false),
480 mTrustSettingsFoundMatchingEntry(false),
481 mAllowedErrs(NULL),
482 mNumAllowedErrs(0),
483 mIgnoredError(false),
484 mTrustSettingsKeyUsage(0),
485 mCertHashStr(NULL)
486 {
487 CSSM_RETURN crtn;
488
489 tpCertInfoDbg("TPCertInfo construct this %p", this);
490 mDlDbHandle.DLHandle = 0;
491 mDlDbHandle.DBHandle = 0;
492
493 /* fetch subject name */
494 crtn = fetchField(&CSSMOID_X509V1SubjectName, &mSubjectName);
495 if(crtn) {
496 /* bad cert */
497 releaseResources();
498 CssmError::throwMe(crtn);
499 }
500
501 /* this cert's public key */
502 crtn = fetchField(&CSSMOID_CSSMKeyStruct, &mPublicKeyData);
503 if(crtn || (mPublicKeyData->Length != sizeof(CSSM_KEY))) {
504 /* bad cert */
505 releaseResources();
506 CssmError::throwMe(crtn);
507 }
508 mPublicKey = (CSSM_KEY_PTR)mPublicKeyData->Data;
509
510 /* calculate other commonly used fields */
511 if(tpCompareCssmData(mSubjectName, issuerName())) {
512 /*
513 * Per Radar 3374978, perform complete signature verification
514 * lazily - just check subject/issuer match here.
515 */
516 tpAnchorDebug("TPCertInfo potential anchor");
517 mIsRoot = TRS_NamesMatch;
518 }
519 else {
520 mIsRoot = TRS_NotRoot;
521 }
522 }
523
524 /* frees mSubjectName, mIssuerName, mCacheHand via mClHand */
525 TPCertInfo::~TPCertInfo()
526 {
527 tpCertInfoDbg("TPCertInfo destruct this %p", this);
528 releaseResources();
529 }
530
531 void TPCertInfo::releaseResources()
532 {
533 if(mSubjectName) {
534 freeField(&CSSMOID_X509V1SubjectName, mSubjectName);
535 mSubjectName = NULL;
536 }
537 if(mPublicKeyData) {
538 freeField(&CSSMOID_CSSMKeyStruct, mPublicKeyData);
539 mPublicKey = NULL;
540 mPublicKeyData = NULL;
541 }
542 if(mStatusCodes) {
543 free(mStatusCodes);
544 mStatusCodes = NULL;
545 }
546 if(mAllowedErrs) {
547 free(mAllowedErrs);
548 }
549 if(mCertHashStr) {
550 CFRelease(mCertHashStr);
551 }
552 TPClItemInfo::releaseResources();
553 }
554
555 const CSSM_DATA *TPCertInfo::subjectName()
556 {
557 assert(mSubjectName != NULL);
558 return mSubjectName;
559 }
560
561 /*
562 * Perform semi-lazy evaluation of "rootness". Subject and issuer names
563 * compared at constructor.
564 * If avoidVerify is true, we won't do the signature verify: caller
565 * just wants to know if the subject and issuer names match.
566 */
567 bool TPCertInfo::isSelfSigned(bool avoidVerify)
568 {
569 switch(mIsRoot) {
570 case TRS_NotRoot: // known not to be root
571 return false;
572 case TRS_IsRoot:
573 return true;
574 case TRS_NamesMatch:
575 if(avoidVerify) {
576 return true;
577 }
578 /* else drop through and verify */
579 case TRS_Unknown: // actually shouldn't happen, but to be safe...
580 default:
581 /* do the signature verify */
582 if(verifyWithIssuer(this) == CSSM_OK) {
583 tpAnchorDebug("isSelfSigned anchor verified");
584 mIsRoot = TRS_IsRoot;
585 return true;
586 }
587 else {
588 tpAnchorDebug("isSelfSigned anchor vfy FAIL");
589 mIsRoot = TRS_NotRoot;
590 return false;
591 }
592 }
593 }
594
595 /*
596 * Am I the issuer of the specified subject item? Returns true if so.
597 * Works for subject certs as well as CRLs.
598 */
599 bool TPCertInfo::isIssuerOf(
600 const TPClItemInfo &subject)
601 {
602 assert(mSubjectName != NULL);
603 assert(subject.issuerName() != NULL);
604 if(tpCompareCssmData(mSubjectName, subject.issuerName())) {
605 return true;
606 }
607 else {
608 return false;
609 }
610 }
611
612 /*
613 * Does my subjectKeyID match the authorityKeyID of the specified subject?
614 * Returns true if so (and if both fields are available).
615 */
616 bool TPCertInfo::isAuthorityKeyOf(
617 const TPClItemInfo &subject)
618 {
619 const CSSM_DATA *subjectKeyID = this->subjectKeyID();
620 const CSSM_DATA *authorityKeyID = subject.authorityKeyID();
621 if(!subjectKeyID || !authorityKeyID) {
622 tpDebug("isAuthorityKeyOf FALSE (one or both key ids missing)");
623 return false;
624 }
625 CSSM_X509_EXTENSION *ske = (CSSM_X509_EXTENSION *)subjectKeyID->Data;
626 CSSM_X509_EXTENSION *ake = (CSSM_X509_EXTENSION *)authorityKeyID->Data;
627 if( !ske || ske->format != CSSM_X509_DATAFORMAT_PARSED ||
628 !ake || ake->format != CSSM_X509_DATAFORMAT_PARSED ||
629 !ske->value.parsedValue || !ake->value.parsedValue) {
630 tpDebug("isAuthorityKeyOf FALSE (no parsed value present)");
631 return false;
632 }
633
634 const CE_SubjectKeyID *skid = (CE_SubjectKeyID *)ske->value.parsedValue;
635 const CE_AuthorityKeyID *akid = (CE_AuthorityKeyID *)ake->value.parsedValue;
636
637 if(!akid->keyIdentifierPresent) {
638 tpDebug("isAuthorityKeyOf FALSE (no key identifier present)");
639 return false;
640 }
641 if(tpCompareCssmData(skid, &akid->keyIdentifier)) {
642 #ifndef NDEBUG
643 tpDebug("isAuthorityKeyOf TRUE (len:s=%lu/a=%lu, %08lX../%08lX..)",
644 skid->Length,
645 akid->keyIdentifier.Length,
646 (skid->Data) ? *((unsigned long *)skid->Data) : 0L,
647 (akid->keyIdentifier.Data) ? *((unsigned long *)akid->keyIdentifier.Data) : 0L);
648 #endif
649 return true;
650 }
651 else {
652 #ifndef NDEBUG
653 tpDebug("isAuthorityKeyOf FALSE (len:s=%lu/a=%lu, %08lX../%08lX..)",
654 skid->Length,
655 akid->keyIdentifier.Length,
656 (skid->Data) ? *((unsigned long *)skid->Data) : 0L,
657 (akid->keyIdentifier.Data) ? *((unsigned long *)akid->keyIdentifier.Data) : 0L);
658 #endif
659 return false;
660 }
661 }
662
663 bool TPCertInfo::addStatusCode(CSSM_RETURN code)
664 {
665 mNumStatusCodes++;
666 mStatusCodes = (CSSM_RETURN *)realloc(mStatusCodes,
667 mNumStatusCodes * sizeof(CSSM_RETURN));
668 mStatusCodes[mNumStatusCodes - 1] = code;
669 return isStatusFatal(code);
670 }
671
672 bool TPCertInfo::hasStatusCode(CSSM_RETURN code)
673 {
674 for(unsigned dex=0; dex<mNumStatusCodes; dex++) {
675 if(mStatusCodes[dex] == code) {
676 return true;
677 }
678 }
679 return false;
680 }
681
682 bool TPCertInfo::isStatusFatal(CSSM_RETURN code)
683 {
684 for(unsigned dex=0; dex<mNumAllowedErrs; dex++) {
685 if(mAllowedErrs[dex] == code) {
686 tpTrustSettingsDbg("isStatusFatal(%ld): ALLOWED", (unsigned long)code);
687 mIgnoredError = true;
688 return false;
689 }
690 }
691 return true;
692 }
693
694 /*
695 * Indicate whether this cert's public key is a CSSM_KEYATTR_PARTIAL
696 * key.
697 */
698 bool TPCertInfo::hasPartialKey()
699 {
700 if(mPublicKey->KeyHeader.KeyAttr & CSSM_KEYATTR_PARTIAL) {
701 return true;
702 }
703 else {
704 return false;
705 }
706 }
707
708 /*
709 * <rdar://9145531>
710 */
711 bool TPCertInfo::shouldReject()
712 {
713 static unsigned char _UTN_UF_H_ISSUER_BYTES[154] = {
714 0x30, 0x81, 0x97, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
715 0x13, 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
716 0x08, 0x13, 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55,
717 0x04, 0x07, 0x13, 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b,
718 0x45, 0x20, 0x43, 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03,
719 0x55, 0x04, 0x0a, 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45,
720 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f,
721 0x52, 0x4b, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
722 0x18, 0x48, 0x54, 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e,
723 0x55, 0x53, 0x45, 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f,
724 0x4d, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16,
725 0x55, 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53,
726 0x54, 0x2d, 0x48, 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45
727 };
728 CSSM_DATA _UTN_UF_H_ISSUER = { sizeof(_UTN_UF_H_ISSUER_BYTES), _UTN_UF_H_ISSUER_BYTES };
729
730 static CSSM_DATA _UTN_UF_H_SERIALS[] = {
731 { 17, (uint8*)"\x00\x92\x39\xd5\x34\x8f\x40\xd1\x69\x5a\x74\x54\x70\xe1\xf2\x3f\x43" }, // amo
732 { 17, (uint8*)"\x00\xd8\xf3\x5f\x4e\xb7\x87\x2b\x2d\xab\x06\x92\xe3\x15\x38\x2f\xb0" }, // gt
733 { 17, (uint8*)"\x00\xb0\xb7\x13\x3e\xd0\x96\xf9\xb5\x6f\xae\x91\xc8\x74\xbd\x3a\xc0" }, // llc
734 { 17, (uint8*)"\x00\xe9\x02\x8b\x95\x78\xe4\x15\xdc\x1a\x71\x0a\x2b\x88\x15\x44\x47" }, // lsc
735 { 17, (uint8*)"\x00\xd7\x55\x8f\xda\xf5\xf1\x10\x5b\xb2\x13\x28\x2b\x70\x77\x29\xa3" }, // lyc
736 { 16, (uint8*)"\x39\x2a\x43\x4f\x0e\x07\xdf\x1f\x8a\xa3\x05\xde\x34\xe0\xc2\x29" }, // lyc1
737 { 16, (uint8*)"\x3e\x75\xce\xd4\x6b\x69\x30\x21\x21\x88\x30\xae\x86\xa8\x2a\x71" }, // lyc2
738 { 16, (uint8*)"\x04\x7e\xcb\xe9\xfc\xa5\x5f\x7b\xd0\x9e\xae\x36\xe1\x0c\xae\x1e" }, // mgc
739 { 17, (uint8*)"\x00\xf5\xc8\x6a\xf3\x61\x62\xf1\x3a\x64\xf5\x4f\x6d\xc9\x58\x7c\x06" }, // wgc
740 { 0, NULL }
741 };
742
743 const CSSM_DATA *issuer=issuerName();
744 if(!issuer || !(tpCompareCssmData(issuer, &_UTN_UF_H_ISSUER)))
745 return false;
746
747 CSSM_DATA *serialNumber=NULL;
748 CSSM_RETURN crtn = fetchField(&CSSMOID_X509V1SerialNumber, &serialNumber);
749 if(crtn || !serialNumber)
750 return false;
751
752 CSSM_DATA *p=_UTN_UF_H_SERIALS;
753 bool matched=false;
754 while(p->Length) {
755 if(tpCompareCssmData(serialNumber, p)) {
756 matched=true;
757 addStatusCode(CSSMERR_TP_CERT_REVOKED);
758 mCrlReason=2;
759 break;
760 }
761 ++p;
762 }
763 freeField(&CSSMOID_X509V1SerialNumber, serialNumber);
764 return matched;
765 }
766
767 /*
768 * Evaluate trust settings; returns true in *foundMatchingEntry if positive
769 * match found - i.e., cert chain construction is done.
770 */
771 OSStatus TPCertInfo::evaluateTrustSettings(
772 const CSSM_OID &policyOid,
773 const char *policyString, // optional
774 uint32 policyStringLen,
775 SecTrustSettingsKeyUsage keyUse, // required
776 bool *foundMatchingEntry, // RETURNED
777 bool *foundAnyEntry) // RETURNED
778 {
779 /*
780 * We might have to force a re-evaluation if the requested key usage
781 * is not a subset of what we already checked for (and cached).
782 */
783 if(mTrustSettingsEvaluated) {
784 bool doFlush = false;
785 if(mTrustSettingsKeyUsage != kSecTrustSettingsKeyUseAny) {
786 if(keyUse == kSecTrustSettingsKeyUseAny) {
787 /* now want "any", checked something else before */
788 doFlush = true;
789 }
790 else if((keyUse & mTrustSettingsKeyUsage) != keyUse) {
791 /* want bits that we didn't ask for before */
792 doFlush = true;
793 }
794 }
795 if(doFlush) {
796 tpTrustSettingsDbg("evaluateTrustSettings: flushing cached trust for "
797 "%p due to keyUse 0x%x", this, (int)keyUse);
798 mTrustSettingsEvaluated = false;
799 mTrustSettingsFoundAnyEntry = false;
800 mTrustSettingsResult = kSecTrustSettingsResultInvalid;
801 mTrustSettingsFoundMatchingEntry = false;
802 if(mAllowedErrs != NULL) {
803 free(mAllowedErrs);
804 }
805 mNumAllowedErrs = 0;
806 }
807 /* else we can safely use the cached values */
808 }
809 if(!mTrustSettingsEvaluated) {
810
811 if(mCertHashStr == NULL) {
812 const CSSM_DATA *certData = itemData();
813 mCertHashStr = SecTrustSettingsCertHashStrFromData(certData->Data,
814 certData->Length);
815 }
816
817 OSStatus ortn = SecTrustSettingsEvaluateCert(mCertHashStr,
818 &policyOid,
819 policyString,
820 policyStringLen,
821 keyUse,
822 /*
823 * This is the purpose of the avoidVerify option, right here.
824 * If this is a root cert and it has trust settings, we avoid
825 * the signature verify. If it turns out there are no trust
826 * settings and this is a root, we'll verify the signature
827 * elsewhere (e.g. post_trust_setting: in buildCertGroup()).
828 */
829 isSelfSigned(true),
830 &mTrustSettingsDomain,
831 &mAllowedErrs,
832 &mNumAllowedErrs,
833 &mTrustSettingsResult,
834 &mTrustSettingsFoundMatchingEntry,
835 &mTrustSettingsFoundAnyEntry);
836 if(ortn) {
837 tpTrustSettingsDbg("evaluateTrustSettings: SecTrustSettingsEvaluateCert error!");
838 return ortn;
839 }
840 mTrustSettingsEvaluated = true;
841 mTrustSettingsKeyUsage = keyUse;
842 #ifndef NDEBUG
843 if(mTrustSettingsFoundMatchingEntry) {
844 tpTrustSettingsDbg("evaluateTrustSettings: found for %p result %d",
845 this, (int)mTrustSettingsResult);
846 }
847 #endif
848 /* one more thing... */
849 if(shouldReject()) {
850 return CSSMERR_TP_INVALID_CERTIFICATE;
851 }
852 }
853 *foundMatchingEntry = mTrustSettingsFoundMatchingEntry;
854 *foundAnyEntry = mTrustSettingsFoundAnyEntry;
855
856 return errSecSuccess;
857 }
858
859 /* true means "verification terminated due to user trust setting" */
860 bool TPCertInfo::trustSettingsFound()
861 {
862 switch(mTrustSettingsResult) {
863 case kSecTrustSettingsResultUnspecified: /* entry but not definitive */
864 case kSecTrustSettingsResultInvalid: /* no entry */
865 return false;
866 default:
867 return true;
868 }
869 }
870
871 /*
872 * Determine if this has an empty SubjectName field. Returns true if so.
873 */
874 bool TPCertInfo::hasEmptySubjectName()
875 {
876 /*
877 * A "pure" empty subject is two bytes (0x30 00) - constructed sequence,
878 * short form length, length 0. We'll be robust and tolerate a missing
879 * field, as well as a possible BER-encoded subject with some extra cruft.
880 */
881 if((mSubjectName == NULL) || (mSubjectName->Length <= 4)) {
882 return true;
883 }
884 else {
885 return false;
886 }
887 }
888
889 /*
890 * Free mUniqueRecord if it exists.
891 * This is *not* done in our destructor because this record sometimes
892 * has to persist in the form of a CSSM evidence chain.
893 */
894 void TPCertInfo::freeUniqueRecord()
895 {
896 if(mUniqueRecord == NULL) {
897 return;
898 }
899 tpDbDebug("freeUniqueRecord: freeing cert record %p", mUniqueRecord);
900 CSSM_DL_FreeUniqueRecord(mDlDbHandle, mUniqueRecord);
901 }
902
903 /***
904 *** TPCertGroup class
905 ***/
906
907 /* build empty group */
908 TPCertGroup::TPCertGroup(
909 Allocator &alloc,
910 TPGroupOwner whoOwns) :
911 mAlloc(alloc),
912 mCertInfo(NULL),
913 mNumCerts(0),
914 mSizeofCertInfo(0),
915 mWhoOwns(whoOwns)
916 {
917 tpCertInfoDbg("TPCertGroup simple construct this %p", this);
918 /* nothing for now */
919 }
920
921 /*
922 * Construct from unordered, untrusted CSSM_CERTGROUP. Resulting
923 * TPCertInfos are more or less in the same order as the incoming
924 * certs, though incoming certs are discarded if they don't parse.
925 * No verification of any sort is performed.
926 */
927 TPCertGroup::TPCertGroup(
928 const CSSM_CERTGROUP &CertGroupFrag,
929 CSSM_CL_HANDLE clHand,
930 CSSM_CSP_HANDLE cspHand,
931 Allocator &alloc,
932 const char *verifyTime, // may be NULL
933 bool firstCertMustBeValid,
934 TPGroupOwner whoOwns) :
935 mAlloc(alloc),
936 mCertInfo(NULL),
937 mNumCerts(0),
938 mSizeofCertInfo(0),
939 mWhoOwns(whoOwns)
940 {
941 tpCertInfoDbg("TPCertGroup hard construct this %p", this);
942
943 /* verify input args */
944 if(cspHand == CSSM_INVALID_HANDLE) {
945 CssmError::throwMe(CSSMERR_TP_INVALID_CSP_HANDLE);
946 }
947 if(clHand == CSSM_INVALID_HANDLE) {
948 CssmError::throwMe(CSSMERR_TP_INVALID_CL_HANDLE);
949 }
950 if(firstCertMustBeValid) {
951 if( (CertGroupFrag.NumCerts == 0) ||
952 (CertGroupFrag.GroupList.CertList[0].Data == NULL) ||
953 (CertGroupFrag.GroupList.CertList[0].Length == 0)) {
954 CssmError::throwMe(CSSMERR_TP_INVALID_CERTIFICATE);
955 }
956 }
957 if(CertGroupFrag.CertGroupType != CSSM_CERTGROUP_DATA) {
958 CssmError::throwMe(CSSMERR_TP_INVALID_CERTGROUP);
959 }
960 switch(CertGroupFrag.CertType) {
961 case CSSM_CERT_X_509v1:
962 case CSSM_CERT_X_509v2:
963 case CSSM_CERT_X_509v3:
964 break;
965 default:
966 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT);
967 }
968 switch(CertGroupFrag.CertEncoding) {
969 case CSSM_CERT_ENCODING_BER:
970 case CSSM_CERT_ENCODING_DER:
971 break;
972 default:
973 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT);
974 }
975
976 /*
977 * Add remaining input certs to mCertInfo.
978 */
979 TPCertInfo *certInfo = NULL;
980 for(unsigned certDex=0; certDex<CertGroupFrag.NumCerts; certDex++) {
981 try {
982 certInfo = new TPCertInfo(clHand,
983 cspHand,
984 &CertGroupFrag.GroupList.CertList[certDex],
985 TIC_NoCopy, // caller owns
986 verifyTime);
987 }
988 catch (...) {
989 if((certDex == 0) && firstCertMustBeValid) {
990 CssmError::throwMe(CSSMERR_TP_INVALID_CERTIFICATE);
991 }
992 /* else just ignore this cert */
993 continue;
994 }
995 certInfo->index(certDex);
996 appendCert(certInfo);
997 }
998 }
999
1000 /*
1001 * Deletes contents of mCertInfo[] if appropriate.
1002 */
1003 TPCertGroup::~TPCertGroup()
1004 {
1005 if(mWhoOwns == TGO_Group) {
1006 unsigned i;
1007 for(i=0; i<mNumCerts; i++) {
1008 delete mCertInfo[i];
1009 }
1010 }
1011 mAlloc.free(mCertInfo);
1012 }
1013
1014 /* add/remove/access TPTCertInfo's. */
1015 /*
1016 * NOTE: I am aware that most folks would just use an array<> here, but
1017 * gdb is so lame that it doesn't even let one examine the contents
1018 * of an array<> (or just about anything else in the STL). I prefer
1019 * debuggability over saving a few lines of trivial code.
1020 */
1021 void TPCertGroup::appendCert(
1022 TPCertInfo *certInfo) // appends to end of mCertInfo
1023 {
1024 if(mNumCerts == mSizeofCertInfo) {
1025 if(mSizeofCertInfo == 0) {
1026 /* appending to empty array */
1027 mSizeofCertInfo = 1;
1028 }
1029 else {
1030 mSizeofCertInfo *= 2;
1031 }
1032 mCertInfo = (TPCertInfo **)mAlloc.realloc(mCertInfo,
1033 mSizeofCertInfo * sizeof(TPCertInfo *));
1034 }
1035 mCertInfo[mNumCerts++] = certInfo;
1036 }
1037
1038 TPCertInfo *TPCertGroup::certAtIndex(
1039 unsigned index)
1040 {
1041 if(index > (mNumCerts - 1)) {
1042 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
1043 }
1044 return mCertInfo[index];
1045 }
1046
1047 TPCertInfo *TPCertGroup::removeCertAtIndex(
1048 unsigned index) // doesn't delete the cert, just
1049 // removes it from out list
1050 {
1051 if(index > (mNumCerts - 1)) {
1052 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
1053 }
1054 TPCertInfo *rtn = mCertInfo[index];
1055
1056 /* removed requested element and compact remaining array */
1057 unsigned i;
1058 for(i=index; i<(mNumCerts - 1); i++) {
1059 mCertInfo[i] = mCertInfo[i+1];
1060 }
1061 mNumCerts--;
1062 return rtn;
1063 }
1064
1065 TPCertInfo *TPCertGroup::firstCert()
1066 {
1067 if(mNumCerts == 0) {
1068 /* the caller really should not do this... */
1069 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
1070 }
1071 else {
1072 return mCertInfo[0];
1073 }
1074 }
1075
1076 TPCertInfo *TPCertGroup::lastCert()
1077 {
1078 if(mNumCerts == 0) {
1079 return NULL;
1080 }
1081 else {
1082 return mCertInfo[mNumCerts - 1];
1083 }
1084 }
1085
1086 /* build a CSSM_CERTGROUP corresponding with our mCertInfo */
1087 CSSM_CERTGROUP_PTR TPCertGroup::buildCssmCertGroup()
1088 {
1089 CSSM_CERTGROUP_PTR cgrp =
1090 (CSSM_CERTGROUP_PTR)mAlloc.malloc(sizeof(CSSM_CERTGROUP));
1091 cgrp->NumCerts = mNumCerts;
1092 cgrp->CertGroupType = CSSM_CERTGROUP_DATA;
1093 cgrp->CertType = CSSM_CERT_X_509v3;
1094 cgrp->CertEncoding = CSSM_CERT_ENCODING_DER;
1095 if(mNumCerts == 0) {
1096 /* legal */
1097 cgrp->GroupList.CertList = NULL;
1098 return cgrp;
1099 }
1100 cgrp->GroupList.CertList = (CSSM_DATA_PTR)mAlloc.calloc(mNumCerts,
1101 sizeof(CSSM_DATA));
1102 for(unsigned i=0; i<mNumCerts; i++) {
1103 tpCopyCssmData(mAlloc, mCertInfo[i]->itemData(),
1104 &cgrp->GroupList.CertList[i]);
1105 }
1106 return cgrp;
1107 }
1108
1109 /* build a CSSM_TP_APPLE_EVIDENCE_INFO array */
1110 CSSM_TP_APPLE_EVIDENCE_INFO *TPCertGroup::buildCssmEvidenceInfo()
1111 {
1112 CSSM_TP_APPLE_EVIDENCE_INFO *infoArray;
1113
1114 infoArray = (CSSM_TP_APPLE_EVIDENCE_INFO *)mAlloc.calloc(mNumCerts,
1115 sizeof(CSSM_TP_APPLE_EVIDENCE_INFO));
1116 for(unsigned i=0; i<mNumCerts; i++) {
1117 TPCertInfo *certInfo = mCertInfo[i];
1118 CSSM_TP_APPLE_EVIDENCE_INFO *evInfo = &infoArray[i];
1119
1120 /* first the booleans */
1121 if(certInfo->isExpired()) {
1122 evInfo->StatusBits |= CSSM_CERT_STATUS_EXPIRED;
1123 }
1124 if(certInfo->isNotValidYet()) {
1125 evInfo->StatusBits |= CSSM_CERT_STATUS_NOT_VALID_YET;
1126 }
1127 if(certInfo->isAnchor()) {
1128 tpAnchorDebug("buildCssmEvidenceInfo: flagging IS_IN_ANCHORS");
1129 evInfo->StatusBits |= CSSM_CERT_STATUS_IS_IN_ANCHORS;
1130 }
1131 if(certInfo->dlDbHandle().DLHandle == 0) {
1132 if(certInfo->isFromNet()) {
1133 evInfo->StatusBits |= CSSM_CERT_STATUS_IS_FROM_NET;
1134 }
1135 else if(certInfo->isFromInputCerts()) {
1136 evInfo->StatusBits |= CSSM_CERT_STATUS_IS_IN_INPUT_CERTS;
1137 }
1138 }
1139 /* If trust settings apply to a root, skip verifying the signature */
1140 bool avoidVerify = false;
1141 switch(certInfo->trustSettingsResult()) {
1142 case kSecTrustSettingsResultTrustRoot:
1143 case kSecTrustSettingsResultTrustAsRoot:
1144 /* these two can be disambiguated by IS_ROOT */
1145 evInfo->StatusBits |= CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST;
1146 avoidVerify = true;
1147 break;
1148 case kSecTrustSettingsResultDeny:
1149 evInfo->StatusBits |= CSSM_CERT_STATUS_TRUST_SETTINGS_DENY;
1150 avoidVerify = true;
1151 break;
1152 case kSecTrustSettingsResultUnspecified:
1153 case kSecTrustSettingsResultInvalid:
1154 default:
1155 break;
1156 }
1157 if(certInfo->isSelfSigned(avoidVerify)) {
1158 evInfo->StatusBits |= CSSM_CERT_STATUS_IS_ROOT;
1159 }
1160 if(certInfo->ignoredError()) {
1161 evInfo->StatusBits |= CSSM_CERT_STATUS_TRUST_SETTINGS_IGNORED_ERROR;
1162 }
1163 unsigned numCodes = certInfo->numStatusCodes();
1164 evInfo->NumStatusCodes = numCodes;
1165 /*
1166 * Always alloc StatusCodes array with one more element than the actual
1167 * number of codes, so we can stash CrlReason at the end of the array.
1168 */
1169 evInfo->StatusCodes = (CSSM_RETURN *)mAlloc.calloc(numCodes+1,
1170 sizeof(CSSM_RETURN));
1171 for(unsigned j=0; j<numCodes; j++) {
1172 evInfo->StatusCodes[j] = (certInfo->statusCodes())[j];
1173 }
1174 evInfo->StatusCodes[numCodes] = (CSSM_RETURN)certInfo->crlReason();
1175 if(evInfo->StatusBits & (CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST |
1176 CSSM_CERT_STATUS_TRUST_SETTINGS_DENY |
1177 CSSM_CERT_STATUS_TRUST_SETTINGS_IGNORED_ERROR)) {
1178 /* Something noteworthy happened involving TrustSettings */
1179 uint32 whichDomain = 0;
1180 switch(certInfo->trustSettingsDomain()) {
1181 case kSecTrustSettingsDomainUser:
1182 whichDomain = CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_USER;
1183 break;
1184 case kSecTrustSettingsDomainAdmin:
1185 whichDomain = CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_ADMIN;
1186 break;
1187 case kSecTrustSettingsDomainSystem:
1188 whichDomain = CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_SYSTEM;
1189 break;
1190 }
1191 evInfo->StatusBits |= whichDomain;
1192 }
1193 evInfo->Index = certInfo->index();
1194 evInfo->DlDbHandle = certInfo->dlDbHandle();
1195 evInfo->UniqueRecord = certInfo->uniqueRecord();
1196 }
1197 return infoArray;
1198 }
1199
1200 /* Given a status for basic construction of a cert group and a status
1201 * of (optional) policy verification, plus the implicit notBefore/notAfter
1202 * status in the certs, calculate a global return code. This just
1203 * encapsulates a policy for CertGroupConstruct and CertGroupVerify.
1204 */
1205 CSSM_RETURN TPCertGroup::getReturnCode(
1206 CSSM_RETURN constructStatus,
1207 CSSM_RETURN policyStatus,
1208 CSSM_APPLE_TP_ACTION_FLAGS actionFlags)
1209 {
1210 if(constructStatus) {
1211 /* CSSMERR_TP_NOT_TRUSTED, CSSMERR_TP_INVALID_ANCHOR_CERT, gross errors */
1212 return constructStatus;
1213 }
1214
1215 bool expired = false;
1216 bool postdated = false;
1217 bool allowExpiredRoot = (actionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT) ?
1218 true : false;
1219 bool allowExpired = (actionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED) ? true : false;
1220 bool allowPostdated = allowExpired; // flag overrides any temporal invalidity
1221 bool requireRevPerCert = (actionFlags & CSSM_TP_ACTION_REQUIRE_REV_PER_CERT) ?
1222 true : false;
1223
1224 /* check for expired, not valid yet */
1225 for(unsigned i=0; i<mNumCerts; i++) {
1226 TPCertInfo *ci = mCertInfo[i];
1227 /*
1228 * Note avoidVerify = true for isSelfSigned(); if it were appropriate to
1229 * verify the signature, that would have happened in
1230 * buildCssmEvidenceInfo() at the latest.
1231 */
1232 if(ci->isExpired() &&
1233 !(allowExpiredRoot && ci->isSelfSigned(true)) && // allowed globally
1234 ci->isStatusFatal(CSSMERR_TP_CERT_EXPIRED)) { // allowed for this cert
1235 expired = true;
1236 }
1237 if(ci->isNotValidYet() &&
1238 ci->isStatusFatal(CSSMERR_TP_CERT_NOT_VALID_YET)) {
1239 postdated = true;
1240 }
1241 }
1242 if(expired && !allowExpired) {
1243 return CSSMERR_TP_CERT_EXPIRED;
1244 }
1245 if(postdated && !allowPostdated) {
1246 return CSSMERR_TP_CERT_NOT_VALID_YET;
1247 }
1248
1249 /* Check for missing revocation check */
1250 if(requireRevPerCert) {
1251 for(unsigned i=0; i<mNumCerts; i++) {
1252 TPCertInfo *ci = mCertInfo[i];
1253 if(ci->isSelfSigned(true)) {
1254 /* revocation check meaningless for a root cert */
1255 tpDebug("getReturnCode: ignoring revocation for self-signed cert %d", i);
1256 continue;
1257 }
1258 if(!ci->revokeCheckGood() &&
1259 ci->isStatusFatal(CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK)) {
1260 tpDebug("getReturnCode: FATAL: revocation check incomplete for cert %d", i);
1261 return CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK;
1262 }
1263 #ifndef NDEBUG
1264 else {
1265 tpDebug("getReturnCode: revocation check %s for cert %d",
1266 (ci->revokeCheckGood()) ? "GOOD" : "OK", i);
1267 }
1268 #endif
1269 }
1270 }
1271 return policyStatus;
1272 }
1273
1274 /* set all TPCertInfo.mUsed flags false */
1275 void TPCertGroup::setAllUnused()
1276 {
1277 for(unsigned dex=0; dex<mNumCerts; dex++) {
1278 mCertInfo[dex]->used(false);
1279 }
1280 }
1281
1282 /*
1283 * See if the specified error status is allowed (return true) or
1284 * fatal (return false) per each cert's mAllowedErrs[]. Returns
1285 * true if any cert returns false for its isStatusFatal() call.
1286 * The list of errors which can apply to cert-chain-wide allowedErrors
1287 * is right here; if the incoming error is not in that list, we
1288 * return false. If the incoming error code is CSSM_OK we return
1289 * true as a convenience for our callers.
1290 */
1291 bool TPCertGroup::isAllowedError(
1292 CSSM_RETURN code)
1293 {
1294 switch(code) {
1295 case CSSM_OK:
1296 return true;
1297 case CSSMERR_TP_NOT_TRUSTED:
1298 case CSSMERR_TP_INVALID_ANCHOR_CERT:
1299 case CSSMERR_TP_VERIFY_ACTION_FAILED:
1300 case CSSMERR_TP_INVALID_CERT_AUTHORITY:
1301 case CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH:
1302 case CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH:
1303 /* continue processing these candidates */
1304 break;
1305 default:
1306 /* not a candidate for cert-chain-wide allowedErrors */
1307 return false;
1308 }
1309
1310 for(unsigned dex=0; dex<mNumCerts; dex++) {
1311 if(!mCertInfo[dex]->isStatusFatal(code)) {
1312 tpTrustSettingsDbg("TPCertGroup::isAllowedError: allowing for cert %u",
1313 dex);
1314 return true;
1315 }
1316 }
1317
1318 /* every cert thought this was fatal; it is. */
1319 return false;
1320 }
1321
1322 /*
1323 * Determine if we already have the specified cert in this group.
1324 */
1325 bool TPCertGroup::isInGroup(TPCertInfo &certInfo)
1326 {
1327 for(unsigned dex=0; dex<mNumCerts; dex++) {
1328 if(tpCompareCssmData(certInfo.itemData(), mCertInfo[dex]->itemData())) {
1329 return true;
1330 }
1331 }
1332 return false;
1333 }
1334
1335 /*
1336 * Encode issuing certs in this group as a PEM-encoded data blob.
1337 * Caller must free.
1338 */
1339 void TPCertGroup::encodeIssuers(CSSM_DATA &issuers)
1340 {
1341 /* FIXME: probably want to rewrite this using pemEncode() from libsecurity_cdsa_utils,
1342 * since use of Sec* APIs from this layer violates the API reentrancy contract.
1343 */
1344 issuers.Data = NULL;
1345 issuers.Length = 0;
1346 CFMutableArrayRef certArray = CFArrayCreateMutable(kCFAllocatorDefault,
1347 0, &kCFTypeArrayCallBacks);
1348 if(!certArray) {
1349 return;
1350 }
1351 for(unsigned certDex=0; certDex<mNumCerts; certDex++) {
1352 TPCertInfo *certInfo = certAtIndex(certDex);
1353 if(!certDex && mNumCerts > 1) {
1354 continue; /* don't need the leaf */
1355 }
1356 CSSM_DATA *cssmData = (CSSM_DATA*)((certInfo) ? certInfo->itemData() : NULL);
1357 if(!cssmData || !cssmData->Data || !cssmData->Length) {
1358 continue;
1359 }
1360 CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
1361 (const UInt8 *)cssmData->Data, cssmData->Length,
1362 kCFAllocatorNull);
1363 if(!dataRef) {
1364 continue;
1365 }
1366 SecCertificateRef certRef = SecCertificateCreateWithData(kCFAllocatorDefault,
1367 dataRef);
1368 if(!certRef) {
1369 CFRelease(dataRef);
1370 continue;
1371 }
1372 CFArrayAppendValue(certArray, certRef);
1373 CFRelease(certRef);
1374 CFRelease(dataRef);
1375 }
1376 CFDataRef exportedPEMData = NULL;
1377 OSStatus status = SecItemExport(certArray,
1378 kSecFormatPEMSequence,
1379 kSecItemPemArmour,
1380 NULL,
1381 &exportedPEMData);
1382 CFRelease(certArray);
1383
1384 if(!status) {
1385 uint8 *dataPtr = (uint8*)CFDataGetBytePtr(exportedPEMData);
1386 size_t dataLen = CFDataGetLength(exportedPEMData);
1387 issuers.Data = (uint8*)malloc(dataLen);
1388 memmove(issuers.Data, dataPtr, dataLen);
1389 issuers.Length = dataLen;
1390 CFRelease(exportedPEMData);
1391 }
1392 }
1393
1394 /*
1395 * Search unused incoming certs to find an issuer of specified cert or CRL.
1396 * WARNING this assumes a valid "used" state for all certs in this group.
1397 * If partialIssuerKey is true on return, caller must re-verify signature
1398 * of subject later when sufficient info is available.
1399 */
1400 TPCertInfo *TPCertGroup::findIssuerForCertOrCrl(
1401 const TPClItemInfo &subject,
1402 bool &partialIssuerKey)
1403 {
1404 partialIssuerKey = false;
1405 TPCertInfo *expiredIssuer = NULL;
1406 TPCertInfo *unmatchedKeyIDIssuer = NULL;
1407
1408 for(unsigned certDex=0; certDex<mNumCerts; certDex++) {
1409 TPCertInfo *certInfo = certAtIndex(certDex);
1410
1411 /* has this one already been used in this search? */
1412 if(certInfo->used()) {
1413 continue;
1414 }
1415
1416 /* subject/issuer names match? */
1417 if(certInfo->isIssuerOf(subject)) {
1418 /* yep, do a sig verify */
1419 tpVfyDebug("findIssuerForCertOrCrl issuer/subj match checking sig");
1420 CSSM_RETURN crtn = subject.verifyWithIssuer(certInfo);
1421 switch(crtn) {
1422 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
1423 /* issuer OK, check sig later */
1424 partialIssuerKey = true;
1425 /* and fall thru */
1426 case CSSM_OK:
1427 /*
1428 * Temporal validity check: if we're not already holding an expired
1429 * issuer, and this one's invalid, hold it and keep going.
1430 */
1431 if((crtn == CSSM_OK) && (expiredIssuer == NULL)) {
1432 if(certInfo->isExpired() || certInfo->isNotValidYet()) {
1433 tpDebug("findIssuerForCertOrCrl: holding expired cert %p",
1434 certInfo);
1435 expiredIssuer = certInfo;
1436 break;
1437 }
1438 }
1439 /* Authority key identifier check: if we can't match subject key id,
1440 * hold onto this cert and keep going.
1441 */
1442 if(unmatchedKeyIDIssuer == NULL) {
1443 if(!certInfo->isAuthorityKeyOf(subject)) {
1444 tpDebug("findIssuerForCertOrCrl: holding issuer without key id match %p",
1445 certInfo);
1446 unmatchedKeyIDIssuer = certInfo;
1447 break;
1448 }
1449 }
1450 /* YES */
1451 certInfo->used(true);
1452 return certInfo;
1453 default:
1454 /* just skip this one and keep looking */
1455 tpVfyDebug("findIssuerForCertOrCrl issuer/subj match BAD SIG");
1456 break;
1457 }
1458 } /* names match */
1459 }
1460 if(unmatchedKeyIDIssuer != NULL) {
1461 /* OK, we'll use this one (preferred over an expired issuer) */
1462 tpDbDebug("findIssuerForCertOrCrl: using issuer without key id match %p", unmatchedKeyIDIssuer);
1463 unmatchedKeyIDIssuer->used(true);
1464 return unmatchedKeyIDIssuer;
1465 }
1466 if(expiredIssuer != NULL) {
1467 /* OK, we'll use this one */
1468 tpDbDebug("findIssuerForCertOrCrl: using expired cert %p", expiredIssuer);
1469 expiredIssuer->used(true);
1470 return expiredIssuer;
1471 }
1472
1473 /* not found */
1474 return NULL;
1475 }
1476
1477 /*
1478 * Construct ordered, verified cert chain from a variety of inputs.
1479 * Time validity does not affect the function return or any status,
1480 * we always try to find a valid cert to replace an expired or
1481 * not-yet-valid cert if we can. Final temporal validity of each
1482 * cert must be checked by caller (it's stored in each TPCertInfo
1483 * we add to ourself during construction).
1484 *
1485 * Only possible error returns are:
1486 * CSSMERR_TP_CERTIFICATE_CANT_OPERATE : issuer cert was found with a partial
1487 * public key, rendering full verification impossible.
1488 * CSSMERR_TP_INVALID_CERT_AUTHORITY : issuer cert was found with a partial
1489 * public key and which failed to perform subsequent signature
1490 * verification.
1491 *
1492 * Other interesting status is returned via the verifiedToRoot and
1493 * verifiedToAnchor flags.
1494 *
1495 * NOTE: is it the caller's responsibility to call setAllUnused() for both
1496 * incoming cert groups (inCertGroup and gatheredCerts). We don't do that
1497 * here because we may call ourself recursively.
1498 */
1499 CSSM_RETURN TPCertGroup::buildCertGroup(
1500 const TPClItemInfo &subjectItem, // Cert or CRL
1501 TPCertGroup *inCertGroup, // optional
1502 const CSSM_DL_DB_LIST *dbList, // optional
1503 CSSM_CL_HANDLE clHand,
1504 CSSM_CSP_HANDLE cspHand,
1505 const char *verifyTime, // optional, for establishing
1506 // validity of new TPCertInfos
1507 /* trusted anchors, optional */
1508 /* FIXME - maybe this should be a TPCertGroup */
1509 uint32 numAnchorCerts,
1510 const CSSM_DATA *anchorCerts,
1511
1512 /*
1513 * Certs to be freed by caller (i.e., TPCertInfo which we allocate
1514 * as a result of using a cert from anchorCerts or dbList) are added
1515 * to this group.
1516 */
1517 TPCertGroup &certsToBeFreed,
1518
1519 /*
1520 * Other certificates gathered during the course of this operation,
1521 * currently consisting of certs fetched from DBs and from the net.
1522 * This is not used when called by AppleTPSession::CertGroupConstructPriv;
1523 * it's an optimization for the case when we're building a cert group
1524 * for TPCrlInfo::verifyWithContext - we avoid re-fetching certs from
1525 * the net which are needed to verify both the subject cert and a CRL.
1526 * We don't modify this TPCertGroup, we only use certs from it.
1527 */
1528 TPCertGroup *gatheredCerts,
1529
1530 /*
1531 * Indicates that subjectItem is a cert in this cert group.
1532 * If true, that cert will be tested for "root-ness", including
1533 * -- subject/issuer compare
1534 * -- signature self-verify
1535 * -- anchor compare
1536 */
1537 CSSM_BOOL subjectIsInGroup,
1538
1539 /*
1540 * CSSM_TP_ACTION_FETCH_CERT_FROM_NET,
1541 * CSSM_TP_ACTION_TRUST_SETTING,
1542 * CSSM_TP_ACTION_IMPLICIT_ANCHORS are interesting
1543 */
1544 CSSM_APPLE_TP_ACTION_FLAGS actionFlags,
1545
1546 /* CSSM_TP_ACTION_TRUST_SETTING parameters */
1547 const CSSM_OID *policyOid,
1548 const char *policyStr,
1549 uint32 policyStrLen,
1550 SecTrustSettingsKeyUsage leafKeyUse, // usage of *first* cert in chain
1551
1552 /* returned */
1553 CSSM_BOOL &verifiedToRoot, // end of chain self-verifies
1554 CSSM_BOOL &verifiedToAnchor, // end of chain in anchors
1555 CSSM_BOOL &verifiedViaTrustSettings) // chain ends per User Trust setting
1556 {
1557 const TPClItemInfo *thisSubject = &subjectItem;
1558 CSSM_RETURN crtn = CSSM_OK;
1559 TPCertInfo *issuerCert = NULL;
1560 unsigned certDex;
1561 TPCertInfo *anchorInfo = NULL;
1562 bool foundPartialIssuer = false;
1563 bool attemptNetworkFetch = false;
1564 CSSM_BOOL firstSubjectIsInGroup = subjectIsInGroup;
1565 TPCertInfo *endCert;
1566
1567 tpVfyDebug("buildCertGroup top");
1568
1569 /* possible expired root which we'll only use if we can't find
1570 * a better one */
1571 TPCertInfo *expiredRoot = NULL;
1572
1573 /* and the general case of an expired or not yet valid cert */
1574 TPCertInfo *expiredIssuer = NULL;
1575
1576 /* and the case of an issuer without a matching subject key id */
1577 TPCertInfo *unmatchedKeyIDIssuer = NULL;
1578
1579 /* and the case of a root that isn't trusted or an anchor */
1580 TPCertInfo *untrustedRoot = NULL;
1581
1582 verifiedToRoot = CSSM_FALSE;
1583 verifiedToAnchor = CSSM_FALSE;
1584 verifiedViaTrustSettings = CSSM_FALSE;
1585
1586 /*** main loop to seach inCertGroup and dbList ***
1587 *
1588 * Exit loop on:
1589 * -- find a root cert in the chain (self-signed)
1590 * -- find a non-root cert which is also in the anchors list
1591 * -- find a cert which is trusted per Trust Settings (if enabled)
1592 * -- memory error
1593 * -- or no more certs to add to chain.
1594 */
1595 for(;;) {
1596 /*
1597 * Top of loop: thisSubject is the item we're trying to verify.
1598 */
1599
1600 /* is thisSubject a root cert or listed in user trust list? */
1601 if(subjectIsInGroup) {
1602 TPCertInfo *subjCert = lastCert();
1603 assert(subjCert != NULL);
1604
1605 if(actionFlags & CSSM_TP_ACTION_TRUST_SETTINGS) {
1606 assert(policyOid != NULL);
1607
1608 /*
1609 * Figure out key usage. If this is a leaf cert, the caller - actually
1610 * the per-policy code - inferred the usage. Else it could be for
1611 * verifying a cert or a CRL.
1612 *
1613 * We want to avoid multiple calls to the effective portion of
1614 * evaluateTrustSettings(), but a CA cert could be usable for only
1615 * signing certs and not CRLs. Thus we're evaluating a CA cert,
1616 * try to evaluate for signing certs *and* CRLs in case we come
1617 * this way again later when performing CRL verification. If that
1618 * fails, then retry with just cert signing.
1619 */
1620 SecTrustSettingsKeyUsage localKeyUse;
1621 bool doRetry = false;
1622 if(subjCert == firstCert()) {
1623 /* leaf - use caller's spec */
1624 localKeyUse = leafKeyUse;
1625 /* FIXME - add in CRL if this is cert checking? */
1626 }
1627 else {
1628 localKeyUse = kSecTrustSettingsKeyUseSignCert | kSecTrustSettingsKeyUseSignRevocation;
1629 /* and if necessary */
1630 doRetry = true;
1631 }
1632 /* this lets us avoid searching for the same thing twice when there
1633 * is in fact no entry for it */
1634 bool foundEntry = false;
1635 bool trustSettingsFound = false;
1636 OSStatus ortn = subjCert->evaluateTrustSettings(*policyOid,
1637 policyStr, policyStrLen, localKeyUse, &trustSettingsFound, &foundEntry);
1638 if(ortn) {
1639 /* this is only a dire error */
1640 crtn = ortn;
1641 goto final_out;
1642 }
1643 if(!trustSettingsFound && foundEntry && doRetry) {
1644 tpTrustSettingsDbg("buildCertGroup: retrying evaluateTrustSettings with Cert only");
1645 ortn = subjCert->evaluateTrustSettings(*policyOid,
1646 policyStr, policyStrLen, kSecTrustSettingsKeyUseSignCert,
1647 &trustSettingsFound, &foundEntry);
1648 if(ortn) {
1649 crtn = ortn;
1650 goto final_out;
1651 }
1652 }
1653 if(trustSettingsFound) {
1654 switch(subjCert->trustSettingsResult()) {
1655 case kSecTrustSettingsResultInvalid:
1656 /* should not happen... */
1657 assert(0);
1658 crtn = CSSMERR_TP_INTERNAL_ERROR;
1659 break;
1660 case kSecTrustSettingsResultTrustRoot:
1661 case kSecTrustSettingsResultTrustAsRoot:
1662 tpTrustSettingsDbg("Trust[As]Root found");
1663 crtn = CSSM_OK;
1664 break;
1665 case kSecTrustSettingsResultDeny:
1666 tpTrustSettingsDbg("TrustResultDeny found");
1667 crtn = CSSMERR_APPLETP_TRUST_SETTING_DENY;
1668 break;
1669 case kSecTrustSettingsResultUnspecified:
1670 /* special case here: this means "keep going, we don't trust or
1671 * distrust this cert". Typically used to express allowed errors
1672 * only.
1673 */
1674 tpTrustSettingsDbg("TrustResultUnspecified found");
1675 goto post_trust_setting;
1676 default:
1677 tpTrustSettingsDbg("Unknown TrustResult (%d)",
1678 (int)subjCert->trustSettingsResult());
1679 crtn = CSSMERR_TP_INTERNAL_ERROR;
1680 break;
1681 }
1682 /* cleanup partial key processing */
1683 verifiedViaTrustSettings = CSSM_TRUE;
1684 goto final_out;
1685 }
1686 } /* CSSM_TP_ACTION_TRUST_SETTING */
1687
1688 post_trust_setting:
1689 /*
1690 * If this cert is in the provided anchors list,
1691 * we can stop building the chain at this point.
1692 *
1693 * If this cert is a leaf, the chain ends in an anchor, but if it's
1694 * also temporally invalid, we can't do anything further. However,
1695 * if it's not a leaf, then we need to roll back the chain to a
1696 * point just before this cert, so Case 1 will subsequently find
1697 * the anchor (and handle the anchor correctly if it's expired.)
1698 */
1699 if(numAnchorCerts && anchorCerts) {
1700 bool foundAnchor = false;
1701 for(certDex=0; certDex<numAnchorCerts; certDex++) {
1702 if(tp_CompareCerts(subjCert->itemData(), &anchorCerts[certDex])) {
1703 foundAnchor = true;
1704 /* if it's not the leaf, remove it from the outgoing cert group. */
1705 if(!firstSubjectIsInGroup || (mNumCerts > 1)) {
1706 if(mNumCerts) {
1707 /* roll back to previous cert */
1708 mNumCerts--;
1709 }
1710 if(mNumCerts == 0) {
1711 /* roll back to caller's initial condition */
1712 thisSubject = &subjectItem;
1713 }
1714 else {
1715 thisSubject = lastCert();
1716 }
1717 tpAnchorDebug("buildCertGroup: CA cert in input AND anchors");
1718 } /* not leaf */
1719 else {
1720 if(subjCert->isExpired() || subjCert->isNotValidYet()) {
1721 crtn = CSSM_CERT_STATUS_EXPIRED;
1722 } else {
1723 crtn = CSSM_OK;
1724 }
1725 subjCert->isAnchor(true);
1726 verifiedToAnchor = CSSM_TRUE;
1727 tpAnchorDebug("buildCertGroup: leaf cert in input AND anchors");
1728 } /* leaf */
1729 if(subjCert->isSelfSigned()) {
1730 verifiedToRoot = CSSM_TRUE;
1731 }
1732 break; /* out of anchor-checking loop */
1733 }
1734 }
1735 if(foundAnchor) {
1736 break; /* out of main loop */
1737 }
1738 }
1739
1740 if(subjCert->isSelfSigned()) {
1741 /*
1742 * Special case if this root is temporally invalid (and it's not
1743 * the leaf): remove it from the outgoing cert group, save it,
1744 * and proceed, looking another (good) root in anchors.
1745 * There's no way we'll find another good one in this loop.
1746 */
1747 if((subjCert->isExpired() || subjCert->isNotValidYet()) &&
1748 (!firstSubjectIsInGroup || (mNumCerts > 1))) {
1749 verifiedToRoot = CSSM_TRUE;
1750 tpDebug("buildCertGroup: EXPIRED ROOT %p, looking for good one", subjCert);
1751 expiredRoot = subjCert;
1752 if(mNumCerts) {
1753 /* roll back to previous cert */
1754 mNumCerts--;
1755 }
1756 if(mNumCerts == 0) {
1757 /* roll back to caller's initial condition */
1758 thisSubject = &subjectItem;
1759 }
1760 else {
1761 thisSubject = lastCert();
1762 }
1763 break; /* out of main loop */
1764 }
1765 /*
1766 * If any root is considered an anchor, we don't need to look
1767 * for a better chain that ends in an anchor or trusted root.
1768 */
1769 if(actionFlags & CSSM_TP_ACTION_IMPLICIT_ANCHORS) {
1770 verifiedToRoot = CSSM_TRUE;
1771 break; /*out of main loop */
1772 }
1773 /*
1774 * The root we have is neither trusted nor an anchor. Continue in
1775 * the loop to look for a better chain if this is not a leaf.
1776 */
1777 if(!firstSubjectIsInGroup || (mNumCerts > 1)) {
1778 tpDebug("buildCertGroup: UNTRUSTED ROOT %p, looking for better chain", subjCert);
1779 untrustedRoot = subjCert;
1780 if(mNumCerts) {
1781 /* roll back to previous cert */
1782 mNumCerts--;
1783 }
1784 if(mNumCerts == 0) {
1785 /* roll back to caller's initial condition */
1786 thisSubject = &subjectItem;
1787 }
1788 else {
1789 thisSubject = lastCert();
1790 }
1791 }
1792 else {
1793 /* the leaf is a root */
1794 break; /* out of main loop */
1795 }
1796 } /* root */
1797 } /* subjectIsInGroup */
1798
1799 /*
1800 * Search unused incoming certs to find an issuer.
1801 * Both cert groups are optional.
1802 * We'll add issuer to outCertGroup below.
1803 * If we find a cert that's expired or not yet valid, we hold on to it
1804 * and look for a better one. If we don't find it here we drop back to the
1805 * expired one at the end of the loop. If that expired cert is a root
1806 * cert, we'll use the expiredRoot mechanism (see above) to roll back and
1807 * see if we can find a good root in the incoming anchors.
1808 */
1809 if(inCertGroup != NULL) {
1810 bool partial = false;
1811 issuerCert = inCertGroup->findIssuerForCertOrCrl(*thisSubject,
1812 partial);
1813 if(issuerCert) {
1814 issuerCert->isFromInputCerts(true);
1815 if(partial) {
1816 /* deal with this later */
1817 foundPartialIssuer = true;
1818 tpDebug("buildCertGroup: PARTIAL Cert FOUND in inCertGroup");
1819 }
1820 else {
1821 tpDebug("buildCertGroup: Cert FOUND in inCertGroup");
1822 }
1823 }
1824 }
1825
1826 if(issuerCert != NULL) {
1827 bool stashedIssuer = false;
1828 /* Check whether candidate issuer is the same as unstrustedRoot */
1829 if((untrustedRoot != NULL) && tp_CompareCerts(issuerCert->itemData(), untrustedRoot->itemData())) {
1830 /* already stashed */
1831 stashedIssuer = true;
1832 }
1833 /* Check whether candidate issuer is expired or not yet valid */
1834 if(issuerCert->isExpired() || issuerCert->isNotValidYet()) {
1835 if(expiredIssuer == NULL) {
1836 tpDebug("buildCertGroup: saving expired cert %p (1)", issuerCert);
1837 expiredIssuer = issuerCert;
1838 stashedIssuer = true;
1839 }
1840 /* else we already have an expired issuer candidate */
1841 }
1842 else {
1843 /* unconditionally done with possible expiredIssuer */
1844 #ifndef NDEBUG
1845 if(expiredIssuer != NULL) {
1846 tpDebug("buildCertGroup: DISCARDING expired cert %p (1)", expiredIssuer);
1847 }
1848 #endif
1849 expiredIssuer = NULL;
1850 }
1851 /* Check whether candidate issuer failed to match authority key id in thisSubject */
1852 if(!issuerCert->isAuthorityKeyOf(*thisSubject)) {
1853 if(unmatchedKeyIDIssuer == NULL) {
1854 tpDebug("buildCertGroup: saving unmatched key id issuer %p (1)", issuerCert);
1855 unmatchedKeyIDIssuer = issuerCert;
1856 stashedIssuer = true;
1857 }
1858 /* else we already have an unmatched key id issuer candidate */
1859 }
1860 else {
1861 /* unconditionally done with possible unmatchedKeyIDIssuer */
1862 #ifndef NDEBUG
1863 if(unmatchedKeyIDIssuer != NULL) {
1864 tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (1)", unmatchedKeyIDIssuer);
1865 }
1866 #endif
1867 unmatchedKeyIDIssuer = NULL;
1868 }
1869 if(stashedIssuer) {
1870 issuerCert = NULL; /* keep looking */
1871 }
1872 }
1873
1874 if((issuerCert == NULL) && (gatheredCerts != NULL)) {
1875 bool partial = false;
1876 issuerCert = gatheredCerts->findIssuerForCertOrCrl(*thisSubject,
1877 partial);
1878 if(issuerCert) {
1879 if(partial) {
1880 /* deal with this later */
1881 foundPartialIssuer = true;
1882 tpDebug("buildCertGroup: PARTIAL Cert FOUND in gatheredCerts");
1883 }
1884 else {
1885 tpDebug("buildCertGroup: Cert FOUND in gatheredCerts");
1886 }
1887 }
1888 }
1889
1890 if(issuerCert != NULL) {
1891 bool stashedIssuer = false;
1892 /* Check whether candidate issuer is the same as untrustedRoot */
1893 if((untrustedRoot != NULL) && tp_CompareCerts(issuerCert->itemData(), untrustedRoot->itemData())) {
1894 /* already stashed */
1895 stashedIssuer = true;
1896 }
1897 /* Check whether candidate issuer is expired or not yet valid */
1898 if(issuerCert->isExpired() || issuerCert->isNotValidYet()) {
1899 if(expiredIssuer == NULL) {
1900 tpDebug("buildCertGroup: saving expired cert %p (2)", issuerCert);
1901 expiredIssuer = issuerCert;
1902 stashedIssuer = true;
1903 }
1904 /* else we already have an expired issuer candidate */
1905 }
1906 else {
1907 /* unconditionally done with possible expiredIssuer */
1908 #ifndef NDEBUG
1909 if(expiredIssuer != NULL) {
1910 tpDebug("buildCertGroup: DISCARDING expired cert %p (2)", expiredIssuer);
1911 }
1912 #endif
1913 expiredIssuer = NULL;
1914 }
1915 /* Check whether candidate issuer failed to match authority key id in thisSubject */
1916 if(!issuerCert->isAuthorityKeyOf(*thisSubject)) {
1917 if(unmatchedKeyIDIssuer == NULL) {
1918 tpDebug("buildCertGroup: saving unmatched key id issuer %p (2)", issuerCert);
1919 unmatchedKeyIDIssuer = issuerCert;
1920 stashedIssuer = true;
1921 }
1922 /* else we already have an unmatched key id issuer candidate */
1923 }
1924 else {
1925 /* unconditionally done with possible unmatchedKeyIdIssuer */
1926 #ifndef NDEBUG
1927 if(unmatchedKeyIDIssuer != NULL) {
1928 tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (2)", unmatchedKeyIDIssuer);
1929 }
1930 #endif
1931 unmatchedKeyIDIssuer = NULL;
1932 }
1933 if(stashedIssuer) {
1934 issuerCert = NULL; /* keep looking */
1935 }
1936 }
1937
1938 /*
1939 * If we found a candidate issuer in input or gathered certs, check whether it
1940 * might be a cross-signed intermediate that can be replaced with an anchor.
1941 */
1942 if(issuerCert != NULL && !issuerCert->isSelfSigned() && (untrustedRoot == NULL)) {
1943 bool partial = false;
1944 TPCertInfo *possibleAnchorCert = NULL;
1945 try {
1946 possibleAnchorCert = tpDbFindIssuerCert(mAlloc,
1947 clHand,
1948 cspHand,
1949 thisSubject,
1950 dbList,
1951 verifyTime,
1952 partial,
1953 untrustedRoot);
1954 }
1955 catch (...) {}
1956
1957 if(possibleAnchorCert != NULL) {
1958 if(possibleAnchorCert->isSelfSigned()) {
1959 /*
1960 * We found a better replacement issuer, so use it.
1961 * note that we don't need to free the old issuerCert first as it
1962 * comes from inCertGroup or gatheredCerts (not from dbList).
1963 * However, code from this point on cannot assume the same thing.
1964 */
1965 tpDebug("buildCertGroup: replacement anchor for issuer FOUND in dbList");
1966 /* mark non-root candidate as unused since we won't use it */
1967 issuerCert->used(false);
1968 issuerCert = possibleAnchorCert;
1969
1970 /* Caller must free, since this cert came from a DLDB */
1971 certsToBeFreed.appendCert(issuerCert);
1972 if(partial) {
1973 /* deal with this later */
1974 foundPartialIssuer = true;
1975 }
1976
1977 /* unconditionally done with possible expiredIssuer */
1978 #ifndef NDEBUG
1979 if(expiredIssuer != NULL) {
1980 tpDebug("buildCertGroup: DISCARDING expired cert %p (3)", expiredIssuer);
1981 }
1982 #endif
1983 expiredIssuer = NULL;
1984 /* unconditionally done with possible unmatchedKeyIDIssuer */
1985 #ifndef NDEBUG
1986 if(unmatchedKeyIDIssuer != NULL) {
1987 tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (3)", unmatchedKeyIDIssuer);
1988 }
1989 #endif
1990 unmatchedKeyIDIssuer = NULL;
1991 }
1992 else {
1993 possibleAnchorCert->freeUniqueRecord();
1994 delete possibleAnchorCert;
1995 possibleAnchorCert = NULL;
1996 }
1997 }
1998 }
1999
2000 if((issuerCert == NULL) && (dbList != NULL)) {
2001 /* Issuer not in incoming cert group or gathered certs. Search DBList. */
2002 bool partial = false;
2003 try {
2004 issuerCert = tpDbFindIssuerCert(mAlloc,
2005 clHand,
2006 cspHand,
2007 thisSubject,
2008 dbList,
2009 verifyTime,
2010 partial,
2011 untrustedRoot);
2012 }
2013 catch (...) {}
2014
2015 if(issuerCert) {
2016 /* unconditionally done with possible expiredIssuer */
2017 #ifndef NDEBUG
2018 if(expiredIssuer != NULL) {
2019 tpDebug("buildCertGroup: DISCARDING expired cert %p (4)", expiredIssuer);
2020 }
2021 #endif
2022 expiredIssuer = NULL;
2023 /* unconditionally done with possible unmatchedKeyIDIssuer */
2024 #ifndef NDEBUG
2025 if(unmatchedKeyIDIssuer != NULL) {
2026 tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (4)", unmatchedKeyIDIssuer);
2027 }
2028 #endif
2029 unmatchedKeyIDIssuer = NULL;
2030
2031 /*
2032 * Handle Radar 4566041, endless loop of cross-signed certs.
2033 * This can only happen when fetching certs from a DLDB or
2034 * from the net; we prevent that from happening when the certs
2035 * are in inCertGroup or gatheredCerts by keeping track of those
2036 * certs' mUsed state.
2037 * Also handle Radar 23734683, endless loop of untrusted roots.
2038 */
2039 if(isInGroup(*issuerCert) || (gatheredCerts && gatheredCerts->isInGroup(*issuerCert))) {
2040 tpDebug("buildCertGroup: Multiple instances of cert");
2041 delete issuerCert;
2042 issuerCert = NULL;
2043 }
2044 else {
2045 /* caller must free */
2046 certsToBeFreed.appendCert(issuerCert);
2047 if(partial) {
2048 /* deal with this later */
2049 foundPartialIssuer = true;
2050 tpDebug("buildCertGroup: PARTIAL Cert FOUND in dbList");
2051 }
2052 else {
2053 tpDebug("buildCertGroup: Cert FOUND in dbList");
2054 }
2055 }
2056 }
2057 } /* searching DLDB list */
2058
2059 /*
2060 * Note: we don't handle an expired cert returned from tpDbFindIssuerCert()
2061 * in any special way like we do with findIssuerForCertOrCrl().
2062 * tpDbFindIssuerCert() does its best to give us a temporally valid cert; if
2063 * it returns an expired cert (or, if findIssuerForCertOrCrl() gave us an
2064 * expired cert and tpDbFindIssuerCert() could not do any better), that's all
2065 * we have to work with at this point. We'll go back to the top of the loop
2066 * and apply trust settings if enabled; if an expired cert is trusted per
2067 * Trust Settings, we're done. (Note that anchors are fetched from a DLDB
2068 * when Trust Settings are enabled, so even if two roots with the same key
2069 * and subject name are in DLDBs, and one of them is expired, we'll have the
2070 * good one at this time because of tpDbFindIssuerCert()'s ability to find
2071 * the best cert.)
2072 *
2073 * If Trust Settings are not enabled, and we have an expired root at this
2074 * point, the expiredRoot mechanism is used to roll back and search for
2075 * an anchor that verifies the last good cert.
2076 */
2077 if((issuerCert == NULL) && /* tpDbFindIssuerCert() hasn't found one and
2078 * we don't have a good one */
2079 (untrustedRoot != NULL)) { /* but we have an untrusted root available*/
2080 /*
2081 * We couldn't find a better issuer, so end loop. In Case 1, we'll look for
2082 * an alternate anchor issuer. If we can't find an anchor, we'll end up using
2083 * the untrusted root.
2084 */
2085 break; /* from main loop */
2086 }
2087
2088 if((issuerCert == NULL) && /* tpDbFindIssuerCert() hasn't found one and
2089 * we don't have a good one */
2090 (unmatchedKeyIDIssuer != NULL)) { /* but we have an unmatched keyID candidate */
2091 /*
2092 * OK, we'll take the unmatched key id issuer.
2093 * Note we don't have to free unmatchedKeyIDIssuer if we found a good one since
2094 * unmatchedKeyIDIssuer can only come from inCertGroup or gatheredCerts (not from
2095 * dbList).
2096 */
2097 tpDebug("buildCertGroup: USING unmatched key id issuer %p", unmatchedKeyIDIssuer);
2098 issuerCert = unmatchedKeyIDIssuer;
2099 unmatchedKeyIDIssuer = NULL;
2100 }
2101 if((issuerCert == NULL) && /* tpDbFindIssuerCert() hasn't found one and
2102 * we don't have a good one */
2103 (expiredIssuer != NULL)) { /* but we have an expired candidate */
2104 /*
2105 * OK, we'll take the expired issuer.
2106 * Note we don't have to free expiredIssuer if we found a good one since
2107 * expiredIssuer can only come from inCertGroup or gatheredCerts (not from
2108 * dbList).
2109 */
2110 tpDebug("buildCertGroup: USING expired cert %p", expiredIssuer);
2111 issuerCert = expiredIssuer;
2112 expiredIssuer = NULL;
2113 }
2114 if(issuerCert == NULL) {
2115 /* end of search, broken chain */
2116 break;
2117 }
2118
2119 /*
2120 * One way or the other, we've found a cert which verifies subjectCert.
2121 * Add the issuer to outCertGroup and make it the new thisSubject for
2122 * the next pass.
2123 */
2124 appendCert(issuerCert);
2125 thisSubject = issuerCert;
2126 subjectIsInGroup = CSSM_TRUE;
2127 issuerCert = NULL;
2128 /*
2129 * We've found a (potentially) better chain, so discard the untrusted root.
2130 * Note we don't have to free untrustedRoot because it either:
2131 * -came from inCertGroup or gatherCerts; or
2132 * -came from the dbList and was already added to certToBeFreed.
2133 */
2134 untrustedRoot = NULL;
2135 } /* main loop */
2136
2137 /*
2138 * This can be NULL if we're evaluating a CRL (and we haven't
2139 * gotten very far).
2140 */
2141 endCert = lastCert();
2142
2143 /*
2144 * This, on the other hand, is always valid. It could be a CRL.
2145 */
2146 assert(thisSubject != NULL);
2147
2148 if( (actionFlags & CSSM_TP_ACTION_IMPLICIT_ANCHORS) &&
2149 ( (endCert && endCert->isSelfSigned()) || expiredRoot) ) {
2150 /*
2151 * Caller will be satisfied with this; skip further anchor processing.
2152 */
2153 tpAnchorDebug("buildCertGroup: found IMPLICIT anchor");
2154 goto post_anchor;
2155 }
2156 if(numAnchorCerts == 0) {
2157 /* we're probably done */
2158 goto post_anchor;
2159 }
2160 assert(anchorCerts != NULL);
2161
2162 /*** anchor cert handling ***/
2163
2164 /*
2165 * Case 1: If thisSubject is not a root cert, try to validate with incoming anchor certs.
2166 */
2167 expiredIssuer = NULL;
2168 if(!(endCert && endCert->isSelfSigned())) {
2169 for(certDex=0; certDex<numAnchorCerts; certDex++) {
2170
2171 try {
2172 anchorInfo = new TPCertInfo(clHand,
2173 cspHand,
2174 &anchorCerts[certDex],
2175 TIC_NoCopy,
2176 verifyTime);
2177 }
2178 catch(...) {
2179 /* bad anchor cert - ignore it */
2180 anchorInfo = NULL;
2181 continue;
2182 }
2183
2184 /*
2185 * We must subsequently delete anchorInfo one way or the other.
2186 * If we add it to tpCertGroup, we also add it to certsToBeFreed.
2187 * Otherwise we delete it.
2188 */
2189 if(!anchorInfo->isIssuerOf(*thisSubject)) {
2190 /* not this anchor */
2191 tpAnchorDebug("buildCertGroup anchor not issuer");
2192 delete anchorInfo;
2193 anchorInfo = NULL;
2194 continue;
2195 }
2196
2197 crtn = thisSubject->verifyWithIssuer(anchorInfo);
2198
2199 if(crtn == CSSM_OK) {
2200 if(anchorInfo->isExpired() || anchorInfo->isNotValidYet()) {
2201 if(expiredIssuer == NULL) {
2202 /*
2203 * Hang on to this one; keep looking for a better one.
2204 */
2205 tpDebug("buildCertGroup: saving expired anchor %p", anchorInfo);
2206 expiredIssuer = anchorInfo;
2207 /* flag this condition for the switch below */
2208 crtn = CSSM_CERT_STATUS_EXPIRED;
2209 expiredIssuer->isAnchor(true);
2210 assert(!anchorInfo->isFromInputCerts());
2211 expiredIssuer->index(certDex);
2212 certsToBeFreed.appendCert(expiredIssuer);
2213 }
2214 /* else we already have an expired candidate anchor */
2215 }
2216 else {
2217 /*
2218 * Done with possible expiredIssuer. We don't delete it, since we already added
2219 * it to certsToBeFreed, above.
2220 */
2221 if(expiredIssuer != NULL) {
2222 tpDebug("buildCertGroup: DISCARDING expired anchor %p", expiredIssuer);
2223 expiredIssuer = NULL;
2224 }
2225 }
2226 }
2227
2228 switch(crtn) {
2229 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
2230 /*
2231 * A bit of a corner case. Found an issuer in AnchorCerts, but
2232 * we can't do a signature verify since the issuer has a partial
2233 * public key. Proceed but return
2234 * CSSMERR_TP_CERTIFICATE_CANT_OPERATE.
2235 */
2236 if(anchorInfo->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE)) {
2237 foundPartialIssuer = true;
2238 crtn = CSSMERR_TP_CERTIFICATE_CANT_OPERATE;
2239 }
2240 else {
2241 /* ignore */
2242 crtn = CSSM_OK;
2243 }
2244 /* drop thru */
2245 case CSSM_OK:
2246 /* A fully successful return. */
2247 verifiedToAnchor = CSSM_TRUE;
2248 if(anchorInfo->isSelfSigned()) {
2249 verifiedToRoot = CSSM_TRUE;
2250 }
2251
2252 /*
2253 * Add this anchor cert to the output group
2254 * and to certsToBeFreed.
2255 */
2256 appendCert(anchorInfo);
2257 anchorInfo->isAnchor(true);
2258 assert(!anchorInfo->isFromInputCerts());
2259 anchorInfo->index(certDex);
2260 certsToBeFreed.appendCert(anchorInfo);
2261 tpDebug("buildCertGroup: Cert FOUND by signer in AnchorList");
2262 tpAnchorDebug("buildCertGroup: Cert FOUND by signer in AnchorList");
2263 /* one more thing: partial public key processing needed? */
2264 if(foundPartialIssuer) {
2265 return verifyWithPartialKeys(subjectItem);
2266 }
2267 else {
2268 return crtn;
2269 }
2270
2271 default:
2272 /* continue to next anchor */
2273 if(crtn != CSSM_CERT_STATUS_EXPIRED) {
2274 /* Expired means we're saving it in expiredIssuer */
2275 tpVfyDebug("buildCertGroup found issuer in anchor, BAD SIG");
2276 delete anchorInfo;
2277 }
2278 anchorInfo = NULL;
2279 break;
2280 }
2281 } /* for each anchor */
2282 } /* thisSubject not a root cert */
2283
2284 /*
2285 * Case 2: Check whether endCert is present in anchor certs.
2286 *
2287 * Also used to validate an expiredRoot that we pulled off the chain in
2288 * hopes of finding something better (which, if we're here, we haven't done).
2289 *
2290 * Note that the main loop above did the actual root self-verify test.
2291 */
2292 if(endCert || expiredRoot) {
2293
2294 TPCertInfo *theRoot;
2295 if(expiredRoot) {
2296 /* this is NOT in our outgoing cert group (yet) */
2297 theRoot = expiredRoot;
2298 }
2299 else {
2300 theRoot = endCert;
2301 }
2302 /* see if that root cert is identical to one of the anchor certs */
2303 for(certDex=0; certDex<numAnchorCerts; certDex++) {
2304 if(tp_CompareCerts(theRoot->itemData(), &anchorCerts[certDex])) {
2305 /* one fully successful return */
2306 tpAnchorDebug("buildCertGroup: end cert in input AND anchors");
2307 verifiedToAnchor = CSSM_TRUE;
2308 theRoot->isAnchor(true);
2309 if(!theRoot->isFromInputCerts()) {
2310 /* Don't override index into input certs */
2311 theRoot->index(certDex);
2312 }
2313 if(expiredRoot) {
2314 /* verified to anchor but caller will see
2315 * CSSMERR_TP_CERT_EXPIRED */
2316 appendCert(expiredRoot);
2317 }
2318 /* one more thing: partial public key processing needed? */
2319 if(foundPartialIssuer) {
2320 return verifyWithPartialKeys(subjectItem);
2321 }
2322 else {
2323 return CSSM_OK;
2324 }
2325 }
2326 }
2327 tpAnchorDebug("buildCertGroup: end cert in input, NOT anchors");
2328
2329 if(!expiredRoot && endCert->isSelfSigned()) {
2330 /* verified to a root cert which is not an anchor */
2331 /* Generally maps to CSSMERR_TP_INVALID_ANCHOR_CERT by caller */
2332 /* one more thing: partial public key processing needed? */
2333 if(foundPartialIssuer) {
2334 return verifyWithPartialKeys(subjectItem);
2335 }
2336 else {
2337 return CSSM_OK;
2338 }
2339 }
2340 /* else try finding a good anchor */
2341 }
2342
2343 /* regardless of anchor search status... */
2344 crtn = CSSM_OK;
2345 if(!verifiedToAnchor && (expiredIssuer != NULL)) {
2346 /* expiredIssuer here is always an anchor */
2347 tpDebug("buildCertGroup: accepting expired anchor %p", expiredIssuer);
2348 appendCert(expiredIssuer);
2349 verifiedToAnchor = CSSM_TRUE;
2350 if(expiredIssuer->isSelfSigned()) {
2351 verifiedToRoot = CSSM_TRUE;
2352 }
2353 /* no matter what, we don't want these */
2354 expiredRoot = NULL;
2355 untrustedRoot = NULL;
2356 }
2357 post_anchor:
2358 if(untrustedRoot) {
2359 /*
2360 * Special case: untrustedRoot found, but no luck resolving the problem with
2361 * anchors. Go ahead and (re-)append the untrusted root and return
2362 */
2363 tpDebug("buildCertGroup: accepted UNTRUSTED root");
2364 appendCert(untrustedRoot);
2365 if(foundPartialIssuer) {
2366 return verifyWithPartialKeys(subjectItem);
2367 }
2368 else {
2369 return CSSM_OK;
2370 }
2371 }
2372 if(expiredRoot) {
2373 /*
2374 * One remaining special case: expiredRoot found in input certs, but
2375 * no luck resolving the problem with the anchors. Go ahead and (re-)append
2376 * the expired root and return.
2377 */
2378 tpDebug("buildCertGroup: accepting EXPIRED root");
2379 appendCert(expiredRoot);
2380 if(foundPartialIssuer) {
2381 return verifyWithPartialKeys(subjectItem);
2382 }
2383 else {
2384 return CSSM_OK;
2385 }
2386 }
2387
2388 /* If we get here, determine if fetching the issuer from the network
2389 * should be attempted: <rdar://6113890&7419584&7422356>
2390 */
2391 attemptNetworkFetch = (actionFlags & CSSM_TP_ACTION_FETCH_CERT_FROM_NET);
2392 if( (!dbList || (dbList->NumHandles == 0)) &&
2393 (!anchorCerts || (numAnchorCerts == 0)) ) {
2394 /* DB list is empty *and* anchors are empty; there is no point in going
2395 * out to the network, since we cannot build a chain to a trusted root.
2396 * (This can occur when the caller wants to evaluate a single certificate
2397 * without trying to build the chain, e.g. to check its key usage.)
2398 */
2399 attemptNetworkFetch = false;
2400 }
2401
2402 /*
2403 * If we haven't verified to a root, and net fetch of certs is enabled,
2404 * try to get the issuer of the last cert in the chain from the net.
2405 * If that succeeds, then call ourself recursively to perform the
2406 * whole search again (including comparing to or verifying against
2407 * anchor certs).
2408 */
2409 if(!verifiedToRoot && !verifiedToAnchor &&
2410 (endCert != NULL) && attemptNetworkFetch) {
2411 TPCertInfo *issuer = NULL;
2412 CSSM_RETURN cr = tpFetchIssuerFromNet(*endCert,
2413 clHand,
2414 cspHand,
2415 verifyTime,
2416 issuer);
2417 switch(cr) {
2418 case CSSMERR_TP_CERTGROUP_INCOMPLETE:
2419 /* no issuerAltName, no reason to log this */
2420 break;
2421 default:
2422 /* gross error */
2423 endCert->addStatusCode(cr);
2424 break;
2425 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
2426 /* use this one but re-verify later */
2427 foundPartialIssuer = true;
2428 /* and drop thru */
2429 case CSSM_OK:
2430 if (!issuer)
2431 break;
2432 tpDebug("buildCertGroup: Cert FOUND from Net; recursing");
2433
2434 if(isInGroup(*issuer)) {
2435 tpDebug("buildCertGroup: Multiple instances of cert from net");
2436 delete issuer;
2437 issuer = NULL;
2438 crtn = CSSMERR_TP_CERTGROUP_INCOMPLETE;
2439 break;
2440 }
2441
2442 /* add this fetched cert to constructed group */
2443 appendCert(issuer);
2444 issuer->isFromNet(true);
2445 certsToBeFreed.appendCert(issuer);
2446
2447 /* and go again */
2448 cr = buildCertGroup(*issuer,
2449 inCertGroup,
2450 dbList,
2451 clHand,
2452 cspHand,
2453 verifyTime,
2454 numAnchorCerts,
2455 anchorCerts,
2456 certsToBeFreed,
2457 gatheredCerts,
2458 CSSM_TRUE, // subjectIsInGroup
2459 actionFlags,
2460 policyOid,
2461 policyStr,
2462 policyStrLen,
2463 leafKeyUse, // actually don't care since the leaf will not
2464 // be evaluated
2465 verifiedToRoot,
2466 verifiedToAnchor,
2467 verifiedViaTrustSettings);
2468 if(cr) {
2469 return cr;
2470 }
2471
2472 /* one more thing: partial public key processing needed? */
2473 if(foundPartialIssuer) {
2474 return verifyWithPartialKeys(subjectItem);
2475 }
2476 else {
2477 return CSSM_OK;
2478 }
2479 }
2480 }
2481 final_out:
2482 /* regardless of outcome, check for partial keys to log per-cert status */
2483 CSSM_RETURN partRtn = CSSM_OK;
2484 if(foundPartialIssuer) {
2485 partRtn = verifyWithPartialKeys(subjectItem);
2486 }
2487 if(crtn) {
2488 return crtn;
2489 }
2490 else {
2491 return partRtn;
2492 }
2493 }
2494
2495 /*
2496 * Called from buildCertGroup as final processing of a constructed
2497 * group when CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE has been
2498 * detected. Perform partial public key processing.
2499 *
2500 * We don't have to verify every element, just the ones whose
2501 * issuers have partial public keys.
2502 *
2503 * Returns:
2504 * CSSMERR_TP_CERTIFICATE_CANT_OPERATE in the case of an issuer cert
2505 * with a partial public key which can't be completed.
2506 * CSSMERR_TP_INVALID_CERT_AUTHORITY if sig verify failed with
2507 * a (supposedly) completed partial key
2508 */
2509 CSSM_RETURN TPCertGroup::verifyWithPartialKeys(
2510 const TPClItemInfo &subjectItem) // Cert or CRL
2511 {
2512 TPCertInfo *lastFullKeyCert = NULL;
2513 tpDebug("verifyWithPartialKeys top");
2514
2515 /* start from the end - it's easier */
2516 for(int dex=mNumCerts-1; dex >= 0; dex--) {
2517 TPCertInfo *thisCert = mCertInfo[dex];
2518
2519 /*
2520 * If this is the start of the cert chain, and it's not being
2521 * used to verify subjectItem, then we're done.
2522 */
2523 if(dex == 0) {
2524 if((void *)thisCert == (void *)&subjectItem) {
2525 tpDebug("verifyWithPartialKeys: success at leaf cert");
2526 return CSSM_OK;
2527 }
2528 }
2529 if(!thisCert->hasPartialKey()) {
2530 /*
2531 * Good to know. Record this and move on.
2532 */
2533 lastFullKeyCert = thisCert;
2534 tpDebug("full key cert found at index %d", dex);
2535 continue;
2536 }
2537 if(lastFullKeyCert == NULL) {
2538 /*
2539 * No full keys between here and the end!
2540 */
2541 tpDebug("UNCOMPLETABLE cert at index %d", dex);
2542 if(thisCert->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE)) {
2543 return CSSMERR_TP_CERTIFICATE_CANT_OPERATE;
2544 }
2545 else {
2546 break;
2547 }
2548 }
2549
2550 /* do the verify - of next cert in chain or of subjectItem */
2551 const TPClItemInfo *subject;
2552 if(dex == 0) {
2553 subject = &subjectItem;
2554 tpDebug("...verifying subject item with partial cert 0");
2555 }
2556 else {
2557 subject = mCertInfo[dex - 1];
2558 tpDebug("...verifying with partial cert %d", dex);
2559 }
2560 CSSM_RETURN crtn = subject->verifyWithIssuer(thisCert,
2561 lastFullKeyCert);
2562 if(crtn) {
2563 tpDebug("CERT VERIFY ERROR with partial cert at index %d", dex);
2564 if(thisCert->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE)) {
2565 return CSSMERR_TP_INVALID_CERT_AUTHORITY;
2566 }
2567 else {
2568 break;
2569 }
2570 }
2571 }
2572
2573 /* we just verified subjectItem - right? */
2574 assert((void *)mCertInfo[0] != (void *)&subjectItem);
2575 tpDebug("verifyWithPartialKeys: success at subjectItem");
2576 return CSSM_OK;
2577 }
2578
2579 /*
2580 * Free records obtained from DBs. Called when these records are not going to
2581 * be passed to caller of CertGroupConstruct or CertGroupVerify.
2582 */
2583 void TPCertGroup::freeDbRecords()
2584 {
2585 for(unsigned dex=0; dex<mNumCerts; dex++) {
2586 TPCertInfo *certInfo = mCertInfo[dex];
2587 certInfo->freeUniqueRecord();
2588 }
2589 }