2 * Copyright (c) 2000-2011 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 mSigAlg(CSSM_ALGID_NONE
),
79 mIsNotValidYet(false),
83 CSSM_RETURN crtn
= cacheItem(itemData
, copyItemData
);
85 CssmError::throwMe(crtn
);
89 * Fetch standard fields...
90 * Issue name assumes same OID for Certs and CRLs!
92 crtn
= fetchField(&CSSMOID_X509V1IssuerName
, &mIssuerName
);
94 CssmError::throwMe(crtn
);
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.
102 CSSM_DATA_PTR algField
;
103 crtn
= fetchField(&CSSMOID_X509V1SignatureAlgorithmTBS
, &algField
);
106 CssmError::throwMe(crtn
);
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
);
112 CSSM_X509_ALGORITHM_IDENTIFIER
*algId
=
113 (CSSM_X509_ALGORITHM_IDENTIFIER
*)algField
->Data
;
114 bool algFound
= cssmOidToAlg(&algId
->algorithm
, &mSigAlg
);
116 tpErrorLog("TPClItemInfo: unknown signature algorithm\n");
117 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT
);
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
);
126 freeField(&CSSMOID_X509V1SignatureAlgorithmTBS
, algField
);
128 fetchNotBeforeAfter();
129 calculateCurrent(verifyTime
);
137 TPClItemInfo::~TPClItemInfo()
139 tpCertInfoDbg("TPClItemInfo destruct this %p", this);
143 void TPClItemInfo::releaseResources()
145 if(mWeOwnTheData
&& (mItemData
!= NULL
)) {
146 tpFreeCssmData(Allocator::standard(), mItemData
, CSSM_TRUE
);
147 mWeOwnTheData
= false;
151 freeField(&CSSMOID_X509V1IssuerName
, mIssuerName
);
154 if(mCacheHand
!= 0) {
155 mClCalls
.abortCache(mClHand
, mCacheHand
);
159 CFRelease(mNotBefore
);
163 CFRelease(mNotAfter
);
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
175 uint32 NumberOfFields
= 0;
176 CSSM_HANDLE resultHand
= 0;
179 assert(mClCalls
.getField
!= NULL
);
180 assert(mCacheHand
!= 0);
181 crtn
= mClCalls
.getField(
191 if(NumberOfFields
!= 1) {
192 tpErrorLog("TPCertInfo::fetchField: numFields %d, expected 1\n",
193 (int)NumberOfFields
);
195 mClCalls
.abortQuery(mClHand
, resultHand
);
199 /* free arbitrary field obtained from fetchField() */
200 CSSM_RETURN
TPClItemInfo::freeField(
201 const CSSM_OID
*fieldOid
,
202 CSSM_DATA_PTR fieldData
)
204 return CSSM_CL_FreeFieldValue(mClHand
, fieldOid
, fieldData
);
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.
214 CSSM_RETURN
TPClItemInfo::verifyWithIssuer(
215 TPCertInfo
*issuerCert
,
216 TPCertInfo
*paramCert
/* = NULL */) const
220 assert(mClHand
!= 0);
221 assert(issuerCert
->isIssuerOf(*this));
222 assert(mCspHand
!= 0);
225 * Special case: detect partial public key right now; don't even
226 * bother trying the cert verify in that case.
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
;
234 CSSM_CC_HANDLE ccHand
;
235 crtn
= CSSM_CSP_CreateSignatureContext(mCspHand
,
237 NULL
, // Access Creds
238 issuerCert
->pubKey(),
240 if(crtn
!= CSSM_OK
) {
241 tpErrorLog("verifyWithIssuer: CreateSignatureContext error\n");
242 CssmError::throwMe(crtn
);
244 if(paramCert
!= NULL
) {
245 assert(issuerCert
->hasPartialKey());
247 /* add in parameter-bearing key */
248 CSSM_CONTEXT_ATTRIBUTE newAttr
;
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
);
255 tpErrorLog("verifyWithIssuer: CSSM_UpdateContextAttributes error\n");
256 CssmError::throwMe(crtn
);
259 crtn
= mClCalls
.itemVerify(mClHand
,
267 case CSSM_OK
: // success
268 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
: // caller handles
269 tpVfyDebug("verifyWithIssuer GOOD");
272 /* all others appear here as general cert verify error */
273 crtn
= CSSMERR_TP_VERIFICATION_FAILURE
;
274 tpVfyDebug("verifyWithIssuer BAD");
277 CSSM_DeleteContext(ccHand
);
281 CSSM_RETURN
TPClItemInfo::cacheItem(
282 const CSSM_DATA
*itemData
,
283 TPItemCopy copyItemData
)
285 switch(copyItemData
) {
287 mItemData
= const_cast<CSSM_DATA
*>(itemData
);
290 mItemData
= tpMallocCopyCssmData(Allocator::standard(), itemData
);
291 mWeOwnTheData
= true;
295 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
298 /* cache the cert/CRL in the CL */
299 return mClCalls
.cacheItem(mClHand
, mItemData
, &mCacheHand
);
303 * Calculate not before/after times as struct tm. Only throws on
304 * gross error (CSSMERR_TP_INVALID_CERT_POINTER, etc.).
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
311 void TPClItemInfo::fetchNotBeforeAfter()
313 CSSM_DATA_PTR notBeforeField
= NULL
;
314 CSSM_DATA_PTR notAfterField
= NULL
;
315 CSSM_RETURN crtn
= CSSM_OK
;
316 CSSM_X509_TIME
*xTime
;
318 assert(cacheHand() != CSSM_INVALID_HANDLE
);
319 crtn
= fetchField(mClCalls
.notBeforeOid
, ¬BeforeField
);
321 tpErrorLog("fetchNotBeforeAfter: GetField error\n");
322 CssmError::throwMe(mClCalls
.invalidItemRtn
);
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
;
333 crtn
= fetchField(mClCalls
.notAfterOid
, ¬AfterField
);
336 * Tolerate a missing NextUpdate in CRL only
338 if(mClCalls
.notAfterOid
== &CSSMOID_X509V1ValidityNotAfter
) {
339 tpErrorLog("fetchNotBeforeAfter: GetField error\n");
340 crtn
= mClCalls
.invalidItemRtn
;
345 * Fake NextUpdate to be "at the end of time"
347 timeStringToCfDate(CSSM_APPLE_CRL_END_OF_TIME
,
348 strlen(CSSM_APPLE_CRL_END_OF_TIME
),
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
;
363 freeField(mClCalls
.notAfterOid
, notAfterField
);
366 freeField(mClCalls
.notBeforeOid
, notBeforeField
);
368 if(crtn
!= CSSM_OK
) {
369 CssmError::throwMe(crtn
);
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.
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).
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.
386 ModuleNexus
<Mutex
> tpTimeLock
;
388 CSSM_RETURN
TPClItemInfo::calculateCurrent(
389 const char *verifyString
)
391 CFDateRef refTime
= NULL
;
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
;
401 /* time base = right now */
402 refTime
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
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
));
409 return mClCalls
.notValidYetRtn
;
412 mIsNotValidYet
= false;
415 if(compareTimes(refTime
, mNotAfter
) > 0) {
417 tpTimeDbg("\nTP_CERT_EXPIRED: now %g notBefore %g",
418 CFDateGetAbsoluteTime(refTime
), CFDateGetAbsoluteTime(mNotBefore
));
420 return mClCalls
.expiredRtn
;
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.
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
443 TPClItemInfo(clHand
, cspHand
, tpCertClCalls
, certData
,
444 copyCertData
, verifyTime
),
446 mPublicKeyData(NULL
),
449 mIsFromInputCerts(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),
466 mIgnoredError(false),
467 mTrustSettingsKeyUsage(0),
472 tpCertInfoDbg("TPCertInfo construct this %p", this);
473 mDlDbHandle
.DLHandle
= 0;
474 mDlDbHandle
.DBHandle
= 0;
476 /* fetch subject name */
477 crtn
= fetchField(&CSSMOID_X509V1SubjectName
, &mSubjectName
);
481 CssmError::throwMe(crtn
);
484 /* this cert's public key */
485 crtn
= fetchField(&CSSMOID_CSSMKeyStruct
, &mPublicKeyData
);
486 if(crtn
|| (mPublicKeyData
->Length
!= sizeof(CSSM_KEY
))) {
489 CssmError::throwMe(crtn
);
491 mPublicKey
= (CSSM_KEY_PTR
)mPublicKeyData
->Data
;
493 /* calculate other commonly used fields */
494 if(tpCompareCssmData(mSubjectName
, issuerName())) {
496 * Per Radar 3374978, perform complete signature verification
497 * lazily - just check subject/issuer match here.
499 tpAnchorDebug("TPCertInfo potential anchor");
500 mIsRoot
= TRS_NamesMatch
;
503 mIsRoot
= TRS_NotRoot
;
507 /* frees mSubjectName, mIssuerName, mCacheHand via mClHand */
508 TPCertInfo::~TPCertInfo()
510 tpCertInfoDbg("TPCertInfo destruct this %p", this);
514 void TPCertInfo::releaseResources()
517 freeField(&CSSMOID_X509V1SubjectName
, mSubjectName
);
521 freeField(&CSSMOID_CSSMKeyStruct
, mPublicKeyData
);
523 mPublicKeyData
= NULL
;
533 CFRelease(mCertHashStr
);
535 TPClItemInfo::releaseResources();
538 const CSSM_DATA
*TPCertInfo::subjectName()
540 assert(mSubjectName
!= NULL
);
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.
550 bool TPCertInfo::isSelfSigned(bool avoidVerify
)
553 case TRS_NotRoot
: // known not to be root
561 /* else drop through and verify */
562 case TRS_Unknown
: // actually shouldn't happen, but to be safe...
564 /* do the signature verify */
565 if(verifyWithIssuer(this) == CSSM_OK
) {
566 tpAnchorDebug("isSelfSigned anchor verified");
567 mIsRoot
= TRS_IsRoot
;
571 tpAnchorDebug("isSelfSigned anchor vfy FAIL");
572 mIsRoot
= TRS_NotRoot
;
579 * Am I the issuer of the specified subject item? Returns true if so.
580 * Works for subject certs as well as CRLs.
582 bool TPCertInfo::isIssuerOf(
583 const TPClItemInfo
&subject
)
585 assert(mSubjectName
!= NULL
);
586 assert(subject
.issuerName() != NULL
);
587 if(tpCompareCssmData(mSubjectName
, subject
.issuerName())) {
595 bool TPCertInfo::addStatusCode(CSSM_RETURN code
)
598 mStatusCodes
= (CSSM_RETURN
*)realloc(mStatusCodes
,
599 mNumStatusCodes
* sizeof(CSSM_RETURN
));
600 mStatusCodes
[mNumStatusCodes
- 1] = code
;
601 return isStatusFatal(code
);
604 bool TPCertInfo::hasStatusCode(CSSM_RETURN code
)
606 for(unsigned dex
=0; dex
<mNumStatusCodes
; dex
++) {
607 if(mStatusCodes
[dex
] == code
) {
614 bool TPCertInfo::isStatusFatal(CSSM_RETURN code
)
616 for(unsigned dex
=0; dex
<mNumAllowedErrs
; dex
++) {
617 if(mAllowedErrs
[dex
] == code
) {
618 tpTrustSettingsDbg("isStatusFatal(%ld): ALLOWED", (unsigned long)code
);
619 mIgnoredError
= true;
627 * Indicate whether this cert's public key is a CSSM_KEYATTR_PARTIAL
630 bool TPCertInfo::hasPartialKey()
632 if(mPublicKey
->KeyHeader
.KeyAttr
& CSSM_KEYATTR_PARTIAL
) {
643 bool TPCertInfo::shouldReject()
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
660 CSSM_DATA _UTN_UF_H_ISSUER
= { sizeof(_UTN_UF_H_ISSUER_BYTES
), _UTN_UF_H_ISSUER_BYTES
};
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
675 const CSSM_DATA
*issuer
=issuerName();
676 if(!issuer
|| !(tpCompareCssmData(issuer
, &_UTN_UF_H_ISSUER
)))
679 CSSM_DATA
*serialNumber
=NULL
;
680 CSSM_RETURN crtn
= fetchField(&CSSMOID_X509V1SerialNumber
, &serialNumber
);
681 if(crtn
|| !serialNumber
)
684 CSSM_DATA
*p
=_UTN_UF_H_SERIALS
;
687 if(tpCompareCssmData(serialNumber
, p
)) {
689 addStatusCode(CSSMERR_TP_CERT_REVOKED
);
694 freeField(&CSSMOID_X509V1SerialNumber
, serialNumber
);
699 * Evaluate trust settings; returns true in *foundMatchingEntry if positive
700 * match found - i.e., cert chain construction is done.
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
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).
714 if(mTrustSettingsEvaluated
) {
715 bool doFlush
= false;
716 if(mTrustSettingsKeyUsage
!= kSecTrustSettingsKeyUseAny
) {
717 if(keyUse
== kSecTrustSettingsKeyUseAny
) {
718 /* now want "any", checked something else before */
721 else if((keyUse
& mTrustSettingsKeyUsage
) != keyUse
) {
722 /* want bits that we didn't ask for before */
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
) {
738 /* else we can safely use the cached values */
740 if(!mTrustSettingsEvaluated
) {
742 if(mCertHashStr
== NULL
) {
743 const CSSM_DATA
*certData
= itemData();
744 mCertHashStr
= SecTrustSettingsCertHashStrFromData(certData
->Data
,
748 OSStatus ortn
= SecTrustSettingsEvaluateCert(mCertHashStr
,
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()).
761 &mTrustSettingsDomain
,
764 &mTrustSettingsResult
,
765 &mTrustSettingsFoundMatchingEntry
,
766 &mTrustSettingsFoundAnyEntry
);
768 tpTrustSettingsDbg("evaluateTrustSettings: SecTrustSettingsEvaluateCert error!");
771 mTrustSettingsEvaluated
= true;
772 mTrustSettingsKeyUsage
= keyUse
;
774 if(mTrustSettingsFoundMatchingEntry
) {
775 tpTrustSettingsDbg("evaluateTrustSettings: found for %p result %d",
776 this, (int)mTrustSettingsResult
);
779 /* one more thing... */
781 return CSSMERR_TP_INVALID_CERTIFICATE
;
784 *foundMatchingEntry
= mTrustSettingsFoundMatchingEntry
;
785 *foundAnyEntry
= mTrustSettingsFoundAnyEntry
;
790 /* true means "verification terminated due to user trust setting" */
791 bool TPCertInfo::trustSettingsFound()
793 switch(mTrustSettingsResult
) {
794 case kSecTrustSettingsResultUnspecified
: /* entry but not definitive */
795 case kSecTrustSettingsResultInvalid
: /* no entry */
803 * Determine if this has an empty SubjectName field. Returns true if so.
805 bool TPCertInfo::hasEmptySubjectName()
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.
812 if((mSubjectName
== NULL
) || (mSubjectName
->Length
<= 4)) {
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.
825 void TPCertInfo::freeUniqueRecord()
827 if(mUniqueRecord
== NULL
) {
830 tpDbDebug("freeUniqueRecord: freeing cert record %p", mUniqueRecord
);
831 CSSM_DL_FreeUniqueRecord(mDlDbHandle
, mUniqueRecord
);
835 *** TPCertGroup class
838 /* build empty group */
839 TPCertGroup::TPCertGroup(
841 TPGroupOwner whoOwns
) :
848 tpCertInfoDbg("TPCertGroup simple construct this %p", this);
849 /* nothing for now */
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.
858 TPCertGroup::TPCertGroup(
859 const CSSM_CERTGROUP
&CertGroupFrag
,
860 CSSM_CL_HANDLE clHand
,
861 CSSM_CSP_HANDLE cspHand
,
863 const char *verifyTime
, // may be NULL
864 bool firstCertMustBeValid
,
865 TPGroupOwner whoOwns
) :
872 tpCertInfoDbg("TPCertGroup hard construct this %p", this);
874 /* verify input args */
875 if(cspHand
== CSSM_INVALID_HANDLE
) {
876 CssmError::throwMe(CSSMERR_TP_INVALID_CSP_HANDLE
);
878 if(clHand
== CSSM_INVALID_HANDLE
) {
879 CssmError::throwMe(CSSMERR_TP_INVALID_CL_HANDLE
);
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
);
888 if(CertGroupFrag
.CertGroupType
!= CSSM_CERTGROUP_DATA
) {
889 CssmError::throwMe(CSSMERR_TP_INVALID_CERTGROUP
);
891 switch(CertGroupFrag
.CertType
) {
892 case CSSM_CERT_X_509v1
:
893 case CSSM_CERT_X_509v2
:
894 case CSSM_CERT_X_509v3
:
897 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT
);
899 switch(CertGroupFrag
.CertEncoding
) {
900 case CSSM_CERT_ENCODING_BER
:
901 case CSSM_CERT_ENCODING_DER
:
904 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT
);
908 * Add remaining input certs to mCertInfo.
910 TPCertInfo
*certInfo
= NULL
;
911 for(unsigned certDex
=0; certDex
<CertGroupFrag
.NumCerts
; certDex
++) {
913 certInfo
= new TPCertInfo(clHand
,
915 &CertGroupFrag
.GroupList
.CertList
[certDex
],
916 TIC_NoCopy
, // caller owns
920 if((certDex
== 0) && firstCertMustBeValid
) {
921 CssmError::throwMe(CSSMERR_TP_INVALID_CERTIFICATE
);
923 /* else just ignore this cert */
926 certInfo
->index(certDex
);
927 appendCert(certInfo
);
932 * Deletes contents of mCertInfo[] if appropriate.
934 TPCertGroup::~TPCertGroup()
936 if(mWhoOwns
== TGO_Group
) {
938 for(i
=0; i
<mNumCerts
; i
++) {
942 mAlloc
.free(mCertInfo
);
945 /* add/remove/access TPTCertInfo's. */
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.
952 void TPCertGroup::appendCert(
953 TPCertInfo
*certInfo
) // appends to end of mCertInfo
955 if(mNumCerts
== mSizeofCertInfo
) {
956 if(mSizeofCertInfo
== 0) {
957 /* appending to empty array */
961 mSizeofCertInfo
*= 2;
963 mCertInfo
= (TPCertInfo
**)mAlloc
.realloc(mCertInfo
,
964 mSizeofCertInfo
* sizeof(TPCertInfo
*));
966 mCertInfo
[mNumCerts
++] = certInfo
;
969 TPCertInfo
*TPCertGroup::certAtIndex(
972 if(index
> (mNumCerts
- 1)) {
973 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
975 return mCertInfo
[index
];
978 TPCertInfo
*TPCertGroup::removeCertAtIndex(
979 unsigned index
) // doesn't delete the cert, just
980 // removes it from out list
982 if(index
> (mNumCerts
- 1)) {
983 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
985 TPCertInfo
*rtn
= mCertInfo
[index
];
987 /* removed requested element and compact remaining array */
989 for(i
=index
; i
<(mNumCerts
- 1); i
++) {
990 mCertInfo
[i
] = mCertInfo
[i
+1];
996 TPCertInfo
*TPCertGroup::firstCert()
999 /* the caller really should not do this... */
1000 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
1003 return mCertInfo
[0];
1007 TPCertInfo
*TPCertGroup::lastCert()
1009 if(mNumCerts
== 0) {
1013 return mCertInfo
[mNumCerts
- 1];
1017 /* build a CSSM_CERTGROUP corresponding with our mCertInfo */
1018 CSSM_CERTGROUP_PTR
TPCertGroup::buildCssmCertGroup()
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) {
1028 cgrp
->GroupList
.CertList
= NULL
;
1031 cgrp
->GroupList
.CertList
= (CSSM_DATA_PTR
)mAlloc
.calloc(mNumCerts
,
1033 for(unsigned i
=0; i
<mNumCerts
; i
++) {
1034 tpCopyCssmData(mAlloc
, mCertInfo
[i
]->itemData(),
1035 &cgrp
->GroupList
.CertList
[i
]);
1040 /* build a CSSM_TP_APPLE_EVIDENCE_INFO array */
1041 CSSM_TP_APPLE_EVIDENCE_INFO
*TPCertGroup::buildCssmEvidenceInfo()
1043 CSSM_TP_APPLE_EVIDENCE_INFO
*infoArray
;
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
];
1051 /* first the booleans */
1052 if(certInfo
->isExpired()) {
1053 evInfo
->StatusBits
|= CSSM_CERT_STATUS_EXPIRED
;
1055 if(certInfo
->isNotValidYet()) {
1056 evInfo
->StatusBits
|= CSSM_CERT_STATUS_NOT_VALID_YET
;
1058 if(certInfo
->isAnchor()) {
1059 tpAnchorDebug("buildCssmEvidenceInfo: flagging IS_IN_ANCHORS");
1060 evInfo
->StatusBits
|= CSSM_CERT_STATUS_IS_IN_ANCHORS
;
1062 if(certInfo
->dlDbHandle().DLHandle
== 0) {
1063 if(certInfo
->isFromNet()) {
1064 evInfo
->StatusBits
|= CSSM_CERT_STATUS_IS_FROM_NET
;
1066 else if(certInfo
->isFromInputCerts()) {
1067 evInfo
->StatusBits
|= CSSM_CERT_STATUS_IS_IN_INPUT_CERTS
;
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
;
1079 case kSecTrustSettingsResultDeny
:
1080 evInfo
->StatusBits
|= CSSM_CERT_STATUS_TRUST_SETTINGS_DENY
;
1083 case kSecTrustSettingsResultUnspecified
:
1084 case kSecTrustSettingsResultInvalid
:
1088 if(certInfo
->isSelfSigned(avoidVerify
)) {
1089 evInfo
->StatusBits
|= CSSM_CERT_STATUS_IS_ROOT
;
1091 if(certInfo
->ignoredError()) {
1092 evInfo
->StatusBits
|= CSSM_CERT_STATUS_TRUST_SETTINGS_IGNORED_ERROR
;
1094 unsigned numCodes
= certInfo
->numStatusCodes();
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
];
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
;
1112 case kSecTrustSettingsDomainAdmin
:
1113 whichDomain
= CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_ADMIN
;
1115 case kSecTrustSettingsDomainSystem
:
1116 whichDomain
= CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_SYSTEM
;
1119 evInfo
->StatusBits
|= whichDomain
;
1121 evInfo
->Index
= certInfo
->index();
1122 evInfo
->DlDbHandle
= certInfo
->dlDbHandle();
1123 evInfo
->UniqueRecord
= certInfo
->uniqueRecord();
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.
1133 CSSM_RETURN
TPCertGroup::getReturnCode(
1134 CSSM_RETURN constructStatus
,
1135 CSSM_RETURN policyStatus
,
1136 CSSM_APPLE_TP_ACTION_FLAGS actionFlags
)
1138 if(constructStatus
) {
1139 /* CSSMERR_TP_NOT_TRUSTED, CSSMERR_TP_INVALID_ANCHOR_CERT, gross errors */
1140 return constructStatus
;
1143 bool expired
= false;
1144 bool postdated
= false;
1145 bool allowExpiredRoot
= (actionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT
) ?
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
) ?
1152 /* check for expired, not valid yet */
1153 for(unsigned i
=0; i
<mNumCerts
; i
++) {
1154 TPCertInfo
*ci
= mCertInfo
[i
];
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.
1160 if(ci
->isExpired() &&
1161 !(allowExpiredRoot
&& ci
->isSelfSigned(true)) && // allowed globally
1162 ci
->isStatusFatal(CSSMERR_TP_CERT_EXPIRED
)) { // allowed for this cert
1165 if(ci
->isNotValidYet() &&
1166 ci
->isStatusFatal(CSSMERR_TP_CERT_NOT_VALID_YET
)) {
1170 if(expired
&& !allowExpired
) {
1171 return CSSMERR_TP_CERT_EXPIRED
;
1173 if(postdated
&& !allowPostdated
) {
1174 return CSSMERR_TP_CERT_NOT_VALID_YET
;
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
);
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
;
1193 tpDebug("getReturnCode: revocation check %s for cert %d",
1194 (ci
->revokeCheckGood()) ? "GOOD" : "OK", i
);
1199 return policyStatus
;
1202 /* set all TPCertInfo.mUsed flags false */
1203 void TPCertGroup::setAllUnused()
1205 for(unsigned dex
=0; dex
<mNumCerts
; dex
++) {
1206 mCertInfo
[dex
]->used(false);
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.
1219 bool TPCertGroup::isAllowedError(
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 */
1234 /* not a candidate for cert-chain-wide allowedErrors */
1238 for(unsigned dex
=0; dex
<mNumCerts
; dex
++) {
1239 if(!mCertInfo
[dex
]->isStatusFatal(code
)) {
1240 tpTrustSettingsDbg("TPCertGroup::isAllowedError: allowing for cert %u",
1246 /* every cert thought this was fatal; it is. */
1251 * Determine if we already have the specified cert in this group.
1253 bool TPCertGroup::isInGroup(TPCertInfo
&certInfo
)
1255 for(unsigned dex
=0; dex
<mNumCerts
; dex
++) {
1256 if(tpCompareCssmData(certInfo
.itemData(), mCertInfo
[dex
]->itemData())) {
1264 * Encode issuing certs in this group as a PEM-encoded data blob.
1267 void TPCertGroup::encodeIssuers(CSSM_DATA
&issuers
)
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.
1272 issuers
.Data
= NULL
;
1274 CFMutableArrayRef certArray
= CFArrayCreateMutable(kCFAllocatorDefault
,
1275 0, &kCFTypeArrayCallBacks
);
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 */
1284 CSSM_DATA
*cssmData
= (CSSM_DATA
*)((certInfo
) ? certInfo
->itemData() : NULL
);
1285 if(!cssmData
|| !cssmData
->Data
|| !cssmData
->Length
) {
1288 CFDataRef dataRef
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
1289 (const UInt8
*)cssmData
->Data
, cssmData
->Length
,
1294 SecCertificateRef certRef
= SecCertificateCreateWithData(kCFAllocatorDefault
,
1300 CFArrayAppendValue(certArray
, certRef
);
1304 CFDataRef exportedPEMData
= NULL
;
1305 OSStatus status
= SecItemExport(certArray
,
1306 kSecFormatPEMSequence
,
1310 CFRelease(certArray
);
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
);
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.
1328 TPCertInfo
*TPCertGroup::findIssuerForCertOrCrl(
1329 const TPClItemInfo
&subject
,
1330 bool &partialIssuerKey
)
1332 partialIssuerKey
= false;
1333 TPCertInfo
*expiredIssuer
= NULL
;
1335 for(unsigned certDex
=0; certDex
<mNumCerts
; certDex
++) {
1336 TPCertInfo
*certInfo
= certAtIndex(certDex
);
1338 /* has this one already been used in this search? */
1339 if(certInfo
->used()) {
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
);
1349 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
:
1350 /* issuer OK, check sig later */
1351 partialIssuerKey
= true;
1355 * Temporal validity check: if we're not already holding an expired
1356 * issuer, and this one's invalid, hold it and keep going.
1358 if((crtn
== CSSM_OK
) && (expiredIssuer
== NULL
)) {
1359 if(certInfo
->isExpired() || certInfo
->isNotValidYet()) {
1360 tpDebug("findIssuerForCertOrCrl: holding expired cert %p",
1362 expiredIssuer
= certInfo
;
1367 certInfo
->used(true);
1370 /* just skip this one and keep looking */
1371 tpVfyDebug("findIssuerForCertOrCrl issuer/subj match BAD SIG");
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
;
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).
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
1402 * Other interesting status is returned via the verifiedToRoot and
1403 * verifiedToAnchor flags.
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.
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
,
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
1427 TPCertGroup
&certsToBeFreed
,
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.
1438 TPCertGroup
*gatheredCerts
,
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
1447 CSSM_BOOL subjectIsInGroup
,
1450 * CSSM_TP_ACTION_FETCH_CERT_FROM_NET,
1451 * CSSM_TP_ACTION_TRUST_SETTING,
1452 * CSSM_TP_ACTION_IMPLICIT_ANCHORS are interesting
1454 CSSM_APPLE_TP_ACTION_FLAGS actionFlags
,
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
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
1467 const TPClItemInfo
*thisSubject
= &subjectItem
;
1468 CSSM_RETURN crtn
= CSSM_OK
;
1469 TPCertInfo
*issuerCert
= NULL
;
1471 TPCertInfo
*anchorInfo
= NULL
;
1472 bool foundPartialIssuer
= false;
1473 bool attemptNetworkFetch
= false;
1474 CSSM_BOOL firstSubjectIsInGroup
= subjectIsInGroup
;
1475 TPCertInfo
*endCert
;
1477 tpVfyDebug("buildCertGroup top");
1479 /* possible expired root which we'll only use if we can't find
1481 TPCertInfo
*expiredRoot
= NULL
;
1483 /* and the general case of an expired or not yet valid cert */
1484 TPCertInfo
*expiredIssuer
= NULL
;
1486 verifiedToRoot
= CSSM_FALSE
;
1487 verifiedToAnchor
= CSSM_FALSE
;
1488 verifiedViaTrustSettings
= CSSM_FALSE
;
1490 /*** main loop to seach inCertGroup and dbList ***
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)
1497 * -- or no more certs to add to chain.
1501 * Top of loop: thisSubject is the item we're trying to verify.
1504 /* is thisSubject a root cert or listed in user trust list? */
1505 if(subjectIsInGroup
) {
1506 TPCertInfo
*subjCert
= lastCert();
1507 assert(subjCert
!= NULL
);
1509 if(actionFlags
& CSSM_TP_ACTION_TRUST_SETTINGS
) {
1510 assert(policyOid
!= NULL
);
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.
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.
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? */
1532 localKeyUse
= kSecTrustSettingsKeyUseSignCert
| kSecTrustSettingsKeyUseSignRevocation
;
1533 /* and if necessary */
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
);
1543 /* this is only a dire error */
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
);
1557 if(trustSettingsFound
) {
1558 switch(subjCert
->trustSettingsResult()) {
1559 case kSecTrustSettingsResultInvalid
:
1560 /* should not happen... */
1562 crtn
= CSSMERR_TP_INTERNAL_ERROR
;
1564 case kSecTrustSettingsResultTrustRoot
:
1565 case kSecTrustSettingsResultTrustAsRoot
:
1566 tpTrustSettingsDbg("Trust[As]Root found");
1569 case kSecTrustSettingsResultDeny
:
1570 tpTrustSettingsDbg("TrustResultDeny found");
1571 crtn
= CSSMERR_APPLETP_TRUST_SETTING_DENY
;
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
1578 tpTrustSettingsDbg("TrustResultUnspecified found");
1579 goto post_trust_setting
;
1581 tpTrustSettingsDbg("Unknown TrustResult (%d)",
1582 (int)subjCert
->trustSettingsResult());
1583 crtn
= CSSMERR_TP_INTERNAL_ERROR
;
1586 /* cleanup partial key processing */
1587 verifiedViaTrustSettings
= CSSM_TRUE
;
1590 } /* CSSM_TP_ACTION_TRUST_SETTING */
1593 if(subjCert
->isSelfSigned()) {
1594 /* We're at the end of the chain. */
1595 verifiedToRoot
= CSSM_TRUE
;
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.
1603 if((subjCert
->isExpired() || subjCert
->isNotValidYet()) &&
1604 (!firstSubjectIsInGroup
|| (mNumCerts
> 1))) {
1605 tpDebug("buildCertGroup: EXPIRED ROOT %p, looking for good one", subjCert
);
1606 expiredRoot
= subjCert
;
1608 /* roll back to previous cert */
1611 if(mNumCerts
== 0) {
1612 /* roll back to caller's initial condition */
1613 thisSubject
= &subjectItem
;
1616 thisSubject
= lastCert();
1619 break; /* out of main loop */
1623 * If this non-root cert is in the provided anchors list,
1624 * we can stop building the chain at this point.
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.)
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)) {
1640 /* roll back to previous cert */
1643 if(mNumCerts
== 0) {
1644 /* roll back to caller's initial condition */
1645 thisSubject
= &subjectItem
;
1648 thisSubject
= lastCert();
1650 tpAnchorDebug("buildCertGroup: CA cert in input AND anchors");
1653 if(subjCert
->isExpired() || subjCert
->isNotValidYet()) {
1654 crtn
= CSSM_CERT_STATUS_EXPIRED
;
1658 subjCert
->isAnchor(true);
1659 verifiedToAnchor
= CSSM_TRUE
;
1660 tpAnchorDebug("buildCertGroup: leaf cert in input AND anchors");
1662 break; /* out of anchor-checking loop */
1665 if(foundNonRootAnchor
) {
1666 break; /* out of main loop */
1670 } /* subjectIsInGroup */
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.
1682 if(inCertGroup
!= NULL
) {
1683 bool partial
= false;
1684 issuerCert
= inCertGroup
->findIssuerForCertOrCrl(*thisSubject
,
1687 issuerCert
->isFromInputCerts(true);
1689 /* deal with this later */
1690 foundPartialIssuer
= true;
1691 tpDebug("buildCertGroup: PARTIAL Cert FOUND in inCertGroup");
1694 tpDebug("buildCertGroup: Cert FOUND in inCertGroup");
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
;
1705 /* else we already have an expired issuer candidate */
1708 /* unconditionally done with possible expiredIssuer */
1710 if(expiredIssuer
!= NULL
) {
1711 tpDebug("buildCertGroup: DISCARDING expired cert %p (1)", expiredIssuer
);
1714 expiredIssuer
= NULL
;
1718 if((issuerCert
== NULL
) && (gatheredCerts
!= NULL
)) {
1719 bool partial
= false;
1720 issuerCert
= gatheredCerts
->findIssuerForCertOrCrl(*thisSubject
,
1724 /* deal with this later */
1725 foundPartialIssuer
= true;
1726 tpDebug("buildCertGroup: PARTIAL Cert FOUND in gatheredCerts");
1729 tpDebug("buildCertGroup: Cert FOUND in gatheredCerts");
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
;
1741 /* else we already have an expired issuer candidate */
1744 /* unconditionally done with possible expiredIssuer */
1746 if(expiredIssuer
!= NULL
) {
1747 tpDebug("buildCertGroup: DISCARDING expired cert %p (2)", expiredIssuer
);
1750 expiredIssuer
= NULL
;
1754 if((issuerCert
== NULL
) && (dbList
!= NULL
)) {
1755 /* Issuer not in incoming cert group or gathered certs. Search DBList. */
1756 bool partial
= false;
1758 issuerCert
= tpDbFindIssuerCert(mAlloc
,
1769 /* unconditionally done with possible expiredIssuer */
1771 if(expiredIssuer
!= NULL
) {
1772 tpDebug("buildCertGroup: DISCARDING expired cert %p (3)", expiredIssuer
);
1775 expiredIssuer
= NULL
;
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.
1784 if(isInGroup(*issuerCert
)) {
1785 tpDebug("buildCertGroup: Multiple instances of cert");
1790 /* caller must free */
1791 certsToBeFreed
.appendCert(issuerCert
);
1793 /* deal with this later */
1794 foundPartialIssuer
= true;
1795 tpDebug("buildCertGroup: PARTIAL Cert FOUND in dbList");
1798 tpDebug("buildCertGroup: Cert FOUND in dbList");
1802 } /* searching DLDB list */
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
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.
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 */
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
1832 tpDebug("buildCertGroup: USING expired cert %p", expiredIssuer
);
1833 issuerCert
= expiredIssuer
;
1834 expiredIssuer
= NULL
;
1836 if(issuerCert
== NULL
) {
1837 /* end of search, broken chain */
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
1846 appendCert(issuerCert
);
1847 thisSubject
= issuerCert
;
1848 subjectIsInGroup
= CSSM_TRUE
;
1853 * This can be NULL if we're evaluating a CRL (and we haven't
1856 endCert
= lastCert();
1859 * This, on the other hand, is always valid. It could be a CRL.
1861 assert(thisSubject
!= NULL
);
1863 if( (actionFlags
& CSSM_TP_ACTION_IMPLICIT_ANCHORS
) &&
1864 ( (endCert
&& endCert
->isSelfSigned()) || expiredRoot
) ) {
1866 * Caller will be satisfied with this; skip further anchor processing.
1868 tpAnchorDebug("buildCertGroup: found IMPLICIT anchor");
1871 if(numAnchorCerts
== 0) {
1872 /* we're probably done */
1875 assert(anchorCerts
!= NULL
);
1877 /*** anchor cert handling ***/
1880 * Case 1: If thisSubject is not a root cert, try to validate with incoming anchor certs.
1882 expiredIssuer
= NULL
;
1883 if(!(endCert
&& endCert
->isSelfSigned())) {
1884 for(certDex
=0; certDex
<numAnchorCerts
; certDex
++) {
1887 anchorInfo
= new TPCertInfo(clHand
,
1889 &anchorCerts
[certDex
],
1894 /* bad anchor cert - ignore it */
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.
1904 if(!anchorInfo
->isIssuerOf(*thisSubject
)) {
1905 /* not this anchor */
1906 tpAnchorDebug("buildCertGroup anchor not issuer");
1912 crtn
= thisSubject
->verifyWithIssuer(anchorInfo
);
1914 if(crtn
== CSSM_OK
) {
1915 if(anchorInfo
->isExpired() || anchorInfo
->isNotValidYet()) {
1916 if(expiredIssuer
== NULL
) {
1918 * Hang on to this one; keep looking for a better one.
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
);
1929 /* else we already have an expired candidate anchor */
1933 * Done with possible expiredIssuer. We don't delete it, since we already added
1934 * it to certsToBeFreed, above.
1936 if(expiredIssuer
!= NULL
) {
1937 tpDebug("buildCertGroup: DISCARDING expired anchor %p", expiredIssuer
);
1938 expiredIssuer
= NULL
;
1944 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
:
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.
1951 if(anchorInfo
->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE
)) {
1952 foundPartialIssuer
= true;
1953 crtn
= CSSMERR_TP_CERTIFICATE_CANT_OPERATE
;
1961 /* A fully successful return. */
1962 verifiedToAnchor
= CSSM_TRUE
;
1963 if(anchorInfo
->isSelfSigned()) {
1964 verifiedToRoot
= CSSM_TRUE
;
1968 * Add this anchor cert to the output group
1969 * and to certsToBeFreed.
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
);
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");
1996 } /* for each anchor */
1997 } /* thisSubject not a root cert */
2000 * Case 2: Check whether endCert is present in anchor certs.
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).
2005 * Note that the main loop above did the actual root self-verify test.
2007 if(endCert
|| expiredRoot
) {
2009 TPCertInfo
*theRoot
;
2011 /* this is NOT in our outgoing cert group (yet) */
2012 theRoot
= expiredRoot
;
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
);
2029 /* verified to anchor but caller will see
2030 * CSSMERR_TP_CERT_EXPIRED */
2031 appendCert(expiredRoot
);
2033 /* one more thing: partial public key processing needed? */
2034 if(foundPartialIssuer
) {
2035 return verifyWithPartialKeys(subjectItem
);
2042 tpAnchorDebug("buildCertGroup: end cert in input, NOT anchors");
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
);
2055 /* else try finding a good anchor */
2058 /* regardless of anchor search status... */
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
;
2068 /* no matter what, we don't want this one */
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.
2078 tpDebug("buildCertGroup: accepting EXPIRED root");
2079 appendCert(expiredRoot
);
2080 if(foundPartialIssuer
) {
2081 return verifyWithPartialKeys(subjectItem
);
2088 /* If we get here, determine if fetching the issuer from the network
2089 * should be attempted: <rdar://6113890&7419584&7422356>
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.)
2099 attemptNetworkFetch
= false;
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
2109 if(!verifiedToRoot
&& !verifiedToAnchor
&&
2110 (endCert
!= NULL
) && attemptNetworkFetch
) {
2111 TPCertInfo
*issuer
= NULL
;
2112 CSSM_RETURN cr
= tpFetchIssuerFromNet(*endCert
,
2118 case CSSMERR_TP_CERTGROUP_INCOMPLETE
:
2119 /* no issuerAltName, no reason to log this */
2123 endCert
->addStatusCode(cr
);
2125 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
:
2126 /* use this one but re-verify later */
2127 foundPartialIssuer
= true;
2132 tpDebug("buildCertGroup: Cert FOUND from Net; recursing");
2134 if(isInGroup(*issuer
)) {
2135 tpDebug("buildCertGroup: Multiple instances of cert from net");
2138 crtn
= CSSMERR_TP_CERTGROUP_INCOMPLETE
;
2142 /* add this fetched cert to constructed group */
2144 issuer
->isFromNet(true);
2145 certsToBeFreed
.appendCert(issuer
);
2148 cr
= buildCertGroup(*issuer
,
2158 CSSM_TRUE
, // subjectIsInGroup
2163 leafKeyUse
, // actually don't care since the leaf will not
2167 verifiedViaTrustSettings
);
2172 /* one more thing: partial public key processing needed? */
2173 if(foundPartialIssuer
) {
2174 return verifyWithPartialKeys(subjectItem
);
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
);
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.
2200 * We don't have to verify every element, just the ones whose
2201 * issuers have partial public keys.
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
2209 CSSM_RETURN
TPCertGroup::verifyWithPartialKeys(
2210 const TPClItemInfo
&subjectItem
) // Cert or CRL
2212 TPCertInfo
*lastFullKeyCert
= NULL
;
2213 tpDebug("verifyWithPartialKeys top");
2215 /* start from the end - it's easier */
2216 for(int dex
=mNumCerts
-1; dex
>= 0; dex
--) {
2217 TPCertInfo
*thisCert
= mCertInfo
[dex
];
2220 * If this is the start of the cert chain, and it's not being
2221 * used to verify subjectItem, then we're done.
2224 if((void *)thisCert
== (void *)&subjectItem
) {
2225 tpDebug("verifyWithPartialKeys: success at leaf cert");
2229 if(!thisCert
->hasPartialKey()) {
2231 * Good to know. Record this and move on.
2233 lastFullKeyCert
= thisCert
;
2234 tpDebug("full key cert found at index %d", dex
);
2237 if(lastFullKeyCert
== NULL
) {
2239 * No full keys between here and the end!
2241 tpDebug("UNCOMPLETABLE cert at index %d", dex
);
2242 if(thisCert
->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE
)) {
2243 return CSSMERR_TP_CERTIFICATE_CANT_OPERATE
;
2250 /* do the verify - of next cert in chain or of subjectItem */
2251 const TPClItemInfo
*subject
;
2253 subject
= &subjectItem
;
2254 tpDebug("...verifying subject item with partial cert 0");
2257 subject
= mCertInfo
[dex
- 1];
2258 tpDebug("...verifying with partial cert %d", dex
);
2260 CSSM_RETURN crtn
= subject
->verifyWithIssuer(thisCert
,
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
;
2273 /* we just verified subjectItem - right? */
2274 assert((void *)mCertInfo
[0] != (void *)&subjectItem
);
2275 tpDebug("verifyWithPartialKeys: success at subjectItem");
2280 * Free records obtained from DBs. Called when these records are not going to
2281 * be passed to caller of CertGroupConstruct or CertGroupVerify.
2283 void TPCertGroup::freeDbRecords()
2285 for(unsigned dex
=0; dex
<mNumCerts
; dex
++) {
2286 TPCertInfo
*certInfo
= mCertInfo
[dex
];
2287 certInfo
->freeUniqueRecord();