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