1/* 2 * Copyright (c) 2004,2011-2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 * 23 * SecImport.cpp - high-level facility for importing Sec layer objects. 24 */ 25 26#include "SecImportExport.h" 27#include "SecExternalRep.h" 28#include "SecImportExportPem.h" 29#include "SecImportExportUtils.h" 30#include <security_cdsa_utils/cuCdsaUtils.h> 31#include <security_utilities/globalizer.h> 32#include <Security/SecBase.h> 33 34#define SecImpInferDbg(args...) secdebug("SecImpInfer", ## args) 35 36using namespace Security; 37using namespace KeychainCore; 38 39/* 40 * Do our best to ensure that a SecImportRep's type and format are known. 41 * A return of true means that both format and type (and, if the item 42 * is a raw public or private key, the algorithm) are known. 43 */ 44static bool impExpInferTypeAndFormat( 45 SecImportRep *rep, 46 CFStringRef fileStr, 47 SecExternalFormat inputFormat, 48 SecExternalItemType itemType) 49{ 50 /* fill in blanks if caller knows them */ 51 if((rep->mExternType == kSecItemTypeUnknown) && (itemType != kSecItemTypeUnknown)) { 52 rep->mExternType = itemType; 53 } 54 if((rep->mExternFormat == kSecFormatUnknown) && (inputFormat != kSecFormatUnknown)) { 55 rep->mExternFormat = inputFormat; 56 } 57 58 /* some types can be inferred from format */ 59 if(rep->mExternType == kSecItemTypeUnknown) { 60 SecExternalFormat format; 61 if(rep->mExternFormat == kSecFormatUnknown) { 62 /* caller specified */ 63 format = inputFormat; 64 } 65 else { 66 /* maybe this is already set */ 67 format = rep->mExternFormat; 68 } 69 switch(format) { 70 case kSecFormatUnknown: 71 break; 72 case kSecFormatPKCS7: 73 case kSecFormatPKCS12: 74 case kSecFormatPEMSequence: 75 case kSecFormatNetscapeCertSequence: 76 rep->mExternType = kSecItemTypeAggregate; 77 break; 78 case kSecFormatRawKey: 79 rep->mExternType = kSecItemTypeSessionKey; 80 break; 81 case kSecFormatX509Cert: 82 rep->mExternType = kSecItemTypeCertificate; 83 break; 84 case kSecFormatWrappedPKCS8: 85 case kSecFormatWrappedOpenSSL: 86 case kSecFormatWrappedSSH: 87 rep->mExternType = kSecItemTypePrivateKey; 88 break; 89 case kSecFormatSSHv2: 90 rep->mExternType = kSecItemTypePublicKey; 91 break; 92 case kSecFormatOpenSSL: 93 case kSecFormatBSAFE: 94 case kSecFormatWrappedLSH: 95 default: 96 /* can be private or session (right? */ 97 break; 98 } 99 } 100 101 /* some formats can be inferred from type */ 102 if(rep->mExternFormat == kSecFormatUnknown) { 103 SecExternalItemType thisType; 104 if(rep->mExternType == kSecItemTypeUnknown) { 105 /* caller specified */ 106 thisType = itemType; 107 } 108 else { 109 /* maybe this is already set */ 110 thisType = rep->mExternType; 111 } 112 switch(thisType) { 113 case kSecItemTypeCertificate: 114 rep->mExternFormat = kSecFormatX509Cert; 115 break; 116 /* any others? */ 117 default: 118 break; 119 } 120 } 121 122 /* 123 * Wrapped private keys don't need algorithm 124 * Some formats implies algorithm 125 */ 126 bool isWrapped = false; 127 switch(rep->mExternFormat) { 128 case kSecFormatWrappedPKCS8: 129 case kSecFormatWrappedOpenSSL: 130 case kSecFormatWrappedLSH: 131 isWrapped = true; 132 break; 133 case kSecFormatWrappedSSH: 134 isWrapped = true; 135 rep->mKeyAlg = CSSM_ALGID_RSA; 136 break; 137 case kSecFormatSSH: 138 rep->mKeyAlg = CSSM_ALGID_RSA; 139 break; 140 default: 141 break; 142 } 143 144 /* Are we there yet? */ 145 bool done = true; 146 if((rep->mExternType == kSecItemTypeUnknown) || 147 (rep->mExternFormat == kSecFormatUnknown)) { 148 done = false; 149 } 150 if(done) { 151 switch(rep->mExternType) { 152 case kSecItemTypePrivateKey: 153 case kSecItemTypePublicKey: 154 if(!isWrapped && (rep->mKeyAlg == CSSM_ALGID_NONE)) { 155 /* gotta know this too */ 156 done = false; 157 } 158 break; 159 default: 160 break; 161 } 162 } 163 if(!done) { 164 /* infer from filename if possible */ 165 done = impExpImportParseFileExten(fileStr, &rep->mExternFormat, 166 &rep->mExternType); 167 } 168 if(done) { 169 return true; 170 } 171 172 /* invoke black magic: try decoding various forms */ 173 return impExpImportGuessByExamination(rep->mExternal, &rep->mExternFormat, 174 &rep->mExternType, &rep->mKeyAlg); 175} 176 177class CSPDLMaker 178{ 179protected: 180 CSSM_CSP_HANDLE mHandle; 181 RecursiveMutex mMutex; 182 183public: 184 CSPDLMaker() : mHandle(cuCspStartup(CSSM_FALSE)) {} 185 operator CSSM_CSP_HANDLE() {return mHandle;} 186}; 187 188static ModuleNexus<CSPDLMaker> gCSPHandle; 189 190OSStatus SecKeychainItemImport( 191 CFDataRef importedData, 192 CFStringRef fileNameOrExtension, // optional 193 SecExternalFormat *inputFormat, // optional, IN/OUT 194 SecExternalItemType *itemType, // optional, IN/OUT 195 SecItemImportExportFlags flags, 196 const SecKeyImportExportParameters *keyParams, // optional 197 SecKeychainRef importKeychain, // optional 198 CFArrayRef *outItems) /* optional */ 199{ 200 BEGIN_IMP_EXP_SECAPI 201 202 bool isPem; 203 OSStatus ortn = errSecSuccess; 204 OSStatus pem_ortn = errSecSuccess; 205 SecImportRep *rep = NULL; 206 SecExternalFormat callerInputFormat; 207 SecExternalItemType callerItemType; 208 CSSM_CSP_HANDLE cspHand = 0; 209 CFIndex dex; 210 CFStringRef ourFileStr = NULL; 211 212 if((importedData == NULL) || (CFDataGetLength(importedData) == 0)) { 213 return errSecParam; 214 } 215 /* all other args are optional */ 216 217 if(inputFormat) { 218 callerInputFormat = *inputFormat; 219 } 220 else { 221 callerInputFormat = kSecFormatUnknown; 222 } 223 if(itemType) { 224 callerItemType = *itemType; 225 } 226 else { 227 callerItemType = kSecItemTypeUnknown; 228 } 229 230 CFIndex numReps = 0; 231 SecExternalFormat tempFormat = callerInputFormat; 232 SecExternalItemType tempType = callerItemType; 233 ImpPrivKeyImportState keyImportState = PIS_NoLimit; 234 235 CFMutableArrayRef importReps = CFArrayCreateMutable(NULL, 0, NULL); 236 CFMutableArrayRef createdKcItems = CFArrayCreateMutable(NULL, 0, 237 &kCFTypeArrayCallBacks); 238 /* subsequent errors to errOut: */ 239 240 /* 241 * importedData --> one or more SecImportReps. 242 * Note successful PEM decode can override caller's inputFormat and/or itemType. 243 */ 244 pem_ortn = impExpParsePemToImportRefs(importedData, importReps, &isPem); 245 /* remember how PEM decode failed, but continue to examine other possibilities */ 246 if(!isPem) { 247 /* incoming blob is one binary item, type possibly unknown */ 248 rep = new SecImportRep(importedData, callerItemType, callerInputFormat, 249 CSSM_ALGID_NONE); 250 CFArrayAppendValue(importReps, rep); 251 if(fileNameOrExtension) { 252 ourFileStr = fileNameOrExtension; 253 CFRetain(ourFileStr); 254 } 255 } 256 else { 257 /* 258 * Strip off possible .pem extension in case there's another one in 259 * front of it 260 */ 261 assert(CFArrayGetCount(importReps) >= 1); 262 if(fileNameOrExtension) { 263 if(CFStringHasSuffix(fileNameOrExtension, CFSTR(".pem"))) { 264 ourFileStr = impExpImportDeleteExtension(fileNameOrExtension); 265 } 266 else { 267 ourFileStr = fileNameOrExtension; 268 CFRetain(ourFileStr); 269 } 270 } 271 } 272 273 /* 274 * Ensure we know type and format (and, for raw keys, algorithm) of each item. 275 */ 276 numReps = CFArrayGetCount(importReps); 277 if(numReps > 1) { 278 /* 279 * Incoming kSecFormatPEMSequence, caller specs are useless now. 280 * Hopefully the PEM parsing disclosed the info we'll need. 281 */ 282 if(ourFileStr) { 283 CFRelease(ourFileStr); 284 ourFileStr = NULL; 285 } 286 tempFormat = kSecFormatUnknown; 287 tempType = kSecItemTypeUnknown; 288 } 289 for(dex=0; dex<numReps; dex++) { 290 rep = (SecImportRep *)CFArrayGetValueAtIndex(importReps, dex); 291 bool ok = impExpInferTypeAndFormat(rep, ourFileStr, tempFormat, tempType); 292 if(!ok) { 293 ortn = errSecUnknownFormat; 294 goto errOut; 295 } 296 } 297 298 /* Get a CSPDL handle, somehow, as convenience for lower level code */ 299 if(importKeychain != NULL) { 300 ortn = SecKeychainGetCSPHandle(importKeychain, &cspHand); 301 if(ortn) { 302 goto errOut; 303 } 304 } 305 else { 306 cspHand = gCSPHandle(); 307 } 308 309 if(keyParams && (keyParams->flags & kSecKeyImportOnlyOne)) { 310 keyImportState = PIS_AllowOne; 311 } 312 313 /* Everything looks good: Go */ 314 for(CFIndex dex=0; dex<numReps; dex++) { 315 rep = (SecImportRep *)CFArrayGetValueAtIndex(importReps, dex); 316 ortn = rep->importRep(importKeychain, cspHand, flags, keyParams, 317 keyImportState, createdKcItems); 318 if(ortn) { 319 goto errOut; 320 } 321 } 322 323 /* Give as much info to caller as we can even if we got an error on import */ 324 if(inputFormat != NULL) { 325 if(numReps > 1) { 326 assert(isPem); 327 *inputFormat = kSecFormatPEMSequence; 328 } 329 else { 330 /* format from sole item in importReps */ 331 assert(numReps != 0); 332 rep = (SecImportRep *)CFArrayGetValueAtIndex(importReps, 0); 333 *inputFormat = rep->mExternFormat; 334 } 335 } 336 if(itemType != NULL) { 337 if(numReps > 1) { 338 assert(isPem); 339 *itemType = kSecItemTypeAggregate; 340 } 341 else { 342 /* itemType from sole item in importReps */ 343 assert(numReps != 0); 344 rep = (SecImportRep *)CFArrayGetValueAtIndex(importReps, 0); 345 *itemType = rep->mExternType; 346 } 347 } 348 if((ortn == errSecSuccess) && (outItems != NULL)) { 349 /* return the array */ 350 *outItems = createdKcItems; 351 createdKcItems = NULL; 352 } 353 /* else caller doesn't want SecKeychainItemsRefs; we'll release below */ 354 355errOut: 356 if(createdKcItems) { 357 CFRelease(createdKcItems); 358 } 359 if(importReps != NULL) { 360 /* CFArray of our own classes, no auto release */ 361 CFIndex num = CFArrayGetCount(importReps); 362 for(dex=0; dex<num; dex++) { 363 rep = (SecImportRep *)CFArrayGetValueAtIndex(importReps, dex); 364 delete rep; 365 } 366 CFRelease(importReps); 367 } 368 if(ourFileStr) { 369 CFRelease(ourFileStr); 370 } 371 if(ortn) { 372 /* error occurred importing non-PEM representation */ 373 return SecKeychainErrFromOSStatus(ortn); 374 } 375 if(pem_ortn == errSecUnsupportedFormat && numReps == 0) { 376 /* error occurred importing as PEM, and no other rep was imported */ 377 return SecKeychainErrFromOSStatus(pem_ortn); 378 } 379 return errSecSuccess; 380 381 END_IMP_EXP_SECAPI 382} 383 384OSStatus SecItemImport( 385 CFDataRef importedData, 386 CFStringRef fileNameOrExtension, /* optional */ 387 SecExternalFormat *inputFormat, /* optional, IN/OUT */ 388 SecExternalItemType *itemType, /* optional, IN/OUT */ 389 SecItemImportExportFlags flags, 390 const SecItemImportExportKeyParameters *keyParams, /* optional */ 391 SecKeychainRef importKeychain, /* optional */ 392 CFArrayRef *outItems) 393{ 394 395 SecKeyImportExportParameters* oldStructPtr = NULL; 396 SecKeyImportExportParameters oldStruct; 397 memset(&oldStruct, 0, sizeof(oldStruct)); 398 399 400 if (NULL != keyParams) 401 { 402 if (ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(NULL, 403 keyParams, &oldStruct)) 404 { 405 oldStructPtr = &oldStruct; 406 } 407 } 408 409 return SecKeychainItemImport(importedData, fileNameOrExtension, inputFormat, 410 itemType, flags, oldStructPtr, importKeychain, outItems); 411} 412 413