]> git.saurik.com Git - apple/security.git/blob - libsecurity_keychain/lib/SecImport.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_keychain / lib / SecImport.cpp
1 /*
2 * Copyright (c) 2004 Apple Computer, 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
33 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
34
35 #define SecImpInferDbg(args...) secdebug("SecImpInfer", ## args)
36
37 using namespace Security;
38 using namespace KeychainCore;
39
40 /*
41 * Do our best to ensure that a SecImportRep's type and format are known.
42 * A return of true means that both format and type (and, if the item
43 * is a raw public or private key, the algorithm) are known.
44 */
45 static bool impExpInferTypeAndFormat(
46 SecImportRep *rep,
47 CFStringRef fileStr,
48 SecExternalFormat inputFormat,
49 SecExternalItemType itemType)
50 {
51 /* fill in blanks if caller knows them */
52 if((rep->mExternType == kSecItemTypeUnknown) && (itemType != kSecItemTypeUnknown)) {
53 rep->mExternType = itemType;
54 }
55 if((rep->mExternFormat == kSecFormatUnknown) && (inputFormat != kSecFormatUnknown)) {
56 rep->mExternFormat = inputFormat;
57 }
58
59 /* some types can be inferred from format */
60 if(rep->mExternType == kSecItemTypeUnknown) {
61 SecExternalFormat format;
62 if(rep->mExternFormat == kSecFormatUnknown) {
63 /* caller specified */
64 format = inputFormat;
65 }
66 else {
67 /* maybe this is already set */
68 format = rep->mExternFormat;
69 }
70 switch(format) {
71 case kSecFormatUnknown:
72 break;
73 case kSecFormatPKCS7:
74 case kSecFormatPKCS12:
75 case kSecFormatPEMSequence:
76 case kSecFormatNetscapeCertSequence:
77 rep->mExternType = kSecItemTypeAggregate;
78 break;
79 case kSecFormatRawKey:
80 rep->mExternType = kSecItemTypeSessionKey;
81 break;
82 case kSecFormatX509Cert:
83 rep->mExternType = kSecItemTypeCertificate;
84 break;
85 case kSecFormatWrappedPKCS8:
86 case kSecFormatWrappedOpenSSL:
87 case kSecFormatWrappedSSH:
88 rep->mExternType = kSecItemTypePrivateKey;
89 break;
90 case kSecFormatSSHv2:
91 rep->mExternType = kSecItemTypePublicKey;
92 break;
93 case kSecFormatOpenSSL:
94 case kSecFormatBSAFE:
95 case kSecFormatWrappedLSH:
96 default:
97 /* can be private or session (right? */
98 break;
99 }
100 }
101
102 /* some formats can be inferred from type */
103 if(rep->mExternFormat == kSecFormatUnknown) {
104 SecExternalItemType thisType;
105 if(rep->mExternType == kSecItemTypeUnknown) {
106 /* caller specified */
107 thisType = itemType;
108 }
109 else {
110 /* maybe this is already set */
111 thisType = rep->mExternType;
112 }
113 switch(thisType) {
114 case kSecItemTypeCertificate:
115 rep->mExternFormat = kSecFormatX509Cert;
116 break;
117 /* any others? */
118 default:
119 break;
120 }
121 }
122
123 /*
124 * Wrapped private keys don't need algorithm
125 * Some formats implies algorithm
126 */
127 bool isWrapped = false;
128 switch(rep->mExternFormat) {
129 case kSecFormatWrappedPKCS8:
130 case kSecFormatWrappedOpenSSL:
131 case kSecFormatWrappedLSH:
132 isWrapped = true;
133 break;
134 case kSecFormatWrappedSSH:
135 isWrapped = true;
136 rep->mKeyAlg = CSSM_ALGID_RSA;
137 break;
138 case kSecFormatSSH:
139 rep->mKeyAlg = CSSM_ALGID_RSA;
140 break;
141 default:
142 break;
143 }
144
145 /* Are we there yet? */
146 bool done = true;
147 if((rep->mExternType == kSecItemTypeUnknown) ||
148 (rep->mExternFormat == kSecFormatUnknown)) {
149 done = false;
150 }
151 if(done) {
152 switch(rep->mExternType) {
153 case kSecItemTypePrivateKey:
154 case kSecItemTypePublicKey:
155 if(!isWrapped && (rep->mKeyAlg == CSSM_ALGID_NONE)) {
156 /* gotta know this too */
157 done = false;
158 }
159 break;
160 default:
161 break;
162 }
163 }
164 if(!done) {
165 /* infer from filename if possible */
166 done = impExpImportParseFileExten(fileStr, &rep->mExternFormat,
167 &rep->mExternType);
168 }
169 if(done) {
170 return true;
171 }
172
173 /* invoke black magic: try decoding various forms */
174 return impExpImportGuessByExamination(rep->mExternal, &rep->mExternFormat,
175 &rep->mExternType, &rep->mKeyAlg);
176 }
177
178 class CSPDLMaker
179 {
180 protected:
181 CSSM_CSP_HANDLE mHandle;
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 = noErr;
204 OSStatus pem_ortn = noErr;
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 paramErr;
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 == noErr) && (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 noErr;
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