2 * Copyright (c) 2003-2009,2012,2014-2019 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 * keychain_utilities.c
26 #include "keychain_utilities.h"
27 #include "security_tool.h"
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>
36 #include <sys/param.h>
37 #include <libkern/OSByteOrder.h>
38 #include <utilities/SecCFRelease.h>
40 #include "readline_cssm.h"
42 // SecTrustedApplicationValidateWithPath
43 #include <Security/SecTrustedApplicationPriv.h>
44 #include <Security/SecKeychainPriv.h>
47 void check_obsolete_keychain(const char *kcName
)
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");
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");
76 SecKeychainRef CF_RETURNS_RETAINED
77 keychain_open(const char *name
)
79 SecKeychainRef keychain
= NULL
;
82 check_obsolete_keychain(name
);
83 if (name
&& name
[0] != '/')
85 CFArrayRef dynamic
= NULL
;
86 result
= SecKeychainCopyDomainSearchList(
87 kSecPreferencesDomainDynamic
, &dynamic
);
90 sec_error("SecKeychainCopyDomainSearchList %s: %s",
91 name
, sec_errstr(result
));
97 CFIndex count
= dynamic
? CFArrayGetCount(dynamic
) : 0;
99 for (i
= 0; i
< count
; ++i
)
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
);
108 sec_error("SecKeychainGetPath %s: %s",
109 name
, sec_errstr(result
));
112 if (!strncmp(pathName
, name
, ioPathLength
))
119 CFReleaseNull(dynamic
);
124 result
= SecKeychainOpen(name
, &keychain
);
126 result
= errSecParam
;
130 sec_error("SecKeychainOpen %s: %s", name
, sec_errstr(result
));
137 keychain_create_array(int argc
, char * const *argv
)
142 return keychain_open(argv
[0]);
145 CFMutableArrayRef keychains
= CFArrayCreateMutable(NULL
, argc
, &kCFTypeArrayCallBacks
);
147 for (ix
= 0; ix
< argc
; ++ix
)
149 SecKeychainRef keychain
= keychain_open(argv
[ix
]);
152 CFArrayAppendValue(keychains
, keychain
);
162 parse_fourcharcode(const char *name
, UInt32
*code
)
165 size_t len
= (name
) ? strlen(name
) : 0;
167 // error check the name
170 fprintf(stderr
, "Error: four-character types must be exactly 4 characters long.\n");
172 fprintf(stderr
, "(Try \"%s \" instead of \"%s\")\n", name
, name
);
178 for (i
= 0; i
< 4; ++i
)
180 cc
= (cc
<< 8) | name
[i
];
183 *code
= cc
; // note: this is in host byte order, suitable for passing to APIs
189 print_keychain_name(FILE *stream
, SecKeychainRef keychain
)
192 char pathName
[MAXPATHLEN
];
193 UInt32 ioPathLength
= sizeof(pathName
);
194 OSStatus status
= SecKeychainGetPath(keychain
, &ioPathLength
, pathName
);
197 sec_perror("SecKeychainGetPath", status
);
202 print_buffer(stream
, ioPathLength
, pathName
);
209 print_keychain_version(FILE* stream
, SecKeychainRef keychain
)
213 OSStatus status
= SecKeychainGetKeychainVersion(keychain
, &version
);
215 sec_perror("SecKeychainGetKeychainVersion", status
);
220 fprintf(stream
, "%d", (uint32_t) version
);
227 print_cfdata(FILE *stream
, CFDataRef data
)
230 return print_buffer(stream
, CFDataGetLength(data
), CFDataGetBytePtr(data
));
232 fprintf(stream
, "<NULL>");
236 print_cfstring(FILE *stream
, CFStringRef string
)
239 fprintf(stream
, "<NULL>");
242 const char *utf8
= CFStringGetCStringPtr(string
, kCFStringEncodingUTF8
);
244 fprintf(stream
, "%s", utf8
);
247 CFRange rangeToProcess
= CFRangeMake(0, CFStringGetLength(string
));
248 while (rangeToProcess
.length
> 0)
250 UInt8 localBuffer
[256];
251 CFIndex usedBufferLength
;
252 CFIndex numChars
= CFStringGetBytes(string
, rangeToProcess
,
253 kCFStringEncodingUTF8
, '?', FALSE
, localBuffer
,
254 sizeof(localBuffer
), &usedBufferLength
);
256 break; // Failed to convert anything...
258 fprintf(stream
, "%.*s", (int)usedBufferLength
, localBuffer
);
259 rangeToProcess
.location
+= numChars
;
260 rangeToProcess
.length
-= numChars
;
267 print_access(FILE *stream
, SecAccessRef access
, Boolean interactive
)
269 CFArrayRef aclList
= NULL
;
270 CFIndex aclix
, aclCount
;
274 status
= SecAccessCopyACLList(access
, &aclList
);
277 sec_perror("SecAccessCopyACLList", status
);
282 aclCount
= CFArrayGetCount(aclList
);
283 fprintf(stream
, "access: %lu entries\n", aclCount
);
284 for (aclix
= 0; aclix
< aclCount
; ++aclix
)
286 CFArrayRef applicationList
= NULL
;
287 CFStringRef description
= NULL
;
288 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector
= {};
289 CFIndex appix
, appCount
;
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
);
297 sec_perror("SecACLGetAuthorizations", status
);
302 fprintf(stream
, " entry %lu:\n authorizations (%lu):", aclix
,
303 (unsigned long)tagCount
);
304 bool printPartitionIDList
= false;
305 for (tagix
= 0; tagix
< tagCount
; ++tagix
)
307 CSSM_ACL_AUTHORIZATION_TAG tag
= tags
[tagix
];
310 case CSSM_ACL_AUTHORIZATION_ANY
:
311 fputs(" any", stream
);
313 case CSSM_ACL_AUTHORIZATION_LOGIN
:
314 fputs(" login", stream
);
316 case CSSM_ACL_AUTHORIZATION_GENKEY
:
317 fputs(" genkey", stream
);
319 case CSSM_ACL_AUTHORIZATION_DELETE
:
320 fputs(" delete", stream
);
322 case CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED
:
323 fputs(" export_wrapped", stream
);
325 case CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR
:
326 fputs(" export_clear", stream
);
328 case CSSM_ACL_AUTHORIZATION_IMPORT_WRAPPED
:
329 fputs(" import_wrapped", stream
);
331 case CSSM_ACL_AUTHORIZATION_IMPORT_CLEAR
:
332 fputs(" import_clear", stream
);
334 case CSSM_ACL_AUTHORIZATION_SIGN
:
335 fputs(" sign", stream
);
337 case CSSM_ACL_AUTHORIZATION_ENCRYPT
:
338 fputs(" encrypt", stream
);
340 case CSSM_ACL_AUTHORIZATION_DECRYPT
:
341 fputs(" decrypt", stream
);
343 case CSSM_ACL_AUTHORIZATION_MAC
:
344 fputs(" mac", stream
);
346 case CSSM_ACL_AUTHORIZATION_DERIVE
:
347 fputs(" derive", stream
);
349 case CSSM_ACL_AUTHORIZATION_DBS_CREATE
:
350 fputs(" dbs_create", stream
);
352 case CSSM_ACL_AUTHORIZATION_DBS_DELETE
:
353 fputs(" dbs_delete", stream
);
355 case CSSM_ACL_AUTHORIZATION_DB_READ
:
356 fputs(" db_read", stream
);
358 case CSSM_ACL_AUTHORIZATION_DB_INSERT
:
359 fputs(" db_insert", stream
);
361 case CSSM_ACL_AUTHORIZATION_DB_MODIFY
:
362 fputs(" db_modify", stream
);
364 case CSSM_ACL_AUTHORIZATION_DB_DELETE
:
365 fputs(" db_delete", stream
);
367 case CSSM_ACL_AUTHORIZATION_CHANGE_ACL
:
368 fputs(" change_acl", stream
);
370 case CSSM_ACL_AUTHORIZATION_CHANGE_OWNER
:
371 fputs(" change_owner", stream
);
373 case CSSM_ACL_AUTHORIZATION_INTEGRITY
:
374 fputs(" integrity", stream
);
376 case CSSM_ACL_AUTHORIZATION_PARTITION_ID
:
377 fputs(" partition_id", stream
);
378 printPartitionIDList
= true;
381 fprintf(stream
, " tag=%lu", (unsigned long)tag
);
387 status
= SecACLCopySimpleContents(acl
, &applicationList
, &description
, &promptSelector
);
390 sec_perror("SecACLCopySimpleContents", status
);
394 if (promptSelector
.flags
& CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE
)
395 fputs(" require-password\n", stream
);
397 fputs(" don't-require-password\n", stream
);
399 fputs(" description: ", stream
);
400 // special case for Partition IDs
401 if(printPartitionIDList
) {
402 print_partition_id_list(stream
, description
);
404 print_cfstring(stream
, description
);
410 appCount
= CFArrayGetCount(applicationList
);
411 fprintf(stream
, " applications (%lu):\n", appCount
);
416 fprintf(stream
, " applications: <null>\n");
419 for (appix
= 0; appix
< appCount
; ++appix
)
422 SecTrustedApplicationRef app
= (SecTrustedApplicationRef
)CFArrayGetValueAtIndex(applicationList
, appix
);
423 CFDataRef data
= NULL
;
424 fprintf(stream
, " %lu: ", appix
);
425 status
= SecTrustedApplicationCopyData(app
, &data
);
428 sec_perror("SecTrustedApplicationCopyData", status
);
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)");
438 fprintf(stream
, " (status %d)", (int)status
);
440 fprintf(stream
, "\n");
442 print_cfdata(stream
, data
);
450 CFRelease(applicationList
);
453 CFRelease(description
);
457 char buffer
[10] = {};
458 fprintf(stderr
, "Remove this acl? ");
459 if (readline(buffer
, sizeof(buffer
)) && buffer
[0] == 'y')
461 fprintf(stderr
, "removing acl\n");
462 status
= SecACLRemove(acl
);
465 sec_perror("SecACLRemove", status
);
480 print_keychain_item_attributes(FILE *stream
, SecKeychainItemRef item
, Boolean show_data
, Boolean show_raw_data
, Boolean show_acl
, Boolean interactive
)
485 SecKeychainRef keychain
= NULL
;
486 SecAccessRef access
= NULL
;
487 SecItemClass itemClass
= 0;
489 SecKeychainAttributeList
*attrList
= NULL
;
490 SecKeychainAttributeInfo
*info
= NULL
;
494 status
= SecKeychainItemCopyKeychain(item
, &keychain
);
497 sec_perror("SecKeychainItemCopyKeychain", status
);
502 fputs("keychain: ", stream
);
503 result
= print_keychain_name(stream
, keychain
);
507 fputs("version: ", stream
);
508 result
= print_keychain_version(stream
, keychain
);
514 /* First find out the item class. */
515 status
= SecKeychainItemCopyAttributesAndData(item
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
518 sec_perror("SecKeychainItemCopyAttributesAndData", status
);
523 fputs("class: ", stream
);
525 buffer
[3] = itemClass
& 0xFF;
526 buffer
[2] = (itemClass
>> 8) & 0xFF;
527 buffer
[1] = (itemClass
>> 16) & 0xFF;
528 buffer
[0] = (itemClass
>> 24) & 0xFF;
530 print_buffer(stream
, 4, buffer
);
531 fputs("\nattributes:\n", stream
);
535 case kSecInternetPasswordItemClass
:
536 itemID
= CSSM_DL_DB_RECORD_INTERNET_PASSWORD
;
538 case kSecGenericPasswordItemClass
:
539 itemID
= CSSM_DL_DB_RECORD_GENERIC_PASSWORD
;
541 case 'ashp': /* kSecAppleSharePasswordItemClass */
542 itemID
= CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD
;
549 /* Now get the AttributeInfo for it. */
550 status
= SecKeychainAttributeInfoForItemID(keychain
, itemID
, &info
);
553 sec_perror("SecKeychainAttributeInfoForItemID", status
);
558 status
= SecKeychainItemCopyAttributesAndData(item
, info
, &itemClass
, &attrList
,
559 show_data
? &length
: NULL
,
560 show_data
? &data
: NULL
);
563 sec_perror("SecKeychainItemCopyAttributesAndData", status
);
568 if (info
->count
!= attrList
->count
)
570 sec_error("info count: %ld != attribute count: %ld", info
->count
, attrList
->count
);
575 for (ix
= 0; ix
< info
->count
; ++ix
)
577 UInt32 tag
= info
->tag
[ix
];
578 UInt32 format
= info
->format
[ix
];
579 SecKeychainAttribute
*attribute
= &attrList
->attr
[ix
];
580 if (tag
!= attribute
->tag
)
582 sec_error("attribute %d of %ld info tag: %ld != attribute tag: %ld", ix
, info
->count
, tag
, attribute
->tag
);
588 print_uint32(stream
, tag
);
591 case CSSM_DB_ATTRIBUTE_FORMAT_STRING
:
592 fputs("<string>", stream
);
594 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32
:
595 fputs("<sint32>", stream
);
597 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32
:
598 fputs("<uint32>", stream
);
600 case CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM
:
601 fputs("<bignum>", stream
);
603 case CSSM_DB_ATTRIBUTE_FORMAT_REAL
:
604 fputs("<real>", stream
);
606 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
:
607 fputs("<timedate>", stream
);
609 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB
:
610 fputs("<blob>", stream
);
612 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
:
613 fputs("<uint32>", stream
);
615 case CSSM_DB_ATTRIBUTE_FORMAT_COMPLEX
:
616 fputs("<complex>", stream
);
619 fprintf(stream
, "<format: %d>", (int)format
);
623 if (!attribute
->length
&& !attribute
->data
)
624 fputs("<NULL>", stream
);
628 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32
:
629 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32
:
631 print_uint32(stream
, *(UInt32
*) attribute
->data
);
635 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
:
637 int n
= attribute
->length
/ sizeof(UInt32
);
638 UInt32
* ptr
= (UInt32
*) attribute
->data
;
642 print_uint32(stream
, *ptr
++);
649 print_buffer(stream
, attribute
->length
, attribute
->data
);
659 fputs("data:\n", stream
);
660 print_buffer(stream
, length
, data
);
666 CSSM_DL_DB_HANDLE dldbHandle
= {};
667 const CSSM_DB_UNIQUE_RECORD
*uniqueRecordID
= NULL
;
669 status
= SecKeychainItemGetDLDBHandle(item
, &dldbHandle
);
672 sec_perror("SecKeychainItemGetDLDBHandle", status
);
677 status
= SecKeychainItemGetUniqueRecordID(item
, &uniqueRecordID
);
680 sec_perror("SecKeychainItemGetUniqueRecordID", status
);
685 status
= CSSM_DL_DataGetFromUniqueRecordId(dldbHandle
, uniqueRecordID
, NULL
, &data
);
688 sec_perror("CSSM_DL_DataGetFromUniqueRecordId", status
);
693 fputs("raw data:\n", stream
);
694 print_buffer(stream
, data
.Length
, data
.Data
);
697 /* @@@ Hmm which allocators should we use here? */
703 status
= SecKeychainItemCopyAccess(item
, &access
);
704 if (status
== errSecNoAccessForItem
)
705 fprintf(stream
, "no access control for this item\n");
710 sec_perror("SecKeychainItemCopyAccess", status
);
715 result
= print_access(stream
, access
, interactive
);
721 char buffer
[10] = {};
722 fprintf(stderr
, "Update access? ");
723 if (readline(buffer
, sizeof(buffer
)) && buffer
[0] == 'y')
725 fprintf(stderr
, "Updating access\n");
726 status
= SecKeychainItemSetAccess(item
, access
);
729 sec_perror("SecKeychainItemSetAccess", status
);
744 status
= SecKeychainItemFreeAttributesAndData(attrList
, data
);
746 sec_perror("SecKeychainItemFreeAttributesAndData", status
);
751 status
= SecKeychainFreeAttributeInfo(info
);
753 sec_perror("SecKeychainFreeAttributeInfo", status
);
763 print_buffer_hex(FILE *stream
, size_t length
, const void *data
)
765 uint8
*p
= (uint8
*) data
;
769 fprintf(stream
, "%02X", ch
);
774 print_buffer_ascii(FILE *stream
, size_t length
, const void *data
)
776 uint8
*p
= (uint8
*) data
;
780 if (ch
>= ' ' && ch
<= '~' && ch
!= '\\')
787 fputc('0' + ((ch
>> 6) & 7), stream
);
788 fputc('0' + ((ch
>> 3) & 7), stream
);
789 fputc('0' + ((ch
>> 0) & 7), stream
);
795 print_buffer(FILE *stream
, size_t length
, const void *data
)
797 uint8
*p
= (uint8
*) data
;
799 Boolean ascii
= FALSE
;
801 for (ix
= 0; ix
< length
; ++ix
)
804 if (ch
>= ' ' && ch
<= '~' && ch
!= '\\')
814 print_buffer_hex(stream
, length
, data
);
822 print_buffer_ascii(stream
, length
, data
);
828 print_uint32(FILE *stream
, uint32 n
)
830 n
= OSSwapHostToBigInt32 (n
);
831 print_buffer(stream
, sizeof(UInt32
), &n
);
834 // Returns base16 value of input hexadecimal character.
835 // If the input character is not valid hex, output error flag is set.
838 hexToValue(char c
, char *error
)
840 static const char digits
[] = "0123456789abcdef";
841 char *p
= strchr(digits
, tolower(c
));
842 if (p
) { return p
- digits
; }
843 if (error
) { *error
= 1; }
850 return hexToValue(c
, NULL
);
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.
860 convertHex(const char *hexString
, uint8_t **outData
, size_t *outLength
)
862 if (!hexString
) { return false; }
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
));
870 data
[0] = hexToValue(hexString
[0], &error
);
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
);
882 memset(data
, 0, num_bytes
);
886 *outLength
= num_bytes
;
892 fromHex(const char *hexDigits
, CSSM_DATA
*data
)
894 size_t bytes
= strlen(hexDigits
) / 2; // (discards malformed odd end)
895 if (bytes
> data
->Length
)
897 // length(bytes); // (will assert if we try to grow it)
899 for (n
= 0; n
< bytes
; n
++) {
900 data
->Data
[n
] = (uint8
)(hexValue(hexDigits
[2*n
]) << 4 | hexValue(hexDigits
[2*n
+1]));
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.
910 GetCStringFromCFString(hex
, &chex
, &len
);
915 size_t bytes
= len
/2;
916 CFMutableDataRef bin
= CFDataCreateMutable(kCFAllocatorDefault
, bytes
);
917 CFDataIncreaseLength(bin
, bytes
);
919 if(!bin
|| (size_t) CFDataGetLength(bin
) != bytes
) {
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]));
934 CFStringRef CF_RETURNS_RETAINED
cfToHex(CFDataRef bin
) {
935 size_t len
= CFDataGetLength(bin
) * 2;
936 CFMutableStringRef str
= CFStringCreateMutable(NULL
, len
);
938 static const char* digits
[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
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);
949 safe_CFRelease(void *cfTypeRefPtr
)
951 CFTypeRef
*obj
= (CFTypeRef
*)cfTypeRefPtr
;
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
)) {
967 // Handle non-8-bit characters.
968 *len
= strnlen(*cstr
, strLen
);
971 CFDictionaryRef CF_RETURNS_RETAINED
makeCFDictionaryFromData(CFDataRef data
)
974 CFPropertyListRef plist
= CFPropertyListCreateFromXMLData(NULL
, data
, kCFPropertyListImmutable
, NULL
);
975 if (plist
&& CFGetTypeID(plist
) != CFDictionaryGetTypeID()) {
976 safe_CFRelease(&plist
);
979 return (CFDictionaryRef
) plist
;
985 void print_partition_id_list(FILE* stream
, CFStringRef description
) {
986 CFDataRef binary
= NULL
;
987 CFDictionaryRef partitionList
= NULL
;
988 CFArrayRef partitionIDs
= NULL
;
993 binary
= cfFromHex(description
);
997 partitionList
= makeCFDictionaryFromData(binary
);
1002 partitionIDs
= CFDictionaryGetValue(partitionList
, CFSTR("Partitions"));
1007 for(CFIndex i
= 0; i
< CFArrayGetCount(partitionIDs
); i
++) {
1008 CFStringRef s
= CFArrayGetValueAtIndex(partitionIDs
, i
);
1014 fprintf(stream
, ", ");
1016 print_cfstring(stream
, s
);
1021 fprintf(stream
, "invalid partition ID: ");
1022 print_cfstring(stream
, description
);
1024 // don't release partitionIDs; it's an element of partitionList
1025 safe_CFRelease(&binary
);
1026 safe_CFRelease(&partitionList
);
1032 * map a 6-bit binary value to a printable character.
1035 unsigned char bintoasc
[] =
1036 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1039 * map 6 bits to a printing char
1041 #define ENC(c) (bintoasc[((c) & 0x3f)])
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.
1051 encChunk(const unsigned char *inp
,
1052 unsigned char *outp
,
1055 unsigned char c1
, c2
, c3
, c4
;
1058 c2
= ((inp
[0] << 4) & 0x30) | ((inp
[1] >> 4) & 0xf);
1059 c3
= ((inp
[1] << 2) & 0x3c) | ((inp
[2] >> 6) & 0x3);
1077 static unsigned char *
1078 malloc_enc64_with_lines(const unsigned char *inbuf
,
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];
1089 unsigned char *outp
;
1093 outTextLen
= ((((unsigned)inlen
) + 2) / 3) * 4;
1096 * linelen must be 0 mod 4 for this to work; round up...
1098 if((linelen
& 0x03) != 0) {
1099 linelen
= (linelen
+ 3) & 0xfffffffc;
1101 numLines
= (outTextLen
+ ((unsigned)linelen
) - 1)/ linelen
;
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.
1112 len
= outTextLen
+ (2 * numLines
) + 1;
1113 outbuf
= (unsigned char*)malloc(len
);
1119 for(i
=0; i
<3; i
++) {
1121 endbuf
[i
] = inbuf
[i
];
1127 encChunk(endbuf
, outp
, inlen
);
1131 encChunk(inbuf
, outp
, 3);
1138 if((linelen
!= 0) && (thisLine
>= linelen
) && inlen
) {
1140 * last trailing newline added below
1141 * Note we don't split 4-byte output chunks over newlines
1155 print_buffer_pem(FILE *stream
, const char *headerString
, size_t length
, const void *data
)
1161 fprintf(stream
, "-----BEGIN %s-----\n", headerString
);
1162 buf
= malloc_enc64_with_lines(data
, length
, 64, &bufLen
);
1163 fwrite(buf
, bufLen
, 1, stream
);
1166 fprintf(stream
, "-----END %s-----\n", headerString
);
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
);