]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
5c19dc3a A |
2 | * Copyright (c) 2002,2011-2012,2014-2015 Apple Inc. All Rights Reserved. |
3 | * | |
b1ab9ed8 A |
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. | |
5c19dc3a | 9 | * |
b1ab9ed8 A |
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 | * | |
b1ab9ed8 A |
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> | |
866f8763 | 35 | #include <utilities/SecCFRelease.h> |
b1ab9ed8 A |
36 | #include <string.h> /* for memcmp */ |
37 | #include <Security/cssmapple.h> | |
38 | ||
39 | /* | |
5c19dc3a A |
40 | * Replacement for CSSM_CL_CrlGetFirstCachedFieldValue for use with |
41 | * TPCrlItemInfo's generic getFirstCachedField mechanism. | |
b1ab9ed8 A |
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 | ||
5c19dc3a | 74 | /* |
b1ab9ed8 A |
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 | |
5c19dc3a A |
86 | |
87 | : TPClItemInfo(clHand, cspHand, tpCrlClCalls, crlData, | |
b1ab9ed8 A |
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; | |
5c19dc3a | 100 | |
b1ab9ed8 A |
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 | } | |
5c19dc3a | 116 | |
b1ab9ed8 A |
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 | */ | |
5c19dc3a | 144 | static const CSSM_OID *const TPGoodCrlExtens[] = |
b1ab9ed8 A |
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 | } | |
5c19dc3a | 192 | |
b1ab9ed8 | 193 | /* Specific extension handling. */ |
5c19dc3a | 194 | if(tpCompareOids(&exten->extnId, |
b1ab9ed8 A |
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); | |
5c19dc3a | 200 | CE_IssuingDistributionPoint *idp = |
b1ab9ed8 A |
201 | (CE_IssuingDistributionPoint *) |
202 | exten->value.parsedValue; | |
5c19dc3a | 203 | |
b1ab9ed8 A |
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. */ | |
5c19dc3a | 213 | |
b1ab9ed8 A |
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 | } | |
822b670c A |
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 | } | |
6b200bc3 | 295 | #if 0 |
822b670c A |
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 | } | |
6b200bc3 | 316 | #endif |
822b670c A |
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 */ | |
b1ab9ed8 A |
355 | } /* IDP */ |
356 | } /* have target cert */ | |
357 | } | |
5c19dc3a | 358 | |
b1ab9ed8 A |
359 | return CSSM_OK; |
360 | } | |
361 | ||
5c19dc3a | 362 | /* |
b1ab9ed8 A |
363 | * The heavyweight "perform full verification of this CRL" op. |
364 | * Must verify to an anchor cert in tpVerifyContext or via | |
5c19dc3a A |
365 | * Trust Settings if so enabled. |
366 | * Intermediate certs can come from signerCerts or dBList. | |
b1ab9ed8 A |
367 | */ |
368 | CSSM_RETURN TPCrlInfo::verifyWithContext( | |
369 | TPVerifyContext &tpVerifyContext, | |
5c19dc3a A |
370 | TPCertInfo *forCert, // optional |
371 | bool doCrlVerify) | |
b1ab9ed8 A |
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 | } | |
5c19dc3a | 396 | |
b1ab9ed8 | 397 | /* |
5c19dc3a | 398 | * Step 2: parse & understand all critical CRL extensions. |
b1ab9ed8 A |
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 | } | |
5c19dc3a | 415 | CSSM_X509_REVOKED_CERT_LIST_PTR revoked = |
b1ab9ed8 A |
416 | mX509Crl->tbsCertList.revokedCertificates; |
417 | if(revoked != NULL) { | |
418 | for(uint32 dex=0; dex<revoked->numberOfRevokedCertEntries; dex++) { | |
5c19dc3a | 419 | bool dummyIsIndirect; // can't be set here |
b1ab9ed8 A |
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 | } | |
5c19dc3a | 434 | |
b1ab9ed8 A |
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; | |
5c19dc3a A |
441 | |
442 | TPCertGroup outCertGroup(tpVerifyContext.alloc, | |
b1ab9ed8 A |
443 | TGO_Caller); // CRLs owned by inCertGroup |
444 | ||
5c19dc3a | 445 | /* set up for disposal of TPCertInfos created by |
b1ab9ed8 A |
446 | * CertGroupConstructPriv */ |
447 | TPCertGroup certsToBeFreed(tpVerifyContext.alloc, TGO_Group); | |
5c19dc3a | 448 | |
b1ab9ed8 A |
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, | |
5c19dc3a | 470 | verifiedToRoot, |
b1ab9ed8 A |
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 | } | |
5c19dc3a A |
503 | |
504 | /* | |
505 | * Step 4: policy verification on the returned cert group | |
b1ab9ed8 | 506 | * We need to (temporarily) assert the "leaf cert is a CA" flag |
5c19dc3a | 507 | * here. |
b1ab9ed8 A |
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 | |
5c19dc3a | 519 | NULL); // policyOpts, not currently used |
b1ab9ed8 A |
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 | } | |
5c19dc3a | 528 | |
b1ab9ed8 | 529 | /* |
5c19dc3a A |
530 | * Step 5: recursively perform CRL verification on the certs |
531 | * gathered to verify this CRL. | |
b1ab9ed8 A |
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. | |
5c19dc3a | 562 | * In this case, on entry, TPVerifyContext.verifyTime is the |
b1ab9ed8 A |
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 | /* | |
5c19dc3a | 577 | * Do I have the same issuer as the specified subject cert? Returns |
b1ab9ed8 A |
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 | |
5c19dc3a | 594 | * provided time; a NULL timestring indicates "now". |
b1ab9ed8 | 595 | * |
5c19dc3a | 596 | * Assumes current CRL is verified good and that issuer names of |
b1ab9ed8 A |
597 | * the cert and CRL match. |
598 | * | |
599 | * This duplicates similar logic in the CL, but to avoid re-parsing | |
5c19dc3a | 600 | * the subject cert (which we have parsed and cached), we just do it |
b1ab9ed8 A |
601 | * here. |
602 | * | |
5c19dc3a | 603 | * Possible errors are |
b1ab9ed8 A |
604 | * CSSMERR_TP_CERT_REVOKED |
605 | * CSSMERR_TP_CERT_SUSPENDED | |
606 | * TBD | |
607 | * | |
5c19dc3a | 608 | * Error status is added to subjectCert. |
b1ab9ed8 A |
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; | |
5c19dc3a | 616 | |
b1ab9ed8 A |
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 | } | |
5c19dc3a | 623 | |
b1ab9ed8 A |
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: */ | |
5c19dc3a | 640 | |
b1ab9ed8 | 641 | uint32 numEntries = tbs->revokedCertificates->numberOfRevokedCertEntries; |
5c19dc3a | 642 | CSSM_X509_REVOKED_CERT_ENTRY_PTR entries = |
b1ab9ed8 A |
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)) { | |
5c19dc3a A |
651 | /* |
652 | * It's in there. Compare revocation time in the CRL to | |
b1ab9ed8 A |
653 | * our caller-specified verifyTime. |
654 | */ | |
655 | CSSM_X509_TIME_PTR xTime = &entry->revocationDate; | |
656 | int rtn; | |
427c49bc | 657 | rtn = timeStringToCfDate((char *)xTime->time.Data, (unsigned)xTime->time.Length, |
b1ab9ed8 A |
658 | &cfRevokedTime); |
659 | if(rtn) { | |
660 | tpErrorLog("fetchNotBeforeAfter: malformed revocationDate\n"); | |
661 | } | |
662 | else { | |
663 | if(verifyTime != NULL) { | |
427c49bc | 664 | rtn = timeStringToCfDate((char *)verifyTime, (unsigned)strlen(verifyTime), |
b1ab9ed8 A |
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 */ | |
5c19dc3a | 675 | tpCrlDebug(" isCertRevoked: cert %u NOT YET REVOKED by CRL %u", |
b1ab9ed8 A |
676 | subjectCert.index(), index()); |
677 | break; | |
678 | } | |
679 | } | |
680 | } | |
681 | ||
682 | /* | |
5c19dc3a | 683 | * REQUIRED TBD: parse the entry's extensions, specifically to |
b1ab9ed8 A |
684 | * get a reason. This will entail a bunch of new TP/cert specific |
685 | * CSSM_RETURNS. | |
5c19dc3a | 686 | * For now, just flag it revoked. |
b1ab9ed8 A |
687 | */ |
688 | crtn = CSSMERR_TP_CERT_REVOKED; | |
5c19dc3a A |
689 | subjectCert.crlReason(1); |
690 | tpCrlDebug(" isCertRevoked: cert %u REVOKED by CRL %u", | |
b1ab9ed8 A |
691 | subjectCert.index(), index()); |
692 | break; | |
693 | } | |
694 | } | |
5c19dc3a | 695 | |
b1ab9ed8 | 696 | subjectCert.freeField(&CSSMOID_X509V1SerialNumber, subjSerial); |
866f8763 A |
697 | |
698 | CFReleaseNull(cfRevokedTime); | |
699 | CFReleaseNull(cfVerifyTime); | |
700 | ||
b1ab9ed8 A |
701 | if(crtn && !subjectCert.addStatusCode(crtn)) { |
702 | return CSSM_OK; | |
703 | } | |
b1ab9ed8 A |
704 | return crtn; |
705 | } | |
706 | ||
707 | /*** | |
708 | *** TPCrlGroup class | |
709 | ***/ | |
5c19dc3a | 710 | |
b1ab9ed8 A |
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 | } | |
5c19dc3a | 723 | |
b1ab9ed8 A |
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. | |
5c19dc3a | 728 | * No verification of any sort is performed. |
b1ab9ed8 A |
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 | } | |
5c19dc3a A |
770 | |
771 | /* | |
b1ab9ed8 A |
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], | |
5c19dc3a | 780 | TIC_NoCopy, // don't copy data |
b1ab9ed8 A |
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 | } | |
5c19dc3a | 824 | mCrlInfo = (TPCrlInfo **)mAlloc.realloc(mCrlInfo, |
b1ab9ed8 A |
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( | |
5c19dc3a | 840 | unsigned index) // doesn't delete the cert, just |
b1ab9ed8 A |
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]; | |
5c19dc3a | 847 | |
b1ab9ed8 A |
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 | ||
5c19dc3a | 892 | /* |
b1ab9ed8 A |
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 | } |