2 * Copyright (c) 2003-2009,2012,2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #include "keychain_import.h"
27 #include "keychain_utilities.h"
28 #include "access_utils.h"
29 #include "security_tool.h"
31 #include <utilities/fileIo.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>
43 // SecTrustedApplicationCreateApplicationGroup
44 #include <Security/SecTrustedApplicationPriv.h>
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$@\":")
49 static int do_keychain_import(
52 SecExternalFormat externFormat
,
53 SecExternalItemType itemType
,
55 Boolean nonExtractable
,
56 const char *passphrase
,
60 unsigned numExtendedAttributes
)
62 SecKeyImportExportParameters keyParams
;
65 CFArrayRef outArray
= NULL
;
69 int numIdentities
= 0;
73 CFStringRef passStr
= NULL
;
74 CFStringRef promptStr
= NULL
;
75 CFStringRef retryStr
= NULL
;
78 * Specify some kind of passphrase in case caller doesn't know this
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
;
88 keyParams
.flags
= kSecKeySecurePassphrase
;
91 // explicitly set the key attributes, omitting the CSSM_KEYATTR_EXTRACTABLE bit
92 keyParams
.keyAttributes
= CSSM_KEYATTR_PERMANENT
| CSSM_KEYATTR_SENSITIVE
;
94 keyParams
.accessRef
= access
;
96 fileStr
= CFStringCreateWithCString(NULL
, fileName
, kCFStringEncodingUTF8
);
98 CFURLRef fileURL
= CFURLCreateWithFileSystemPath(NULL
, fileStr
, kCFURLPOSIXPathStyle
, FALSE
);
100 CFStringRef nameStr
= CFURLCopyLastPathComponent(fileURL
);
102 safe_CFRelease(&fileStr
);
105 safe_CFRelease(&fileURL
);
108 promptStr
= CFStringCreateWithFormat(NULL
, NULL
, KC_IMPORT_KEY_PASSWORD_MESSAGE
, fileStr
);
109 retryStr
= CFStringCreateWithFormat(NULL
, NULL
, KC_IMPORT_KEY_PASSWORD_RETRYMESSAGE
, fileStr
);
113 keyParams
.alertPrompt
= (tryCount
== 0) ? promptStr
: retryStr
;
115 ortn
= SecKeychainItemImport(inData
,
119 0, /* flags not used (yet) */
125 if (ortn
== errSecPkcs12VerifyFailure
&& ++tryCount
< 3) {
128 sec_perror("SecKeychainItemImport", ortn
);
136 * Parse returned items & report to user
138 if(outArray
== NULL
) {
139 sec_error("No keychain items found");
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()) {
150 else if(itemType
== SecCertificateGetTypeID()) {
153 else if(itemType
== SecKeyGetTypeID()) {
157 sec_error("Unexpected item type returned from SecKeychainItemImport");
164 if(numIdentities
> 1) {
170 fprintf(stdout
, "%d %s imported.\n", numIdentities
, str
);
180 fprintf(stdout
, "%d %s imported.\n", numKeys
, str
);
185 str
= "certificates";
190 fprintf(stdout
, "%d %s imported.\n", numCerts
, str
);
193 /* optionally apply extended attributes */
194 if(numExtendedAttributes
) {
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
);
206 cssmPerror("SecKeychainItemSetExtendedAttribute", ortn
);
210 } /* for each imported item */
211 CFRelease(attrNameStr
);
212 CFRelease(attrValueData
);
216 } /* for each extended attribute */
220 safe_CFRelease(&fileStr
);
221 safe_CFRelease(&outArray
);
222 safe_CFRelease(&passStr
);
223 safe_CFRelease(&promptStr
);
224 safe_CFRelease(&retryStr
);
230 keychain_import(int argc
, char * const *argv
)
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
);
254 result
= 2; /* @@@ Return 2 triggers usage message. */
258 if((argc
== 2) && (inFile
[0] == '-')) {
264 while ((ch
= getopt(argc
, argv
, "k:t:f:P:wxa:hAT:")) != -1)
272 if(!strcmp("pub", optarg
)) {
273 itemType
= kSecItemTypePublicKey
;
275 else if(!strcmp("priv", optarg
)) {
276 itemType
= kSecItemTypePrivateKey
;
278 else if(!strcmp("session", optarg
)) {
279 itemType
= kSecItemTypeSessionKey
;
281 else if(!strcmp("cert", optarg
)) {
282 itemType
= kSecItemTypeCertificate
;
284 else if(!strcmp("agg", optarg
)) {
285 itemType
= kSecItemTypeAggregate
;
288 result
= 2; /* @@@ Return 2 triggers usage message. */
293 if(!strcmp("openssl", optarg
)) {
294 externFormat
= kSecFormatOpenSSL
;
296 else if(!strcmp("openssh1", optarg
)) {
297 externFormat
= kSecFormatSSH
;
299 else if(!strcmp("openssh2", optarg
)) {
300 externFormat
= kSecFormatSSHv2
;
302 else if(!strcmp("bsafe", optarg
)) {
303 externFormat
= kSecFormatBSAFE
;
305 else if(!strcmp("raw", optarg
)) {
306 externFormat
= kSecFormatRawKey
;
308 else if(!strcmp("pkcs7", optarg
)) {
309 externFormat
= kSecFormatPKCS7
;
311 else if(!strcmp("pkcs8", optarg
)) {
312 externFormat
= kSecFormatWrappedPKCS8
;
314 else if(!strcmp("pkcs12", optarg
)) {
315 externFormat
= kSecFormatPKCS12
;
317 else if(!strcmp("netscape", optarg
)) {
318 externFormat
= kSecFormatNetscapeCertSequence
;
320 else if(!strcmp("x509", optarg
)) {
321 externFormat
= kSecFormatX509Cert
;
323 else if(!strcmp("pemseq", optarg
)) {
324 externFormat
= kSecFormatPEMSequence
;
327 result
= 2; /* @@@ Return 2 triggers usage message. */
335 nonExtractable
= TRUE
;
341 /* this takes an additional argument */
342 if(optind
> (argc
- 1)) {
343 result
= 2; /* @@@ Return 2 triggers usage message. */
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
++;
355 access_specified
= TRUE
;
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
));
371 if ((status
= SecTrustedApplicationCreateFromPath(optarg
, &app
)) != noErr
) {
372 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg
, sec_errstr(status
));
381 CFArrayAppendValue(trusted_list
, app
);
384 access_specified
= TRUE
;
388 result
= 2; /* @@@ Return 2 triggers usage message. */
394 switch(externFormat
) {
395 case kSecFormatOpenSSL
:
396 case kSecFormatUnknown
: // i.e., use default
397 externFormat
= kSecFormatWrappedOpenSSL
;
400 externFormat
= kSecFormatWrappedSSH
;
402 case kSecFormatSSHv2
:
403 /* there is no wrappedSSHv2 */
404 externFormat
= kSecFormatWrappedOpenSSL
;
406 case kSecFormatWrappedPKCS8
:
410 fprintf(stderr
, "Don't know how to wrap in specified format/type\n");
411 result
= 2; /* @@@ Return 2 triggers usage message. */
417 kcRef
= keychain_open(kcName
);
422 OSStatus status
= SecKeychainCopyDefault(&kcRef
);
423 if (status
!= noErr
|| kcRef
== NULL
) {
427 if(readFileSizet(inFile
, &inFileData
, &inFileLen
)) {
428 sec_error("Error reading infile %s: %s", inFile
, strerror(errno
));
432 inData
= CFDataCreate(NULL
, inFileData
, inFileLen
);
441 char *accessName
= NULL
;
442 CFStringRef fileStr
= CFStringCreateWithCString(NULL
, inFile
, kCFStringEncodingUTF8
);
444 CFURLRef fileURL
= CFURLCreateWithFileSystemPath(NULL
, fileStr
, kCFURLPOSIXPathStyle
, FALSE
);
446 CFStringRef nameStr
= CFURLCopyLastPathComponent(fileURL
);
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
))
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
462 safe_CFRelease(&nameStr
);
464 safe_CFRelease(&fileURL
);
466 safe_CFRelease(&fileStr
);
469 result
= create_access(accessName
, always_allow
, trusted_list
, &access
);
479 result
= do_keychain_import(kcRef
, inData
, externFormat
, itemType
, access
,
480 nonExtractable
, passphrase
, inFile
, attrNames
,
481 attrValues
, numExtendedAttributes
);
484 safe_CFRelease(&trusted_list
);
485 safe_CFRelease(&access
);
486 safe_CFRelease(&kcRef
);
487 safe_CFRelease(&inData
);