2 * Copyright (c) 2000-2013 Apple Inc. All Rights Reserved.
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
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.
20 * TPCertInfo.cpp - TP's private certificate info classes
23 #include "TPCertInfo.h"
24 #include "tpdebugging.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>
43 #define tpTimeDbg(args...) secdebug("tpTime", ## args)
44 #define tpCertInfoDbg(args...) secdebug("tpCert", ## args)
46 static const TPClItemCalls tpCertClCalls
=
48 CSSM_CL_CertGetFirstCachedFieldValue
,
49 CSSM_CL_CertAbortQuery
,
51 CSSM_CL_CertAbortCache
,
53 &CSSMOID_X509V1ValidityNotBefore
,
54 &CSSMOID_X509V1ValidityNotAfter
,
55 CSSMERR_TP_INVALID_CERT_POINTER
,
56 CSSMERR_TP_CERT_EXPIRED
,
57 CSSMERR_TP_CERT_NOT_VALID_YET
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
75 mAuthorityKeyID(NULL
),
77 mSigAlg(CSSM_ALGID_NONE
),
81 mIsNotValidYet(false),
85 CSSM_RETURN crtn
= cacheItem(itemData
, copyItemData
);
87 CssmError::throwMe(crtn
);
91 * Fetch standard fields...
92 * Issuer name assumes same OID for Certs and CRLs!
94 crtn
= fetchField(&CSSMOID_X509V1IssuerName
, &mIssuerName
);
96 CssmError::throwMe(crtn
);
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.
104 CSSM_DATA_PTR algField
;
105 crtn
= fetchField(&CSSMOID_X509V1SignatureAlgorithmTBS
, &algField
);
108 CssmError::throwMe(crtn
);
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
);
114 CSSM_X509_ALGORITHM_IDENTIFIER
*algId
=
115 (CSSM_X509_ALGORITHM_IDENTIFIER
*)algField
->Data
;
116 bool algFound
= cssmOidToAlg(&algId
->algorithm
, &mSigAlg
);
118 tpErrorLog("TPClItemInfo: unknown signature algorithm\n");
119 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT
);
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
);
128 freeField(&CSSMOID_X509V1SignatureAlgorithmTBS
, algField
);
130 /* Attempt to fetch authority key id and subject key id,
131 * ignore error if they do not exist.
133 fetchField(&CSSMOID_SubjectKeyIdentifier
, &mSubjectKeyID
);
134 fetchField(&CSSMOID_AuthorityKeyIdentifier
, &mAuthorityKeyID
);
136 fetchNotBeforeAfter();
137 calculateCurrent(verifyTime
);
145 TPClItemInfo::~TPClItemInfo()
147 tpCertInfoDbg("TPClItemInfo destruct this %p", this);
151 void TPClItemInfo::releaseResources()
153 if(mWeOwnTheData
&& (mItemData
!= NULL
)) {
154 tpFreeCssmData(Allocator::standard(), mItemData
, CSSM_TRUE
);
155 mWeOwnTheData
= false;
159 freeField(&CSSMOID_X509V1IssuerName
, mIssuerName
);
163 freeField(&CSSMOID_SubjectKeyIdentifier
, mSubjectKeyID
);
164 mSubjectKeyID
= NULL
;
166 if(mAuthorityKeyID
) {
167 freeField(&CSSMOID_AuthorityKeyIdentifier
, mAuthorityKeyID
);
168 mAuthorityKeyID
= NULL
;
170 if(mCacheHand
!= 0) {
171 mClCalls
.abortCache(mClHand
, mCacheHand
);
175 CFRelease(mNotBefore
);
179 CFRelease(mNotAfter
);
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
191 uint32 NumberOfFields
= 0;
192 CSSM_HANDLE resultHand
= 0;
195 assert(mClCalls
.getField
!= NULL
);
196 assert(mCacheHand
!= 0);
197 crtn
= mClCalls
.getField(
207 if(NumberOfFields
!= 1) {
208 tpCertInfoDbg("TPClItemInfo::fetchField: numFields %d, expected 1\n",
209 (int)NumberOfFields
);
211 mClCalls
.abortQuery(mClHand
, resultHand
);
215 /* free arbitrary field obtained from fetchField() */
216 CSSM_RETURN
TPClItemInfo::freeField(
217 const CSSM_OID
*fieldOid
,
218 CSSM_DATA_PTR fieldData
)
220 return CSSM_CL_FreeFieldValue(mClHand
, fieldOid
, fieldData
);
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.
230 CSSM_RETURN
TPClItemInfo::verifyWithIssuer(
231 TPCertInfo
*issuerCert
,
232 TPCertInfo
*paramCert
/* = NULL */) const
236 assert(mClHand
!= 0);
237 assert(issuerCert
->isIssuerOf(*this));
238 assert(mCspHand
!= 0);
241 * Special case: detect partial public key right now; don't even
242 * bother trying the cert verify in that case.
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
;
250 CSSM_CC_HANDLE ccHand
;
251 crtn
= CSSM_CSP_CreateSignatureContext(mCspHand
,
253 NULL
, // Access Creds
254 issuerCert
->pubKey(),
256 if(crtn
!= CSSM_OK
) {
257 tpErrorLog("verifyWithIssuer: CreateSignatureContext error\n");
258 CssmError::throwMe(crtn
);
260 if(paramCert
!= NULL
) {
261 assert(issuerCert
->hasPartialKey());
263 /* add in parameter-bearing key */
264 CSSM_CONTEXT_ATTRIBUTE newAttr
;
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
);
271 tpErrorLog("verifyWithIssuer: CSSM_UpdateContextAttributes error\n");
272 CssmError::throwMe(crtn
);
275 crtn
= mClCalls
.itemVerify(mClHand
,
283 case CSSM_OK
: // success
284 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
: // caller handles
285 tpVfyDebug("verifyWithIssuer GOOD");
288 /* all others appear here as general cert verify error */
289 crtn
= CSSMERR_TP_VERIFICATION_FAILURE
;
290 tpVfyDebug("verifyWithIssuer BAD");
293 CSSM_DeleteContext(ccHand
);
297 CSSM_RETURN
TPClItemInfo::cacheItem(
298 const CSSM_DATA
*itemData
,
299 TPItemCopy copyItemData
)
301 switch(copyItemData
) {
303 mItemData
= const_cast<CSSM_DATA
*>(itemData
);
306 mItemData
= tpMallocCopyCssmData(Allocator::standard(), itemData
);
307 mWeOwnTheData
= true;
311 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
314 /* cache the cert/CRL in the CL */
315 return mClCalls
.cacheItem(mClHand
, mItemData
, &mCacheHand
);
319 * Calculate not before/after times as struct tm. Only throws on
320 * gross error (CSSMERR_TP_INVALID_CERT_POINTER, etc.).
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
327 void TPClItemInfo::fetchNotBeforeAfter()
329 CSSM_DATA_PTR notBeforeField
= NULL
;
330 CSSM_DATA_PTR notAfterField
= NULL
;
331 CSSM_RETURN crtn
= CSSM_OK
;
332 CSSM_X509_TIME
*xTime
;
334 assert(cacheHand() != CSSM_INVALID_HANDLE
);
335 crtn
= fetchField(mClCalls
.notBeforeOid
, ¬BeforeField
);
337 tpErrorLog("fetchNotBeforeAfter: GetField error\n");
338 CssmError::throwMe(mClCalls
.invalidItemRtn
);
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
;
349 crtn
= fetchField(mClCalls
.notAfterOid
, ¬AfterField
);
352 * Tolerate a missing NextUpdate in CRL only
354 if(mClCalls
.notAfterOid
== &CSSMOID_X509V1ValidityNotAfter
) {
355 tpErrorLog("fetchNotBeforeAfter: GetField error\n");
356 crtn
= mClCalls
.invalidItemRtn
;
361 * Fake NextUpdate to be "at the end of time"
363 timeStringToCfDate(CSSM_APPLE_CRL_END_OF_TIME
,
364 strlen(CSSM_APPLE_CRL_END_OF_TIME
),
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
;
379 freeField(mClCalls
.notAfterOid
, notAfterField
);
382 freeField(mClCalls
.notBeforeOid
, notBeforeField
);
384 if(crtn
!= CSSM_OK
) {
385 CssmError::throwMe(crtn
);
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.
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).
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.
402 ModuleNexus
<Mutex
> tpTimeLock
;
404 CSSM_RETURN
TPClItemInfo::calculateCurrent(
405 const char *verifyString
)
407 CFDateRef refTime
= NULL
;
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
;
417 /* time base = right now */
418 refTime
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
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
));
425 return mClCalls
.notValidYetRtn
;
428 mIsNotValidYet
= false;
431 if(compareTimes(refTime
, mNotAfter
) > 0) {
433 tpTimeDbg("\nTP_CERT_EXPIRED: now %g notBefore %g",
434 CFDateGetAbsoluteTime(refTime
), CFDateGetAbsoluteTime(mNotBefore
));
436 return mClCalls
.expiredRtn
;
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.
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
459 TPClItemInfo(clHand
, cspHand
, tpCertClCalls
, certData
,
460 copyCertData
, verifyTime
),
462 mPublicKeyData(NULL
),
465 mIsFromInputCerts(false),
472 mIsRoot(TRS_Unknown
),
473 mRevCheckGood(false),
474 mRevCheckComplete(false),
475 mTrustSettingsEvaluated(false),
476 mTrustSettingsDomain(kSecTrustSettingsDomainSystem
),
477 mTrustSettingsResult(kSecTrustSettingsResultInvalid
),
478 mTrustSettingsFoundAnyEntry(false),
479 mTrustSettingsFoundMatchingEntry(false),
482 mIgnoredError(false),
483 mTrustSettingsKeyUsage(0),
488 tpCertInfoDbg("TPCertInfo construct this %p", this);
489 mDlDbHandle
.DLHandle
= 0;
490 mDlDbHandle
.DBHandle
= 0;
492 /* fetch subject name */
493 crtn
= fetchField(&CSSMOID_X509V1SubjectName
, &mSubjectName
);
497 CssmError::throwMe(crtn
);
500 /* this cert's public key */
501 crtn
= fetchField(&CSSMOID_CSSMKeyStruct
, &mPublicKeyData
);
502 if(crtn
|| (mPublicKeyData
->Length
!= sizeof(CSSM_KEY
))) {
505 CssmError::throwMe(crtn
);
507 mPublicKey
= (CSSM_KEY_PTR
)mPublicKeyData
->Data
;
509 /* calculate other commonly used fields */
510 if(tpCompareCssmData(mSubjectName
, issuerName())) {
512 * Per Radar 3374978, perform complete signature verification
513 * lazily - just check subject/issuer match here.
515 tpAnchorDebug("TPCertInfo potential anchor");
516 mIsRoot
= TRS_NamesMatch
;
519 mIsRoot
= TRS_NotRoot
;
523 /* frees mSubjectName, mIssuerName, mCacheHand via mClHand */
524 TPCertInfo::~TPCertInfo()
526 tpCertInfoDbg("TPCertInfo destruct this %p", this);
530 void TPCertInfo::releaseResources()
533 freeField(&CSSMOID_X509V1SubjectName
, mSubjectName
);
537 freeField(&CSSMOID_CSSMKeyStruct
, mPublicKeyData
);
539 mPublicKeyData
= NULL
;
549 CFRelease(mCertHashStr
);
551 TPClItemInfo::releaseResources();
554 const CSSM_DATA
*TPCertInfo::subjectName()
556 assert(mSubjectName
!= NULL
);
561 * Perform semi-lazy evaluation of "rootness". Subject and issuer names
562 * compared at constructor.
563 * If avoidVerify is true, we won't do the signature verify: caller
564 * just wants to know if the subject and issuer names match.
566 bool TPCertInfo::isSelfSigned(bool avoidVerify
)
569 case TRS_NotRoot
: // known not to be root
577 /* else drop through and verify */
578 case TRS_Unknown
: // actually shouldn't happen, but to be safe...
580 /* do the signature verify */
581 if(verifyWithIssuer(this) == CSSM_OK
) {
582 tpAnchorDebug("isSelfSigned anchor verified");
583 mIsRoot
= TRS_IsRoot
;
587 tpAnchorDebug("isSelfSigned anchor vfy FAIL");
588 mIsRoot
= TRS_NotRoot
;
595 * Am I the issuer of the specified subject item? Returns true if so.
596 * Works for subject certs as well as CRLs.
598 bool TPCertInfo::isIssuerOf(
599 const TPClItemInfo
&subject
)
601 assert(mSubjectName
!= NULL
);
602 assert(subject
.issuerName() != NULL
);
603 if(tpCompareCssmData(mSubjectName
, subject
.issuerName())) {
612 * Does my subjectKeyID match the authorityKeyID of the specified subject?
613 * Returns true if so (and if both fields are available).
615 bool TPCertInfo::isAuthorityKeyOf(
616 const TPClItemInfo
&subject
)
618 const CSSM_DATA
*subjectKeyID
= this->subjectKeyID();
619 const CSSM_DATA
*authorityKeyID
= subject
.authorityKeyID();
620 if(!subjectKeyID
|| !authorityKeyID
) {
621 tpDebug("isAuthorityKeyOf FALSE (one or both key ids missing)");
624 CSSM_X509_EXTENSION
*ske
= (CSSM_X509_EXTENSION
*)subjectKeyID
->Data
;
625 CSSM_X509_EXTENSION
*ake
= (CSSM_X509_EXTENSION
*)authorityKeyID
->Data
;
626 if( !ske
|| ske
->format
!= CSSM_X509_DATAFORMAT_PARSED
||
627 !ake
|| ake
->format
!= CSSM_X509_DATAFORMAT_PARSED
||
628 !ske
->value
.parsedValue
|| !ake
->value
.parsedValue
) {
629 tpDebug("isAuthorityKeyOf FALSE (no parsed value present)");
633 const CE_SubjectKeyID
*skid
= (CE_SubjectKeyID
*)ske
->value
.parsedValue
;
634 const CE_AuthorityKeyID
*akid
= (CE_AuthorityKeyID
*)ake
->value
.parsedValue
;
636 if(!akid
->keyIdentifierPresent
) {
637 tpDebug("isAuthorityKeyOf FALSE (no key identifier present)");
640 if(tpCompareCssmData(skid
, &akid
->keyIdentifier
)) {
642 tpDebug("isAuthorityKeyOf TRUE (len:s=%lu/a=%lu, %08lX../%08lX..)",
644 akid
->keyIdentifier
.Length
,
645 (skid
->Data
) ? *((unsigned long *)skid
->Data
) : 0L,
646 (akid
->keyIdentifier
.Data
) ? *((unsigned long *)akid
->keyIdentifier
.Data
) : 0L);
652 tpDebug("isAuthorityKeyOf FALSE (len:s=%lu/a=%lu, %08lX../%08lX..)",
654 akid
->keyIdentifier
.Length
,
655 (skid
->Data
) ? *((unsigned long *)skid
->Data
) : 0L,
656 (akid
->keyIdentifier
.Data
) ? *((unsigned long *)akid
->keyIdentifier
.Data
) : 0L);
662 bool TPCertInfo::addStatusCode(CSSM_RETURN code
)
665 mStatusCodes
= (CSSM_RETURN
*)realloc(mStatusCodes
,
666 mNumStatusCodes
* sizeof(CSSM_RETURN
));
667 mStatusCodes
[mNumStatusCodes
- 1] = code
;
668 return isStatusFatal(code
);
671 bool TPCertInfo::hasStatusCode(CSSM_RETURN code
)
673 for(unsigned dex
=0; dex
<mNumStatusCodes
; dex
++) {
674 if(mStatusCodes
[dex
] == code
) {
681 bool TPCertInfo::isStatusFatal(CSSM_RETURN code
)
683 for(unsigned dex
=0; dex
<mNumAllowedErrs
; dex
++) {
684 if(mAllowedErrs
[dex
] == code
) {
685 tpTrustSettingsDbg("isStatusFatal(%ld): ALLOWED", (unsigned long)code
);
686 mIgnoredError
= true;
694 * Indicate whether this cert's public key is a CSSM_KEYATTR_PARTIAL
697 bool TPCertInfo::hasPartialKey()
699 if(mPublicKey
->KeyHeader
.KeyAttr
& CSSM_KEYATTR_PARTIAL
) {
710 bool TPCertInfo::shouldReject()
712 static unsigned char _UTN_UF_H_ISSUER_BYTES
[154] = {
713 0x30, 0x81, 0x97, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
714 0x13, 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
715 0x08, 0x13, 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55,
716 0x04, 0x07, 0x13, 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b,
717 0x45, 0x20, 0x43, 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03,
718 0x55, 0x04, 0x0a, 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45,
719 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f,
720 0x52, 0x4b, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
721 0x18, 0x48, 0x54, 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e,
722 0x55, 0x53, 0x45, 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f,
723 0x4d, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16,
724 0x55, 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53,
725 0x54, 0x2d, 0x48, 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45
727 CSSM_DATA _UTN_UF_H_ISSUER
= { sizeof(_UTN_UF_H_ISSUER_BYTES
), _UTN_UF_H_ISSUER_BYTES
};
729 static CSSM_DATA _UTN_UF_H_SERIALS
[] = {
730 { 17, (uint8
*)"\x00\x92\x39\xd5\x34\x8f\x40\xd1\x69\x5a\x74\x54\x70\xe1\xf2\x3f\x43" }, // amo
731 { 17, (uint8
*)"\x00\xd8\xf3\x5f\x4e\xb7\x87\x2b\x2d\xab\x06\x92\xe3\x15\x38\x2f\xb0" }, // gt
732 { 17, (uint8
*)"\x00\xb0\xb7\x13\x3e\xd0\x96\xf9\xb5\x6f\xae\x91\xc8\x74\xbd\x3a\xc0" }, // llc
733 { 17, (uint8
*)"\x00\xe9\x02\x8b\x95\x78\xe4\x15\xdc\x1a\x71\x0a\x2b\x88\x15\x44\x47" }, // lsc
734 { 17, (uint8
*)"\x00\xd7\x55\x8f\xda\xf5\xf1\x10\x5b\xb2\x13\x28\x2b\x70\x77\x29\xa3" }, // lyc
735 { 16, (uint8
*)"\x39\x2a\x43\x4f\x0e\x07\xdf\x1f\x8a\xa3\x05\xde\x34\xe0\xc2\x29" }, // lyc1
736 { 16, (uint8
*)"\x3e\x75\xce\xd4\x6b\x69\x30\x21\x21\x88\x30\xae\x86\xa8\x2a\x71" }, // lyc2
737 { 16, (uint8
*)"\x04\x7e\xcb\xe9\xfc\xa5\x5f\x7b\xd0\x9e\xae\x36\xe1\x0c\xae\x1e" }, // mgc
738 { 17, (uint8
*)"\x00\xf5\xc8\x6a\xf3\x61\x62\xf1\x3a\x64\xf5\x4f\x6d\xc9\x58\x7c\x06" }, // wgc
742 const CSSM_DATA
*issuer
=issuerName();
743 if(!issuer
|| !(tpCompareCssmData(issuer
, &_UTN_UF_H_ISSUER
)))
746 CSSM_DATA
*serialNumber
=NULL
;
747 CSSM_RETURN crtn
= fetchField(&CSSMOID_X509V1SerialNumber
, &serialNumber
);
748 if(crtn
|| !serialNumber
)
751 CSSM_DATA
*p
=_UTN_UF_H_SERIALS
;
754 if(tpCompareCssmData(serialNumber
, p
)) {
756 addStatusCode(CSSMERR_TP_CERT_REVOKED
);
761 freeField(&CSSMOID_X509V1SerialNumber
, serialNumber
);
766 * Evaluate trust settings; returns true in *foundMatchingEntry if positive
767 * match found - i.e., cert chain construction is done.
769 OSStatus
TPCertInfo::evaluateTrustSettings(
770 const CSSM_OID
&policyOid
,
771 const char *policyString
, // optional
772 uint32 policyStringLen
,
773 SecTrustSettingsKeyUsage keyUse
, // required
774 bool *foundMatchingEntry
, // RETURNED
775 bool *foundAnyEntry
) // RETURNED
778 * We might have to force a re-evaluation if the requested key usage
779 * is not a subset of what we already checked for (and cached).
781 if(mTrustSettingsEvaluated
) {
782 bool doFlush
= false;
783 if(mTrustSettingsKeyUsage
!= kSecTrustSettingsKeyUseAny
) {
784 if(keyUse
== kSecTrustSettingsKeyUseAny
) {
785 /* now want "any", checked something else before */
788 else if((keyUse
& mTrustSettingsKeyUsage
) != keyUse
) {
789 /* want bits that we didn't ask for before */
794 tpTrustSettingsDbg("evaluateTrustSettings: flushing cached trust for "
795 "%p due to keyUse 0x%x", this, (int)keyUse
);
796 mTrustSettingsEvaluated
= false;
797 mTrustSettingsFoundAnyEntry
= false;
798 mTrustSettingsResult
= kSecTrustSettingsResultInvalid
;
799 mTrustSettingsFoundMatchingEntry
= false;
800 if(mAllowedErrs
!= NULL
) {
805 /* else we can safely use the cached values */
807 if(!mTrustSettingsEvaluated
) {
809 if(mCertHashStr
== NULL
) {
810 const CSSM_DATA
*certData
= itemData();
811 mCertHashStr
= SecTrustSettingsCertHashStrFromData(certData
->Data
,
815 OSStatus ortn
= SecTrustSettingsEvaluateCert(mCertHashStr
,
821 * This is the purpose of the avoidVerify option, right here.
822 * If this is a root cert and it has trust settings, we avoid
823 * the signature verify. If it turns out there are no trust
824 * settings and this is a root, we'll verify the signature
825 * elsewhere (e.g. post_trust_setting: in buildCertGroup()).
828 &mTrustSettingsDomain
,
831 &mTrustSettingsResult
,
832 &mTrustSettingsFoundMatchingEntry
,
833 &mTrustSettingsFoundAnyEntry
);
835 tpTrustSettingsDbg("evaluateTrustSettings: SecTrustSettingsEvaluateCert error!");
838 mTrustSettingsEvaluated
= true;
839 mTrustSettingsKeyUsage
= keyUse
;
841 if(mTrustSettingsFoundMatchingEntry
) {
842 tpTrustSettingsDbg("evaluateTrustSettings: found for %p result %d",
843 this, (int)mTrustSettingsResult
);
846 /* one more thing... */
848 return CSSMERR_TP_INVALID_CERTIFICATE
;
851 *foundMatchingEntry
= mTrustSettingsFoundMatchingEntry
;
852 *foundAnyEntry
= mTrustSettingsFoundAnyEntry
;
854 return errSecSuccess
;
857 /* true means "verification terminated due to user trust setting" */
858 bool TPCertInfo::trustSettingsFound()
860 switch(mTrustSettingsResult
) {
861 case kSecTrustSettingsResultUnspecified
: /* entry but not definitive */
862 case kSecTrustSettingsResultInvalid
: /* no entry */
870 * Determine if this has an empty SubjectName field. Returns true if so.
872 bool TPCertInfo::hasEmptySubjectName()
875 * A "pure" empty subject is two bytes (0x30 00) - constructed sequence,
876 * short form length, length 0. We'll be robust and tolerate a missing
877 * field, as well as a possible BER-encoded subject with some extra cruft.
879 if((mSubjectName
== NULL
) || (mSubjectName
->Length
<= 4)) {
888 * Free mUniqueRecord if it exists.
889 * This is *not* done in our destructor because this record sometimes
890 * has to persist in the form of a CSSM evidence chain.
892 void TPCertInfo::freeUniqueRecord()
894 if(mUniqueRecord
== NULL
) {
897 tpDbDebug("freeUniqueRecord: freeing cert record %p", mUniqueRecord
);
898 CSSM_DL_FreeUniqueRecord(mDlDbHandle
, mUniqueRecord
);
902 *** TPCertGroup class
905 /* build empty group */
906 TPCertGroup::TPCertGroup(
908 TPGroupOwner whoOwns
) :
915 tpCertInfoDbg("TPCertGroup simple construct this %p", this);
916 /* nothing for now */
920 * Construct from unordered, untrusted CSSM_CERTGROUP. Resulting
921 * TPCertInfos are more or less in the same order as the incoming
922 * certs, though incoming certs are discarded if they don't parse.
923 * No verification of any sort is performed.
925 TPCertGroup::TPCertGroup(
926 const CSSM_CERTGROUP
&CertGroupFrag
,
927 CSSM_CL_HANDLE clHand
,
928 CSSM_CSP_HANDLE cspHand
,
930 const char *verifyTime
, // may be NULL
931 bool firstCertMustBeValid
,
932 TPGroupOwner whoOwns
) :
939 tpCertInfoDbg("TPCertGroup hard construct this %p", this);
941 /* verify input args */
942 if(cspHand
== CSSM_INVALID_HANDLE
) {
943 CssmError::throwMe(CSSMERR_TP_INVALID_CSP_HANDLE
);
945 if(clHand
== CSSM_INVALID_HANDLE
) {
946 CssmError::throwMe(CSSMERR_TP_INVALID_CL_HANDLE
);
948 if(firstCertMustBeValid
) {
949 if( (CertGroupFrag
.NumCerts
== 0) ||
950 (CertGroupFrag
.GroupList
.CertList
[0].Data
== NULL
) ||
951 (CertGroupFrag
.GroupList
.CertList
[0].Length
== 0)) {
952 CssmError::throwMe(CSSMERR_TP_INVALID_CERTIFICATE
);
955 if(CertGroupFrag
.CertGroupType
!= CSSM_CERTGROUP_DATA
) {
956 CssmError::throwMe(CSSMERR_TP_INVALID_CERTGROUP
);
958 switch(CertGroupFrag
.CertType
) {
959 case CSSM_CERT_X_509v1
:
960 case CSSM_CERT_X_509v2
:
961 case CSSM_CERT_X_509v3
:
964 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT
);
966 switch(CertGroupFrag
.CertEncoding
) {
967 case CSSM_CERT_ENCODING_BER
:
968 case CSSM_CERT_ENCODING_DER
:
971 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT
);
975 * Add remaining input certs to mCertInfo.
977 TPCertInfo
*certInfo
= NULL
;
978 for(unsigned certDex
=0; certDex
<CertGroupFrag
.NumCerts
; certDex
++) {
980 certInfo
= new TPCertInfo(clHand
,
982 &CertGroupFrag
.GroupList
.CertList
[certDex
],
983 TIC_NoCopy
, // caller owns
987 if((certDex
== 0) && firstCertMustBeValid
) {
988 CssmError::throwMe(CSSMERR_TP_INVALID_CERTIFICATE
);
990 /* else just ignore this cert */
993 certInfo
->index(certDex
);
994 appendCert(certInfo
);
999 * Deletes contents of mCertInfo[] if appropriate.
1001 TPCertGroup::~TPCertGroup()
1003 if(mWhoOwns
== TGO_Group
) {
1005 for(i
=0; i
<mNumCerts
; i
++) {
1006 delete mCertInfo
[i
];
1009 mAlloc
.free(mCertInfo
);
1012 /* add/remove/access TPTCertInfo's. */
1014 * NOTE: I am aware that most folks would just use an array<> here, but
1015 * gdb is so lame that it doesn't even let one examine the contents
1016 * of an array<> (or just about anything else in the STL). I prefer
1017 * debuggability over saving a few lines of trivial code.
1019 void TPCertGroup::appendCert(
1020 TPCertInfo
*certInfo
) // appends to end of mCertInfo
1022 if(mNumCerts
== mSizeofCertInfo
) {
1023 if(mSizeofCertInfo
== 0) {
1024 /* appending to empty array */
1025 mSizeofCertInfo
= 1;
1028 mSizeofCertInfo
*= 2;
1030 mCertInfo
= (TPCertInfo
**)mAlloc
.realloc(mCertInfo
,
1031 mSizeofCertInfo
* sizeof(TPCertInfo
*));
1033 mCertInfo
[mNumCerts
++] = certInfo
;
1036 TPCertInfo
*TPCertGroup::certAtIndex(
1039 if(index
> (mNumCerts
- 1)) {
1040 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
1042 return mCertInfo
[index
];
1045 TPCertInfo
*TPCertGroup::removeCertAtIndex(
1046 unsigned index
) // doesn't delete the cert, just
1047 // removes it from out list
1049 if(index
> (mNumCerts
- 1)) {
1050 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
1052 TPCertInfo
*rtn
= mCertInfo
[index
];
1054 /* removed requested element and compact remaining array */
1056 for(i
=index
; i
<(mNumCerts
- 1); i
++) {
1057 mCertInfo
[i
] = mCertInfo
[i
+1];
1063 TPCertInfo
*TPCertGroup::firstCert()
1065 if(mNumCerts
== 0) {
1066 /* the caller really should not do this... */
1067 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
1070 return mCertInfo
[0];
1074 TPCertInfo
*TPCertGroup::lastCert()
1076 if(mNumCerts
== 0) {
1080 return mCertInfo
[mNumCerts
- 1];
1084 /* build a CSSM_CERTGROUP corresponding with our mCertInfo */
1085 CSSM_CERTGROUP_PTR
TPCertGroup::buildCssmCertGroup()
1087 CSSM_CERTGROUP_PTR cgrp
=
1088 (CSSM_CERTGROUP_PTR
)mAlloc
.malloc(sizeof(CSSM_CERTGROUP
));
1089 cgrp
->NumCerts
= mNumCerts
;
1090 cgrp
->CertGroupType
= CSSM_CERTGROUP_DATA
;
1091 cgrp
->CertType
= CSSM_CERT_X_509v3
;
1092 cgrp
->CertEncoding
= CSSM_CERT_ENCODING_DER
;
1093 if(mNumCerts
== 0) {
1095 cgrp
->GroupList
.CertList
= NULL
;
1098 cgrp
->GroupList
.CertList
= (CSSM_DATA_PTR
)mAlloc
.calloc(mNumCerts
,
1100 for(unsigned i
=0; i
<mNumCerts
; i
++) {
1101 tpCopyCssmData(mAlloc
, mCertInfo
[i
]->itemData(),
1102 &cgrp
->GroupList
.CertList
[i
]);
1107 /* build a CSSM_TP_APPLE_EVIDENCE_INFO array */
1108 CSSM_TP_APPLE_EVIDENCE_INFO
*TPCertGroup::buildCssmEvidenceInfo()
1110 CSSM_TP_APPLE_EVIDENCE_INFO
*infoArray
;
1112 infoArray
= (CSSM_TP_APPLE_EVIDENCE_INFO
*)mAlloc
.calloc(mNumCerts
,
1113 sizeof(CSSM_TP_APPLE_EVIDENCE_INFO
));
1114 for(unsigned i
=0; i
<mNumCerts
; i
++) {
1115 TPCertInfo
*certInfo
= mCertInfo
[i
];
1116 CSSM_TP_APPLE_EVIDENCE_INFO
*evInfo
= &infoArray
[i
];
1118 /* first the booleans */
1119 if(certInfo
->isExpired()) {
1120 evInfo
->StatusBits
|= CSSM_CERT_STATUS_EXPIRED
;
1122 if(certInfo
->isNotValidYet()) {
1123 evInfo
->StatusBits
|= CSSM_CERT_STATUS_NOT_VALID_YET
;
1125 if(certInfo
->isAnchor()) {
1126 tpAnchorDebug("buildCssmEvidenceInfo: flagging IS_IN_ANCHORS");
1127 evInfo
->StatusBits
|= CSSM_CERT_STATUS_IS_IN_ANCHORS
;
1129 if(certInfo
->dlDbHandle().DLHandle
== 0) {
1130 if(certInfo
->isFromNet()) {
1131 evInfo
->StatusBits
|= CSSM_CERT_STATUS_IS_FROM_NET
;
1133 else if(certInfo
->isFromInputCerts()) {
1134 evInfo
->StatusBits
|= CSSM_CERT_STATUS_IS_IN_INPUT_CERTS
;
1137 /* If trust settings apply to a root, skip verifying the signature */
1138 bool avoidVerify
= false;
1139 switch(certInfo
->trustSettingsResult()) {
1140 case kSecTrustSettingsResultTrustRoot
:
1141 case kSecTrustSettingsResultTrustAsRoot
:
1142 /* these two can be disambiguated by IS_ROOT */
1143 evInfo
->StatusBits
|= CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST
;
1146 case kSecTrustSettingsResultDeny
:
1147 evInfo
->StatusBits
|= CSSM_CERT_STATUS_TRUST_SETTINGS_DENY
;
1150 case kSecTrustSettingsResultUnspecified
:
1151 case kSecTrustSettingsResultInvalid
:
1155 if(certInfo
->isSelfSigned(avoidVerify
)) {
1156 evInfo
->StatusBits
|= CSSM_CERT_STATUS_IS_ROOT
;
1158 if(certInfo
->ignoredError()) {
1159 evInfo
->StatusBits
|= CSSM_CERT_STATUS_TRUST_SETTINGS_IGNORED_ERROR
;
1161 unsigned numCodes
= certInfo
->numStatusCodes();
1163 evInfo
->NumStatusCodes
= numCodes
;
1164 evInfo
->StatusCodes
= (CSSM_RETURN
*)mAlloc
.calloc(numCodes
,
1165 sizeof(CSSM_RETURN
));
1166 for(unsigned j
=0; j
<numCodes
; j
++) {
1167 evInfo
->StatusCodes
[j
] = (certInfo
->statusCodes())[j
];
1170 if(evInfo
->StatusBits
& (CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST
|
1171 CSSM_CERT_STATUS_TRUST_SETTINGS_DENY
|
1172 CSSM_CERT_STATUS_TRUST_SETTINGS_IGNORED_ERROR
)) {
1173 /* Something noteworthy happened involving TrustSettings */
1174 uint32 whichDomain
= 0;
1175 switch(certInfo
->trustSettingsDomain()) {
1176 case kSecTrustSettingsDomainUser
:
1177 whichDomain
= CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_USER
;
1179 case kSecTrustSettingsDomainAdmin
:
1180 whichDomain
= CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_ADMIN
;
1182 case kSecTrustSettingsDomainSystem
:
1183 whichDomain
= CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_SYSTEM
;
1186 evInfo
->StatusBits
|= whichDomain
;
1188 evInfo
->Index
= certInfo
->index();
1189 evInfo
->DlDbHandle
= certInfo
->dlDbHandle();
1190 evInfo
->UniqueRecord
= certInfo
->uniqueRecord();
1195 /* Given a status for basic construction of a cert group and a status
1196 * of (optional) policy verification, plus the implicit notBefore/notAfter
1197 * status in the certs, calculate a global return code. This just
1198 * encapsulates a policy for CertGroupConstruct and CertGroupVerify.
1200 CSSM_RETURN
TPCertGroup::getReturnCode(
1201 CSSM_RETURN constructStatus
,
1202 CSSM_RETURN policyStatus
,
1203 CSSM_APPLE_TP_ACTION_FLAGS actionFlags
)
1205 if(constructStatus
) {
1206 /* CSSMERR_TP_NOT_TRUSTED, CSSMERR_TP_INVALID_ANCHOR_CERT, gross errors */
1207 return constructStatus
;
1210 bool expired
= false;
1211 bool postdated
= false;
1212 bool allowExpiredRoot
= (actionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT
) ?
1214 bool allowExpired
= (actionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED
) ? true : false;
1215 bool allowPostdated
= allowExpired
; // flag overrides any temporal invalidity
1216 bool requireRevPerCert
= (actionFlags
& CSSM_TP_ACTION_REQUIRE_REV_PER_CERT
) ?
1219 /* check for expired, not valid yet */
1220 for(unsigned i
=0; i
<mNumCerts
; i
++) {
1221 TPCertInfo
*ci
= mCertInfo
[i
];
1223 * Note avoidVerify = true for isSelfSigned(); if it were appropriate to
1224 * verify the signature, that would have happened in
1225 * buildCssmEvidenceInfo() at the latest.
1227 if(ci
->isExpired() &&
1228 !(allowExpiredRoot
&& ci
->isSelfSigned(true)) && // allowed globally
1229 ci
->isStatusFatal(CSSMERR_TP_CERT_EXPIRED
)) { // allowed for this cert
1232 if(ci
->isNotValidYet() &&
1233 ci
->isStatusFatal(CSSMERR_TP_CERT_NOT_VALID_YET
)) {
1237 if(expired
&& !allowExpired
) {
1238 return CSSMERR_TP_CERT_EXPIRED
;
1240 if(postdated
&& !allowPostdated
) {
1241 return CSSMERR_TP_CERT_NOT_VALID_YET
;
1244 /* Check for missing revocation check */
1245 if(requireRevPerCert
) {
1246 for(unsigned i
=0; i
<mNumCerts
; i
++) {
1247 TPCertInfo
*ci
= mCertInfo
[i
];
1248 if(ci
->isSelfSigned(true)) {
1249 /* revocation check meaningless for a root cert */
1250 tpDebug("getReturnCode: ignoring revocation for self-signed cert %d", i
);
1253 if(!ci
->revokeCheckGood() &&
1254 ci
->isStatusFatal(CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK
)) {
1255 tpDebug("getReturnCode: FATAL: revocation check incomplete for cert %d", i
);
1256 return CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK
;
1260 tpDebug("getReturnCode: revocation check %s for cert %d",
1261 (ci
->revokeCheckGood()) ? "GOOD" : "OK", i
);
1266 return policyStatus
;
1269 /* set all TPCertInfo.mUsed flags false */
1270 void TPCertGroup::setAllUnused()
1272 for(unsigned dex
=0; dex
<mNumCerts
; dex
++) {
1273 mCertInfo
[dex
]->used(false);
1278 * See if the specified error status is allowed (return true) or
1279 * fatal (return false) per each cert's mAllowedErrs[]. Returns
1280 * true if any cert returns false for its isStatusFatal() call.
1281 * The list of errors which can apply to cert-chain-wide allowedErrors
1282 * is right here; if the incoming error is not in that list, we
1283 * return false. If the incoming error code is CSSM_OK we return
1284 * true as a convenience for our callers.
1286 bool TPCertGroup::isAllowedError(
1292 case CSSMERR_TP_NOT_TRUSTED
:
1293 case CSSMERR_TP_INVALID_ANCHOR_CERT
:
1294 case CSSMERR_TP_VERIFY_ACTION_FAILED
:
1295 case CSSMERR_TP_INVALID_CERT_AUTHORITY
:
1296 case CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH
:
1297 case CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH
:
1298 /* continue processing these candidates */
1301 /* not a candidate for cert-chain-wide allowedErrors */
1305 for(unsigned dex
=0; dex
<mNumCerts
; dex
++) {
1306 if(!mCertInfo
[dex
]->isStatusFatal(code
)) {
1307 tpTrustSettingsDbg("TPCertGroup::isAllowedError: allowing for cert %u",
1313 /* every cert thought this was fatal; it is. */
1318 * Determine if we already have the specified cert in this group.
1320 bool TPCertGroup::isInGroup(TPCertInfo
&certInfo
)
1322 for(unsigned dex
=0; dex
<mNumCerts
; dex
++) {
1323 if(tpCompareCssmData(certInfo
.itemData(), mCertInfo
[dex
]->itemData())) {
1331 * Encode issuing certs in this group as a PEM-encoded data blob.
1334 void TPCertGroup::encodeIssuers(CSSM_DATA
&issuers
)
1336 /* FIXME: probably want to rewrite this using pemEncode() from libsecurity_cdsa_utils,
1337 * since use of Sec* APIs from this layer violates the API reentrancy contract.
1339 issuers
.Data
= NULL
;
1341 CFMutableArrayRef certArray
= CFArrayCreateMutable(kCFAllocatorDefault
,
1342 0, &kCFTypeArrayCallBacks
);
1346 for(unsigned certDex
=0; certDex
<mNumCerts
; certDex
++) {
1347 TPCertInfo
*certInfo
= certAtIndex(certDex
);
1348 if(!certDex
&& mNumCerts
> 1) {
1349 continue; /* don't need the leaf */
1351 CSSM_DATA
*cssmData
= (CSSM_DATA
*)((certInfo
) ? certInfo
->itemData() : NULL
);
1352 if(!cssmData
|| !cssmData
->Data
|| !cssmData
->Length
) {
1355 CFDataRef dataRef
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
1356 (const UInt8
*)cssmData
->Data
, cssmData
->Length
,
1361 SecCertificateRef certRef
= SecCertificateCreateWithData(kCFAllocatorDefault
,
1367 CFArrayAppendValue(certArray
, certRef
);
1371 CFDataRef exportedPEMData
= NULL
;
1372 OSStatus status
= SecItemExport(certArray
,
1373 kSecFormatPEMSequence
,
1377 CFRelease(certArray
);
1380 uint8
*dataPtr
= (uint8
*)CFDataGetBytePtr(exportedPEMData
);
1381 size_t dataLen
= CFDataGetLength(exportedPEMData
);
1382 issuers
.Data
= (uint8
*)malloc(dataLen
);
1383 memmove(issuers
.Data
, dataPtr
, dataLen
);
1384 issuers
.Length
= dataLen
;
1385 CFRelease(exportedPEMData
);
1390 * Search unused incoming certs to find an issuer of specified cert or CRL.
1391 * WARNING this assumes a valid "used" state for all certs in this group.
1392 * If partialIssuerKey is true on return, caller must re-verify signature
1393 * of subject later when sufficient info is available.
1395 TPCertInfo
*TPCertGroup::findIssuerForCertOrCrl(
1396 const TPClItemInfo
&subject
,
1397 bool &partialIssuerKey
)
1399 partialIssuerKey
= false;
1400 TPCertInfo
*expiredIssuer
= NULL
;
1401 TPCertInfo
*unmatchedKeyIDIssuer
= NULL
;
1403 for(unsigned certDex
=0; certDex
<mNumCerts
; certDex
++) {
1404 TPCertInfo
*certInfo
= certAtIndex(certDex
);
1406 /* has this one already been used in this search? */
1407 if(certInfo
->used()) {
1411 /* subject/issuer names match? */
1412 if(certInfo
->isIssuerOf(subject
)) {
1413 /* yep, do a sig verify */
1414 tpVfyDebug("findIssuerForCertOrCrl issuer/subj match checking sig");
1415 CSSM_RETURN crtn
= subject
.verifyWithIssuer(certInfo
);
1417 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
:
1418 /* issuer OK, check sig later */
1419 partialIssuerKey
= true;
1423 * Temporal validity check: if we're not already holding an expired
1424 * issuer, and this one's invalid, hold it and keep going.
1426 if((crtn
== CSSM_OK
) && (expiredIssuer
== NULL
)) {
1427 if(certInfo
->isExpired() || certInfo
->isNotValidYet()) {
1428 tpDebug("findIssuerForCertOrCrl: holding expired cert %p",
1430 expiredIssuer
= certInfo
;
1434 /* Authority key identifier check: if we can't match subject key id,
1435 * hold onto this cert and keep going.
1437 if(unmatchedKeyIDIssuer
== NULL
) {
1438 if(!certInfo
->isAuthorityKeyOf(subject
)) {
1439 tpDebug("findIssuerForCertOrCrl: holding issuer without key id match %p",
1441 unmatchedKeyIDIssuer
= certInfo
;
1446 certInfo
->used(true);
1449 /* just skip this one and keep looking */
1450 tpVfyDebug("findIssuerForCertOrCrl issuer/subj match BAD SIG");
1455 if(unmatchedKeyIDIssuer
!= NULL
) {
1456 /* OK, we'll use this one (preferred over an expired issuer) */
1457 tpDbDebug("findIssuerForCertOrCrl: using issuer without key id match %p", unmatchedKeyIDIssuer
);
1458 unmatchedKeyIDIssuer
->used(true);
1459 return unmatchedKeyIDIssuer
;
1461 if(expiredIssuer
!= NULL
) {
1462 /* OK, we'll use this one */
1463 tpDbDebug("findIssuerForCertOrCrl: using expired cert %p", expiredIssuer
);
1464 expiredIssuer
->used(true);
1465 return expiredIssuer
;
1473 * Construct ordered, verified cert chain from a variety of inputs.
1474 * Time validity does not affect the function return or any status,
1475 * we always try to find a valid cert to replace an expired or
1476 * not-yet-valid cert if we can. Final temporal validity of each
1477 * cert must be checked by caller (it's stored in each TPCertInfo
1478 * we add to ourself during construction).
1480 * Only possible error returns are:
1481 * CSSMERR_TP_CERTIFICATE_CANT_OPERATE : issuer cert was found with a partial
1482 * public key, rendering full verification impossible.
1483 * CSSMERR_TP_INVALID_CERT_AUTHORITY : issuer cert was found with a partial
1484 * public key and which failed to perform subsequent signature
1487 * Other interesting status is returned via the verifiedToRoot and
1488 * verifiedToAnchor flags.
1490 * NOTE: is it the caller's responsibility to call setAllUnused() for both
1491 * incoming cert groups (inCertGroup and gatheredCerts). We don't do that
1492 * here because we may call ourself recursively.
1494 CSSM_RETURN
TPCertGroup::buildCertGroup(
1495 const TPClItemInfo
&subjectItem
, // Cert or CRL
1496 TPCertGroup
*inCertGroup
, // optional
1497 const CSSM_DL_DB_LIST
*dbList
, // optional
1498 CSSM_CL_HANDLE clHand
,
1499 CSSM_CSP_HANDLE cspHand
,
1500 const char *verifyTime
, // optional, for establishing
1501 // validity of new TPCertInfos
1502 /* trusted anchors, optional */
1503 /* FIXME - maybe this should be a TPCertGroup */
1504 uint32 numAnchorCerts
,
1505 const CSSM_DATA
*anchorCerts
,
1508 * Certs to be freed by caller (i.e., TPCertInfo which we allocate
1509 * as a result of using a cert from anchorCerts or dbList) are added
1512 TPCertGroup
&certsToBeFreed
,
1515 * Other certificates gathered during the course of this operation,
1516 * currently consisting of certs fetched from DBs and from the net.
1517 * This is not used when called by AppleTPSession::CertGroupConstructPriv;
1518 * it's an optimization for the case when we're building a cert group
1519 * for TPCrlInfo::verifyWithContext - we avoid re-fetching certs from
1520 * the net which are needed to verify both the subject cert and a CRL.
1521 * We don't modify this TPCertGroup, we only use certs from it.
1523 TPCertGroup
*gatheredCerts
,
1526 * Indicates that subjectItem is a cert in this cert group.
1527 * If true, that cert will be tested for "root-ness", including
1528 * -- subject/issuer compare
1529 * -- signature self-verify
1532 CSSM_BOOL subjectIsInGroup
,
1535 * CSSM_TP_ACTION_FETCH_CERT_FROM_NET,
1536 * CSSM_TP_ACTION_TRUST_SETTING,
1537 * CSSM_TP_ACTION_IMPLICIT_ANCHORS are interesting
1539 CSSM_APPLE_TP_ACTION_FLAGS actionFlags
,
1541 /* CSSM_TP_ACTION_TRUST_SETTING parameters */
1542 const CSSM_OID
*policyOid
,
1543 const char *policyStr
,
1544 uint32 policyStrLen
,
1545 SecTrustSettingsKeyUsage leafKeyUse
, // usage of *first* cert in chain
1548 CSSM_BOOL
&verifiedToRoot
, // end of chain self-verifies
1549 CSSM_BOOL
&verifiedToAnchor
, // end of chain in anchors
1550 CSSM_BOOL
&verifiedViaTrustSettings
) // chain ends per User Trust setting
1552 const TPClItemInfo
*thisSubject
= &subjectItem
;
1553 CSSM_RETURN crtn
= CSSM_OK
;
1554 TPCertInfo
*issuerCert
= NULL
;
1556 TPCertInfo
*anchorInfo
= NULL
;
1557 bool foundPartialIssuer
= false;
1558 bool attemptNetworkFetch
= false;
1559 CSSM_BOOL firstSubjectIsInGroup
= subjectIsInGroup
;
1560 TPCertInfo
*endCert
;
1562 tpVfyDebug("buildCertGroup top");
1564 /* possible expired root which we'll only use if we can't find
1566 TPCertInfo
*expiredRoot
= NULL
;
1568 /* and the general case of an expired or not yet valid cert */
1569 TPCertInfo
*expiredIssuer
= NULL
;
1571 /* and the case of an issuer without a matching subject key id */
1572 TPCertInfo
*unmatchedKeyIDIssuer
= NULL
;
1574 /* and the case of a root that isn't trusted or an anchor */
1575 TPCertInfo
*untrustedRoot
= NULL
;
1577 verifiedToRoot
= CSSM_FALSE
;
1578 verifiedToAnchor
= CSSM_FALSE
;
1579 verifiedViaTrustSettings
= CSSM_FALSE
;
1581 /*** main loop to seach inCertGroup and dbList ***
1584 * -- find a root cert in the chain (self-signed)
1585 * -- find a non-root cert which is also in the anchors list
1586 * -- find a cert which is trusted per Trust Settings (if enabled)
1588 * -- or no more certs to add to chain.
1592 * Top of loop: thisSubject is the item we're trying to verify.
1595 /* is thisSubject a root cert or listed in user trust list? */
1596 if(subjectIsInGroup
) {
1597 TPCertInfo
*subjCert
= lastCert();
1598 assert(subjCert
!= NULL
);
1600 if(actionFlags
& CSSM_TP_ACTION_TRUST_SETTINGS
) {
1601 assert(policyOid
!= NULL
);
1604 * Figure out key usage. If this is a leaf cert, the caller - actually
1605 * the per-policy code - inferred the usage. Else it could be for
1606 * verifying a cert or a CRL.
1608 * We want to avoid multiple calls to the effective portion of
1609 * evaluateTrustSettings(), but a CA cert could be usable for only
1610 * signing certs and not CRLs. Thus we're evaluating a CA cert,
1611 * try to evaluate for signing certs *and* CRLs in case we come
1612 * this way again later when performing CRL verification. If that
1613 * fails, then retry with just cert signing.
1615 SecTrustSettingsKeyUsage localKeyUse
;
1616 bool doRetry
= false;
1617 if(subjCert
== firstCert()) {
1618 /* leaf - use caller's spec */
1619 localKeyUse
= leafKeyUse
;
1620 /* FIXME - add in CRL if this is cert checking? */
1623 localKeyUse
= kSecTrustSettingsKeyUseSignCert
| kSecTrustSettingsKeyUseSignRevocation
;
1624 /* and if necessary */
1627 /* this lets us avoid searching for the same thing twice when there
1628 * is in fact no entry for it */
1629 bool foundEntry
= false;
1630 bool trustSettingsFound
= false;
1631 OSStatus ortn
= subjCert
->evaluateTrustSettings(*policyOid
,
1632 policyStr
, policyStrLen
, localKeyUse
, &trustSettingsFound
, &foundEntry
);
1634 /* this is only a dire error */
1638 if(!trustSettingsFound
&& foundEntry
&& doRetry
) {
1639 tpTrustSettingsDbg("buildCertGroup: retrying evaluateTrustSettings with Cert only");
1640 ortn
= subjCert
->evaluateTrustSettings(*policyOid
,
1641 policyStr
, policyStrLen
, kSecTrustSettingsKeyUseSignCert
,
1642 &trustSettingsFound
, &foundEntry
);
1648 if(trustSettingsFound
) {
1649 switch(subjCert
->trustSettingsResult()) {
1650 case kSecTrustSettingsResultInvalid
:
1651 /* should not happen... */
1653 crtn
= CSSMERR_TP_INTERNAL_ERROR
;
1655 case kSecTrustSettingsResultTrustRoot
:
1656 case kSecTrustSettingsResultTrustAsRoot
:
1657 tpTrustSettingsDbg("Trust[As]Root found");
1660 case kSecTrustSettingsResultDeny
:
1661 tpTrustSettingsDbg("TrustResultDeny found");
1662 crtn
= CSSMERR_APPLETP_TRUST_SETTING_DENY
;
1664 case kSecTrustSettingsResultUnspecified
:
1665 /* special case here: this means "keep going, we don't trust or
1666 * distrust this cert". Typically used to express allowed errors
1669 tpTrustSettingsDbg("TrustResultUnspecified found");
1670 goto post_trust_setting
;
1672 tpTrustSettingsDbg("Unknown TrustResult (%d)",
1673 (int)subjCert
->trustSettingsResult());
1674 crtn
= CSSMERR_TP_INTERNAL_ERROR
;
1677 /* cleanup partial key processing */
1678 verifiedViaTrustSettings
= CSSM_TRUE
;
1681 } /* CSSM_TP_ACTION_TRUST_SETTING */
1685 * If this cert is in the provided anchors list,
1686 * we can stop building the chain at this point.
1688 * If this cert is a leaf, the chain ends in an anchor, but if it's
1689 * also temporally invalid, we can't do anything further. However,
1690 * if it's not a leaf, then we need to roll back the chain to a
1691 * point just before this cert, so Case 1 will subsequently find
1692 * the anchor (and handle the anchor correctly if it's expired.)
1694 if(numAnchorCerts
&& anchorCerts
) {
1695 bool foundAnchor
= false;
1696 for(certDex
=0; certDex
<numAnchorCerts
; certDex
++) {
1697 if(tp_CompareCerts(subjCert
->itemData(), &anchorCerts
[certDex
])) {
1699 /* if it's not the leaf, remove it from the outgoing cert group. */
1700 if(!firstSubjectIsInGroup
|| (mNumCerts
> 1)) {
1702 /* roll back to previous cert */
1705 if(mNumCerts
== 0) {
1706 /* roll back to caller's initial condition */
1707 thisSubject
= &subjectItem
;
1710 thisSubject
= lastCert();
1712 tpAnchorDebug("buildCertGroup: CA cert in input AND anchors");
1715 if(subjCert
->isExpired() || subjCert
->isNotValidYet()) {
1716 crtn
= CSSM_CERT_STATUS_EXPIRED
;
1720 subjCert
->isAnchor(true);
1721 verifiedToAnchor
= CSSM_TRUE
;
1722 tpAnchorDebug("buildCertGroup: leaf cert in input AND anchors");
1724 if(subjCert
->isSelfSigned()) {
1725 verifiedToRoot
= CSSM_TRUE
;
1727 break; /* out of anchor-checking loop */
1731 break; /* out of main loop */
1735 if(subjCert
->isSelfSigned()) {
1737 * Special case if this root is temporally invalid (and it's not
1738 * the leaf): remove it from the outgoing cert group, save it,
1739 * and proceed, looking another (good) root in anchors.
1740 * There's no way we'll find another good one in this loop.
1742 if((subjCert
->isExpired() || subjCert
->isNotValidYet()) &&
1743 (!firstSubjectIsInGroup
|| (mNumCerts
> 1))) {
1744 verifiedToRoot
= CSSM_TRUE
;
1745 tpDebug("buildCertGroup: EXPIRED ROOT %p, looking for good one", subjCert
);
1746 expiredRoot
= subjCert
;
1748 /* roll back to previous cert */
1751 if(mNumCerts
== 0) {
1752 /* roll back to caller's initial condition */
1753 thisSubject
= &subjectItem
;
1756 thisSubject
= lastCert();
1758 break; /* out of main loop */
1761 * If any root is considered an anchor, we don't need to look
1762 * for a better chain that ends in an anchor or trusted root.
1764 if(actionFlags
& CSSM_TP_ACTION_IMPLICIT_ANCHORS
) {
1765 verifiedToRoot
= CSSM_TRUE
;
1766 break; /*out of main loop */
1769 * The root we have is neither trusted nor an anchor. Continue in
1770 * the loop to look for a better chain if this is not a leaf.
1772 if(!firstSubjectIsInGroup
|| (mNumCerts
> 1)) {
1773 tpDebug("buildCertGroup: UNTRUSTED ROOT %p, looking for better chain", subjCert
);
1774 untrustedRoot
= subjCert
;
1776 /* roll back to previous cert */
1779 if(mNumCerts
== 0) {
1780 /* roll back to caller's initial condition */
1781 thisSubject
= &subjectItem
;
1784 thisSubject
= lastCert();
1788 /* the leaf is a root */
1789 break; /* out of main loop */
1792 } /* subjectIsInGroup */
1795 * Search unused incoming certs to find an issuer.
1796 * Both cert groups are optional.
1797 * We'll add issuer to outCertGroup below.
1798 * If we find a cert that's expired or not yet valid, we hold on to it
1799 * and look for a better one. If we don't find it here we drop back to the
1800 * expired one at the end of the loop. If that expired cert is a root
1801 * cert, we'll use the expiredRoot mechanism (see above) to roll back and
1802 * see if we can find a good root in the incoming anchors.
1804 if(inCertGroup
!= NULL
) {
1805 bool partial
= false;
1806 issuerCert
= inCertGroup
->findIssuerForCertOrCrl(*thisSubject
,
1809 issuerCert
->isFromInputCerts(true);
1811 /* deal with this later */
1812 foundPartialIssuer
= true;
1813 tpDebug("buildCertGroup: PARTIAL Cert FOUND in inCertGroup");
1816 tpDebug("buildCertGroup: Cert FOUND in inCertGroup");
1821 if(issuerCert
!= NULL
) {
1822 bool stashedIssuer
= false;
1823 /* Check whether candidate issuer is the same as unstrustedRoot */
1824 if((untrustedRoot
!= NULL
) && tp_CompareCerts(issuerCert
->itemData(), untrustedRoot
->itemData())) {
1825 /* already stashed */
1826 stashedIssuer
= true;
1828 /* Check whether candidate issuer is expired or not yet valid */
1829 if(issuerCert
->isExpired() || issuerCert
->isNotValidYet()) {
1830 if(expiredIssuer
== NULL
) {
1831 tpDebug("buildCertGroup: saving expired cert %p (1)", issuerCert
);
1832 expiredIssuer
= issuerCert
;
1833 stashedIssuer
= true;
1835 /* else we already have an expired issuer candidate */
1838 /* unconditionally done with possible expiredIssuer */
1840 if(expiredIssuer
!= NULL
) {
1841 tpDebug("buildCertGroup: DISCARDING expired cert %p (1)", expiredIssuer
);
1844 expiredIssuer
= NULL
;
1846 /* Check whether candidate issuer failed to match authority key id in thisSubject */
1847 if(!issuerCert
->isAuthorityKeyOf(*thisSubject
)) {
1848 if(unmatchedKeyIDIssuer
== NULL
) {
1849 tpDebug("buildCertGroup: saving unmatched key id issuer %p (1)", issuerCert
);
1850 unmatchedKeyIDIssuer
= issuerCert
;
1851 stashedIssuer
= true;
1853 /* else we already have an unmatched key id issuer candidate */
1856 /* unconditionally done with possible unmatchedKeyIDIssuer */
1858 if(unmatchedKeyIDIssuer
!= NULL
) {
1859 tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (1)", unmatchedKeyIDIssuer
);
1862 unmatchedKeyIDIssuer
= NULL
;
1865 issuerCert
= NULL
; /* keep looking */
1869 if((issuerCert
== NULL
) && (gatheredCerts
!= NULL
)) {
1870 bool partial
= false;
1871 issuerCert
= gatheredCerts
->findIssuerForCertOrCrl(*thisSubject
,
1875 /* deal with this later */
1876 foundPartialIssuer
= true;
1877 tpDebug("buildCertGroup: PARTIAL Cert FOUND in gatheredCerts");
1880 tpDebug("buildCertGroup: Cert FOUND in gatheredCerts");
1885 if(issuerCert
!= NULL
) {
1886 bool stashedIssuer
= false;
1887 /* Check whether candidate issuer is the same as untrustedRoot */
1888 if((untrustedRoot
!= NULL
) && tp_CompareCerts(issuerCert
->itemData(), untrustedRoot
->itemData())) {
1889 /* already stashed */
1890 stashedIssuer
= true;
1892 /* Check whether candidate issuer is expired or not yet valid */
1893 if(issuerCert
->isExpired() || issuerCert
->isNotValidYet()) {
1894 if(expiredIssuer
== NULL
) {
1895 tpDebug("buildCertGroup: saving expired cert %p (2)", issuerCert
);
1896 expiredIssuer
= issuerCert
;
1897 stashedIssuer
= true;
1899 /* else we already have an expired issuer candidate */
1902 /* unconditionally done with possible expiredIssuer */
1904 if(expiredIssuer
!= NULL
) {
1905 tpDebug("buildCertGroup: DISCARDING expired cert %p (2)", expiredIssuer
);
1908 expiredIssuer
= NULL
;
1910 /* Check whether candidate issuer failed to match authority key id in thisSubject */
1911 if(!issuerCert
->isAuthorityKeyOf(*thisSubject
)) {
1912 if(unmatchedKeyIDIssuer
== NULL
) {
1913 tpDebug("buildCertGroup: saving unmatched key id issuer %p (2)", issuerCert
);
1914 unmatchedKeyIDIssuer
= issuerCert
;
1915 stashedIssuer
= true;
1917 /* else we already have an unmatched key id issuer candidate */
1920 /* unconditionally done with possible unmatchedKeyIdIssuer */
1922 if(unmatchedKeyIDIssuer
!= NULL
) {
1923 tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (2)", unmatchedKeyIDIssuer
);
1926 unmatchedKeyIDIssuer
= NULL
;
1929 issuerCert
= NULL
; /* keep looking */
1934 * If we found a candidate issuer in input or gathered certs, check whether it
1935 * might be a cross-signed intermediate that can be replaced with an anchor.
1937 if(issuerCert
!= NULL
&& !issuerCert
->isSelfSigned() && (untrustedRoot
== NULL
)) {
1938 bool partial
= false;
1939 TPCertInfo
*possibleAnchorCert
= NULL
;
1941 possibleAnchorCert
= tpDbFindIssuerCert(mAlloc
,
1952 if(possibleAnchorCert
!= NULL
) {
1953 if(possibleAnchorCert
->isSelfSigned()) {
1955 * We found a better replacement issuer, so use it.
1956 * note that we don't need to free the old issuerCert first as it
1957 * comes from inCertGroup or gatheredCerts (not from dbList).
1958 * However, code from this point on cannot assume the same thing.
1960 tpDebug("buildCertGroup: replacement anchor for issuer FOUND in dbList");
1961 /* mark non-root candidate as unused since we won't use it */
1962 issuerCert
->used(false);
1963 issuerCert
= possibleAnchorCert
;
1965 /* Caller must free, since this cert came from a DLDB */
1966 certsToBeFreed
.appendCert(issuerCert
);
1968 /* deal with this later */
1969 foundPartialIssuer
= true;
1972 /* unconditionally done with possible expiredIssuer */
1974 if(expiredIssuer
!= NULL
) {
1975 tpDebug("buildCertGroup: DISCARDING expired cert %p (3)", expiredIssuer
);
1978 expiredIssuer
= NULL
;
1979 /* unconditionally done with possible unmatchedKeyIDIssuer */
1981 if(unmatchedKeyIDIssuer
!= NULL
) {
1982 tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (3)", unmatchedKeyIDIssuer
);
1985 unmatchedKeyIDIssuer
= NULL
;
1988 possibleAnchorCert
->freeUniqueRecord();
1989 delete possibleAnchorCert
;
1990 possibleAnchorCert
= NULL
;
1995 if((issuerCert
== NULL
) && (dbList
!= NULL
)) {
1996 /* Issuer not in incoming cert group or gathered certs. Search DBList. */
1997 bool partial
= false;
1999 issuerCert
= tpDbFindIssuerCert(mAlloc
,
2011 /* unconditionally done with possible expiredIssuer */
2013 if(expiredIssuer
!= NULL
) {
2014 tpDebug("buildCertGroup: DISCARDING expired cert %p (4)", expiredIssuer
);
2017 expiredIssuer
= NULL
;
2018 /* unconditionally done with possible unmatchedKeyIDIssuer */
2020 if(unmatchedKeyIDIssuer
!= NULL
) {
2021 tpDebug("buildCertGroup: DISCARDING unmatched key id issuer %p (4)", unmatchedKeyIDIssuer
);
2024 unmatchedKeyIDIssuer
= NULL
;
2027 * Handle Radar 4566041, endless loop of cross-signed certs.
2028 * This can only happen when fetching certs from a DLDB or
2029 * from the net; we prevent that from happening when the certs
2030 * are in inCertGroup or gatheredCerts by keeping track of those
2031 * certs' mUsed state.
2033 if(isInGroup(*issuerCert
)) {
2034 tpDebug("buildCertGroup: Multiple instances of cert");
2039 /* caller must free */
2040 certsToBeFreed
.appendCert(issuerCert
);
2042 /* deal with this later */
2043 foundPartialIssuer
= true;
2044 tpDebug("buildCertGroup: PARTIAL Cert FOUND in dbList");
2047 tpDebug("buildCertGroup: Cert FOUND in dbList");
2051 } /* searching DLDB list */
2054 * Note: we don't handle an expired cert returned from tpDbFindIssuerCert()
2055 * in any special way like we do with findIssuerForCertOrCrl().
2056 * tpDbFindIssuerCert() does its best to give us a temporally valid cert; if
2057 * it returns an expired cert (or, if findIssuerForCertOrCrl() gave us an
2058 * expired cert and tpDbFindIssuerCert() could not do any better), that's all
2059 * we have to work with at this point. We'll go back to the top of the loop
2060 * and apply trust settings if enabled; if an expired cert is trusted per
2061 * Trust Settings, we're done. (Note that anchors are fetched from a DLDB
2062 * when Trust Settings are enabled, so even if two roots with the same key
2063 * and subject name are in DLDBs, and one of them is expired, we'll have the
2064 * good one at this time because of tpDbFindIssuerCert()'s ability to find
2067 * If Trust Settings are not enabled, and we have an expired root at this
2068 * point, the expiredRoot mechanism is used to roll back and search for
2069 * an anchor that verifies the last good cert.
2071 if((issuerCert
== NULL
) && /* tpDbFindIssuerCert() hasn't found one and
2072 * we don't have a good one */
2073 (untrustedRoot
!= NULL
)) { /* but we have an untrusted root available*/
2075 * We couldn't find a better issuer, so end loop. In Case 1, we'll look for
2076 * an alternate anchor issuer. If we can't find an anchor, we'll end up using
2077 * the untrusted root.
2079 break; /* from main loop */
2082 if((issuerCert
== NULL
) && /* tpDbFindIssuerCert() hasn't found one and
2083 * we don't have a good one */
2084 (unmatchedKeyIDIssuer
!= NULL
)) { /* but we have an unmatched keyID candidate */
2086 * OK, we'll take the unmatched key id issuer.
2087 * Note we don't have to free unmatchedKeyIDIssuer if we found a good one since
2088 * unmatchedKeyIDIssuer can only come from inCertGroup or gatheredCerts (not from
2091 tpDebug("buildCertGroup: USING unmatched key id issuer %p", unmatchedKeyIDIssuer
);
2092 issuerCert
= unmatchedKeyIDIssuer
;
2093 unmatchedKeyIDIssuer
= NULL
;
2095 if((issuerCert
== NULL
) && /* tpDbFindIssuerCert() hasn't found one and
2096 * we don't have a good one */
2097 (expiredIssuer
!= NULL
)) { /* but we have an expired candidate */
2099 * OK, we'll take the expired issuer.
2100 * Note we don't have to free expiredIssuer if we found a good one since
2101 * expiredIssuer can only come from inCertGroup or gatheredCerts (not from
2104 tpDebug("buildCertGroup: USING expired cert %p", expiredIssuer
);
2105 issuerCert
= expiredIssuer
;
2106 expiredIssuer
= NULL
;
2108 if(issuerCert
== NULL
) {
2109 /* end of search, broken chain */
2114 * One way or the other, we've found a cert which verifies subjectCert.
2115 * Add the issuer to outCertGroup and make it the new thisSubject for
2118 appendCert(issuerCert
);
2119 thisSubject
= issuerCert
;
2120 subjectIsInGroup
= CSSM_TRUE
;
2123 * We've found a (potentially) better chain, so discard the untrusted root.
2124 * Note we don't have to free untrustedRoot because it either:
2125 * -came from inCertGroup or gatherCerts; or
2126 * -came from the dbList and was already added to certToBeFreed.
2128 untrustedRoot
= NULL
;
2132 * This can be NULL if we're evaluating a CRL (and we haven't
2135 endCert
= lastCert();
2138 * This, on the other hand, is always valid. It could be a CRL.
2140 assert(thisSubject
!= NULL
);
2142 if( (actionFlags
& CSSM_TP_ACTION_IMPLICIT_ANCHORS
) &&
2143 ( (endCert
&& endCert
->isSelfSigned()) || expiredRoot
) ) {
2145 * Caller will be satisfied with this; skip further anchor processing.
2147 tpAnchorDebug("buildCertGroup: found IMPLICIT anchor");
2150 if(numAnchorCerts
== 0) {
2151 /* we're probably done */
2154 assert(anchorCerts
!= NULL
);
2156 /*** anchor cert handling ***/
2159 * Case 1: If thisSubject is not a root cert, try to validate with incoming anchor certs.
2161 expiredIssuer
= NULL
;
2162 if(!(endCert
&& endCert
->isSelfSigned())) {
2163 for(certDex
=0; certDex
<numAnchorCerts
; certDex
++) {
2166 anchorInfo
= new TPCertInfo(clHand
,
2168 &anchorCerts
[certDex
],
2173 /* bad anchor cert - ignore it */
2179 * We must subsequently delete anchorInfo one way or the other.
2180 * If we add it to tpCertGroup, we also add it to certsToBeFreed.
2181 * Otherwise we delete it.
2183 if(!anchorInfo
->isIssuerOf(*thisSubject
)) {
2184 /* not this anchor */
2185 tpAnchorDebug("buildCertGroup anchor not issuer");
2191 crtn
= thisSubject
->verifyWithIssuer(anchorInfo
);
2193 if(crtn
== CSSM_OK
) {
2194 if(anchorInfo
->isExpired() || anchorInfo
->isNotValidYet()) {
2195 if(expiredIssuer
== NULL
) {
2197 * Hang on to this one; keep looking for a better one.
2199 tpDebug("buildCertGroup: saving expired anchor %p", anchorInfo
);
2200 expiredIssuer
= anchorInfo
;
2201 /* flag this condition for the switch below */
2202 crtn
= CSSM_CERT_STATUS_EXPIRED
;
2203 expiredIssuer
->isAnchor(true);
2204 assert(!anchorInfo
->isFromInputCerts());
2205 expiredIssuer
->index(certDex
);
2206 certsToBeFreed
.appendCert(expiredIssuer
);
2208 /* else we already have an expired candidate anchor */
2212 * Done with possible expiredIssuer. We don't delete it, since we already added
2213 * it to certsToBeFreed, above.
2215 if(expiredIssuer
!= NULL
) {
2216 tpDebug("buildCertGroup: DISCARDING expired anchor %p", expiredIssuer
);
2217 expiredIssuer
= NULL
;
2223 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
:
2225 * A bit of a corner case. Found an issuer in AnchorCerts, but
2226 * we can't do a signature verify since the issuer has a partial
2227 * public key. Proceed but return
2228 * CSSMERR_TP_CERTIFICATE_CANT_OPERATE.
2230 if(anchorInfo
->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE
)) {
2231 foundPartialIssuer
= true;
2232 crtn
= CSSMERR_TP_CERTIFICATE_CANT_OPERATE
;
2240 /* A fully successful return. */
2241 verifiedToAnchor
= CSSM_TRUE
;
2242 if(anchorInfo
->isSelfSigned()) {
2243 verifiedToRoot
= CSSM_TRUE
;
2247 * Add this anchor cert to the output group
2248 * and to certsToBeFreed.
2250 appendCert(anchorInfo
);
2251 anchorInfo
->isAnchor(true);
2252 assert(!anchorInfo
->isFromInputCerts());
2253 anchorInfo
->index(certDex
);
2254 certsToBeFreed
.appendCert(anchorInfo
);
2255 tpDebug("buildCertGroup: Cert FOUND by signer in AnchorList");
2256 tpAnchorDebug("buildCertGroup: Cert FOUND by signer in AnchorList");
2257 /* one more thing: partial public key processing needed? */
2258 if(foundPartialIssuer
) {
2259 return verifyWithPartialKeys(subjectItem
);
2266 /* continue to next anchor */
2267 if(crtn
!= CSSM_CERT_STATUS_EXPIRED
) {
2268 /* Expired means we're saving it in expiredIssuer */
2269 tpVfyDebug("buildCertGroup found issuer in anchor, BAD SIG");
2275 } /* for each anchor */
2276 } /* thisSubject not a root cert */
2279 * Case 2: Check whether endCert is present in anchor certs.
2281 * Also used to validate an expiredRoot that we pulled off the chain in
2282 * hopes of finding something better (which, if we're here, we haven't done).
2284 * Note that the main loop above did the actual root self-verify test.
2286 if(endCert
|| expiredRoot
) {
2288 TPCertInfo
*theRoot
;
2290 /* this is NOT in our outgoing cert group (yet) */
2291 theRoot
= expiredRoot
;
2296 /* see if that root cert is identical to one of the anchor certs */
2297 for(certDex
=0; certDex
<numAnchorCerts
; certDex
++) {
2298 if(tp_CompareCerts(theRoot
->itemData(), &anchorCerts
[certDex
])) {
2299 /* one fully successful return */
2300 tpAnchorDebug("buildCertGroup: end cert in input AND anchors");
2301 verifiedToAnchor
= CSSM_TRUE
;
2302 theRoot
->isAnchor(true);
2303 if(!theRoot
->isFromInputCerts()) {
2304 /* Don't override index into input certs */
2305 theRoot
->index(certDex
);
2308 /* verified to anchor but caller will see
2309 * CSSMERR_TP_CERT_EXPIRED */
2310 appendCert(expiredRoot
);
2312 /* one more thing: partial public key processing needed? */
2313 if(foundPartialIssuer
) {
2314 return verifyWithPartialKeys(subjectItem
);
2321 tpAnchorDebug("buildCertGroup: end cert in input, NOT anchors");
2323 if(!expiredRoot
&& endCert
->isSelfSigned()) {
2324 /* verified to a root cert which is not an anchor */
2325 /* Generally maps to CSSMERR_TP_INVALID_ANCHOR_CERT by caller */
2326 /* one more thing: partial public key processing needed? */
2327 if(foundPartialIssuer
) {
2328 return verifyWithPartialKeys(subjectItem
);
2334 /* else try finding a good anchor */
2337 /* regardless of anchor search status... */
2339 if(!verifiedToAnchor
&& (expiredIssuer
!= NULL
)) {
2340 /* expiredIssuer here is always an anchor */
2341 tpDebug("buildCertGroup: accepting expired anchor %p", expiredIssuer
);
2342 appendCert(expiredIssuer
);
2343 verifiedToAnchor
= CSSM_TRUE
;
2344 if(expiredIssuer
->isSelfSigned()) {
2345 verifiedToRoot
= CSSM_TRUE
;
2347 /* no matter what, we don't want these */
2349 untrustedRoot
= NULL
;
2354 * Special case: untrustedRoot found, but no luck resolving the problem with
2355 * anchors. Go ahead and (re-)append the untrusted root and return
2357 tpDebug("buildCertGroup: accepted UNTRUSTED root");
2358 appendCert(untrustedRoot
);
2359 if(foundPartialIssuer
) {
2360 return verifyWithPartialKeys(subjectItem
);
2368 * One remaining special case: expiredRoot found in input certs, but
2369 * no luck resolving the problem with the anchors. Go ahead and (re-)append
2370 * the expired root and return.
2372 tpDebug("buildCertGroup: accepting EXPIRED root");
2373 appendCert(expiredRoot
);
2374 if(foundPartialIssuer
) {
2375 return verifyWithPartialKeys(subjectItem
);
2382 /* If we get here, determine if fetching the issuer from the network
2383 * should be attempted: <rdar://6113890&7419584&7422356>
2385 attemptNetworkFetch
= (actionFlags
& CSSM_TP_ACTION_FETCH_CERT_FROM_NET
);
2386 if( (!dbList
|| (dbList
->NumHandles
== 0)) &&
2387 (!anchorCerts
|| (numAnchorCerts
== 0)) ) {
2388 /* DB list is empty *and* anchors are empty; there is no point in going
2389 * out to the network, since we cannot build a chain to a trusted root.
2390 * (This can occur when the caller wants to evaluate a single certificate
2391 * without trying to build the chain, e.g. to check its key usage.)
2393 attemptNetworkFetch
= false;
2397 * If we haven't verified to a root, and net fetch of certs is enabled,
2398 * try to get the issuer of the last cert in the chain from the net.
2399 * If that succeeds, then call ourself recursively to perform the
2400 * whole search again (including comparing to or verifying against
2403 if(!verifiedToRoot
&& !verifiedToAnchor
&&
2404 (endCert
!= NULL
) && attemptNetworkFetch
) {
2405 TPCertInfo
*issuer
= NULL
;
2406 CSSM_RETURN cr
= tpFetchIssuerFromNet(*endCert
,
2412 case CSSMERR_TP_CERTGROUP_INCOMPLETE
:
2413 /* no issuerAltName, no reason to log this */
2417 endCert
->addStatusCode(cr
);
2419 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
:
2420 /* use this one but re-verify later */
2421 foundPartialIssuer
= true;
2426 tpDebug("buildCertGroup: Cert FOUND from Net; recursing");
2428 if(isInGroup(*issuer
)) {
2429 tpDebug("buildCertGroup: Multiple instances of cert from net");
2432 crtn
= CSSMERR_TP_CERTGROUP_INCOMPLETE
;
2436 /* add this fetched cert to constructed group */
2438 issuer
->isFromNet(true);
2439 certsToBeFreed
.appendCert(issuer
);
2442 cr
= buildCertGroup(*issuer
,
2452 CSSM_TRUE
, // subjectIsInGroup
2457 leafKeyUse
, // actually don't care since the leaf will not
2461 verifiedViaTrustSettings
);
2466 /* one more thing: partial public key processing needed? */
2467 if(foundPartialIssuer
) {
2468 return verifyWithPartialKeys(subjectItem
);
2476 /* regardless of outcome, check for partial keys to log per-cert status */
2477 CSSM_RETURN partRtn
= CSSM_OK
;
2478 if(foundPartialIssuer
) {
2479 partRtn
= verifyWithPartialKeys(subjectItem
);
2490 * Called from buildCertGroup as final processing of a constructed
2491 * group when CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE has been
2492 * detected. Perform partial public key processing.
2494 * We don't have to verify every element, just the ones whose
2495 * issuers have partial public keys.
2498 * CSSMERR_TP_CERTIFICATE_CANT_OPERATE in the case of an issuer cert
2499 * with a partial public key which can't be completed.
2500 * CSSMERR_TP_INVALID_CERT_AUTHORITY if sig verify failed with
2501 * a (supposedly) completed partial key
2503 CSSM_RETURN
TPCertGroup::verifyWithPartialKeys(
2504 const TPClItemInfo
&subjectItem
) // Cert or CRL
2506 TPCertInfo
*lastFullKeyCert
= NULL
;
2507 tpDebug("verifyWithPartialKeys top");
2509 /* start from the end - it's easier */
2510 for(int dex
=mNumCerts
-1; dex
>= 0; dex
--) {
2511 TPCertInfo
*thisCert
= mCertInfo
[dex
];
2514 * If this is the start of the cert chain, and it's not being
2515 * used to verify subjectItem, then we're done.
2518 if((void *)thisCert
== (void *)&subjectItem
) {
2519 tpDebug("verifyWithPartialKeys: success at leaf cert");
2523 if(!thisCert
->hasPartialKey()) {
2525 * Good to know. Record this and move on.
2527 lastFullKeyCert
= thisCert
;
2528 tpDebug("full key cert found at index %d", dex
);
2531 if(lastFullKeyCert
== NULL
) {
2533 * No full keys between here and the end!
2535 tpDebug("UNCOMPLETABLE cert at index %d", dex
);
2536 if(thisCert
->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE
)) {
2537 return CSSMERR_TP_CERTIFICATE_CANT_OPERATE
;
2544 /* do the verify - of next cert in chain or of subjectItem */
2545 const TPClItemInfo
*subject
;
2547 subject
= &subjectItem
;
2548 tpDebug("...verifying subject item with partial cert 0");
2551 subject
= mCertInfo
[dex
- 1];
2552 tpDebug("...verifying with partial cert %d", dex
);
2554 CSSM_RETURN crtn
= subject
->verifyWithIssuer(thisCert
,
2557 tpDebug("CERT VERIFY ERROR with partial cert at index %d", dex
);
2558 if(thisCert
->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE
)) {
2559 return CSSMERR_TP_INVALID_CERT_AUTHORITY
;
2567 /* we just verified subjectItem - right? */
2568 assert((void *)mCertInfo
[0] != (void *)&subjectItem
);
2569 tpDebug("verifyWithPartialKeys: success at subjectItem");
2574 * Free records obtained from DBs. Called when these records are not going to
2575 * be passed to caller of CertGroupConstruct or CertGroupVerify.
2577 void TPCertGroup::freeDbRecords()
2579 for(unsigned dex
=0; dex
<mNumCerts
; dex
++) {
2580 TPCertInfo
*certInfo
= mCertInfo
[dex
];
2581 certInfo
->freeUniqueRecord();