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"
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>
42 // SecTrustedApplicationCreateApplicationGroup
43 #include <Security/SecTrustedApplicationPriv.h>
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$@\":")
48 static int do_keychain_import(
51 SecExternalFormat externFormat
,
52 SecExternalItemType itemType
,
54 Boolean nonExtractable
,
55 const char *passphrase
,
59 unsigned numExtendedAttributes
)
61 SecKeyImportExportParameters keyParams
;
64 CFArrayRef outArray
= NULL
;
68 int numIdentities
= 0;
72 CFStringRef passStr
= NULL
;
73 CFStringRef promptStr
= NULL
;
74 CFStringRef retryStr
= NULL
;
77 * Specify some kind of passphrase in case caller doesn't know this
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
;
87 keyParams
.flags
= kSecKeySecurePassphrase
;
90 // explicitly set the key attributes, omitting the CSSM_KEYATTR_EXTRACTABLE bit
91 keyParams
.keyAttributes
= CSSM_KEYATTR_PERMANENT
| CSSM_KEYATTR_SENSITIVE
;
93 keyParams
.accessRef
= access
;
95 fileStr
= CFStringCreateWithCString(NULL
, fileName
, kCFStringEncodingUTF8
);
97 CFURLRef fileURL
= CFURLCreateWithFileSystemPath(NULL
, fileStr
, kCFURLPOSIXPathStyle
, FALSE
);
99 CFStringRef nameStr
= CFURLCopyLastPathComponent(fileURL
);
101 safe_CFRelease(&fileStr
);
104 safe_CFRelease(&fileURL
);
107 promptStr
= CFStringCreateWithFormat(NULL
, NULL
, KC_IMPORT_KEY_PASSWORD_MESSAGE
, fileStr
);
108 retryStr
= CFStringCreateWithFormat(NULL
, NULL
, KC_IMPORT_KEY_PASSWORD_RETRYMESSAGE
, fileStr
);
112 keyParams
.alertPrompt
= (tryCount
== 0) ? promptStr
: retryStr
;
114 ortn
= SecKeychainItemImport(inData
,
118 0, /* flags not used (yet) */
124 if (ortn
== errSecPkcs12VerifyFailure
&& ++tryCount
< 3) {
127 sec_perror("SecKeychainItemImport", ortn
);
135 * Parse returned items & report to user
137 if(outArray
== NULL
) {
138 sec_error("No keychain items found");
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()) {
149 else if(itemType
== SecCertificateGetTypeID()) {
152 else if(itemType
== SecKeyGetTypeID()) {
156 sec_error("Unexpected item type returned from SecKeychainItemImport");
163 if(numIdentities
> 1) {
169 fprintf(stdout
, "%d %s imported.\n", numIdentities
, str
);
179 fprintf(stdout
, "%d %s imported.\n", numKeys
, str
);
184 str
= "certificates";
189 fprintf(stdout
, "%d %s imported.\n", numCerts
, str
);
192 /* optionally apply extended attributes */
193 if(numExtendedAttributes
) {
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
);
205 cssmPerror("SecKeychainItemSetExtendedAttribute", ortn
);
209 } /* for each imported item */
210 CFRelease(attrNameStr
);
211 CFRelease(attrValueData
);
215 } /* for each extended attribute */
219 safe_CFRelease(&fileStr
);
220 safe_CFRelease(&outArray
);
221 safe_CFRelease(&passStr
);
222 safe_CFRelease(&promptStr
);
223 safe_CFRelease(&retryStr
);
229 keychain_import(int argc
, char * const *argv
)
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
);
253 result
= 2; /* @@@ Return 2 triggers usage message. */
257 if((argc
== 2) && (inFile
[0] == '-')) {
263 while ((ch
= getopt(argc
, argv
, "k:t:f:P:wxa:hAT:")) != -1)
271 if(!strcmp("pub", optarg
)) {
272 itemType
= kSecItemTypePublicKey
;
274 else if(!strcmp("priv", optarg
)) {
275 itemType
= kSecItemTypePrivateKey
;
277 else if(!strcmp("session", optarg
)) {
278 itemType
= kSecItemTypeSessionKey
;
280 else if(!strcmp("cert", optarg
)) {
281 itemType
= kSecItemTypeCertificate
;
283 else if(!strcmp("agg", optarg
)) {
284 itemType
= kSecItemTypeAggregate
;
287 result
= 2; /* @@@ Return 2 triggers usage message. */
292 if(!strcmp("openssl", optarg
)) {
293 externFormat
= kSecFormatOpenSSL
;
295 else if(!strcmp("openssh1", optarg
)) {
296 externFormat
= kSecFormatSSH
;
298 else if(!strcmp("openssh2", optarg
)) {
299 externFormat
= kSecFormatSSHv2
;
301 else if(!strcmp("bsafe", optarg
)) {
302 externFormat
= kSecFormatBSAFE
;
304 else if(!strcmp("raw", optarg
)) {
305 externFormat
= kSecFormatRawKey
;
307 else if(!strcmp("pkcs7", optarg
)) {
308 externFormat
= kSecFormatPKCS7
;
310 else if(!strcmp("pkcs8", optarg
)) {
311 externFormat
= kSecFormatWrappedPKCS8
;
313 else if(!strcmp("pkcs12", optarg
)) {
314 externFormat
= kSecFormatPKCS12
;
316 else if(!strcmp("netscape", optarg
)) {
317 externFormat
= kSecFormatNetscapeCertSequence
;
319 else if(!strcmp("x509", optarg
)) {
320 externFormat
= kSecFormatX509Cert
;
322 else if(!strcmp("pemseq", optarg
)) {
323 externFormat
= kSecFormatPEMSequence
;
326 result
= 2; /* @@@ Return 2 triggers usage message. */
334 nonExtractable
= TRUE
;
340 /* this takes an additional argument */
341 if(optind
> (argc
- 1)) {
342 result
= 2; /* @@@ Return 2 triggers usage message. */
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
++;
354 access_specified
= TRUE
;
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
));
370 if ((status
= SecTrustedApplicationCreateFromPath(optarg
, &app
)) != noErr
) {
371 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg
, sec_errstr(status
));
380 CFArrayAppendValue(trusted_list
, app
);
383 access_specified
= TRUE
;
387 result
= 2; /* @@@ Return 2 triggers usage message. */
393 switch(externFormat
) {
394 case kSecFormatOpenSSL
:
395 case kSecFormatUnknown
: // i.e., use default
396 externFormat
= kSecFormatWrappedOpenSSL
;
399 externFormat
= kSecFormatWrappedSSH
;
401 case kSecFormatSSHv2
:
402 /* there is no wrappedSSHv2 */
403 externFormat
= kSecFormatWrappedOpenSSL
;
405 case kSecFormatWrappedPKCS8
:
409 fprintf(stderr
, "Don't know how to wrap in specified format/type\n");
410 result
= 2; /* @@@ Return 2 triggers usage message. */
416 kcRef
= keychain_open(kcName
);
421 if(readFile(inFile
, &inFileData
, &inFileLen
)) {
422 sec_error("Error reading infile %s: %s", inFile
, strerror(errno
));
426 inData
= CFDataCreate(NULL
, inFileData
, inFileLen
);
435 char *accessName
= NULL
;
436 CFStringRef fileStr
= CFStringCreateWithCString(NULL
, inFile
, kCFStringEncodingUTF8
);
438 CFURLRef fileURL
= CFURLCreateWithFileSystemPath(NULL
, fileStr
, kCFURLPOSIXPathStyle
, FALSE
);
440 CFStringRef nameStr
= CFURLCopyLastPathComponent(fileURL
);
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
))
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
456 safe_CFRelease(&nameStr
);
458 safe_CFRelease(&fileURL
);
460 safe_CFRelease(&fileStr
);
463 result
= create_access(accessName
, always_allow
, trusted_list
, &access
);
473 result
= do_keychain_import(kcRef
, inData
, externFormat
, itemType
, access
,
474 nonExtractable
, passphrase
, inFile
, attrNames
,
475 attrValues
, numExtendedAttributes
);
478 safe_CFRelease(&trusted_list
);
479 safe_CFRelease(&access
);
480 safe_CFRelease(&kcRef
);
481 safe_CFRelease(&inData
);