]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_pkcs12/lib/pkcs12Keychain.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_pkcs12 / lib / pkcs12Keychain.cpp
diff --git a/Security/libsecurity_pkcs12/lib/pkcs12Keychain.cpp b/Security/libsecurity_pkcs12/lib/pkcs12Keychain.cpp
new file mode 100644 (file)
index 0000000..ce7a3b4
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) 2003-2004,2011-2014 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The 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.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+/*
+ * pkcs12Keychain.h - P12Coder keychain-related functions.
+ */
+#include "pkcs12Coder.h"
+#include "pkcs12Templates.h"
+#include "pkcs12Utils.h"
+#include "pkcs12Debug.h"
+#include "pkcs12Crypto.h"
+#include <Security/cssmerr.h>
+#include <security_cdsa_utils/cuDbUtils.h>                     // cuAddCrlToDb()
+#include <security_asn1/nssUtils.h>
+#include <security_cdsa_utilities/KeySchema.h>                 /* private API */
+#include <security_keychain/SecImportExportCrypto.h>   /* private API */
+
+/*
+ * Store the results of a successful decode in app-specified 
+ * keychain per mImportFlags. Also assign public key hash attributes to any 
+ * private keys found.
+ */
+void P12Coder::storeDecodeResults()
+{
+       assert(mKeychain != NULL);
+       assert(mDlDbHand.DLHandle != 0);
+       if(mImportFlags & kSecImportKeys) {
+               setPrivateKeyHashes();
+       }
+       if(mImportFlags & kSecImportCertificates) {
+               for(unsigned dex=0; dex<numCerts(); dex++) {
+                       P12CertBag *certBag = mCerts[dex];
+                       SecCertificateRef secCert = certBag->getSecCert();
+                       OSStatus ortn = SecCertificateAddToKeychain(secCert, mKeychain);
+                       CFRelease(secCert);
+                       switch(ortn) {
+                               case errSecSuccess:                                     // normal
+                                       p12DecodeLog("cert added to keychain");
+                                       break;
+                               case errSecDuplicateItem:       // dup cert, OK< skip
+                                       p12DecodeLog("skipping dup cert");
+                                       break;
+                               default:
+                                       p12ErrorLog("SecCertificateAddToKeychain failure\n");
+                                       MacOSError::throwMe(ortn);
+                       }
+               }
+       }
+       
+       if(mImportFlags & kSecImportCRLs) {
+               for(unsigned dex=0; dex<numCrls(); dex++) {
+                       P12CrlBag *crlBag = mCrls[dex];
+                       CSSM_RETURN crtn = cuAddCrlToDb(mDlDbHand,
+                               clHand(),
+                               &crlBag->crlData(),
+                               NULL);                  // no URI known
+                       switch(crtn) {
+                               case CSSM_OK:                                                           // normal
+                                       p12DecodeLog("CRL added to keychain");
+                                       break;
+                               case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA:      // dup, ignore
+                                       p12DecodeLog("skipping dup CRL");
+                                       break;
+                               default:
+                                       p12LogCssmError("Error adding CRL to keychain", crtn);
+                                       CssmError::throwMe(crtn);
+                       }
+               }
+       }
+       
+       /* If all of that succeeded, post notification for imported keys */
+       if(mImportFlags & kSecImportKeys) {
+               notifyKeyImport();
+       }
+}
+
+/*
+ * Assign appropriate public key hash attribute to each 
+ * private key. 
+ */
+void P12Coder::setPrivateKeyHashes()
+{
+       CSSM_KEY_PTR newKey;
+       
+       for(unsigned dex=0; dex<numKeys(); dex++) {
+               P12KeyBag *keyBag = mKeys[dex];
+               
+               CSSM_DATA newLabel = {0, NULL};
+               CFStringRef friendlyName = keyBag->friendlyName();
+               newKey = NULL;
+               CSSM_RETURN crtn = p12SetPubKeyHash(mCspHand,
+                       mDlDbHand,
+                       keyBag->label(),
+                       p12StringToUtf8(friendlyName, mCoder),
+                       mCoder,
+                       newLabel,
+                       newKey);
+               if(friendlyName) {
+                       CFRelease(friendlyName);
+               }
+               switch(crtn) {
+                       case CSSM_OK:
+                               /* update key's label in case we have to delete on error */
+                               keyBag->setLabel(newLabel);
+                               p12DecodeLog("set pub key hash for private key");
+                               break;
+                       case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA:
+                               /*
+                                * Special case: update keyBag's CSSM_KEY and proceed without error
+                                */
+                               p12DecodeLog("ignoring dup private key");
+                               assert(newKey != NULL);
+                               keyBag->setKey(newKey);
+                               keyBag->dupKey(true);
+                               /* update key's label in case we have to delete on error */
+                               keyBag->setLabel(newLabel);
+                               break;
+                       default:
+                               p12ErrorLog("p12SetPubKeyHash failure\n");
+                               CssmError::throwMe(crtn);
+               }
+       }
+}
+
+/*
+ * Post keychain notification for imported keys. 
+ */
+void P12Coder::notifyKeyImport()
+{
+       if(mKeychain == NULL) {
+               /* Can't notify if user only gave us DLDB */
+               return;
+       }
+       for(unsigned dex=0; dex<numKeys(); dex++) {
+               P12KeyBag *keyBag = mKeys[dex];
+               if(keyBag->dupKey()) {
+                       /* no notification for keys we merely looked up */
+                       continue;
+               }
+               CssmData &labelData = CssmData::overlay(keyBag->label());
+               OSStatus ortn = impExpKeyNotify(mKeychain, labelData, *keyBag->key());
+               if(ortn) {
+                       p12ErrorLog("notifyKeyImport: impExpKeyNotify returned %ld\n", (unsigned long)ortn);
+                       MacOSError::throwMe(ortn);
+               }
+       }
+}
+
+/*
+ * Given a P12KeyBag, find a matching P12CertBag. Keys and certs
+ * "match" if their localKeyIds match. Returns NULL if not found.
+ */
+P12CertBag *P12Coder::findCertForKey(
+       P12KeyBag *keyBag)
+{
+       assert(keyBag != NULL);
+       CSSM_DATA &keyKeyId = keyBag->localKeyIdCssm();
+       
+       for(unsigned dex=0; dex<numCerts(); dex++) {
+               P12CertBag *certBag = mCerts[dex];
+               CSSM_DATA &certKeyId = certBag->localKeyIdCssm();
+               if(nssCompareCssmData(&keyKeyId, &certKeyId)) {
+                       p12DecodeLog("findCertForKey SUCCESS");
+                       return certBag;
+               }
+       }
+       p12DecodeLog("findCertForKey FAILURE");
+       return NULL;
+}
+
+/*
+ * Export items specified as SecKeychainItemRefs.
+ */
+void P12Coder::exportKeychainItems(
+       CFArrayRef                              items)
+{
+       assert(items != NULL);
+       CFIndex numItems = CFArrayGetCount(items);
+       for(CFIndex dex=0; dex<numItems; dex++) {
+               const void *item = CFArrayGetValueAtIndex(items, dex);
+               if(item == NULL) {
+                       p12ErrorLog("exportKeychainItems: NULL item\n");
+                       MacOSError::throwMe(errSecParam);
+               }
+               CFTypeID itemType = CFGetTypeID(item);
+               if(itemType == SecCertificateGetTypeID()) {
+                       addSecCert((SecCertificateRef)item);
+               }
+               else if(itemType == SecKeyGetTypeID()) {
+                       addSecKey((SecKeyRef)item);
+               }
+               else {
+                       p12ErrorLog("exportKeychainItems: unknown item\n");
+                       MacOSError::throwMe(errSecParam);               
+               }
+       }
+}
+
+/*
+ * Gross kludge to work around the fact that SecKeyRefs have no attributes which 
+ * are visible at the Sec layer. Not only are the attribute names we happen 
+ * to know about (Label, PrintName) not publically visible anywhere in the 
+ * system, but the *format* of the attr names for SecKeyRefs differs from
+ * the format of all other SecKeychainItems (NAME_AS_STRING for SecKeys, 
+ * NAME_AS_INTEGER for everything else).
+ *
+ * So. We use the privately accessible schema definition table for
+ * keys to map from the attr name strings we happen to know about to a 
+ * totally private name-as-int index which we can then use in the 
+ * SecKeychainItemCopyAttributesAndData mechanism. 
+ *
+ * This will go away if SecKeyRef defines its actual attrs as strings, AND
+ * the SecKeychainSearch mechanism knows to specify attr names for SecKeyRefs
+ * as strings rather than integers. 
+ */
+static OSStatus attrNameToInt(
+       const char *name, 
+       UInt32 *attrInt)
+{
+       const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *attrList = 
+               KeySchema::KeySchemaAttributeList;
+       unsigned numAttrs = KeySchema::KeySchemaAttributeCount;
+       for(unsigned dex=0; dex<numAttrs; dex++) {
+               const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *info = &attrList[dex];
+               if(!strcmp(name, info->AttributeName)) {
+                       *attrInt = info->AttributeId;
+                       return errSecSuccess;
+               }
+       }
+       return errSecParam;
+}
+
+void P12Coder::addSecKey(
+       SecKeyRef       keyRef)
+{
+       /* get the cert's attrs (not data) */
+       
+       /* 
+        * Convert the attr name strings we happen to know about to 
+        * unknowable name-as-int values.
+        */
+       UInt32 printNameTag;
+       OSStatus ortn = attrNameToInt(P12_KEY_ATTR_PRINT_NAME, &printNameTag);
+       if(ortn) {
+               p12ErrorLog("addSecKey: problem looking up key attr name\n");
+               MacOSError::throwMe(ortn);
+       }
+       UInt32 labelHashTag;
+       ortn = attrNameToInt(P12_KEY_ATTR_LABEL_AND_HASH, &labelHashTag);
+       if(ortn) {
+               p12ErrorLog("addSecKey: problem looking up key attr name\n");
+               MacOSError::throwMe(ortn);
+       }
+
+       UInt32 tags[2];
+       tags[0] = printNameTag;
+       tags[1] = labelHashTag;
+       
+       /* I don't know what the format field is for */
+       SecKeychainAttributeInfo attrInfo;
+       attrInfo.count = 2;
+       attrInfo.tag = tags;
+       attrInfo.format = NULL; // ???
+       
+       /* FIXME header says this is an IN/OUT param, but it's not */
+       SecKeychainAttributeList *attrList = NULL;
+       
+       ortn = SecKeychainItemCopyAttributesAndData(
+               (SecKeychainItemRef)keyRef, 
+               &attrInfo,
+               NULL,                   // itemClass
+               &attrList, 
+               NULL,                   // don't need the data
+               NULL);
+       if(ortn) {
+               p12ErrorLog("addSecKey: SecKeychainItemCopyAttributesAndData "
+                       "error\n");
+               MacOSError::throwMe(ortn);              
+       }
+       
+       /* Snag the attrs, convert to something useful */
+       CFStringRef friendName = NULL;
+       CFDataRef localKeyId = NULL;
+       for(unsigned i=0; i<attrList->count; i++) {
+               SecKeychainAttribute *attr = &attrList->attr[i];
+               if(attr->tag == printNameTag) {
+                       friendName = CFStringCreateWithBytes(NULL, 
+                               (UInt8 *)attr->data, attr->length, 
+                               kCFStringEncodingUTF8,  false);
+               }
+               else if(attr->tag == labelHashTag) {
+                       localKeyId = CFDataCreate(NULL, (UInt8 *)attr->data, attr->length);
+               }
+               else {
+                       p12ErrorLog("addSecKey: unexpected attr tag\n");
+                       MacOSError::throwMe(errSecParam);               
+                       
+               }
+       }
+       
+       /*
+        * Infer the CSP associated with this key.
+        * FIXME: this should be an attribute of the SecKeyRef itself,
+        * not inferred from the keychain it happens to be living on
+        * (SecKeyRefs should not have to be attached to Keychains at
+        * this point).
+        */
+       SecKeychainRef kcRef;
+       ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)keyRef, &kcRef);
+       if(ortn) {
+               p12ErrorLog("addSecKey: SecKeychainItemCopyKeychain returned %d\n", (int)ortn);
+               MacOSError::throwMe(ortn);              
+       }
+       CSSM_CSP_HANDLE cspHand;
+       ortn = SecKeychainGetCSPHandle(kcRef, &cspHand);
+       if(ortn) {
+               p12ErrorLog("addSecKey: SecKeychainGetCSPHandle returned %d\n", (int)ortn);
+               MacOSError::throwMe(ortn);
+       }
+       CFRelease(kcRef);
+       
+       /* and the CSSM_KEY itself */
+       const CSSM_KEY *cssmKey;
+       ortn = SecKeyGetCSSMKey(keyRef, &cssmKey);
+       if(ortn) {
+               p12ErrorLog("addSecKey: SecKeyGetCSSMKey returned %d\n", (int)ortn);
+               MacOSError::throwMe(ortn);              
+       }
+       
+       /* Cook up a key bag and save it */
+       P12KeyBag *keyBag = new P12KeyBag(cssmKey,
+               cspHand, 
+               friendName,     localKeyId, 
+               NULL,                   // other attrs
+               mCoder,
+               keyRef);
+       addKey(keyBag);
+       SecKeychainItemFreeAttributesAndData(attrList, NULL);
+       if(friendName) {
+               CFRelease(friendName);
+       }
+       if(localKeyId) {
+               CFRelease(localKeyId);
+       }
+}
+
+void P12Coder::addSecCert(
+       SecCertificateRef       certRef)
+{
+       /* get the cert's attrs and data */
+       /* I don't know what the format field is for */
+       SecKeychainAttributeInfo attrInfo;
+       attrInfo.count = 2;
+       UInt32 tags[2] = {kSecLabelItemAttr, kSecPublicKeyHashItemAttr};
+       attrInfo.tag = tags;
+       attrInfo.format = NULL; // ???
+       
+       /* FIXME header says this is an IN/OUT param, but it's not */
+       SecKeychainAttributeList *attrList = NULL;
+       UInt32 certLen;
+       void *certData;
+       
+       OSStatus ortn = SecKeychainItemCopyAttributesAndData(
+               (SecKeychainItemRef)certRef, 
+               &attrInfo,
+               NULL,                   // itemClass
+               &attrList, 
+               &certLen,       
+               &certData);
+       if(ortn) {
+               p12ErrorLog("addSecCert: SecKeychainItemCopyAttributesAndData "
+                       "error\n");
+               MacOSError::throwMe(ortn);              
+       }
+       
+       /* Snag the attrs, convert to something useful */
+       CFStringRef friendName = NULL;
+       CFDataRef localKeyId = NULL;
+       for(unsigned i=0; i<attrList->count; i++) {
+               SecKeychainAttribute *attr = &attrList->attr[i];
+               switch(attr->tag) {
+                       case kSecPublicKeyHashItemAttr:
+                               localKeyId = CFDataCreate(NULL, (UInt8 *)attr->data, attr->length);
+                               break;
+                       case kSecLabelItemAttr:
+                               /* FIXME: always in UTF8? */
+                               friendName = CFStringCreateWithBytes(NULL, 
+                                       (UInt8 *)attr->data, attr->length, kCFStringEncodingUTF8,
+                                       false);
+                               break;
+                       default:
+                               p12ErrorLog("addSecCert: unexpected attr tag\n");
+                               MacOSError::throwMe(errSecParam);               
+                       
+               }
+       }
+       
+       /* Cook up a cert bag and save it */
+       CSSM_DATA cData = {certLen, (uint8 *)certData};
+       P12CertBag *certBag = new P12CertBag(CT_X509, cData, friendName,
+               localKeyId, NULL, mCoder);
+       addCert(certBag);
+       SecKeychainItemFreeAttributesAndData(attrList, certData);
+       if(friendName) {
+               CFRelease(friendName);
+       }
+       if(localKeyId) {
+               CFRelease(localKeyId);
+       }
+}
+
+/*
+ * Delete anything stored in a keychain during decode, called on 
+ * decode error.
+ * Currently the only thing we have to deal with is private keys,
+ * since certs and CRLs don't get stored until the end of a successful
+ * decode. 
+ */
+void P12Coder::deleteDecodedItems()
+{
+       if(!(mImportFlags & kSecImportKeys)) {
+               /* no keys stored, done */
+               return;
+       }
+       if(mDlDbHand.DLHandle == 0) {
+               /* no keychain, done */
+               return;
+       }
+       
+       unsigned nKeys = numKeys();
+       for(unsigned dex=0; dex<nKeys; dex++) {
+               P12KeyBag *keyBag = mKeys[dex];
+               p12DeleteKey(mDlDbHand, keyBag->label());
+       }
+
+}
+