--- /dev/null
+/*
+ * 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@
+ *
+ * SecImport.cpp - high-level facility for importing Sec layer objects.
+ */
+
+#include "SecImportExport.h"
+#include "SecExternalRep.h"
+#include "SecImportExportPem.h"
+#include "SecImportExportUtils.h"
+#include <security_cdsa_utils/cuCdsaUtils.h>
+#include <security_utilities/globalizer.h>
+#include <Security/SecBase.h>
+
+#define SecImpInferDbg(args...) secdebug("SecImpInfer", ## args)
+
+using namespace Security;
+using namespace KeychainCore;
+
+/*
+ * Do our best to ensure that a SecImportRep's type and format are known.
+ * A return of true means that both format and type (and, if the item
+ * is a raw public or private key, the algorithm) are known.
+ */
+static bool impExpInferTypeAndFormat(
+ SecImportRep *rep,
+ CFStringRef fileStr,
+ SecExternalFormat inputFormat,
+ SecExternalItemType itemType)
+{
+ /* fill in blanks if caller knows them */
+ if((rep->mExternType == kSecItemTypeUnknown) && (itemType != kSecItemTypeUnknown)) {
+ rep->mExternType = itemType;
+ }
+ if((rep->mExternFormat == kSecFormatUnknown) && (inputFormat != kSecFormatUnknown)) {
+ rep->mExternFormat = inputFormat;
+ }
+
+ /* some types can be inferred from format */
+ if(rep->mExternType == kSecItemTypeUnknown) {
+ SecExternalFormat format;
+ if(rep->mExternFormat == kSecFormatUnknown) {
+ /* caller specified */
+ format = inputFormat;
+ }
+ else {
+ /* maybe this is already set */
+ format = rep->mExternFormat;
+ }
+ switch(format) {
+ case kSecFormatUnknown:
+ break;
+ case kSecFormatPKCS7:
+ case kSecFormatPKCS12:
+ case kSecFormatPEMSequence:
+ case kSecFormatNetscapeCertSequence:
+ rep->mExternType = kSecItemTypeAggregate;
+ break;
+ case kSecFormatRawKey:
+ rep->mExternType = kSecItemTypeSessionKey;
+ break;
+ case kSecFormatX509Cert:
+ rep->mExternType = kSecItemTypeCertificate;
+ break;
+ case kSecFormatWrappedPKCS8:
+ case kSecFormatWrappedOpenSSL:
+ case kSecFormatWrappedSSH:
+ rep->mExternType = kSecItemTypePrivateKey;
+ break;
+ case kSecFormatSSHv2:
+ rep->mExternType = kSecItemTypePublicKey;
+ break;
+ case kSecFormatOpenSSL:
+ case kSecFormatBSAFE:
+ case kSecFormatWrappedLSH:
+ default:
+ /* can be private or session (right? */
+ break;
+ }
+ }
+
+ /* some formats can be inferred from type */
+ if(rep->mExternFormat == kSecFormatUnknown) {
+ SecExternalItemType thisType;
+ if(rep->mExternType == kSecItemTypeUnknown) {
+ /* caller specified */
+ thisType = itemType;
+ }
+ else {
+ /* maybe this is already set */
+ thisType = rep->mExternType;
+ }
+ switch(thisType) {
+ case kSecItemTypeCertificate:
+ rep->mExternFormat = kSecFormatX509Cert;
+ break;
+ /* any others? */
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Wrapped private keys don't need algorithm
+ * Some formats implies algorithm
+ */
+ bool isWrapped = false;
+ switch(rep->mExternFormat) {
+ case kSecFormatWrappedPKCS8:
+ case kSecFormatWrappedOpenSSL:
+ case kSecFormatWrappedLSH:
+ isWrapped = true;
+ break;
+ case kSecFormatWrappedSSH:
+ isWrapped = true;
+ rep->mKeyAlg = CSSM_ALGID_RSA;
+ break;
+ case kSecFormatSSH:
+ rep->mKeyAlg = CSSM_ALGID_RSA;
+ break;
+ default:
+ break;
+ }
+
+ /* Are we there yet? */
+ bool done = true;
+ if((rep->mExternType == kSecItemTypeUnknown) ||
+ (rep->mExternFormat == kSecFormatUnknown)) {
+ done = false;
+ }
+ if(done) {
+ switch(rep->mExternType) {
+ case kSecItemTypePrivateKey:
+ case kSecItemTypePublicKey:
+ if(!isWrapped && (rep->mKeyAlg == CSSM_ALGID_NONE)) {
+ /* gotta know this too */
+ done = false;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if(!done) {
+ /* infer from filename if possible */
+ done = impExpImportParseFileExten(fileStr, &rep->mExternFormat,
+ &rep->mExternType);
+ }
+ if(done) {
+ return true;
+ }
+
+ /* invoke black magic: try decoding various forms */
+ return impExpImportGuessByExamination(rep->mExternal, &rep->mExternFormat,
+ &rep->mExternType, &rep->mKeyAlg);
+}
+
+class CSPDLMaker
+{
+protected:
+ CSSM_CSP_HANDLE mHandle;
+ RecursiveMutex mMutex;
+
+public:
+ CSPDLMaker() : mHandle(cuCspStartup(CSSM_FALSE)) {}
+ operator CSSM_CSP_HANDLE() {return mHandle;}
+};
+
+static ModuleNexus<CSPDLMaker> gCSPHandle;
+
+OSStatus SecKeychainItemImport(
+ CFDataRef importedData,
+ CFStringRef fileNameOrExtension, // optional
+ SecExternalFormat *inputFormat, // optional, IN/OUT
+ SecExternalItemType *itemType, // optional, IN/OUT
+ SecItemImportExportFlags flags,
+ const SecKeyImportExportParameters *keyParams, // optional
+ SecKeychainRef importKeychain, // optional
+ CFArrayRef *outItems) /* optional */
+{
+ BEGIN_IMP_EXP_SECAPI
+
+ bool isPem;
+ OSStatus ortn = errSecSuccess;
+ OSStatus pem_ortn = errSecSuccess;
+ SecImportRep *rep = NULL;
+ SecExternalFormat callerInputFormat;
+ SecExternalItemType callerItemType;
+ CSSM_CSP_HANDLE cspHand = 0;
+ CFIndex dex;
+ CFStringRef ourFileStr = NULL;
+
+ if((importedData == NULL) || (CFDataGetLength(importedData) == 0)) {
+ return errSecParam;
+ }
+ /* all other args are optional */
+
+ if(inputFormat) {
+ callerInputFormat = *inputFormat;
+ }
+ else {
+ callerInputFormat = kSecFormatUnknown;
+ }
+ if(itemType) {
+ callerItemType = *itemType;
+ }
+ else {
+ callerItemType = kSecItemTypeUnknown;
+ }
+
+ CFIndex numReps = 0;
+ SecExternalFormat tempFormat = callerInputFormat;
+ SecExternalItemType tempType = callerItemType;
+ ImpPrivKeyImportState keyImportState = PIS_NoLimit;
+
+ CFMutableArrayRef importReps = CFArrayCreateMutable(NULL, 0, NULL);
+ CFMutableArrayRef createdKcItems = CFArrayCreateMutable(NULL, 0,
+ &kCFTypeArrayCallBacks);
+ /* subsequent errors to errOut: */
+
+ /*
+ * importedData --> one or more SecImportReps.
+ * Note successful PEM decode can override caller's inputFormat and/or itemType.
+ */
+ pem_ortn = impExpParsePemToImportRefs(importedData, importReps, &isPem);
+ /* remember how PEM decode failed, but continue to examine other possibilities */
+ if(!isPem) {
+ /* incoming blob is one binary item, type possibly unknown */
+ rep = new SecImportRep(importedData, callerItemType, callerInputFormat,
+ CSSM_ALGID_NONE);
+ CFArrayAppendValue(importReps, rep);
+ if(fileNameOrExtension) {
+ ourFileStr = fileNameOrExtension;
+ CFRetain(ourFileStr);
+ }
+ }
+ else {
+ /*
+ * Strip off possible .pem extension in case there's another one in
+ * front of it
+ */
+ assert(CFArrayGetCount(importReps) >= 1);
+ if(fileNameOrExtension) {
+ if(CFStringHasSuffix(fileNameOrExtension, CFSTR(".pem"))) {
+ ourFileStr = impExpImportDeleteExtension(fileNameOrExtension);
+ }
+ else {
+ ourFileStr = fileNameOrExtension;
+ CFRetain(ourFileStr);
+ }
+ }
+ }
+
+ /*
+ * Ensure we know type and format (and, for raw keys, algorithm) of each item.
+ */
+ numReps = CFArrayGetCount(importReps);
+ if(numReps > 1) {
+ /*
+ * Incoming kSecFormatPEMSequence, caller specs are useless now.
+ * Hopefully the PEM parsing disclosed the info we'll need.
+ */
+ if(ourFileStr) {
+ CFRelease(ourFileStr);
+ ourFileStr = NULL;
+ }
+ tempFormat = kSecFormatUnknown;
+ tempType = kSecItemTypeUnknown;
+ }
+ for(dex=0; dex<numReps; dex++) {
+ rep = (SecImportRep *)CFArrayGetValueAtIndex(importReps, dex);
+ bool ok = impExpInferTypeAndFormat(rep, ourFileStr, tempFormat, tempType);
+ if(!ok) {
+ ortn = errSecUnknownFormat;
+ goto errOut;
+ }
+ }
+
+ /* Get a CSPDL handle, somehow, as convenience for lower level code */
+ if(importKeychain != NULL) {
+ ortn = SecKeychainGetCSPHandle(importKeychain, &cspHand);
+ if(ortn) {
+ goto errOut;
+ }
+ }
+ else {
+ cspHand = gCSPHandle();
+ }
+
+ if(keyParams && (keyParams->flags & kSecKeyImportOnlyOne)) {
+ keyImportState = PIS_AllowOne;
+ }
+
+ /* Everything looks good: Go */
+ for(CFIndex dex=0; dex<numReps; dex++) {
+ rep = (SecImportRep *)CFArrayGetValueAtIndex(importReps, dex);
+ ortn = rep->importRep(importKeychain, cspHand, flags, keyParams,
+ keyImportState, createdKcItems);
+ if(ortn) {
+ goto errOut;
+ }
+ }
+
+ /* Give as much info to caller as we can even if we got an error on import */
+ if(inputFormat != NULL) {
+ if(numReps > 1) {
+ assert(isPem);
+ *inputFormat = kSecFormatPEMSequence;
+ }
+ else {
+ /* format from sole item in importReps */
+ assert(numReps != 0);
+ rep = (SecImportRep *)CFArrayGetValueAtIndex(importReps, 0);
+ *inputFormat = rep->mExternFormat;
+ }
+ }
+ if(itemType != NULL) {
+ if(numReps > 1) {
+ assert(isPem);
+ *itemType = kSecItemTypeAggregate;
+ }
+ else {
+ /* itemType from sole item in importReps */
+ assert(numReps != 0);
+ rep = (SecImportRep *)CFArrayGetValueAtIndex(importReps, 0);
+ *itemType = rep->mExternType;
+ }
+ }
+ if((ortn == errSecSuccess) && (outItems != NULL)) {
+ /* return the array */
+ *outItems = createdKcItems;
+ createdKcItems = NULL;
+ }
+ /* else caller doesn't want SecKeychainItemsRefs; we'll release below */
+
+errOut:
+ if(createdKcItems) {
+ CFRelease(createdKcItems);
+ }
+ if(importReps != NULL) {
+ /* CFArray of our own classes, no auto release */
+ CFIndex num = CFArrayGetCount(importReps);
+ for(dex=0; dex<num; dex++) {
+ rep = (SecImportRep *)CFArrayGetValueAtIndex(importReps, dex);
+ delete rep;
+ }
+ CFRelease(importReps);
+ }
+ if(ourFileStr) {
+ CFRelease(ourFileStr);
+ }
+ if(ortn) {
+ /* error occurred importing non-PEM representation */
+ return SecKeychainErrFromOSStatus(ortn);
+ }
+ if(pem_ortn == errSecUnsupportedFormat && numReps == 0) {
+ /* error occurred importing as PEM, and no other rep was imported */
+ return SecKeychainErrFromOSStatus(pem_ortn);
+ }
+ return errSecSuccess;
+
+ END_IMP_EXP_SECAPI
+}
+
+OSStatus SecItemImport(
+ CFDataRef importedData,
+ CFStringRef fileNameOrExtension, /* optional */
+ SecExternalFormat *inputFormat, /* optional, IN/OUT */
+ SecExternalItemType *itemType, /* optional, IN/OUT */
+ SecItemImportExportFlags flags,
+ const SecItemImportExportKeyParameters *keyParams, /* optional */
+ SecKeychainRef importKeychain, /* optional */
+ CFArrayRef *outItems)
+{
+
+ SecKeyImportExportParameters* oldStructPtr = NULL;
+ SecKeyImportExportParameters oldStruct;
+ memset(&oldStruct, 0, sizeof(oldStruct));
+
+
+ if (NULL != keyParams)
+ {
+ if (ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(NULL,
+ keyParams, &oldStruct))
+ {
+ oldStructPtr = &oldStruct;
+ }
+ }
+
+ return SecKeychainItemImport(importedData, fileNameOrExtension, inputFormat,
+ itemType, flags, oldStructPtr, importKeychain, outItems);
+}
+