CrlInfo::~CrlInfo() { freeAttrs(&mAttrData[0], NUM_CRL_ATTRS); CSSM_DL_FreeUniqueRecord(mDlDbHand, mRecord); if(mCrlBlob.Data) { APP_FREE(mCrlBlob.Data); } }
/* * Find private key by specified label, delete it. */ CSSM_RETURN p12DeleteKey( CSSM_DL_DB_HANDLE dlDbHand, const CSSM_DATA &keyLabel) { CSSM_QUERY query; CSSM_SELECTION_PREDICATE predicate; CSSM_DB_UNIQUE_RECORD_PTR record = NULL; CSSM_RETURN crtn; CSSM_HANDLE resultHand = 0; query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; query.Conjunctive = CSSM_DB_NONE; query.NumSelectionPredicates = 1; predicate.DbOperator = CSSM_DB_EQUAL; predicate.Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; predicate.Attribute.Info.Label.AttributeName = (char*) P12_KEY_ATTR_LABEL_AND_HASH; predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; predicate.Attribute.Value = const_cast<CSSM_DATA_PTR>(&keyLabel); query.SelectionPredicate = &predicate; query.QueryLimits.TimeLimit = 0; query.QueryLimits.SizeLimit = 1; query.QueryFlags = 0; crtn = CSSM_DL_DataGetFirst(dlDbHand, &query, &resultHand, NULL, // attrs - don't need 'em NULL, // theData - don't need it &record); /* abort only on success */ if(crtn) { p12LogCssmError("CSSM_DL_DataGetFirst", crtn); p12ErrorLog("***p12DeleteKey: can't find private key\n"); return crtn; } crtn = CSSM_DL_DataDelete(dlDbHand, record); if(crtn) { p12LogCssmError("CSSM_DL_DataDelete", crtn); p12ErrorLog("***p12DeleteKey: can't delete private key\n"); } CSSM_DL_DataAbortQuery(dlDbHand, resultHand); CSSM_DL_FreeUniqueRecord(dlDbHand, record); return crtn; }
/* * Find private key by label, modify its Label attr to be the * hash of the associated public key. * Also optionally re-sets the key's PrintName attribute; used to reset * this attr from the random label we create when first unwrap it * to the friendly name we find later after parsing attributes. * Detection of a duplicate key when updating the key's attributes * results in a lookup of the original key and returning it in * foundKey. */ CSSM_RETURN p12SetPubKeyHash( CSSM_CSP_HANDLE cspHand, // where the key lives CSSM_DL_DB_HANDLE dlDbHand, // ditto CSSM_DATA &keyLabel, // for DB lookup CSSM_DATA_PTR newPrintName, // optional SecNssCoder &coder, // for mallocing newLabel CSSM_DATA &newLabel, // RETURNED with label as hash CSSM_KEY_PTR &foundKey) // RETURNED { CSSM_QUERY query; CSSM_SELECTION_PREDICATE predicate; CSSM_DB_UNIQUE_RECORD_PTR record = NULL; CSSM_RETURN crtn; CSSM_HANDLE resultHand = 0; CSSM_DATA keyData = {0, NULL}; CSSM_CC_HANDLE ccHand = 0; CSSM_KEY_PTR privKey = NULL; CSSM_DATA_PTR keyDigest = NULL; assert(cspHand != 0); query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; query.Conjunctive = CSSM_DB_NONE; query.NumSelectionPredicates = 1; predicate.DbOperator = CSSM_DB_EQUAL; predicate.Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; predicate.Attribute.Info.Label.AttributeName = (char*) P12_KEY_ATTR_LABEL_AND_HASH; predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; /* hope this cast is OK */ predicate.Attribute.Value = &keyLabel; query.SelectionPredicate = &predicate; query.QueryLimits.TimeLimit = 0; // FIXME - meaningful? query.QueryLimits.SizeLimit = 1; // FIXME - meaningful? query.QueryFlags = CSSM_QUERY_RETURN_DATA; /* build Record attribute with one or two attrs */ CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; CSSM_DB_ATTRIBUTE_DATA attr[2]; attr[0].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr[0].Info.Label.AttributeName = (char*) P12_KEY_ATTR_LABEL_AND_HASH; attr[0].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; if(newPrintName) { attr[1].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr[1].Info.Label.AttributeName = (char*) P12_KEY_ATTR_PRINT_NAME; attr[1].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; } recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; recordAttrs.NumberOfAttributes = newPrintName ? 2 : 1; recordAttrs.AttributeData = attr; crtn = CSSM_DL_DataGetFirst(dlDbHand, &query, &resultHand, &recordAttrs, &keyData, // theData &record); /* abort only on success */ if(crtn != CSSM_OK) { p12LogCssmError("CSSM_DL_DataGetFirst", crtn); p12ErrorLog("***p12SetPubKeyHash: can't find private key\n"); return crtn; } /* subsequent errors to errOut: */ if(keyData.Data == NULL) { p12ErrorLog("***p12SetPubKeyHash: private key lookup failure\n"); crtn = CSSMERR_CSSM_INTERNAL_ERROR; goto errOut; } privKey = (CSSM_KEY_PTR)keyData.Data; /* public key hash via passthrough - works on any key, any CSP/CSPDL.... */ /* * Warning! This relies on the current default ACL meaning "allow this * current app to access this private key" since we created the key. */ crtn = CSSM_CSP_CreatePassThroughContext(cspHand, privKey, &ccHand); if(crtn) { p12LogCssmError("CSSM_CSP_CreatePassThroughContext", crtn); goto errOut; } crtn = CSSM_CSP_PassThrough(ccHand, CSSM_APPLECSP_KEYDIGEST, NULL, (void **)&keyDigest); if(crtn) { p12LogCssmError("CSSM_CSP_PassThrough", crtn); goto errOut; } /* * Replace Label attr data with hash. * NOTE: the module which allocated this attribute data - a DL - * was loaded and attached by out client layer, not by us. Thus * we can't use the memory allocator functions *we* used when * attaching to the CSP - we have to use the ones * which the client registered with the DL. */ freeCssmMemory(dlDbHand.DLHandle, attr[0].Value->Data); freeCssmMemory(dlDbHand.DLHandle, attr[0].Value); if(newPrintName) { freeCssmMemory(dlDbHand.DLHandle, attr[1].Value->Data); freeCssmMemory(dlDbHand.DLHandle, attr[1].Value); } /* modify key attributes */ attr[0].Value = keyDigest; if(newPrintName) { attr[1].Value = newPrintName; } crtn = CSSM_DL_DataModify(dlDbHand, CSSM_DL_DB_RECORD_PRIVATE_KEY, record, &recordAttrs, NULL, // DataToBeModified CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); switch(crtn) { case CSSM_OK: /* give caller the key's new label */ coder.allocCopyItem(*keyDigest, newLabel); break; default: p12LogCssmError("CSSM_DL_DataModify", crtn); break; case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA: { /* * Special case: dup private key. The label we just tried to modify is * the public key hash so we can be confident that this really is a dup. * Delete it, look up the original, and return the original to caller. */ CSSM_RETURN drtn = CSSM_DL_DataDelete(dlDbHand, record); if(drtn) { p12LogCssmError("CSSM_DL_DataDelete on dup key", drtn); crtn = drtn; break; } /* Free items created in last search */ CSSM_DL_DataAbortQuery(dlDbHand, resultHand); resultHand = 0; CSSM_DL_FreeUniqueRecord(dlDbHand, record); record = NULL; /* lookup by label as public key hash this time */ predicate.Attribute.Value = keyDigest; drtn = CSSM_DL_DataGetFirst(dlDbHand, &query, &resultHand, NULL, // no attrs this time &keyData, &record); if(drtn) { p12LogCssmError("CSSM_DL_DataGetFirst on original key", crtn); crtn = drtn; break; } foundKey = (CSSM_KEY_PTR)keyData.Data; /* give caller the key's actual label */ coder.allocCopyItem(*keyDigest, newLabel); break; } } errOut: /* free resources */ if(resultHand) { CSSM_DL_DataAbortQuery(dlDbHand, resultHand); } if(record) { CSSM_DL_FreeUniqueRecord(dlDbHand, record); } if(ccHand) { CSSM_DeleteContext(ccHand); } if(privKey) { /* key created by the CSPDL */ CSSM_FreeKey(cspHand, NULL, privKey, CSSM_FALSE); freeCssmMemory(dlDbHand.DLHandle, privKey); } if(keyDigest) { /* mallocd by someone else's CSP */ freeCssmMemory(cspHand, keyDigest->Data); freeCssmMemory(cspHand, keyDigest); } return crtn; }
/* * Search a list of DBs for a cert which verifies specified subject item. * Just a boolean return - we found it, or not. If we did, we return * TPCertInfo associated with the raw cert. * A true partialIssuerKey on return indicates that caller must deal * with partial public key processing later. * If verifyCurrent is true, we will not return a cert which is not * temporally valid; else we may well do so. */ TPCertInfo *tpDbFindIssuerCert( Allocator &alloc, CSSM_CL_HANDLE clHand, CSSM_CSP_HANDLE cspHand, const TPClItemInfo *subjectItem, const CSSM_DL_DB_LIST *dbList, const char *verifyTime, // may be NULL bool &partialIssuerKey) // RETURNED { StLock<Mutex> _(SecTrustKeychainsGetMutex()); uint32 dbDex; CSSM_HANDLE resultHand; CSSM_DATA cert; CSSM_DL_DB_HANDLE dlDb; CSSM_DB_UNIQUE_RECORD_PTR record; TPCertInfo *issuerCert = NULL; bool foundIt; TPCertInfo *expiredIssuer = NULL; TPCertInfo *nonRootIssuer = NULL; partialIssuerKey = false; if(dbList == NULL) { return NULL; } for(dbDex=0; dbDex<dbList->NumHandles; dbDex++) { dlDb = dbList->DLDBHandle[dbDex]; cert.Data = NULL; cert.Length = 0; resultHand = 0; record = tpCertLookup(dlDb, subjectItem->issuerName(), &resultHand, &cert); /* remember we have to: * -- abort this query regardless, and * -- free the CSSM_DATA cert regardless, and * -- free the unique record if we don't use it * (by placing it in issuerCert)... */ if(record != NULL) { /* Found one */ assert(cert.Data != NULL); tpDbDebug("tpDbFindIssuerCert: found cert record (1) %p", record); issuerCert = NULL; CSSM_RETURN crtn = CSSM_OK; try { issuerCert = new TPCertInfo(clHand, cspHand, &cert, TIC_CopyData, verifyTime); } catch(...) { crtn = CSSMERR_TP_INVALID_CERTIFICATE; } /* we're done with raw cert data */ tpFreePluginMemory(dlDb.DLHandle, cert.Data); cert.Data = NULL; cert.Length = 0; /* Does it verify the subject cert? */ if(crtn == CSSM_OK) { crtn = subjectItem->verifyWithIssuer(issuerCert); } /* * Handle temporal invalidity - if so and this is the first one * we've seen, hold on to it while we search for better one. */ if((crtn == CSSM_OK) && (expiredIssuer == NULL)) { if(issuerCert->isExpired() || issuerCert->isNotValidYet()) { /* * Exact value not important here, this just uniquely identifies * this situation in the switch below. */ tpDbDebug("tpDbFindIssuerCert: holding expired cert (1)"); crtn = CSSM_CERT_STATUS_EXPIRED; expiredIssuer = issuerCert; expiredIssuer->dlDbHandle(dlDb); expiredIssuer->uniqueRecord(record); } } /* * Prefer a root over an intermediate issuer if we can get one * (in case a cross-signed intermediate and root are both available) */ if((crtn == CSSM_OK) && (nonRootIssuer == NULL)) { if(!issuerCert->isSelfSigned()) { /* * Exact value not important here, this just uniquely identifies * this situation in the switch below. */ tpDbDebug("tpDbFindIssuerCert: holding non-root cert (1)"); crtn = CSSM_CERT_STATUS_IS_ROOT; nonRootIssuer = issuerCert; nonRootIssuer->dlDbHandle(dlDb); nonRootIssuer->uniqueRecord(record); } } switch(crtn) { case CSSM_OK: break; case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: partialIssuerKey = true; break; default: if(issuerCert != NULL) { /* either holding onto this cert, or done with it. */ if(crtn != CSSM_CERT_STATUS_EXPIRED && crtn != CSSM_CERT_STATUS_IS_ROOT) { delete issuerCert; CSSM_DL_FreeUniqueRecord(dlDb, record); } issuerCert = NULL; } /* * Continue searching this DB. Break on finding the holy * grail or no more records found. */ for(;;) { cert.Data = NULL; cert.Length = 0; record = NULL; CSSM_RETURN crtn = CSSM_DL_DataGetNext(dlDb, resultHand, NULL, // no attrs &cert, &record); if(crtn) { /* no more, done with this DB */ assert(cert.Data == NULL); break; } assert(cert.Data != NULL); tpDbDebug("tpDbFindIssuerCert: found cert record (2) %p", record); /* found one - does it verify subject? */ try { issuerCert = new TPCertInfo(clHand, cspHand, &cert, TIC_CopyData, verifyTime); } catch(...) { crtn = CSSMERR_TP_INVALID_CERTIFICATE; } /* we're done with raw cert data */ tpFreePluginMemory(dlDb.DLHandle, cert.Data); cert.Data = NULL; cert.Length = 0; if(crtn == CSSM_OK) { crtn = subjectItem->verifyWithIssuer(issuerCert); } /* temporal validity check, again */ if((crtn == CSSM_OK) && (expiredIssuer == NULL)) { if(issuerCert->isExpired() || issuerCert->isNotValidYet()) { tpDbDebug("tpDbFindIssuerCert: holding expired cert (2)"); crtn = CSSM_CERT_STATUS_EXPIRED; expiredIssuer = issuerCert; expiredIssuer->dlDbHandle(dlDb); expiredIssuer->uniqueRecord(record); } } /* self-signed check, again */ if((crtn == CSSM_OK) && (nonRootIssuer == NULL)) { if(!issuerCert->isSelfSigned()) { tpDbDebug("tpDbFindIssuerCert: holding non-root cert (2)"); crtn = CSSM_CERT_STATUS_IS_ROOT; nonRootIssuer = issuerCert; nonRootIssuer->dlDbHandle(dlDb); nonRootIssuer->uniqueRecord(record); } } foundIt = false; switch(crtn) { case CSSM_OK: foundIt = true; break; case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: partialIssuerKey = true; foundIt = true; break; default: break; } if(foundIt) { /* yes! */ break; } if(issuerCert != NULL) { /* either holding onto this cert, or done with it. */ if(crtn != CSSM_CERT_STATUS_EXPIRED && crtn != CSSM_CERT_STATUS_IS_ROOT) { delete issuerCert; CSSM_DL_FreeUniqueRecord(dlDb, record); } issuerCert = NULL; } } /* searching subsequent records */ } /* switch verify */ if(record != NULL) { /* NULL record --> end of search --> DB auto-aborted */ crtn = CSSM_DL_DataAbortQuery(dlDb, resultHand); assert(crtn == CSSM_OK); } if(issuerCert != NULL) { /* successful return */ tpDbDebug("tpDbFindIssuer: returning record %p", record); issuerCert->dlDbHandle(dlDb); issuerCert->uniqueRecord(record); if(expiredIssuer != NULL) { /* We found a replacement */ tpDbDebug("tpDbFindIssuer: discarding expired cert"); expiredIssuer->freeUniqueRecord(); delete expiredIssuer; } /* Avoid deleting the non-root cert if same as expired cert */ if(nonRootIssuer != NULL && nonRootIssuer != expiredIssuer) { /* We found a replacement */ tpDbDebug("tpDbFindIssuer: discarding non-root cert"); nonRootIssuer->freeUniqueRecord(); delete nonRootIssuer; } return issuerCert; } } /* tpCertLookup, i.e., CSSM_DL_DataGetFirst, succeeded */ else { assert(cert.Data == NULL); assert(resultHand == 0); } } /* main loop searching dbList */ if(nonRootIssuer != NULL) { /* didn't find root issuer, so use this one */ tpDbDebug("tpDbFindIssuer: taking non-root issuer cert, record %p", nonRootIssuer->uniqueRecord()); if(expiredIssuer != NULL && expiredIssuer != nonRootIssuer) { expiredIssuer->freeUniqueRecord(); delete expiredIssuer; } return nonRootIssuer; } if(expiredIssuer != NULL) { /* OK, we'll take this one */ tpDbDebug("tpDbFindIssuer: taking expired cert after all, record %p", expiredIssuer->uniqueRecord()); return expiredIssuer; } /* issuer not found */ return NULL; }
/* * Search a list of DBs for a CRL from the specified issuer and (optional) * TPVerifyContext.verifyTime. * Just a boolean return - we found it, or not. If we did, we return a * TPCrlInfo which has been verified with the specified TPVerifyContext. */ TPCrlInfo *tpDbFindIssuerCrl( TPVerifyContext &vfyCtx, const CSSM_DATA &issuer, TPCertInfo &forCert) { StLock<Mutex> _(SecTrustKeychainsGetMutex()); uint32 dbDex; CSSM_HANDLE resultHand; CSSM_DATA crl; CSSM_DL_DB_HANDLE dlDb; CSSM_DB_UNIQUE_RECORD_PTR record; TPCrlInfo *issuerCrl = NULL; CSSM_DL_DB_LIST_PTR dbList = vfyCtx.dbList; CSSM_RETURN crtn; if(dbList == NULL) { return NULL; } for(dbDex=0; dbDex<dbList->NumHandles; dbDex++) { dlDb = dbList->DLDBHandle[dbDex]; crl.Data = NULL; crl.Length = 0; record = tpCrlLookup(dlDb, &issuer, vfyCtx.verifyTime, &resultHand, &crl); /* remember we have to: * -- abort this query regardless, and * -- free the CSSM_DATA crl regardless, and * -- free the unique record if we don't use it * (by placing it in issuerCert)... */ if(record != NULL) { /* Found one */ assert(crl.Data != NULL); issuerCrl = new TPCrlInfo(vfyCtx.clHand, vfyCtx.cspHand, &crl, TIC_CopyData, vfyCtx.verifyTime); /* we're done with raw CRL data */ /* FIXME this assumes that vfyCtx.alloc is the same as the * allocator associated with DlDB...OK? */ tpFreeCssmData(vfyCtx.alloc, &crl, CSSM_FALSE); crl.Data = NULL; crl.Length = 0; /* and we're done with the record */ CSSM_DL_FreeUniqueRecord(dlDb, record); /* Does it verify with specified context? */ crtn = issuerCrl->verifyWithContextNow(vfyCtx, &forCert); if(crtn) { delete issuerCrl; issuerCrl = NULL; /* * Verify fail. Continue searching this DB. Break on * finding the holy grail or no more records found. */ for(;;) { crl.Data = NULL; crl.Length = 0; crtn = CSSM_DL_DataGetNext(dlDb, resultHand, NULL, // no attrs &crl, &record); if(crtn) { /* no more, done with this DB */ assert(crl.Data == NULL); break; } assert(crl.Data != NULL); /* found one - is it any good? */ issuerCrl = new TPCrlInfo(vfyCtx.clHand, vfyCtx.cspHand, &crl, TIC_CopyData, vfyCtx.verifyTime); /* we're done with raw CRL data */ /* FIXME this assumes that vfyCtx.alloc is the same as the * allocator associated with DlDB...OK? */ tpFreeCssmData(vfyCtx.alloc, &crl, CSSM_FALSE); crl.Data = NULL; crl.Length = 0; CSSM_DL_FreeUniqueRecord(dlDb, record); crtn = issuerCrl->verifyWithContextNow(vfyCtx, &forCert); if(crtn == CSSM_OK) { /* yes! */ break; } delete issuerCrl; issuerCrl = NULL; } /* searching subsequent records */ } /* verify fail */ /* else success! */ if(issuerCrl != NULL) { /* successful return */ CSSM_DL_DataAbortQuery(dlDb, resultHand); tpDebug("tpDbFindIssuerCrl: found CRL record %p", record); return issuerCrl; } } /* tpCrlLookup, i.e., CSSM_DL_DataGetFirst, succeeded */ else { assert(crl.Data == NULL); } /* in any case, abort the query for this db */ CSSM_DL_DataAbortQuery(dlDb, resultHand); } /* main loop searching dbList */ /* issuer not found */ return NULL; }
/* * Add a certificate to an open DLDB. */ CSSM_RETURN cuAddCertToDb( CSSM_DL_DB_HANDLE dlDbHand, const CSSM_DATA *cert, CSSM_CERT_TYPE certType, CSSM_CERT_ENCODING certEncoding, const char *printName, // C string const CSSM_DATA *publicKeyHash) { CSSM_DB_ATTRIBUTE_DATA attrs[6]; CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; CSSM_DB_ATTRIBUTE_DATA_PTR attr = &attrs[0]; CSSM_DATA certTypeData; CSSM_DATA certEncData; CSSM_DATA printNameData; CSSM_RETURN crtn; CSSM_DB_UNIQUE_RECORD_PTR recordPtr; /* issuer and serial number required, fake 'em */ CSSM_DATA issuer = {6, (uint8 *)"issuer"}; CSSM_DATA serial = {6, (uint8 *)"serial"}; /* we spec six attributes, skipping alias */ certTypeData.Data = (uint8 *)&certType; certTypeData.Length = sizeof(CSSM_CERT_TYPE); certEncData.Data = (uint8 *)&certEncoding; certEncData.Length = sizeof(CSSM_CERT_ENCODING); printNameData.Data = (uint8 *)printName; printNameData.Length = strlen(printName) + 1; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = (char*) "CertType"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32; attr->NumberOfValues = 1; attr->Value = &certTypeData; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = (char*) "CertEncoding"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32; attr->NumberOfValues = 1; attr->Value = &certEncData; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = (char*) "PrintName"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = &printNameData; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = (char*) "PublicKeyHash"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = (CSSM_DATA_PTR)publicKeyHash; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = (char*) "Issuer"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = &issuer; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = (char*) "SerialNumber"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = &serial; recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE; recordAttrs.SemanticInformation = 0; recordAttrs.NumberOfAttributes = 6; recordAttrs.AttributeData = attrs; crtn = CSSM_DL_DataInsert(dlDbHand, CSSM_DL_DB_RECORD_X509_CERTIFICATE, &recordAttrs, cert, &recordPtr); if(crtn) { cuPrintError("CSSM_DL_DataInsert", crtn); } else { CSSM_DL_FreeUniqueRecord(dlDbHand, recordPtr); } return crtn; }
/* * Search DB for all records of type CRL or cert, calling appropriate * parse/print routine for each record. */ CSSM_RETURN cuDumpCrlsCerts( CSSM_DL_DB_HANDLE dlDbHand, CSSM_CL_HANDLE clHand, CSSM_BOOL isCert, unsigned &numItems, // returned CSSM_BOOL verbose) { CSSM_QUERY query; CSSM_DB_UNIQUE_RECORD_PTR record = NULL; CSSM_HANDLE resultHand; CSSM_RETURN crtn; CSSM_DATA certCrl; const char *itemStr; numItems = 0; itemStr = isCert ? "Certificate" : "CRL"; /* just search by recordType, no predicates, no attributes */ if(isCert) { query.RecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE; } else { query.RecordType = CSSM_DL_DB_RECORD_X509_CRL; } query.Conjunctive = CSSM_DB_NONE; query.NumSelectionPredicates = 0; query.SelectionPredicate = NULL; query.QueryLimits.TimeLimit = 0; // FIXME - meaningful? query.QueryLimits.SizeLimit = 1; // FIXME - meaningful? query.QueryFlags = 0; // CSSM_QUERY_RETURN_DATA...FIXME - used? certCrl.Data = NULL; certCrl.Length = 0; crtn = CSSM_DL_DataGetFirst(dlDbHand, &query, &resultHand, NULL, // no attrs &certCrl, &record); switch(crtn) { case CSSM_OK: break; // proceed case CSSMERR_DL_ENDOFDATA: /* no data, otherwise OK */ return CSSM_OK; case CSSMERR_DL_INVALID_RECORDTYPE: /* invalid record type just means "this hasn't been set up * for certs yet". */ return crtn; default: cuPrintError("DataGetFirst", crtn); return crtn; } /* got one; print it */ dprintf("%s %u:\n", itemStr, numItems); if(isCert) { printCert(certCrl.Data, (unsigned)certCrl.Length, verbose); } else { printCrl(certCrl.Data, (unsigned)certCrl.Length, verbose); } CSSM_DL_FreeUniqueRecord(dlDbHand, record); APP_FREE(certCrl.Data); certCrl.Data = NULL; certCrl.Length = 0; numItems++; /* get the rest */ for(;;) { crtn = CSSM_DL_DataGetNext(dlDbHand, resultHand, NULL, &certCrl, &record); switch(crtn) { case CSSM_OK: dprintf("%s %u:\n", itemStr, numItems); if(isCert) { printCert(certCrl.Data, (unsigned)certCrl.Length, verbose); } else { printCrl(certCrl.Data, (unsigned)certCrl.Length, verbose); } CSSM_DL_FreeUniqueRecord(dlDbHand, record); APP_FREE(certCrl.Data); certCrl.Data = NULL; certCrl.Length = 0; numItems++; break; // and go again case CSSMERR_DL_ENDOFDATA: /* normal termination */ return CSSM_OK; default: cuPrintError("DataGetNext", crtn); return crtn; } } /* NOT REACHED */ }
CSSM_RETURN cuAddCrlToDb( CSSM_DL_DB_HANDLE dlDbHand, CSSM_CL_HANDLE clHand, const CSSM_DATA *crl, const CSSM_DATA *URI) // optional { CSSM_DB_ATTRIBUTE_DATA attrs[MAX_CRL_ATTRS]; CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; CSSM_DB_ATTRIBUTE_DATA_PTR attr = &attrs[0]; CSSM_DATA crlTypeData; CSSM_DATA crlEncData; CSSM_DATA printNameData; CSSM_RETURN crtn; CSSM_DB_UNIQUE_RECORD_PTR recordPtr; CSSM_DATA_PTR issuer = NULL; // mallocd by CL CSSM_DATA_PTR crlValue = NULL; // ditto uint32 numFields; CSSM_HANDLE result; CSSM_CRL_ENCODING crlEnc = CSSM_CRL_ENCODING_DER; const CSSM_X509_SIGNED_CRL *signedCrl; const CSSM_X509_TBS_CERTLIST *tbsCrl; CSSM_CRL_TYPE crlType; CSSM_DATA thisUpdateData = {0, NULL}; CSSM_DATA nextUpdateData = {0, NULL}; char *thisUpdate = NULL; char *nextUpdate = NULL; unsigned timeLen; uint32 crlNumber; uint32 deltaCrlNumber; CSSM_DATA crlNumberData; CSSM_DATA deltaCrlNumberData; bool crlNumberPresent = false; bool deltaCrlPresent = false; CSSM_DATA attrUri; /* get normalized issuer name as Issuer attr */ crtn = CSSM_CL_CrlGetFirstFieldValue(clHand, crl, &CSSMOID_X509V1IssuerName, &result, &numFields, &issuer); if(crtn) { cuPrintError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn); return crtn; } CSSM_CL_CrlAbortQuery(clHand, result); /* get parsed CRL from the CL */ crtn = CSSM_CL_CrlGetFirstFieldValue(clHand, crl, &CSSMOID_X509V2CRLSignedCrlCStruct, &result, &numFields, &crlValue); if(crtn) { cuPrintError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn); goto errOut; } CSSM_CL_CrlAbortQuery(clHand, result); if(crlValue == NULL) { dprintf("***CSSM_CL_CrlGetFirstFieldValue: value error (1)\n"); crtn = CSSMERR_CL_INVALID_CRL_POINTER; goto errOut; } if((crlValue->Data == NULL) || (crlValue->Length != sizeof(CSSM_X509_SIGNED_CRL))) { dprintf("***CSSM_CL_CrlGetFirstFieldValue: value error (2)\n"); crtn = CSSMERR_CL_INVALID_CRL_POINTER; goto errOut; } signedCrl = (const CSSM_X509_SIGNED_CRL *)crlValue->Data; tbsCrl = &signedCrl->tbsCertList; /* CrlType inferred from version */ if(tbsCrl->version.Length == 0) { /* should never happen... */ crlType = CSSM_CRL_TYPE_X_509v1; } else { uint8 vers = tbsCrl->version.Data[tbsCrl->version.Length - 1]; switch(vers) { case 0: crlType = CSSM_CRL_TYPE_X_509v1; break; case 1: crlType = CSSM_CRL_TYPE_X_509v2; break; default: dprintf("***Unknown version in CRL (%u)\n", vers); crlType = CSSM_CRL_TYPE_X_509v1; break; } } crlTypeData.Data = (uint8 *)&crlType; crlTypeData.Length = sizeof(CSSM_CRL_TYPE); /* encoding more-or-less assumed here */ crlEncData.Data = (uint8 *)&crlEnc; crlEncData.Length = sizeof(CSSM_CRL_ENCODING); /* printName inferred from issuer */ cuInferCrlLabel(&tbsCrl->issuer, &printNameData); /* cook up CSSM_TIMESTRING versions of this/next update */ thisUpdate = cuX509TimeToCssmTimestring(&tbsCrl->thisUpdate, &timeLen); if(thisUpdate == NULL) { dprintf("***Badly formatted thisUpdate\n"); } else { thisUpdateData.Data = (uint8 *)thisUpdate; thisUpdateData.Length = timeLen; } if(tbsCrl->nextUpdate.time.Data != NULL) { nextUpdate = cuX509TimeToCssmTimestring(&tbsCrl->nextUpdate, &timeLen); if(nextUpdate == NULL) { dprintf("***Badly formatted nextUpdate\n"); } else { nextUpdateData.Data = (uint8 *)nextUpdate; nextUpdateData.Length = timeLen; } } else { /* * NextUpdate not present; fake it by using "virtual end of time" */ CSSM_X509_TIME tempTime = { 0, // timeType, not used { strlen(CSSM_APPLE_CRL_END_OF_TIME), (uint8 *)CSSM_APPLE_CRL_END_OF_TIME} }; nextUpdate = cuX509TimeToCssmTimestring(&tempTime, &timeLen); nextUpdateData.Data = (uint8 *)nextUpdate; nextUpdateData.Length = CSSM_TIME_STRLEN; } /* optional CrlNumber and DeltaCrlNumber */ if(cuSearchNumericExtension(&tbsCrl->extensions, &CSSMOID_CrlNumber, &crlNumber)) { crlNumberData.Data = (uint8 *)&crlNumber; crlNumberData.Length = sizeof(uint32); crlNumberPresent = true; } if(cuSearchNumericExtension(&tbsCrl->extensions, &CSSMOID_DeltaCrlIndicator, &deltaCrlNumber)) { deltaCrlNumberData.Data = (uint8 *)&deltaCrlNumber; deltaCrlNumberData.Length = sizeof(uint32); deltaCrlPresent = true; } attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = (char*) "CrlType"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32; attr->NumberOfValues = 1; attr->Value = &crlTypeData; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = (char*) "CrlEncoding"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32; attr->NumberOfValues = 1; attr->Value = &crlEncData; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = (char*) "PrintName"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = &printNameData; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = (char*) "Issuer"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = issuer; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = (char*) "ThisUpdate"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = &thisUpdateData; attr++; attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = (char*) "NextUpdate"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = &nextUpdateData; attr++; /* now the optional attributes */ if(crlNumberPresent) { attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = (char*) "CrlNumber"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32; attr->NumberOfValues = 1; attr->Value = &crlNumberData; attr++; } if(deltaCrlPresent) { attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = (char*) "DeltaCrlNumber"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32; attr->NumberOfValues = 1; attr->Value = &deltaCrlNumberData; attr++; } if(URI) { /* ensure URI string does not contain NULL */ attrUri = *URI; if((attrUri.Length != 0) && (attrUri.Data[attrUri.Length - 1] == 0)) { attrUri.Length--; } attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr->Info.Label.AttributeName = (char*) "URI"; attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; attr->NumberOfValues = 1; attr->Value = &attrUri; attr++; } recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_X509_CRL; recordAttrs.SemanticInformation = 0; recordAttrs.NumberOfAttributes = (uint32)(attr - attrs); recordAttrs.AttributeData = attrs; crtn = CSSM_DL_DataInsert(dlDbHand, CSSM_DL_DB_RECORD_X509_CRL, &recordAttrs, crl, &recordPtr); if(crtn == CSSMERR_DL_INVALID_RECORDTYPE) { /* gross hack of inserting this "new" schema that Keychain didn't specify */ crtn = cuAddCrlSchema(dlDbHand); if(crtn == CSSM_OK) { /* Retry with a fully capable DLDB */ crtn = CSSM_DL_DataInsert(dlDbHand, CSSM_DL_DB_RECORD_X509_CRL, &recordAttrs, crl, &recordPtr); } } if(crtn == CSSM_OK) { CSSM_DL_FreeUniqueRecord(dlDbHand, recordPtr); } errOut: /* free all the stuff we allocated to get here */ if(issuer) { CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1IssuerName, issuer); } if(crlValue) { CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V2CRLSignedCrlCStruct, crlValue); } if(thisUpdate) { free(thisUpdate); } if(nextUpdate) { free(nextUpdate); } return crtn; }
/* * Find private key by label, modify its Label attr to be the * hash of the associated public key. */ static CSSM_RETURN setPubKeyHash( CSSM_CSP_HANDLE cspHand, CSSM_DL_DB_HANDLE dlDbHand, const CSSM_KEY *pubOrPrivKey, // to get hash; raw or ref/CSPDL const char *keyLabel) // look up by this { CSSM_QUERY query; CSSM_SELECTION_PREDICATE predicate; CSSM_DB_UNIQUE_RECORD_PTR record = NULL; CSSM_RETURN crtn; CSSM_DATA labelData; CSSM_HANDLE resultHand; labelData.Data = (uint8 *)keyLabel; labelData.Length = strlen(keyLabel) + 1; // incl. NULL query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; query.Conjunctive = CSSM_DB_NONE; query.NumSelectionPredicates = 1; predicate.DbOperator = CSSM_DB_EQUAL; predicate.Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; predicate.Attribute.Info.Label.AttributeName = "Label"; predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; predicate.Attribute.Value = &labelData; query.SelectionPredicate = &predicate; query.QueryLimits.TimeLimit = 0; query.QueryLimits.SizeLimit = 1; query.QueryFlags = 0; /* build Record attribute with one attr */ CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; CSSM_DB_ATTRIBUTE_DATA attr; attr.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr.Info.Label.AttributeName = "Label"; attr.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; recordAttrs.NumberOfAttributes = 1; recordAttrs.AttributeData = &attr; crtn = CSSM_DL_DataGetFirst(dlDbHand, &query, &resultHand, &recordAttrs, NULL, // hopefully optional ...theData, &record); /* abort only on success */ if(crtn != CSSM_OK) { sec_error("CSSM_DL_DataGetFirst: setPubKeyHash: can't find private key: %s", sec_errstr(crtn)); return crtn; } /* * If specified key is a ref key, do NULL unwrap for use with raw CSP. * If the CSPDL and SecurityServer support the key digest passthrough * this is unnecessary. */ CSSM_KEY rawKeyToDigest; if(pubOrPrivKey->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE) { crtn = refKeyToRaw(cspHand, pubOrPrivKey, &rawKeyToDigest); if(crtn) { sec_error("setPubKeyHash: Error converting public key to raw format: %s", sec_errstr(crtn)); return crtn; } } else { /* use as is */ rawKeyToDigest = *pubOrPrivKey; } /* connect to raw CSP */ CSSM_CSP_HANDLE rawCspHand = srCspStartup(CSSM_TRUE); if(rawCspHand == 0) { printf("***Error connecting to raw CSP; aborting.\n"); return -1; } /* calculate hash of pub key from private or public part */ CSSM_DATA_PTR keyDigest = NULL; CSSM_CC_HANDLE ccHand; crtn = CSSM_CSP_CreatePassThroughContext(rawCspHand, &rawKeyToDigest, &ccHand); if(ccHand == 0) { sec_error("CSSM_CSP_CreatePassThroughContext: Error calculating public key hash. Aborting: %s", sec_errstr(crtn)); return -1; } crtn = CSSM_CSP_PassThrough(ccHand, CSSM_APPLECSP_KEYDIGEST, NULL, (void **)&keyDigest); if(crtn) { sec_error("CSSM_CSP_PassThrough(PUBKEYHASH): Error calculating public key hash. Aborting: %s", sec_errstr(crtn)); return crtn; } if(pubOrPrivKey->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE) { /* created in refKeyToRaw().... */ CSSM_FreeKey(cspHand, NULL, &rawKeyToDigest, CSSM_FALSE); } CSSM_DeleteContext(ccHand); CSSM_ModuleDetach(rawCspHand); /* * Replace Label attr data with hash. * NOTE: the module which allocated this attribute data - a DL - * was loaded and attached by the Sec layer, not by us. Thus * we can't use the memory allocator functions *we* used when * attaching to the CSPDL - we have to use the ones * which the Sec layer registered with the DL. */ CSSM_API_MEMORY_FUNCS memFuncs; crtn = CSSM_GetAPIMemoryFunctions(dlDbHand.DLHandle, &memFuncs); if(crtn) { sec_error("CSSM_GetAPIMemoryFunctions(DLHandle): Error: %s", sec_errstr(crtn)); /* oh well, leak and continue */ } else { memFuncs.free_func(attr.Value->Data, memFuncs.AllocRef); memFuncs.free_func(attr.Value, memFuncs.AllocRef); } attr.Value = keyDigest; /* modify key attributes */ crtn = CSSM_DL_DataModify(dlDbHand, CSSM_DL_DB_RECORD_PRIVATE_KEY, record, &recordAttrs, NULL, // DataToBeModified CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); if(crtn) { sec_error("CSSM_DL_DataModify(PUBKEYHASH): Error setting public key hash. Aborting: %s", sec_errstr(crtn)); return crtn; } crtn = CSSM_DL_DataAbortQuery(dlDbHand, resultHand); if(crtn) { sec_error("CSSM_DL_DataAbortQuery: Error while stopping query: %s", sec_errstr(crtn)); /* let's keep going in this case */ } crtn = CSSM_DL_FreeUniqueRecord(dlDbHand, record); if(crtn) { sec_error("CSSM_DL_FreeUniqueRecord: Error while freeing record: %s", sec_errstr(crtn)); /* let's keep going in this case */ crtn = CSSM_OK; } /* free resources */ if (keyDigest) { srAppFree(keyDigest->Data, NULL); srAppFree(keyDigest, NULL); } return CSSM_OK; }