X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTests/cspxutils/dbTool/dbCert.cpp diff --git a/SecurityTests/cspxutils/dbTool/dbCert.cpp b/SecurityTests/cspxutils/dbTool/dbCert.cpp new file mode 100644 index 00000000..aa73443d --- /dev/null +++ b/SecurityTests/cspxutils/dbTool/dbCert.cpp @@ -0,0 +1,508 @@ +/* + * Copyright (c) 2003-2005,2008 Apple 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. + */ + +/* + * dbCert.cpp - import a possibly bad cert along with its private key + */ + +#include "dbCert.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/* Copied from clxutils/clAppUtils/tpUtils */ +/* defined in SecKeychainAPIPriv.h */ +static const int kSecAliasItemAttr = 'alis'; + +/* Macro to declare a CSSM_DB_SCHEMA_ATTRIBUTE_INFO */ +#define SCHEMA_ATTR_INFO(id, name, type) \ + { id, (char *)name, {0, NULL}, CSSM_DB_ATTRIBUTE_FORMAT_ ## type } + +/* Too bad we can't get this from inside of the Security framework. */ +static CSSM_DB_SCHEMA_ATTRIBUTE_INFO certSchemaAttrInfo[] = +{ + SCHEMA_ATTR_INFO(kSecCertTypeItemAttr, "CertType", UINT32), + SCHEMA_ATTR_INFO(kSecCertEncodingItemAttr, "CertEncoding", UINT32), + SCHEMA_ATTR_INFO(kSecLabelItemAttr, "PrintName", BLOB), + SCHEMA_ATTR_INFO(kSecAliasItemAttr, "Alias", BLOB), + SCHEMA_ATTR_INFO(kSecSubjectItemAttr, "Subject", BLOB), + SCHEMA_ATTR_INFO(kSecIssuerItemAttr, "Issuer", BLOB), + SCHEMA_ATTR_INFO(kSecSerialNumberItemAttr, "SerialNumber", BLOB), + SCHEMA_ATTR_INFO(kSecSubjectKeyIdentifierItemAttr, "SubjectKeyIdentifier", BLOB), + SCHEMA_ATTR_INFO(kSecPublicKeyHashItemAttr, "PublicKeyHash", BLOB) +}; +#define NUM_CERT_SCHEMA_ATTRS \ + (sizeof(certSchemaAttrInfo) / sizeof(CSSM_DB_SCHEMA_ATTRIBUTE_INFO)) + +/* Macro to declare a CSSM_DB_SCHEMA_INDEX_INFO */ +#define SCHEMA_INDEX_INFO(id, indexNum, indexType) \ + { id, CSSM_DB_INDEX_ ## indexType, CSSM_DB_INDEX_ON_ATTRIBUTE } + + +static CSSM_DB_SCHEMA_INDEX_INFO certSchemaIndices[] = +{ + SCHEMA_INDEX_INFO(kSecCertTypeItemAttr, 0, UNIQUE), + SCHEMA_INDEX_INFO(kSecIssuerItemAttr, 0, UNIQUE), + SCHEMA_INDEX_INFO(kSecSerialNumberItemAttr, 0, UNIQUE), + SCHEMA_INDEX_INFO(kSecCertTypeItemAttr, 1, NONUNIQUE), + SCHEMA_INDEX_INFO(kSecSubjectItemAttr, 2, NONUNIQUE), + SCHEMA_INDEX_INFO(kSecIssuerItemAttr, 3, NONUNIQUE), + SCHEMA_INDEX_INFO(kSecSerialNumberItemAttr, 4, NONUNIQUE), + SCHEMA_INDEX_INFO(kSecSubjectKeyIdentifierItemAttr, 5, NONUNIQUE), + SCHEMA_INDEX_INFO(kSecPublicKeyHashItemAttr, 6, NONUNIQUE) +}; +#define NUM_CERT_INDICES \ + (sizeof(certSchemaIndices) / sizeof(CSSM_DB_SCHEMA_INDEX_INFO)) + + +CSSM_RETURN tpAddCertSchema( + CSSM_DL_DB_HANDLE dlDbHand) +{ + return CSSM_DL_CreateRelation(dlDbHand, + CSSM_DL_DB_RECORD_X509_CERTIFICATE, + "CSSM_DL_DB_RECORD_X509_CERTIFICATE", + NUM_CERT_SCHEMA_ATTRS, + certSchemaAttrInfo, + NUM_CERT_INDICES, + certSchemaIndices); +} + + +/* copied verbatim from certTool */ + +/* + * 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 char *keyLabel, // look up by this + CSSM_DATA *rtnKeyDigest) // optionally RETURNED, if so, + // caller owns and must cuAppFree +{ + 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 = (char *)"Label"; + predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; + /* hope this cast is OK */ + predicate.Attribute.Value = &labelData; + query.SelectionPredicate = &predicate; + + query.QueryLimits.TimeLimit = 0; // FIXME - meaningful? + query.QueryLimits.SizeLimit = 1; // FIXME - meaningful? + query.QueryFlags = 0; // CSSM_QUERY_RETURN_DATA; // FIXME - used? + + /* 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 = (char *)"Label"; + attr.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; + + recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; + recordAttrs.NumberOfAttributes = 1; + recordAttrs.AttributeData = &attr; + + CSSM_DATA recordData = {0, NULL}; + crtn = CSSM_DL_DataGetFirst(dlDbHand, + &query, + &resultHand, + &recordAttrs, + &recordData, + &record); + /* abort only on success */ + if(crtn != CSSM_OK) { + cuPrintError("CSSM_DL_DataGetFirst", crtn); + return crtn; + } + + CSSM_KEY_PTR keyToDigest = (CSSM_KEY_PTR)recordData.Data; + CSSM_DATA_PTR keyDigest = NULL; + CSSM_CC_HANDLE ccHand; + crtn = CSSM_CSP_CreatePassThroughContext(cspHand, + keyToDigest, + &ccHand); + if(crtn) { + cuPrintError("CSSM_CSP_CreatePassThroughContext", crtn); + return crtn; + } + crtn = CSSM_CSP_PassThrough(ccHand, + CSSM_APPLECSP_KEYDIGEST, + NULL, + (void **)&keyDigest); + if(crtn) { + cuPrintError("CSSM_CSP_PassThrough(PUBKEYHASH)", crtn); + return -1; + } + CSSM_FreeKey(cspHand, NULL, keyToDigest, CSSM_FALSE); + CSSM_DeleteContext(ccHand); + + /* + * 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) { + cuPrintError("CSSM_GetAPIMemoryFunctions(DLHandle)", 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) { + cuPrintError("CSSM_DL_DataModify(PUBKEYHASH)", crtn); + return crtn; + } + crtn = CSSM_DL_DataAbortQuery(dlDbHand, resultHand); + if(crtn) { + cuPrintError("CSSM_DL_DataAbortQuery", crtn); + /* let's keep going in this case */ + } + crtn = CSSM_DL_FreeUniqueRecord(dlDbHand, record); + if(crtn) { + cuPrintError("CSSM_DL_FreeUniqueRecord", crtn); + /* let's keep going in this case */ + crtn = CSSM_OK; + } + + /* free resources */ + if(rtnKeyDigest) { + *rtnKeyDigest = *keyDigest; + } + else { + cuAppFree(keyDigest->Data, NULL); + /* FIXME - don't we have to free keyDigest itself? */ + } + return CSSM_OK; +} + +static CSSM_RETURN importPrivateKey( + CSSM_DL_DB_HANDLE dlDbHand, + CSSM_CSP_HANDLE cspHand, + const char *privKeyFileName, + CSSM_ALGORITHMS keyAlg, + CSSM_BOOL pemFormat, // of the file + CSSM_KEYBLOB_FORMAT keyFormat, // of the key blob itself, NONE means + // use default + CSSM_DATA *keyHash) // OPTIONALLY RETURNED - if so, caller + // owns and must cuAppFree() +{ + unsigned char *derKey = NULL; + unsigned derKeyLen; + unsigned char *pemKey = NULL; + unsigned pemKeyLen; + CSSM_KEY wrappedKey; + CSSM_KEY unwrappedKey; + CSSM_ACCESS_CREDENTIALS creds; + CSSM_CC_HANDLE ccHand = 0; + CSSM_RETURN crtn; + CSSM_DATA labelData; + CSSM_KEYHEADER_PTR hdr = &wrappedKey.KeyHeader; + CSSM_DATA descData = {0, NULL}; + CSSM_CSP_HANDLE rawCspHand = 0; + const char *privKeyLabel = NULL; + + /* + * Validate specified format for clarity + */ + switch(keyAlg) { + case CSSM_ALGID_RSA: + switch(keyFormat) { + case CSSM_KEYBLOB_RAW_FORMAT_NONE: + keyFormat = CSSM_KEYBLOB_RAW_FORMAT_PKCS1; // default + break; + case CSSM_KEYBLOB_RAW_FORMAT_PKCS1: + case CSSM_KEYBLOB_RAW_FORMAT_PKCS8: + break; + default: + printf("***RSA Private key must be in PKCS1 or PKCS8 " + "format\n"); + return CSSMERR_CSSM_INTERNAL_ERROR; + } + privKeyLabel = "Imported RSA key"; + break; + case CSSM_ALGID_DSA: + switch(keyFormat) { + case CSSM_KEYBLOB_RAW_FORMAT_NONE: + keyFormat = CSSM_KEYBLOB_RAW_FORMAT_OPENSSL; + // default + break; + case CSSM_KEYBLOB_RAW_FORMAT_FIPS186: + case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL: + case CSSM_KEYBLOB_RAW_FORMAT_PKCS8: + break; + default: + printf("***DSA Private key must be in openssl, FIPS186, " + "or PKCS8 format\n"); + return CSSMERR_CSSM_INTERNAL_ERROR; + } + privKeyLabel = "Imported DSA key"; + break; + case CSSM_ALGID_DH: + switch(keyFormat) { + case CSSM_KEYBLOB_RAW_FORMAT_NONE: + keyFormat = CSSM_KEYBLOB_RAW_FORMAT_PKCS8; // default + break; + case CSSM_KEYBLOB_RAW_FORMAT_PKCS8: + break; + default: + printf("***Diffie-Hellman Private key must be in" + "PKCS8 format.\n"); + return CSSMERR_CSSM_INTERNAL_ERROR; + } + privKeyLabel = "Imported Diffie-Hellman key"; + break; + } + if(readFile(privKeyFileName, &pemKey, &pemKeyLen)) { + printf("***Error reading private key from file %s. Aborting.\n", + privKeyFileName); + return CSSMERR_CSSM_INTERNAL_ERROR; + } + /* subsequent errors to done: */ + if(pemFormat) { + int rtn = pemDecode(pemKey, pemKeyLen, &derKey, &derKeyLen); + if(rtn) { + printf("***%s: Bad PEM formatting. Aborting.\n", + privKeyFileName); + crtn = CSSMERR_CSP_INVALID_KEY; + goto done; + } + } + else { + derKey = pemKey; + derKeyLen = pemKeyLen; + } + + /* importing a raw key into the CSPDL involves a NULL unwrap */ + memset(&unwrappedKey, 0, sizeof(CSSM_KEY)); + memset(&wrappedKey, 0, sizeof(CSSM_KEY)); + + /* set up the imported key to look like a CSSM_KEY */ + hdr->HeaderVersion = CSSM_KEYHEADER_VERSION; + hdr->BlobType = CSSM_KEYBLOB_RAW; + hdr->AlgorithmId = keyAlg; + hdr->KeyClass = CSSM_KEYCLASS_PRIVATE_KEY; + hdr->KeyAttr = CSSM_KEYATTR_EXTRACTABLE; + hdr->KeyUsage = CSSM_KEYUSE_ANY; + hdr->Format = keyFormat; + wrappedKey.KeyData.Data = derKey; + wrappedKey.KeyData.Length = derKeyLen; + + /* get key size in bits from raw CSP */ + rawCspHand = cuCspStartup(CSSM_TRUE); + if(rawCspHand == 0) { + printf("***Error attaching to CSP. Aborting.\n"); + crtn = CSSMERR_CSSM_INTERNAL_ERROR; + goto done; + } + CSSM_KEY_SIZE keySize; + crtn = CSSM_QueryKeySizeInBits(rawCspHand, CSSM_INVALID_HANDLE, &wrappedKey, &keySize); + if(crtn) { + cuPrintError("CSSM_QueryKeySizeInBits",crtn); + goto done; + } + hdr->LogicalKeySizeInBits = keySize.LogicalKeySizeInBits; + + memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); + crtn = CSSM_CSP_CreateSymmetricContext(cspHand, + CSSM_ALGID_NONE, // unwrapAlg + CSSM_ALGMODE_NONE, // unwrapMode + &creds, + NULL, // unwrappingKey + NULL, // initVector + CSSM_PADDING_NONE, // unwrapPad + 0, // Params + &ccHand); + if(crtn) { + cuPrintError("CSSM_CSP_CreateSymmetricContext", crtn); + goto done; + } + + /* add DL/DB to context */ + CSSM_CONTEXT_ATTRIBUTE newAttr; + newAttr.AttributeType = CSSM_ATTRIBUTE_DL_DB_HANDLE; + newAttr.AttributeLength = sizeof(CSSM_DL_DB_HANDLE); + newAttr.Attribute.Data = (CSSM_DATA_PTR)&dlDbHand; + crtn = CSSM_UpdateContextAttributes(ccHand, 1, &newAttr); + if(crtn) { + cuPrintError("CSSM_UpdateContextAttributes", crtn); + goto done; + } + + /* do the NULL unwrap */ + labelData.Data = (uint8 *)privKeyLabel; + labelData.Length = strlen(privKeyLabel) + 1; + crtn = CSSM_UnwrapKey(ccHand, + NULL, // PublicKey + &wrappedKey, + CSSM_KEYUSE_ANY, + CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT | + CSSM_KEYATTR_SENSITIVE |CSSM_KEYATTR_EXTRACTABLE, + &labelData, + NULL, // CredAndAclEntry + &unwrappedKey, + &descData); // required + if(crtn != CSSM_OK) { + cuPrintError("CSSM_UnwrapKey", crtn); + goto done; + } + + /* one more thing: bind this private key to its public key */ + crtn = setPubKeyHash(cspHand, dlDbHand, privKeyLabel, keyHash); + + /* We don't need the unwrapped key any more */ + CSSM_FreeKey(cspHand, + NULL, // access cred + &unwrappedKey, + CSSM_FALSE); // delete + +done: + if(ccHand) { + CSSM_DeleteContext(ccHand); + } + if(derKey) { + free(derKey); // mallocd by readFile() */ + } + if(pemFormat && pemKey) { + free(pemKey); + } + if(rawCspHand) { + CSSM_ModuleDetach(rawCspHand); + } + return crtn; +} + + +CSSM_RETURN importBadCert( + CSSM_DL_HANDLE dlHand, + const char *dbFileName, + const char *certFile, + const char *keyFile, + CSSM_ALGORITHMS keyAlg, + CSSM_BOOL pemFormat, // of the file + CSSM_KEYBLOB_FORMAT keyFormat, // of the key blob itself, NONE means + // use default + CSSM_BOOL verbose) +{ + CSSM_DL_DB_HANDLE dlDbHand = {dlHand, 0}; + CSSM_RETURN crtn; + CSSM_DATA keyDigest = {0, NULL}; + CSSM_DATA certData = {0, NULL}; + unsigned len; + + CSSM_CSP_HANDLE cspHand = cuCspStartup(CSSM_FALSE); + if(cspHand == 0) { + printf("***Error attaching to CSPDL. Aborting.\n"); + return CSSMERR_CSSM_ADDIN_LOAD_FAILED; + } + + /* + * 1. Open the (already existing) DB. + */ + dlDbHand.DBHandle = cuDbStartupByName(dlHand, + (char *)dbFileName, // bogus non-const prototype + CSSM_FALSE, // do NOT create it + CSSM_FALSE); // quiet + if(dlDbHand.DBHandle == 0) { + printf("Error opening %s. Aborting.\n", dbFileName); + return CSSMERR_DL_DATASTORE_DOESNOT_EXIST; + } + + /* + * Import key to DB, snagging its key digest along the way. + */ + crtn = importPrivateKey(dlDbHand, cspHand, + keyFile, keyAlg, pemFormat, keyFormat, + &keyDigest); + if(crtn) { + printf("***Error importing key %s. Aborting.\n", keyFile); + goto errOut; + } + + /* + * Now the cert. + */ + if(readFile(certFile, &certData.Data, &len)) { + printf("***Error reading cert from %s. Aborting.\n", certFile); + goto errOut; + } + certData.Length = len; + crtn = cuAddCertToDb(dlDbHand, &certData, + CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, + certFile, // printName + &keyDigest); + if(crtn == CSSMERR_DL_INVALID_RECORDTYPE) { + /* virgin DB, no cert schema: add schema and retry */ + crtn = tpAddCertSchema(dlDbHand); + if(crtn == CSSM_OK) { + crtn = cuAddCertToDb(dlDbHand, &certData, + CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, + certFile, // printName + &keyDigest); + } + } + if(crtn) { + printf("***Error importing cert %s. Aborting.\n", certFile); + } +errOut: + if(keyDigest.Data) { + cuAppFree(keyDigest.Data, NULL); + } + if(dlDbHand.DBHandle) { + CSSM_DL_DbClose(dlDbHand); + } + if(certData.Data) { + free(certData.Data); + } + return crtn; +}