X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/OSX/libsecurity_keychain/Security/SecExport.cpp diff --git a/OSX/libsecurity_keychain/Security/SecExport.cpp b/OSX/libsecurity_keychain/Security/SecExport.cpp new file mode 100644 index 00000000..2e836255 --- /dev/null +++ b/OSX/libsecurity_keychain/Security/SecExport.cpp @@ -0,0 +1,340 @@ +/* + * Copyright (c) 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@ + * + * SecExport.cpp - high-level facility for exporting Sec layer objects. + */ + +#include "SecImportExport.h" +#include "SecImportExportAgg.h" +#include "SecImportExportPem.h" +#include "SecExternalRep.h" +#include "SecImportExportUtils.h" +#include +#include +#include +#include +#include +using namespace Security; +using namespace KeychainCore; + +/* + * Convert Sec item to one or two SecExportReps, append to exportReps array. + * The "one or two" clause exists for SecIdentityRefs, which we split into + * a cert and a key. + * Throws a MacOSError if incoming CFTypeRef is of type other than SecKeyRef, + * SecCertRef, or SecIdentityRef. + */ +static void impExpAddToExportReps( + CFTypeRef thing, // Key, Cert, Identity + CFMutableArrayRef exportReps, + unsigned &numCerts, // IN/OUT - accumulated + unsigned &numKeys) // IN/OUT - accumulated +{ + if(CFGetTypeID(thing) == SecIdentityGetTypeID()) { + /* special case for SecIdentities, creates two SecExportReps */ + OSStatus ortn; + SecIdentityRef idRef = (SecIdentityRef)thing; + SecCertificateRef certRef; + SecKeyRef keyRef; + SecExportRep *rep; + + /* cert */ + SecImpExpDbg("impExpAddToExportReps: adding identity cert and key"); + ortn = SecIdentityCopyCertificate(idRef, &certRef); + if(ortn) { + Security::MacOSError::throwMe(ortn); + } + rep = SecExportRep::vend(certRef); + CFArrayAppendValue(exportReps, rep); + CFRelease(certRef); // SecExportRep holds a reference + numCerts++; + + /* private key */ + ortn = SecIdentityCopyPrivateKey(idRef, &keyRef); + if(ortn) { + Security::MacOSError::throwMe(ortn); + } + rep = SecExportRep::vend(keyRef); + CFArrayAppendValue(exportReps, rep); + CFRelease(keyRef); // SecExportRep holds a reference + numKeys++; + } + else { + /* this throws if 'thing' is an unacceptable type */ + SecExportRep *rep = SecExportRep::vend(thing); + SecImpExpDbg("impExpAddToExportReps: adding single type %d", + (int)rep->externType()); + CFArrayAppendValue(exportReps, rep); + if(rep->externType() == kSecItemTypeCertificate) { + numCerts++; + } + else { + numKeys++; + } + } +} + +#pragma mark --- public export function --- + +OSStatus SecKeychainItemExport( + CFTypeRef keychainItemOrArray, + SecExternalFormat outputFormat, // a SecExternalFormat + SecItemImportExportFlags flags, // kSecItemPemArmour, etc. + const SecKeyImportExportParameters *keyParams, // optional + CFDataRef *exportedData) // external representation + // returned here +{ + BEGIN_IMP_EXP_SECAPI + + /* some basic input validation */ + if(keychainItemOrArray == NULL) { + return errSecParam; + } + if(keyParams != NULL) { + /* can't specify explicit passphrase and ask for secure one */ + if( (keyParams->passphrase != NULL) && + ((keyParams->flags & kSecKeySecurePassphrase) != 0)) { + return errSecParam; + } + } + + unsigned numKeys = 0; + unsigned numCerts = 0; + unsigned numTotalExports = 0; + OSStatus ortn = errSecSuccess; + SecExportRep *rep = NULL; // common temp variable + CFMutableDataRef outputData = NULL; + const char *pemHeader = "UNKNOWN"; + + /* convert keychainItemOrArray to CFArray of SecExportReps */ + CFMutableArrayRef exportReps = CFArrayCreateMutable(NULL, 0, NULL); + /* subsequent errors to errOut: */ + + try { + if(CFGetTypeID(keychainItemOrArray) == CFArrayGetTypeID()) { + CFArrayRef arr = (CFArrayRef)keychainItemOrArray; + CFIndex arraySize = CFArrayGetCount(arr); + for(CFIndex dex=0; dex 1) && (outputFormat == kSecFormatUnknown)) { + /* default aggregate format is PEM sequence */ + outputFormat = kSecFormatPEMSequence; + } + + /* + * Break out to SecExternalFormat-specific code, appending all data to outputData + */ + outputData = CFDataCreateMutable(NULL, 0); + switch(outputFormat) { + case kSecFormatPKCS7: + ortn = impExpPkcs7Export(exportReps, flags, keyParams, outputData); + pemHeader = PEM_STRING_PKCS7; + break; + case kSecFormatPKCS12: + ortn = impExpPkcs12Export(exportReps, flags, keyParams, outputData); + pemHeader = PEM_STRING_PKCS12; + break; + case kSecFormatPEMSequence: + { + /* + * A bit of a special case. Create an intermediate DER encoding + * of each SecExportRef, in the default format for that item; + * PEM encode the result, and append the PEM encoding to + * outputData. + */ + CFIndex numReps = CFArrayGetCount(exportReps); + for(CFIndex dex=0; dexexportRep(kSecFormatUnknown, flags, keyParams, + tmpData, &pemHeader); + if(ortn) { + SecImpExpDbg("ItemExport: releasing tmpData %p", tmpData); + CFRelease(tmpData); + goto errOut; + } + + /* PEM to accumulating output */ + assert(rep->pemParamLines() == NULL); + ortn = impExpPemEncodeExportRep((CFDataRef)tmpData, + pemHeader, NULL, /* no pemParamLines, right? */ + outputData); + CFRelease(tmpData); + if(ortn) { + goto errOut; + } + } + break; + } + + /* Enumerate remainder explicitly for clarity; all are single-item forms */ + case kSecFormatOpenSSL: + case kSecFormatSSH: + case kSecFormatSSHv2: + case kSecFormatBSAFE: + case kSecFormatRawKey: + case kSecFormatWrappedPKCS8: + case kSecFormatWrappedOpenSSL: + case kSecFormatWrappedSSH: + case kSecFormatWrappedLSH: + case kSecFormatX509Cert: + case kSecFormatUnknown: // i.e., default, handled by SecExportRep + { + unsigned foundCount = 0; + + /* verify that we have exactly one of specified item */ + if(outputFormat == kSecFormatX509Cert) { + foundCount = numCerts; + } + else if(outputFormat == kSecFormatUnknown) { + /* can't go wrong */ + foundCount = numTotalExports; + } + else { + foundCount = numKeys; + } + if((numTotalExports != 1) || (foundCount != 1)) { + SecImpExpDbg("Export single item format with other than one item"); + ortn = errSecParam; + goto errOut; + } + assert(CFArrayGetCount(exportReps) == 1); + rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, 0); + ortn = rep->exportRep(outputFormat, flags, + keyParams, outputData, &pemHeader); + break; + } + default: + SecImpExpDbg("SecKeychainItemExport: bad format (%u)", + (unsigned)outputFormat); + ortn = errSecParam; + goto errOut; + } + + /* + * Final step: possible PEM encode. Skip for kSecFormatPEMSequence (in which + * case outputData is all ready to ship out to the caller); mandatory + * if exportRep has a non-NULL pemParamLines (which can only happen if we're + * exporting a single item). + */ + if(ortn == errSecSuccess) { + if(outputFormat == kSecFormatPEMSequence) { + *exportedData = outputData; + outputData = NULL; + } + else { + rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, 0); + if((flags & kSecItemPemArmour) || (rep->pemParamLines() != NULL)) { + /* PEM encode a single item */ + CFMutableDataRef tmpData = CFDataCreateMutable(NULL, 0); + ortn = impExpPemEncodeExportRep((CFDataRef)outputData, pemHeader, + rep->pemParamLines(), tmpData); + CFRelease(outputData); // done with this + outputData = NULL; + *exportedData = tmpData; // caller gets PEM + } + else { + *exportedData = outputData; + outputData = NULL; + } + } + } +errOut: + if(exportReps != NULL) { + /* CFArray of our own classes, no auto release */ + CFIndex num = CFArrayGetCount(exportReps); + for(CFIndex dex=0; dex