/ OSX / libsecurity_keychain / lib / SecImport.cpp
SecImport.cpp
  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 <Security/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...)	secinfo("SecImpInfer", ## args)
 35  
 36  using namespace Security;
 37  using 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   */
 44  static 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  
177  class CSPDLMaker
178  {
179  protected:
180  	CSSM_CSP_HANDLE mHandle;
181      RecursiveMutex mMutex;
182  
183  public:
184  	CSPDLMaker() : mHandle(cuCspStartup(CSSM_FALSE)) {}
185  	operator CSSM_CSP_HANDLE() {return mHandle;}
186  };
187  
188  static ModuleNexus<CSPDLMaker> gCSPHandle;
189  
190  OSStatus 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  
355  errOut:
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  
384  OSStatus 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