]> git.saurik.com Git - apple/security.git/blob - SecurityTool/keychain_import.c
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTool / keychain_import.c
1 /*
2 * Copyright (c) 2003-2009,2012,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 * keychain_import.c
24 */
25
26 #include "keychain_import.h"
27 #include "keychain_utilities.h"
28 #include "access_utils.h"
29 #include "security.h"
30
31 #include <errno.h>
32 #include <unistd.h>
33 #include <stdio.h>
34 #include <Security/SecImportExport.h>
35 #include <Security/SecIdentity.h>
36 #include <Security/SecKey.h>
37 #include <Security/SecCertificate.h>
38 #include <Security/SecKeychainItemExtendedAttributes.h>
39 #include <security_cdsa_utils/cuFileIo.h>
40 #include <CoreFoundation/CoreFoundation.h>
41
42 // SecTrustedApplicationCreateApplicationGroup
43 #include <Security/SecTrustedApplicationPriv.h>
44
45 #define KC_IMPORT_KEY_PASSWORD_MESSAGE CFSTR("Enter the password for \"%1$@\":")
46 #define KC_IMPORT_KEY_PASSWORD_RETRYMESSAGE CFSTR("Sorry, you entered an invalid password.\n\nEnter the password for \"%1$@\":")
47
48 static int do_keychain_import(
49 SecKeychainRef kcRef,
50 CFDataRef inData,
51 SecExternalFormat externFormat,
52 SecExternalItemType itemType,
53 SecAccessRef access,
54 Boolean nonExtractable,
55 const char *passphrase,
56 const char *fileName,
57 char **attrNames,
58 char **attrValues,
59 unsigned numExtendedAttributes)
60 {
61 SecKeyImportExportParameters keyParams;
62 OSStatus ortn;
63 CFStringRef fileStr;
64 CFArrayRef outArray = NULL;
65 int result = 0;
66 int numCerts = 0;
67 int numKeys = 0;
68 int numIdentities = 0;
69 int tryCount = 0;
70 CFIndex dex;
71 CFIndex numItems = 0;
72 CFStringRef passStr = NULL;
73 CFStringRef promptStr = NULL;
74 CFStringRef retryStr = NULL;
75
76 /*
77 * Specify some kind of passphrase in case caller doesn't know this
78 * is a wrapped object
79 */
80 memset(&keyParams, 0, sizeof(SecKeyImportExportParameters));
81 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
82 if(passphrase != NULL) {
83 passStr = CFStringCreateWithCString(NULL, passphrase, kCFStringEncodingASCII);
84 keyParams.passphrase = passStr;
85 }
86 else {
87 keyParams.flags = kSecKeySecurePassphrase;
88 }
89 if(nonExtractable) {
90 // explicitly set the key attributes, omitting the CSSM_KEYATTR_EXTRACTABLE bit
91 keyParams.keyAttributes = CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE;
92 }
93 keyParams.accessRef = access;
94
95 fileStr = CFStringCreateWithCString(NULL, fileName, kCFStringEncodingUTF8);
96 if (fileStr) {
97 CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, fileStr, kCFURLPOSIXPathStyle, FALSE);
98 if (fileURL) {
99 CFStringRef nameStr = CFURLCopyLastPathComponent(fileURL);
100 if (nameStr) {
101 safe_CFRelease(&fileStr);
102 fileStr = nameStr;
103 }
104 safe_CFRelease(&fileURL);
105 }
106 }
107 promptStr = CFStringCreateWithFormat(NULL, NULL, KC_IMPORT_KEY_PASSWORD_MESSAGE, fileStr);
108 retryStr = CFStringCreateWithFormat(NULL, NULL, KC_IMPORT_KEY_PASSWORD_RETRYMESSAGE, fileStr);
109
110 while (TRUE)
111 {
112 keyParams.alertPrompt = (tryCount == 0) ? promptStr : retryStr;
113
114 ortn = SecKeychainItemImport(inData,
115 fileStr,
116 &externFormat,
117 &itemType,
118 0, /* flags not used (yet) */
119 &keyParams,
120 kcRef,
121 &outArray);
122
123 if(ortn) {
124 if (ortn == errSecPkcs12VerifyFailure && ++tryCount < 3) {
125 continue;
126 }
127 sec_perror("SecKeychainItemImport", ortn);
128 result = 1;
129 goto cleanup;
130 }
131 break;
132 }
133
134 /*
135 * Parse returned items & report to user
136 */
137 if(outArray == NULL) {
138 sec_error("No keychain items found");
139 result = 1;
140 goto cleanup;
141 }
142 numItems = CFArrayGetCount(outArray);
143 for(dex=0; dex<numItems; dex++) {
144 CFTypeRef item = CFArrayGetValueAtIndex(outArray, dex);
145 CFTypeID itemType = CFGetTypeID(item);
146 if(itemType == SecIdentityGetTypeID()) {
147 numIdentities++;
148 }
149 else if(itemType == SecCertificateGetTypeID()) {
150 numCerts++;
151 }
152 else if(itemType == SecKeyGetTypeID()) {
153 numKeys++;
154 }
155 else {
156 sec_error("Unexpected item type returned from SecKeychainItemImport");
157 result = 1;
158 goto cleanup;
159 }
160 }
161 if(numIdentities) {
162 char *str;
163 if(numIdentities > 1) {
164 str = "identities";
165 }
166 else {
167 str = "identity";
168 }
169 fprintf(stdout, "%d %s imported.\n", numIdentities, str);
170 }
171 if(numKeys) {
172 char *str;
173 if(numKeys > 1) {
174 str = "keys";
175 }
176 else {
177 str = "key";
178 }
179 fprintf(stdout, "%d %s imported.\n", numKeys, str);
180 }
181 if(numCerts) {
182 char *str;
183 if(numCerts > 1) {
184 str = "certificates";
185 }
186 else {
187 str = "certificate";
188 }
189 fprintf(stdout, "%d %s imported.\n", numCerts, str);
190 }
191
192 /* optionally apply extended attributes */
193 if(numExtendedAttributes) {
194 unsigned attrDex;
195 for(attrDex=0; attrDex<numExtendedAttributes; attrDex++) {
196 CFStringRef attrNameStr = CFStringCreateWithCString(NULL, attrNames[attrDex],
197 kCFStringEncodingASCII);
198 CFDataRef attrValueData = CFDataCreate(NULL, (const UInt8 *)attrValues[attrDex],
199 strlen(attrValues[attrDex]));
200 for(dex=0; dex<numItems; dex++) {
201 SecKeychainItemRef itemRef =
202 (SecKeychainItemRef)CFArrayGetValueAtIndex(outArray, dex);
203 ortn = SecKeychainItemSetExtendedAttribute(itemRef, attrNameStr, attrValueData);
204 if(ortn) {
205 cssmPerror("SecKeychainItemSetExtendedAttribute", ortn);
206 result = 1;
207 break;
208 }
209 } /* for each imported item */
210 CFRelease(attrNameStr);
211 CFRelease(attrValueData);
212 if(result) {
213 break;
214 }
215 } /* for each extended attribute */
216 }
217
218 cleanup:
219 safe_CFRelease(&fileStr);
220 safe_CFRelease(&outArray);
221 safe_CFRelease(&passStr);
222 safe_CFRelease(&promptStr);
223 safe_CFRelease(&retryStr);
224
225 return result;
226 }
227
228 int
229 keychain_import(int argc, char * const *argv)
230 {
231 int ch, result = 0;
232
233 char *inFile = NULL;
234 char *kcName = NULL;
235 SecKeychainRef kcRef = NULL;
236 SecExternalFormat externFormat = kSecFormatUnknown;
237 SecExternalItemType itemType = kSecItemTypeUnknown;
238 Boolean wrapped = FALSE;
239 Boolean nonExtractable = FALSE;
240 const char *passphrase = NULL;
241 unsigned char *inFileData = NULL;
242 unsigned inFileLen = 0;
243 CFDataRef inData = NULL;
244 unsigned numExtendedAttributes = 0;
245 char **attrNames = NULL;
246 char **attrValues = NULL;
247 Boolean access_specified = FALSE;
248 Boolean always_allow = FALSE;
249 SecAccessRef access = NULL;
250 CFMutableArrayRef trusted_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
251
252 if(argc < 2) {
253 result = 2; /* @@@ Return 2 triggers usage message. */
254 goto cleanup;
255 }
256 inFile = argv[1];
257 if((argc == 2) && (inFile[0] == '-')) {
258 result = 2;
259 goto cleanup;
260 }
261 optind = 2;
262
263 while ((ch = getopt(argc, argv, "k:t:f:P:wxa:hAT:")) != -1)
264 {
265 switch (ch)
266 {
267 case 'k':
268 kcName = optarg;
269 break;
270 case 't':
271 if(!strcmp("pub", optarg)) {
272 itemType = kSecItemTypePublicKey;
273 }
274 else if(!strcmp("priv", optarg)) {
275 itemType = kSecItemTypePrivateKey;
276 }
277 else if(!strcmp("session", optarg)) {
278 itemType = kSecItemTypeSessionKey;
279 }
280 else if(!strcmp("cert", optarg)) {
281 itemType = kSecItemTypeCertificate;
282 }
283 else if(!strcmp("agg", optarg)) {
284 itemType = kSecItemTypeAggregate;
285 }
286 else {
287 result = 2; /* @@@ Return 2 triggers usage message. */
288 goto cleanup;
289 }
290 break;
291 case 'f':
292 if(!strcmp("openssl", optarg)) {
293 externFormat = kSecFormatOpenSSL;
294 }
295 else if(!strcmp("openssh1", optarg)) {
296 externFormat = kSecFormatSSH;
297 }
298 else if(!strcmp("openssh2", optarg)) {
299 externFormat = kSecFormatSSHv2;
300 }
301 else if(!strcmp("bsafe", optarg)) {
302 externFormat = kSecFormatBSAFE;
303 }
304 else if(!strcmp("raw", optarg)) {
305 externFormat = kSecFormatRawKey;
306 }
307 else if(!strcmp("pkcs7", optarg)) {
308 externFormat = kSecFormatPKCS7;
309 }
310 else if(!strcmp("pkcs8", optarg)) {
311 externFormat = kSecFormatWrappedPKCS8;
312 }
313 else if(!strcmp("pkcs12", optarg)) {
314 externFormat = kSecFormatPKCS12;
315 }
316 else if(!strcmp("netscape", optarg)) {
317 externFormat = kSecFormatNetscapeCertSequence;
318 }
319 else if(!strcmp("x509", optarg)) {
320 externFormat = kSecFormatX509Cert;
321 }
322 else if(!strcmp("pemseq", optarg)) {
323 externFormat = kSecFormatPEMSequence;
324 }
325 else {
326 result = 2; /* @@@ Return 2 triggers usage message. */
327 goto cleanup;
328 }
329 break;
330 case 'w':
331 wrapped = TRUE;
332 break;
333 case 'x':
334 nonExtractable = TRUE;
335 break;
336 case 'P':
337 passphrase = optarg;
338 break;
339 case 'a':
340 /* this takes an additional argument */
341 if(optind > (argc - 1)) {
342 result = 2; /* @@@ Return 2 triggers usage message. */
343 goto cleanup;
344 }
345 attrNames = (char **)realloc(attrNames, numExtendedAttributes * sizeof(char *));
346 attrValues = (char **)realloc(attrValues, numExtendedAttributes * sizeof(char *));
347 attrNames[numExtendedAttributes] = optarg;
348 attrValues[numExtendedAttributes] = argv[optind];
349 numExtendedAttributes++;
350 optind++;
351 break;
352 case 'A':
353 always_allow = TRUE;
354 access_specified = TRUE;
355 break;
356 case 'T':
357 if (optarg[0])
358 {
359 SecTrustedApplicationRef app = NULL;
360 OSStatus status = noErr;
361 /* check whether the argument specifies an application group */
362 const char *groupPrefix = "group://";
363 size_t prefixLen = strlen(groupPrefix);
364 if (strlen(optarg) > prefixLen && !memcmp(optarg, groupPrefix, prefixLen)) {
365 const char *groupName = &optarg[prefixLen];
366 if ((status = SecTrustedApplicationCreateApplicationGroup(groupName, NULL, &app)) != noErr) {
367 sec_error("SecTrustedApplicationCreateApplicationGroup %s: %s", optarg, sec_errstr(status));
368 }
369 } else {
370 if ((status = SecTrustedApplicationCreateFromPath(optarg, &app)) != noErr) {
371 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg, sec_errstr(status));
372 }
373 }
374
375 if (status) {
376 result = 1;
377 goto cleanup;
378 }
379
380 CFArrayAppendValue(trusted_list, app);
381 CFRelease(app);
382 }
383 access_specified = TRUE;
384 break;
385 case '?':
386 default:
387 result = 2; /* @@@ Return 2 triggers usage message. */
388 goto cleanup;
389 }
390 }
391
392 if(wrapped) {
393 switch(externFormat) {
394 case kSecFormatOpenSSL:
395 case kSecFormatUnknown: // i.e., use default
396 externFormat = kSecFormatWrappedOpenSSL;
397 break;
398 case kSecFormatSSH:
399 externFormat = kSecFormatWrappedSSH;
400 break;
401 case kSecFormatSSHv2:
402 /* there is no wrappedSSHv2 */
403 externFormat = kSecFormatWrappedOpenSSL;
404 break;
405 case kSecFormatWrappedPKCS8:
406 /* proceed */
407 break;
408 default:
409 fprintf(stderr, "Don't know how to wrap in specified format/type\n");
410 result = 2; /* @@@ Return 2 triggers usage message. */
411 goto cleanup;
412 }
413 }
414
415 if(kcName) {
416 kcRef = keychain_open(kcName);
417 if(kcRef == NULL) {
418 return 1;
419 }
420 }
421 if(readFile(inFile, &inFileData, &inFileLen)) {
422 sec_error("Error reading infile %s: %s", inFile, strerror(errno));
423 result = 1;
424 goto cleanup;
425 }
426 inData = CFDataCreate(NULL, inFileData, inFileLen);
427 if(inData == NULL) {
428 result = 1;
429 goto cleanup;
430 }
431 free(inFileData);
432
433 if(access_specified)
434 {
435 char *accessName = NULL;
436 CFStringRef fileStr = CFStringCreateWithCString(NULL, inFile, kCFStringEncodingUTF8);
437 if (fileStr) {
438 CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, fileStr, kCFURLPOSIXPathStyle, FALSE);
439 if (fileURL) {
440 CFStringRef nameStr = CFURLCopyLastPathComponent(fileURL);
441 if (nameStr) {
442 CFIndex nameLen = CFStringGetLength(nameStr);
443 CFIndex bufLen = 1 + CFStringGetMaximumSizeForEncoding(nameLen, kCFStringEncodingUTF8);
444 accessName = (char *)malloc(bufLen);
445 if (!CFStringGetCString(nameStr, accessName, bufLen-1, kCFStringEncodingUTF8))
446 accessName[0]=0;
447 nameLen = strlen(accessName);
448 char *p = &accessName[nameLen]; // initially points to terminating null
449 while (--nameLen > 0) {
450 if (*p == '.') { // strip extension, if any
451 *p = '\0';
452 break;
453 }
454 p--;
455 }
456 safe_CFRelease(&nameStr);
457 }
458 safe_CFRelease(&fileURL);
459 }
460 safe_CFRelease(&fileStr);
461 }
462
463 result = create_access(accessName, always_allow, trusted_list, &access);
464
465 if (accessName) {
466 free(accessName);
467 }
468 if (result != 0) {
469 goto cleanup;
470 }
471 }
472
473 result = do_keychain_import(kcRef, inData, externFormat, itemType, access,
474 nonExtractable, passphrase, inFile, attrNames,
475 attrValues, numExtendedAttributes);
476
477 cleanup:
478 safe_CFRelease(&trusted_list);
479 safe_CFRelease(&access);
480 safe_CFRelease(&kcRef);
481 safe_CFRelease(&inData);
482
483 return result;
484 }