2 * Copyright (c) 2000-2001 Apple Computer, 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.h - TP's private certificate info classes
22 * Written 10/23/2000 by Doug Mitchell.
25 #include "TPCertInfo.h"
26 #include "tpdebugging.h"
28 #include "certGroupUtils.h"
29 #include "TPDatabase.h"
30 #include "TPNetwork.h"
31 #include <Security/cssmapi.h>
32 #include <Security/x509defs.h>
33 #include <Security/oidscert.h>
34 #include <Security/oidsalg.h>
35 #include <string.h> /* for memcmp */
36 #include <Security/threading.h> /* for Mutex */
37 #include <Security/globalizer.h>
38 #include <Security/debugging.h>
39 #include <Security/cssmapple.h>
41 #define tpTimeDbg(args...) secdebug("tpTime", ## args)
42 #define tpCertInfoDbg(args...) secdebug("tpCert", ## args)
44 static const TPClItemCalls tpCertClCalls
=
46 CSSM_CL_CertGetFirstCachedFieldValue
,
47 CSSM_CL_CertAbortQuery
,
49 CSSM_CL_CertAbortCache
,
51 &CSSMOID_X509V1ValidityNotBefore
,
52 &CSSMOID_X509V1ValidityNotAfter
,
53 CSSMERR_TP_INVALID_CERT_POINTER
,
54 CSSMERR_TP_CERT_EXPIRED
,
55 CSSMERR_TP_CERT_NOT_VALID_YET
58 TPClItemInfo::TPClItemInfo(
59 CSSM_CL_HANDLE clHand
,
60 CSSM_CSP_HANDLE cspHand
,
61 const TPClItemCalls
&clCalls
,
62 const CSSM_DATA
*itemData
,
63 TPItemCopy copyItemData
,
64 const char *verifyTime
) // may be NULL
73 mSigAlg(CSSM_ALGID_NONE
),
75 mIsNotValidYet(false),
79 cacheItem(itemData
, copyItemData
);
81 * Fetch standard fields...
82 * Issue name assumes same OID for Certs and CRLs!
84 CSSM_RETURN crtn
= fetchField(&CSSMOID_X509V1IssuerName
, &mIssuerName
);
86 CssmError::throwMe(crtn
);
90 * Signing algorithm, infer from TBS algId
91 * Note this assumesÊthat the OID for fetching this field is the
92 * same for CRLs and Certs.
94 CSSM_DATA_PTR algField
;
95 crtn
= fetchField(&CSSMOID_X509V1SignatureAlgorithmTBS
, &algField
);
98 CssmError::throwMe(crtn
);
100 if(algField
->Length
!= sizeof(CSSM_X509_ALGORITHM_IDENTIFIER
)) {
101 tpErrorLog("TPClItemInfo: bad CSSM_X509_ALGORITHM_IDENTIFIER\n");
102 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
104 CSSM_X509_ALGORITHM_IDENTIFIER
*algId
=
105 (CSSM_X509_ALGORITHM_IDENTIFIER
*)algField
->Data
;
106 bool algFound
= cssmOidToAlg(&algId
->algorithm
, &mSigAlg
);
108 tpErrorLog("TPClItemInfo: unknown signature algorithm\n");
109 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT
);
111 freeField(&CSSMOID_X509V1SignatureAlgorithmTBS
, algField
);
113 fetchNotBeforeAfter();
114 calculateCurrent(verifyTime
);
122 TPClItemInfo::~TPClItemInfo()
124 tpCertInfoDbg("TPClItemInfo destruct this %p", this);
128 void TPClItemInfo::releaseResources()
130 if(mWeOwnTheData
&& (mItemData
!= NULL
)) {
131 tpFreeCssmData(CssmAllocator::standard(), mItemData
, CSSM_TRUE
);
132 mWeOwnTheData
= false;
136 freeField(&CSSMOID_X509V1IssuerName
, mIssuerName
);
139 if(mCacheHand
!= 0) {
140 mClCalls
.abortCache(mClHand
, mCacheHand
);
145 /* fetch arbitrary field from cached cert */
146 CSSM_RETURN
TPClItemInfo::fetchField(
147 const CSSM_OID
*fieldOid
,
148 CSSM_DATA_PTR
*fieldData
) // mallocd by CL and RETURNED
152 uint32 NumberOfFields
= 0;
153 CSSM_HANDLE resultHand
= 0;
156 assert(mClCalls
.getField
!= NULL
);
157 assert(mCacheHand
!= 0);
158 crtn
= mClCalls
.getField(
168 if(NumberOfFields
!= 1) {
169 tpErrorLog("TPCertInfo::fetchField: numFields %d, expected 1\n",
170 (int)NumberOfFields
);
172 mClCalls
.abortQuery(mClHand
, resultHand
);
176 /* free arbitrary field obtained from fetchField() */
177 CSSM_RETURN
TPClItemInfo::freeField(
178 const CSSM_OID
*fieldOid
,
179 CSSM_DATA_PTR fieldData
)
181 return CSSM_CL_FreeFieldValue(mClHand
, fieldOid
, fieldData
);
186 * Verify with an issuer cert - works on certs and CRLs.
187 * Issuer/subject name match already performed by caller.
188 * Optional paramCert is used to provide parameters when issuer
189 * has a partial public key.
191 CSSM_RETURN
TPClItemInfo::verifyWithIssuer(
192 TPCertInfo
*issuerCert
,
193 TPCertInfo
*paramCert
/* = NULL */) const
197 assert(mClHand
!= 0);
198 assert(issuerCert
->isIssuerOf(*this));
199 assert(mCspHand
!= 0);
202 * Special case: detect partial public key right now; don't even
203 * bother trying the cert verify in that case.
205 if(issuerCert
->hasPartialKey() && (paramCert
== NULL
)) {
206 /* caller deals with this later */
207 tpVfyDebug("verifyWithIssuer PUBLIC_KEY_INCOMPLETE");
208 return CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
;
211 CSSM_CC_HANDLE ccHand
;
212 crtn
= CSSM_CSP_CreateSignatureContext(mCspHand
,
214 NULL
, // Access Creds
215 issuerCert
->pubKey(),
217 if(crtn
!= CSSM_OK
) {
218 tpErrorLog("verifyWithIssuer: CreateSignatureContext error\n");
219 CssmError::throwMe(crtn
);
221 if(paramCert
!= NULL
) {
222 assert(issuerCert
->hasPartialKey());
224 /* add in parameter-bearing key */
225 CSSM_CONTEXT_ATTRIBUTE newAttr
;
227 newAttr
.AttributeType
= CSSM_ATTRIBUTE_PARAM_KEY
;
228 newAttr
.AttributeLength
= sizeof(CSSM_KEY
);
229 newAttr
.Attribute
.Key
= paramCert
->pubKey();
230 crtn
= CSSM_UpdateContextAttributes(ccHand
, 1, &newAttr
);
232 tpErrorLog("verifyWithIssuer: CSSM_UpdateContextAttributes error\n");
233 CssmError::throwMe(crtn
);
236 crtn
= mClCalls
.itemVerify(mClHand
,
244 case CSSM_OK
: // success
245 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
: // caller handles
246 tpVfyDebug("verifyWithIssuer GOOD");
249 /* all others appear here as general cert verify error */
250 crtn
= CSSMERR_TP_VERIFICATION_FAILURE
;
251 tpVfyDebug("verifyWithIssuer BAD");
254 CSSM_DeleteContext(ccHand
);
258 CSSM_RETURN
TPClItemInfo::cacheItem(
259 const CSSM_DATA
*itemData
,
260 TPItemCopy copyItemData
)
262 switch(copyItemData
) {
264 mItemData
= const_cast<CSSM_DATA
*>(itemData
);
267 mItemData
= tpMallocCopyCssmData(CssmAllocator::standard(), itemData
);
268 mWeOwnTheData
= true;
272 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
275 /* cache the cert/CRL in the CL */
276 return mClCalls
.cacheItem(mClHand
, mItemData
, &mCacheHand
);
280 * Calculate not before/after times as struct tm. Only throws on
281 * gross error (CSSMERR_TP_INVALID_CERT_POINTER, etc.).
283 * Only differences between Cert and CRL flavors of this are the
284 * OIDs used to fetch the appropriate before/after times, both of
285 * which are expressed as CSSM_X509_TIME structs for both Certs
288 void TPClItemInfo::fetchNotBeforeAfter()
290 CSSM_DATA_PTR notBeforeField
= NULL
;
291 CSSM_DATA_PTR notAfterField
= NULL
;
292 CSSM_RETURN crtn
= CSSM_OK
;
293 CSSM_X509_TIME
*xTime
;
295 assert(cacheHand() != CSSM_INVALID_HANDLE
);
296 crtn
= fetchField(mClCalls
.notBeforeOid
, ¬BeforeField
);
298 tpErrorLog("fetchNotBeforeAfter: GetField error\n");
299 CssmError::throwMe(mClCalls
.invalidItemRtn
);
302 /* subsequent errors to errOut */
303 xTime
= (CSSM_X509_TIME
*)notBeforeField
->Data
;
304 if(timeStringToTm((char *)xTime
->time
.Data
, xTime
->time
.Length
, &mNotBefore
)) {
305 tpErrorLog("fetchNotBeforeAfter: malformed notBefore time\n");
306 crtn
= mClCalls
.invalidItemRtn
;
310 crtn
= fetchField(mClCalls
.notAfterOid
, ¬AfterField
);
313 * Tolerate a missing NextUpdate in CRL only
315 if(mClCalls
.notAfterOid
== &CSSMOID_X509V1ValidityNotAfter
) {
316 tpErrorLog("fetchNotBeforeAfter: GetField error\n");
317 crtn
= mClCalls
.invalidItemRtn
;
322 * Fake NextUpdate to be "at the end of time"
324 timeStringToTm(CSSM_APPLE_CRL_END_OF_TIME
,
325 strlen(CSSM_APPLE_CRL_END_OF_TIME
),
330 xTime
= (CSSM_X509_TIME
*)notAfterField
->Data
;
331 if(timeStringToTm((char *)xTime
->time
.Data
, xTime
->time
.Length
, &mNotAfter
)) {
332 tpErrorLog("fetchNotBeforeAfter: malformed notAfter time\n");
333 crtn
= mClCalls
.invalidItemRtn
;
340 freeField(mClCalls
.notAfterOid
, notAfterField
);
343 freeField(mClCalls
.notBeforeOid
, notBeforeField
);
345 if(crtn
!= CSSM_OK
) {
346 CssmError::throwMe(crtn
);
351 * Verify validity (not before/after) by comparing the reference
352 * time (verifyString if present, or "now" if NULL) to the
353 * not before/after fields fetched from the item at construction.
355 * Called implicitly at construction; can be called again any time
356 * to re-establish validity (e.g. after fetching an item from a cache).
358 * We use some stdlib time calls over in tpTime.c; the stdlib function
359 * gmtime() is not thread-safe, so we do the protection here. Note that
360 * this makes *our* calls to gmtime() thread-safe, but if the app has
361 * other threads which are also calling gmtime, we're out of luck.
363 ModuleNexus
<Mutex
> tpTimeLock
;
365 CSSM_RETURN
TPClItemInfo::calculateCurrent(
366 const char *verifyString
)
370 if(verifyString
!= NULL
) {
371 /* caller specifies verification time base */
372 if(timeStringToTm(verifyString
, strlen(verifyString
), &refTime
)) {
373 tpErrorLog("calculateCurrent: timeStringToTm error\n");
374 return CSSMERR_TP_INVALID_TIMESTRING
;
378 /* time base = right now */
379 StLock
<Mutex
> _(tpTimeLock());
382 if(compareTimes(&refTime
, &mNotBefore
) < 0) {
383 mIsNotValidYet
= true;
384 tpTimeDbg("\nTP_CERT_NOT_VALID_YET:\n now y:%d m:%d d:%d h:%d m:%d",
385 refTime
.tm_year
, refTime
.tm_mon
, refTime
.tm_mday
,
386 refTime
.tm_hour
, refTime
.tm_min
);
387 tpTimeDbg(" notBefore y:%d m:%d d:%d h:%d m:%d",
388 mNotBefore
.tm_year
, mNotBefore
.tm_mon
, mNotBefore
.tm_mday
,
389 mNotBefore
.tm_hour
, mNotBefore
.tm_min
);
390 return mClCalls
.notValidYetRtn
;
393 mIsNotValidYet
= false;
396 if(compareTimes(&refTime
, &mNotAfter
) > 0) {
398 tpTimeDbg("\nTP_CERT_EXPIRED: \n now y:%d m:%d d:%d "
400 refTime
.tm_year
, refTime
.tm_mon
, refTime
.tm_mday
,
401 refTime
.tm_hour
, refTime
.tm_min
);
402 tpTimeDbg(" notAfter y:%d m:%d d:%d h:%d m:%d",
403 mNotAfter
.tm_year
, mNotAfter
.tm_mon
, mNotAfter
.tm_mday
,
404 mNotAfter
.tm_hour
, mNotAfter
.tm_min
);
405 return mClCalls
.expiredRtn
;
415 * No default constructor - this is the only way.
416 * This caches the cert and fetches subjectName, issuerName, and
417 * mPublicKey to ensure the incoming certData is well-constructed.
419 TPCertInfo::TPCertInfo(
420 CSSM_CL_HANDLE clHand
,
421 CSSM_CSP_HANDLE cspHand
,
422 const CSSM_DATA
*certData
,
423 TPItemCopy copyCertData
, // true: we copy, we free
424 // false - caller owns
425 const char *verifyTime
) // may be NULL
427 TPClItemInfo(clHand
, cspHand
, tpCertClCalls
, certData
,
428 copyCertData
, verifyTime
),
443 tpCertInfoDbg("TPCertInfo construct this %p", this);
444 mDlDbHandle
.DLHandle
= 0;
445 mDlDbHandle
.DBHandle
= 0;
447 /* fetch subject name */
448 crtn
= fetchField(&CSSMOID_X509V1SubjectName
, &mSubjectName
);
452 CssmError::throwMe(crtn
);
455 /* this cert's public key */
456 crtn
= CSSM_CL_CertGetKeyInfo(clHand
, certData
, &mPublicKey
);
460 CssmError::throwMe(crtn
);
463 /* calculate other commonly used fields */
464 if(tpCompareCssmData(mSubjectName
, issuerName())) {
466 * Per Radar 3374978, perform complete signature verification
467 * lazily - just check subject/issuer match here.
469 tpAnchorDebug("TPCertInfo potential anchor");
470 mIsRoot
= TRS_NamesMatch
;
473 mIsRoot
= TRS_NotRoot
;
477 /* frees mSubjectName, mIssuerName, mCacheHand via mClHand */
478 TPCertInfo::~TPCertInfo()
480 tpCertInfoDbg("TPCertInfo destruct this %p", this);
484 void TPCertInfo::releaseResources()
487 freeField(&CSSMOID_X509V1SubjectName
, mSubjectName
);
495 /* allocated by CL */
496 tpFreePluginMemory(clHand(), mPublicKey
->KeyData
.Data
);
497 tpFreePluginMemory(clHand(), mPublicKey
);
500 TPClItemInfo::releaseResources();
503 const CSSM_DATA
*TPCertInfo::subjectName()
505 assert(mSubjectName
!= NULL
);
510 * Perform semi-lazy evaluation of "rootness". Subject and issuer names
511 * compared at constructor.
513 bool TPCertInfo::isSelfSigned()
516 case TRS_NotRoot
: // known not to be root
520 case TRS_Unknown
: // actually shouldn't happen, but to be safe...
523 /* do the signature verify */
524 if(verifyWithIssuer(this) == CSSM_OK
) {
525 tpAnchorDebug("isSelfSigned anchor verified");
526 mIsRoot
= TRS_IsRoot
;
530 tpAnchorDebug("isSelfSigned anchor vfy FAIL");
531 mIsRoot
= TRS_NotRoot
;
538 * Am I the issuer of the specified subject item? Returns true if so.
539 * Works for subject certs as well as CRLs.
541 bool TPCertInfo::isIssuerOf(
542 const TPClItemInfo
&subject
)
544 assert(mSubjectName
!= NULL
);
545 assert(subject
.issuerName() != NULL
);
546 if(tpCompareCssmData(mSubjectName
, subject
.issuerName())) {
554 void TPCertInfo::addStatusCode(CSSM_RETURN code
)
557 mStatusCodes
= (CSSM_RETURN
*)realloc(mStatusCodes
,
558 mNumStatusCodes
* sizeof(CSSM_RETURN
));
559 mStatusCodes
[mNumStatusCodes
- 1] = code
;
563 * Indicate whether this cert's public key is a CSSM_KEYATTR_PARTIAL
566 bool TPCertInfo::hasPartialKey()
568 if(mPublicKey
->KeyHeader
.KeyAttr
& CSSM_KEYATTR_PARTIAL
) {
577 *** TPCertGroup class
580 /* build empty group */
581 TPCertGroup::TPCertGroup(
582 CssmAllocator
&alloc
,
583 TPGroupOwner whoOwns
) :
590 tpCertInfoDbg("TPCertGroup simple construct this %p", this);
591 /* nothing for now */
595 * Construct from unordered, untrusted CSSM_CERTGROUP. Resulting
596 * TPCertInfos are more or less in the same order as the incoming
597 * certs, though incoming certs are discarded if they don't parse.
598 * No verification of any sort is performed.
600 TPCertGroup::TPCertGroup(
601 const CSSM_CERTGROUP
&CertGroupFrag
,
602 CSSM_CL_HANDLE clHand
,
603 CSSM_CSP_HANDLE cspHand
,
604 CssmAllocator
&alloc
,
605 const char *verifyTime
, // may be NULL
606 bool firstCertMustBeValid
,
607 TPGroupOwner whoOwns
) :
614 tpCertInfoDbg("TPCertGroup hard construct this %p", this);
616 /* verify input args */
617 if(cspHand
== CSSM_INVALID_HANDLE
) {
618 CssmError::throwMe(CSSMERR_TP_INVALID_CSP_HANDLE
);
620 if(clHand
== CSSM_INVALID_HANDLE
) {
621 CssmError::throwMe(CSSMERR_TP_INVALID_CL_HANDLE
);
623 if(firstCertMustBeValid
) {
624 if( (CertGroupFrag
.NumCerts
== 0) ||
625 (CertGroupFrag
.GroupList
.CertList
[0].Data
== NULL
) ||
626 (CertGroupFrag
.GroupList
.CertList
[0].Length
== 0)) {
627 CssmError::throwMe(CSSMERR_TP_INVALID_CERTIFICATE
);
630 if(CertGroupFrag
.CertGroupType
!= CSSM_CERTGROUP_DATA
) {
631 CssmError::throwMe(CSSMERR_TP_INVALID_CERTGROUP
);
633 switch(CertGroupFrag
.CertType
) {
634 case CSSM_CERT_X_509v1
:
635 case CSSM_CERT_X_509v2
:
636 case CSSM_CERT_X_509v3
:
639 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT
);
641 switch(CertGroupFrag
.CertEncoding
) {
642 case CSSM_CERT_ENCODING_BER
:
643 case CSSM_CERT_ENCODING_DER
:
646 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT
);
650 * Add remaining input certs to mCertInfo.
652 TPCertInfo
*certInfo
= NULL
;
653 for(unsigned certDex
=0; certDex
<CertGroupFrag
.NumCerts
; certDex
++) {
655 certInfo
= new TPCertInfo(clHand
,
657 &CertGroupFrag
.GroupList
.CertList
[certDex
],
658 TIC_NoCopy
, // caller owns
662 if((certDex
== 0) && firstCertMustBeValid
) {
663 CssmError::throwMe(CSSMERR_TP_INVALID_CERTIFICATE
);
665 /* else just ignore this cert */
668 certInfo
->index(certDex
);
669 appendCert(certInfo
);
674 * Deletes contents of mCertInfo[] if appropriate.
676 TPCertGroup::~TPCertGroup()
678 if(mWhoOwns
== TGO_Group
) {
680 for(i
=0; i
<mNumCerts
; i
++) {
684 mAlloc
.free(mCertInfo
);
687 /* add/remove/access TPTCertInfo's. */
689 * NOTE: I am aware that most folks would just use an array<> here, but
690 * gdb is so lame that it doesn't even let one examine the contents
691 * of an array<> (or just about anything else in the STL). I prefer
692 * debuggability over saving a few lines of trivial code.
694 void TPCertGroup::appendCert(
695 TPCertInfo
*certInfo
) // appends to end of mCertInfo
697 if(mNumCerts
== mSizeofCertInfo
) {
698 if(mSizeofCertInfo
== 0) {
699 /* appending to empty array */
703 mSizeofCertInfo
*= 2;
705 mCertInfo
= (TPCertInfo
**)mAlloc
.realloc(mCertInfo
,
706 mSizeofCertInfo
* sizeof(TPCertInfo
*));
708 mCertInfo
[mNumCerts
++] = certInfo
;
711 TPCertInfo
*TPCertGroup::certAtIndex(
714 if(index
> (mNumCerts
- 1)) {
715 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
717 return mCertInfo
[index
];
720 TPCertInfo
*TPCertGroup::removeCertAtIndex(
721 unsigned index
) // doesn't delete the cert, just
722 // removes it from out list
724 if(index
> (mNumCerts
- 1)) {
725 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
727 TPCertInfo
*rtn
= mCertInfo
[index
];
729 /* removed requested element and compact remaining array */
731 for(i
=index
; i
<(mNumCerts
- 1); i
++) {
732 mCertInfo
[i
] = mCertInfo
[i
+1];
738 TPCertInfo
*TPCertGroup::firstCert()
741 /* the caller really should not do this... */
742 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
749 TPCertInfo
*TPCertGroup::lastCert()
755 return mCertInfo
[mNumCerts
- 1];
759 /* build a CSSM_CERTGROUP corresponding with our mCertInfo */
760 CSSM_CERTGROUP_PTR
TPCertGroup::buildCssmCertGroup()
762 CSSM_CERTGROUP_PTR cgrp
=
763 (CSSM_CERTGROUP_PTR
)mAlloc
.malloc(sizeof(CSSM_CERTGROUP
));
764 cgrp
->NumCerts
= mNumCerts
;
765 cgrp
->CertGroupType
= CSSM_CERTGROUP_DATA
;
766 cgrp
->CertType
= CSSM_CERT_X_509v3
;
767 cgrp
->CertEncoding
= CSSM_CERT_ENCODING_DER
;
770 cgrp
->GroupList
.CertList
= NULL
;
773 cgrp
->GroupList
.CertList
= (CSSM_DATA_PTR
)mAlloc
.calloc(mNumCerts
,
775 for(unsigned i
=0; i
<mNumCerts
; i
++) {
776 tpCopyCssmData(mAlloc
, mCertInfo
[i
]->itemData(),
777 &cgrp
->GroupList
.CertList
[i
]);
782 /* build a CSSM_TP_APPLE_EVIDENCE_INFO array */
783 CSSM_TP_APPLE_EVIDENCE_INFO
*TPCertGroup::buildCssmEvidenceInfo()
785 CSSM_TP_APPLE_EVIDENCE_INFO
*infoArray
;
787 infoArray
= (CSSM_TP_APPLE_EVIDENCE_INFO
*)mAlloc
.calloc(mNumCerts
,
788 sizeof(CSSM_TP_APPLE_EVIDENCE_INFO
));
789 for(unsigned i
=0; i
<mNumCerts
; i
++) {
790 TPCertInfo
*certInfo
= mCertInfo
[i
];
791 CSSM_TP_APPLE_EVIDENCE_INFO
*evInfo
= &infoArray
[i
];
793 /* first the booleans */
794 if(certInfo
->isExpired()) {
795 evInfo
->StatusBits
|= CSSM_CERT_STATUS_EXPIRED
;
797 if(certInfo
->isNotValidYet()) {
798 evInfo
->StatusBits
|= CSSM_CERT_STATUS_NOT_VALID_YET
;
800 if(certInfo
->dlDbHandle().DLHandle
== 0) {
801 if(certInfo
->isAnchor()) {
802 evInfo
->StatusBits
|= CSSM_CERT_STATUS_IS_IN_ANCHORS
;
804 else if(certInfo
->isFromNet()) {
805 evInfo
->StatusBits
|= CSSM_CERT_STATUS_IS_FROM_NET
;
808 evInfo
->StatusBits
|= CSSM_CERT_STATUS_IS_IN_INPUT_CERTS
;
811 if(certInfo
->isSelfSigned()) {
812 evInfo
->StatusBits
|= CSSM_CERT_STATUS_IS_ROOT
;
815 unsigned numCodes
= certInfo
->numStatusCodes();
817 evInfo
->NumStatusCodes
= numCodes
;
818 evInfo
->StatusCodes
= (CSSM_RETURN
*)mAlloc
.calloc(numCodes
,
819 sizeof(CSSM_RETURN
));
820 for(unsigned j
=0; j
<numCodes
; j
++) {
821 evInfo
->StatusCodes
[j
] = (certInfo
->statusCodes())[j
];
825 evInfo
->Index
= certInfo
->index();
826 evInfo
->DlDbHandle
= certInfo
->dlDbHandle();
827 evInfo
->UniqueRecord
= certInfo
->uniqueRecord();
832 /* Given a status for basic construction of a cert group and a status
833 * of (optional) policy verification, plus the implicit notBefore/notAfter
834 * status in the certs, calculate a global return code. This just
835 * encapsulates a policy for CertGroupConstruct and CertGroupVerify.
837 CSSM_RETURN
TPCertGroup::getReturnCode(
838 CSSM_RETURN constructStatus
,
839 CSSM_BOOL allowExpired
,
840 CSSM_BOOL allowExpiredRoot
,
841 CSSM_RETURN policyStatus
/* = CSSM_OK */)
843 if(constructStatus
) {
844 /* CSSMERR_TP_NOT_TRUSTED, CSSMERR_TP_INVALID_ANCHOR_CERT, gross errors */
845 return constructStatus
;
848 /* check for expired, not valid yet */
849 bool expired
= false;
850 bool notValid
= false;
851 for(unsigned i
=0; i
<mNumCerts
; i
++) {
852 if(mCertInfo
[i
]->isExpired() &&
853 !(allowExpiredRoot
&& mCertInfo
[i
]->isSelfSigned())) {
856 if(mCertInfo
[i
]->isNotValidYet()) {
860 if(expired
&& !allowExpired
) {
861 return CSSMERR_TP_CERT_EXPIRED
;
864 return CSSMERR_TP_CERT_NOT_VALID_YET
;
869 /* set all TPCertINfo.mUsed flags false */
870 void TPCertGroup::setAllUnused()
872 for(unsigned dex
=0; dex
<mNumCerts
; dex
++) {
873 mCertInfo
[dex
]->used(false);
878 * Search unused incoming certs to find an issuer of specified cert or CRL.
879 * WARNING this assumes a valid "used" state for all certs in this group.
880 * If partialIssuerKey is true on return, caller must re-verify signature
881 * of subject later when sufficient info is available.
883 TPCertInfo
*TPCertGroup::findIssuerForCertOrCrl(
884 const TPClItemInfo
&subject
,
885 bool &partialIssuerKey
)
887 partialIssuerKey
= false;
888 for(unsigned certDex
=0; certDex
<mNumCerts
; certDex
++) {
889 TPCertInfo
*certInfo
= certAtIndex(certDex
);
891 /* has this one already been used in this search? */
892 if(certInfo
->used()) {
896 /* subject/issuer names match? */
897 if(certInfo
->isIssuerOf(subject
)) {
898 /* yep, do a sig verify */
899 tpVfyDebug("findIssuerForCertOrCrl issuer/subj match checking sig");
900 CSSM_RETURN crtn
= subject
.verifyWithIssuer(certInfo
);
902 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
:
903 /* issuer OK, check sig later */
904 partialIssuerKey
= true;
908 certInfo
->used(true);
911 /* just skip this one and keep looking */
912 tpVfyDebug("findIssuerForCertOrCrl issuer/subj match BAD SIG");
922 * Construct ordered, verified cert chain from a variety of inputs.
923 * Time validity is ignored and needs to be checked by caller (it's
924 * stored in each TPCertInfo we add to ourself during construction).
925 * The only error returned is CSSMERR_APPLETP_INVALID_ROOT, meaning
926 * we verified back to a supposed root cert which did not in fact
927 * self-verify. Other interesting status is returned via the
928 * verifiedToRoot and verifiedToAnchor flags.
930 * NOTE: is it the caller's responsibility to call setAllUnused() for both
931 * incoming cert groups (inCertGroup and gatheredCerts). We don't do that
932 * here because we may call ourself recursively.
934 CSSM_RETURN
TPCertGroup::buildCertGroup(
935 const TPClItemInfo
&subjectItem
, // Cert or CRL
936 TPCertGroup
*inCertGroup
, // optional
937 const CSSM_DL_DB_LIST
*dbList
, // optional
938 CSSM_CL_HANDLE clHand
,
939 CSSM_CSP_HANDLE cspHand
,
940 const char *verifyTime
, // optional, for establishing
941 // validity of new TPCertInfos
942 /* trusted anchors, optional */
943 /* FIXME - maybe this should be a TPCertGroup */
944 uint32 numAnchorCerts
,
945 const CSSM_DATA
*anchorCerts
,
948 * Certs to be freed by caller (i.e., TPCertInfo which we allocate
949 * as a result of using a cert from anchorCerts or dbList) are added
952 TPCertGroup
&certsToBeFreed
,
955 * Other certificates gathered during the course of this operation,
956 * currently consisting of certs fetched from DBs and from the net.
957 * This is not used when called by AppleTPSession::CertGroupConstructPriv;
958 * it's an optimization for the case when we're building a cert group
959 * for TPCrlInfo::verifyWithContext - we avoid re-fetching certs from
960 * the net which are needed to verify both the subject cert and a CRL.
962 TPCertGroup
*gatheredCerts
,
965 * Indicates that subjectItem is the last element in this cert group.
966 * If true, that cert will be tested for "root-ness", including
967 * -- subject/issuer compare
968 * -- signature self-verify
971 CSSM_BOOL subjectIsInGroup
,
973 /* currently, only CSSM_TP_ACTION_FETCH_CERT_FROM_NET is interesting */
974 CSSM_APPLE_TP_ACTION_FLAGS actionFlags
,
977 CSSM_BOOL
&verifiedToRoot
, // end of chain self-verifies
978 CSSM_BOOL
&verifiedToAnchor
) // end of chain in anchors
980 const TPClItemInfo
*thisSubject
= &subjectItem
;
981 CSSM_RETURN crtn
= CSSM_OK
;
982 TPCertInfo
*issuerCert
= NULL
;
984 TPCertInfo
*anchorInfo
= NULL
;
985 bool foundPartialIssuer
= false;
987 tpVfyDebug("buildCertGroup top");
989 /* possible expired root which we'll only use if we can't find
991 TPCertInfo
*expiredRoot
= NULL
;
993 verifiedToRoot
= CSSM_FALSE
;
994 verifiedToAnchor
= CSSM_FALSE
;
996 /*** main loop to seach inCertGroup and dbList ***
999 * -- find a root cert in the chain
1001 * -- or no more certs to add to chain.
1005 * Top of loop: thisSubject is the item we're trying to verify.
1008 /* is thisSubject a root cert? */
1009 if(subjectIsInGroup
) {
1010 TPCertInfo
*subjCert
= lastCert();
1011 assert(subjCert
!= NULL
);
1012 if(subjCert
->isSelfSigned()) {
1013 /* We're at the end of the chain. */
1014 verifiedToRoot
= CSSM_TRUE
;
1017 * Special case if this root is expired (and it's not the
1018 * leaf): remove it from the outgoing cert group, save it,
1019 * and try to proceed with anchor cert processing.
1021 if(subjCert
->isExpired() && (mNumCerts
> 1)) {
1022 tpDebug("buildCertGroup: EXPIRED ROOT, looking for good one");
1024 expiredRoot
= subjCert
;
1025 thisSubject
= lastCert();
1032 * Search unused incoming certs to find an issuer.
1033 * Both cert groups are optional.
1034 * We'll add issuer to outCertGroup below.
1036 if(inCertGroup
!= NULL
) {
1037 bool partial
= false;
1038 issuerCert
= inCertGroup
->findIssuerForCertOrCrl(*thisSubject
,
1042 /* deal with this later */
1043 foundPartialIssuer
= true;
1044 tpDebug("buildCertGroup: PARTIAL Cert FOUND in inCertGroup");
1047 tpDebug("buildCertGroup: Cert FOUND in inCertGroup");
1051 if((issuerCert
== NULL
) && (gatheredCerts
!= NULL
)) {
1052 bool partial
= false;
1053 issuerCert
= gatheredCerts
->findIssuerForCertOrCrl(*thisSubject
,
1057 /* deal with this later */
1058 foundPartialIssuer
= true;
1059 tpDebug("buildCertGroup: PARTIAL Cert FOUND in gatheredCerts");
1062 tpDebug("buildCertGroup: Cert FOUND in gatheredCerts");
1067 if((issuerCert
== NULL
) && (dbList
!= NULL
)) {
1068 /* Issuer not in incoming cert group. Search DBList. */
1069 bool partial
= false;
1070 issuerCert
= tpDbFindIssuerCert(mAlloc
,
1078 /* caller must free */
1079 certsToBeFreed
.appendCert(issuerCert
);
1081 /* deal with this later */
1082 foundPartialIssuer
= true;
1083 tpDebug("buildCertGroup: PARTIAL Cert FOUND in dbList");
1086 tpDebug("buildCertGroup: Cert FOUND in dbList");
1089 } /* Issuer not in incoming cert group */
1091 if(issuerCert
== NULL
) {
1092 /* end of search, broken chain */
1097 * One way or the other, we've found a cert which verifies subjectCert.
1098 * Add the issuer to outCertGroup and make it the new thisSubject for
1101 appendCert(issuerCert
);
1102 thisSubject
= issuerCert
;
1103 subjectIsInGroup
= CSSM_TRUE
;
1108 * This can be NULL if we're evaluating a CRL (and we haven't
1111 TPCertInfo
*endCert
= lastCert();
1113 if(numAnchorCerts
== 0) {
1114 /* we're probably done */
1117 assert(anchorCerts
!= NULL
);
1119 /*** anchor cert handling ***/
1121 * Case 1: last cert in output is a root cert. See if
1122 * the root cert is in AnchorCerts. This also applies to
1123 * the expiredRoot case; we report a different error for
1124 * "we trust the root but it's expired" versus "we don't
1126 * Note that the above loop did the actual root self-verify test.
1127 * FIXME - shouldn't we be searching for a match in AnchorCerts
1128 * whether or not endCert is a root!!?
1130 if((endCert
&& endCert
->isSelfSigned()) || expiredRoot
) {
1132 TPCertInfo
*theRoot
;
1134 /* this is NOT in our outgoing cert group (yet) */
1135 theRoot
= expiredRoot
;
1140 /* see if that root cert is identical to one of the anchor certs */
1141 for(certDex
=0; certDex
<numAnchorCerts
; certDex
++) {
1142 if(tp_CompareCerts(theRoot
->itemData(), &anchorCerts
[certDex
])) {
1143 /* one fully successful return */
1144 verifiedToAnchor
= CSSM_TRUE
;
1145 theRoot
->isAnchor(true);
1146 theRoot
->index(certDex
);
1148 /* verified to anchor but caller will see
1149 * CSSMERR_TP_CERT_EXPIRED */
1150 appendCert(expiredRoot
);
1152 /* one more thing: partial public key processing needed? */
1153 if(foundPartialIssuer
) {
1154 return verifyWithPartialKeys(subjectItem
);
1163 /* verified to a root cert which is not an anchor */
1164 /* Generally maps to CSSMERR_TP_INVALID_ANCHOR_CERT by caller */
1165 /* one more thing: partial public key processing needed? */
1166 if(foundPartialIssuer
) {
1167 return verifyWithPartialKeys(subjectItem
);
1173 /* else try finding a good anchor */
1177 * Case 2: try to validate thisSubject with anchor certs
1179 for(certDex
=0; certDex
<numAnchorCerts
; certDex
++) {
1182 anchorInfo
= new TPCertInfo(clHand
,
1184 &anchorCerts
[certDex
],
1189 /* bad anchor cert - ignore it */
1195 * We must subsequently delete goodAnchor one way or the other.
1196 * If we add it to tpCertGroup, we also add it to certsToBeFreed.
1197 * Otherwise we delete it.
1199 if(!anchorInfo
->isIssuerOf(*thisSubject
)) {
1200 /* not this anchor */
1201 tpAnchorDebug("buildCertGroup anchor not issuer");
1207 crtn
= thisSubject
->verifyWithIssuer(anchorInfo
);
1209 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
:
1211 * A bit of a corner case. Found an issuer in AnchorCerts, but
1212 * we can't do a signature verify since the issuer has a partial
1213 * public key. Proceed but return
1214 * CSSMERR_TP_CERTIFICATE_CANT_OPERATE.
1216 crtn
= CSSMERR_TP_CERTIFICATE_CANT_OPERATE
;
1217 anchorInfo
->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE
);
1218 foundPartialIssuer
= true;
1221 /* The other normal fully successful return. */
1222 verifiedToAnchor
= CSSM_TRUE
;
1223 if(anchorInfo
->isSelfSigned()) {
1224 verifiedToRoot
= CSSM_TRUE
;
1228 * Add this anchor cert to the output group
1229 * and to certsToBeFreed.
1231 appendCert(anchorInfo
);
1232 anchorInfo
->isAnchor(true);
1233 anchorInfo
->index(certDex
);
1234 certsToBeFreed
.appendCert(anchorInfo
);
1235 tpDebug("buildCertGroup: Cert FOUND by signer in AnchorList");
1236 /* one more thing: partial public key processing needed? */
1237 if(foundPartialIssuer
) {
1238 return verifyWithPartialKeys(subjectItem
);
1245 /* continue to next anchor */
1246 tpVfyDebug("buildCertGroup found issuer in anchor, BAD SIG");
1251 } /* for each anchor */
1252 /* regardless of anchor search status... */
1257 * One remaining special case: expiredRoot found in input certs, but
1258 * no luck resolving the problem with the anchors. Go ahead and append
1259 * the expired root and return.
1261 tpDebug("buildCertGroup: accepting EXPIRED root");
1262 appendCert(expiredRoot
);
1263 if(foundPartialIssuer
) {
1264 return verifyWithPartialKeys(subjectItem
);
1272 * If we haven't verified to a root, and net fetch of certs is enabled,
1273 * try to get the issuer of the last cert in the chain from the net.
1274 * If that succeeds, then call ourself recursively to perform the
1275 * whole search again (including comparing to or verifying against
1278 if(!verifiedToRoot
&& !verifiedToAnchor
&&
1279 (endCert
!= NULL
) &&
1280 (actionFlags
& CSSM_TP_ACTION_FETCH_CERT_FROM_NET
)) {
1281 TPCertInfo
*issuer
= NULL
;
1282 CSSM_RETURN cr
= tpFetchIssuerFromNet(*endCert
,
1288 case CSSMERR_TP_CERTGROUP_INCOMPLETE
:
1289 /* no issuerAltName, no reason to log this */
1293 endCert
->addStatusCode(cr
);
1295 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
:
1296 /* use this one but re-verify later */
1297 foundPartialIssuer
= true;
1300 tpDebug("buildCertGroup: Cert FOUND from Net; recursing");
1302 /* add this fetched cert to constructed group */
1304 issuer
->isFromNet(true);
1305 certsToBeFreed
.appendCert(issuer
);
1308 cr
= buildCertGroup(*issuer
,
1318 CSSM_TRUE
, // subjectIsInGroup
1326 /* one more thing: partial public key processing needed? */
1327 if(foundPartialIssuer
) {
1328 return verifyWithPartialKeys(subjectItem
);
1335 /* regardless of outcome, check for partial keys to log per-cert status */
1336 CSSM_RETURN partRtn
= CSSM_OK
;
1337 if(foundPartialIssuer
) {
1338 partRtn
= verifyWithPartialKeys(subjectItem
);
1349 * Called from buildCertGroup as final processing of a constructed
1350 * group when CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE has been
1351 * detected. Perform partial public key processing.
1353 * We don't have to verify every element, just the ones whose
1354 * issuers have partial public keys.
1357 * CSSMERR_TP_CERTIFICATE_CANT_OPERATE in the case of an issuer cert
1358 * with a partial public key which can't be completed.
1359 * CSSMERR_TP_INVALID_CERT_AUTHORITY if sig verify failed with
1360 * a (supposedly) completed partial key
1362 CSSM_RETURN
TPCertGroup::verifyWithPartialKeys(
1363 const TPClItemInfo
&subjectItem
) // Cert or CRL
1365 TPCertInfo
*lastFullKeyCert
= NULL
;
1366 tpDebug("verifyWithPartialKeys top");
1368 /* start from the end - it's easier */
1369 for(int dex
=mNumCerts
-1; dex
>= 0; dex
--) {
1370 TPCertInfo
*thisCert
= mCertInfo
[dex
];
1373 * If this is the start of the cert chain, and it's not being
1374 * used to verify subjectItem, then we're done.
1377 if((void *)thisCert
== (void *)&subjectItem
) {
1378 tpDebug("verifyWithPartialKeys: success at leaf cert");
1382 if(!thisCert
->hasPartialKey()) {
1384 * Good to know. Record this and move on.
1386 lastFullKeyCert
= thisCert
;
1387 tpDebug("full key cert found at index %d", dex
);
1390 if(lastFullKeyCert
== NULL
) {
1392 * No full keys between here and the end!
1394 tpDebug("UNCOMPLETABLE cert at index %d", dex
);
1395 thisCert
->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE
);
1396 return CSSMERR_TP_CERTIFICATE_CANT_OPERATE
;
1399 /* do the verify - of next cert in chain or of subjectItem */
1400 const TPClItemInfo
*subject
;
1402 subject
= &subjectItem
;
1403 tpDebug("...verifying subject item with partial cert 0");
1406 subject
= mCertInfo
[dex
- 1];
1407 tpDebug("...verifying with partial cert %d", dex
);
1409 CSSM_RETURN crtn
= subject
->verifyWithIssuer(thisCert
,
1412 tpDebug("CERT VERIFY ERROR with partial cert at index %d", dex
);
1413 thisCert
->addStatusCode(CSSMERR_TP_CERTIFICATE_CANT_OPERATE
);
1414 return CSSMERR_TP_INVALID_CERT_AUTHORITY
;
1418 /* we just verified subjectItem - right? */
1419 assert((void *)mCertInfo
[0] != (void *)&subjectItem
);
1420 tpDebug("verifyWithPartialKeys: success at subjectItem");