]> git.saurik.com Git - apple/security.git/blob - Security/libsecurity_apple_x509_tp/lib/tpCrlVerify.cpp
Security-57031.40.6.tar.gz
[apple/security.git] / Security / libsecurity_apple_x509_tp / lib / tpCrlVerify.cpp
1 /*
2 * Copyright (c) 2002-2012 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 * tpCrlVerify.cpp - routines to verify CRLs and to verify certs against CRLs.
21 */
22
23 #include "tpCrlVerify.h"
24 #include "TPCertInfo.h"
25 #include "TPCrlInfo.h"
26 #include "tpOcspVerify.h"
27 #include "tpdebugging.h"
28 #include "TPNetwork.h"
29 #include "TPDatabase.h"
30 #include <CommonCrypto/CommonDigest.h>
31 #include <Security/oidscert.h>
32 #include <security_ocspd/ocspdClient.h>
33 #include <security_utilities/globalizer.h>
34 #include <security_utilities/threading.h>
35 #include <security_cdsa_utilities/cssmerrors.h>
36 #include <sys/stat.h>
37
38 /* general purpose, switch to policy-specific code based on TPVerifyContext.policy */
39 CSSM_RETURN tpRevocationPolicyVerify(
40 TPVerifyContext &tpVerifyContext,
41 TPCertGroup &certGroup)
42 {
43 switch(tpVerifyContext.policy) {
44 case kRevokeNone:
45 return CSSM_OK;
46 case kRevokeCrlBasic:
47 return tpVerifyCertGroupWithCrls(tpVerifyContext, certGroup);
48 case kRevokeOcsp:
49 return tpVerifyCertGroupWithOCSP(tpVerifyContext, certGroup);
50 default:
51 assert(0);
52 return CSSMERR_TP_INTERNAL_ERROR;
53 }
54 }
55
56 /*
57 * For now, a process-wide memory resident CRL cache.
58 * We are responsible for deleting the CRLs which get added to this
59 * cache. Currently the only time we add a CRL to this cache is
60 * when we fetch one from the net. We ref count CRLs in this cache
61 * to allow multi-threaded access.
62 * Entries do not persist past the tpVerifyCertGroupWithCrls() in
63 * which they were created unless another thread in the same
64 * process snags a refcount (also from tpVerifyCertGroupWithCrls()).
65 * I.e. when cert verification is complete the cache will be empty.
66 * This is a change from Tiger and previous. CRLs get pretty big,
67 * up to a megabyte or so, and it's just not worth it to keep those
68 * around in memory. (OCSP responses, which are much smaller than
69 * CRLs, are indeed cached in memory. See tpOcspCache.cpp.)
70 */
71 class TPCRLCache : private TPCrlGroup
72 {
73 public:
74 TPCRLCache();
75 ~TPCRLCache() { }
76 TPCrlInfo *search(
77 TPCertInfo &cert,
78 TPVerifyContext &vfyCtx);
79 void add(
80 TPCrlInfo &crl);
81 void remove(
82 TPCrlInfo &crl);
83 void release(
84 TPCrlInfo &crl);
85
86 private:
87 /* Protects ref count of all members of the cache */
88 Mutex mLock;
89 };
90
91 TPCRLCache::TPCRLCache()
92 : TPCrlGroup(Allocator::standard(), TGO_Group)
93 {
94
95 }
96
97 TPCrlInfo *TPCRLCache::search(
98 TPCertInfo &cert,
99 TPVerifyContext &vfyCtx)
100 {
101 StLock<Mutex> _(mLock);
102 TPCrlInfo *crl = findCrlForCert(cert);
103 if(crl) {
104 /* reevaluate validity */
105 crl->calculateCurrent(vfyCtx.verifyTime);
106 crl->mRefCount++;
107 tpCrlDebug("TPCRLCache hit");
108 }
109 else {
110 tpCrlDebug("TPCRLCache miss");
111 }
112 return crl;
113 }
114
115 /* bumps ref count - caller is going to be using the CRL */
116 void TPCRLCache::add(
117 TPCrlInfo &crl)
118 {
119 StLock<Mutex> _(mLock);
120 tpCrlDebug("TPCRLCache add");
121 crl.mRefCount++;
122 appendCrl(crl);
123 }
124
125 /* delete and remove from cache if refCount zero */
126 void TPCRLCache::release(
127 TPCrlInfo &crl)
128 {
129 StLock<Mutex> _(mLock);
130 assert(crl.mRefCount > 0);
131 crl.mRefCount--;
132 if(crl.mRefCount == 0) {
133 tpCrlDebug("TPCRLCache release; deleting");
134 removeCrl(crl);
135 delete &crl;
136 }
137 else {
138 tpCrlDebug("TPCRLCache release; in use");
139 }
140 }
141
142 static ModuleNexus<TPCRLCache> tpGlobalCrlCache;
143
144 /*
145 * Find CRL for specified cert. Only returns a fully verified CRL.
146 * Cert-specific errors such as CSSMERR_APPLETP_CRL_NOT_FOUND will be added
147 * to cert's return codes.
148 */
149 static CSSM_RETURN tpFindCrlForCert(
150 TPCertInfo &subject,
151 TPCrlInfo *&foundCrl, // RETURNED
152 TPVerifyContext &vfyCtx)
153 {
154
155 tpCrlDebug("tpFindCrlForCert top");
156 TPCrlInfo *crl = NULL;
157 foundCrl = NULL;
158 CSSM_APPLE_TP_CRL_OPT_FLAGS crlOptFlags = 0;
159
160 if(vfyCtx.crlOpts) {
161 crlOptFlags = vfyCtx.crlOpts->CrlFlags;
162 }
163
164 /* Search inputCrls for a CRL for subject cert */
165 if(vfyCtx.inputCrls != NULL) {
166 crl = vfyCtx.inputCrls->findCrlForCert(subject);
167 if(crl && (crl->verifyWithContextNow(vfyCtx, &subject) == CSSM_OK)) {
168 foundCrl = crl;
169 crl->mFromWhere = CFW_InGroup;
170 tpCrlDebug(" ...CRL found in CrlGroup");
171 return CSSM_OK;
172 }
173 }
174
175 /* local process-wide cache */
176 crl = tpGlobalCrlCache().search(subject, vfyCtx);
177 if(crl) {
178 tpCrlDebug("...tpFindCrlForCert found CRL in cache, calling verifyWithContext");
179 if(crl->verifyWithContextNow(vfyCtx, &subject) == CSSM_OK) {
180 foundCrl = crl;
181 crl->mFromWhere = CFW_LocalCache;
182 tpCrlDebug(" ...CRL found in local cache");
183 return CSSM_OK;
184 }
185 else {
186 tpGlobalCrlCache().release(*crl);
187 }
188 }
189
190 /*
191 * Try DL/DB.
192 * Note tpDbFindIssuerCrl() returns a verified CRL.
193 */
194 crl = tpDbFindIssuerCrl(vfyCtx, *subject.issuerName(), subject);
195 if(crl) {
196 foundCrl = crl;
197 crl->mFromWhere = CFW_DlDb;
198 tpCrlDebug(" ...CRL found in DlDb");
199 return CSSM_OK;
200 }
201
202 /* Last resort: try net if enabled */
203 CSSM_RETURN crtn = CSSMERR_APPLETP_CRL_NOT_FOUND;
204 crl = NULL;
205 if(crlOptFlags & CSSM_TP_ACTION_FETCH_CRL_FROM_NET) {
206 crtn = tpFetchCrlFromNet(subject, vfyCtx, crl);
207 }
208
209 if(crtn) {
210 tpCrlDebug(" ...tpFindCrlForCert: CRL not found");
211 if(subject.addStatusCode(crtn)) {
212 return crtn;
213 }
214 else {
215 return CSSM_OK;
216 }
217 }
218
219 /* got one from net - add to global cache */
220 assert(crl != NULL);
221 tpGlobalCrlCache().add(*crl);
222 crl->mFromWhere = CFW_Net;
223 tpCrlDebug(" ...CRL found from net");
224
225 foundCrl = crl;
226 return CSSM_OK;
227 }
228
229 /*
230 * Dispose of a CRL obtained from tpFindCrlForCert().
231 */
232 static void tpDisposeCrl(
233 TPCrlInfo &crl,
234 TPVerifyContext &vfyCtx)
235 {
236 switch(crl.mFromWhere) {
237 case CFW_Nowhere:
238 default:
239 assert(0);
240 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
241 case CFW_InGroup:
242 /* nothing to do, handled by TPCrlGroup */
243 return;
244 case CFW_DlDb:
245 /* cooked up specially for this call */
246 delete &crl;
247 return;
248 case CFW_LocalCache: // cache hit
249 case CFW_Net: // fetched from net & added to cache
250 tpGlobalCrlCache().release(crl);
251 return;
252 /* probably others */
253 }
254 }
255
256 /*
257 * Does this cert have a CrlDistributionPoints extension? We don't parse it, we
258 * just tell the caller whether or not it has one.
259 */
260 static bool tpCertHasCrlDistPt(
261 TPCertInfo &cert)
262 {
263 CSSM_DATA_PTR fieldValue;
264 CSSM_RETURN crtn = cert.fetchField(&CSSMOID_CrlDistributionPoints, &fieldValue);
265 if(crtn) {
266 return false;
267 }
268 else {
269 cert.freeField(&CSSMOID_CrlDistributionPoints, fieldValue);
270 return true;
271 }
272 }
273
274 /*
275 * Get current CRL status for a certificate and its issuers.
276 *
277 * Possible results:
278 *
279 * CSSM_OK (we have a valid CRL; certificate is not revoked)
280 * CSSMERR_TP_CERT_REVOKED (we have a valid CRL; certificate is revoked)
281 * CSSMERR_APPLETP_NETWORK_FAILURE (CRL not available, download in progress)
282 * CSSMERR_APPLETP_CRL_NOT_FOUND (CRL not available, and not being fetched)
283 * CSSMERR_TP_INTERNAL_ERROR (unexpected error)
284 *
285 * Note that ocspdCRLStatus does NOT wait for the CRL to be downloaded before
286 * returning, nor does it initiate a CRL download.
287 */
288 static
289 CSSM_RETURN tpGetCrlStatusForCert(
290 TPCertInfo &subject,
291 const CSSM_DATA &issuers)
292 {
293 CSSM_DATA *serialNumber=NULL;
294 CSSM_RETURN crtn = subject.fetchField(&CSSMOID_X509V1SerialNumber, &serialNumber);
295 if(crtn || !serialNumber) {
296 return CSSMERR_TP_INTERNAL_ERROR;
297 }
298 crtn = ocspdCRLStatus(*serialNumber, issuers, subject.issuerName(), NULL);
299 subject.freeField(&CSSMOID_X509V1SerialNumber, serialNumber);
300 return crtn;
301 }
302
303 /*
304 * Perform CRL verification on a cert group.
305 * The cert group has already passed basic issuer/subject and signature
306 * verification. The status of the incoming CRLs is completely unknown.
307 *
308 * FIXME - No mechanism to get CRLs from net with non-NULL verifyTime.
309 * How are we supposed to get the CRL which was valid at a specified
310 * time in the past?
311 */
312 CSSM_RETURN tpVerifyCertGroupWithCrls(
313 TPVerifyContext &vfyCtx,
314 TPCertGroup &certGroup) // to be verified
315 {
316 CSSM_RETURN crtn;
317 CSSM_RETURN ourRtn = CSSM_OK;
318
319 assert(vfyCtx.clHand != 0);
320 assert(vfyCtx.policy == kRevokeCrlBasic);
321 tpCrlDebug("tpVerifyCertGroupWithCrls numCerts %u", certGroup.numCerts());
322 CSSM_DATA issuers = { 0, NULL };
323 CSSM_APPLE_TP_CRL_OPT_FLAGS optFlags = 0;
324 if(vfyCtx.crlOpts != NULL) {
325 optFlags = vfyCtx.crlOpts->CrlFlags;
326 }
327
328 /* found & verified CRLs we need to release */
329 TPCrlGroup foundCrls(vfyCtx.alloc, TGO_Caller);
330
331 try {
332
333 unsigned certDex;
334 TPCrlInfo *crl = NULL;
335
336 /* get issuers as PEM-encoded data blob; we need to release */
337 certGroup.encodeIssuers(issuers);
338
339 /* main loop, verify each cert */
340 for(certDex=0; certDex<certGroup.numCerts(); certDex++) {
341 TPCertInfo *cert = certGroup.certAtIndex(certDex);
342
343 tpCrlDebug("...verifying %s cert %u",
344 cert->isAnchor() ? "anchor " : "", cert->index());
345 if(cert->isSelfSigned() || cert->trustSettingsFound()) {
346 /* CRL meaningless for a root or trusted cert */
347 continue;
348 }
349 if(cert->revokeCheckComplete()) {
350 /* Another revocation policy claimed that this cert is good to go */
351 tpCrlDebug(" ...cert at index %u revokeCheckComplete; skipping",
352 cert->index());
353 continue;
354 }
355 crl = NULL;
356 do {
357 /* first, see if we have CRL status available for this cert */
358 crtn = tpGetCrlStatusForCert(*cert, issuers);
359 tpCrlDebug("...tpGetCrlStatusForCert: %u", crtn);
360 if(crtn == CSSM_OK) {
361 tpCrlDebug("tpVerifyCertGroupWithCrls: cert %u verified by local .crl\n",
362 cert->index());
363 cert->revokeCheckGood(true);
364 if(optFlags & CSSM_TP_ACTION_CRL_SUFFICIENT) {
365 /* no more revocation checking necessary for this cert */
366 cert->revokeCheckComplete(true);
367 }
368 break;
369 }
370 if(crtn == CSSMERR_TP_CERT_REVOKED) {
371 tpCrlDebug("tpVerifyCertGroupWithCrls: cert %u revoked in local .crl\n",
372 cert->index());
373 cert->addStatusCode(crtn);
374 break;
375 }
376 if(crtn == CSSMERR_APPLETP_NETWORK_FAILURE) {
377 /* crl is being fetched from net, but we don't have it yet */
378 if((optFlags & CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT) &&
379 tpCertHasCrlDistPt(*cert)) {
380 /* crl is required; we don't have it yet, so we fail */
381 tpCrlDebug(" ...cert %u: REQUIRE_CRL_IF_PRESENT abort",
382 cert->index());
383 break;
384 }
385 /* "Best Attempt" case, so give the cert a pass for now */
386 tpCrlDebug(" ...cert %u: no CRL; tolerating", cert->index());
387 crtn = CSSM_OK;
388 break;
389 }
390 /* all other CRL status results: try to fetch the CRL */
391
392 /* find a CRL for this cert by hook or crook */
393 crtn = tpFindCrlForCert(*cert, crl, vfyCtx);
394 if(crtn) {
395 /* tpFindCrlForCert may have simply caused ocspd to start
396 * downloading a CRL asynchronously; depending on the speed
397 * of the network and the CRL size, this may return 0 bytes
398 * of data with a CSSMERR_APPLETP_NETWORK_FAILURE result.
399 * We won't know the actual revocation result until the
400 * next time we call tpGetCrlStatusForCert after the full
401 * CRL has been downloaded successfully.
402 */
403 if(optFlags & CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT) {
404 tpCrlDebug(" ...cert %u: REQUIRE_CRL_PER_CERT abort",
405 cert->index());
406 break;
407 }
408 if((optFlags & CSSM_TP_ACTION_REQUIRE_CRL_IF_PRESENT) &&
409 tpCertHasCrlDistPt(*cert)) {
410 tpCrlDebug(" ...cert %u: REQUIRE_CRL_IF_PRESENT abort",
411 cert->index());
412 break;
413 }
414 /*
415 * This is the only place where "Best Attempt" tolerates an error
416 */
417 tpCrlDebug(" ...cert %u: no CRL; tolerating", cert->index());
418 crtn = CSSM_OK;
419 assert(crl == NULL);
420 break;
421 }
422
423 /* Keep track; we'll release all when done. */
424 assert(crl != NULL);
425 foundCrls.appendCrl(*crl);
426
427 /* revoked? */
428 crtn = crl->isCertRevoked(*cert, vfyCtx.verifyTime);
429 if(crtn) {
430 break;
431 }
432 tpCrlDebug(" ...cert %u VERIFIED by CRL", cert->index());
433 cert->revokeCheckGood(true);
434 if(optFlags & CSSM_TP_ACTION_CRL_SUFFICIENT) {
435 /* no more revocation checking necessary for this cert */
436 cert->revokeCheckComplete(true);
437 }
438 } while(0);
439
440 /* done processing one cert */
441 if(crtn) {
442 tpCrlDebug(" ...cert at index %u FAILED crl vfy",
443 cert->index());
444 if(ourRtn == CSSM_OK) {
445 ourRtn = crtn;
446 }
447 /* continue on to next cert */
448 } /* error on one cert */
449 } /* for each cert */
450 }
451 catch(const CssmError &cerr) {
452 if(ourRtn == CSSM_OK) {
453 ourRtn = cerr.error;
454 }
455 }
456 /* other exceptions fatal */
457
458 /* release all found CRLs */
459 for(unsigned dex=0; dex<foundCrls.numCrls(); dex++) {
460 TPCrlInfo *crl = foundCrls.crlAtIndex(dex);
461 assert(crl != NULL);
462 tpDisposeCrl(*crl, vfyCtx);
463 }
464 /* release issuers */
465 if(issuers.Data) {
466 free(issuers.Data);
467 }
468 return ourRtn;
469 }
470