]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTests/cspxutils/dbTool/dbCert.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTests / cspxutils / dbTool / dbCert.cpp
diff --git a/SecurityTests/cspxutils/dbTool/dbCert.cpp b/SecurityTests/cspxutils/dbTool/dbCert.cpp
new file mode 100644 (file)
index 0000000..aa73443
--- /dev/null
@@ -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 <Security/Security.h>
+#include <security_cdsa_utils/cuFileIo.h>
+#include <security_cdsa_utils/cuDbUtils.h> 
+#include <security_cdsa_utils/cuCdsaUtils.h>
+#include <security_cdsa_utils/cuPem.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* 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;
+}