/* * 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 #include #include #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 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; dexflags & kSecKeyImportOnlyOne)) { keyImportState = PIS_AllowOne; } /* Everything looks good: Go */ for(CFIndex dex=0; deximportRep(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