1/* 2 * Copyright (c) 2004-2012 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * ocspdDb.cpp - OCSP daemon database 26 */ 27 28#if OCSP_DEBUG 29#define OCSP_USE_SYSLOG 1 30#endif 31#include "ocspdDb.h" 32#include "attachCommon.h" 33#include <security_ocspd/ocspdDbSchema.h> 34#include <security_ocspd/ocspdDebug.h> 35#include <security_ocspd/ocspResponse.h> 36#include <security_ocspd/ocspdUtils.h> 37#include <Security/Security.h> 38#include <security_utilities/utilities.h> 39#include <security_cdsa_utils/cuCdsaUtils.h> 40#include <security_utilities/globalizer.h> 41#include <security_utilities/threading.h> 42#include <security_utilities/logging.h> 43#include <vproc.h> 44 45class TransactionLock 46{ 47private: 48 vproc_transaction_t mHandle; 49 50public: 51 TransactionLock() {mHandle = vproc_transaction_begin(NULL);} 52 ~TransactionLock() {vproc_transaction_end(NULL, mHandle);} 53}; 54 55 56 57#define OCSP_DB_FILE "/private/var/db/crls/ocspcache.db" 58 59/* max default TTL currently 24 hours */ 60#define OCSPD_CACHE_TTL (60.0 * 60.0 * 24.0) 61 62#pragma mark ---- OCSPD database singleton ---- 63 64class OcspdDatabase 65{ 66 NOCOPY(OcspdDatabase) 67public: 68 OcspdDatabase(); 69 ~OcspdDatabase(); 70 71 /* methods associated with public API of this module */ 72 bool lookup( 73 SecAsn1CoderRef coder, 74 const CSSM_DATA &certID, 75 const CSSM_DATA *localResponder, // optional 76 CSSM_DATA &derResp); // RETURNED 77 78 void addResponse( 79 const CSSM_DATA &ocspResp, // DER encoded SecAsn1OCSPResponse 80 const CSSM_DATA &URI); // where it came from 81 82 void flushCertID( 83 const CSSM_DATA &certID); 84 85 void flushStale(); 86 87private: 88 CSSM_RETURN dlAttach(); 89 CSSM_RETURN dbCreate(); 90 CSSM_RETURN dbOpen(bool doCreate); 91 92 /* see implementations for comments */ 93 bool validateRecord( 94 const CSSM_DATA &certID, 95 const CSSM_DATA &recordData, // raw OCSP response 96 const CSSM_DATA &expireTime, // the attribute data 97 CSSM_DB_UNIQUE_RECORD_PTR recordPtr); 98 99 CSSM_RETURN lookupPriv( 100 /* search predicates, both optional */ 101 const CSSM_DATA *certID, 102 const CSSM_DATA *localResponder, 103 104 /* always returned on success */ 105 CSSM_HANDLE_PTR resultHand, 106 CSSM_DB_UNIQUE_RECORD_PTR *recordPtr, 107 108 /* optionally returned */ 109 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR attrData, 110 CSSM_DATA_PTR data); // i.e., an encoded OCSP response 111 112 113 /* everything this module does is protected by this global lock */ 114 Mutex mLock; 115 CSSM_DL_DB_HANDLE mDlDbHandle; 116}; 117 118OcspdDatabase::OcspdDatabase() 119{ 120 mDlDbHandle.DLHandle = 0; 121 mDlDbHandle.DBHandle = 0; 122} 123 124/* I believe that this code actually never executes */ 125OcspdDatabase::~OcspdDatabase() 126{ 127 if(mDlDbHandle.DBHandle != 0) { 128 CSSM_DL_DbClose(mDlDbHandle); 129 mDlDbHandle.DBHandle = 0; 130 } 131 if(mDlDbHandle.DLHandle != 0) { 132 CSSM_ModuleDetach(mDlDbHandle.DLHandle); 133 CSSM_ModuleUnload(&gGuidAppleFileDL, NULL, NULL); 134 mDlDbHandle.DLHandle = 0; 135 } 136} 137 138/* 139 * Ensure we're attached to AppleFileDL. Caller must hold mLock. 140 */ 141CSSM_RETURN OcspdDatabase::dlAttach() 142{ 143 if(mDlDbHandle.DLHandle != 0) { 144 return CSSM_OK; 145 } 146 ocspdDbDebug("OcspdDatabase: attaching to DL"); 147 mDlDbHandle.DLHandle = attachCommon(&gGuidAppleFileDL, CSSM_SERVICE_DL); 148 if(mDlDbHandle.DLHandle == 0) { 149 Syslog::alert("Error loading AppleFileDL"); 150 return CSSMERR_CSSM_ADDIN_LOAD_FAILED; 151 } 152 return CSSM_OK; 153} 154 155/* 156 * Create the database, which caller has determined does not exist. 157 * Caller must hold mLock. 158 */ 159CSSM_RETURN OcspdDatabase::dbCreate() 160{ 161 assert(mDlDbHandle.DLHandle != 0); 162 assert(mDlDbHandle.DBHandle == 0); 163 164 ocspdDbDebug("OcspdDatabase: creating DB"); 165 CSSM_DBINFO dbInfo; 166 memset(&dbInfo, 0, sizeof(dbInfo)); 167 dbInfo.NumberOfRecordTypes = 1; 168 dbInfo.IsLocal = CSSM_TRUE; // TBD - what does this mean? 169 dbInfo.AccessPath = NULL; // TBD 170 171 /* 172 * Alloc kNumOcspDbRelations elements for parsingModule, recordAttr, 173 * and recordIndex info arrays 174 */ 175 unsigned size = sizeof(CSSM_DB_PARSING_MODULE_INFO) * kNumOcspDbRelations; 176 dbInfo.DefaultParsingModules = (CSSM_DB_PARSING_MODULE_INFO_PTR)malloc(size); 177 memset(dbInfo.DefaultParsingModules, 0, size); 178 size = sizeof(CSSM_DB_RECORD_ATTRIBUTE_INFO) * kNumOcspDbRelations; 179 dbInfo.RecordAttributeNames = (CSSM_DB_RECORD_ATTRIBUTE_INFO_PTR)malloc(size); 180 memset(dbInfo.RecordAttributeNames, 0, size); 181 size = sizeof(CSSM_DB_RECORD_INDEX_INFO) * kNumOcspDbRelations; 182 dbInfo.RecordIndexes = (CSSM_DB_RECORD_INDEX_INFO_PTR)malloc(size); 183 memset(dbInfo.RecordIndexes, 0, size); 184 185 /* cook up attribute and index info for each relation */ 186 unsigned relation; 187 for(relation=0; relation<kNumOcspDbRelations; relation++) { 188 const OcspdDbRelationInfo *relp = &kOcspDbRelations[relation]; // source 189 CSSM_DB_RECORD_ATTRIBUTE_INFO_PTR attrInfo = 190 &dbInfo.RecordAttributeNames[relation]; // dest 1 191 CSSM_DB_RECORD_INDEX_INFO_PTR indexInfo = 192 &dbInfo.RecordIndexes[relation]; // dest 2 193 194 attrInfo->DataRecordType = relp->recordType; 195 attrInfo->NumberOfAttributes = relp->numberOfAttributes; 196 attrInfo->AttributeInfo = 197 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR>(relp->attrInfo); 198 199 indexInfo->DataRecordType = relp->recordType; 200 indexInfo->NumberOfIndexes = relp->numIndexes; 201 indexInfo->IndexInfo = const_cast<CSSM_DB_INDEX_INFO_PTR>(relp->indexInfo); 202 } 203 204 /* autocommit and mode */ 205 CSSM_APPLEDL_OPEN_PARAMETERS openParams; 206 memset(&openParams, 0, sizeof(openParams)); 207 openParams.length = sizeof(openParams); 208 openParams.version = CSSM_APPLEDL_OPEN_PARAMETERS_VERSION; 209 openParams.autoCommit = CSSM_TRUE; 210 /* ensure mode 644 */ 211 openParams.mask = kCSSM_APPLEDL_MASK_MODE; 212 openParams.mode = 0644; 213 214 CSSM_RETURN crtn; 215 crtn = CSSM_DL_DbCreate(mDlDbHandle.DLHandle, 216 OCSP_DB_FILE, // DbName is the same as file path 217 NULL, // DbLocation 218 &dbInfo, 219 CSSM_DB_ACCESS_READ | CSSM_DB_ACCESS_WRITE, 220 NULL, // CredAndAclEntry 221 &openParams, 222 &mDlDbHandle.DBHandle); 223 if(crtn) { 224 Syslog::alert("Error creating DB (%d)", crtn); 225 } 226 free(dbInfo.DefaultParsingModules); 227 free(dbInfo.RecordAttributeNames); 228 free(dbInfo.RecordIndexes); 229 return crtn; 230} 231 232/* 233 * Open, optionally creating. If !doCreate and DB doesn't exist, 234 * CSSMERR_DL_DATASTORE_DOESNOT_EXIST is returned. Any other error is 235 * a gross failure. 236 * 237 * Caller must hold mLock. 238 */ 239CSSM_RETURN OcspdDatabase::dbOpen(bool doCreate) 240{ 241 /* first ensure we're attached to the DL */ 242 CSSM_RETURN crtn = dlAttach(); 243 if(crtn) { 244 return crtn; 245 } 246 if(mDlDbHandle.DBHandle != 0) { 247 return CSSM_OK; 248 } 249 crtn = CSSM_DL_DbOpen(mDlDbHandle.DLHandle, 250 OCSP_DB_FILE, 251 NULL, // DbLocation 252 CSSM_DB_ACCESS_READ | CSSM_DB_ACCESS_WRITE, 253 NULL, // CSSM_ACCESS_CREDENTIALS *AccessCred 254 NULL, // void *OpenParameters 255 &mDlDbHandle.DBHandle); 256 switch(crtn) { 257 case CSSM_OK: 258 return CSSM_OK; 259 case CSSMERR_DL_DATASTORE_DOESNOT_EXIST: 260 if(!doCreate) { 261 /* just trying to read, doesn't exist */ 262 ocspdDbDebug("OcspdDatabase: read access, DB does not yet exist"); 263 return crtn; 264 } 265 else { 266 return dbCreate(); 267 } 268 case CSSMERR_DL_DATABASE_CORRUPT: 269 /* corrupt database; delete and recreate it */ 270 if(unlink(OCSP_DB_FILE)) { 271 Syslog::alert("Failed to remove OCSP cache (%d)", errno); 272 } 273 else { 274 Syslog::alert("Error opening OCSP cache (%d), recovering", crtn); 275 crtn = dbCreate(); 276 } 277 return crtn; 278 default: 279 Syslog::alert("Error opening OCSP cache (%d)", crtn); 280 return crtn; 281 } 282} 283 284/* 285 * Free CSSM_DB_ATTRIBUTE_DATA 286 */ 287static void freeAttrData( 288 CSSM_DB_ATTRIBUTE_DATA &attrData) 289{ 290 if(attrData.Value == NULL) { 291 return; 292 } 293 for(unsigned dex=0; dex<attrData.NumberOfValues; dex++) { 294 CSSM_DATA_PTR d = &attrData.Value[dex]; 295 if(d->Data) { 296 APP_FREE(d->Data); 297 } 298 } 299 APP_FREE(attrData.Value); 300} 301 302/* 303 * Validate a record found in the DB. Returns true if record is good to go. 304 * We delete the record if it's stale. 305 */ 306bool OcspdDatabase::validateRecord( 307 const CSSM_DATA &certID, 308 const CSSM_DATA &recordData, // raw OCSP response 309 const CSSM_DATA &expireTime, // the attribute data 310 CSSM_DB_UNIQUE_RECORD_PTR recordPtr) 311{ 312 /* 313 * First off, if the entire record is stale, we're done. 314 */ 315 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); 316 CFAbsoluteTime cfExpireTime = genTimeToCFAbsTime(&expireTime); 317 if(now >= cfExpireTime) { 318 ocspdDbDebug("OcspdDatabase::validateRecord: record EXPIRED"); 319 CSSM_DL_DataDelete(mDlDbHandle, recordPtr); 320 return false; 321 } 322 323 /* 324 * Parse: this should never fail since we did this when we first got the 325 * response, and we just made sure it isn't stale. 326 */ 327 OCSPResponse *ocspResp = NULL; 328 bool ourRtn = false; 329 try { 330 ocspResp = new OCSPResponse(recordData, OCSPD_CACHE_TTL); 331 } 332 catch(...) { 333 Syslog::alert("Error parsing stored record"); 334 return false; 335 } 336 337 /* 338 * Find the singleResponse for this certID. 339 */ 340 OCSPSingleResponse *singleResp = ocspResp->singleResponseFor(certID); 341 if(singleResp != NULL) { 342 CFAbsoluteTime nextUpdate = singleResp->nextUpdate(); 343 /* SingleResponses with no nextUpdate goes stale with the response as a whole */ 344 if((nextUpdate != NULL_TIME) && (now > nextUpdate)) { 345 ocspdDbDebug("OcspdDatabase::validateRecord: SingleResponse EXPIRED"); 346 } 347 else { 348 ourRtn = true; 349 } 350 delete singleResp; 351 } 352 else { 353 /* Not sure how this could happen */ 354 ocspdDbDebug("OcspdDatabase::validateRecord: SingleResponse NOT FOUND"); 355 } 356 delete ocspResp; 357 ocspdDbDebug("OcspdDatabase::validateRecord returning %s", 358 ourRtn ? "TRUE" : "FALSE"); 359 return ourRtn; 360} 361 362/* 363 * Basic private lookup. Key on any or all attrs. On success, always returns a 364 * CSSM_DB_UNIQUE_RECORD_PTR (which caller must either use to delete the 365 * record or free with CSSM_DL_FreeUniqueRecord()) *AND* a CSSM_HANDLE_PTR 366 * ResultHandle(which caller must free with CSSM_DL_DataAbortQuery, possibly 367 * after doing some CSSM_DL_DataGetNext() ops with it). On success, also 368 * optionally returns CSSM_DB_RECORD_ATTRIBUTE_DATA which caller must free 369 * via APP_FREE(). 370 * 371 * Caller holds mLock and has ensured the the DB is open. 372 */ 373CSSM_RETURN OcspdDatabase::lookupPriv( 374 /* search predicates, both optional */ 375 const CSSM_DATA *certID, 376 const CSSM_DATA *URI, 377 378 /* these two always returned on success */ 379 CSSM_HANDLE_PTR resultHandPtr, 380 CSSM_DB_UNIQUE_RECORD_PTR *recordPtr, 381 382 /* 383 * Optionally returned, in/out (in to specify which attrs, out as the 384 * attrs fetched) 385 */ 386 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR attrData, 387 388 /* optionally returned - an encoded OCSP response */ 389 CSSM_DATA_PTR data) 390{ 391 CSSM_QUERY query; 392 CSSM_SELECTION_PREDICATE predicate[2]; 393 CSSM_SELECTION_PREDICATE *predPtr = predicate; 394 CSSM_DB_ATTRIBUTE_INFO certIDAttr = OCSPD_DBATTR_CERT_ID; 395 CSSM_DB_ATTRIBUTE_INFO uriAttr = OCSPD_DBATTR_URI; 396 397 assert(resultHandPtr != NULL); 398 assert(recordPtr != NULL); 399 assert(mDlDbHandle.DBHandle != 0); 400 401 memset(&query, 0, sizeof(query)); 402 query.RecordType = OCSPD_DB_RECORDTYPE; 403 query.Conjunctive = CSSM_DB_NONE; 404 405 if(certID) { 406 predPtr->DbOperator = CSSM_DB_EQUAL; 407 predPtr->Attribute.Info = certIDAttr; 408 predPtr->Attribute.NumberOfValues = 1; 409 predPtr->Attribute.Value = const_cast<CSSM_DATA_PTR>(certID); 410 query.NumSelectionPredicates++; 411 query.SelectionPredicate = predicate; 412 predPtr++; 413 } 414 if(URI) { 415 predPtr->DbOperator = CSSM_DB_EQUAL; 416 predPtr->Attribute.Info = uriAttr; 417 predPtr->Attribute.NumberOfValues = 1; 418 predPtr->Attribute.Value = const_cast<CSSM_DATA_PTR>(URI); 419 query.NumSelectionPredicates++; 420 query.SelectionPredicate = predicate; 421 } 422 if(data) { 423 query.QueryFlags = CSSM_QUERY_RETURN_DATA; // FIXME - used? 424 } 425 if(query.NumSelectionPredicates > 1) { 426 query.Conjunctive = CSSM_DB_AND; 427 } 428 return CSSM_DL_DataGetFirst(mDlDbHandle, &query, resultHandPtr, 429 attrData, data, recordPtr); 430} 431 432/* methods associated with public API of this module */ 433bool OcspdDatabase::lookup( 434 SecAsn1CoderRef coder, 435 const CSSM_DATA &certID, 436 const CSSM_DATA *URI, // optional 437 CSSM_DATA &derResp) // RETURNED 438{ 439 TransactionLock tLock; 440 StLock<Mutex> _(mLock); 441 if(dbOpen(false)) { 442 return false; 443 } 444 445 CSSM_RETURN crtn; 446 CSSM_HANDLE resultHand; 447 CSSM_DB_UNIQUE_RECORD_PTR recordPtr; 448 CSSM_DATA resultData; // if found, free via APP_FREE() 449 bool foundIt = false; 450 451 /* set up to retrieve just the expiration time */ 452 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrData; 453 memset(&recordAttrData, 0, sizeof(recordAttrData)); 454 CSSM_DB_ATTRIBUTE_DATA attrData; 455 memset(&attrData, 0, sizeof(attrData)); 456 CSSM_DB_ATTRIBUTE_INFO expireInfo = OCSPD_DBATTR_EXPIRATION; 457 attrData.Info = expireInfo; 458 recordAttrData.DataRecordType = OCSPD_DB_RECORDTYPE; 459 recordAttrData.NumberOfAttributes = 1; 460 recordAttrData.AttributeData = &attrData; 461 462 crtn = lookupPriv(&certID, URI, &resultHand, &recordPtr, 463 &recordAttrData, &resultData); 464 if(crtn) { 465 ocspdDbDebug("OcspdDatabase::lookup: MISS"); 466 return false; 467 } 468 foundIt = validateRecord(certID, resultData, *attrData.Value, recordPtr); 469 /* done with attrs and the record itself regardless.... */ 470 freeAttrData(attrData); 471 CSSM_DL_FreeUniqueRecord(mDlDbHandle, recordPtr); 472 473 if(!foundIt) { 474 /* no good. free what we just got and try again */ 475 ocspdDbDebug("OcspdDatabase::lookup: invalid record (1)"); 476 if(resultData.Data) { 477 APP_FREE(resultData.Data); 478 resultData.Data = NULL; 479 resultData.Length = 0; 480 } 481 do { 482 crtn = CSSM_DL_DataGetNext(mDlDbHandle, resultHand, 483 &recordAttrData, &resultData, &recordPtr); 484 if(crtn) { 485 /* done, not found */ 486 break; 487 } 488 foundIt = validateRecord(certID, resultData, *attrData.Value, recordPtr); 489 ocspdDbDebug("OcspdDatabase::lookup: %s", 490 foundIt ? "HIT (2)" : "invalid record (2)"); 491 freeAttrData(attrData); 492 CSSM_DL_FreeUniqueRecord(mDlDbHandle, recordPtr); 493 if(!foundIt && (resultData.Data != NULL)) { 494 APP_FREE(resultData.Data); 495 resultData.Data = NULL; 496 resultData.Length = 0; 497 } 498 /* break on full success, or end of DB search */ 499 } while(!foundIt && (crtn == CSSM_OK)); 500 } 501 else { 502 ocspdDbDebug("OcspdDatabase::lookup: HIT (1)"); 503 } 504 CSSM_DL_DataAbortQuery(mDlDbHandle, resultHand); 505 if(foundIt) { 506 assert(resultData.Data != NULL); 507 SecAsn1AllocCopyItem(coder, &resultData, &derResp); 508 APP_FREE(resultData.Data); 509 } 510 return foundIt; 511} 512 513void OcspdDatabase::addResponse( 514 const CSSM_DATA &ocspResp, // DER encoded SecAsn1OCSPResponse 515 const CSSM_DATA &URI) 516{ 517 TransactionLock tLock; 518 StLock<Mutex> _(mLock); 519 ocspdDbDebug("addResponse: top"); 520 if(dbOpen(true)) { 521 return; 522 } 523 524 /* open it up... */ 525 OCSPResponse *resp = NULL; 526 try { 527 resp = new OCSPResponse(ocspResp, OCSPD_CACHE_TTL); 528 } 529 catch(...) { 530 ocspdDbDebug("addResponse: error parsing response"); 531 return; 532 } 533 if(resp->responseStatus() != RS_Success) { 534 /* e.g., RS_Unauthorized */ 535 ocspdDbDebug("addResponse: responseStatus %d, aborting", (int)resp->responseStatus()); 536 delete resp; 537 return; 538 } 539 540 /* 541 * Get expiration date in the form of the latest of all of the enclosed 542 * SingleResponse nextUpdate fields. 543 */ 544 CFAbsoluteTime expireTime = resp->expireTime(); 545 char expireStr[GENERAL_TIME_STRLEN+1]; 546 cfAbsTimeToGgenTime(expireTime, expireStr); 547 CSSM_DATA expireData = {GENERAL_TIME_STRLEN, (uint8 *)expireStr}; 548 CSSM_RETURN crtn; 549 550 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; 551 CSSM_DB_ATTRIBUTE_DATA attrData[OCSPD_NUM_DB_ATTRS]; 552 CSSM_DB_UNIQUE_RECORD_PTR recordPtr = NULL; 553 memset(&recordAttrs, 0, sizeof(recordAttrs)); 554 555 recordAttrs.DataRecordType = OCSPD_DB_RECORDTYPE; 556 recordAttrs.SemanticInformation = 0; // what's this for? 557 recordAttrs.NumberOfAttributes = OCSPD_NUM_DB_ATTRS; 558 recordAttrs.AttributeData = attrData; 559 560 /* 561 * Now fill in the attributes. CertID is unusual in that it can contain multiple 562 * values, one for each SingleResponse. 563 */ 564 SecAsn1CoderRef coder; // for tons of mallocs 565 SecAsn1CoderCreate(&coder); 566 const SecAsn1OCSPResponseData &respData = resp->responseData(); 567 unsigned numSingleResps = ocspdArraySize((const void **)respData.responses); 568 CSSM_DB_ATTRIBUTE_INFO oneAttr = OCSPD_DBATTR_CERT_ID; 569 CSSM_DB_ATTRIBUTE_INFO twoAttr = OCSPD_DBATTR_URI; 570 CSSM_DB_ATTRIBUTE_INFO threeAttr = OCSPD_DBATTR_EXPIRATION; 571 attrData[0].Info = oneAttr; 572 attrData[0].NumberOfValues = numSingleResps; 573 #ifndef NDEBUG 574 if(numSingleResps > 1) { 575 ocspdDbDebug("addResponse: MULTIPLE SINGLE RESPONSES (%u)", numSingleResps); 576 } 577 #endif 578 attrData[0].Value = (CSSM_DATA_PTR)SecAsn1Malloc(coder, 579 numSingleResps * sizeof(CSSM_DATA)); 580 memset(attrData[0].Value, 0, numSingleResps * sizeof(CSSM_DATA)); 581 for(unsigned dex=0; dex<numSingleResps; dex++) { 582 /* 583 * Get this single response. SKIP IT if the hash algorithm is not 584 * SHA1 since we do lookups in the DB by encoded value assuming SHA1 585 * hash. Incoming responses with other hash values would never be found. 586 */ 587 SecAsn1OCSPSingleResponse *resp = respData.responses[dex]; 588 SecAsn1OCSPCertID &certID = resp->certID; 589 if(!ocspdCompareCssmData(&certID.algId.algorithm, &CSSMOID_SHA1)) { 590 ocspdDbDebug("addResponse: SKIPPING resp due to nonstandard hash alg"); 591 attrData[0].NumberOfValues--; 592 continue; 593 } 594 /* encode this certID as attr[0]value[dex] */ 595 if(SecAsn1EncodeItem(coder, &certID, kSecAsn1OCSPCertIDTemplate, 596 &attrData[0].Value[dex])) { 597 ocspdErrorLog("OcspdDatabase::addResponse: encode error\n"); 598 crtn = CSSMERR_TP_INTERNAL_ERROR; 599 goto errOut; 600 } 601 } 602 603 attrData[1].Info = twoAttr; 604 attrData[1].NumberOfValues = 1; 605 attrData[1].Value = const_cast<CSSM_DATA_PTR>(&URI); 606 607 attrData[2].Info = threeAttr; 608 attrData[2].NumberOfValues = 1; 609 attrData[2].Value = &expireData; 610 611 crtn = CSSM_DL_DataInsert(mDlDbHandle, 612 OCSPD_DB_RECORDTYPE, 613 &recordAttrs, 614 &ocspResp, 615 &recordPtr); 616 if(crtn) { 617 /* delete and recreate our cache if there is any error */ 618 if(unlink(OCSP_DB_FILE)) { 619 Syslog::alert("Writing to OCSP cache failed (%d)", errno); 620 } 621 else { 622 Syslog::alert("Writing to OCSP cache failed (%d), recovering", crtn); 623 crtn = dbCreate(); 624 } 625 } 626 else { 627 CSSM_DL_FreeUniqueRecord(mDlDbHandle, recordPtr); 628 } 629errOut: 630 if(crtn) { 631 ocspdDbDebug("addResponse: error %d", crtn); 632 } 633 delete resp; 634 SecAsn1CoderRelease(coder); 635} 636 637void OcspdDatabase::flushCertID( 638 const CSSM_DATA &certID) 639{ 640 TransactionLock tLock; 641 StLock<Mutex> _(mLock); 642 if(dbOpen(false)) { 643 return; 644 } 645 646 CSSM_RETURN crtn; 647 CSSM_HANDLE resultHand; 648 CSSM_DB_UNIQUE_RECORD_PTR recordPtr; 649 650 /* just retrieve the record, no attrs, no data */ 651 crtn = lookupPriv(&certID, NULL, &resultHand, &recordPtr, NULL, NULL); 652 if(crtn) { 653 ocspdDbDebug("OcspdDatabase::flushCertID: no such record"); 654 return; 655 } 656 try { 657 ocspdDbDebug("OcspdDatabase::flushCertID: deleting (1)"); 658 CSSM_DL_DataDelete(mDlDbHandle, recordPtr); 659 CSSM_DL_FreeUniqueRecord(mDlDbHandle, recordPtr); 660 661 /* any more? */ 662 do { 663 crtn = CSSM_DL_DataGetNext(mDlDbHandle, resultHand, NULL, NULL, &recordPtr); 664 if(crtn) { 665 /* done, not found */ 666 break; 667 } 668 ocspdDbDebug("OcspdDatabase::flushCertID: deleting (2)"); 669 CSSM_DL_DataDelete(mDlDbHandle, recordPtr); 670 CSSM_DL_FreeUniqueRecord(mDlDbHandle, recordPtr); 671 } while(crtn == CSSM_OK); 672 CSSM_DL_DataAbortQuery(mDlDbHandle, resultHand); 673 } 674 catch (...) {}; // <rdar://8833413> 675 return; 676} 677 678void OcspdDatabase::flushStale() 679{ 680 TransactionLock tLock; 681 StLock<Mutex> _(mLock); 682 if(dbOpen(false)) { 683 return; 684 } 685 686 CSSM_RETURN crtn; 687 CSSM_HANDLE resultHand; 688 CSSM_DB_UNIQUE_RECORD_PTR recordPtr; 689 690 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); 691 692 /* retrieve all records, one attr (expiration time), no data */ 693 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrData; 694 memset(&recordAttrData, 0, sizeof(recordAttrData)); 695 CSSM_DB_ATTRIBUTE_DATA attrData; 696 memset(&attrData, 0, sizeof(attrData)); 697 CSSM_DB_ATTRIBUTE_INFO expireInfo = OCSPD_DBATTR_EXPIRATION; 698 attrData.Info = expireInfo; 699 recordAttrData.DataRecordType = OCSPD_DB_RECORDTYPE; 700 recordAttrData.NumberOfAttributes = 1; 701 recordAttrData.AttributeData = &attrData; 702 703 crtn = lookupPriv(NULL, NULL, &resultHand, &recordPtr, &recordAttrData, NULL); 704 if(crtn) { 705 ocspdDbDebug("OcspdDatabase::flushStale: no records found"); 706 return; 707 } 708 try { 709 CFAbsoluteTime cfExpireTime = genTimeToCFAbsTime(attrData.Value); 710 if(now >= cfExpireTime) { 711 ocspdDbDebug("OcspdDatabase::flushStale: record EXPIRED"); 712 CSSM_DL_DataDelete(mDlDbHandle, recordPtr); 713 } 714 CSSM_DL_FreeUniqueRecord(mDlDbHandle, recordPtr); 715 716 /* any more? */ 717 do { 718 crtn = CSSM_DL_DataGetNext(mDlDbHandle, resultHand, NULL, NULL, &recordPtr); 719 if(crtn) { 720 /* done, not found */ 721 break; 722 } 723 cfExpireTime = genTimeToCFAbsTime(attrData.Value); 724 if(now >= cfExpireTime) { 725 ocspdDbDebug("OcspdDatabase::flushStale: record EXPIRED"); 726 CSSM_DL_DataDelete(mDlDbHandle, recordPtr); 727 } 728 CSSM_DL_FreeUniqueRecord(mDlDbHandle, recordPtr); 729 } while(crtn == CSSM_OK); 730 CSSM_DL_DataAbortQuery(mDlDbHandle, resultHand); 731 } 732 catch (...) {}; // <rdar://8833413> 733 return; 734} 735 736static ModuleNexus<OcspdDatabase> gOcspdDatabase; 737 738 739#pragma mark ---- Public API ---- 740 741/* 742 * Lookup cached response. Result is a DER-encoded OCSP response,t he same bits 743 * originally obtained from the net. Result is allocated in specified 744 * SecAsn1CoderRef's memory. Never returns a stale entry; we always check the 745 * enclosed SingleResponse for temporal validity. 746 * 747 * Just a boolean returned; we found it, or not. 748 */ 749bool ocspdDbCacheLookup( 750 SecAsn1CoderRef coder, 751 const CSSM_DATA &certID, 752 const CSSM_DATA *localResponder, // optional 753 CSSM_DATA &derResp) // RETURNED 754{ 755 return gOcspdDatabase().lookup(coder, certID, localResponder, derResp); 756} 757 758/* 759 * Add a OCSP response to cache. Incoming response is completely unverified; 760 * we just verify that we can parse it and is has at least one SingleResponse 761 * which is temporally valid. 762 */ 763void ocspdDbCacheAdd( 764 const CSSM_DATA &ocspResp, // DER encoded SecAsn1OCSPResponse 765 const CSSM_DATA &URI) // where it came from 766{ 767 gOcspdDatabase().addResponse(ocspResp, URI); 768} 769 770/* 771 * Delete any entry associated with specified certID from cache. 772 */ 773void ocspdDbCacheFlush( 774 const CSSM_DATA &certID) 775{ 776 gOcspdDatabase().flushCertID(certID); 777} 778 779/* 780 * Flush stale entries from cache. 781 */ 782void ocspdDbCacheFlushStale() 783{ 784 gOcspdDatabase().flushStale(); 785} 786 787 788