]>
Commit | Line | Data |
---|---|---|
df0e469f A |
1 | /* |
2 | * Copyright (c) 2003 Apple Computer, 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 | |
7 | * obtain a copy of the License at http://www.apple.com/publicsource and | |
8 | * read it before 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 | |
12 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
13 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
14 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
15 | * Please see the License for the specific language governing rights and | |
16 | * limitations under the License. | |
17 | */ | |
18 | ||
19 | /* | |
20 | * Examine the CRLs in the system CRL cache, looking for expired CRLs | |
21 | * for which we don't have current valid entries. Perform net fetch for | |
22 | * all such entries to get up-to-date entries. Purge entries older | |
23 | * than specified date (i.e., "stale" CRLs). | |
24 | * | |
25 | * Terminology used here: | |
26 | * | |
27 | * 'nowTime' is the absolute current time. | |
28 | * 'updateTime' is the time at which we evaluate a CRL's NextUpdate | |
29 | * attribute to determine whether a CRL has expired. This is | |
30 | * generally subsequent to nowTime. | |
31 | * 'expired' means that a CRL's NextUpdate time has passed, relative | |
32 | * to updateTime, and that we need to fetch a new CRL to replace | |
33 | * the expired CRL. | |
34 | * 'expireOverlap' is (nowTime - updateTime) in seconds. It's the | |
35 | * distance into the future at which we evaluate a CRL's expiration | |
36 | * status. | |
37 | * 'stale' means that a CRL is so old that it should be deleted from | |
38 | * the cache. | |
39 | * 'staleTime' is maximum age (relative to nowTime) that a CRL can | |
40 | * achieve in cache before being deemed stale. StaleTime is always | |
41 | * greater than expireOverlap (i.e., if a CRL is stale, it MUST be | |
42 | * expired, but a CRL can be expired without being stale). | |
43 | * | |
44 | * CRLs are only deleted from cache if they are stale; multiple | |
45 | * CRLs from one CA may exist in cache at a given time but (generally) | |
46 | * only one of them is not expired. | |
47 | * | |
48 | * expireOverlap and staleTime have defaults which can be overridden | |
49 | * via command line arguments. | |
50 | */ | |
51 | ||
52 | #include <stdlib.h> | |
53 | #include <stdio.h> | |
54 | #include <sys/types.h> | |
55 | #include <sys/stat.h> | |
56 | #include <CdsaUtils/cuCdsaUtils.h> | |
57 | #include <CdsaUtils/cuTimeStr.h> | |
58 | #include <CdsaUtils/cuDbUtils.h> | |
59 | #include <CdsaUtils/cuFileIo.h> | |
60 | #include <strings.h> | |
61 | #include "ldapFetch.h" | |
62 | #include <Security/keychainacl.h> | |
63 | #include <Security/cssmacl.h> | |
64 | #include <Security/aclclient.h> | |
65 | #include <Security/cssmdata.h> | |
66 | #include <Security/SecTrust.h> | |
67 | ||
68 | #define DEFAULT_STALE_DAYS 10 | |
69 | #define DEFAULT_EXPIRE_OVERLAP_SECONDS 3600 | |
70 | ||
71 | #define SECONDS_PER_DAY (60 * 60 * 24) | |
72 | ||
73 | #define CRL_CACHE_DB "/var/db/crls/crlcache.db" | |
74 | #define X509_CERT_DB "/System/Library/Keychains/X509Certificates" | |
75 | ||
76 | #ifdef NDEBUG | |
77 | #define DEBUG_PRINT 0 | |
78 | #else | |
79 | #define DEBUG_PRINT 1 | |
80 | #endif | |
81 | ||
82 | #if DEBUG_PRINT | |
83 | #define dprintf(args...) fprintf(stderr, args) | |
84 | #else | |
85 | #define dprintf(args...) | |
86 | #endif | |
87 | ||
88 | static void usage(char **argv) | |
89 | { | |
90 | printf("Usage\n"); | |
91 | printf("Refresh : %s r [options]\n", argv[0]); | |
92 | printf("Fetch CRL : %s f URI [options]\n", argv[0]); | |
93 | printf("Fetch cert : %s F URI [options]\n", argv[0]); | |
94 | printf("Refresh options:\n"); | |
95 | printf(" s=stale_period in DAYS; default=%d\n", DEFAULT_STALE_DAYS); | |
96 | printf(" o=expire_overlap in SECONDS; default=%d\n", | |
97 | DEFAULT_EXPIRE_OVERLAP_SECONDS); | |
98 | printf(" p (Purge all entries, ensuring refresh with fresh CRLs)\n"); | |
99 | printf(" f (Full crypto CRL verification)\n"); | |
100 | printf(" k=keychainName (default=%s\n", CRL_CACHE_DB); | |
101 | printf(" v(erbose)\n"); | |
102 | printf("Fetch options:\n"); | |
103 | printf(" F=outFileName (default is stdout)\n"); | |
104 | printf(" n (no write to cache after fetch)\n"); | |
105 | exit(1); | |
106 | } | |
107 | ||
108 | /* | |
109 | * Print string. Null terminator is not assumed. | |
110 | */ | |
111 | static void printString( | |
112 | const CSSM_DATA *str) | |
113 | { | |
114 | unsigned i; | |
115 | char *cp = (char *)str->Data; | |
116 | for(i=0; i<str->Length; i++) { | |
117 | printf("%c", *cp++); | |
118 | } | |
119 | } | |
120 | ||
121 | /* declare a CSSM_DB_ATTRIBUTE_INFO with NAME_AS_STRING */ | |
122 | #define DB_ATTRIBUTE(name, type) \ | |
123 | { CSSM_DB_ATTRIBUTE_NAME_AS_STRING, \ | |
124 | {#name}, \ | |
125 | CSSM_DB_ATTRIBUTE_FORMAT_ ## type \ | |
126 | } | |
127 | ||
128 | /* The CRL DB attributes we care about*/ | |
129 | /* Keep these positions in sync with ATTR_DEX_xxx, below */ | |
130 | static const CSSM_DB_ATTRIBUTE_INFO x509CrlRecordAttrs[] = { | |
131 | DB_ATTRIBUTE(CrlType, UINT32), // 0 | |
132 | DB_ATTRIBUTE(CrlEncoding, UINT32), // 1 | |
133 | DB_ATTRIBUTE(PrintName, BLOB), // 2 | |
134 | DB_ATTRIBUTE(Issuer, BLOB), // 3 | |
135 | DB_ATTRIBUTE(NextUpdate, BLOB), // 4 | |
136 | DB_ATTRIBUTE(URI, BLOB), // 5 | |
137 | ||
138 | /* we don't use these */ | |
139 | // DB_ATTRIBUTE(ThisUpdate, BLOB), // 4 | |
140 | // DB_ATTRIBUTE(DeltaCrlNumber, UINT32) | |
141 | // DB_ATTRIBUTE(Alias, BLOB), | |
142 | // DB_ATTRIBUTE(CrlNumber, UINT32), | |
143 | }; | |
144 | ||
145 | #define NUM_CRL_ATTRS \ | |
146 | (sizeof(x509CrlRecordAttrs) / sizeof(x509CrlRecordAttrs[0])) | |
147 | ||
148 | #define ATTR_DEX_CRL_TYPE 0 | |
149 | #define ATTR_DEX_CRL_ENC 1 | |
150 | #define ATTR_DEX_PRINT_NAME 2 | |
151 | #define ATTR_DEX_ISSUER 3 | |
152 | #define ATTR_DEX_NEXT_UPDATE 4 | |
153 | #define ATTR_DEX_URI 5 | |
154 | ||
155 | /* free attribute(s) allocated by DL */ | |
156 | static void freeAttrs( | |
157 | CSSM_DB_ATTRIBUTE_DATA *attrs, | |
158 | unsigned numAttrs) | |
159 | { | |
160 | unsigned i; | |
161 | ||
162 | for(i=0; i<numAttrs; i++) { | |
163 | CSSM_DB_ATTRIBUTE_DATA_PTR attrData = &attrs[i]; | |
164 | unsigned j; | |
165 | for(j=0; j<attrData->NumberOfValues; j++) { | |
166 | CSSM_DATA_PTR data = &attrData->Value[j]; | |
167 | if(data == NULL) { | |
168 | /* fault of DL, who said there was a value here */ | |
169 | printf("***freeAttrs screwup: NULL data\n"); | |
170 | return; | |
171 | } | |
172 | APP_FREE(data->Data); | |
173 | data->Data = NULL; | |
174 | data->Length = 0; | |
175 | } | |
176 | APP_FREE(attrData->Value); | |
177 | attrData->Value = NULL; | |
178 | } | |
179 | } | |
180 | ||
181 | /* | |
182 | * Compare two CSSM_TIMESTRINGs. Returns: | |
183 | * -1 if t1 < t2 | |
184 | * 0 if t1 == t2 | |
185 | * 1 if t1 > t2 | |
186 | */ | |
187 | int compareTimes( | |
188 | const char *t1, | |
189 | const char *t2) | |
190 | { | |
191 | for(unsigned dex=0; dex<CSSM_TIME_STRLEN; dex++, t1++, t2++) { | |
192 | if(*t1 > *t2) { | |
193 | return 1; | |
194 | } | |
195 | if(*t1 < *t2) { | |
196 | return -1; | |
197 | } | |
198 | /* else same, on to next byte */ | |
199 | } | |
200 | /* equal */ | |
201 | return 0; | |
202 | } | |
203 | ||
204 | /* | |
205 | * everything we know or care about a CRL. | |
206 | */ | |
207 | class CrlInfo | |
208 | { | |
209 | public: | |
210 | CrlInfo( | |
211 | CSSM_DL_DB_HANDLE dlDbHand, | |
212 | CSSM_DB_ATTRIBUTE_DATA *attrData, // [NUM_CRL_ATTRS] | |
213 | CSSM_DB_UNIQUE_RECORD_PTR record, | |
214 | CSSM_DATA_PTR crlBlob); // optional | |
215 | ~CrlInfo(); | |
216 | ||
217 | CSSM_DATA_PTR fetchValidAttr( | |
218 | unsigned attrDex); | |
219 | int fetchIntAttr( | |
220 | unsigned dex, | |
221 | uint32 &rtn); | |
222 | ||
223 | bool isSameIssuer( | |
224 | CrlInfo *other); | |
225 | ||
226 | /* print the printable name + '\n' to stdout */ | |
227 | void printName(); | |
228 | ||
229 | void validateTimes( | |
230 | const char *updateTime, | |
231 | const char *staleTime, | |
232 | unsigned dex); | |
233 | ||
234 | /* state inferred from attributes, and maintained by | |
235 | * owner (not by us) */ | |
236 | bool mIsBadlyFormed; // general parse error | |
237 | bool mIsExpired; // compare to 'now' | |
238 | bool mIsStale; // compared to "staleTime' | |
239 | bool mRefreshed; // already refreshed | |
240 | ||
241 | /* | |
242 | * Actual CRL, optionally fetched from DB if doing a full crypto verify | |
243 | */ | |
244 | CSSM_DATA mCrlBlob; | |
245 | ||
246 | ||
247 | /* accessors for read-only member vars */ | |
248 | CSSM_DL_DB_HANDLE dlDbHand() { return mDlDbHand; } | |
249 | CSSM_DB_ATTRIBUTE_DATA_PTR attrData() { return &mAttrData[0]; } | |
250 | CSSM_DB_UNIQUE_RECORD_PTR record() { return mRecord; }; | |
251 | ||
252 | private: | |
253 | /* member variables which are read-only subsequent to construction */ | |
254 | CSSM_DL_DB_HANDLE mDlDbHand; | |
255 | ||
256 | /* | |
257 | * array of attr data | |
258 | * contents APP_MALLOCd by DL | |
259 | * contents APP_FREEd by our destructor | |
260 | */ | |
261 | CSSM_DB_ATTRIBUTE_DATA mAttrData[NUM_CRL_ATTRS]; | |
262 | ||
263 | /* | |
264 | * For possible use in CSSM_DL_DataDelete | |
265 | * Our destructor does CSSM_DL_FreeUniqueRecord | |
266 | */ | |
267 | CSSM_DB_UNIQUE_RECORD_PTR mRecord; | |
268 | }; | |
269 | ||
270 | CrlInfo::CrlInfo( | |
271 | CSSM_DL_DB_HANDLE dlDbHand, | |
272 | CSSM_DB_ATTRIBUTE_DATA *attrData, // [NUM_CRL_ATTRS] | |
273 | CSSM_DB_UNIQUE_RECORD_PTR record, | |
274 | CSSM_DATA_PTR crlBlob) // optional | |
275 | : mIsBadlyFormed(false), | |
276 | mIsExpired(false), | |
277 | mIsStale(false), | |
278 | mRefreshed(false), | |
279 | mDlDbHand(dlDbHand), | |
280 | mRecord(record) | |
281 | { | |
282 | if(crlBlob) { | |
283 | mCrlBlob = *crlBlob; | |
284 | } | |
285 | else { | |
286 | mCrlBlob.Data = NULL; | |
287 | mCrlBlob.Length = 0; | |
288 | } | |
289 | memmove(mAttrData, attrData, | |
290 | sizeof(CSSM_DB_ATTRIBUTE_DATA) * NUM_CRL_ATTRS); | |
291 | } | |
292 | ||
293 | CrlInfo::~CrlInfo() | |
294 | { | |
295 | freeAttrs(&mAttrData[0], NUM_CRL_ATTRS); | |
296 | CSSM_DL_FreeUniqueRecord(mDlDbHand, mRecord); | |
297 | if(mCrlBlob.Data) { | |
298 | APP_FREE(mCrlBlob.Data); | |
299 | } | |
300 | } | |
301 | ||
302 | /* | |
303 | * Is attribute at specified index present with one value? Returns the | |
304 | * value if so, else returns NULL. | |
305 | */ | |
306 | CSSM_DATA_PTR CrlInfo::fetchValidAttr( | |
307 | unsigned attrDex) | |
308 | { | |
309 | if(mAttrData[attrDex].NumberOfValues != 1) { | |
310 | return NULL; | |
311 | } | |
312 | return mAttrData[attrDex].Value; | |
313 | } | |
314 | ||
315 | /* | |
316 | * Fetch uint32 attr if it's there at specified attr index. | |
317 | * Returns non zero if it's not there and flags the CRL as bad. | |
318 | */ | |
319 | int CrlInfo::fetchIntAttr( | |
320 | unsigned dex, | |
321 | uint32 &rtn) | |
322 | { | |
323 | CSSM_DATA *val = fetchValidAttr(dex); | |
324 | if((val == NULL) || (val->Length != sizeof(uint32))) { | |
325 | dprintf("***Badly formed uint32 attr at dex %u\n", dex); | |
326 | mIsBadlyFormed = true; | |
327 | return 1; | |
328 | } | |
329 | rtn = cuDER_ToInt(val); | |
330 | return 0; | |
331 | } | |
332 | ||
333 | ||
334 | /* | |
335 | * See if two CRLs have same issuer. Requires (and verifies) that both | |
336 | * issuer attrs are well formed. | |
337 | */ | |
338 | bool CrlInfo::isSameIssuer( | |
339 | CrlInfo *other) | |
340 | { | |
341 | CSSM_DATA_PTR thisIssuer = fetchValidAttr(ATTR_DEX_ISSUER); | |
342 | if(thisIssuer == NULL) { | |
343 | return false; | |
344 | } | |
345 | CSSM_DATA_PTR otherIssuer = other->fetchValidAttr(ATTR_DEX_ISSUER); | |
346 | if(otherIssuer == NULL) { | |
347 | return false; | |
348 | } | |
349 | return cuCompareCssmData(thisIssuer, otherIssuer) ? true : false; | |
350 | } | |
351 | ||
352 | /* Print a CRL's PrintName attr */ | |
353 | void CrlInfo::printName() | |
354 | { | |
355 | CSSM_DATA_PTR val = fetchValidAttr(ATTR_DEX_PRINT_NAME); | |
356 | if(val == NULL) { | |
357 | printf("X509 CRL\n"); | |
358 | } | |
359 | else { | |
360 | printString(val); | |
361 | printf("\n"); | |
362 | } | |
363 | } | |
364 | ||
365 | /* | |
366 | * Given time strings representing 'update time' and 'stale time', | |
367 | * calculate mIsExpired and mIsStale. | |
368 | */ | |
369 | void CrlInfo::validateTimes( | |
370 | const char *updateTime, // now - expireOverlap | |
371 | const char *staleTime, // now - staleTime | |
372 | unsigned dex) // for debug info | |
373 | { | |
374 | CSSM_DATA *nextUpdateData = fetchValidAttr(ATTR_DEX_NEXT_UPDATE); | |
375 | if((nextUpdateData == NULL) || | |
376 | (nextUpdateData->Length != CSSM_TIME_STRLEN)) { | |
377 | printf("***Badly formed NextUpdate attr on CRL %u\n", dex); | |
378 | mIsBadlyFormed = true; | |
379 | return; | |
380 | } | |
381 | #if DEBUG_PRINT | |
382 | printf("Crl %u NextUpdate : ", dex); printString(nextUpdateData); | |
383 | printf("\n"); | |
384 | #endif | |
385 | char *nextUpdate = (char *)nextUpdateData->Data; | |
386 | if(compareTimes(nextUpdate, updateTime) < 0) { | |
387 | dprintf("...CRL %u is expired\n", dex); | |
388 | mIsExpired = true; | |
389 | if(compareTimes(nextUpdate, staleTime) < 0) { | |
390 | dprintf("...CRL %u is stale\n", dex); | |
391 | mIsStale = true; | |
392 | } | |
393 | /* note it can't be stale and not expired */ | |
394 | } | |
395 | } | |
396 | ||
397 | /* | |
398 | * Fetch attrs for all CRLs from DB. CRL blobs themselves are not fetched | |
399 | * unless the fetchBlobs argument is asserted. | |
400 | */ | |
401 | static CSSM_RETURN fetchAllCrls( | |
402 | CSSM_DL_DB_HANDLE dlDbHand, | |
403 | bool fetchBlobs, // fetch actual CRL data | |
404 | CrlInfo **&rtnCrlInfo, // RETURNED | |
405 | unsigned &numCrls) // RETURNED | |
406 | { | |
407 | CSSM_QUERY query; | |
408 | CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; | |
409 | CSSM_DB_UNIQUE_RECORD_PTR record = NULL; | |
410 | CSSM_RETURN crtn; | |
411 | CSSM_HANDLE resultHand; | |
412 | unsigned attrDex; | |
413 | CSSM_DB_ATTRIBUTE_DATA attrData[NUM_CRL_ATTRS]; | |
414 | CSSM_DATA_PTR crlDataPtr = NULL; | |
415 | CSSM_DATA crlData; | |
416 | ||
417 | numCrls = 0; | |
418 | rtnCrlInfo = NULL; | |
419 | ||
420 | /* build an ATTRIBUTE_DATA array from list attrs */ | |
421 | memset(attrData, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA) * NUM_CRL_ATTRS); | |
422 | for(attrDex=0; attrDex<NUM_CRL_ATTRS; attrDex++) { | |
423 | attrData[attrDex].Info = x509CrlRecordAttrs[attrDex]; | |
424 | } | |
425 | ||
426 | recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_X509_CRL; | |
427 | recordAttrs.NumberOfAttributes = NUM_CRL_ATTRS; | |
428 | recordAttrs.AttributeData = &attrData[0]; | |
429 | ||
430 | /* just search by recordType, no predicates */ | |
431 | query.RecordType = CSSM_DL_DB_RECORD_X509_CRL; | |
432 | query.Conjunctive = CSSM_DB_NONE; | |
433 | query.NumSelectionPredicates = 0; | |
434 | query.SelectionPredicate = NULL; | |
435 | query.QueryLimits.TimeLimit = 0; // FIXME - meaningful? | |
436 | query.QueryLimits.SizeLimit = 1; // FIXME - meaningful? | |
437 | query.QueryFlags = 0; // CSSM_QUERY_RETURN_DATA...FIXME - used? | |
438 | ||
439 | if(fetchBlobs) { | |
440 | crlDataPtr = &crlData; | |
441 | } | |
442 | ||
443 | crtn = CSSM_DL_DataGetFirst(dlDbHand, | |
444 | &query, | |
445 | &resultHand, | |
446 | &recordAttrs, | |
447 | crlDataPtr, | |
448 | &record); | |
449 | switch(crtn) { | |
450 | case CSSM_OK: | |
451 | break; // proceed | |
452 | case CSSMERR_DL_ENDOFDATA: | |
453 | /* we're done here */ | |
454 | return CSSM_OK; | |
455 | case CSSMERR_DL_INVALID_RECORDTYPE: | |
456 | /* this means that this keychain hasn't been initialized | |
457 | * for CRL schema; treat it as empty. */ | |
458 | return CSSM_OK; | |
459 | default: | |
460 | cuPrintError("DataGetFirst", crtn); | |
461 | return crtn; | |
462 | } | |
463 | ||
464 | /* Cook up a CrlInfo, add it to outgoing array */ | |
465 | CrlInfo *crlInfo = new CrlInfo(dlDbHand, &attrData[0], record, crlDataPtr); | |
466 | rtnCrlInfo = (CrlInfo **)malloc(sizeof(CrlInfo*)); | |
467 | rtnCrlInfo[0] = crlInfo; | |
468 | numCrls++; | |
469 | ||
470 | /* now the rest of them */ | |
471 | for(;;) { | |
472 | crtn = CSSM_DL_DataGetNext(dlDbHand, | |
473 | resultHand, | |
474 | &recordAttrs, | |
475 | crlDataPtr, | |
476 | &record); | |
477 | switch(crtn) { | |
478 | case CSSM_OK: | |
479 | rtnCrlInfo = (CrlInfo **)realloc(rtnCrlInfo, | |
480 | sizeof(CrlInfo *) * (numCrls + 1)); | |
481 | rtnCrlInfo[numCrls] = new CrlInfo(dlDbHand, &attrData[0], record, | |
482 | crlDataPtr); | |
483 | numCrls++; | |
484 | break; // and go again | |
485 | case CSSMERR_DL_ENDOFDATA: | |
486 | /* normal termination */ | |
487 | return CSSM_OK; | |
488 | default: | |
489 | cuPrintError("DataGetNext", crtn); | |
490 | return crtn; | |
491 | } | |
492 | } | |
493 | /* not reached */ | |
494 | } | |
495 | ||
496 | /* | |
497 | * Validate each CRL's integrity (Not including expiration or stale time). | |
498 | */ | |
499 | static void validateCrls( | |
500 | CrlInfo **crlInfo, | |
501 | unsigned numCrls, | |
502 | bool verbose) | |
503 | { | |
504 | CrlInfo *crl; | |
505 | ||
506 | for(unsigned dex=0; dex<numCrls; dex++) { | |
507 | crl = crlInfo[dex]; | |
508 | ||
509 | /* get CrlType, make sure it's acceptable */ | |
510 | uint32 i; | |
511 | ||
512 | if(crl->fetchIntAttr(ATTR_DEX_CRL_TYPE, i)) { | |
513 | continue; | |
514 | } | |
515 | switch(i) { | |
516 | case CSSM_CRL_TYPE_X_509v1: | |
517 | case CSSM_CRL_TYPE_X_509v2: | |
518 | /* OK */ | |
519 | break; | |
520 | default: | |
521 | printf("***bad CRL type (%u) on CRL %u\n", (unsigned)i, dex); | |
522 | crl->mIsBadlyFormed = true; | |
523 | continue; | |
524 | } | |
525 | ||
526 | /* ditto for encoding */ | |
527 | if(crl->fetchIntAttr(ATTR_DEX_CRL_ENC, i)) { | |
528 | continue; | |
529 | } | |
530 | switch(i) { | |
531 | case CSSM_CRL_ENCODING_BER: | |
532 | case CSSM_CRL_ENCODING_DER: | |
533 | /* OK */ | |
534 | break; | |
535 | default: | |
536 | printf("***bad CRL encoding (%u) on CRL %u\n", | |
537 | (unsigned)i, dex); | |
538 | crl->mIsBadlyFormed = true; | |
539 | continue; | |
540 | } | |
541 | /* any other grounds for deletion? */ | |
542 | } | |
543 | } | |
544 | ||
545 | /* | |
546 | * Perform full crypto CRL validation. | |
547 | * We use the system-wide intermediate cert keychain here, but do | |
548 | * NOT use the CRL cache we're working on (or any other), since | |
549 | * we dont' really want to trust anything at this point. | |
550 | */ | |
551 | static void cryptoValidateCrls( | |
552 | CrlInfo **crlInfo, | |
553 | unsigned numCrls, | |
554 | bool verbose, | |
555 | CSSM_TP_HANDLE tpHand, | |
556 | CSSM_CSP_HANDLE cspHand, | |
557 | CSSM_CL_HANDLE clHand, | |
558 | CSSM_DL_HANDLE dlHand) | |
559 | { | |
560 | CrlInfo *crl; | |
561 | const CSSM_DATA *anchors; | |
562 | uint32 anchorCount; | |
563 | OSStatus ortn; | |
564 | ||
565 | /* just snag these once */ | |
566 | ortn = SecTrustGetCSSMAnchorCertificates(&anchors, &anchorCount); | |
567 | if(ortn) { | |
568 | printf("SecTrustGetCSSMAnchorCertificates returned %u\n", (int)ortn); | |
569 | return; | |
570 | } | |
571 | ||
572 | /* and the system-wide intermediate certs */ | |
573 | CSSM_DL_DB_HANDLE certDb; | |
574 | CSSM_DL_DB_HANDLE_PTR certDbPtr = NULL; | |
575 | CSSM_RETURN crtn = CSSM_DL_DbOpen(dlHand, | |
576 | X509_CERT_DB, | |
577 | NULL, // DbLocation | |
578 | CSSM_DB_ACCESS_READ, | |
579 | NULL, // CSSM_ACCESS_CREDENTIALS *AccessCred | |
580 | NULL, // void *OpenParameters | |
581 | &certDb.DBHandle); | |
582 | if(crtn) { | |
583 | cuPrintError("CSSM_DL_DbOpen", crtn); | |
584 | printf("***Error opening intermediate cert file %s.\n", X509_CERT_DB); | |
585 | /* Oh well, keep trying */ | |
586 | } | |
587 | else { | |
588 | certDb.DLHandle = dlHand; | |
589 | certDbPtr = &certDb; | |
590 | } | |
591 | ||
592 | for(unsigned dex=0; dex<numCrls; dex++) { | |
593 | crl = crlInfo[dex]; | |
594 | crtn = cuCrlVerify(tpHand, clHand, cspHand, | |
595 | &crl->mCrlBlob, | |
596 | certDbPtr, | |
597 | anchors, | |
598 | anchorCount); | |
599 | switch(crtn) { | |
600 | case CSSMERR_APPLETP_CRL_EXPIRED: | |
601 | /* special case, we'll handle this via its attrs */ | |
602 | case CSSM_OK: | |
603 | break; | |
604 | default: | |
605 | if(verbose) { | |
606 | printf("...CRL %u FAILED crypto verify\n", dex); | |
607 | } | |
608 | crl->mIsBadlyFormed = true; | |
609 | break; | |
610 | } | |
611 | } | |
612 | CSSM_DL_DbClose(certDb); | |
613 | } | |
614 | ||
615 | /* | |
616 | * Calculate expired/stale state for all CRLs. | |
617 | */ | |
618 | int calcCurrent( | |
619 | CrlInfo **crlInfo, | |
620 | unsigned numCrls, | |
621 | int expireOverlapSeconds, | |
622 | int staleTimeSeconds) | |
623 | { | |
624 | if(expireOverlapSeconds > staleTimeSeconds) { | |
625 | printf("***ExpireOverlap greater than StaleTime; aborting.\n"); | |
626 | return 1; | |
627 | } | |
628 | char *updateTime = cuTimeAtNowPlus(expireOverlapSeconds, TIME_CSSM); | |
629 | char *staleTime = cuTimeAtNowPlus(-staleTimeSeconds, TIME_CSSM); | |
630 | ||
631 | dprintf("updateTime : %s\n", updateTime); | |
632 | dprintf("staleTime : %s\n", staleTime); | |
633 | ||
634 | for(unsigned dex=0; dex<numCrls; dex++) { | |
635 | crlInfo[dex]->validateTimes(updateTime, staleTime, dex); | |
636 | } | |
637 | APP_FREE(updateTime); | |
638 | APP_FREE(staleTime); | |
639 | return 0; | |
640 | } | |
641 | ||
642 | /* | |
643 | * Mark all CRLs as stale (i.e., force them to be deleted later). | |
644 | */ | |
645 | static void purgeAllCrls( | |
646 | CrlInfo **crlInfo, | |
647 | unsigned numCrls, | |
648 | bool verbose) | |
649 | { | |
650 | for(unsigned dex=0; dex<numCrls; dex++) { | |
651 | CrlInfo *crl = crlInfo[dex]; | |
652 | crl->mIsExpired = true; | |
653 | crl->mIsStale = true; | |
654 | } | |
655 | } | |
656 | ||
657 | /* | |
658 | * Delete all stale and badly formed CRLs from cache. | |
659 | */ | |
660 | static void deleteBadCrls( | |
661 | CrlInfo **crlInfo, | |
662 | unsigned numCrls, | |
663 | bool verbose) | |
664 | { | |
665 | CrlInfo *crl; | |
666 | ||
667 | for(unsigned dex=0; dex<numCrls; dex++) { | |
668 | crl = crlInfo[dex]; | |
669 | ||
670 | /* | |
671 | * expired is not grounds for deletion; mIsStale is. | |
672 | */ | |
673 | if(crl->mIsBadlyFormed || crl->mIsStale) { | |
674 | if(verbose || DEBUG_PRINT) { | |
675 | printf("...deleting CRL %u from ", dex); | |
676 | crl->printName(); | |
677 | } | |
678 | CSSM_RETURN crtn = CSSM_DL_DataDelete(crl->dlDbHand(), | |
679 | crl->record()); | |
680 | if(crtn) { | |
681 | cuPrintError("CSSM_DL_DataDelete", crtn); | |
682 | } | |
683 | } | |
684 | } | |
685 | } | |
686 | ||
687 | /* | |
688 | * For each expired CRL, fetch a new one if we don't have a current | |
689 | * CRL from the same place. | |
690 | */ | |
691 | static void refreshExpiredCrls( | |
692 | CrlInfo **crlInfo, | |
693 | unsigned numCrls, | |
694 | CSSM_CL_HANDLE clHand, | |
695 | bool verbose) | |
696 | { | |
697 | CrlInfo *crl; | |
698 | bool haveCurrent; | |
699 | CSSM_DATA newCrl; | |
700 | ||
701 | for(unsigned dex=0; dex<numCrls; dex++) { | |
702 | crl = crlInfo[dex]; | |
703 | ||
704 | if(!crl->mIsExpired || crl->mRefreshed) { | |
705 | continue; | |
706 | } | |
707 | ||
708 | /* do we have one for the same issuer that's current? */ | |
709 | haveCurrent = false; | |
710 | for(unsigned i=0; i<numCrls; i++) { | |
711 | if(i == dex) { | |
712 | /* skip identity */ | |
713 | continue; | |
714 | } | |
715 | CrlInfo *checkCrl = crlInfo[i]; | |
716 | if(checkCrl->mIsBadlyFormed) { | |
717 | /* forget this one */ | |
718 | continue; | |
719 | } | |
720 | if(checkCrl->mIsExpired && !checkCrl->mRefreshed) { | |
721 | continue; | |
722 | } | |
723 | if(crl->isSameIssuer(checkCrl)) { | |
724 | /* have a match; this one's OK */ | |
725 | dprintf("up-to-date CRL at dex %u matching expired CRL %u\n", | |
726 | i, dex); | |
727 | haveCurrent = true; | |
728 | break; | |
729 | } | |
730 | } | |
731 | if(haveCurrent) { | |
732 | continue; | |
733 | } | |
734 | ||
735 | /* | |
736 | * Not all CRLs have a URI attribute, which is required for | |
737 | * refresh | |
738 | */ | |
739 | CSSM_DATA_PTR uri = crl->fetchValidAttr(ATTR_DEX_URI); | |
740 | if(uri == NULL) { | |
741 | dprintf("Expired CRL with no URI at dex %u\n", dex); | |
742 | continue; | |
743 | } | |
744 | ||
745 | /* fetch a new one */ | |
746 | if(verbose || DEBUG_PRINT) { | |
747 | printf("...fetching new CRL from net to update CRL %u from ", | |
748 | dex); | |
749 | crl->printName(); | |
750 | } | |
751 | CSSM_RETURN crtn = netFetch(*uri, LT_Crl, newCrl); | |
752 | if(crtn) { | |
753 | cuPrintError("netFetch", crtn); | |
754 | continue; | |
755 | } | |
756 | ||
757 | /* store it in the DB */ | |
758 | crtn = cuAddCrlToDb(crl->dlDbHand(), clHand, &newCrl, uri); | |
759 | ||
760 | /* | |
761 | * One special error case - UNIQUE_INDEX_DATA indicates that | |
762 | * the CRL we just fetched is already in the cache. This | |
763 | * can occur when expireOverlap is sufficiently large that | |
764 | * we decide to fetch before a CRL is actually expired. In | |
765 | * this case process as usual, avoiding any further updates | |
766 | * from this CA/URI. | |
767 | */ | |
768 | switch(crtn) { | |
769 | case CSSM_OK: | |
770 | dprintf("...refreshed CRL added to DB to account " | |
771 | "for expired CRL %u\n", dex); | |
772 | break; | |
773 | case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA: | |
774 | dprintf("...refreshed CRL is a dup of CRL %u; skipping\n", | |
775 | dex); | |
776 | break; | |
777 | default: | |
778 | continue; | |
779 | } | |
780 | ||
781 | ||
782 | /* | |
783 | * In case there are other CRLs still to be discovered | |
784 | * in our list which are a) expired, and b) from this same issuer, | |
785 | * we flag the current (expired) CRL as refreshed to ensure that | |
786 | * we don't do this fetch again. A lot easier than cooking up | |
787 | * a new CrlInfo object for the CRL we just fetched. | |
788 | */ | |
789 | crl->mRefreshed = true; | |
790 | } | |
791 | } | |
792 | ||
793 | /* | |
794 | * Open an existing keychain or create a new one. | |
795 | * This is a known "insecure" keychain/DB, since you don't need | |
796 | * to unlock it to add or remove CRLs to/from it. Thus if | |
797 | * we create it we use the filename as password. | |
798 | */ | |
799 | CSSM_RETURN openDatabase( | |
800 | CSSM_DL_HANDLE dlHand, | |
801 | const char *dbFileName, | |
802 | bool verbose, | |
803 | CSSM_DB_HANDLE &dbHand, // RETURNED | |
804 | bool &didCreate) // RETURNED | |
805 | { | |
806 | didCreate = false; | |
807 | ||
808 | /* try to open existing DB */ | |
809 | CSSM_RETURN crtn = CSSM_DL_DbOpen(dlHand, | |
810 | dbFileName, | |
811 | NULL, // DbLocation | |
812 | CSSM_DB_ACCESS_READ | CSSM_DB_ACCESS_WRITE, | |
813 | NULL, // CSSM_ACCESS_CREDENTIALS *AccessCred | |
814 | NULL, // void *OpenParameters | |
815 | &dbHand); | |
816 | switch(crtn) { | |
817 | case CSSM_OK: | |
818 | return CSSM_OK; | |
819 | case CSSMERR_DL_DATASTORE_DOESNOT_EXIST: | |
820 | /* proceed to create it */ | |
821 | break; | |
822 | default: | |
823 | cuPrintError("CSSM_DL_DbOpen", crtn); | |
824 | return crtn; | |
825 | } | |
826 | ||
827 | /* create new one */ | |
828 | if(verbose) { | |
829 | printf("...creating database %s\n", dbFileName); | |
830 | } | |
831 | CSSM_DBINFO dbInfo; | |
832 | memset(&dbInfo, 0, sizeof(CSSM_DBINFO)); | |
833 | ||
834 | CssmAllocator &alloc = CssmAllocator::standard(); | |
835 | CssmClient::AclFactory::PasswordChangeCredentials pCreds((StringData(dbFileName)), alloc); | |
836 | const AccessCredentials* aa = pCreds; | |
837 | ||
838 | // @@@ Create a nice wrapper for building the default AclEntryPrototype. | |
839 | TypedList subject(alloc, CSSM_ACL_SUBJECT_TYPE_ANY); | |
840 | AclEntryPrototype protoType(subject); | |
841 | AuthorizationGroup &authGroup = protoType.authorization(); | |
842 | CSSM_ACL_AUTHORIZATION_TAG tag = CSSM_ACL_AUTHORIZATION_ANY; | |
843 | authGroup.NumberOfAuthTags = 1; | |
844 | authGroup.AuthTags = &tag; | |
845 | ||
846 | const ResourceControlContext rcc(protoType, const_cast<AccessCredentials *>(aa)); | |
847 | ||
848 | crtn = CSSM_DL_DbCreate(dlHand, | |
849 | dbFileName, | |
850 | NULL, // DbLocation | |
851 | &dbInfo, | |
852 | CSSM_DB_ACCESS_PRIVILEGED, | |
853 | &rcc, // CredAndAclEntry | |
854 | NULL, // OpenParameters | |
855 | &dbHand); | |
856 | if(crtn) { | |
857 | cuPrintError("CSSM_DL_DbCreate", crtn); | |
858 | return crtn; | |
859 | } | |
860 | else { | |
861 | /* one more thing: make it world writable by convention */ | |
862 | if(chmod(dbFileName, 0666)) { | |
863 | perror(dbFileName); | |
864 | crtn = CSSMERR_DL_DB_LOCKED; | |
865 | } | |
866 | didCreate = true; | |
867 | } | |
868 | return crtn; | |
869 | } | |
870 | ||
871 | /* | |
872 | * Add CRL fetched from net to local cache, used only by fetchItemFromNet. | |
873 | * Note we're not dealing with fetched certs here; they are not | |
874 | * stored on the fly. | |
875 | */ | |
876 | static int writeFetchedItem( | |
877 | LF_Type lfType, | |
878 | const CSSM_DATA *itemData, | |
879 | const CSSM_DATA *uriData) | |
880 | { | |
881 | if(lfType == LT_Cert) { | |
882 | return 0; | |
883 | } | |
884 | ||
885 | /* | |
886 | * The awkward part of this operation is that we have to open a DLDB | |
887 | * (whose filename can only be hard coded at this point) and attach | |
888 | * to the CL. | |
889 | */ | |
890 | CSSM_DL_DB_HANDLE dlDbHand = {0, 0}; | |
891 | CSSM_CL_HANDLE clHand = 0; | |
892 | CSSM_RETURN crtn; | |
893 | bool didCreate; | |
894 | int ourRtn = 0; | |
895 | ||
896 | clHand = cuClStartup(); | |
897 | if(clHand == 0) { | |
898 | return 1; | |
899 | } | |
900 | /* subsequent errors to done: */ | |
901 | dlDbHand.DLHandle = cuDlStartup(); | |
902 | if(dlDbHand.DLHandle == 0) { | |
903 | ourRtn = 1; | |
904 | goto done; | |
905 | } | |
906 | crtn = openDatabase(dlDbHand.DLHandle, | |
907 | CRL_CACHE_DB, | |
908 | false, // verbose | |
909 | dlDbHand.DBHandle, | |
910 | didCreate); | |
911 | if(crtn) { | |
912 | dprintf("***Error opening keychain %s. Aborting.\n", CRL_CACHE_DB); | |
913 | ourRtn = 1; | |
914 | goto done; | |
915 | } | |
916 | ||
917 | /* store it in the DB */ | |
918 | crtn = cuAddCrlToDb(dlDbHand, clHand, itemData, uriData); | |
919 | ||
920 | /* | |
921 | * One special error case - UNIQUE_INDEX_DATA indicates that | |
922 | * the CRL we just fetched is already in the cache. This | |
923 | * can occur as a result of a race condition between searching | |
924 | * for a CRL in the cache (currently done by the TP, who execs us) | |
925 | * and the fetch we just completed, if multiple tasks or threads are | |
926 | * searching for the same CRL. | |
927 | * Eventually this will be handled more robustly by all of the searching | |
928 | * and fetching being done in a daemon. | |
929 | */ | |
930 | switch(crtn) { | |
931 | case CSSM_OK: | |
932 | dprintf("...fetched CRL added to DB\n"); | |
933 | break; | |
934 | case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA: | |
935 | dprintf("...fetched CRL is a dup; skipping\n"); | |
936 | break; | |
937 | default: | |
938 | /* specific error logged by cuAddCrlToDb() */ | |
939 | dprintf("Error writing CRL to cache\n"); | |
940 | ourRtn = 1; | |
941 | break; | |
942 | } | |
943 | done: | |
944 | if(dlDbHand.DBHandle) { | |
945 | CSSM_DL_DbClose(dlDbHand); | |
946 | } | |
947 | if(dlDbHand.DLHandle) { | |
948 | CSSM_ModuleDetach(dlDbHand.DLHandle); | |
949 | } | |
950 | if(clHand) { | |
951 | CSSM_ModuleDetach(clHand); | |
952 | } | |
953 | return ourRtn; | |
954 | } | |
955 | /* | |
956 | * Fetch a CRL or Cert from net; write it to a file. | |
957 | */ | |
958 | int fetchItemFromNet( | |
959 | LF_Type lfType, | |
960 | const char *URI, | |
961 | char *outFileName, // NULL indicates write to stdout | |
962 | bool writeToCache) | |
963 | { | |
964 | const CSSM_DATA uriData = {strlen(URI) + 1, (uint8 *)URI}; | |
965 | CSSM_DATA item; | |
966 | CSSM_RETURN crtn; | |
967 | int irtn; | |
968 | ||
969 | dprintf("fetchItemFromNet %s outFile %s\n", | |
970 | URI, outFileName ? outFileName : "stdout"); | |
971 | ||
972 | /* netFetch deals with NULL-terminated string */ | |
973 | uriData.Data[uriData.Length - 1] = 0; | |
974 | crtn = netFetch(uriData, lfType, item); | |
975 | if(crtn) { | |
976 | cuPrintError("netFetch", crtn); | |
977 | return 1; | |
978 | } | |
979 | dprintf("fetchItemFromNet netFetch complete, %u bytes read\n", | |
980 | (unsigned)item.Length); | |
981 | if(outFileName == NULL) { | |
982 | irtn = write(STDOUT_FILENO, item.Data, item.Length); | |
983 | if(irtn != (int)item.Length) { | |
984 | irtn = errno; | |
985 | perror("write"); | |
986 | } | |
987 | else { | |
988 | irtn = 0; | |
989 | } | |
990 | } | |
991 | else { | |
992 | irtn = writeFile(outFileName, item.Data, item.Length); | |
993 | if(irtn) { | |
994 | perror(outFileName); | |
995 | } | |
996 | } | |
997 | if((irtn == 0) && writeToCache) { | |
998 | irtn = writeFetchedItem(lfType, &item, &uriData); | |
999 | } | |
1000 | free(item.Data); | |
1001 | dprintf("fetchItemFromNet returning %d\n", irtn); | |
1002 | return irtn; | |
1003 | } | |
1004 | ||
1005 | int main(int argc, char **argv) | |
1006 | { | |
1007 | CSSM_RETURN crtn; | |
1008 | CSSM_DL_DB_HANDLE dlDbHand; | |
1009 | CSSM_CL_HANDLE clHand; | |
1010 | CSSM_CSP_HANDLE cspHand = 0; | |
1011 | CSSM_TP_HANDLE tpHand = 0; | |
1012 | int arg; | |
1013 | char *argp; | |
1014 | bool didCreate = false; | |
1015 | int optArg; | |
1016 | ||
1017 | /* user-specified variables */ | |
1018 | bool verbose = false; | |
1019 | bool purgeAll = false; | |
1020 | bool fullCryptoValidation = false; | |
1021 | int staleDays = DEFAULT_STALE_DAYS; | |
1022 | int expireOverlapSeconds = | |
1023 | DEFAULT_EXPIRE_OVERLAP_SECONDS; | |
1024 | char *dbFileName = CRL_CACHE_DB; | |
1025 | /* fetch options */ | |
1026 | LF_Type lfType = LT_Crl; | |
1027 | char *outFileName = NULL; | |
1028 | bool writeToCache = true; | |
1029 | char *uri = NULL; | |
1030 | ||
1031 | if(argc < 2) { | |
1032 | usage(argv); | |
1033 | } | |
1034 | switch(argv[1][0]) { | |
1035 | case 'F': | |
1036 | lfType = LT_Cert; | |
1037 | /* and drop thru */ | |
1038 | case 'f': | |
1039 | if(argc < 3) { | |
1040 | usage(argv); | |
1041 | } | |
1042 | uri = argv[2]; | |
1043 | optArg = 3; | |
1044 | break; | |
1045 | case 'r': | |
1046 | optArg = 2; | |
1047 | break; | |
1048 | default: | |
1049 | usage(argv); | |
1050 | } | |
1051 | /* refresh options */ | |
1052 | for(arg=optArg; arg<argc; arg++) { | |
1053 | argp = argv[arg]; | |
1054 | switch(argp[0]) { | |
1055 | case 's': | |
1056 | if(argp[1] != '=') { | |
1057 | usage(argv); | |
1058 | } | |
1059 | staleDays = atoi(&argp[2]); | |
1060 | break; | |
1061 | case 'o': | |
1062 | if(argp[1] != '=') { | |
1063 | usage(argv); | |
1064 | } | |
1065 | expireOverlapSeconds = atoi(&argp[2]); | |
1066 | break; | |
1067 | case 'p': | |
1068 | purgeAll = true; | |
1069 | break; | |
1070 | case 'f': | |
1071 | fullCryptoValidation = true; | |
1072 | break; | |
1073 | case 'k': | |
1074 | if(argp[1] != '=') { | |
1075 | usage(argv); | |
1076 | } | |
1077 | dbFileName = &argp[2]; | |
1078 | break; | |
1079 | case 'n': | |
1080 | writeToCache = false; | |
1081 | break; | |
1082 | case 'F': | |
1083 | if(argp[1] != '=') { | |
1084 | usage(argv); | |
1085 | } | |
1086 | outFileName = &argp[2]; | |
1087 | break; | |
1088 | case 'v': | |
1089 | verbose = true; | |
1090 | break; | |
1091 | default: | |
1092 | usage(argv); | |
1093 | } | |
1094 | } | |
1095 | if(argv[1][0] != 'r') { | |
1096 | return fetchItemFromNet(lfType, uri, outFileName, writeToCache); | |
1097 | } | |
1098 | ||
1099 | dprintf("...staleDays %d expireOverlapSeconds %d\n", | |
1100 | staleDays, expireOverlapSeconds); | |
1101 | ||
1102 | /* | |
1103 | * Open the keychain. | |
1104 | * Note that since we're doing a lot of CDSA_level DB ops, we | |
1105 | * just acces the keychain as a DLDB direwctly - that way we | |
1106 | * have control over the app-level memory callbacks. | |
1107 | */ | |
1108 | dlDbHand.DLHandle = cuDlStartup(); | |
1109 | if(dlDbHand.DLHandle == 0) { | |
1110 | exit(1); | |
1111 | } | |
1112 | crtn = openDatabase(dlDbHand.DLHandle, | |
1113 | dbFileName, | |
1114 | verbose, | |
1115 | dlDbHand.DBHandle, | |
1116 | didCreate); | |
1117 | if(crtn) { | |
1118 | printf("***Error opening keychain %s. Aborting.\n", dbFileName); | |
1119 | exit(1); | |
1120 | } | |
1121 | ||
1122 | if(didCreate) { | |
1123 | /* New, empty keychain. I guarantee you we're done. */ | |
1124 | CSSM_DL_DbClose(dlDbHand); | |
1125 | CSSM_ModuleDetach(dlDbHand.DLHandle); | |
1126 | return 0; | |
1127 | } | |
1128 | ||
1129 | clHand = cuClStartup(); | |
1130 | if(clHand == 0) { | |
1131 | exit(1); | |
1132 | } | |
1133 | if(fullCryptoValidation) { | |
1134 | /* also need TP, CSP */ | |
1135 | cspHand = cuCspStartup(CSSM_TRUE); | |
1136 | if(cspHand == 0) { | |
1137 | exit(1); | |
1138 | } | |
1139 | tpHand = cuTpStartup(); | |
1140 | if(tpHand == 0) { | |
1141 | exit(1); | |
1142 | } | |
1143 | } | |
1144 | ||
1145 | /* fetch all CRLs from the keychain */ | |
1146 | CrlInfo **crlInfo; | |
1147 | unsigned numCrls; | |
1148 | crtn = fetchAllCrls(dlDbHand, fullCryptoValidation, crlInfo, numCrls); | |
1149 | if(crtn) { | |
1150 | printf("***Error reading CRLs from %s. Aborting.\n", dbFileName); | |
1151 | exit(1); | |
1152 | } | |
1153 | dprintf("...%u CRLs found\n", numCrls); | |
1154 | ||
1155 | /* basic validation */ | |
1156 | validateCrls(crlInfo, numCrls, verbose); | |
1157 | ||
1158 | /* Optional full crypto validation */ | |
1159 | if(fullCryptoValidation) { | |
1160 | cryptoValidateCrls(crlInfo, numCrls, verbose, | |
1161 | tpHand, cspHand, clHand, dlDbHand.DLHandle); | |
1162 | } | |
1163 | ||
1164 | /* update the validity time flags on the CRLs */ | |
1165 | if(calcCurrent(crlInfo, numCrls, expireOverlapSeconds, | |
1166 | staleDays * SECONDS_PER_DAY)) { | |
1167 | printf("***Error calculating CRL times. Aborting\n"); | |
1168 | exit(1); | |
1169 | } | |
1170 | ||
1171 | if(purgeAll) { | |
1172 | /* mark all of them stale */ | |
1173 | purgeAllCrls(crlInfo, numCrls, verbose); | |
1174 | } | |
1175 | ||
1176 | /* | |
1177 | * Delete all bad CRLs from DB. We do this before the refresh in | |
1178 | * case of the purgeAll option, in which case we really want to | |
1179 | * insert newly fetched CRLs in the DB even if they appear to | |
1180 | * be trhe same as the ones they're replacing. | |
1181 | */ | |
1182 | deleteBadCrls(crlInfo, numCrls, verbose); | |
1183 | ||
1184 | /* refresh the out-of-date CRLs */ | |
1185 | refreshExpiredCrls(crlInfo, numCrls, clHand, verbose); | |
1186 | ||
1187 | /* clean up */ | |
1188 | for(unsigned dex=0; dex<numCrls; dex++) { | |
1189 | delete crlInfo[dex]; | |
1190 | } | |
1191 | free(crlInfo); | |
1192 | CSSM_DL_DbClose(dlDbHand); | |
1193 | CSSM_ModuleDetach(dlDbHand.DLHandle); | |
1194 | CSSM_ModuleDetach(clHand); | |
1195 | if(tpHand) { | |
1196 | CSSM_ModuleDetach(tpHand); | |
1197 | } | |
1198 | if(cspHand) { | |
1199 | CSSM_ModuleDetach(cspHand); | |
1200 | } | |
1201 | return 0; | |
1202 | } |