2 * Copyright (c) 2002 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 * TPCrlInfo.h - TP's private CRL and CRL group
22 * Written 9/30/2002 by Doug Mitchell.
25 #include "TPCrlInfo.h"
26 #include "tpdebugging.h"
27 #include "certGroupUtils.h"
28 #include "tpCrlVerify.h"
29 #include "tpPolicies.h"
31 #include <Security/cssmapi.h>
32 #include <Security/x509defs.h>
33 #include <Security/oidscert.h>
34 #include <Security/oidscrl.h>
35 #include <security_cdsa_utilities/cssmerrors.h>
36 #include <string.h> /* for memcmp */
37 #include <Security/cssmapple.h>
40 * Replacement for CSSM_CL_CrlGetFirstCachedFieldValue for use with
41 * TPCrlItemInfo's generic getFirstCachedField mechanism.
43 static CSSM_RETURN
tpGetFirstCachedFieldValue (CSSM_CL_HANDLE CLHandle
,
44 CSSM_HANDLE CrlHandle
,
45 const CSSM_OID
*CrlField
,
46 CSSM_HANDLE_PTR ResultsHandle
,
47 uint32
*NumberOfMatchedFields
,
50 return CSSM_CL_CrlGetFirstCachedFieldValue(CLHandle
,
52 NULL
, // const CSSM_DATA *CrlRecordIndex,
55 NumberOfMatchedFields
,
59 static const TPClItemCalls tpCrlClCalls
=
61 tpGetFirstCachedFieldValue
,
62 CSSM_CL_CrlAbortQuery
,
64 CSSM_CL_CrlAbortCache
,
66 &CSSMOID_X509V1CRLThisUpdate
,
67 &CSSMOID_X509V1CRLNextUpdate
,
68 CSSMERR_TP_INVALID_CRL_POINTER
,
69 CSSMERR_APPLETP_CRL_EXPIRED
,
70 CSSMERR_APPLETP_CRL_NOT_VALID_YET
75 * No default constructor - this is the only way.
76 * This caches the cert and fetches subjectName and issuerName
77 * to ensure the incoming certData is well-constructed.
80 CSSM_CL_HANDLE clHand
,
81 CSSM_CSP_HANDLE cspHand
,
82 const CSSM_DATA
*crlData
,
83 TPItemCopy copyCrlData
, // true: we copy, we free
84 // false - caller owns
85 const char *verifyTime
) // = NULL
87 : TPClItemInfo(clHand
, cspHand
, tpCrlClCalls
, crlData
,
88 copyCrlData
, verifyTime
),
90 mFromWhere(CFW_Nowhere
),
92 mCrlFieldToFree(NULL
),
93 mVerifyState(CVS_Unknown
),
94 mVerifyError(CSSMERR_TP_INTERNAL_ERROR
)
101 /* fetch parsed CRL */
102 crtn
= fetchField(&CSSMOID_X509V2CRLSignedCrlCStruct
, &mCrlFieldToFree
);
106 CssmError::throwMe(crtn
);
108 if(mCrlFieldToFree
->Length
!= sizeof(CSSM_X509_SIGNED_CRL
)) {
109 tpErrorLog("fetchField(SignedCrlCStruct) length error\n");
111 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
113 mX509Crl
= (CSSM_X509_SIGNED_CRL
*)mCrlFieldToFree
->Data
;
114 /* any other other commonly used fields? */
117 TPCrlInfo::~TPCrlInfo()
122 void TPCrlInfo::releaseResources()
124 if(mCrlFieldToFree
) {
125 freeField(&CSSMOID_X509V2CRLSignedCrlCStruct
, mCrlFieldToFree
);
126 mCrlFieldToFree
= NULL
;
129 Allocator::standard().free(mUri
.Data
);
133 TPClItemInfo::releaseResources();
136 void TPCrlInfo::uri(const CSSM_DATA
&uri
)
138 tpCopyCssmData(Allocator::standard(), &uri
, &mUri
);
142 * List of extensions we understand and can accept as critical.
144 static const CSSM_OID
*const TPGoodCrlExtens
[] =
147 /* Note NOT CSSMOID_DeltaCrlIndicator! That's fatal */
150 &CSSMOID_IssuingDistributionPoint
,
151 &CSSMOID_HoldInstructionCode
,
152 &CSSMOID_InvalidityDate
,
153 &CSSMOID_AuthorityKeyIdentifier
,
154 &CSSMOID_SubjectAltName
,
155 &CSSMOID_IssuerAltName
158 #define NUM_KNOWN_EXTENS (sizeof(TPGoodCrlExtens) / sizeof(CSSM_OID_PTR))
161 * Do our best to understand all the entries in a CSSM_X509_EXTENSIONS,
162 * which may be per-CRL or per-entry.
164 * For now, we just ensure that for every critical extension,
165 * we actually understand it and can deal it.
167 CSSM_RETURN
TPCrlInfo::parseExtensions(
168 TPVerifyContext
&vfyCtx
,
170 uint32 entryIndex
, // if isPerEntry
171 const CSSM_X509_EXTENSIONS
&extens
,
172 TPCertInfo
*forCert
, // optional
173 bool &isIndirectCrl
) // RETURNED
175 isIndirectCrl
= false;
176 for(uint32 dex
=0; dex
<extens
.numberOfExtensions
; dex
++) {
177 CSSM_X509_EXTENSION_PTR exten
= &extens
.extensions
[dex
];
178 if(exten
->critical
) {
179 /* critical: is it in our list of understood extensions? */
181 for(i
=0; i
<NUM_KNOWN_EXTENS
; i
++) {
182 if(tpCompareOids(&exten
->extnId
, TPGoodCrlExtens
[i
])) {
183 /* we're cool with this one */
187 if(i
== NUM_KNOWN_EXTENS
) {
188 tpCrlDebug("parseExtensions: Unknown Critical Extension\n");
189 return CSSMERR_APPLETP_UNKNOWN_CRL_EXTEN
;
193 /* Specific extension handling. */
194 if(tpCompareOids(&exten
->extnId
,
195 &CSSMOID_IssuingDistributionPoint
)) {
197 * If this assertion fails, we're out of sync with the CL
199 assert(exten
->format
== CSSM_X509_DATAFORMAT_PARSED
);
200 CE_IssuingDistributionPoint
*idp
=
201 (CE_IssuingDistributionPoint
*)
202 exten
->value
.parsedValue
;
205 * Snag indirectCrl flag for caller in any case
207 if(idp
->indirectCrlPresent
&& idp
->indirectCrl
) {
208 isIndirectCrl
= true;
210 if(forCert
!= NULL
) {
211 /* If no target cert, i.e., we're just verifying a CRL,
212 * skip the remaining IDP checks. */
214 /* verify onlyCACerts/onlyUserCerts */
216 if(forCert
->isLeaf() &&
217 !(vfyCtx
.actionFlags
& CSSM_TP_ACTION_LEAF_IS_CA
)) {
223 if((idp
->onlyUserCertsPresent
) && (idp
->onlyUserCerts
)) {
225 tpCrlDebug("parseExtensions: onlyUserCerts, "
227 return CSSMERR_APPLETP_IDP_FAIL
;
230 if((idp
->onlyCACertsPresent
) && (idp
->onlyCACerts
)) {
232 tpCrlDebug("parseExtensions: onlyCACerts, leaf\n");
233 return CSSMERR_APPLETP_IDP_FAIL
;
237 } /* have target cert */
244 * The heavyweight "perform full verification of this CRL" op.
245 * Must verify to an anchor cert in tpVerifyContext or via
246 * Trust Settings if so enabled.
247 * Intermediate certs can come from signerCerts or dBList.
249 CSSM_RETURN
TPCrlInfo::verifyWithContext(
250 TPVerifyContext
&tpVerifyContext
,
251 TPCertInfo
*forCert
, // optional
255 * Step 1: this CRL must be current. Caller might have re-evaluated
256 * expired/notValidYet since our construction via calculateCurrent().
259 return CSSMERR_APPLETP_CRL_EXPIRED
;
261 if(isNotValidYet()) {
262 return CSSMERR_APPLETP_CRL_NOT_VALID_YET
;
265 /* subsequent verify state is cached */
266 switch(mVerifyState
) {
274 tpErrorLog("verifyWithContext: bad verifyState\n");
275 return CSSMERR_TP_INTERNAL_ERROR
;
279 * Step 2: parse & understand all critical CRL extensions.
283 crtn
= parseExtensions(tpVerifyContext
,
286 mX509Crl
->tbsCertList
.extensions
,
290 mVerifyState
= CVS_Bad
;
291 if(!forCert
|| forCert
->addStatusCode(crtn
)) {
296 CSSM_X509_REVOKED_CERT_LIST_PTR revoked
=
297 mX509Crl
->tbsCertList
.revokedCertificates
;
298 if(revoked
!= NULL
) {
299 for(uint32 dex
=0; dex
<revoked
->numberOfRevokedCertEntries
; dex
++) {
300 bool dummyIsIndirect
; // can't be set here
301 crtn
= parseExtensions(tpVerifyContext
,
304 revoked
->revokedCertEntry
[dex
].extensions
,
308 if(!forCert
|| forCert
->addStatusCode(crtn
)) {
309 mVerifyState
= CVS_Bad
;
317 * Step 3: obtain a fully verified cert chain which verifies this CRL.
319 CSSM_BOOL verifiedToRoot
;
320 CSSM_BOOL verifiedToAnchor
;
321 CSSM_BOOL verifiedViaTrustSetting
;
323 TPCertGroup
outCertGroup(tpVerifyContext
.alloc
,
324 TGO_Caller
); // CRLs owned by inCertGroup
326 /* set up for disposal of TPCertInfos created by
327 * CertGroupConstructPriv */
328 TPCertGroup
certsToBeFreed(tpVerifyContext
.alloc
, TGO_Group
);
330 if(tpVerifyContext
.signerCerts
) {
331 /* start from scratch with this group */
332 tpVerifyContext
.signerCerts
->setAllUnused();
334 crtn
= outCertGroup
.buildCertGroup(
335 *this, // subject item
336 tpVerifyContext
.signerCerts
, // inCertGroup, optional
337 tpVerifyContext
.dbList
, // optional
338 tpVerifyContext
.clHand
,
339 tpVerifyContext
.cspHand
,
340 tpVerifyContext
.verifyTime
,
341 tpVerifyContext
.numAnchorCerts
,
342 tpVerifyContext
.anchorCerts
,
344 &tpVerifyContext
.gatheredCerts
,
345 CSSM_FALSE
, // subjectIsInGroup
346 tpVerifyContext
.actionFlags
,
347 tpVerifyContext
.policyOid
,
348 tpVerifyContext
.policyStr
,
349 tpVerifyContext
.policyStrLen
,
350 kSecTrustSettingsKeyUseSignRevocation
,
353 verifiedViaTrustSetting
);
354 /* subsequent errors to errOut: */
357 tpCrlDebug("TPCrlInfo::verifyWithContext buildCertGroup failure "
358 "index %u", index());
359 if(!forCert
|| forCert
->addStatusCode(crtn
)) {
363 if (verifiedToRoot
&& (tpVerifyContext
.actionFlags
& CSSM_TP_ACTION_IMPLICIT_ANCHORS
))
364 verifiedToAnchor
= CSSM_TRUE
;
365 if(!verifiedToAnchor
&& !verifiedViaTrustSetting
) {
368 /* verified to root which is not an anchor */
369 tpCrlDebug("TPCrlInfo::verifyWithContext root, no anchor, "
370 "index %u", index());
371 crtn
= CSSMERR_APPLETP_CRL_INVALID_ANCHOR_CERT
;
374 /* partial chain, no root, not verifiable by anchor */
375 tpCrlDebug("TPCrlInfo::verifyWithContext no root, no anchor, "
376 "index %u", index());
377 crtn
= CSSMERR_APPLETP_CRL_NOT_TRUSTED
;
379 if(!forCert
|| forCert
->addStatusCode(crtn
)) {
380 mVerifyState
= CVS_Bad
;
386 * Step 4: policy verification on the returned cert group
387 * We need to (temporarily) assert the "leaf cert is a CA" flag
390 outCertGroup
.certAtIndex(0)->isLeaf(true);
391 crtn
= tp_policyVerify(kCrlPolicy
,
392 tpVerifyContext
.alloc
,
393 tpVerifyContext
.clHand
,
394 tpVerifyContext
.cspHand
,
397 verifiedViaTrustSetting
,
398 tpVerifyContext
.actionFlags
| CSSM_TP_ACTION_LEAF_IS_CA
,
400 NULL
); // policyOpts, not currently used
402 tpCrlDebug(" ...verifyWithContext policy FAILURE CRL %u",
404 if(!forCert
|| forCert
->addStatusCode(CSSMERR_APPLETP_CRL_POLICY_FAIL
)) {
405 mVerifyState
= CVS_Bad
;
411 * Step 5: recursively perform CRL verification on the certs
412 * gathered to verify this CRL.
413 * Only performed if this CRL is an indirect CRL or the caller
414 * explicitly told us to do this (i.e., caller is verifying a
415 * CRL, not a cert chain).
417 if(isIndirectCrl
|| doCrlVerify
) {
418 tpCrlDebug("verifyWithContext recursing to "
419 "tpVerifyCertGroupWithCrls");
420 crtn
= tpVerifyCertGroupWithCrls(tpVerifyContext
,
423 tpCrlDebug(" ...verifyWithContext CRL reverify FAILURE CRL %u",
425 if(!forCert
|| forCert
->addStatusCode(crtn
)) {
426 mVerifyState
= CVS_Bad
;
432 tpCrlDebug(" ...verifyWithContext CRL %u SUCCESS", index());
433 mVerifyState
= CVS_Good
;
435 /* we own these, we free the DB records */
436 certsToBeFreed
.freeDbRecords();
441 * Wrapper for verifyWithContext for use when evaluating a CRL
442 * "now" instead of at the time in TPVerifyContext.verifyTime.
443 * In this case, on entry, TPVerifyContext.verifyTime is the
444 * time at which a cert is being evaluated.
446 CSSM_RETURN
TPCrlInfo::verifyWithContextNow(
447 TPVerifyContext
&tpVerifyContext
,
448 TPCertInfo
*forCert
, // optional
451 CSSM_TIMESTRING ctxTime
= tpVerifyContext
.verifyTime
;
452 CSSM_RETURN crtn
= verifyWithContext(tpVerifyContext
, forCert
, doCrlVerify
);
453 tpVerifyContext
.verifyTime
= ctxTime
;
458 * Do I have the same issuer as the specified subject cert? Returns
461 bool TPCrlInfo::hasSameIssuer(
462 const TPCertInfo
&subject
)
464 assert(subject
.issuerName() != NULL
);
465 if(tpCompareCssmData(issuerName(), subject
.issuerName())) {
474 * Determine if specified cert has been revoked as of the
475 * provided time; a NULL timestring indicates "now".
477 * Assumes current CRL is verified good and that issuer names of
478 * the cert and CRL match.
480 * This duplicates similar logic in the CL, but to avoid re-parsing
481 * the subject cert (which we have parsed and cached), we just do it
484 * Possible errors are
485 * CSSMERR_TP_CERT_REVOKED
486 * CSSMERR_TP_CERT_SUSPENDED
489 * Error status is added to subjectCert.
491 CSSM_RETURN
TPCrlInfo::isCertRevoked(
492 TPCertInfo
&subjectCert
,
493 CSSM_TIMESTRING verifyTime
)
495 assert(mVerifyState
== CVS_Good
);
496 CSSM_X509_TBS_CERTLIST_PTR tbs
= &mX509Crl
->tbsCertList
;
498 /* trivial case - empty CRL */
499 if((tbs
->revokedCertificates
== NULL
) ||
500 (tbs
->revokedCertificates
->numberOfRevokedCertEntries
== 0)) {
501 tpCrlDebug(" isCertRevoked: empty CRL at index %u", index());
505 /* is subject cert's serial number in this CRL? */
506 CSSM_DATA_PTR subjSerial
= NULL
;
508 crtn
= subjectCert
.fetchField(&CSSMOID_X509V1SerialNumber
, &subjSerial
);
510 /* should never happen */
511 tpErrorLog("TPCrlInfo:isCertRevoked: error fetching serial number\n");
512 if(subjectCert
.addStatusCode(crtn
)) {
516 /* allowed error - can't proceed; punt with success */
520 /* subsequent errors to errOut: */
522 uint32 numEntries
= tbs
->revokedCertificates
->numberOfRevokedCertEntries
;
523 CSSM_X509_REVOKED_CERT_ENTRY_PTR entries
=
524 tbs
->revokedCertificates
->revokedCertEntry
;
526 CFDateRef cfRevokedTime
= NULL
;
527 CFDateRef cfVerifyTime
= NULL
;
529 for(uint32 dex
=0; dex
<numEntries
; dex
++) {
530 CSSM_X509_REVOKED_CERT_ENTRY_PTR entry
= &entries
[dex
];
531 if(tpCompareCssmData(subjSerial
, &entry
->certificateSerialNumber
)) {
533 * It's in there. Compare revocation time in the CRL to
534 * our caller-specified verifyTime.
536 CSSM_X509_TIME_PTR xTime
= &entry
->revocationDate
;
538 rtn
= timeStringToCfDate((char *)xTime
->time
.Data
, (unsigned)xTime
->time
.Length
,
541 tpErrorLog("fetchNotBeforeAfter: malformed revocationDate\n");
544 if(verifyTime
!= NULL
) {
545 rtn
= timeStringToCfDate((char *)verifyTime
, (unsigned)strlen(verifyTime
),
549 /* verify right now */
550 cfVerifyTime
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
552 if((rtn
== 0) && cfVerifyTime
!= NULL
) {
553 CFComparisonResult res
= CFDateCompare(cfVerifyTime
, cfRevokedTime
, NULL
);
554 if(res
== kCFCompareLessThan
) {
555 /* cfVerifyTime < cfRevokedTime; I guess this one's OK */
556 tpCrlDebug(" isCertRevoked: cert %u NOT YET REVOKED by CRL %u",
557 subjectCert
.index(), index());
564 * REQUIRED TBD: parse the entry's extensions, specifically to
565 * get a reason. This will entail a bunch of new TP/cert specific
567 * For now, just flag it revoked.
569 crtn
= CSSMERR_TP_CERT_REVOKED
;
570 tpCrlDebug(" isCertRevoked: cert %u REVOKED by CRL %u",
571 subjectCert
.index(), index());
576 subjectCert
.freeField(&CSSMOID_X509V1SerialNumber
, subjSerial
);
577 if(crtn
&& !subjectCert
.addStatusCode(crtn
)) {
581 CFRelease(cfRevokedTime
);
584 CFRelease(cfVerifyTime
);
593 /* build empty group */
594 TPCrlGroup::TPCrlGroup(
596 TPGroupOwner whoOwns
) :
603 /* nothing for now */
607 * Construct from unordered, untrusted CSSM_CRLGROUP. Resulting
608 * TPCrlInfos are more or less in the same order as the incoming
609 * CRLs, though incoming CRLs are discarded if they don't parse.
610 * No verification of any sort is performed.
612 TPCrlGroup::TPCrlGroup(
613 const CSSM_CRLGROUP
*cssmCrlGroup
, // optional
614 CSSM_CL_HANDLE clHand
,
615 CSSM_CSP_HANDLE cspHand
,
617 const char *verifyTime
, // may be NULL
618 TPGroupOwner whoOwns
) :
625 /* verify input args */
626 if((cssmCrlGroup
== NULL
) || (cssmCrlGroup
->NumberOfCrls
== 0)) {
629 if(cspHand
== CSSM_INVALID_HANDLE
) {
630 CssmError::throwMe(CSSMERR_TP_INVALID_CSP_HANDLE
);
632 if(clHand
== CSSM_INVALID_HANDLE
) {
633 CssmError::throwMe(CSSMERR_TP_INVALID_CL_HANDLE
);
635 if(cssmCrlGroup
->CrlGroupType
!= CSSM_CRLGROUP_DATA
) {
636 CssmError::throwMe(CSSMERR_TP_INVALID_CERTGROUP
);
638 switch(cssmCrlGroup
->CrlType
) {
639 case CSSM_CRL_TYPE_X_509v1
:
640 case CSSM_CRL_TYPE_X_509v2
:
643 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT
);
645 switch(cssmCrlGroup
->CrlEncoding
) {
646 case CSSM_CRL_ENCODING_BER
:
647 case CSSM_CRL_ENCODING_DER
:
650 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT
);
654 * Add remaining input certs to mCrlInfo.
656 TPCrlInfo
*crlInfo
= NULL
;
657 for(unsigned crlDex
=0; crlDex
<cssmCrlGroup
->NumberOfCrls
; crlDex
++) {
659 crlInfo
= new TPCrlInfo(clHand
,
661 &cssmCrlGroup
->GroupCrlList
.CrlList
[crlDex
],
662 TIC_NoCopy
, // don't copy data
666 /* just ignore this CRL */
669 crlInfo
->index(crlDex
);
675 * Deletes all TPCrlInfo's if appropriate.
677 TPCrlGroup::~TPCrlGroup()
679 if(mWhoOwns
== TGO_Group
) {
681 for(i
=0; i
<mNumCrls
; i
++) {
685 mAlloc
.free(mCrlInfo
);
688 /* add/remove/access TPTCrlInfo's. */
690 * NOTE: I am aware that most folks would just use an array<> here, but
691 * gdb is so lame that it doesn't even let one examine the contents
692 * of an array<> (or just about anything else in the STL). I prefer
693 * debuggability over saving a few lines of trivial code.
695 void TPCrlGroup::appendCrl(
698 if(mNumCrls
== mSizeofCrlInfo
) {
699 if(mSizeofCrlInfo
== 0) {
700 /* appending to empty array */
706 mCrlInfo
= (TPCrlInfo
**)mAlloc
.realloc(mCrlInfo
,
707 mSizeofCrlInfo
* sizeof(TPCrlInfo
*));
709 mCrlInfo
[mNumCrls
++] = &crlInfo
;
712 TPCrlInfo
*TPCrlGroup::crlAtIndex(
715 if(index
> (mNumCrls
- 1)) {
716 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
718 return mCrlInfo
[index
];
721 TPCrlInfo
&TPCrlGroup::removeCrlAtIndex(
722 unsigned index
) // doesn't delete the cert, just
723 // removes it from our list
725 if(index
> (mNumCrls
- 1)) {
726 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
728 TPCrlInfo
&rtn
= *mCrlInfo
[index
];
730 /* removed requested element and compact remaining array */
732 for(i
=index
; i
<(mNumCrls
- 1); i
++) {
733 mCrlInfo
[i
] = mCrlInfo
[i
+1];
739 void TPCrlGroup::removeCrl(
742 for(unsigned dex
=0; dex
<mNumCrls
; dex
++) {
743 if(mCrlInfo
[dex
] == &crlInfo
) {
744 removeCrlAtIndex(dex
);
748 tpErrorLog("TPCrlGroup::removeCrl: CRL NOT FOUND\n");
752 TPCrlInfo
*TPCrlGroup::firstCrl()
755 /* the caller really should not do this... */
756 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
763 TPCrlInfo
*TPCrlGroup::lastCrl()
766 /* the caller really should not do this... */
767 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
770 return mCrlInfo
[mNumCrls
- 1];
775 * Find a CRL whose issuer matches specified subject cert.
776 * Returned CRL has not necessarily been verified.
778 TPCrlInfo
*TPCrlGroup::findCrlForCert(
781 for(unsigned dex
=0; dex
<mNumCrls
; dex
++) {
782 TPCrlInfo
*crl
= mCrlInfo
[dex
];
783 if(crl
->hasSameIssuer(subject
)) {