]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_x509_tp/lib/TPCrlInfo.cpp
Security-58286.1.32.tar.gz
[apple/security.git] / OSX / libsecurity_apple_x509_tp / lib / TPCrlInfo.cpp
1 /*
2 * Copyright (c) 2002,2011-2012,2014-2015 Apple Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19 /*
20 * TPCrlInfo.h - TP's private CRL and CRL group
21 *
22 */
23
24 #include "TPCrlInfo.h"
25 #include "tpdebugging.h"
26 #include "certGroupUtils.h"
27 #include "tpCrlVerify.h"
28 #include "tpPolicies.h"
29 #include "tpTime.h"
30 #include <Security/cssmapi.h>
31 #include <Security/x509defs.h>
32 #include <Security/oidscert.h>
33 #include <Security/oidscrl.h>
34 #include <security_cdsa_utilities/cssmerrors.h>
35 #include <utilities/SecCFRelease.h>
36 #include <string.h> /* for memcmp */
37 #include <Security/cssmapple.h>
38
39 /*
40 * Replacement for CSSM_CL_CrlGetFirstCachedFieldValue for use with
41 * TPCrlItemInfo's generic getFirstCachedField mechanism.
42 */
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,
48 CSSM_DATA_PTR *Value)
49 {
50 return CSSM_CL_CrlGetFirstCachedFieldValue(CLHandle,
51 CrlHandle,
52 NULL, // const CSSM_DATA *CrlRecordIndex,
53 CrlField,
54 ResultsHandle,
55 NumberOfMatchedFields,
56 Value);
57 }
58
59 static const TPClItemCalls tpCrlClCalls =
60 {
61 tpGetFirstCachedFieldValue,
62 CSSM_CL_CrlAbortQuery,
63 CSSM_CL_CrlCache,
64 CSSM_CL_CrlAbortCache,
65 CSSM_CL_CrlVerify,
66 &CSSMOID_X509V1CRLThisUpdate,
67 &CSSMOID_X509V1CRLNextUpdate,
68 CSSMERR_TP_INVALID_CRL_POINTER,
69 CSSMERR_APPLETP_CRL_EXPIRED,
70 CSSMERR_APPLETP_CRL_NOT_VALID_YET
71 };
72
73
74 /*
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.
78 */
79 TPCrlInfo::TPCrlInfo(
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
86
87 : TPClItemInfo(clHand, cspHand, tpCrlClCalls, crlData,
88 copyCrlData, verifyTime),
89 mRefCount(0),
90 mFromWhere(CFW_Nowhere),
91 mX509Crl(NULL),
92 mCrlFieldToFree(NULL),
93 mVerifyState(CVS_Unknown),
94 mVerifyError(CSSMERR_TP_INTERNAL_ERROR)
95 {
96 CSSM_RETURN crtn;
97
98 mUri.Data = NULL;
99 mUri.Length = 0;
100
101 /* fetch parsed CRL */
102 crtn = fetchField(&CSSMOID_X509V2CRLSignedCrlCStruct, &mCrlFieldToFree);
103 if(crtn) {
104 /* bad CRL */
105 releaseResources();
106 CssmError::throwMe(crtn);
107 }
108 if(mCrlFieldToFree->Length != sizeof(CSSM_X509_SIGNED_CRL)) {
109 tpErrorLog("fetchField(SignedCrlCStruct) length error\n");
110 releaseResources();
111 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
112 }
113 mX509Crl = (CSSM_X509_SIGNED_CRL *)mCrlFieldToFree->Data;
114 /* any other other commonly used fields? */
115 }
116
117 TPCrlInfo::~TPCrlInfo()
118 {
119 releaseResources();
120 }
121
122 void TPCrlInfo::releaseResources()
123 {
124 if(mCrlFieldToFree) {
125 freeField(&CSSMOID_X509V2CRLSignedCrlCStruct, mCrlFieldToFree);
126 mCrlFieldToFree = NULL;
127 }
128 if(mUri.Data) {
129 Allocator::standard().free(mUri.Data);
130 mUri.Data = NULL;
131 mUri.Length = 0;
132 }
133 TPClItemInfo::releaseResources();
134 }
135
136 void TPCrlInfo::uri(const CSSM_DATA &uri)
137 {
138 tpCopyCssmData(Allocator::standard(), &uri, &mUri);
139 }
140
141 /*
142 * List of extensions we understand and can accept as critical.
143 */
144 static const CSSM_OID *const TPGoodCrlExtens[] =
145 {
146 &CSSMOID_CrlNumber,
147 /* Note NOT CSSMOID_DeltaCrlIndicator! That's fatal */
148 &CSSMOID_CrlReason,
149 &CSSMOID_CertIssuer,
150 &CSSMOID_IssuingDistributionPoint,
151 &CSSMOID_HoldInstructionCode,
152 &CSSMOID_InvalidityDate,
153 &CSSMOID_AuthorityKeyIdentifier,
154 &CSSMOID_SubjectAltName,
155 &CSSMOID_IssuerAltName
156 };
157
158 #define NUM_KNOWN_EXTENS (sizeof(TPGoodCrlExtens) / sizeof(CSSM_OID_PTR))
159
160 /*
161 * Do our best to understand all the entries in a CSSM_X509_EXTENSIONS,
162 * which may be per-CRL or per-entry.
163 *
164 * For now, we just ensure that for every critical extension,
165 * we actually understand it and can deal it.
166 */
167 CSSM_RETURN TPCrlInfo::parseExtensions(
168 TPVerifyContext &vfyCtx,
169 bool isPerEntry,
170 uint32 entryIndex, // if isPerEntry
171 const CSSM_X509_EXTENSIONS &extens,
172 TPCertInfo *forCert, // optional
173 bool &isIndirectCrl) // RETURNED
174 {
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? */
180 unsigned i;
181 for(i=0; i<NUM_KNOWN_EXTENS; i++) {
182 if(tpCompareOids(&exten->extnId, TPGoodCrlExtens[i])) {
183 /* we're cool with this one */
184 break;
185 }
186 }
187 if(i == NUM_KNOWN_EXTENS) {
188 tpCrlDebug("parseExtensions: Unknown Critical Extension\n");
189 return CSSMERR_APPLETP_UNKNOWN_CRL_EXTEN;
190 }
191 }
192
193 /* Specific extension handling. */
194 if(tpCompareOids(&exten->extnId,
195 &CSSMOID_IssuingDistributionPoint)) {
196 /*
197 * If this assertion fails, we're out of sync with the CL
198 */
199 assert(exten->format == CSSM_X509_DATAFORMAT_PARSED);
200 CE_IssuingDistributionPoint *idp =
201 (CE_IssuingDistributionPoint *)
202 exten->value.parsedValue;
203
204 /*
205 * Snag indirectCrl flag for caller in any case
206 */
207 if(idp->indirectCrlPresent && idp->indirectCrl) {
208 isIndirectCrl = true;
209 }
210 if(forCert != NULL) {
211 /* If no target cert, i.e., we're just verifying a CRL,
212 * skip the remaining IDP checks. */
213
214 /* verify onlyCACerts/onlyUserCerts */
215 bool isUserCert;
216 if(forCert->isLeaf() &&
217 !(vfyCtx.actionFlags & CSSM_TP_ACTION_LEAF_IS_CA)) {
218 isUserCert = true;
219 }
220 else {
221 isUserCert = false;
222 }
223 if((idp->onlyUserCertsPresent) && (idp->onlyUserCerts)) {
224 if(!isUserCert) {
225 tpCrlDebug("parseExtensions: onlyUserCerts, "
226 "!leaf\n");
227 return CSSMERR_APPLETP_IDP_FAIL;
228 }
229 }
230 if((idp->onlyCACertsPresent) && (idp->onlyCACerts)) {
231 if(isUserCert) {
232 tpCrlDebug("parseExtensions: onlyCACerts, leaf\n");
233 return CSSMERR_APPLETP_IDP_FAIL;
234 }
235 }
236
237 /* Verify DistributionPointName matches cRLDistributionPoints
238 * in cert.
239 */
240 if(idp->distPointName) {
241 CSSM_DATA_PTR certDistPoints;
242 CSSM_RETURN crtn = forCert->fetchField(&CSSMOID_CrlDistributionPoints, &certDistPoints);
243 switch(crtn) {
244 case CSSM_OK:
245 break;
246 case CSSMERR_CL_NO_FIELD_VALUES:
247 return CSSM_OK;
248 default:
249 return crtn;
250 }
251 if (certDistPoints->Length != sizeof(CSSM_X509_EXTENSION)) {
252 forCert->freeField(&CSSMOID_CrlDistributionPoints, certDistPoints);
253 return CSSMERR_TP_UNKNOWN_FORMAT;
254 }
255 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)certDistPoints->Data;
256 if (cssmExt == NULL) {
257 forCert->freeField(&CSSMOID_CrlDistributionPoints, certDistPoints);
258 return CSSMERR_TP_UNKNOWN_FORMAT;
259 }
260 CE_CRLDistPointsSyntax *dps = (CE_CRLDistPointsSyntax *)cssmExt->value.parsedValue;
261 if (dps == NULL) {
262 forCert->freeField(&CSSMOID_CrlDistributionPoints, certDistPoints);
263 return CSSMERR_TP_UNKNOWN_FORMAT;
264 }
265 if (!dps->numDistPoints) {
266 /* no distribution points in the cert extension */
267 forCert->freeField(&CSSMOID_CrlDistributionPoints, certDistPoints);
268 return CSSM_OK;
269 }
270
271 /* Loop over the cRLDistributionPoints in the cert. */
272 CSSM_BOOL sameType = CSSM_FALSE;
273 CSSM_BOOL found = CSSM_FALSE;
274 for (unsigned dex=0; dex<dps->numDistPoints; dex++) {
275 CE_CRLDistributionPoint *dp = &dps->distPoints[dex];
276 if (dp->distPointName == NULL) {
277 continue;
278 }
279 if (idp->distPointName->nameType != dp->distPointName->nameType) {
280 /* Not the same name type; move on. */
281 continue;
282 }
283 sameType = CSSM_TRUE;
284 switch (dp->distPointName->nameType) {
285 case CE_CDNT_NameRelativeToCrlIssuer: {
286 if (true) {
287 /* RDN code below is not tested, so we won't use it.
288 * Defaulting to prior behavior of accepting without testing.
289 */
290 found = CSSM_TRUE;
291 tpErrorLog("parseExtensions: "
292 "CE_CDNT_NameRelativeToCrlIssuer not implemented\n");
293 break;
294 }
295 #if 0
296 /* relativeName is a RDN sequence */
297 CSSM_X509_RDN_PTR idpName = idp->distPointName->dpn.rdn;
298 CSSM_X509_RDN_PTR certName = dp->distPointName->dpn.rdn;
299 if (idpName == NULL || certName == NULL || idpName->numberOfPairs != certName->numberOfPairs) {
300 /* They don't have the same number of attribute/value pairs; move on. */
301 continue;
302 }
303 unsigned nDex;
304 for (nDex=0; nDex<idpName->numberOfPairs; nDex++) {
305 CSSM_X509_TYPE_VALUE_PAIR_PTR iPair = idpName->AttributeTypeAndValue;
306 CSSM_X509_TYPE_VALUE_PAIR_PTR cPair = certName->AttributeTypeAndValue;
307 if (!tpCompareCssmData(&iPair->type, &cPair->type) ||
308 !tpCompareCssmData(&iPair->value, &cPair->value)) {
309 break;
310 }
311 }
312 if (nDex==idpName->numberOfPairs) {
313 /* All the pairs matched. */
314 found = CSSM_TRUE;
315 }
316 #endif
317 }
318 case CE_CDNT_FullName: {
319 /* fullName is a GeneralNames sequence */
320 CE_GeneralNames *idpNames = idp->distPointName->dpn.fullName;
321 CE_GeneralNames *certNames = dp->distPointName->dpn.fullName;
322 if (idpNames == NULL || certNames == NULL || idpNames->numNames != certNames->numNames) {
323 /* They don't have the same number of names; move on. */
324 continue;
325 }
326 unsigned nDex;
327 for (nDex=0; nDex<idpNames->numNames; nDex++) {
328 CE_GeneralName *idpName = &idpNames->generalName[nDex];
329 CE_GeneralName *certName = &certNames->generalName[nDex];
330 if ((idpName->nameType != certName->nameType) ||
331 (!tpCompareCssmData(&idpName->name, &certName->name))) {
332 break;
333 }
334 }
335 if (nDex==idpNames->numNames) {
336 /* All the names matched. */
337 found = CSSM_TRUE;
338 }
339 break;
340 }
341 default: {
342 forCert->freeField(&CSSMOID_CrlDistributionPoints, certDistPoints);
343 return CSSMERR_TP_UNKNOWN_FORMAT;
344 }
345 }
346 if (found) {
347 break; /* out of loop over crlDistribtionPoints in cert. */
348 }
349 }
350 forCert->freeField(&CSSMOID_CrlDistributionPoints, certDistPoints);
351 if(sameType && !found) {
352 return CSSMERR_APPLETP_IDP_FAIL;
353 }
354 } /* distPointName check */
355 } /* IDP */
356 } /* have target cert */
357 }
358
359 return CSSM_OK;
360 }
361
362 /*
363 * The heavyweight "perform full verification of this CRL" op.
364 * Must verify to an anchor cert in tpVerifyContext or via
365 * Trust Settings if so enabled.
366 * Intermediate certs can come from signerCerts or dBList.
367 */
368 CSSM_RETURN TPCrlInfo::verifyWithContext(
369 TPVerifyContext &tpVerifyContext,
370 TPCertInfo *forCert, // optional
371 bool doCrlVerify)
372 {
373 /*
374 * Step 1: this CRL must be current. Caller might have re-evaluated
375 * expired/notValidYet since our construction via calculateCurrent().
376 */
377 if(isExpired()) {
378 return CSSMERR_APPLETP_CRL_EXPIRED;
379 }
380 if(isNotValidYet()) {
381 return CSSMERR_APPLETP_CRL_NOT_VALID_YET;
382 }
383
384 /* subsequent verify state is cached */
385 switch(mVerifyState) {
386 case CVS_Good:
387 return CSSM_OK;
388 case CVS_Bad:
389 return mVerifyError;
390 case CVS_Unknown:
391 break;
392 default:
393 tpErrorLog("verifyWithContext: bad verifyState\n");
394 return CSSMERR_TP_INTERNAL_ERROR;
395 }
396
397 /*
398 * Step 2: parse & understand all critical CRL extensions.
399 */
400 CSSM_RETURN crtn;
401 bool isIndirectCrl;
402 crtn = parseExtensions(tpVerifyContext,
403 false,
404 0,
405 mX509Crl->tbsCertList.extensions,
406 forCert,
407 isIndirectCrl);
408 if(crtn) {
409 mVerifyState = CVS_Bad;
410 if(!forCert || forCert->addStatusCode(crtn)) {
411 return crtn;
412 }
413 /* else continue */
414 }
415 CSSM_X509_REVOKED_CERT_LIST_PTR revoked =
416 mX509Crl->tbsCertList.revokedCertificates;
417 if(revoked != NULL) {
418 for(uint32 dex=0; dex<revoked->numberOfRevokedCertEntries; dex++) {
419 bool dummyIsIndirect; // can't be set here
420 crtn = parseExtensions(tpVerifyContext,
421 true,
422 dex,
423 revoked->revokedCertEntry[dex].extensions,
424 forCert,
425 dummyIsIndirect);
426 if(crtn) {
427 if(!forCert || forCert->addStatusCode(crtn)) {
428 mVerifyState = CVS_Bad;
429 return crtn;
430 }
431 }
432 }
433 }
434
435 /*
436 * Step 3: obtain a fully verified cert chain which verifies this CRL.
437 */
438 CSSM_BOOL verifiedToRoot;
439 CSSM_BOOL verifiedToAnchor;
440 CSSM_BOOL verifiedViaTrustSetting;
441
442 TPCertGroup outCertGroup(tpVerifyContext.alloc,
443 TGO_Caller); // CRLs owned by inCertGroup
444
445 /* set up for disposal of TPCertInfos created by
446 * CertGroupConstructPriv */
447 TPCertGroup certsToBeFreed(tpVerifyContext.alloc, TGO_Group);
448
449 if(tpVerifyContext.signerCerts) {
450 /* start from scratch with this group */
451 tpVerifyContext.signerCerts->setAllUnused();
452 }
453 crtn = outCertGroup.buildCertGroup(
454 *this, // subject item
455 tpVerifyContext.signerCerts, // inCertGroup, optional
456 tpVerifyContext.dbList, // optional
457 tpVerifyContext.clHand,
458 tpVerifyContext.cspHand,
459 tpVerifyContext.verifyTime,
460 tpVerifyContext.numAnchorCerts,
461 tpVerifyContext.anchorCerts,
462 certsToBeFreed,
463 &tpVerifyContext.gatheredCerts,
464 CSSM_FALSE, // subjectIsInGroup
465 tpVerifyContext.actionFlags,
466 tpVerifyContext.policyOid,
467 tpVerifyContext.policyStr,
468 tpVerifyContext.policyStrLen,
469 kSecTrustSettingsKeyUseSignRevocation,
470 verifiedToRoot,
471 verifiedToAnchor,
472 verifiedViaTrustSetting);
473 /* subsequent errors to errOut: */
474
475 if(crtn) {
476 tpCrlDebug("TPCrlInfo::verifyWithContext buildCertGroup failure "
477 "index %u", index());
478 if(!forCert || forCert->addStatusCode(crtn)) {
479 goto errOut;
480 }
481 }
482 if (verifiedToRoot && (tpVerifyContext.actionFlags & CSSM_TP_ACTION_IMPLICIT_ANCHORS))
483 verifiedToAnchor = CSSM_TRUE;
484 if(!verifiedToAnchor && !verifiedViaTrustSetting) {
485 /* required */
486 if(verifiedToRoot) {
487 /* verified to root which is not an anchor */
488 tpCrlDebug("TPCrlInfo::verifyWithContext root, no anchor, "
489 "index %u", index());
490 crtn = CSSMERR_APPLETP_CRL_INVALID_ANCHOR_CERT;
491 }
492 else {
493 /* partial chain, no root, not verifiable by anchor */
494 tpCrlDebug("TPCrlInfo::verifyWithContext no root, no anchor, "
495 "index %u", index());
496 crtn = CSSMERR_APPLETP_CRL_NOT_TRUSTED;
497 }
498 if(!forCert || forCert->addStatusCode(crtn)) {
499 mVerifyState = CVS_Bad;
500 goto errOut;
501 }
502 }
503
504 /*
505 * Step 4: policy verification on the returned cert group
506 * We need to (temporarily) assert the "leaf cert is a CA" flag
507 * here.
508 */
509 outCertGroup.certAtIndex(0)->isLeaf(true);
510 crtn = tp_policyVerify(kCrlPolicy,
511 tpVerifyContext.alloc,
512 tpVerifyContext.clHand,
513 tpVerifyContext.cspHand,
514 &outCertGroup,
515 verifiedToRoot,
516 verifiedViaTrustSetting,
517 tpVerifyContext.actionFlags | CSSM_TP_ACTION_LEAF_IS_CA,
518 NULL, // sslOpts
519 NULL); // policyOpts, not currently used
520 if(crtn) {
521 tpCrlDebug(" ...verifyWithContext policy FAILURE CRL %u",
522 index());
523 if(!forCert || forCert->addStatusCode(CSSMERR_APPLETP_CRL_POLICY_FAIL)) {
524 mVerifyState = CVS_Bad;
525 goto errOut;
526 }
527 }
528
529 /*
530 * Step 5: recursively perform CRL verification on the certs
531 * gathered to verify this CRL.
532 * Only performed if this CRL is an indirect CRL or the caller
533 * explicitly told us to do this (i.e., caller is verifying a
534 * CRL, not a cert chain).
535 */
536 if(isIndirectCrl || doCrlVerify) {
537 tpCrlDebug("verifyWithContext recursing to "
538 "tpVerifyCertGroupWithCrls");
539 crtn = tpVerifyCertGroupWithCrls(tpVerifyContext,
540 outCertGroup);
541 if(crtn) {
542 tpCrlDebug(" ...verifyWithContext CRL reverify FAILURE CRL %u",
543 index());
544 if(!forCert || forCert->addStatusCode(crtn)) {
545 mVerifyState = CVS_Bad;
546 goto errOut;
547 }
548 }
549 }
550
551 tpCrlDebug(" ...verifyWithContext CRL %u SUCCESS", index());
552 mVerifyState = CVS_Good;
553 errOut:
554 /* we own these, we free the DB records */
555 certsToBeFreed.freeDbRecords();
556 return crtn;
557 }
558
559 /*
560 * Wrapper for verifyWithContext for use when evaluating a CRL
561 * "now" instead of at the time in TPVerifyContext.verifyTime.
562 * In this case, on entry, TPVerifyContext.verifyTime is the
563 * time at which a cert is being evaluated.
564 */
565 CSSM_RETURN TPCrlInfo::verifyWithContextNow(
566 TPVerifyContext &tpVerifyContext,
567 TPCertInfo *forCert, // optional
568 bool doCrlVerify)
569 {
570 CSSM_TIMESTRING ctxTime = tpVerifyContext.verifyTime;
571 CSSM_RETURN crtn = verifyWithContext(tpVerifyContext, forCert, doCrlVerify);
572 tpVerifyContext.verifyTime = ctxTime;
573 return crtn;
574 }
575
576 /*
577 * Do I have the same issuer as the specified subject cert? Returns
578 * true if so.
579 */
580 bool TPCrlInfo::hasSameIssuer(
581 const TPCertInfo &subject)
582 {
583 assert(subject.issuerName() != NULL);
584 if(tpCompareCssmData(issuerName(), subject.issuerName())) {
585 return true;
586 }
587 else {
588 return false;
589 }
590 }
591
592 /*
593 * Determine if specified cert has been revoked as of the
594 * provided time; a NULL timestring indicates "now".
595 *
596 * Assumes current CRL is verified good and that issuer names of
597 * the cert and CRL match.
598 *
599 * This duplicates similar logic in the CL, but to avoid re-parsing
600 * the subject cert (which we have parsed and cached), we just do it
601 * here.
602 *
603 * Possible errors are
604 * CSSMERR_TP_CERT_REVOKED
605 * CSSMERR_TP_CERT_SUSPENDED
606 * TBD
607 *
608 * Error status is added to subjectCert.
609 */
610 CSSM_RETURN TPCrlInfo::isCertRevoked(
611 TPCertInfo &subjectCert,
612 CSSM_TIMESTRING verifyTime)
613 {
614 assert(mVerifyState == CVS_Good);
615 CSSM_X509_TBS_CERTLIST_PTR tbs = &mX509Crl->tbsCertList;
616
617 /* trivial case - empty CRL */
618 if((tbs->revokedCertificates == NULL) ||
619 (tbs->revokedCertificates->numberOfRevokedCertEntries == 0)) {
620 tpCrlDebug(" isCertRevoked: empty CRL at index %u", index());
621 return CSSM_OK;
622 }
623
624 /* is subject cert's serial number in this CRL? */
625 CSSM_DATA_PTR subjSerial = NULL;
626 CSSM_RETURN crtn;
627 crtn = subjectCert.fetchField(&CSSMOID_X509V1SerialNumber, &subjSerial);
628 if(crtn) {
629 /* should never happen */
630 tpErrorLog("TPCrlInfo:isCertRevoked: error fetching serial number\n");
631 if(subjectCert.addStatusCode(crtn)) {
632 return crtn;
633 }
634 else {
635 /* allowed error - can't proceed; punt with success */
636 return CSSM_OK;
637 }
638 }
639 /* subsequent errors to errOut: */
640
641 uint32 numEntries = tbs->revokedCertificates->numberOfRevokedCertEntries;
642 CSSM_X509_REVOKED_CERT_ENTRY_PTR entries =
643 tbs->revokedCertificates->revokedCertEntry;
644 crtn = CSSM_OK;
645 CFDateRef cfRevokedTime = NULL;
646 CFDateRef cfVerifyTime = NULL;
647
648 for(uint32 dex=0; dex<numEntries; dex++) {
649 CSSM_X509_REVOKED_CERT_ENTRY_PTR entry = &entries[dex];
650 if(tpCompareCssmData(subjSerial, &entry->certificateSerialNumber)) {
651 /*
652 * It's in there. Compare revocation time in the CRL to
653 * our caller-specified verifyTime.
654 */
655 CSSM_X509_TIME_PTR xTime = &entry->revocationDate;
656 int rtn;
657 rtn = timeStringToCfDate((char *)xTime->time.Data, (unsigned)xTime->time.Length,
658 &cfRevokedTime);
659 if(rtn) {
660 tpErrorLog("fetchNotBeforeAfter: malformed revocationDate\n");
661 }
662 else {
663 if(verifyTime != NULL) {
664 rtn = timeStringToCfDate((char *)verifyTime, (unsigned)strlen(verifyTime),
665 &cfVerifyTime);
666 }
667 else {
668 /* verify right now */
669 cfVerifyTime = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
670 }
671 if((rtn == 0) && cfVerifyTime != NULL) {
672 CFComparisonResult res = CFDateCompare(cfVerifyTime, cfRevokedTime, NULL);
673 if(res == kCFCompareLessThan) {
674 /* cfVerifyTime < cfRevokedTime; I guess this one's OK */
675 tpCrlDebug(" isCertRevoked: cert %u NOT YET REVOKED by CRL %u",
676 subjectCert.index(), index());
677 break;
678 }
679 }
680 }
681
682 /*
683 * REQUIRED TBD: parse the entry's extensions, specifically to
684 * get a reason. This will entail a bunch of new TP/cert specific
685 * CSSM_RETURNS.
686 * For now, just flag it revoked.
687 */
688 crtn = CSSMERR_TP_CERT_REVOKED;
689 subjectCert.crlReason(1);
690 tpCrlDebug(" isCertRevoked: cert %u REVOKED by CRL %u",
691 subjectCert.index(), index());
692 break;
693 }
694 }
695
696 subjectCert.freeField(&CSSMOID_X509V1SerialNumber, subjSerial);
697
698 CFReleaseNull(cfRevokedTime);
699 CFReleaseNull(cfVerifyTime);
700
701 if(crtn && !subjectCert.addStatusCode(crtn)) {
702 return CSSM_OK;
703 }
704 return crtn;
705 }
706
707 /***
708 *** TPCrlGroup class
709 ***/
710
711 /* build empty group */
712 TPCrlGroup::TPCrlGroup(
713 Allocator &alloc,
714 TPGroupOwner whoOwns) :
715 mAlloc(alloc),
716 mCrlInfo(NULL),
717 mNumCrls(0),
718 mSizeofCrlInfo(0),
719 mWhoOwns(whoOwns)
720 {
721 /* nothing for now */
722 }
723
724 /*
725 * Construct from unordered, untrusted CSSM_CRLGROUP. Resulting
726 * TPCrlInfos are more or less in the same order as the incoming
727 * CRLs, though incoming CRLs are discarded if they don't parse.
728 * No verification of any sort is performed.
729 */
730 TPCrlGroup::TPCrlGroup(
731 const CSSM_CRLGROUP *cssmCrlGroup, // optional
732 CSSM_CL_HANDLE clHand,
733 CSSM_CSP_HANDLE cspHand,
734 Allocator &alloc,
735 const char *verifyTime, // may be NULL
736 TPGroupOwner whoOwns) :
737 mAlloc(alloc),
738 mCrlInfo(NULL),
739 mNumCrls(0),
740 mSizeofCrlInfo(0),
741 mWhoOwns(whoOwns)
742 {
743 /* verify input args */
744 if((cssmCrlGroup == NULL) || (cssmCrlGroup->NumberOfCrls == 0)) {
745 return;
746 }
747 if(cspHand == CSSM_INVALID_HANDLE) {
748 CssmError::throwMe(CSSMERR_TP_INVALID_CSP_HANDLE);
749 }
750 if(clHand == CSSM_INVALID_HANDLE) {
751 CssmError::throwMe(CSSMERR_TP_INVALID_CL_HANDLE);
752 }
753 if(cssmCrlGroup->CrlGroupType != CSSM_CRLGROUP_DATA) {
754 CssmError::throwMe(CSSMERR_TP_INVALID_CERTGROUP);
755 }
756 switch(cssmCrlGroup->CrlType) {
757 case CSSM_CRL_TYPE_X_509v1:
758 case CSSM_CRL_TYPE_X_509v2:
759 break;
760 default:
761 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT);
762 }
763 switch(cssmCrlGroup->CrlEncoding) {
764 case CSSM_CRL_ENCODING_BER:
765 case CSSM_CRL_ENCODING_DER:
766 break;
767 default:
768 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT);
769 }
770
771 /*
772 * Add remaining input certs to mCrlInfo.
773 */
774 TPCrlInfo *crlInfo = NULL;
775 for(unsigned crlDex=0; crlDex<cssmCrlGroup->NumberOfCrls; crlDex++) {
776 try {
777 crlInfo = new TPCrlInfo(clHand,
778 cspHand,
779 &cssmCrlGroup->GroupCrlList.CrlList[crlDex],
780 TIC_NoCopy, // don't copy data
781 verifyTime);
782 }
783 catch (...) {
784 /* just ignore this CRL */
785 continue;
786 }
787 crlInfo->index(crlDex);
788 appendCrl(*crlInfo);
789 }
790 }
791
792 /*
793 * Deletes all TPCrlInfo's if appropriate.
794 */
795 TPCrlGroup::~TPCrlGroup()
796 {
797 if(mWhoOwns == TGO_Group) {
798 unsigned i;
799 for(i=0; i<mNumCrls; i++) {
800 delete mCrlInfo[i];
801 }
802 }
803 mAlloc.free(mCrlInfo);
804 }
805
806 /* add/remove/access TPTCrlInfo's. */
807 /*
808 * NOTE: I am aware that most folks would just use an array<> here, but
809 * gdb is so lame that it doesn't even let one examine the contents
810 * of an array<> (or just about anything else in the STL). I prefer
811 * debuggability over saving a few lines of trivial code.
812 */
813 void TPCrlGroup::appendCrl(
814 TPCrlInfo &crlInfo)
815 {
816 if(mNumCrls == mSizeofCrlInfo) {
817 if(mSizeofCrlInfo == 0) {
818 /* appending to empty array */
819 mSizeofCrlInfo = 1;
820 }
821 else {
822 mSizeofCrlInfo *= 2;
823 }
824 mCrlInfo = (TPCrlInfo **)mAlloc.realloc(mCrlInfo,
825 mSizeofCrlInfo * sizeof(TPCrlInfo *));
826 }
827 mCrlInfo[mNumCrls++] = &crlInfo;
828 }
829
830 TPCrlInfo *TPCrlGroup::crlAtIndex(
831 unsigned index)
832 {
833 if(index > (mNumCrls - 1)) {
834 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
835 }
836 return mCrlInfo[index];
837 }
838
839 TPCrlInfo &TPCrlGroup::removeCrlAtIndex(
840 unsigned index) // doesn't delete the cert, just
841 // removes it from our list
842 {
843 if(index > (mNumCrls - 1)) {
844 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
845 }
846 TPCrlInfo &rtn = *mCrlInfo[index];
847
848 /* removed requested element and compact remaining array */
849 unsigned i;
850 for(i=index; i<(mNumCrls - 1); i++) {
851 mCrlInfo[i] = mCrlInfo[i+1];
852 }
853 mNumCrls--;
854 return rtn;
855 }
856
857 void TPCrlGroup::removeCrl(
858 TPCrlInfo &crlInfo)
859 {
860 for(unsigned dex=0; dex<mNumCrls; dex++) {
861 if(mCrlInfo[dex] == &crlInfo) {
862 removeCrlAtIndex(dex);
863 return;
864 }
865 }
866 tpErrorLog("TPCrlGroup::removeCrl: CRL NOT FOUND\n");
867 assert(0);
868 }
869
870 TPCrlInfo *TPCrlGroup::firstCrl()
871 {
872 if(mNumCrls == 0) {
873 /* the caller really should not do this... */
874 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
875 }
876 else {
877 return mCrlInfo[0];
878 }
879 }
880
881 TPCrlInfo *TPCrlGroup::lastCrl()
882 {
883 if(mNumCrls == 0) {
884 /* the caller really should not do this... */
885 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
886 }
887 else {
888 return mCrlInfo[mNumCrls - 1];
889 }
890 }
891
892 /*
893 * Find a CRL whose issuer matches specified subject cert.
894 * Returned CRL has not necessarily been verified.
895 */
896 TPCrlInfo *TPCrlGroup::findCrlForCert(
897 TPCertInfo &subject)
898 {
899 for(unsigned dex=0; dex<mNumCrls; dex++) {
900 TPCrlInfo *crl = mCrlInfo[dex];
901 if(crl->hasSameIssuer(subject)) {
902 return crl;
903 }
904 }
905 return NULL;
906 }