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