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 * SecExport.cpp - high-level facility for exporting Sec layer objects.
24 */
25
26#include "SecImportExport.h"
27#include "SecImportExportAgg.h"
28#include "SecImportExportPem.h"
29#include "SecExternalRep.h"
30#include "SecImportExportUtils.h"
31#include <security_utilities/errors.h>
32#include <Security/SecIdentity.h>
33#include <Security/SecIdentityPriv.h>
34#include <Security/SecItem.h>
35#include <Security/SecBase.h>
36using namespace Security;
37using namespace KeychainCore;
38
39/*
40 * Convert Sec item to one or two SecExportReps, append to exportReps array.
41 * The "one or two" clause exists for SecIdentityRefs, which we split into
42 * a cert and a key.
43 * Throws a MacOSError if incoming CFTypeRef is of type other than SecKeyRef,
44 * SecCertRef, or SecIdentityRef.
45 */
46static void impExpAddToExportReps(
47	CFTypeRef			thing,				// Key, Cert, Identity
48	CFMutableArrayRef   exportReps,
49	unsigned			&numCerts,			// IN/OUT - accumulated
50	unsigned			&numKeys)			// IN/OUT - accumulated
51{
52	if(CFGetTypeID(thing) == SecIdentityGetTypeID()) {
53		/* special case for SecIdentities, creates two SecExportReps */
54		OSStatus ortn;
55		SecIdentityRef idRef = (SecIdentityRef)thing;
56		SecCertificateRef certRef;
57		SecKeyRef keyRef;
58		SecExportRep *rep;
59
60		/* cert */
61		SecImpExpDbg("impExpAddToExportReps: adding identity cert and key");
62		ortn = SecIdentityCopyCertificate(idRef, &certRef);
63		if(ortn) {
64			Security::MacOSError::throwMe(ortn);
65		}
66		rep = SecExportRep::vend(certRef);
67		CFArrayAppendValue(exportReps, rep);
68		CFRelease(certRef);			// SecExportRep holds a reference
69		numCerts++;
70
71		/* private key */
72		ortn = SecIdentityCopyPrivateKey(idRef, &keyRef);
73		if(ortn) {
74			Security::MacOSError::throwMe(ortn);
75		}
76		rep = SecExportRep::vend(keyRef);
77		CFArrayAppendValue(exportReps, rep);
78		CFRelease(keyRef);			// SecExportRep holds a reference
79		numKeys++;
80	}
81	else {
82		/* this throws if 'thing' is an unacceptable type */
83		SecExportRep *rep = SecExportRep::vend(thing);
84		SecImpExpDbg("impExpAddToExportReps: adding single type %d",
85			(int)rep->externType());
86		CFArrayAppendValue(exportReps, rep);
87		if(rep->externType() == kSecItemTypeCertificate) {
88			numCerts++;
89		}
90		else {
91			numKeys++;
92		}
93	}
94}
95
96#pragma mark --- public export function ---
97
98OSStatus SecKeychainItemExport(
99	CFTypeRef							keychainItemOrArray,
100	SecExternalFormat					outputFormat,	// a SecExternalFormat
101	SecItemImportExportFlags			flags,			// kSecItemPemArmour, etc.
102	const SecKeyImportExportParameters  *keyParams,		// optional
103	CFDataRef							*exportedData)	// external representation
104														//    returned here
105{
106	BEGIN_IMP_EXP_SECAPI
107
108	/* some basic input validation */
109	if(keychainItemOrArray == NULL) {
110		return errSecParam;
111	}
112	if(keyParams != NULL) {
113		/* can't specify explicit passphrase and ask for secure one */
114		if( (keyParams->passphrase != NULL) &&
115		    ((keyParams->flags & kSecKeySecurePassphrase) != 0)) {
116			return errSecParam;
117		}
118	}
119
120	unsigned numKeys			= 0;
121	unsigned numCerts			= 0;
122	unsigned numTotalExports	= 0;
123	OSStatus ortn				= errSecSuccess;
124	SecExportRep *rep			= NULL;				// common temp variable
125	CFMutableDataRef outputData = NULL;
126	const char *pemHeader		= "UNKNOWN";
127
128	/* convert keychainItemOrArray to CFArray of SecExportReps */
129	CFMutableArrayRef exportReps = CFArrayCreateMutable(NULL, 0, NULL);
130	/* subsequent errors to errOut: */
131
132	try {
133		if(CFGetTypeID(keychainItemOrArray) == CFArrayGetTypeID()) {
134			CFArrayRef arr = (CFArrayRef)keychainItemOrArray;
135			CFIndex arraySize = CFArrayGetCount(arr);
136			for(CFIndex dex=0; dex<arraySize; dex++) {
137				impExpAddToExportReps(CFArrayGetValueAtIndex(arr, dex),
138					exportReps, numCerts, numKeys);
139			}
140		}
141		else {
142			impExpAddToExportReps(keychainItemOrArray, exportReps, numCerts, numKeys);
143		}
144	}
145	catch(const Security::MacOSError osErr) {
146		ortn = osErr.error;
147		goto errOut;
148	}
149	catch(...) {
150		ortn = errSecParam;
151		goto errOut;
152	}
153	numTotalExports = (unsigned int)CFArrayGetCount(exportReps);
154	assert((numCerts + numKeys) == numTotalExports);
155	if((numTotalExports > 1) && (outputFormat == kSecFormatUnknown)) {
156		/* default aggregate format is PEM sequence */
157		outputFormat = kSecFormatPEMSequence;
158	}
159
160	/*
161	 * Break out to SecExternalFormat-specific code, appending all data to outputData
162	 */
163	outputData = CFDataCreateMutable(NULL, 0);
164	switch(outputFormat) {
165		case kSecFormatPKCS7:
166			ortn = impExpPkcs7Export(exportReps, flags, keyParams, outputData);
167			pemHeader = PEM_STRING_PKCS7;
168			break;
169		case kSecFormatPKCS12:
170			ortn = impExpPkcs12Export(exportReps, flags, keyParams, outputData);
171			pemHeader = PEM_STRING_PKCS12;
172			break;
173		case kSecFormatPEMSequence:
174			{
175				/*
176				 * A bit of a special case. Create an intermediate DER encoding
177				 * of each SecExportRef, in the default format for that item;
178				 * PEM encode the result, and append the PEM encoding to
179				 * outputData.
180				 */
181				CFIndex numReps = CFArrayGetCount(exportReps);
182				for(CFIndex dex=0; dex<numReps; dex++) {
183
184					rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, dex);
185
186					/* default DER encoding */
187					CFMutableDataRef tmpData = CFDataCreateMutable(NULL, 0);
188					ortn = rep->exportRep(kSecFormatUnknown, flags, keyParams,
189						tmpData, &pemHeader);
190					if(ortn) {
191						SecImpExpDbg("ItemExport: releasing tmpData %p", tmpData);
192						CFRelease(tmpData);
193						goto errOut;
194					}
195
196					/* PEM to accumulating output */
197					assert(rep->pemParamLines() == NULL);
198					ortn = impExpPemEncodeExportRep((CFDataRef)tmpData,
199							pemHeader, NULL,		/* no pemParamLines, right? */
200							outputData);
201					CFRelease(tmpData);
202					if(ortn) {
203						goto errOut;
204					}
205				}
206				break;
207			}
208
209		/* Enumerate remainder explicitly for clarity; all are single-item forms */
210		case kSecFormatOpenSSL:
211		case kSecFormatSSH:
212		case kSecFormatSSHv2:
213		case kSecFormatBSAFE:
214		case kSecFormatRawKey:
215		case kSecFormatWrappedPKCS8:
216		case kSecFormatWrappedOpenSSL:
217		case kSecFormatWrappedSSH:
218		case kSecFormatWrappedLSH:
219		case kSecFormatX509Cert:
220		case kSecFormatUnknown:		// i.e., default, handled by SecExportRep
221			{
222				unsigned foundCount = 0;
223
224				/* verify that we have exactly one of specified item */
225				if(outputFormat == kSecFormatX509Cert) {
226					foundCount = numCerts;
227				}
228				else if(outputFormat == kSecFormatUnknown) {
229					/* can't go wrong */
230					foundCount = numTotalExports;
231				}
232				else {
233					foundCount = numKeys;
234				}
235				if((numTotalExports != 1) || (foundCount != 1)) {
236					SecImpExpDbg("Export single item format with other than one item");
237					ortn = errSecParam;
238					goto errOut;
239				}
240				assert(CFArrayGetCount(exportReps) == 1);
241				rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, 0);
242				ortn = rep->exportRep(outputFormat, flags,
243						keyParams, outputData, &pemHeader);
244				break;
245			}
246		default:
247			SecImpExpDbg("SecKeychainItemExport: bad format (%u)",
248				(unsigned)outputFormat);
249			ortn = errSecParam;
250			goto errOut;
251	}
252
253	/*
254	 * Final step: possible PEM encode. Skip for kSecFormatPEMSequence (in which
255	 * case outputData is all ready to ship out to the caller); mandatory
256	 * if exportRep has a non-NULL pemParamLines (which can only happen if we're
257	 * exporting a single item).
258	 */
259	if(ortn == errSecSuccess) {
260		if(outputFormat == kSecFormatPEMSequence) {
261			*exportedData = outputData;
262			outputData = NULL;
263		}
264		else {
265			rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, 0);
266			if((flags & kSecItemPemArmour) || (rep->pemParamLines() != NULL)) {
267				/* PEM encode a single item */
268				CFMutableDataRef tmpData = CFDataCreateMutable(NULL, 0);
269				ortn = impExpPemEncodeExportRep((CFDataRef)outputData, pemHeader,
270					rep->pemParamLines(), tmpData);
271				CFRelease(outputData);		// done with this
272				outputData = NULL;
273				*exportedData = tmpData;	// caller gets PEM
274			}
275			else {
276				*exportedData = outputData;
277				outputData = NULL;
278			}
279		}
280	}
281errOut:
282	if(exportReps != NULL) {
283		/* CFArray of our own classes, no auto release */
284		CFIndex num = CFArrayGetCount(exportReps);
285		for(CFIndex dex=0; dex<num; dex++) {
286			rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, dex);
287			delete rep;
288		}
289		CFRelease(exportReps);
290	}
291	if(outputData != NULL) {
292		CFRelease(outputData);
293		outputData = NULL;
294	}
295	if(ortn) {
296		return SecKeychainErrFromOSStatus(ortn);
297	}
298	else {
299		return errSecSuccess;
300	}
301
302	END_IMP_EXP_SECAPI
303}
304
305
306OSStatus SecItemExport(CFTypeRef secItemOrArray, SecExternalFormat outputFormat,
307	SecItemImportExportFlags			flags,				/* kSecItemPemArmor, etc. */
308	const SecItemImportExportKeyParameters  *keyParams,			/* optional */
309	CFDataRef							*exportedData)
310{
311	SecKeyImportExportParameters* oldStructPtr = NULL;
312	SecKeyImportExportParameters oldStruct;
313	memset(&oldStruct, 0, sizeof(oldStruct));
314
315	if (NULL != keyParams)
316	{
317
318		SecKeyRef tempKey = NULL;
319
320		if (SecKeyGetTypeID() == CFGetTypeID(secItemOrArray))
321		{
322			tempKey = (SecKeyRef)secItemOrArray;
323		}
324
325		if (ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(tempKey,
326			keyParams, &oldStruct))
327		{
328			oldStructPtr = &oldStruct;
329		}
330	}
331
332	return SecKeychainItemExport(secItemOrArray, outputFormat, flags, oldStructPtr, exportedData);
333}
334
335
336
337
338
339
340
341