2 * Copyright (c) 2003 Apple Computer, Inc. All Rights Reserved.
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.
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.
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).
25 * Terminology used here:
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
34 * 'expireOverlap' is (nowTime - updateTime) in seconds. It's the
35 * distance into the future at which we evaluate a CRL's expiration
37 * 'stale' means that a CRL is so old that it should be deleted from
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).
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.
48 * expireOverlap and staleTime have defaults which can be overridden
49 * via command line arguments.
54 #include <sys/types.h>
56 #include <CdsaUtils/cuCdsaUtils.h>
57 #include <CdsaUtils/cuTimeStr.h>
58 #include <CdsaUtils/cuDbUtils.h>
59 #include <CdsaUtils/cuFileIo.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>
68 #define DEFAULT_STALE_DAYS 10
69 #define DEFAULT_EXPIRE_OVERLAP_SECONDS 3600
71 #define SECONDS_PER_DAY (60 * 60 * 24)
73 #define CRL_CACHE_DB "/var/db/crls/crlcache.db"
74 #define X509_CERT_DB "/System/Library/Keychains/X509Certificates"
83 #define dprintf(args...) fprintf(stderr, args)
85 #define dprintf(args...)
88 static void usage(char **argv
)
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");
109 * Print string. Null terminator is not assumed.
111 static void printString(
112 const CSSM_DATA
*str
)
115 char *cp
= (char *)str
->Data
;
116 for(i
=0; i
<str
->Length
; i
++) {
121 /* declare a CSSM_DB_ATTRIBUTE_INFO with NAME_AS_STRING */
122 #define DB_ATTRIBUTE(name, type) \
123 { CSSM_DB_ATTRIBUTE_NAME_AS_STRING, \
125 CSSM_DB_ATTRIBUTE_FORMAT_ ## type \
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
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),
145 #define NUM_CRL_ATTRS \
146 (sizeof(x509CrlRecordAttrs) / sizeof(x509CrlRecordAttrs[0]))
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
155 /* free attribute(s) allocated by DL */
156 static void freeAttrs(
157 CSSM_DB_ATTRIBUTE_DATA
*attrs
,
162 for(i
=0; i
<numAttrs
; i
++) {
163 CSSM_DB_ATTRIBUTE_DATA_PTR attrData
= &attrs
[i
];
165 for(j
=0; j
<attrData
->NumberOfValues
; j
++) {
166 CSSM_DATA_PTR data
= &attrData
->Value
[j
];
168 /* fault of DL, who said there was a value here */
169 printf("***freeAttrs screwup: NULL data\n");
172 APP_FREE(data
->Data
);
176 APP_FREE(attrData
->Value
);
177 attrData
->Value
= NULL
;
182 * Compare two CSSM_TIMESTRINGs. Returns:
191 for(unsigned dex
=0; dex
<CSSM_TIME_STRLEN
; dex
++, t1
++, t2
++) {
198 /* else same, on to next byte */
205 * everything we know or care about a CRL.
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
217 CSSM_DATA_PTR
fetchValidAttr(
226 /* print the printable name + '\n' to stdout */
230 const char *updateTime
,
231 const char *staleTime
,
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
242 * Actual CRL, optionally fetched from DB if doing a full crypto verify
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
; };
253 /* member variables which are read-only subsequent to construction */
254 CSSM_DL_DB_HANDLE mDlDbHand
;
258 * contents APP_MALLOCd by DL
259 * contents APP_FREEd by our destructor
261 CSSM_DB_ATTRIBUTE_DATA mAttrData
[NUM_CRL_ATTRS
];
264 * For possible use in CSSM_DL_DataDelete
265 * Our destructor does CSSM_DL_FreeUniqueRecord
267 CSSM_DB_UNIQUE_RECORD_PTR mRecord
;
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),
286 mCrlBlob
.Data
= NULL
;
289 memmove(mAttrData
, attrData
,
290 sizeof(CSSM_DB_ATTRIBUTE_DATA
) * NUM_CRL_ATTRS
);
295 freeAttrs(&mAttrData
[0], NUM_CRL_ATTRS
);
296 CSSM_DL_FreeUniqueRecord(mDlDbHand
, mRecord
);
298 APP_FREE(mCrlBlob
.Data
);
303 * Is attribute at specified index present with one value? Returns the
304 * value if so, else returns NULL.
306 CSSM_DATA_PTR
CrlInfo::fetchValidAttr(
309 if(mAttrData
[attrDex
].NumberOfValues
!= 1) {
312 return mAttrData
[attrDex
].Value
;
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.
319 int CrlInfo::fetchIntAttr(
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;
329 rtn
= cuDER_ToInt(val
);
335 * See if two CRLs have same issuer. Requires (and verifies) that both
336 * issuer attrs are well formed.
338 bool CrlInfo::isSameIssuer(
341 CSSM_DATA_PTR thisIssuer
= fetchValidAttr(ATTR_DEX_ISSUER
);
342 if(thisIssuer
== NULL
) {
345 CSSM_DATA_PTR otherIssuer
= other
->fetchValidAttr(ATTR_DEX_ISSUER
);
346 if(otherIssuer
== NULL
) {
349 return cuCompareCssmData(thisIssuer
, otherIssuer
) ? true : false;
352 /* Print a CRL's PrintName attr */
353 void CrlInfo::printName()
355 CSSM_DATA_PTR val
= fetchValidAttr(ATTR_DEX_PRINT_NAME
);
357 printf("X509 CRL\n");
366 * Given time strings representing 'update time' and 'stale time',
367 * calculate mIsExpired and mIsStale.
369 void CrlInfo::validateTimes(
370 const char *updateTime
, // now - expireOverlap
371 const char *staleTime
, // now - staleTime
372 unsigned dex
) // for debug info
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;
382 printf("Crl %u NextUpdate : ", dex
); printString(nextUpdateData
);
385 char *nextUpdate
= (char *)nextUpdateData
->Data
;
386 if(compareTimes(nextUpdate
, updateTime
) < 0) {
387 dprintf("...CRL %u is expired\n", dex
);
389 if(compareTimes(nextUpdate
, staleTime
) < 0) {
390 dprintf("...CRL %u is stale\n", dex
);
393 /* note it can't be stale and not expired */
398 * Fetch attrs for all CRLs from DB. CRL blobs themselves are not fetched
399 * unless the fetchBlobs argument is asserted.
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
408 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs
;
409 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
411 CSSM_HANDLE resultHand
;
413 CSSM_DB_ATTRIBUTE_DATA attrData
[NUM_CRL_ATTRS
];
414 CSSM_DATA_PTR crlDataPtr
= NULL
;
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
];
426 recordAttrs
.DataRecordType
= CSSM_DL_DB_RECORD_X509_CRL
;
427 recordAttrs
.NumberOfAttributes
= NUM_CRL_ATTRS
;
428 recordAttrs
.AttributeData
= &attrData
[0];
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?
440 crlDataPtr
= &crlData
;
443 crtn
= CSSM_DL_DataGetFirst(dlDbHand
,
452 case CSSMERR_DL_ENDOFDATA
:
453 /* we're done here */
455 case CSSMERR_DL_INVALID_RECORDTYPE
:
456 /* this means that this keychain hasn't been initialized
457 * for CRL schema; treat it as empty. */
460 cuPrintError("DataGetFirst", crtn
);
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
;
470 /* now the rest of them */
472 crtn
= CSSM_DL_DataGetNext(dlDbHand
,
479 rtnCrlInfo
= (CrlInfo
**)realloc(rtnCrlInfo
,
480 sizeof(CrlInfo
*) * (numCrls
+ 1));
481 rtnCrlInfo
[numCrls
] = new CrlInfo(dlDbHand
, &attrData
[0], record
,
484 break; // and go again
485 case CSSMERR_DL_ENDOFDATA
:
486 /* normal termination */
489 cuPrintError("DataGetNext", crtn
);
497 * Validate each CRL's integrity (Not including expiration or stale time).
499 static void validateCrls(
506 for(unsigned dex
=0; dex
<numCrls
; dex
++) {
509 /* get CrlType, make sure it's acceptable */
512 if(crl
->fetchIntAttr(ATTR_DEX_CRL_TYPE
, i
)) {
516 case CSSM_CRL_TYPE_X_509v1
:
517 case CSSM_CRL_TYPE_X_509v2
:
521 printf("***bad CRL type (%u) on CRL %u\n", (unsigned)i
, dex
);
522 crl
->mIsBadlyFormed
= true;
526 /* ditto for encoding */
527 if(crl
->fetchIntAttr(ATTR_DEX_CRL_ENC
, i
)) {
531 case CSSM_CRL_ENCODING_BER
:
532 case CSSM_CRL_ENCODING_DER
:
536 printf("***bad CRL encoding (%u) on CRL %u\n",
538 crl
->mIsBadlyFormed
= true;
541 /* any other grounds for deletion? */
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.
551 static void cryptoValidateCrls(
555 CSSM_TP_HANDLE tpHand
,
556 CSSM_CSP_HANDLE cspHand
,
557 CSSM_CL_HANDLE clHand
,
558 CSSM_DL_HANDLE dlHand
)
561 const CSSM_DATA
*anchors
;
565 /* just snag these once */
566 ortn
= SecTrustGetCSSMAnchorCertificates(&anchors
, &anchorCount
);
568 printf("SecTrustGetCSSMAnchorCertificates returned %u\n", (int)ortn
);
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
,
579 NULL
, // CSSM_ACCESS_CREDENTIALS *AccessCred
580 NULL
, // void *OpenParameters
583 cuPrintError("CSSM_DL_DbOpen", crtn
);
584 printf("***Error opening intermediate cert file %s.\n", X509_CERT_DB
);
585 /* Oh well, keep trying */
588 certDb
.DLHandle
= dlHand
;
592 for(unsigned dex
=0; dex
<numCrls
; dex
++) {
594 crtn
= cuCrlVerify(tpHand
, clHand
, cspHand
,
600 case CSSMERR_APPLETP_CRL_EXPIRED
:
601 /* special case, we'll handle this via its attrs */
606 printf("...CRL %u FAILED crypto verify\n", dex
);
608 crl
->mIsBadlyFormed
= true;
612 CSSM_DL_DbClose(certDb
);
616 * Calculate expired/stale state for all CRLs.
621 int expireOverlapSeconds
,
622 int staleTimeSeconds
)
624 if(expireOverlapSeconds
> staleTimeSeconds
) {
625 printf("***ExpireOverlap greater than StaleTime; aborting.\n");
628 char *updateTime
= cuTimeAtNowPlus(expireOverlapSeconds
, TIME_CSSM
);
629 char *staleTime
= cuTimeAtNowPlus(-staleTimeSeconds
, TIME_CSSM
);
631 dprintf("updateTime : %s\n", updateTime
);
632 dprintf("staleTime : %s\n", staleTime
);
634 for(unsigned dex
=0; dex
<numCrls
; dex
++) {
635 crlInfo
[dex
]->validateTimes(updateTime
, staleTime
, dex
);
637 APP_FREE(updateTime
);
643 * Mark all CRLs as stale (i.e., force them to be deleted later).
645 static void purgeAllCrls(
650 for(unsigned dex
=0; dex
<numCrls
; dex
++) {
651 CrlInfo
*crl
= crlInfo
[dex
];
652 crl
->mIsExpired
= true;
653 crl
->mIsStale
= true;
658 * Delete all stale and badly formed CRLs from cache.
660 static void deleteBadCrls(
667 for(unsigned dex
=0; dex
<numCrls
; dex
++) {
671 * expired is not grounds for deletion; mIsStale is.
673 if(crl
->mIsBadlyFormed
|| crl
->mIsStale
) {
674 if(verbose
|| DEBUG_PRINT
) {
675 printf("...deleting CRL %u from ", dex
);
678 CSSM_RETURN crtn
= CSSM_DL_DataDelete(crl
->dlDbHand(),
681 cuPrintError("CSSM_DL_DataDelete", crtn
);
688 * For each expired CRL, fetch a new one if we don't have a current
689 * CRL from the same place.
691 static void refreshExpiredCrls(
694 CSSM_CL_HANDLE clHand
,
701 for(unsigned dex
=0; dex
<numCrls
; dex
++) {
704 if(!crl
->mIsExpired
|| crl
->mRefreshed
) {
708 /* do we have one for the same issuer that's current? */
710 for(unsigned i
=0; i
<numCrls
; i
++) {
715 CrlInfo
*checkCrl
= crlInfo
[i
];
716 if(checkCrl
->mIsBadlyFormed
) {
717 /* forget this one */
720 if(checkCrl
->mIsExpired
&& !checkCrl
->mRefreshed
) {
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",
736 * Not all CRLs have a URI attribute, which is required for
739 CSSM_DATA_PTR uri
= crl
->fetchValidAttr(ATTR_DEX_URI
);
741 dprintf("Expired CRL with no URI at dex %u\n", dex
);
745 /* fetch a new one */
746 if(verbose
|| DEBUG_PRINT
) {
747 printf("...fetching new CRL from net to update CRL %u from ",
751 CSSM_RETURN crtn
= netFetch(*uri
, LT_Crl
, newCrl
);
753 cuPrintError("netFetch", crtn
);
757 /* store it in the DB */
758 crtn
= cuAddCrlToDb(crl
->dlDbHand(), clHand
, &newCrl
, uri
);
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
770 dprintf("...refreshed CRL added to DB to account "
771 "for expired CRL %u\n", dex
);
773 case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
:
774 dprintf("...refreshed CRL is a dup of CRL %u; skipping\n",
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.
789 crl
->mRefreshed
= true;
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.
799 CSSM_RETURN
openDatabase(
800 CSSM_DL_HANDLE dlHand
,
801 const char *dbFileName
,
803 CSSM_DB_HANDLE
&dbHand
, // RETURNED
804 bool &didCreate
) // RETURNED
808 /* try to open existing DB */
809 CSSM_RETURN crtn
= CSSM_DL_DbOpen(dlHand
,
812 CSSM_DB_ACCESS_READ
| CSSM_DB_ACCESS_WRITE
,
813 NULL
, // CSSM_ACCESS_CREDENTIALS *AccessCred
814 NULL
, // void *OpenParameters
819 case CSSMERR_DL_DATASTORE_DOESNOT_EXIST
:
820 /* proceed to create it */
823 cuPrintError("CSSM_DL_DbOpen", crtn
);
829 printf("...creating database %s\n", dbFileName
);
832 memset(&dbInfo
, 0, sizeof(CSSM_DBINFO
));
834 CssmAllocator
&alloc
= CssmAllocator::standard();
835 CssmClient::AclFactory::PasswordChangeCredentials
pCreds((StringData(dbFileName
)), alloc
);
836 const AccessCredentials
* aa
= pCreds
;
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
;
846 const ResourceControlContext
rcc(protoType
, const_cast<AccessCredentials
*>(aa
));
848 crtn
= CSSM_DL_DbCreate(dlHand
,
852 CSSM_DB_ACCESS_PRIVILEGED
,
853 &rcc
, // CredAndAclEntry
854 NULL
, // OpenParameters
857 cuPrintError("CSSM_DL_DbCreate", crtn
);
861 /* one more thing: make it world writable by convention */
862 if(chmod(dbFileName
, 0666)) {
864 crtn
= CSSMERR_DL_DB_LOCKED
;
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
876 static int writeFetchedItem(
878 const CSSM_DATA
*itemData
,
879 const CSSM_DATA
*uriData
)
881 if(lfType
== LT_Cert
) {
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
890 CSSM_DL_DB_HANDLE dlDbHand
= {0, 0};
891 CSSM_CL_HANDLE clHand
= 0;
896 clHand
= cuClStartup();
900 /* subsequent errors to done: */
901 dlDbHand
.DLHandle
= cuDlStartup();
902 if(dlDbHand
.DLHandle
== 0) {
906 crtn
= openDatabase(dlDbHand
.DLHandle
,
912 dprintf("***Error opening keychain %s. Aborting.\n", CRL_CACHE_DB
);
917 /* store it in the DB */
918 crtn
= cuAddCrlToDb(dlDbHand
, clHand
, itemData
, uriData
);
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.
932 dprintf("...fetched CRL added to DB\n");
934 case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
:
935 dprintf("...fetched CRL is a dup; skipping\n");
938 /* specific error logged by cuAddCrlToDb() */
939 dprintf("Error writing CRL to cache\n");
944 if(dlDbHand
.DBHandle
) {
945 CSSM_DL_DbClose(dlDbHand
);
947 if(dlDbHand
.DLHandle
) {
948 CSSM_ModuleDetach(dlDbHand
.DLHandle
);
951 CSSM_ModuleDetach(clHand
);
956 * Fetch a CRL or Cert from net; write it to a file.
958 int fetchItemFromNet(
961 char *outFileName
, // NULL indicates write to stdout
964 const CSSM_DATA uriData
= {strlen(URI
) + 1, (uint8
*)URI
};
969 dprintf("fetchItemFromNet %s outFile %s\n",
970 URI
, outFileName
? outFileName
: "stdout");
972 /* netFetch deals with NULL-terminated string */
973 uriData
.Data
[uriData
.Length
- 1] = 0;
974 crtn
= netFetch(uriData
, lfType
, item
);
976 cuPrintError("netFetch", crtn
);
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
) {
992 irtn
= writeFile(outFileName
, item
.Data
, item
.Length
);
997 if((irtn
== 0) && writeToCache
) {
998 irtn
= writeFetchedItem(lfType
, &item
, &uriData
);
1001 dprintf("fetchItemFromNet returning %d\n", irtn
);
1005 int main(int argc
, char **argv
)
1008 CSSM_DL_DB_HANDLE dlDbHand
;
1009 CSSM_CL_HANDLE clHand
;
1010 CSSM_CSP_HANDLE cspHand
= 0;
1011 CSSM_TP_HANDLE tpHand
= 0;
1014 bool didCreate
= false;
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
;
1026 LF_Type lfType
= LT_Crl
;
1027 char *outFileName
= NULL
;
1028 bool writeToCache
= true;
1034 switch(argv
[1][0]) {
1051 /* refresh options */
1052 for(arg
=optArg
; arg
<argc
; arg
++) {
1056 if(argp
[1] != '=') {
1059 staleDays
= atoi(&argp
[2]);
1062 if(argp
[1] != '=') {
1065 expireOverlapSeconds
= atoi(&argp
[2]);
1071 fullCryptoValidation
= true;
1074 if(argp
[1] != '=') {
1077 dbFileName
= &argp
[2];
1080 writeToCache
= false;
1083 if(argp
[1] != '=') {
1086 outFileName
= &argp
[2];
1095 if(argv
[1][0] != 'r') {
1096 return fetchItemFromNet(lfType
, uri
, outFileName
, writeToCache
);
1099 dprintf("...staleDays %d expireOverlapSeconds %d\n",
1100 staleDays
, expireOverlapSeconds
);
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.
1108 dlDbHand
.DLHandle
= cuDlStartup();
1109 if(dlDbHand
.DLHandle
== 0) {
1112 crtn
= openDatabase(dlDbHand
.DLHandle
,
1118 printf("***Error opening keychain %s. Aborting.\n", dbFileName
);
1123 /* New, empty keychain. I guarantee you we're done. */
1124 CSSM_DL_DbClose(dlDbHand
);
1125 CSSM_ModuleDetach(dlDbHand
.DLHandle
);
1129 clHand
= cuClStartup();
1133 if(fullCryptoValidation
) {
1134 /* also need TP, CSP */
1135 cspHand
= cuCspStartup(CSSM_TRUE
);
1139 tpHand
= cuTpStartup();
1145 /* fetch all CRLs from the keychain */
1148 crtn
= fetchAllCrls(dlDbHand
, fullCryptoValidation
, crlInfo
, numCrls
);
1150 printf("***Error reading CRLs from %s. Aborting.\n", dbFileName
);
1153 dprintf("...%u CRLs found\n", numCrls
);
1155 /* basic validation */
1156 validateCrls(crlInfo
, numCrls
, verbose
);
1158 /* Optional full crypto validation */
1159 if(fullCryptoValidation
) {
1160 cryptoValidateCrls(crlInfo
, numCrls
, verbose
,
1161 tpHand
, cspHand
, clHand
, dlDbHand
.DLHandle
);
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");
1172 /* mark all of them stale */
1173 purgeAllCrls(crlInfo
, numCrls
, verbose
);
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.
1182 deleteBadCrls(crlInfo
, numCrls
, verbose
);
1184 /* refresh the out-of-date CRLs */
1185 refreshExpiredCrls(crlInfo
, numCrls
, clHand
, verbose
);
1188 for(unsigned dex
=0; dex
<numCrls
; dex
++) {
1189 delete crlInfo
[dex
];
1192 CSSM_DL_DbClose(dlDbHand
);
1193 CSSM_ModuleDetach(dlDbHand
.DLHandle
);
1194 CSSM_ModuleDetach(clHand
);
1196 CSSM_ModuleDetach(tpHand
);
1199 CSSM_ModuleDetach(cspHand
);