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