]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_x509_tp/lib/TPCrlInfo.cpp
Security-57337.20.44.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 /* relativeName is a RDN sequence */
295 CSSM_X509_RDN_PTR idpName = idp->distPointName->dpn.rdn;
296 CSSM_X509_RDN_PTR certName = dp->distPointName->dpn.rdn;
297 if (idpName == NULL || certName == NULL || idpName->numberOfPairs != certName->numberOfPairs) {
298 /* They don't have the same number of attribute/value pairs; move on. */
299 continue;
300 }
301 unsigned nDex;
302 for (nDex=0; nDex<idpName->numberOfPairs; nDex++) {
303 CSSM_X509_TYPE_VALUE_PAIR_PTR iPair = idpName->AttributeTypeAndValue;
304 CSSM_X509_TYPE_VALUE_PAIR_PTR cPair = certName->AttributeTypeAndValue;
305 if (!tpCompareCssmData(&iPair->type, &cPair->type) ||
306 !tpCompareCssmData(&iPair->value, &cPair->value)) {
307 break;
308 }
309 }
310 if (nDex==idpName->numberOfPairs) {
311 /* All the pairs matched. */
312 found = CSSM_TRUE;
313 }
314 }
315 case CE_CDNT_FullName: {
316 /* fullName is a GeneralNames sequence */
317 CE_GeneralNames *idpNames = idp->distPointName->dpn.fullName;
318 CE_GeneralNames *certNames = dp->distPointName->dpn.fullName;
319 if (idpNames == NULL || certNames == NULL || idpNames->numNames != certNames->numNames) {
320 /* They don't have the same number of names; move on. */
321 continue;
322 }
323 unsigned nDex;
324 for (nDex=0; nDex<idpNames->numNames; nDex++) {
325 CE_GeneralName *idpName = &idpNames->generalName[nDex];
326 CE_GeneralName *certName = &certNames->generalName[nDex];
327 if ((idpName->nameType != certName->nameType) ||
328 (!tpCompareCssmData(&idpName->name, &certName->name))) {
329 break;
330 }
331 }
332 if (nDex==idpNames->numNames) {
333 /* All the names matched. */
334 found = CSSM_TRUE;
335 }
336 break;
337 }
338 default: {
339 forCert->freeField(&CSSMOID_CrlDistributionPoints, certDistPoints);
340 return CSSMERR_TP_UNKNOWN_FORMAT;
341 }
342 }
343 if (found) {
344 break; /* out of loop over crlDistribtionPoints in cert. */
345 }
346 }
347 forCert->freeField(&CSSMOID_CrlDistributionPoints, certDistPoints);
348 if(sameType && !found) {
349 return CSSMERR_APPLETP_IDP_FAIL;
350 }
351 } /* distPointName check */
352 } /* IDP */
353 } /* have target cert */
354 }
355
356 return CSSM_OK;
357 }
358
359 /*
360 * The heavyweight "perform full verification of this CRL" op.
361 * Must verify to an anchor cert in tpVerifyContext or via
362 * Trust Settings if so enabled.
363 * Intermediate certs can come from signerCerts or dBList.
364 */
365 CSSM_RETURN TPCrlInfo::verifyWithContext(
366 TPVerifyContext &tpVerifyContext,
367 TPCertInfo *forCert, // optional
368 bool doCrlVerify)
369 {
370 /*
371 * Step 1: this CRL must be current. Caller might have re-evaluated
372 * expired/notValidYet since our construction via calculateCurrent().
373 */
374 if(isExpired()) {
375 return CSSMERR_APPLETP_CRL_EXPIRED;
376 }
377 if(isNotValidYet()) {
378 return CSSMERR_APPLETP_CRL_NOT_VALID_YET;
379 }
380
381 /* subsequent verify state is cached */
382 switch(mVerifyState) {
383 case CVS_Good:
384 return CSSM_OK;
385 case CVS_Bad:
386 return mVerifyError;
387 case CVS_Unknown:
388 break;
389 default:
390 tpErrorLog("verifyWithContext: bad verifyState\n");
391 return CSSMERR_TP_INTERNAL_ERROR;
392 }
393
394 /*
395 * Step 2: parse & understand all critical CRL extensions.
396 */
397 CSSM_RETURN crtn;
398 bool isIndirectCrl;
399 crtn = parseExtensions(tpVerifyContext,
400 false,
401 0,
402 mX509Crl->tbsCertList.extensions,
403 forCert,
404 isIndirectCrl);
405 if(crtn) {
406 mVerifyState = CVS_Bad;
407 if(!forCert || forCert->addStatusCode(crtn)) {
408 return crtn;
409 }
410 /* else continue */
411 }
412 CSSM_X509_REVOKED_CERT_LIST_PTR revoked =
413 mX509Crl->tbsCertList.revokedCertificates;
414 if(revoked != NULL) {
415 for(uint32 dex=0; dex<revoked->numberOfRevokedCertEntries; dex++) {
416 bool dummyIsIndirect; // can't be set here
417 crtn = parseExtensions(tpVerifyContext,
418 true,
419 dex,
420 revoked->revokedCertEntry[dex].extensions,
421 forCert,
422 dummyIsIndirect);
423 if(crtn) {
424 if(!forCert || forCert->addStatusCode(crtn)) {
425 mVerifyState = CVS_Bad;
426 return crtn;
427 }
428 }
429 }
430 }
431
432 /*
433 * Step 3: obtain a fully verified cert chain which verifies this CRL.
434 */
435 CSSM_BOOL verifiedToRoot;
436 CSSM_BOOL verifiedToAnchor;
437 CSSM_BOOL verifiedViaTrustSetting;
438
439 TPCertGroup outCertGroup(tpVerifyContext.alloc,
440 TGO_Caller); // CRLs owned by inCertGroup
441
442 /* set up for disposal of TPCertInfos created by
443 * CertGroupConstructPriv */
444 TPCertGroup certsToBeFreed(tpVerifyContext.alloc, TGO_Group);
445
446 if(tpVerifyContext.signerCerts) {
447 /* start from scratch with this group */
448 tpVerifyContext.signerCerts->setAllUnused();
449 }
450 crtn = outCertGroup.buildCertGroup(
451 *this, // subject item
452 tpVerifyContext.signerCerts, // inCertGroup, optional
453 tpVerifyContext.dbList, // optional
454 tpVerifyContext.clHand,
455 tpVerifyContext.cspHand,
456 tpVerifyContext.verifyTime,
457 tpVerifyContext.numAnchorCerts,
458 tpVerifyContext.anchorCerts,
459 certsToBeFreed,
460 &tpVerifyContext.gatheredCerts,
461 CSSM_FALSE, // subjectIsInGroup
462 tpVerifyContext.actionFlags,
463 tpVerifyContext.policyOid,
464 tpVerifyContext.policyStr,
465 tpVerifyContext.policyStrLen,
466 kSecTrustSettingsKeyUseSignRevocation,
467 verifiedToRoot,
468 verifiedToAnchor,
469 verifiedViaTrustSetting);
470 /* subsequent errors to errOut: */
471
472 if(crtn) {
473 tpCrlDebug("TPCrlInfo::verifyWithContext buildCertGroup failure "
474 "index %u", index());
475 if(!forCert || forCert->addStatusCode(crtn)) {
476 goto errOut;
477 }
478 }
479 if (verifiedToRoot && (tpVerifyContext.actionFlags & CSSM_TP_ACTION_IMPLICIT_ANCHORS))
480 verifiedToAnchor = CSSM_TRUE;
481 if(!verifiedToAnchor && !verifiedViaTrustSetting) {
482 /* required */
483 if(verifiedToRoot) {
484 /* verified to root which is not an anchor */
485 tpCrlDebug("TPCrlInfo::verifyWithContext root, no anchor, "
486 "index %u", index());
487 crtn = CSSMERR_APPLETP_CRL_INVALID_ANCHOR_CERT;
488 }
489 else {
490 /* partial chain, no root, not verifiable by anchor */
491 tpCrlDebug("TPCrlInfo::verifyWithContext no root, no anchor, "
492 "index %u", index());
493 crtn = CSSMERR_APPLETP_CRL_NOT_TRUSTED;
494 }
495 if(!forCert || forCert->addStatusCode(crtn)) {
496 mVerifyState = CVS_Bad;
497 goto errOut;
498 }
499 }
500
501 /*
502 * Step 4: policy verification on the returned cert group
503 * We need to (temporarily) assert the "leaf cert is a CA" flag
504 * here.
505 */
506 outCertGroup.certAtIndex(0)->isLeaf(true);
507 crtn = tp_policyVerify(kCrlPolicy,
508 tpVerifyContext.alloc,
509 tpVerifyContext.clHand,
510 tpVerifyContext.cspHand,
511 &outCertGroup,
512 verifiedToRoot,
513 verifiedViaTrustSetting,
514 tpVerifyContext.actionFlags | CSSM_TP_ACTION_LEAF_IS_CA,
515 NULL, // sslOpts
516 NULL); // policyOpts, not currently used
517 if(crtn) {
518 tpCrlDebug(" ...verifyWithContext policy FAILURE CRL %u",
519 index());
520 if(!forCert || forCert->addStatusCode(CSSMERR_APPLETP_CRL_POLICY_FAIL)) {
521 mVerifyState = CVS_Bad;
522 goto errOut;
523 }
524 }
525
526 /*
527 * Step 5: recursively perform CRL verification on the certs
528 * gathered to verify this CRL.
529 * Only performed if this CRL is an indirect CRL or the caller
530 * explicitly told us to do this (i.e., caller is verifying a
531 * CRL, not a cert chain).
532 */
533 if(isIndirectCrl || doCrlVerify) {
534 tpCrlDebug("verifyWithContext recursing to "
535 "tpVerifyCertGroupWithCrls");
536 crtn = tpVerifyCertGroupWithCrls(tpVerifyContext,
537 outCertGroup);
538 if(crtn) {
539 tpCrlDebug(" ...verifyWithContext CRL reverify FAILURE CRL %u",
540 index());
541 if(!forCert || forCert->addStatusCode(crtn)) {
542 mVerifyState = CVS_Bad;
543 goto errOut;
544 }
545 }
546 }
547
548 tpCrlDebug(" ...verifyWithContext CRL %u SUCCESS", index());
549 mVerifyState = CVS_Good;
550 errOut:
551 /* we own these, we free the DB records */
552 certsToBeFreed.freeDbRecords();
553 return crtn;
554 }
555
556 /*
557 * Wrapper for verifyWithContext for use when evaluating a CRL
558 * "now" instead of at the time in TPVerifyContext.verifyTime.
559 * In this case, on entry, TPVerifyContext.verifyTime is the
560 * time at which a cert is being evaluated.
561 */
562 CSSM_RETURN TPCrlInfo::verifyWithContextNow(
563 TPVerifyContext &tpVerifyContext,
564 TPCertInfo *forCert, // optional
565 bool doCrlVerify)
566 {
567 CSSM_TIMESTRING ctxTime = tpVerifyContext.verifyTime;
568 CSSM_RETURN crtn = verifyWithContext(tpVerifyContext, forCert, doCrlVerify);
569 tpVerifyContext.verifyTime = ctxTime;
570 return crtn;
571 }
572
573 /*
574 * Do I have the same issuer as the specified subject cert? Returns
575 * true if so.
576 */
577 bool TPCrlInfo::hasSameIssuer(
578 const TPCertInfo &subject)
579 {
580 assert(subject.issuerName() != NULL);
581 if(tpCompareCssmData(issuerName(), subject.issuerName())) {
582 return true;
583 }
584 else {
585 return false;
586 }
587 }
588
589 /*
590 * Determine if specified cert has been revoked as of the
591 * provided time; a NULL timestring indicates "now".
592 *
593 * Assumes current CRL is verified good and that issuer names of
594 * the cert and CRL match.
595 *
596 * This duplicates similar logic in the CL, but to avoid re-parsing
597 * the subject cert (which we have parsed and cached), we just do it
598 * here.
599 *
600 * Possible errors are
601 * CSSMERR_TP_CERT_REVOKED
602 * CSSMERR_TP_CERT_SUSPENDED
603 * TBD
604 *
605 * Error status is added to subjectCert.
606 */
607 CSSM_RETURN TPCrlInfo::isCertRevoked(
608 TPCertInfo &subjectCert,
609 CSSM_TIMESTRING verifyTime)
610 {
611 assert(mVerifyState == CVS_Good);
612 CSSM_X509_TBS_CERTLIST_PTR tbs = &mX509Crl->tbsCertList;
613
614 /* trivial case - empty CRL */
615 if((tbs->revokedCertificates == NULL) ||
616 (tbs->revokedCertificates->numberOfRevokedCertEntries == 0)) {
617 tpCrlDebug(" isCertRevoked: empty CRL at index %u", index());
618 return CSSM_OK;
619 }
620
621 /* is subject cert's serial number in this CRL? */
622 CSSM_DATA_PTR subjSerial = NULL;
623 CSSM_RETURN crtn;
624 crtn = subjectCert.fetchField(&CSSMOID_X509V1SerialNumber, &subjSerial);
625 if(crtn) {
626 /* should never happen */
627 tpErrorLog("TPCrlInfo:isCertRevoked: error fetching serial number\n");
628 if(subjectCert.addStatusCode(crtn)) {
629 return crtn;
630 }
631 else {
632 /* allowed error - can't proceed; punt with success */
633 return CSSM_OK;
634 }
635 }
636 /* subsequent errors to errOut: */
637
638 uint32 numEntries = tbs->revokedCertificates->numberOfRevokedCertEntries;
639 CSSM_X509_REVOKED_CERT_ENTRY_PTR entries =
640 tbs->revokedCertificates->revokedCertEntry;
641 crtn = CSSM_OK;
642 CFDateRef cfRevokedTime = NULL;
643 CFDateRef cfVerifyTime = NULL;
644
645 for(uint32 dex=0; dex<numEntries; dex++) {
646 CSSM_X509_REVOKED_CERT_ENTRY_PTR entry = &entries[dex];
647 if(tpCompareCssmData(subjSerial, &entry->certificateSerialNumber)) {
648 /*
649 * It's in there. Compare revocation time in the CRL to
650 * our caller-specified verifyTime.
651 */
652 CSSM_X509_TIME_PTR xTime = &entry->revocationDate;
653 int rtn;
654 rtn = timeStringToCfDate((char *)xTime->time.Data, (unsigned)xTime->time.Length,
655 &cfRevokedTime);
656 if(rtn) {
657 tpErrorLog("fetchNotBeforeAfter: malformed revocationDate\n");
658 }
659 else {
660 if(verifyTime != NULL) {
661 rtn = timeStringToCfDate((char *)verifyTime, (unsigned)strlen(verifyTime),
662 &cfVerifyTime);
663 }
664 else {
665 /* verify right now */
666 cfVerifyTime = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
667 }
668 if((rtn == 0) && cfVerifyTime != NULL) {
669 CFComparisonResult res = CFDateCompare(cfVerifyTime, cfRevokedTime, NULL);
670 if(res == kCFCompareLessThan) {
671 /* cfVerifyTime < cfRevokedTime; I guess this one's OK */
672 tpCrlDebug(" isCertRevoked: cert %u NOT YET REVOKED by CRL %u",
673 subjectCert.index(), index());
674 break;
675 }
676 }
677 }
678
679 /*
680 * REQUIRED TBD: parse the entry's extensions, specifically to
681 * get a reason. This will entail a bunch of new TP/cert specific
682 * CSSM_RETURNS.
683 * For now, just flag it revoked.
684 */
685 crtn = CSSMERR_TP_CERT_REVOKED;
686 subjectCert.crlReason(1);
687 tpCrlDebug(" isCertRevoked: cert %u REVOKED by CRL %u",
688 subjectCert.index(), index());
689 break;
690 }
691 }
692
693 subjectCert.freeField(&CSSMOID_X509V1SerialNumber, subjSerial);
694 if(crtn && !subjectCert.addStatusCode(crtn)) {
695 return CSSM_OK;
696 }
697 if(cfRevokedTime) {
698 CFRelease(cfRevokedTime);
699 }
700 if(cfVerifyTime) {
701 CFRelease(cfVerifyTime);
702 }
703 return crtn;
704 }
705
706 /***
707 *** TPCrlGroup class
708 ***/
709
710 /* build empty group */
711 TPCrlGroup::TPCrlGroup(
712 Allocator &alloc,
713 TPGroupOwner whoOwns) :
714 mAlloc(alloc),
715 mCrlInfo(NULL),
716 mNumCrls(0),
717 mSizeofCrlInfo(0),
718 mWhoOwns(whoOwns)
719 {
720 /* nothing for now */
721 }
722
723 /*
724 * Construct from unordered, untrusted CSSM_CRLGROUP. Resulting
725 * TPCrlInfos are more or less in the same order as the incoming
726 * CRLs, though incoming CRLs are discarded if they don't parse.
727 * No verification of any sort is performed.
728 */
729 TPCrlGroup::TPCrlGroup(
730 const CSSM_CRLGROUP *cssmCrlGroup, // optional
731 CSSM_CL_HANDLE clHand,
732 CSSM_CSP_HANDLE cspHand,
733 Allocator &alloc,
734 const char *verifyTime, // may be NULL
735 TPGroupOwner whoOwns) :
736 mAlloc(alloc),
737 mCrlInfo(NULL),
738 mNumCrls(0),
739 mSizeofCrlInfo(0),
740 mWhoOwns(whoOwns)
741 {
742 /* verify input args */
743 if((cssmCrlGroup == NULL) || (cssmCrlGroup->NumberOfCrls == 0)) {
744 return;
745 }
746 if(cspHand == CSSM_INVALID_HANDLE) {
747 CssmError::throwMe(CSSMERR_TP_INVALID_CSP_HANDLE);
748 }
749 if(clHand == CSSM_INVALID_HANDLE) {
750 CssmError::throwMe(CSSMERR_TP_INVALID_CL_HANDLE);
751 }
752 if(cssmCrlGroup->CrlGroupType != CSSM_CRLGROUP_DATA) {
753 CssmError::throwMe(CSSMERR_TP_INVALID_CERTGROUP);
754 }
755 switch(cssmCrlGroup->CrlType) {
756 case CSSM_CRL_TYPE_X_509v1:
757 case CSSM_CRL_TYPE_X_509v2:
758 break;
759 default:
760 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT);
761 }
762 switch(cssmCrlGroup->CrlEncoding) {
763 case CSSM_CRL_ENCODING_BER:
764 case CSSM_CRL_ENCODING_DER:
765 break;
766 default:
767 CssmError::throwMe(CSSMERR_TP_UNKNOWN_FORMAT);
768 }
769
770 /*
771 * Add remaining input certs to mCrlInfo.
772 */
773 TPCrlInfo *crlInfo = NULL;
774 for(unsigned crlDex=0; crlDex<cssmCrlGroup->NumberOfCrls; crlDex++) {
775 try {
776 crlInfo = new TPCrlInfo(clHand,
777 cspHand,
778 &cssmCrlGroup->GroupCrlList.CrlList[crlDex],
779 TIC_NoCopy, // don't copy data
780 verifyTime);
781 }
782 catch (...) {
783 /* just ignore this CRL */
784 continue;
785 }
786 crlInfo->index(crlDex);
787 appendCrl(*crlInfo);
788 }
789 }
790
791 /*
792 * Deletes all TPCrlInfo's if appropriate.
793 */
794 TPCrlGroup::~TPCrlGroup()
795 {
796 if(mWhoOwns == TGO_Group) {
797 unsigned i;
798 for(i=0; i<mNumCrls; i++) {
799 delete mCrlInfo[i];
800 }
801 }
802 mAlloc.free(mCrlInfo);
803 }
804
805 /* add/remove/access TPTCrlInfo's. */
806 /*
807 * NOTE: I am aware that most folks would just use an array<> here, but
808 * gdb is so lame that it doesn't even let one examine the contents
809 * of an array<> (or just about anything else in the STL). I prefer
810 * debuggability over saving a few lines of trivial code.
811 */
812 void TPCrlGroup::appendCrl(
813 TPCrlInfo &crlInfo)
814 {
815 if(mNumCrls == mSizeofCrlInfo) {
816 if(mSizeofCrlInfo == 0) {
817 /* appending to empty array */
818 mSizeofCrlInfo = 1;
819 }
820 else {
821 mSizeofCrlInfo *= 2;
822 }
823 mCrlInfo = (TPCrlInfo **)mAlloc.realloc(mCrlInfo,
824 mSizeofCrlInfo * sizeof(TPCrlInfo *));
825 }
826 mCrlInfo[mNumCrls++] = &crlInfo;
827 }
828
829 TPCrlInfo *TPCrlGroup::crlAtIndex(
830 unsigned index)
831 {
832 if(index > (mNumCrls - 1)) {
833 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
834 }
835 return mCrlInfo[index];
836 }
837
838 TPCrlInfo &TPCrlGroup::removeCrlAtIndex(
839 unsigned index) // doesn't delete the cert, just
840 // removes it from our list
841 {
842 if(index > (mNumCrls - 1)) {
843 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
844 }
845 TPCrlInfo &rtn = *mCrlInfo[index];
846
847 /* removed requested element and compact remaining array */
848 unsigned i;
849 for(i=index; i<(mNumCrls - 1); i++) {
850 mCrlInfo[i] = mCrlInfo[i+1];
851 }
852 mNumCrls--;
853 return rtn;
854 }
855
856 void TPCrlGroup::removeCrl(
857 TPCrlInfo &crlInfo)
858 {
859 for(unsigned dex=0; dex<mNumCrls; dex++) {
860 if(mCrlInfo[dex] == &crlInfo) {
861 removeCrlAtIndex(dex);
862 return;
863 }
864 }
865 tpErrorLog("TPCrlGroup::removeCrl: CRL NOT FOUND\n");
866 assert(0);
867 }
868
869 TPCrlInfo *TPCrlGroup::firstCrl()
870 {
871 if(mNumCrls == 0) {
872 /* the caller really should not do this... */
873 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
874 }
875 else {
876 return mCrlInfo[0];
877 }
878 }
879
880 TPCrlInfo *TPCrlGroup::lastCrl()
881 {
882 if(mNumCrls == 0) {
883 /* the caller really should not do this... */
884 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
885 }
886 else {
887 return mCrlInfo[mNumCrls - 1];
888 }
889 }
890
891 /*
892 * Find a CRL whose issuer matches specified subject cert.
893 * Returned CRL has not necessarily been verified.
894 */
895 TPCrlInfo *TPCrlGroup::findCrlForCert(
896 TPCertInfo &subject)
897 {
898 for(unsigned dex=0; dex<mNumCrls; dex++) {
899 TPCrlInfo *crl = mCrlInfo[dex];
900 if(crl->hasSameIssuer(subject)) {
901 return crl;
902 }
903 }
904 return NULL;
905 }