]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecExport.cpp
Security-57740.51.3.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / 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 using namespace Security;
37 using 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 */
46 static 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
98 OSStatus 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 }
281 errOut:
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
306 OSStatus 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