/* * Copyright (c) 2003 Apple Computer, Inc. All Rights Reserved. * * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please * obtain a copy of the License at http://www.apple.com/publicsource and * read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. */ /* * Examine the CRLs in the system CRL cache, looking for expired CRLs * for which we don't have current valid entries. Perform net fetch for * all such entries to get up-to-date entries. Purge entries older * than specified date (i.e., "stale" CRLs). * * Terminology used here: * * 'nowTime' is the absolute current time. * 'updateTime' is the time at which we evaluate a CRL's NextUpdate * attribute to determine whether a CRL has expired. This is * generally subsequent to nowTime. * 'expired' means that a CRL's NextUpdate time has passed, relative * to updateTime, and that we need to fetch a new CRL to replace * the expired CRL. * 'expireOverlap' is (nowTime - updateTime) in seconds. It's the * distance into the future at which we evaluate a CRL's expiration * status. * 'stale' means that a CRL is so old that it should be deleted from * the cache. * 'staleTime' is maximum age (relative to nowTime) that a CRL can * achieve in cache before being deemed stale. StaleTime is always * greater than expireOverlap (i.e., if a CRL is stale, it MUST be * expired, but a CRL can be expired without being stale). * * CRLs are only deleted from cache if they are stale; multiple * CRLs from one CA may exist in cache at a given time but (generally) * only one of them is not expired. * * expireOverlap and staleTime have defaults which can be overridden * via command line arguments. */ #include #include #include #include #include #include #include #include #include #include "ldapFetch.h" #include #include #include #include #include #define DEFAULT_STALE_DAYS 10 #define DEFAULT_EXPIRE_OVERLAP_SECONDS 3600 #define SECONDS_PER_DAY (60 * 60 * 24) #define CRL_CACHE_DB "/var/db/crls/crlcache.db" #define X509_CERT_DB "/System/Library/Keychains/X509Certificates" #ifdef NDEBUG #define DEBUG_PRINT 0 #else #define DEBUG_PRINT 1 #endif #if DEBUG_PRINT #define dprintf(args...) fprintf(stderr, args) #else #define dprintf(args...) #endif static void usage(char **argv) { printf("Usage\n"); printf("Refresh : %s r [options]\n", argv[0]); printf("Fetch CRL : %s f URI [options]\n", argv[0]); printf("Fetch cert : %s F URI [options]\n", argv[0]); printf("Refresh options:\n"); printf(" s=stale_period in DAYS; default=%d\n", DEFAULT_STALE_DAYS); printf(" o=expire_overlap in SECONDS; default=%d\n", DEFAULT_EXPIRE_OVERLAP_SECONDS); printf(" p (Purge all entries, ensuring refresh with fresh CRLs)\n"); printf(" f (Full crypto CRL verification)\n"); printf(" k=keychainName (default=%s\n", CRL_CACHE_DB); printf(" v(erbose)\n"); printf("Fetch options:\n"); printf(" F=outFileName (default is stdout)\n"); printf(" n (no write to cache after fetch)\n"); exit(1); } /* * Print string. Null terminator is not assumed. */ static void printString( const CSSM_DATA *str) { unsigned i; char *cp = (char *)str->Data; for(i=0; iLength; i++) { printf("%c", *cp++); } } /* declare a CSSM_DB_ATTRIBUTE_INFO with NAME_AS_STRING */ #define DB_ATTRIBUTE(name, type) \ { CSSM_DB_ATTRIBUTE_NAME_AS_STRING, \ {#name}, \ CSSM_DB_ATTRIBUTE_FORMAT_ ## type \ } /* The CRL DB attributes we care about*/ /* Keep these positions in sync with ATTR_DEX_xxx, below */ static const CSSM_DB_ATTRIBUTE_INFO x509CrlRecordAttrs[] = { DB_ATTRIBUTE(CrlType, UINT32), // 0 DB_ATTRIBUTE(CrlEncoding, UINT32), // 1 DB_ATTRIBUTE(PrintName, BLOB), // 2 DB_ATTRIBUTE(Issuer, BLOB), // 3 DB_ATTRIBUTE(NextUpdate, BLOB), // 4 DB_ATTRIBUTE(URI, BLOB), // 5 /* we don't use these */ // DB_ATTRIBUTE(ThisUpdate, BLOB), // 4 // DB_ATTRIBUTE(DeltaCrlNumber, UINT32) // DB_ATTRIBUTE(Alias, BLOB), // DB_ATTRIBUTE(CrlNumber, UINT32), }; #define NUM_CRL_ATTRS \ (sizeof(x509CrlRecordAttrs) / sizeof(x509CrlRecordAttrs[0])) #define ATTR_DEX_CRL_TYPE 0 #define ATTR_DEX_CRL_ENC 1 #define ATTR_DEX_PRINT_NAME 2 #define ATTR_DEX_ISSUER 3 #define ATTR_DEX_NEXT_UPDATE 4 #define ATTR_DEX_URI 5 /* free attribute(s) allocated by DL */ static void freeAttrs( CSSM_DB_ATTRIBUTE_DATA *attrs, unsigned numAttrs) { unsigned i; for(i=0; iNumberOfValues; j++) { CSSM_DATA_PTR data = &attrData->Value[j]; if(data == NULL) { /* fault of DL, who said there was a value here */ printf("***freeAttrs screwup: NULL data\n"); return; } APP_FREE(data->Data); data->Data = NULL; data->Length = 0; } APP_FREE(attrData->Value); attrData->Value = NULL; } } /* * Compare two CSSM_TIMESTRINGs. Returns: * -1 if t1 < t2 * 0 if t1 == t2 * 1 if t1 > t2 */ int compareTimes( const char *t1, const char *t2) { for(unsigned dex=0; dex *t2) { return 1; } if(*t1 < *t2) { return -1; } /* else same, on to next byte */ } /* equal */ return 0; } /* * everything we know or care about a CRL. */ class CrlInfo { public: CrlInfo( CSSM_DL_DB_HANDLE dlDbHand, CSSM_DB_ATTRIBUTE_DATA *attrData, // [NUM_CRL_ATTRS] CSSM_DB_UNIQUE_RECORD_PTR record, CSSM_DATA_PTR crlBlob); // optional ~CrlInfo(); CSSM_DATA_PTR fetchValidAttr( unsigned attrDex); int fetchIntAttr( unsigned dex, uint32 &rtn); bool isSameIssuer( CrlInfo *other); /* print the printable name + '\n' to stdout */ void printName(); void validateTimes( const char *updateTime, const char *staleTime, unsigned dex); /* state inferred from attributes, and maintained by * owner (not by us) */ bool mIsBadlyFormed; // general parse error bool mIsExpired; // compare to 'now' bool mIsStale; // compared to "staleTime' bool mRefreshed; // already refreshed /* * Actual CRL, optionally fetched from DB if doing a full crypto verify */ CSSM_DATA mCrlBlob; /* accessors for read-only member vars */ CSSM_DL_DB_HANDLE dlDbHand() { return mDlDbHand; } CSSM_DB_ATTRIBUTE_DATA_PTR attrData() { return &mAttrData[0]; } CSSM_DB_UNIQUE_RECORD_PTR record() { return mRecord; }; private: /* member variables which are read-only subsequent to construction */ CSSM_DL_DB_HANDLE mDlDbHand; /* * array of attr data * contents APP_MALLOCd by DL * contents APP_FREEd by our destructor */ CSSM_DB_ATTRIBUTE_DATA mAttrData[NUM_CRL_ATTRS]; /* * For possible use in CSSM_DL_DataDelete * Our destructor does CSSM_DL_FreeUniqueRecord */ CSSM_DB_UNIQUE_RECORD_PTR mRecord; }; CrlInfo::CrlInfo( CSSM_DL_DB_HANDLE dlDbHand, CSSM_DB_ATTRIBUTE_DATA *attrData, // [NUM_CRL_ATTRS] CSSM_DB_UNIQUE_RECORD_PTR record, CSSM_DATA_PTR crlBlob) // optional : mIsBadlyFormed(false), mIsExpired(false), mIsStale(false), mRefreshed(false), mDlDbHand(dlDbHand), mRecord(record) { if(crlBlob) { mCrlBlob = *crlBlob; } else { mCrlBlob.Data = NULL; mCrlBlob.Length = 0; } memmove(mAttrData, attrData, sizeof(CSSM_DB_ATTRIBUTE_DATA) * NUM_CRL_ATTRS); } CrlInfo::~CrlInfo() { freeAttrs(&mAttrData[0], NUM_CRL_ATTRS); CSSM_DL_FreeUniqueRecord(mDlDbHand, mRecord); if(mCrlBlob.Data) { APP_FREE(mCrlBlob.Data); } } /* * Is attribute at specified index present with one value? Returns the * value if so, else returns NULL. */ CSSM_DATA_PTR CrlInfo::fetchValidAttr( unsigned attrDex) { if(mAttrData[attrDex].NumberOfValues != 1) { return NULL; } return mAttrData[attrDex].Value; } /* * Fetch uint32 attr if it's there at specified attr index. * Returns non zero if it's not there and flags the CRL as bad. */ int CrlInfo::fetchIntAttr( unsigned dex, uint32 &rtn) { CSSM_DATA *val = fetchValidAttr(dex); if((val == NULL) || (val->Length != sizeof(uint32))) { dprintf("***Badly formed uint32 attr at dex %u\n", dex); mIsBadlyFormed = true; return 1; } rtn = cuDER_ToInt(val); return 0; } /* * See if two CRLs have same issuer. Requires (and verifies) that both * issuer attrs are well formed. */ bool CrlInfo::isSameIssuer( CrlInfo *other) { CSSM_DATA_PTR thisIssuer = fetchValidAttr(ATTR_DEX_ISSUER); if(thisIssuer == NULL) { return false; } CSSM_DATA_PTR otherIssuer = other->fetchValidAttr(ATTR_DEX_ISSUER); if(otherIssuer == NULL) { return false; } return cuCompareCssmData(thisIssuer, otherIssuer) ? true : false; } /* Print a CRL's PrintName attr */ void CrlInfo::printName() { CSSM_DATA_PTR val = fetchValidAttr(ATTR_DEX_PRINT_NAME); if(val == NULL) { printf("X509 CRL\n"); } else { printString(val); printf("\n"); } } /* * Given time strings representing 'update time' and 'stale time', * calculate mIsExpired and mIsStale. */ void CrlInfo::validateTimes( const char *updateTime, // now - expireOverlap const char *staleTime, // now - staleTime unsigned dex) // for debug info { CSSM_DATA *nextUpdateData = fetchValidAttr(ATTR_DEX_NEXT_UPDATE); if((nextUpdateData == NULL) || (nextUpdateData->Length != CSSM_TIME_STRLEN)) { printf("***Badly formed NextUpdate attr on CRL %u\n", dex); mIsBadlyFormed = true; return; } #if DEBUG_PRINT printf("Crl %u NextUpdate : ", dex); printString(nextUpdateData); printf("\n"); #endif char *nextUpdate = (char *)nextUpdateData->Data; if(compareTimes(nextUpdate, updateTime) < 0) { dprintf("...CRL %u is expired\n", dex); mIsExpired = true; if(compareTimes(nextUpdate, staleTime) < 0) { dprintf("...CRL %u is stale\n", dex); mIsStale = true; } /* note it can't be stale and not expired */ } } /* * Fetch attrs for all CRLs from DB. CRL blobs themselves are not fetched * unless the fetchBlobs argument is asserted. */ static CSSM_RETURN fetchAllCrls( CSSM_DL_DB_HANDLE dlDbHand, bool fetchBlobs, // fetch actual CRL data CrlInfo **&rtnCrlInfo, // RETURNED unsigned &numCrls) // RETURNED { CSSM_QUERY query; CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; CSSM_DB_UNIQUE_RECORD_PTR record = NULL; CSSM_RETURN crtn; CSSM_HANDLE resultHand; unsigned attrDex; CSSM_DB_ATTRIBUTE_DATA attrData[NUM_CRL_ATTRS]; CSSM_DATA_PTR crlDataPtr = NULL; CSSM_DATA crlData; numCrls = 0; rtnCrlInfo = NULL; /* build an ATTRIBUTE_DATA array from list attrs */ memset(attrData, 0, sizeof(CSSM_DB_ATTRIBUTE_DATA) * NUM_CRL_ATTRS); for(attrDex=0; attrDexfetchIntAttr(ATTR_DEX_CRL_TYPE, i)) { continue; } switch(i) { case CSSM_CRL_TYPE_X_509v1: case CSSM_CRL_TYPE_X_509v2: /* OK */ break; default: printf("***bad CRL type (%u) on CRL %u\n", (unsigned)i, dex); crl->mIsBadlyFormed = true; continue; } /* ditto for encoding */ if(crl->fetchIntAttr(ATTR_DEX_CRL_ENC, i)) { continue; } switch(i) { case CSSM_CRL_ENCODING_BER: case CSSM_CRL_ENCODING_DER: /* OK */ break; default: printf("***bad CRL encoding (%u) on CRL %u\n", (unsigned)i, dex); crl->mIsBadlyFormed = true; continue; } /* any other grounds for deletion? */ } } /* * Perform full crypto CRL validation. * We use the system-wide intermediate cert keychain here, but do * NOT use the CRL cache we're working on (or any other), since * we dont' really want to trust anything at this point. */ static void cryptoValidateCrls( CrlInfo **crlInfo, unsigned numCrls, bool verbose, CSSM_TP_HANDLE tpHand, CSSM_CSP_HANDLE cspHand, CSSM_CL_HANDLE clHand, CSSM_DL_HANDLE dlHand) { CrlInfo *crl; const CSSM_DATA *anchors; uint32 anchorCount; OSStatus ortn; /* just snag these once */ ortn = SecTrustGetCSSMAnchorCertificates(&anchors, &anchorCount); if(ortn) { printf("SecTrustGetCSSMAnchorCertificates returned %u\n", (int)ortn); return; } /* and the system-wide intermediate certs */ CSSM_DL_DB_HANDLE certDb; CSSM_DL_DB_HANDLE_PTR certDbPtr = NULL; CSSM_RETURN crtn = CSSM_DL_DbOpen(dlHand, X509_CERT_DB, NULL, // DbLocation CSSM_DB_ACCESS_READ, NULL, // CSSM_ACCESS_CREDENTIALS *AccessCred NULL, // void *OpenParameters &certDb.DBHandle); if(crtn) { cuPrintError("CSSM_DL_DbOpen", crtn); printf("***Error opening intermediate cert file %s.\n", X509_CERT_DB); /* Oh well, keep trying */ } else { certDb.DLHandle = dlHand; certDbPtr = &certDb; } for(unsigned dex=0; dexmCrlBlob, certDbPtr, anchors, anchorCount); switch(crtn) { case CSSMERR_APPLETP_CRL_EXPIRED: /* special case, we'll handle this via its attrs */ case CSSM_OK: break; default: if(verbose) { printf("...CRL %u FAILED crypto verify\n", dex); } crl->mIsBadlyFormed = true; break; } } CSSM_DL_DbClose(certDb); } /* * Calculate expired/stale state for all CRLs. */ int calcCurrent( CrlInfo **crlInfo, unsigned numCrls, int expireOverlapSeconds, int staleTimeSeconds) { if(expireOverlapSeconds > staleTimeSeconds) { printf("***ExpireOverlap greater than StaleTime; aborting.\n"); return 1; } char *updateTime = cuTimeAtNowPlus(expireOverlapSeconds, TIME_CSSM); char *staleTime = cuTimeAtNowPlus(-staleTimeSeconds, TIME_CSSM); dprintf("updateTime : %s\n", updateTime); dprintf("staleTime : %s\n", staleTime); for(unsigned dex=0; dexvalidateTimes(updateTime, staleTime, dex); } APP_FREE(updateTime); APP_FREE(staleTime); return 0; } /* * Mark all CRLs as stale (i.e., force them to be deleted later). */ static void purgeAllCrls( CrlInfo **crlInfo, unsigned numCrls, bool verbose) { for(unsigned dex=0; dexmIsExpired = true; crl->mIsStale = true; } } /* * Delete all stale and badly formed CRLs from cache. */ static void deleteBadCrls( CrlInfo **crlInfo, unsigned numCrls, bool verbose) { CrlInfo *crl; for(unsigned dex=0; dexmIsBadlyFormed || crl->mIsStale) { if(verbose || DEBUG_PRINT) { printf("...deleting CRL %u from ", dex); crl->printName(); } CSSM_RETURN crtn = CSSM_DL_DataDelete(crl->dlDbHand(), crl->record()); if(crtn) { cuPrintError("CSSM_DL_DataDelete", crtn); } } } } /* * For each expired CRL, fetch a new one if we don't have a current * CRL from the same place. */ static void refreshExpiredCrls( CrlInfo **crlInfo, unsigned numCrls, CSSM_CL_HANDLE clHand, bool verbose) { CrlInfo *crl; bool haveCurrent; CSSM_DATA newCrl; for(unsigned dex=0; dexmIsExpired || crl->mRefreshed) { continue; } /* do we have one for the same issuer that's current? */ haveCurrent = false; for(unsigned i=0; imIsBadlyFormed) { /* forget this one */ continue; } if(checkCrl->mIsExpired && !checkCrl->mRefreshed) { continue; } if(crl->isSameIssuer(checkCrl)) { /* have a match; this one's OK */ dprintf("up-to-date CRL at dex %u matching expired CRL %u\n", i, dex); haveCurrent = true; break; } } if(haveCurrent) { continue; } /* * Not all CRLs have a URI attribute, which is required for * refresh */ CSSM_DATA_PTR uri = crl->fetchValidAttr(ATTR_DEX_URI); if(uri == NULL) { dprintf("Expired CRL with no URI at dex %u\n", dex); continue; } /* fetch a new one */ if(verbose || DEBUG_PRINT) { printf("...fetching new CRL from net to update CRL %u from ", dex); crl->printName(); } CSSM_RETURN crtn = netFetch(*uri, LT_Crl, newCrl); if(crtn) { cuPrintError("netFetch", crtn); continue; } /* store it in the DB */ crtn = cuAddCrlToDb(crl->dlDbHand(), clHand, &newCrl, uri); /* * One special error case - UNIQUE_INDEX_DATA indicates that * the CRL we just fetched is already in the cache. This * can occur when expireOverlap is sufficiently large that * we decide to fetch before a CRL is actually expired. In * this case process as usual, avoiding any further updates * from this CA/URI. */ switch(crtn) { case CSSM_OK: dprintf("...refreshed CRL added to DB to account " "for expired CRL %u\n", dex); break; case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA: dprintf("...refreshed CRL is a dup of CRL %u; skipping\n", dex); break; default: continue; } /* * In case there are other CRLs still to be discovered * in our list which are a) expired, and b) from this same issuer, * we flag the current (expired) CRL as refreshed to ensure that * we don't do this fetch again. A lot easier than cooking up * a new CrlInfo object for the CRL we just fetched. */ crl->mRefreshed = true; } } /* * Open an existing keychain or create a new one. * This is a known "insecure" keychain/DB, since you don't need * to unlock it to add or remove CRLs to/from it. Thus if * we create it we use the filename as password. */ CSSM_RETURN openDatabase( CSSM_DL_HANDLE dlHand, const char *dbFileName, bool verbose, CSSM_DB_HANDLE &dbHand, // RETURNED bool &didCreate) // RETURNED { didCreate = false; /* try to open existing DB */ CSSM_RETURN crtn = CSSM_DL_DbOpen(dlHand, dbFileName, NULL, // DbLocation CSSM_DB_ACCESS_READ | CSSM_DB_ACCESS_WRITE, NULL, // CSSM_ACCESS_CREDENTIALS *AccessCred NULL, // void *OpenParameters &dbHand); switch(crtn) { case CSSM_OK: return CSSM_OK; case CSSMERR_DL_DATASTORE_DOESNOT_EXIST: /* proceed to create it */ break; default: cuPrintError("CSSM_DL_DbOpen", crtn); return crtn; } /* create new one */ if(verbose) { printf("...creating database %s\n", dbFileName); } CSSM_DBINFO dbInfo; memset(&dbInfo, 0, sizeof(CSSM_DBINFO)); CssmAllocator &alloc = CssmAllocator::standard(); CssmClient::AclFactory::PasswordChangeCredentials pCreds((StringData(dbFileName)), alloc); const AccessCredentials* aa = pCreds; // @@@ Create a nice wrapper for building the default AclEntryPrototype. TypedList subject(alloc, CSSM_ACL_SUBJECT_TYPE_ANY); AclEntryPrototype protoType(subject); AuthorizationGroup &authGroup = protoType.authorization(); CSSM_ACL_AUTHORIZATION_TAG tag = CSSM_ACL_AUTHORIZATION_ANY; authGroup.NumberOfAuthTags = 1; authGroup.AuthTags = &tag; const ResourceControlContext rcc(protoType, const_cast(aa)); crtn = CSSM_DL_DbCreate(dlHand, dbFileName, NULL, // DbLocation &dbInfo, CSSM_DB_ACCESS_PRIVILEGED, &rcc, // CredAndAclEntry NULL, // OpenParameters &dbHand); if(crtn) { cuPrintError("CSSM_DL_DbCreate", crtn); return crtn; } else { /* one more thing: make it world writable by convention */ if(chmod(dbFileName, 0666)) { perror(dbFileName); crtn = CSSMERR_DL_DB_LOCKED; } didCreate = true; } return crtn; } /* * Add CRL fetched from net to local cache, used only by fetchItemFromNet. * Note we're not dealing with fetched certs here; they are not * stored on the fly. */ static int writeFetchedItem( LF_Type lfType, const CSSM_DATA *itemData, const CSSM_DATA *uriData) { if(lfType == LT_Cert) { return 0; } /* * The awkward part of this operation is that we have to open a DLDB * (whose filename can only be hard coded at this point) and attach * to the CL. */ CSSM_DL_DB_HANDLE dlDbHand = {0, 0}; CSSM_CL_HANDLE clHand = 0; CSSM_RETURN crtn; bool didCreate; int ourRtn = 0; clHand = cuClStartup(); if(clHand == 0) { return 1; } /* subsequent errors to done: */ dlDbHand.DLHandle = cuDlStartup(); if(dlDbHand.DLHandle == 0) { ourRtn = 1; goto done; } crtn = openDatabase(dlDbHand.DLHandle, CRL_CACHE_DB, false, // verbose dlDbHand.DBHandle, didCreate); if(crtn) { dprintf("***Error opening keychain %s. Aborting.\n", CRL_CACHE_DB); ourRtn = 1; goto done; } /* store it in the DB */ crtn = cuAddCrlToDb(dlDbHand, clHand, itemData, uriData); /* * One special error case - UNIQUE_INDEX_DATA indicates that * the CRL we just fetched is already in the cache. This * can occur as a result of a race condition between searching * for a CRL in the cache (currently done by the TP, who execs us) * and the fetch we just completed, if multiple tasks or threads are * searching for the same CRL. * Eventually this will be handled more robustly by all of the searching * and fetching being done in a daemon. */ switch(crtn) { case CSSM_OK: dprintf("...fetched CRL added to DB\n"); break; case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA: dprintf("...fetched CRL is a dup; skipping\n"); break; default: /* specific error logged by cuAddCrlToDb() */ dprintf("Error writing CRL to cache\n"); ourRtn = 1; break; } done: if(dlDbHand.DBHandle) { CSSM_DL_DbClose(dlDbHand); } if(dlDbHand.DLHandle) { CSSM_ModuleDetach(dlDbHand.DLHandle); } if(clHand) { CSSM_ModuleDetach(clHand); } return ourRtn; } /* * Fetch a CRL or Cert from net; write it to a file. */ int fetchItemFromNet( LF_Type lfType, const char *URI, char *outFileName, // NULL indicates write to stdout bool writeToCache) { const CSSM_DATA uriData = {strlen(URI) + 1, (uint8 *)URI}; CSSM_DATA item; CSSM_RETURN crtn; int irtn; dprintf("fetchItemFromNet %s outFile %s\n", URI, outFileName ? outFileName : "stdout"); /* netFetch deals with NULL-terminated string */ uriData.Data[uriData.Length - 1] = 0; crtn = netFetch(uriData, lfType, item); if(crtn) { cuPrintError("netFetch", crtn); return 1; } dprintf("fetchItemFromNet netFetch complete, %u bytes read\n", (unsigned)item.Length); if(outFileName == NULL) { irtn = write(STDOUT_FILENO, item.Data, item.Length); if(irtn != (int)item.Length) { irtn = errno; perror("write"); } else { irtn = 0; } } else { irtn = writeFile(outFileName, item.Data, item.Length); if(irtn) { perror(outFileName); } } if((irtn == 0) && writeToCache) { irtn = writeFetchedItem(lfType, &item, &uriData); } free(item.Data); dprintf("fetchItemFromNet returning %d\n", irtn); return irtn; } int main(int argc, char **argv) { CSSM_RETURN crtn; CSSM_DL_DB_HANDLE dlDbHand; CSSM_CL_HANDLE clHand; CSSM_CSP_HANDLE cspHand = 0; CSSM_TP_HANDLE tpHand = 0; int arg; char *argp; bool didCreate = false; int optArg; /* user-specified variables */ bool verbose = false; bool purgeAll = false; bool fullCryptoValidation = false; int staleDays = DEFAULT_STALE_DAYS; int expireOverlapSeconds = DEFAULT_EXPIRE_OVERLAP_SECONDS; char *dbFileName = CRL_CACHE_DB; /* fetch options */ LF_Type lfType = LT_Crl; char *outFileName = NULL; bool writeToCache = true; char *uri = NULL; if(argc < 2) { usage(argv); } switch(argv[1][0]) { case 'F': lfType = LT_Cert; /* and drop thru */ case 'f': if(argc < 3) { usage(argv); } uri = argv[2]; optArg = 3; break; case 'r': optArg = 2; break; default: usage(argv); } /* refresh options */ for(arg=optArg; arg