SecImportExportAgg.cpp [plain text]
#include "SecImportExportAgg.h"
#include "SecExternalRep.h"
#include "SecImportExportUtils.h"
#include "SecNetscapeTemplates.h"
#include "Certificate.h"
#include <security_pkcs12/SecPkcs12.h>
#include <Security/SecBase.h>
#include <Security/SecCmsDecoder.h>
#include <Security/SecCmsEncoder.h>
#include <Security/SecCmsMessage.h>
#include <Security/SecCmsContentInfo.h>
#include <Security/SecCmsSignedData.h>
#include <security_asn1/SecNssCoder.h>
#include <security_asn1/nssUtils.h>
#include <security_cdsa_utils/cuCdsaUtils.h>
#include <security_keychain/Globals.h>
#include <Security/SecCertificatePriv.h>
#include <Security/SecIdentityPriv.h>
#include <Security/SecKeyPriv.h>
using namespace Security;
using namespace KeychainCore;
#pragma mark --- Aggregate Export routines ---
OSStatus impExpPkcs12Export(
CFArrayRef exportReps, SecItemImportExportFlags flags, const SecKeyImportExportParameters *keyParams, CFMutableDataRef outData) {
SecPkcs12CoderRef p12Coder;
OSStatus ortn = errSecSuccess;
CFMutableArrayRef exportItems; CFDataRef tmpData = NULL;
CSSM_CSP_HANDLE cspHand = CSSM_INVALID_HANDLE;
CSSM_KEY *passKey = NULL;
CFStringRef phraseStr = NULL;
if( (keyParams == NULL) ||
( (keyParams->passphrase == NULL) &&
!(keyParams->flags & kSecKeySecurePassphrase) ) ) {
return errSecPassphraseRequired;
}
CFIndex numReps = CFArrayGetCount(exportReps);
if(numReps == 0) {
SecImpExpDbg("impExpPkcs12Export: no items to export");
return errSecItemNotFound;
}
exportItems = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
SecKeychainRef kcRef = nil;
for(CFIndex dex=0; dex<numReps; dex++) {
SecExportRep *exportRep =
(SecExportRep *)CFArrayGetValueAtIndex(exportReps, dex);
SecKeychainItemRef kcItemRef = (SecKeychainItemRef)exportRep->kcItem();
CFArrayAppendValue(exportItems, kcItemRef);
if(kcRef == nil) {
SecKeychainItemCopyKeychain(kcItemRef, &kcRef);
}
}
ortn = SecPkcs12CoderCreate(&p12Coder);
if(ortn) {
return ortn;
}
ortn = SecPkcs12SetKeychain(p12Coder, kcRef);
if(ortn) {
goto errOut;
}
ortn = SecKeychainGetCSPHandle(kcRef, &cspHand);
if(ortn) {
SecImpExpDbg("SecKeychainGetCSPHandle error");
goto errOut;
}
ortn = impExpPassphraseCommon(keyParams, cspHand, SPF_String,
VP_Export, (CFTypeRef *)&phraseStr, &passKey);
if(ortn) {
goto errOut;
}
if(phraseStr != NULL) {
ortn = SecPkcs12SetMACPassphrase(p12Coder, phraseStr);
CFRelease(phraseStr);
if(ortn) {
SecImpExpDbg("SecPkcs12SetMACPassphrase error");
goto errOut;
}
}
else {
assert(passKey != NULL);
ortn = SecPkcs12SetMACPassKey(p12Coder, passKey);
if(ortn) {
SecImpExpDbg("SecPkcs12SetMACPassphrase error");
goto errOut;
}
}
ortn = SecPkcs12ExportKeychainItems(p12Coder, exportItems);
if(ortn) {
SecImpExpDbg("impExpPkcs12Export: SecPkcs12ExportKeychainItems failure");
goto errOut;
}
ortn = SecPkcs12Encode(p12Coder, &tmpData);
if(ortn) {
SecImpExpDbg("impExpPkcs12Export: SecPkcs12Encode failure");
goto errOut;
}
CFDataAppendBytes(outData, CFDataGetBytePtr(tmpData), CFDataGetLength(tmpData));
errOut:
SecPkcs12CoderRelease(p12Coder);
if(passKey != NULL) {
CSSM_FreeKey(cspHand, NULL, passKey, CSSM_FALSE);
free(passKey);
}
if(kcRef) {
CFRelease(kcRef);
}
if(exportItems) {
CFRelease(exportItems);
}
if(tmpData) {
CFRelease(tmpData);
}
return ortn;
}
OSStatus impExpPkcs7Export(
CFArrayRef exportReps, SecItemImportExportFlags flags, const SecKeyImportExportParameters *keyParams, CFMutableDataRef outData) {
SecCmsSignedDataRef sigd = NULL;
SecCertificateRef certRef;
OSStatus ortn;
CFIndex numCerts = CFArrayGetCount(exportReps);
SecExportRep *exportRep;
SecCmsContentInfoRef cinfo = NULL;
SecArenaPoolRef arena = NULL;
SecCmsEncoderRef ecx;
CSSM_DATA output = { 0, NULL };
if(numCerts == 0) {
SecImpExpDbg("impExpPkcs7Export: no certs to export");
return errSecSuccess;
}
SecCmsMessageRef cmsg = SecCmsMessageCreate(NULL);
if(cmsg == NULL) {
SecImpExpDbg("impExpPkcs7Export: SecCmsMessageCreate failure");
return errSecInternalComponent;
}
exportRep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, 0);
assert(exportRep != NULL);
if(exportRep->externType() != kSecItemTypeCertificate) {
SecImpExpDbg("impExpPkcs7Export: non-cert item");
ortn = errSecParam;
goto errOut;
}
certRef = (SecCertificateRef)exportRep->kcItem();
sigd = SecCmsSignedDataCreateCertsOnly(cmsg, certRef, false);
if(sigd == NULL) {
SecImpExpDbg("impExpPkcs7Export: SecCmsSignedDataCreateCertsOnly failure");
ortn = errSecInternalComponent;
goto errOut;
}
for (CFIndex dex=1; dex<numCerts; dex++) {
exportRep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, dex);
assert(exportRep != NULL);
if(exportRep->externType() != kSecItemTypeCertificate) {
SecImpExpDbg("impExpPkcs7Export: non-cert item");
ortn = errSecParam;
goto errOut;
}
certRef = (SecCertificateRef)exportRep->kcItem();
ortn = SecCmsSignedDataAddCertChain(sigd, certRef);
if(ortn) {
SecImpExpDbg("impExpPkcs7Export: SecCmsSignedDataAddCertChain error");
goto errOut;
}
}
cinfo = SecCmsMessageGetContentInfo(cmsg);
if(cinfo == NULL) {
SecImpExpDbg("impExpPkcs7Export: SecCmsMessageGetContentInfo returned NULL");
ortn = errSecInternalComponent;
goto errOut;
}
ortn = SecCmsContentInfoSetContentSignedData(cmsg, cinfo, sigd);
if(ortn) {
SecImpExpDbg("impExpPkcs7Export: SecCmsContentInfoSetContentSignedData error");
goto errOut;
}
cinfo = SecCmsSignedDataGetContentInfo(sigd);
if(cinfo == NULL) {
SecImpExpDbg("impExpPkcs7Export: SecCmsSignedDataGetContentInfo returned NULL");
ortn = errSecInternalComponent;
goto errOut;
}
ortn = SecCmsContentInfoSetContentData(cmsg, cinfo, NULL,
false );
if(ortn) {
SecImpExpDbg("impExpPkcs7Export: SecCmsContentInfoSetContentData error");
goto errOut;
}
ortn = SecArenaPoolCreate(1024, &arena);
if(ortn) {
SecImpExpDbg("impExpPkcs7Export: SecArenaPoolCreate error");
goto errOut;
}
ortn = SecCmsEncoderCreate(cmsg,
NULL, NULL,
&output, arena,
NULL, NULL,
NULL, NULL,
NULL, NULL,
&ecx );
if(ortn) {
SecImpExpDbg("impExpPkcs7Export: SecCmsEncoderCreate error");
goto errOut;
}
ortn = SecCmsEncoderFinish(ecx);
if(ortn) {
SecImpExpDbg("impExpPkcs7Export: SecCmsEncoderFinish returned NULL");
goto errOut;
}
CFDataAppendBytes(outData, output.Data, output.Length);
errOut:
if(cmsg != NULL) {
SecCmsMessageDestroy(cmsg);
}
if(arena != NULL) {
SecArenaPoolFree(arena, false);
}
return ortn;
}
#pragma mark --- Aggregate Import routines ---
OSStatus impExpPkcs12Import(
CFDataRef inData,
SecItemImportExportFlags flags,
const SecKeyImportExportParameters *keyParams, ImpPrivKeyImportState &keyImportState,
SecKeychainRef importKeychain, CSSM_CSP_HANDLE cspHand, CFMutableArrayRef outArray) {
SecPkcs12CoderRef p12Coder = NULL;
OSStatus ortn;
CFIndex numCerts;
CFIndex numKeys;
CFIndex dex;
CFMutableArrayRef privKeys = NULL;
CSSM_KEY *passKey = NULL;
CFStringRef phraseStr = NULL;
CSSM_KEYUSE keyUsage = CSSM_KEYUSE_ANY;
CSSM_KEYATTR_FLAGS keyAttrs = CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_EXTRACTABLE |
CSSM_KEYATTR_RETURN_REF;
if( (keyParams == NULL) ||
( (keyParams->passphrase == NULL) &&
!(keyParams->flags & kSecKeySecurePassphrase) ) ) {
return errSecPassphraseRequired;
}
ortn = SecPkcs12CoderCreate(&p12Coder);
if(ortn) {
SecImpExpDbg("SecPkcs12CoderCreate error");
return ortn;
}
CSSM_CL_HANDLE clHand = cuClStartup();
CSSM_CSP_HANDLE rawCspHand = cuCspStartup(CSSM_TRUE); if((clHand == 0) || (rawCspHand == 0)) {
return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
}
assert(cspHand != CSSM_INVALID_HANDLE);
if(importKeychain != NULL) {
ortn = SecPkcs12SetKeychain(p12Coder, importKeychain);
if(ortn) {
SecImpExpDbg("SecPkcs12SetKeychain error");
goto errOut;
}
}
else {
if(cspHand == CSSM_INVALID_HANDLE) {
ortn = errSecParam;
goto errOut;
}
ortn = SecPkcs12SetCspHandle(p12Coder, cspHand);
if(ortn) {
SecImpExpDbg("SecPkcs12SetCspHandle error");
goto errOut;
}
}
ortn = impExpPassphraseCommon(keyParams, cspHand, SPF_String,
VP_Import, (CFTypeRef *)&phraseStr, &passKey);
if(ortn) {
goto errOut;
}
if(phraseStr != NULL) {
ortn = SecPkcs12SetMACPassphrase(p12Coder, phraseStr);
CFRelease(phraseStr);
if(ortn) {
SecImpExpDbg("SecPkcs12SetMACPassphrase error");
goto errOut;
}
}
else {
assert(passKey != NULL);
ortn = SecPkcs12SetMACPassKey(p12Coder, passKey);
if(ortn) {
SecImpExpDbg("SecPkcs12SetMACPassphrase error");
goto errOut;
}
}
if(keyImportState != PIS_NoLimit) {
bool foundOneKey = false;
if(keyImportState == PIS_NoMore) {
foundOneKey = true;
}
ortn = SecPkcs12LimitPrivateKeyImport(p12Coder, foundOneKey);
if(ortn) {
SecImpExpDbg("SecPkcs12LimitPrivateKeyImport error");
goto errOut;
}
}
if(keyParams != NULL) {
if(keyParams->keyUsage != 0) {
keyUsage = keyParams->keyUsage;
}
if(keyParams->keyAttributes != 0) {
keyAttrs = keyParams->keyAttributes;
}
if(keyParams->flags & kSecKeyNoAccessControl) {
ortn = SecPkcs12SetAccess(p12Coder, NULL);
if(ortn) {
SecImpExpDbg("SecPkcs12SetAccess error");
goto errOut;
}
}
else if(keyParams->accessRef != NULL) {
ortn = SecPkcs12SetAccess(p12Coder, keyParams->accessRef);
if(ortn) {
SecImpExpDbg("SecPkcs12SetAccess error");
goto errOut;
}
}
}
ortn = SecPkcs12SetKeyUsage(p12Coder, keyUsage);
if(ortn) {
SecImpExpDbg("SecPkcs12SetKeyUsage error");
goto errOut;
}
ortn = SecPkcs12SetKeyAttrs(p12Coder, keyAttrs);
if(ortn) {
SecImpExpDbg("SecPkcs12SetKeyAttrs error");
goto errOut;
}
ortn = SecPkcs12Decode(p12Coder, inData);
if(ortn) {
SecImpExpDbg("SecPkcs12Decode error");
goto errOut;
}
if(outArray == NULL) {
goto errOut;
}
ortn = SecPkcs12CertificateCount(p12Coder, &numCerts);
if(ortn) {
SecImpExpDbg("SecPkcs12CertificateCount error");
goto errOut;
}
ortn = SecPkcs12PrivateKeyCount(p12Coder, &numKeys);
if(ortn) {
SecImpExpDbg("SecPkcs12PrivateKeyCount error");
goto errOut;
}
privKeys = CFArrayCreateMutable(NULL, numKeys, NULL);
for(dex=0; dex<numKeys; dex++) {
CSSM_KEY_PTR privKey;
ortn = SecPkcs12GetCssmPrivateKey(p12Coder,
dex, &privKey, NULL, NULL, NULL);
CFArrayAppendValue(privKeys, privKey);
}
for(dex=0; dex<numCerts; dex++) {
SecCertificateRef certRef = NULL; SecCertificateRef importedCertRef = NULL; SecCertificateRef itemImplRef = NULL; CSSM_KEY_PTR pubKey = NULL; CSSM_KEY_PTR privKey = NULL; CSSM_DATA certData; CSSM_DATA pubKeyDigest = {0, NULL}; CSSM_DATA privKeyDigest = {0, NULL}; bool foundIdentity = false;
ortn = SecPkcs12CopyCertificate(p12Coder, dex, &certRef,
NULL, NULL, NULL);
if(ortn) {
SecImpExpDbg("SecPkcs12CopyCertificate error");
goto errOut;
}
if(importKeychain == NULL) {
goto loopEnd;
}
{
StorageManager::KeychainList keychains;
globals().storageManager.optionalSearchList(importKeychain, keychains);
itemImplRef = SecCertificateCreateItemImplInstance(certRef);
SecPointer<Certificate> cert = Certificate::required(itemImplRef);
CFRelease(itemImplRef);
itemImplRef = NULL;
importedCertRef = cert->findInKeychain(keychains)->handle();
if (importedCertRef) {
SecCertificateRef tmpRef = SecCertificateCreateFromItemImplInstance(importedCertRef);
CFRelease(importedCertRef);
importedCertRef = tmpRef;
}
}
if(!importedCertRef) {
SecImpExpDbg("SecCertificateGetData error (couldn't find cert)");
goto loopEnd;
}
ortn = SecCertificateGetData(importedCertRef, &certData);
if(ortn) {
SecImpExpDbg("SecCertificateGetData error");
goto loopEnd;
}
ortn = CSSM_CL_CertGetKeyInfo(clHand, &certData, &pubKey);
if(ortn) {
SecImpExpDbg("SecCertificateGetData error");
goto loopEnd;
}
ortn = impExpKeyDigest(rawCspHand, pubKey, &pubKeyDigest);
if(ortn) {
goto loopEnd;
}
numKeys = CFArrayGetCount(privKeys);
for(CFIndex privDex=0; privDex<numKeys; privDex++) {
privKey = (CSSM_KEY_PTR)CFArrayGetValueAtIndex(privKeys, privDex);
assert(privKey != NULL);
ortn = impExpKeyDigest(cspHand, privKey, &privKeyDigest);
if(ortn) {
goto loopEnd;
}
CSSM_BOOL digestMatch = cuCompareCssmData(&pubKeyDigest, &privKeyDigest);
impExpFreeCssmMemory(cspHand, privKeyDigest.Data);
if(digestMatch) {
SecIdentityRef idRef = NULL;
ortn = SecIdentityCreateWithCertificate(importKeychain,
importedCertRef, &idRef);
if(ortn == errSecSuccess) {
SecImpExpDbg("P12Import: generating a SecIdentityRef");
assert(outArray != NULL);
CFArrayAppendValue(outArray, idRef);
CFRelease(idRef); idRef = NULL;
CFArrayRemoveValueAtIndex(privKeys, privDex);
foundIdentity = true;
goto loopEnd;
}
}
}
loopEnd:
assert(certRef != NULL);
if(!foundIdentity ) {
assert(outArray != NULL);
CFArrayAppendValue(outArray, certRef);
}
CFRelease(certRef); certRef = NULL;
if (importedCertRef) {
CFRelease(importedCertRef);
importedCertRef = NULL;
}
if(pubKey != NULL) {
CSSM_FreeKey(rawCspHand, NULL, pubKey, CSSM_FALSE);
impExpFreeCssmMemory(clHand, pubKey);
pubKey = NULL;
}
if(pubKeyDigest.Data != NULL) {
impExpFreeCssmMemory(rawCspHand, pubKeyDigest.Data);
pubKeyDigest.Data = NULL;
}
if(ortn) {
goto errOut;
}
}
errOut:
#if 0
if(privKeys) {
if(ortn == errSecSuccess) { numKeys = CFArrayGetCount(privKeys);
for(dex=0; dex<numKeys; dex++) {
SecKeyRef keyRef;
CSSM_KEY_PTR privKey =
(CSSM_KEY_PTR)CFArrayGetValueAtIndex(privKeys, dex);
assert(privKey != NULL);
if(ortn == errSecSuccess) {
ortn = SecKeyCreateWithCSSMKey(privKey, &keyRef);
if(ortn) {
SecImpExpDbg("SecKeyCreateWithCSSMKey error");
}
if (keyRef)
CFRelease(keyRef);
}
}
}
}
#endif
SecPkcs12CoderRelease(p12Coder);
if(passKey != NULL) {
CSSM_FreeKey(cspHand, NULL, passKey, CSSM_FALSE);
free(passKey);
}
if(privKeys != NULL) {
CFRelease(privKeys);
}
if(clHand != 0) {
cuClDetachUnload(clHand);
}
if(rawCspHand != 0) {
cuCspDetachUnload(rawCspHand, CSSM_TRUE);
}
return ortn;
}
OSStatus impExpPkcs7Import(
CFDataRef inData,
SecItemImportExportFlags flags,
const SecKeyImportExportParameters *keyParams, SecKeychainRef importKeychain, CFMutableArrayRef outArray) {
SecCmsDecoderRef decoderContext;
SecCmsMessageRef cmsMessage = NULL;
SecCmsContentInfoRef contentInfo;
SecCmsSignedDataRef signedData;
int contentLevelCount;
int i;
SECOidTag contentTypeTag;
OSStatus result;
OSStatus ourRtn = errSecSuccess;
result = SecCmsDecoderCreate (NULL, NULL, NULL, NULL, NULL, NULL, NULL, &decoderContext);
if (result != 0) {
ourRtn = result;
goto errOut;
}
result = SecCmsDecoderUpdate(decoderContext, CFDataGetBytePtr(inData),
CFDataGetLength(inData));
if (result != 0) {
SecImpExpDbg("SecCmsDecoderUpdate error");
ourRtn = errSecUnknownFormat;
SecCmsDecoderDestroy(decoderContext);
goto errOut;
}
ourRtn = SecCmsDecoderFinish(decoderContext, &cmsMessage);
if (ourRtn) {
SecImpExpDbg("SecCmsDecoderFinish error");
ourRtn = errSecUnknownFormat;
goto errOut;
}
contentLevelCount = SecCmsMessageContentLevelCount (cmsMessage);
for (i = 0; i < contentLevelCount; ++i)
{
contentInfo = SecCmsMessageContentLevel (cmsMessage, i);
contentTypeTag = SecCmsContentInfoGetContentTypeTag (contentInfo);
switch (contentTypeTag)
{
case SEC_OID_PKCS7_SIGNED_DATA:
{
signedData =
(SecCmsSignedDataRef) SecCmsContentInfoGetContent (contentInfo);
if (signedData == NULL) {
SecImpExpDbg("SecCmsContentInfoGetContent returned NULL");
ourRtn = errSecUnknownFormat;
goto errOut;
}
CSSM_DATA **outCerts = SecCmsSignedDataGetCertificateList(signedData);
if(outCerts == NULL) {
SecImpExpDbg("SecCmsSignedDataGetCertificateList returned NULL");
ourRtn = errSecUnknownFormat;
goto errOut;
}
unsigned count = 0;
CSSM_DATA **array = outCerts;
if (array) {
while (*array++) {
count++;
}
}
if(count == 0) {
SecImpExpDbg("No certs found in apparently good PKCS7 blob");
goto errOut;
}
for(unsigned dex=0; dex<count; dex++) {
ourRtn = impExpImportCertCommon(outCerts[dex], importKeychain,
outArray);
if(ourRtn) {
goto errOut;
}
}
break;
}
default:
break;
}
}
errOut:
if(cmsMessage) {
SecCmsMessageDestroy(cmsMessage);
}
return ourRtn;
}
OSStatus impExpNetscapeCertImport(
CFDataRef inData,
SecItemImportExportFlags flags,
const SecKeyImportExportParameters *keyParams, SecKeychainRef importKeychain, CFMutableArrayRef outArray) {
SecNssCoder coder;
NetscapeCertSequence certSeq;
memset(&certSeq, 0, sizeof(certSeq));
PRErrorCode perr = coder.decode(CFDataGetBytePtr(inData),
CFDataGetLength(inData),
NetscapeCertSequenceTemplate,
&certSeq);
if(perr) {
SecImpExpDbg("impExpNetscapeCertImport: DER decode failure");
return errSecUnknownFormat;
}
if(!cuCompareOid(&CSSMOID_NetscapeCertSequence, &certSeq.contentType)) {
SecImpExpDbg("impExpNetscapeCertImport: OID mismatch");
return errSecUnknownFormat;
}
unsigned numCerts = nssArraySize((const void **)certSeq.certs);
for(unsigned i=0; i<numCerts; i++) {
CSSM_DATA *cert = certSeq.certs[i];
OSStatus ortn = impExpImportCertCommon(cert, importKeychain, outArray);
if(ortn) {
return ortn;
}
}
return errSecSuccess;
}
#pragma mark --- Utilities ---
OSStatus impExpImportCertCommon(
const CSSM_DATA *cdata,
SecKeychainRef importKeychain, CFMutableArrayRef outArray) {
OSStatus ortn = errSecSuccess;
SecCertificateRef certRef;
if (!cdata)
return errSecUnsupportedFormat;
CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)cdata->Data, (CFIndex)cdata->Length, kCFAllocatorNull);
if (!data)
return errSecUnsupportedFormat;
certRef = SecCertificateCreateWithData(kCFAllocatorDefault, data);
CFRelease(data);
if(!certRef) {
SecImpExpDbg("impExpHandleCert error\n");
return errSecUnsupportedFormat;
}
if(importKeychain != NULL) {
ortn = SecCertificateAddToKeychain(certRef, importKeychain);
if(ortn) {
SecImpExpDbg("SecCertificateAddToKeychain error\n");
CFRelease(certRef);
return ortn;
}
}
if(outArray != NULL) {
CFArrayAppendValue(outArray, certRef);
}
CFRelease(certRef);
return ortn;
}