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