]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecImport.cpp
Security-58286.251.4.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / 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