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