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