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