]> git.saurik.com Git - apple/security.git/blob - SecurityTool/keychain_utilities.c
Security-57337.50.23.tar.gz
[apple/security.git] / SecurityTool / keychain_utilities.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_utilities.c
24 */
25
26 #include "keychain_utilities.h"
27 #include "security.h"
28
29 #include <Security/cssmapi.h>
30 #include <Security/SecAccess.h>
31 #include <Security/SecACL.h>
32 #include <Security/SecTrustedApplication.h>
33 #include <Security/SecKeychainItem.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <sys/param.h>
37 #include <libkern/OSByteOrder.h>
38
39 #include "readline.h"
40
41 // SecTrustedApplicationValidateWithPath
42 #include <Security/SecTrustedApplicationPriv.h>
43
44
45 void check_obsolete_keychain(const char *kcName)
46 {
47 if(kcName == NULL) {
48 return;
49 }
50 if(!strcmp(kcName, "/System/Library/Keychains/X509Anchors")) {
51 fprintf(stderr, "***************************************************************\n");
52 fprintf(stderr, " WARNING\n");
53 fprintf(stderr, "\n");
54 fprintf(stderr, "The keychain you are accessing, X509Anchors, is no longer\n");
55 fprintf(stderr, "used by Mac OS X as the system root certificate store.\n");
56 fprintf(stderr, "Please read the security man page for information on the \n");
57 fprintf(stderr, "add-trusted-cert command. New system root certificates should\n");
58 fprintf(stderr, "be added to the Admin Trust Settings domain and to the \n");
59 fprintf(stderr, "System keychain in /Library/Keychains.\n");
60 fprintf(stderr, "***************************************************************\n");
61 }
62 else if(!strcmp(kcName, "/System/Library/Keychains/X509Certificates")) {
63 fprintf(stderr, "***************************************************************\n");
64 fprintf(stderr, " WARNING\n");
65 fprintf(stderr, "\n");
66 fprintf(stderr, "The keychain you are accessing, X509Certificates, is no longer\n");
67 fprintf(stderr, "used by Mac OS X as the system intermediate certificate\n");
68 fprintf(stderr, "store. New system intermediate certificates should be added\n");
69 fprintf(stderr, "to the System keychain in /Library/Keychains.\n");
70 fprintf(stderr, "***************************************************************\n");
71 }
72 }
73
74 SecKeychainRef
75 keychain_open(const char *name)
76 {
77 SecKeychainRef keychain = NULL;
78 OSStatus result;
79
80 check_obsolete_keychain(name);
81 if (name && name[0] != '/')
82 {
83 CFArrayRef dynamic = NULL;
84 result = SecKeychainCopyDomainSearchList(
85 kSecPreferencesDomainDynamic, &dynamic);
86 if (result)
87 {
88 sec_error("SecKeychainCopyDomainSearchList %s: %s",
89 name, sec_errstr(result));
90 return NULL;
91 }
92 else
93 {
94 uint32_t i;
95 uint32_t count = dynamic ? CFArrayGetCount(dynamic) : 0;
96
97 for (i = 0; i < count; ++i)
98 {
99 char pathName[MAXPATHLEN];
100 UInt32 ioPathLength = sizeof(pathName);
101 bzero(pathName, ioPathLength);
102 keychain = (SecKeychainRef)CFArrayGetValueAtIndex(dynamic, i);
103 result = SecKeychainGetPath(keychain, &ioPathLength, pathName);
104 if (result)
105 {
106 sec_error("SecKeychainGetPath %s: %s",
107 name, sec_errstr(result));
108 return NULL;
109 }
110 if (!strncmp(pathName, name, ioPathLength))
111 {
112 CFRetain(keychain);
113 CFRelease(dynamic);
114 return keychain;
115 }
116 }
117 CFRelease(dynamic);
118 }
119 }
120
121 result = SecKeychainOpen(name, &keychain);
122 if (result)
123 {
124 sec_error("SecKeychainOpen %s: %s", name, sec_errstr(result));
125 }
126
127 return keychain;
128 }
129
130 CFTypeRef
131 keychain_create_array(int argc, char * const *argv)
132 {
133 if (argc == 0)
134 return NULL;
135 else if (argc == 1)
136 return keychain_open(argv[0]);
137 else
138 {
139 CFMutableArrayRef keychains = CFArrayCreateMutable(NULL, argc, &kCFTypeArrayCallBacks);
140 int ix;
141 for (ix = 0; ix < argc; ++ix)
142 {
143 SecKeychainRef keychain = keychain_open(argv[ix]);
144 if (keychain)
145 {
146 CFArrayAppendValue(keychains, keychain);
147 CFRelease(keychain);
148 }
149 }
150
151 return keychains;
152 }
153 }
154
155 int
156 parse_fourcharcode(const char *name, UInt32 *code)
157 {
158 UInt32 cc = 0;
159 int len = (name) ? strlen(name) : 0;
160
161 // error check the name
162 if (len != 4)
163 {
164 fprintf(stderr, "Error: four-character types must be exactly 4 characters long.\n");
165 if (len == 3) {
166 fprintf(stderr, "(Try \"%s \" instead of \"%s\")\n", name, name);
167 }
168 return 1;
169 }
170
171 int i;
172 for (i = 0; i < 4; ++i)
173 {
174 cc = (cc << 8) | name[i];
175 }
176
177 *code = cc; // note: this is in host byte order, suitable for passing to APIs
178
179 return 0;
180 }
181
182 int
183 print_keychain_name(FILE *stream, SecKeychainRef keychain)
184 {
185 int result = 0;
186 char pathName[MAXPATHLEN];
187 UInt32 ioPathLength = sizeof(pathName);
188 OSStatus status = SecKeychainGetPath(keychain, &ioPathLength, pathName);
189 if (status)
190 {
191 sec_perror("SecKeychainGetPath", status);
192 result = 1;
193 goto loser;
194 }
195
196 print_buffer(stream, ioPathLength, pathName);
197
198 loser:
199 return result;
200 }
201
202 int
203 print_keychain_version(FILE* stream, SecKeychainRef keychain)
204 {
205 int result = 0;
206 UInt32 version;
207 OSStatus status = SecKeychainGetKeychainVersion(keychain, &version);
208 if(status) {
209 sec_perror("SecKeychainGetKeychainVersion", status);
210 result = 1;
211 goto loser;
212 }
213
214 fprintf(stream, "%d", version);
215
216 loser:
217 return result;
218 }
219
220 static void
221 print_cfdata(FILE *stream, CFDataRef data)
222 {
223 if (data)
224 return print_buffer(stream, CFDataGetLength(data), CFDataGetBytePtr(data));
225 else
226 fprintf(stream, "<NULL>");
227 }
228
229 void
230 print_cfstring(FILE *stream, CFStringRef string)
231 {
232 if (!string)
233 fprintf(stream, "<NULL>");
234 else
235 {
236 const char *utf8 = CFStringGetCStringPtr(string, kCFStringEncodingUTF8);
237 if (utf8)
238 fprintf(stream, "%s", utf8);
239 else
240 {
241 CFRange rangeToProcess = CFRangeMake(0, CFStringGetLength(string));
242 while (rangeToProcess.length > 0)
243 {
244 UInt8 localBuffer[256];
245 CFIndex usedBufferLength;
246 CFIndex numChars = CFStringGetBytes(string, rangeToProcess,
247 kCFStringEncodingUTF8, '?', FALSE, localBuffer,
248 sizeof(localBuffer), &usedBufferLength);
249 if (numChars == 0)
250 break; // Failed to convert anything...
251
252 fprintf(stream, "%.*s", (int)usedBufferLength, localBuffer);
253 rangeToProcess.location += numChars;
254 rangeToProcess.length -= numChars;
255 }
256 }
257 }
258 }
259
260 static int
261 print_access(FILE *stream, SecAccessRef access, Boolean interactive)
262 {
263 CFArrayRef aclList = NULL;
264 CFIndex aclix, aclCount;
265 int result = 0;
266 OSStatus status;
267
268 status = SecAccessCopyACLList(access, &aclList);
269 if (status)
270 {
271 sec_perror("SecAccessCopyACLList", status);
272 result = 1;
273 goto loser;
274 }
275
276 aclCount = CFArrayGetCount(aclList);
277 fprintf(stream, "access: %lu entries\n", aclCount);
278 for (aclix = 0; aclix < aclCount; ++aclix)
279 {
280 CFArrayRef applicationList = NULL;
281 CFStringRef description = NULL;
282 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector = {};
283 CFIndex appix, appCount;
284
285 SecACLRef acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, aclix);
286 CSSM_ACL_AUTHORIZATION_TAG tags[64]; // Pick some upper limit
287 uint32 tagix, tagCount = sizeof(tags) / sizeof(*tags);
288 status = SecACLGetAuthorizations(acl, tags, &tagCount);
289 if (status)
290 {
291 sec_perror("SecACLGetAuthorizations", status);
292 result = 1;
293 goto loser;
294 }
295
296 fprintf(stream, " entry %lu:\n authorizations (%lu):", aclix,
297 (unsigned long)tagCount);
298 bool printPartitionIDList = false;
299 for (tagix = 0; tagix < tagCount; ++tagix)
300 {
301 CSSM_ACL_AUTHORIZATION_TAG tag = tags[tagix];
302 switch (tag)
303 {
304 case CSSM_ACL_AUTHORIZATION_ANY:
305 fputs(" any", stream);
306 break;
307 case CSSM_ACL_AUTHORIZATION_LOGIN:
308 fputs(" login", stream);
309 break;
310 case CSSM_ACL_AUTHORIZATION_GENKEY:
311 fputs(" genkey", stream);
312 break;
313 case CSSM_ACL_AUTHORIZATION_DELETE:
314 fputs(" delete", stream);
315 break;
316 case CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED:
317 fputs(" export_wrapped", stream);
318 break;
319 case CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR:
320 fputs(" export_clear", stream);
321 break;
322 case CSSM_ACL_AUTHORIZATION_IMPORT_WRAPPED:
323 fputs(" import_wrapped", stream);
324 break;
325 case CSSM_ACL_AUTHORIZATION_IMPORT_CLEAR:
326 fputs(" import_clear", stream);
327 break;
328 case CSSM_ACL_AUTHORIZATION_SIGN:
329 fputs(" sign", stream);
330 break;
331 case CSSM_ACL_AUTHORIZATION_ENCRYPT:
332 fputs(" encrypt", stream);
333 break;
334 case CSSM_ACL_AUTHORIZATION_DECRYPT:
335 fputs(" decrypt", stream);
336 break;
337 case CSSM_ACL_AUTHORIZATION_MAC:
338 fputs(" mac", stream);
339 break;
340 case CSSM_ACL_AUTHORIZATION_DERIVE:
341 fputs(" derive", stream);
342 break;
343 case CSSM_ACL_AUTHORIZATION_DBS_CREATE:
344 fputs(" dbs_create", stream);
345 break;
346 case CSSM_ACL_AUTHORIZATION_DBS_DELETE:
347 fputs(" dbs_delete", stream);
348 break;
349 case CSSM_ACL_AUTHORIZATION_DB_READ:
350 fputs(" db_read", stream);
351 break;
352 case CSSM_ACL_AUTHORIZATION_DB_INSERT:
353 fputs(" db_insert", stream);
354 break;
355 case CSSM_ACL_AUTHORIZATION_DB_MODIFY:
356 fputs(" db_modify", stream);
357 break;
358 case CSSM_ACL_AUTHORIZATION_DB_DELETE:
359 fputs(" db_delete", stream);
360 break;
361 case CSSM_ACL_AUTHORIZATION_CHANGE_ACL:
362 fputs(" change_acl", stream);
363 break;
364 case CSSM_ACL_AUTHORIZATION_CHANGE_OWNER:
365 fputs(" change_owner", stream);
366 break;
367 case CSSM_ACL_AUTHORIZATION_INTEGRITY:
368 fputs(" integrity", stream);
369 break;
370 case CSSM_ACL_AUTHORIZATION_PARTITION_ID:
371 fputs(" partition_id", stream);
372 printPartitionIDList = true;
373 break;
374 default:
375 fprintf(stream, " tag=%lu", (unsigned long)tag);
376 break;
377 }
378 }
379 fputc('\n', stream);
380
381 status = SecACLCopySimpleContents(acl, &applicationList, &description, &promptSelector);
382 if (status)
383 {
384 sec_perror("SecACLCopySimpleContents", status);
385 continue;
386 }
387
388 if (promptSelector.flags & CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE)
389 fputs(" require-password\n", stream);
390 else
391 fputs(" don't-require-password\n", stream);
392
393 fputs(" description: ", stream);
394 // special case for Partition IDs
395 if(printPartitionIDList) {
396 print_partition_id_list(stream, description);
397 } else {
398 print_cfstring(stream, description);
399 }
400 fputc('\n', stream);
401
402 if (applicationList)
403 {
404 appCount = CFArrayGetCount(applicationList);
405 fprintf(stream, " applications (%lu):\n", appCount);
406 }
407 else
408 {
409 appCount = 0;
410 fprintf(stream, " applications: <null>\n");
411 }
412
413 for (appix = 0; appix < appCount; ++appix)
414 {
415 const UInt8* bytes;
416 SecTrustedApplicationRef app = (SecTrustedApplicationRef)CFArrayGetValueAtIndex(applicationList, appix);
417 CFDataRef data = NULL;
418 fprintf(stream, " %lu: ", appix);
419 status = SecTrustedApplicationCopyData(app, &data);
420 if (status)
421 {
422 sec_perror("SecTrustedApplicationCopyData", status);
423 continue;
424 }
425
426 bytes = CFDataGetBytePtr(data);
427 if (bytes && bytes[0] == 0x2f) {
428 fprintf(stream, "%s", (const char *)bytes);
429 if ((status = SecTrustedApplicationValidateWithPath(app, (const char *)bytes)) == noErr) {
430 fprintf(stream, " (OK)");
431 } else {
432 fprintf(stream, " (status %d)", (int)status);
433 }
434 fprintf(stream, "\n");
435 } else {
436 print_cfdata(stream, data);
437 fputc('\n', stream);
438 }
439 if (data)
440 CFRelease(data);
441 }
442
443 if (applicationList)
444 CFRelease(applicationList);
445
446 if (description)
447 CFRelease(description);
448
449 if (interactive)
450 {
451 char buffer[10] = {};
452 fprintf(stderr, "Remove this acl? ");
453 if (readline(buffer, sizeof(buffer)) && buffer[0] == 'y')
454 {
455 fprintf(stderr, "removing acl\n");
456 status = SecACLRemove(acl);
457 if (status)
458 {
459 sec_perror("SecACLRemove", status);
460 continue;
461 }
462 }
463 }
464 }
465
466 loser:
467 if (aclList)
468 CFRelease(aclList);
469
470 return result;
471 }
472
473 int
474 print_keychain_item_attributes(FILE *stream, SecKeychainItemRef item, Boolean show_data, Boolean show_raw_data, Boolean show_acl, Boolean interactive)
475 {
476 int result = 0;
477 unsigned int ix;
478 OSStatus status;
479 SecKeychainRef keychain = NULL;
480 SecAccessRef access = NULL;
481 SecItemClass itemClass = 0;
482 UInt32 itemID;
483 SecKeychainAttributeList *attrList = NULL;
484 SecKeychainAttributeInfo *info = NULL;
485 UInt32 length = 0;
486 void *data = NULL;
487
488 status = SecKeychainItemCopyKeychain(item, &keychain);
489 if (status)
490 {
491 sec_perror("SecKeychainItemCopyKeychain", status);
492 result = 1;
493 goto loser;
494 }
495
496 fputs("keychain: ", stream);
497 result = print_keychain_name(stream, keychain);
498 fputc('\n', stream);
499 if (result)
500 goto loser;
501 fputs("version: ", stream);
502 result = print_keychain_version(stream, keychain);
503 fputc('\n', stream);
504 if (result)
505 goto loser;
506
507 /* First find out the item class. */
508 status = SecKeychainItemCopyAttributesAndData(item, NULL, &itemClass, NULL, NULL, NULL);
509 if (status)
510 {
511 sec_perror("SecKeychainItemCopyAttributesAndData", status);
512 result = 1;
513 goto loser;
514 }
515
516 fputs("class: ", stream);
517 char buffer[4];
518 buffer[3] = itemClass & 0xFF;
519 buffer[2] = (itemClass >> 8) & 0xFF;
520 buffer[1] = (itemClass >> 16) & 0xFF;
521 buffer[0] = (itemClass >> 24) & 0xFF;
522
523 print_buffer(stream, 4, buffer);
524 fputs("\nattributes:\n", stream);
525
526 switch (itemClass)
527 {
528 case kSecInternetPasswordItemClass:
529 itemID = CSSM_DL_DB_RECORD_INTERNET_PASSWORD;
530 break;
531 case kSecGenericPasswordItemClass:
532 itemID = CSSM_DL_DB_RECORD_GENERIC_PASSWORD;
533 break;
534 case 'ashp': /* kSecAppleSharePasswordItemClass */
535 itemID = CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD;
536 break;
537 default:
538 itemID = itemClass;
539 break;
540 }
541
542 /* Now get the AttributeInfo for it. */
543 status = SecKeychainAttributeInfoForItemID(keychain, itemID, &info);
544 if (status)
545 {
546 sec_perror("SecKeychainAttributeInfoForItemID", status);
547 result = 1;
548 goto loser;
549 }
550
551 status = SecKeychainItemCopyAttributesAndData(item, info, &itemClass, &attrList,
552 show_data ? &length : NULL,
553 show_data ? &data : NULL);
554 if (status)
555 {
556 sec_perror("SecKeychainItemCopyAttributesAndData", status);
557 result = 1;
558 goto loser;
559 }
560
561 if (info->count != attrList->count)
562 {
563 sec_error("info count: %ld != attribute count: %ld", info->count, attrList->count);
564 result = 1;
565 goto loser;
566 }
567
568 for (ix = 0; ix < info->count; ++ix)
569 {
570 UInt32 tag = info->tag[ix];
571 UInt32 format = info->format[ix];
572 SecKeychainAttribute *attribute = &attrList->attr[ix];
573 if (tag != attribute->tag)
574 {
575 sec_error("attribute %d of %ld info tag: %ld != attribute tag: %ld", ix, info->count, tag, attribute->tag);
576 result = 1;
577 goto loser;
578 }
579
580 fputs(" ", stream);
581 print_uint32(stream, tag);
582 switch (format)
583 {
584 case CSSM_DB_ATTRIBUTE_FORMAT_STRING:
585 fputs("<string>", stream);
586 break;
587 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32:
588 fputs("<sint32>", stream);
589 break;
590 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
591 fputs("<uint32>", stream);
592 break;
593 case CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM:
594 fputs("<bignum>", stream);
595 break;
596 case CSSM_DB_ATTRIBUTE_FORMAT_REAL:
597 fputs("<real>", stream);
598 break;
599 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE:
600 fputs("<timedate>", stream);
601 break;
602 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB:
603 fputs("<blob>", stream);
604 break;
605 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32:
606 fputs("<uint32>", stream);
607 break;
608 case CSSM_DB_ATTRIBUTE_FORMAT_COMPLEX:
609 fputs("<complex>", stream);
610 break;
611 default:
612 fprintf(stream, "<format: %d>", (int)format);
613 break;
614 }
615 fputs("=", stream);
616 if (!attribute->length && !attribute->data)
617 fputs("<NULL>", stream);
618 else
619 { switch (format)
620 {
621 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32:
622 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
623 {
624 print_uint32(stream, *(UInt32*) attribute->data);
625 break;
626 }
627
628 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32:
629 {
630 int n = attribute->length / sizeof(UInt32);
631 UInt32* ptr = (UInt32*) attribute->data;
632
633 while (n--)
634 {
635 print_uint32(stream, *ptr++);
636 }
637 }
638 break;
639
640 default:
641 {
642 print_buffer(stream, attribute->length, attribute->data);
643 }
644 break;
645 }
646 }
647 fputc('\n', stream);
648 }
649
650 if (show_data)
651 {
652 fputs("data:\n", stream);
653 print_buffer(stream, length, data);
654 fputc('\n', stream);
655 }
656
657 if (show_raw_data)
658 {
659 CSSM_DL_DB_HANDLE dldbHandle = {};
660 const CSSM_DB_UNIQUE_RECORD *uniqueRecordID = NULL;
661 CSSM_DATA data = {};
662 status = SecKeychainItemGetDLDBHandle(item, &dldbHandle);
663 if (status)
664 {
665 sec_perror("SecKeychainItemGetDLDBHandle", status);
666 result = 1;
667 goto loser;
668 }
669
670 status = SecKeychainItemGetUniqueRecordID(item, &uniqueRecordID);
671 if (status)
672 {
673 sec_perror("SecKeychainItemGetUniqueRecordID", status);
674 result = 1;
675 goto loser;
676 }
677
678 status = CSSM_DL_DataGetFromUniqueRecordId(dldbHandle, uniqueRecordID, NULL, &data);
679 if (status)
680 {
681 sec_perror("CSSM_DL_DataGetFromUniqueRecordId", status);
682 result = 1;
683 goto loser;
684 }
685
686 fputs("raw data:\n", stream);
687 print_buffer(stream, data.Length, data.Data);
688 fputc('\n', stream);
689
690 /* @@@ Hmm which allocators should we use here? */
691 free(data.Data);
692 }
693
694 if (show_acl)
695 {
696 status = SecKeychainItemCopyAccess(item, &access);
697 if (status == errSecNoAccessForItem)
698 fprintf(stream, "no access control for this item\n");
699 else
700 {
701 if (status)
702 {
703 sec_perror("SecKeychainItemCopyAccess", status);
704 result = 1;
705 goto loser;
706 }
707
708 result = print_access(stream, access, interactive);
709 if (result)
710 goto loser;
711
712 if (interactive)
713 {
714 char buffer[10] = {};
715 fprintf(stderr, "Update access? ");
716 if (readline(buffer, sizeof(buffer)) && buffer[0] == 'y')
717 {
718 fprintf(stderr, "Updating access\n");
719 status = SecKeychainItemSetAccess(item, access);
720 if (status)
721 {
722 sec_perror("SecKeychainItemSetAccess", status);
723 result = 1;
724 goto loser;
725 }
726 }
727 }
728 }
729 }
730
731 loser:
732 if (access)
733 CFRelease(access);
734
735 if (attrList)
736 {
737 status = SecKeychainItemFreeAttributesAndData(attrList, data);
738 if (status)
739 sec_perror("SecKeychainItemFreeAttributesAndData", status);
740 }
741
742 if (info)
743 {
744 status = SecKeychainFreeAttributeInfo(info);
745 if (status)
746 sec_perror("SecKeychainFreeAttributeInfo", status);
747 }
748
749 if (keychain)
750 CFRelease(keychain);
751
752 return result;
753 }
754
755 static void
756 print_buffer_hex(FILE *stream, UInt32 length, const void *data)
757 {
758 uint8 *p = (uint8 *) data;
759 while (length--)
760 {
761 int ch = *p++;
762 fprintf(stream, "%02X", ch);
763 }
764 }
765
766 static void
767 print_buffer_ascii(FILE *stream, UInt32 length, const void *data)
768 {
769 uint8 *p = (uint8 *) data;
770 while (length--)
771 {
772 int ch = *p++;
773 if (ch >= ' ' && ch <= '~' && ch != '\\')
774 {
775 fputc(ch, stream);
776 }
777 else
778 {
779 fputc('\\', stream);
780 fputc('0' + ((ch >> 6) & 7), stream);
781 fputc('0' + ((ch >> 3) & 7), stream);
782 fputc('0' + ((ch >> 0) & 7), stream);
783 }
784 }
785 }
786
787 void
788 print_buffer(FILE *stream, UInt32 length, const void *data)
789 {
790 uint8 *p = (uint8 *) data;
791 Boolean hex = FALSE;
792 Boolean ascii = FALSE;
793 UInt32 ix;
794 for (ix = 0; ix < length; ++ix)
795 {
796 int ch = *p++;
797 if (ch >= ' ' && ch <= '~' && ch != '\\')
798 ascii = TRUE;
799 else
800 hex = TRUE;
801 }
802
803 if (hex)
804 {
805 fputc('0', stream);
806 fputc('x', stream);
807 print_buffer_hex(stream, length, data);
808 if (ascii)
809 fputc(' ', stream);
810 fputc(' ', stream);
811 }
812 if (ascii)
813 {
814 fputc('"', stream);
815 print_buffer_ascii(stream, length, data);
816 fputc('"', stream);
817 }
818 }
819
820 void
821 print_uint32(FILE *stream, uint32 n)
822 {
823 n = OSSwapHostToBigInt32 (n);
824 print_buffer(stream, sizeof(UInt32), &n);
825 }
826
827 unsigned char
828 hexValue(char c)
829 {
830 static const char digits[] = "0123456789abcdef";
831 char *p;
832 if ((p = strchr(digits, tolower(c))))
833 return p - digits;
834 else
835 return 0;
836 }
837
838 void
839 fromHex(const char *hexDigits, CSSM_DATA *data)
840 {
841 size_t bytes = strlen(hexDigits) / 2; // (discards malformed odd end)
842 if (bytes > data->Length)
843 return;
844 // length(bytes); // (will assert if we try to grow it)
845 size_t n;
846 for (n = 0; n < bytes; n++) {
847 data->Data[n] = (uint8)(hexValue(hexDigits[2*n]) << 4 | hexValue(hexDigits[2*n+1]));
848 }
849 }
850
851 CFDataRef
852 cfFromHex(CFStringRef hex) {
853 // behavior is undefined if you pass in a non-hex string. Don't do that.
854 char* chex;
855 size_t len;
856
857 GetCStringFromCFString(hex, &chex, &len);
858 if(len == 0) {
859 return NULL;
860 }
861
862 size_t bytes = len/2;
863 CFMutableDataRef bin = CFDataCreateMutable(kCFAllocatorDefault, bytes);
864 CFDataIncreaseLength(bin, bytes);
865
866 if(!bin || CFDataGetLength(bin) != bytes) {
867 safe_CFRelease(bin);
868 return NULL;
869 }
870
871 UInt8* data = CFDataGetMutableBytePtr(bin);
872 for(size_t i = 0; i < bytes; i++) {
873 data[i] = (uint8)(hexValue(chex[2*i]) << 4 | hexValue(chex[2*i+1]));
874 }
875
876 return bin;
877 }
878
879 CFStringRef cfToHex(CFDataRef bin) {
880 size_t len = CFDataGetLength(bin) * 2;
881 CFMutableStringRef str = CFStringCreateMutable(NULL, len);
882
883 static const char* digits[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
884
885 const uint8_t* data = CFDataGetBytePtr(bin);
886 for (size_t i = 0; i < CFDataGetLength(bin); i++) {
887 CFStringAppendCString(str, digits[data[i] >> 4], 1);
888 CFStringAppendCString(str, digits[data[i] & 0xf], 1);
889 }
890 return str;
891 }
892
893 void
894 safe_CFRelease(void *cfTypeRefPtr)
895 {
896 CFTypeRef *obj = (CFTypeRef *)cfTypeRefPtr;
897 if (obj && *obj) {
898 CFRelease(*obj);
899 *obj = NULL;
900 }
901 }
902
903
904 void
905 GetCStringFromCFString(CFStringRef cfstring, char** cstr, size_t* len) {
906 CFIndex strLen = CFStringGetLength(cfstring);
907 CFIndex bufLen = 1 + CFStringGetMaximumSizeForEncoding(strLen, kCFStringEncodingUTF8);
908 *cstr = (char *)malloc(bufLen);
909 if (!CFStringGetCString(cfstring, *cstr, bufLen-1, kCFStringEncodingUTF8)) {
910 (*cstr)[0]=0;
911 }
912 // Handle non-8-bit characters.
913 *len = strnlen(*cstr, strLen);
914 }
915
916 CFDictionaryRef makeCFDictionaryFromData(CFDataRef data)
917 {
918 if (data) {
919 CFPropertyListRef plist = CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable, NULL);
920 if (plist && CFGetTypeID(plist) != CFDictionaryGetTypeID()) {
921 safe_CFRelease(&plist);
922 return NULL;
923 }
924 return (CFDictionaryRef) plist;
925 } else {
926 return NULL;
927 }
928 }
929
930 void print_partition_id_list(FILE* stream, CFStringRef description) {
931 CFDataRef binary = NULL;
932 CFDictionaryRef partitionList = NULL;
933 CFArrayRef partitionIDs = NULL;
934
935 if(!description) {
936 goto error;
937 }
938 binary = cfFromHex(description);
939 if(!binary) {
940 goto error;
941 }
942 partitionList = makeCFDictionaryFromData(binary);
943 if(!partitionList) {
944 goto error;
945 }
946
947 partitionIDs = CFDictionaryGetValue(partitionList, CFSTR("Partitions"));
948 if(!partitionIDs) {
949 goto error;
950 }
951
952 for(size_t i = 0; i < CFArrayGetCount(partitionIDs); i++) {
953 CFStringRef s = CFArrayGetValueAtIndex(partitionIDs, i);
954 if(!s) {
955 goto error;
956 }
957
958 if(i != 0) {
959 fprintf(stream, ", ");
960 }
961 print_cfstring(stream, s);
962 }
963
964 goto cleanup;
965 error:
966 fprintf(stream, "invalid partition ID: ");
967 print_cfstring(stream, description);
968 cleanup:
969 // don't release partitionIDs; it's an element of partitionList
970 safe_CFRelease(&binary);
971 safe_CFRelease(&partitionList);
972
973 return;
974 }
975
976 /*
977 * map a 6-bit binary value to a printable character.
978 */
979 static const
980 unsigned char bintoasc[] =
981 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
982
983 /*
984 * map 6 bits to a printing char
985 */
986 #define ENC(c) (bintoasc[((c) & 0x3f)])
987
988 #define PAD '='
989
990 /*
991 * map one group of up to 3 bytes at inp to 4 bytes at outp.
992 * Count is number of valid bytes in *inp; if less than 3, the
993 * 1 or two extras must be zeros.
994 */
995 static void
996 encChunk(const unsigned char *inp,
997 unsigned char *outp,
998 int count)
999 {
1000 unsigned char c1, c2, c3, c4;
1001
1002 c1 = *inp >> 2;
1003 c2 = ((inp[0] << 4) & 0x30) | ((inp[1] >> 4) & 0xf);
1004 c3 = ((inp[1] << 2) & 0x3c) | ((inp[2] >> 6) & 0x3);
1005 c4 = inp[2] & 0x3f;
1006 *outp++ = ENC(c1);
1007 *outp++ = ENC(c2);
1008 if (count == 1) {
1009 *outp++ = PAD;
1010 *outp = PAD;
1011 } else {
1012 *outp++ = ENC(c3);
1013 if (count == 2) {
1014 *outp = PAD;
1015 }
1016 else {
1017 *outp = ENC(c4);
1018 }
1019 }
1020 }
1021
1022 static unsigned char *
1023 malloc_enc64_with_lines(const unsigned char *inbuf,
1024 unsigned inlen,
1025 unsigned linelen,
1026 unsigned *outlen)
1027 {
1028 unsigned outTextLen;
1029 unsigned len; // to malloc, liberal
1030 unsigned olen = 0; // actual output size
1031 unsigned char *outbuf;
1032 unsigned char endbuf[3];
1033 unsigned i;
1034 unsigned char *outp;
1035 unsigned numLines;
1036 unsigned thisLine;
1037
1038 outTextLen = ((inlen + 2) / 3) * 4;
1039 if(linelen) {
1040 /*
1041 * linelen must be 0 mod 4 for this to work; round up...
1042 */
1043 if((linelen & 0x03) != 0) {
1044 linelen = (linelen + 3) & 0xfffffffc;
1045 }
1046 numLines = (outTextLen + linelen - 1)/ linelen;
1047 }
1048 else {
1049 numLines = 1;
1050 }
1051
1052 /*
1053 * Total output size = encoded text size plus one newline per
1054 * line of output, plus trailing NULL. We always generate newlines
1055 * as \n; when decoding, we tolerate \r\n (Microsoft) or \n.
1056 */
1057 len = outTextLen + (2 * numLines) + 1;
1058 outbuf = (unsigned char*)malloc(len);
1059 outp = outbuf;
1060 thisLine = 0;
1061
1062 while(inlen) {
1063 if(inlen < 3) {
1064 for(i=0; i<3; i++) {
1065 if(i < inlen) {
1066 endbuf[i] = inbuf[i];
1067 }
1068 else {
1069 endbuf[i] = 0;
1070 }
1071 }
1072 encChunk(endbuf, outp, inlen);
1073 inlen = 0;
1074 }
1075 else {
1076 encChunk(inbuf, outp, 3);
1077 inlen -= 3;
1078 inbuf += 3;
1079 }
1080 outp += 4;
1081 thisLine += 4;
1082 olen += 4;
1083 if((linelen != 0) && (thisLine >= linelen) && inlen) {
1084 /*
1085 * last trailing newline added below
1086 * Note we don't split 4-byte output chunks over newlines
1087 */
1088 *outp++ = '\n';
1089 olen++;
1090 thisLine = 0;
1091 }
1092 }
1093 *outp++ = '\n';
1094 olen += 1;
1095 *outlen = olen;
1096 return outbuf;
1097 }
1098
1099 void
1100 print_buffer_pem(FILE *stream, const char *headerString, UInt32 length, const void *data)
1101 {
1102 unsigned char *buf;
1103 unsigned bufLen;
1104
1105 if (headerString)
1106 fprintf(stream, "-----BEGIN %s-----\n", headerString);
1107 buf = malloc_enc64_with_lines(data, length, 64, &bufLen);
1108 fwrite(buf, bufLen, 1, stream);
1109 free(buf);
1110 if (headerString)
1111 fprintf(stream, "-----END %s-----\n", headerString);
1112 }
1113
1114 char*
1115 prompt_password(const char* keychainName) {
1116 const char *fmt = "password to unlock %s: ";
1117 const char *name = keychainName ? keychainName : "default";
1118 char *prompt = malloc(strlen(fmt) + strlen(name));
1119 sprintf(prompt, fmt, name);
1120 char *password = getpass(prompt);
1121 free(prompt);
1122 return password;
1123 }