]> git.saurik.com Git - apple/security.git/blame - SecurityTool/keychain_export.c
Security-57740.60.18.tar.gz
[apple/security.git] / SecurityTool / keychain_export.c
CommitLineData
d8f41ccd
A
1/*
2 * Copyright (c) 2003-2004,2006,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_export.c
24 */
25
26#include "keychain_export.h"
27#include "keychain_utilities.h"
fa7225c8 28#include "security_tool.h"
d8f41ccd
A
29
30#include <errno.h>
31#include <string.h>
32#include <unistd.h>
33#include <Security/SecImportExport.h>
34#include <Security/SecKeychainItem.h>
35#include <Security/SecKeychainSearch.h>
36#include <Security/SecIdentitySearch.h>
37#include <Security/SecKey.h>
38#include <Security/SecCertificate.h>
fa7225c8
A
39#include <Security/SecItem.h>
40#include <Security/SecAccessControl.h>
41#include <Security/SecAccessControlPriv.h>
d8f41ccd
A
42#include <security_cdsa_utils/cuFileIo.h>
43#include <CoreFoundation/CoreFoundation.h>
44#include <stdio.h>
45
46typedef enum {
47 IS_Certs,
48 IS_AllKeys,
49 IS_PubKeys,
50 IS_PrivKeys,
51 IS_Identities,
52 IS_All
53} ItemSpec;
54
55/*
56 * Add all itmes of specified class from a keychain to an array.
57 * Item class are things like kSecCertificateItemClass, and
58 * CSSM_DL_DB_RECORD_PRIVATE_KEY. Identities are searched separately.
59 */
60static OSStatus addKcItems(
61 SecKeychainRef kcRef,
62 SecItemClass itemClass, // kSecCertificateItemClass
63 CFMutableArrayRef outArray,
64 unsigned *numItems) // UPDATED on return
65{
66 OSStatus ortn;
67 SecKeychainSearchRef srchRef;
68
69 ortn = SecKeychainSearchCreateFromAttributes(kcRef,
70 itemClass,
71 NULL, // no attrs
72 &srchRef);
73 if(ortn) {
74 sec_perror("SecKeychainSearchCreateFromAttributes", ortn);
75 return ortn;
76 }
77 for(;;) {
78 SecKeychainItemRef itemRef;
79 ortn = SecKeychainSearchCopyNext(srchRef, &itemRef);
80 if(ortn) {
81 if(ortn == errSecItemNotFound) {
82 /* normal search end */
83 ortn = noErr;
84 }
85 else {
86 sec_perror("SecIdentitySearchCopyNext", ortn);
87 }
88 break;
89 }
90 CFArrayAppendValue(outArray, itemRef);
91 CFRelease(itemRef); // array owns the item
92 (*numItems)++;
93 }
94 CFRelease(srchRef);
95 return ortn;
96}
97
98/*
99 * Add all SecIdentityRefs from a keychain into an array.
100 */
101static OSStatus addIdentities(
102 SecKeychainRef kcRef,
103 CFMutableArrayRef outArray,
104 unsigned *numItems) // UPDATED on return
105{
106 /* Search for all identities */
107 SecIdentitySearchRef srchRef;
108 OSStatus ortn = SecIdentitySearchCreate(kcRef,
109 0, // keyUsage - any
110 &srchRef);
111 if(ortn) {
112 sec_perror("SecIdentitySearchCreate", ortn);
113 return ortn;
114 }
115
116 do {
117 SecIdentityRef identity;
118 ortn = SecIdentitySearchCopyNext(srchRef, &identity);
119 if(ortn) {
120 if(ortn == errSecItemNotFound) {
121 /* normal search end */
122 ortn = noErr;
123 }
124 else {
125 sec_perror("SecIdentitySearchCopyNext", ortn);
126 }
127 break;
128 }
129 CFArrayAppendValue(outArray, identity);
130
131 /* the array has the retain count we need */
132 CFRelease(identity);
133 (*numItems)++;
134 } while(ortn == noErr);
135 CFRelease(srchRef);
136 return ortn;
137}
138
139static int do_keychain_export(
140 SecKeychainRef kcRef,
141 SecExternalFormat externFormat,
142 ItemSpec itemSpec,
143 const char *passphrase,
144 int doPem,
145 const char *fileName)
146{
147 int result = 0;
148 CFIndex numItems;
149 unsigned numPrivKeys = 0;
150 unsigned numPubKeys = 0;
151 unsigned numCerts = 0;
152 unsigned numIdents = 0;
153 OSStatus ortn;
154 uint32 expFlags = 0; // SecItemImportExportFlags
155 SecKeyImportExportParameters keyParams;
156 CFStringRef passStr = NULL;
157 CFDataRef outData = NULL;
fa7225c8 158 size_t len;
d8f41ccd
A
159
160 /* gather items */
161 CFMutableArrayRef exportItems = CFArrayCreateMutable(NULL, 0,
162 &kCFTypeArrayCallBacks);
163 switch(itemSpec) {
164 case IS_Certs:
165 ortn = addKcItems(kcRef, kSecCertificateItemClass, exportItems, &numCerts);
166 if(ortn) {
167 result = 1;
168 goto loser;
169 }
170 break;
171
172 case IS_PrivKeys:
173 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, exportItems,
174 &numPrivKeys);
175 if(ortn) {
176 result = 1;
177 goto loser;
178 }
179 break;
180
181 case IS_PubKeys:
182 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PUBLIC_KEY, exportItems,
183 &numPubKeys);
184 if(ortn) {
185 result = 1;
186 goto loser;
187 }
188 break;
189
190 case IS_AllKeys:
191 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, exportItems,
192 &numPrivKeys);
193 if(ortn) {
194 result = 1;
195 goto loser;
196 }
197 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PUBLIC_KEY, exportItems,
198 &numPubKeys);
199 if(ortn) {
200 result = 1;
201 goto loser;
202 }
203 break;
204
205 case IS_All:
206 /* No public keys here - PKCS12 doesn't support them */
207 ortn = addKcItems(kcRef, kSecCertificateItemClass, exportItems, &numCerts);
208 if(ortn) {
209 result = 1;
210 goto loser;
211 }
212 ortn = addKcItems(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, exportItems,
213 &numPrivKeys);
214 if(ortn) {
215 result = 1;
216 goto loser;
217 }
218 break;
219
220 case IS_Identities:
221 ortn = addIdentities(kcRef, exportItems, &numIdents);
222 if(ortn) {
223 result = 1;
224 goto loser;
225 }
226 if(numIdents) {
227 numPrivKeys += numIdents;
228 numCerts += numIdents;
229 }
230 break;
231 default:
232 sec_error("Internal error parsing item_spec");
233 result = 1;
234 goto loser;
235 }
236
237 numItems = CFArrayGetCount(exportItems);
238 if(externFormat == kSecFormatUnknown) {
239 /* Use default export format per set of items */
240 if(numItems > 1) {
241 externFormat = kSecFormatPEMSequence;
242 }
243 else if(numCerts) {
244 externFormat = kSecFormatX509Cert;
245 }
246 else {
247 externFormat = kSecFormatOpenSSL;
248 }
249 }
250 if(doPem) {
251 expFlags |= kSecItemPemArmour;
252 }
253
254 /*
255 * Key related arguments, ignored if we're not exporting keys.
256 * Always specify some kind of passphrase - default is secure passkey.
257 */
258 memset(&keyParams, 0, sizeof(keyParams));
259 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
260 if(passphrase != NULL) {
261 passStr = CFStringCreateWithCString(NULL, passphrase, kCFStringEncodingASCII);
262 keyParams.passphrase = passStr;
263 }
264 else {
265 keyParams.flags = kSecKeySecurePassphrase;
266 }
267
268 /* Go */
269 ortn = SecKeychainItemExport(exportItems, externFormat, expFlags, &keyParams,
270 &outData);
271 if(ortn) {
272 sec_perror("SecKeychainItemExport", ortn);
273 result = 1;
274 goto loser;
275 }
276
277 len = CFDataGetLength(outData);
278 if(fileName) {
fa7225c8 279 int rtn = writeFileSizet(fileName, CFDataGetBytePtr(outData), len);
d8f41ccd
A
280 if(rtn == 0) {
281 if(!do_quiet) {
fa7225c8 282 fprintf(stderr, "...%lu bytes written to %s\n", len, fileName);
d8f41ccd
A
283 }
284 }
285 else {
286 sec_error("Error writing to %s: %s", fileName, strerror(errno));
287 result = 1;
288 }
289 }
290 else {
fa7225c8
A
291 size_t irtn = write(STDOUT_FILENO, CFDataGetBytePtr(outData), len);
292 if(irtn != (size_t)len) {
d8f41ccd
A
293 perror("write");
294 }
295 }
296loser:
297 if(exportItems) {
298 CFRelease(exportItems);
299 }
300 if(passStr) {
301 CFRelease(passStr);
302 }
303 if(outData) {
304 CFRelease(outData);
305 }
306 return result;
307}
308
309int
310keychain_export(int argc, char * const *argv)
311{
312 int ch, result = 0;
313
314 char *outFile = NULL;
315 char *kcName = NULL;
316 SecKeychainRef kcRef = NULL;
317 SecExternalFormat externFormat = kSecFormatUnknown;
318 ItemSpec itemSpec = IS_All;
319 int wrapped = 0;
320 int doPem = 0;
321 const char *passphrase = NULL;
322
323 while ((ch = getopt(argc, argv, "k:o:t:f:P:wph")) != -1)
324 {
325 switch (ch)
326 {
327 case 'k':
328 kcName = optarg;
329 break;
330 case 'o':
331 outFile = optarg;
332 break;
333 case 't':
334 if(!strcmp("certs", optarg)) {
335 itemSpec = IS_Certs;
336 }
337 else if(!strcmp("allKeys", optarg)) {
338 itemSpec = IS_AllKeys;
339 }
340 else if(!strcmp("pubKeys", optarg)) {
341 itemSpec = IS_PubKeys;
342 }
343 else if(!strcmp("privKeys", optarg)) {
344 itemSpec = IS_PrivKeys;
345 }
346 else if(!strcmp("identities", optarg)) {
347 itemSpec = IS_Identities;
348 }
349 else if(!strcmp("all", optarg)) {
350 itemSpec = IS_All;
351 }
352 else {
353 return 2; /* @@@ Return 2 triggers usage message. */
354 }
355 break;
356 case 'f':
357 if(!strcmp("openssl", optarg)) {
358 externFormat = kSecFormatOpenSSL;
359 }
360 else if(!strcmp("openssh1", optarg)) {
361 externFormat = kSecFormatSSH;
362 }
363 else if(!strcmp("openssh2", optarg)) {
364 externFormat = kSecFormatSSHv2;
365 }
366 else if(!strcmp("bsafe", optarg)) {
367 externFormat = kSecFormatBSAFE;
368 }
369 else if(!strcmp("raw", optarg)) {
370 externFormat = kSecFormatRawKey;
371 }
372 else if(!strcmp("pkcs7", optarg)) {
373 externFormat = kSecFormatPKCS7;
374 }
375 else if(!strcmp("pkcs8", optarg)) {
376 externFormat = kSecFormatWrappedPKCS8;
377 }
378 else if(!strcmp("pkcs12", optarg)) {
379 externFormat = kSecFormatPKCS12;
380 }
381 else if(!strcmp("netscape", optarg)) {
382 externFormat = kSecFormatNetscapeCertSequence;
383 }
384 else if(!strcmp("x509", optarg)) {
385 externFormat = kSecFormatX509Cert;
386 }
387 else if(!strcmp("pemseq", optarg)) {
388 externFormat = kSecFormatPEMSequence;
389 }
390 else {
391 return 2; /* @@@ Return 2 triggers usage message. */
392 }
393 break;
394 case 'w':
395 wrapped = 1;
396 break;
397 case 'p':
398 doPem = 1;
399 break;
400 case 'P':
401 passphrase = optarg;
402 break;
403 case '?':
404 default:
405 return 2; /* @@@ Return 2 triggers usage message. */
406 }
407 }
408
409 if(wrapped) {
410 switch(externFormat) {
411 case kSecFormatOpenSSL:
412 case kSecFormatUnknown: // i.e., use default
413 externFormat = kSecFormatWrappedOpenSSL;
414 break;
415 case kSecFormatSSH:
416 externFormat = kSecFormatWrappedSSH;
417 break;
418 case kSecFormatSSHv2:
419 /* there is no wrappedSSHv2 */
420 externFormat = kSecFormatWrappedOpenSSL;
421 break;
422 case kSecFormatWrappedPKCS8:
423 /* proceed */
424 break;
425 default:
426 sec_error("Don't know how to wrap in specified format/type");
427 return 2; /* @@@ Return 2 triggers usage message. */
428 }
429 }
430
431 if(kcName) {
432 kcRef = keychain_open(kcName);
433 if(kcRef == NULL) {
434 return 1;
435 }
436 }
437 result = do_keychain_export(kcRef, externFormat, itemSpec,
438 passphrase, doPem, outFile);
439
440 if(kcRef) {
441 CFRelease(kcRef);
442 }
443 return result;
444}
fa7225c8
A
445
446typedef struct {
447 CFMutableStringRef str;
448} ctk_dict2str_context;
449
450
451static void
452ctk_obj_to_str(CFTypeRef obj, char *buf, int bufLen, Boolean key);
453
454static void
455ctk_dict2str(const void *key, const void *value, void *context)
456{
457 char keyBuf[64] = { 0 };
458 ctk_obj_to_str(key, keyBuf, sizeof(keyBuf), true);
459
460 char valueBuf[1024] = { 0 };
461 ctk_obj_to_str(value, valueBuf, sizeof(valueBuf), false);
462
463 CFStringRef str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("\n\t\t\t%s : %s,"), keyBuf, valueBuf);
464 CFStringAppend(((ctk_dict2str_context *)context)->str, str);
465 CFRelease(str);
466}
467
468static void
469ctk_obj_to_str(CFTypeRef obj, char *buf, int bufLen, Boolean key)
470{
471 CFStringRef str = NULL;
472
473 if(CFGetTypeID(obj) == CFStringGetTypeID()) {
474 // CFStringRef - print the string as is (for keys) or quoted (values)
475 str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, key ? CFSTR("%@") : CFSTR("\"%@\""), obj);
476 } else if(CFGetTypeID(obj) == CFNumberGetTypeID()) {
477 // CFNumber - print the value using current locale
478 CFNumberRef num = (CFNumberRef)obj;
479
480 CFLocaleRef locale = CFLocaleCopyCurrent();
481 CFNumberFormatterRef fmt = CFNumberFormatterCreate(kCFAllocatorDefault, locale, kCFNumberFormatterDecimalStyle);
482 CFRelease(locale);
483
484 str = CFNumberFormatterCreateStringWithNumber(kCFAllocatorDefault, fmt, num);
485 CFRelease(fmt);
486 } else if(CFGetTypeID(obj) == CFDataGetTypeID()) {
487 // CFData - print the data as <hex bytes>
488 CFDataRef data = (CFDataRef)obj;
489
490 CFMutableStringRef hexStr = CFStringCreateMutable(kCFAllocatorDefault, CFDataGetLength(data) * 3);
491
492 for(int i = 0; i < CFDataGetLength(data); i++) {
493 CFStringRef hexByte = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%02x "), *(CFDataGetBytePtr(data) + i));
494 CFStringAppend(hexStr, hexByte);
495 CFRelease(hexByte);
496 }
497
498 // Get rid of the last excessive space.
499 if(CFDataGetLength(data)) {
500 CFStringDelete(hexStr, CFRangeMake(CFStringGetLength(hexStr) - 1, 1));
501 }
502
503 str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<%@>"), hexStr);
504 CFRelease(hexStr);
505 } else if(CFGetTypeID(obj) == CFBooleanGetTypeID()) {
506 // CFBoolean - print true/false
507 CFBooleanRef cfbool = (CFBooleanRef)obj;
508
509 str = CFStringCreateWithCString(kCFAllocatorDefault, CFBooleanGetValue(cfbool) ? "true" : "false", kCFStringEncodingUTF8);
510 } else if(CFGetTypeID(obj) == SecAccessControlGetTypeID()) {
511 // SecAccessControlRef - print the constraints dictionary
512 SecAccessControlRef ac = (SecAccessControlRef)obj;
513
514 CFDictionaryRef constraints = SecAccessControlGetConstraints(ac);
515 CFMutableStringRef constraintsStr = CFStringCreateMutable(kCFAllocatorDefault, 1024);
516 if(constraints && CFDictionaryGetCount(constraints)) {
517 ctk_dict2str_context context;
518 context.str = constraintsStr;
519 CFDictionaryApplyFunction(constraints, ctk_dict2str, &context);
520 CFStringReplace(constraintsStr, CFRangeMake(CFStringGetLength(constraintsStr) - 1, 1), CFSTR("\n\t\t"));
521 }
522
523 CFDictionaryRef protection = SecAccessControlGetProtection(ac);
524 CFMutableStringRef protectionStr = CFStringCreateMutable(kCFAllocatorDefault, 512);
525 if(protection && CFDictionaryGetCount(protection)) {
526 ctk_dict2str_context context;
527 context.str = protectionStr;
528 CFDictionaryApplyFunction(protection, ctk_dict2str, &context);
529 CFStringReplace(protectionStr, CFRangeMake(CFStringGetLength(protectionStr) - 1, 1), CFSTR("\n\t\t"));
530 }
531
532 str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("constraints: {%@}\n\t\tprotection: {%@}"), constraintsStr, protectionStr);
533 CFRelease(constraintsStr);
534 CFRelease(protectionStr);
535 }
536
537 // Fill the provided buffer with the converted string.
538 if(str) {
539 Boolean success = CFStringGetCString(str, buf, bufLen, kCFStringEncodingUTF8);
540 CFRelease(str);
541
542 if(success) {
543 return;
544 }
545 }
546
547 // Use object description as fallback...
548 CFStringRef description = CFCopyDescription(obj);
549 if(!CFStringGetCString(description, buf, bufLen, kCFStringEncodingUTF8)) {
550 // ...or else we don't know.
551 strncpy(buf, "<?>", bufLen);
552 }
553
554 CFRelease(description);
555}
556
557typedef struct {
558 int i;
559 const char *name;
560} ctk_print_context;
561
562
563static void
564ctk_print_dict(const void *key, const void *value, void *context)
565{
566 char keyBuf[64] = { 0 };
567 ctk_obj_to_str(key, keyBuf, sizeof(keyBuf), true);
568
569 char valueBuf[1024] = { 0 };
570 ctk_obj_to_str(value, valueBuf, sizeof(valueBuf), false);
571
572 printf("\t%s : %s\n", keyBuf, valueBuf);
573}
574
575static void
576ctk_dump_item_header(ctk_print_context *ctx)
577{
578 printf("\n");
579 printf("==== %s #%d\n", ctx->name, ctx->i);
580}
581
582static void
583ctk_dump_item_footer(ctk_print_context *ctx)
584{
585 printf("====\n");
586}
587
588static OSStatus
589ctk_dump_item(CFTypeRef item, ctk_print_context *ctx)
590{
591 OSStatus stat = errSecSuccess;
592
593 CFTypeID tid = CFGetTypeID(item);
594 if(tid == CFDictionaryGetTypeID()) {
595 // We expect a dictionary containing item attributes.
596 ctk_dump_item_header(ctx);
597 CFDictionaryApplyFunction((CFDictionaryRef)item, ctk_print_dict, ctx);
598 ctk_dump_item_footer(ctx);
599 } else {
600 stat = errSecInternalComponent;
601 printf("Unexpected item type ID: %lu\n", tid);
602 }
603
604 return stat;
605}
606
607static OSStatus
608ctk_dump_items(CFArrayRef items, CFTypeRef secClass, const char *name)
609{
610 OSStatus stat = errSecSuccess;
611
612 ctk_print_context ctx = { 1, name };
613
614 for(CFIndex i = 0; i < CFArrayGetCount(items); i++) {
615 CFTypeRef item = CFArrayGetValueAtIndex(items, i);
616 stat = ctk_dump_item(item, &ctx);
617 ctx.i++;
618
619 if(stat) {
620 break;
621 }
622 }
623
624 return stat;
625}
626
627static OSStatus
628ctk_dump(CFTypeRef secClass, const char *name, const char *tid)
629{
630 OSStatus stat = errSecSuccess;
631 CFDictionaryRef query = NULL;
632 CFTypeRef result = NULL;
633
634 const void *keys[] = {
635 kSecClass,
636 kSecMatchLimit,
637 kSecAttrAccessGroup,
638 kSecReturnAttributes,
639 };
640
641 const void *values[] = {
642 secClass,
643 kSecMatchLimitAll,
644 kSecAttrAccessGroupToken,
645 kCFBooleanTrue
646 };
647
648 // Query attributes of items of the requested secClass.
649 query = CFDictionaryCreate(kCFAllocatorDefault,
650 keys,
651 values,
652 sizeof(values) / sizeof(values[0]),
653 &kCFTypeDictionaryKeyCallBacks,
654 &kCFTypeDictionaryValueCallBacks);
655
656 if(tid) {
657 CFMutableDictionaryRef updatedQuery = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(query) + 1, query);
658 CFStringRef tidStr = CFStringCreateWithCString(kCFAllocatorDefault, tid, kCFStringEncodingUTF8);
659 CFDictionaryAddValue(updatedQuery, kSecAttrTokenID, tidStr);
660 CFRelease(tidStr);
661
662 CFRelease(query);
663 query = updatedQuery;
664 }
665
666 stat = SecItemCopyMatching(query, (CFTypeRef *)&result);
667 if(stat) {
6b200bc3
A
668 if (stat == errSecItemNotFound) {
669 fprintf(stderr, "No items found.\n");
670 } else {
671 sec_error("SecItemCopyMatching: %x (%d) - %s", stat, stat, sec_errstr(stat));
672 }
fa7225c8
A
673 goto cleanup;
674 }
675
676 // We expect an array of dictionaries containing item attributes as result.
677 if(CFGetTypeID(result) == CFArrayGetTypeID()) {
678 stat = ctk_dump_items((CFArrayRef)result, secClass, name);
679 } else {
680 stat = errSecInternalComponent;
681 }
682
683cleanup:
684 if(query) {
685 CFRelease(query);
686 }
687
688 if(result) {
689 CFRelease(result);
690 }
691
692 return stat;
693}
694
695int
696ctk_export(int argc, char * const *argv)
697{
698 OSStatus stat = errSecSuccess;
699
700 ItemSpec itemSpec = IS_All;
701 const char *tid = NULL;
702 int ch;
703
704 while ((ch = getopt(argc, argv, "i:t:h")) != -1) {
705 switch (ch) {
706 case 't':
707 if(!strcmp("certs", optarg)) {
708 itemSpec = IS_Certs;
709 }
710 else if(!strcmp("privKeys", optarg)) {
711 itemSpec = IS_PrivKeys;
712 }
713 else if(!strcmp("identities", optarg)) {
714 itemSpec = IS_Identities;
715 }
716 else if(!strcmp("all", optarg)) {
717 itemSpec = IS_All;
718 }
719 else {
720 return 2; /* @@@ Return 2 triggers usage message. */
721 }
722 break;
723 case 'i':
724 tid = optarg;
725 break;
726
727 case '?':
728 default:
729 return 2; /* @@@ Return 2 triggers usage message. */
730 }
731 }
732
733 CFTypeRef classes[] = { kSecClassCertificate, kSecClassKey, kSecClassIdentity };
734 const char* names[] = { "certificate", "private key", "identity" };
735 ItemSpec specs[] = { IS_Certs, IS_PrivKeys, IS_Identities };
736
6b200bc3 737 for(size_t i = 0; i < sizeof(classes)/sizeof(classes[0]); i++) {
fa7225c8
A
738 if(specs[i] == itemSpec || itemSpec == IS_All) {
739 stat = ctk_dump(classes[i], names[i], tid);
740 if(stat) {
741 break;
742 }
743 }
744 }
745
746 return stat;
747}