X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/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 index 00000000..ce7a3b4f --- /dev/null +++ b/Security/libsecurity_pkcs12/lib/pkcs12Keychain.cpp @@ -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 +#include // cuAddCrlToDb() +#include +#include /* private API */ +#include /* 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; dexgetSecCert(); + 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; dexcrlData(), + 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; dexfriendlyName(); + 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; dexdupKey()) { + /* 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; dexlocalKeyIdCssm(); + 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; dexAttributeName)) { + *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; icount; 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; icount; 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; dexlabel()); + } + +} +