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
);
513 /* First find out the item class. */
514 status
= SecKeychainItemCopyAttributesAndData(item
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
517 sec_perror("SecKeychainItemCopyAttributesAndData", status
);
522 fputs("class: ", stream
);
524 buffer
[3] = itemClass
& 0xFF;
525 buffer
[2] = (itemClass
>> 8) & 0xFF;
526 buffer
[1] = (itemClass
>> 16) & 0xFF;
527 buffer
[0] = (itemClass
>> 24) & 0xFF;
529 print_buffer(stream
, 4, buffer
);
530 fputs("\nattributes:\n", stream
);
534 case kSecInternetPasswordItemClass
:
535 itemID
= CSSM_DL_DB_RECORD_INTERNET_PASSWORD
;
537 case kSecGenericPasswordItemClass
:
538 itemID
= CSSM_DL_DB_RECORD_GENERIC_PASSWORD
;
540 case 'ashp': /* kSecAppleSharePasswordItemClass */
541 itemID
= CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD
;
548 /* Now get the AttributeInfo for it. */
549 status
= SecKeychainAttributeInfoForItemID(keychain
, itemID
, &info
);
552 sec_perror("SecKeychainAttributeInfoForItemID", status
);
557 status
= SecKeychainItemCopyAttributesAndData(item
, info
, &itemClass
, &attrList
,
558 show_data
? &length
: NULL
,
559 show_data
? &data
: NULL
);
562 sec_perror("SecKeychainItemCopyAttributesAndData", status
);
567 if (info
->count
!= attrList
->count
)
569 sec_error("info count: %ld != attribute count: %ld", info
->count
, attrList
->count
);
574 for (ix
= 0; ix
< info
->count
; ++ix
)
576 UInt32 tag
= info
->tag
[ix
];
577 UInt32 format
= info
->format
[ix
];
578 SecKeychainAttribute
*attribute
= &attrList
->attr
[ix
];
579 if (tag
!= attribute
->tag
)
581 sec_error("attribute %d of %ld info tag: %ld != attribute tag: %ld", ix
, info
->count
, tag
, attribute
->tag
);
587 print_uint32(stream
, tag
);
590 case CSSM_DB_ATTRIBUTE_FORMAT_STRING
:
591 fputs("<string>", stream
);
593 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32
:
594 fputs("<sint32>", stream
);
596 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32
:
597 fputs("<uint32>", stream
);
599 case CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM
:
600 fputs("<bignum>", stream
);
602 case CSSM_DB_ATTRIBUTE_FORMAT_REAL
:
603 fputs("<real>", stream
);
605 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
:
606 fputs("<timedate>", stream
);
608 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB
:
609 fputs("<blob>", stream
);
611 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
:
612 fputs("<uint32>", stream
);
614 case CSSM_DB_ATTRIBUTE_FORMAT_COMPLEX
:
615 fputs("<complex>", stream
);
618 fprintf(stream
, "<format: %d>", (int)format
);
622 if (!attribute
->length
&& !attribute
->data
)
623 fputs("<NULL>", stream
);
627 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32
:
628 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32
:
630 print_uint32(stream
, *(UInt32
*) attribute
->data
);
634 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
:
636 int n
= attribute
->length
/ sizeof(UInt32
);
637 UInt32
* ptr
= (UInt32
*) attribute
->data
;
641 print_uint32(stream
, *ptr
++);
648 print_buffer(stream
, attribute
->length
, attribute
->data
);
658 fputs("data:\n", stream
);
659 print_buffer(stream
, length
, data
);
665 CSSM_DL_DB_HANDLE dldbHandle
= {};
666 const CSSM_DB_UNIQUE_RECORD
*uniqueRecordID
= NULL
;
668 status
= SecKeychainItemGetDLDBHandle(item
, &dldbHandle
);
671 sec_perror("SecKeychainItemGetDLDBHandle", status
);
676 status
= SecKeychainItemGetUniqueRecordID(item
, &uniqueRecordID
);
679 sec_perror("SecKeychainItemGetUniqueRecordID", status
);
684 status
= CSSM_DL_DataGetFromUniqueRecordId(dldbHandle
, uniqueRecordID
, NULL
, &data
);
687 sec_perror("CSSM_DL_DataGetFromUniqueRecordId", status
);
692 fputs("raw data:\n", stream
);
693 print_buffer(stream
, data
.Length
, data
.Data
);
696 /* @@@ Hmm which allocators should we use here? */
702 status
= SecKeychainItemCopyAccess(item
, &access
);
703 if (status
== errSecNoAccessForItem
)
704 fprintf(stream
, "no access control for this item\n");
709 sec_perror("SecKeychainItemCopyAccess", status
);
714 result
= print_access(stream
, access
, interactive
);
720 char buffer
[10] = {};
721 fprintf(stderr
, "Update access? ");
722 if (readline(buffer
, sizeof(buffer
)) && buffer
[0] == 'y')
724 fprintf(stderr
, "Updating access\n");
725 status
= SecKeychainItemSetAccess(item
, access
);
728 sec_perror("SecKeychainItemSetAccess", status
);
743 status
= SecKeychainItemFreeAttributesAndData(attrList
, data
);
745 sec_perror("SecKeychainItemFreeAttributesAndData", status
);
750 status
= SecKeychainFreeAttributeInfo(info
);
752 sec_perror("SecKeychainFreeAttributeInfo", status
);
762 print_buffer_hex(FILE *stream
, size_t length
, const void *data
)
764 uint8
*p
= (uint8
*) data
;
768 fprintf(stream
, "%02X", ch
);
773 print_buffer_ascii(FILE *stream
, size_t length
, const void *data
)
775 uint8
*p
= (uint8
*) data
;
779 if (ch
>= ' ' && ch
<= '~' && ch
!= '\\')
786 fputc('0' + ((ch
>> 6) & 7), stream
);
787 fputc('0' + ((ch
>> 3) & 7), stream
);
788 fputc('0' + ((ch
>> 0) & 7), stream
);
794 print_buffer(FILE *stream
, size_t length
, const void *data
)
796 uint8
*p
= (uint8
*) data
;
798 Boolean ascii
= FALSE
;
800 for (ix
= 0; ix
< length
; ++ix
)
803 if (ch
>= ' ' && ch
<= '~' && ch
!= '\\')
813 print_buffer_hex(stream
, length
, data
);
821 print_buffer_ascii(stream
, length
, data
);
827 print_uint32(FILE *stream
, uint32 n
)
829 n
= OSSwapHostToBigInt32 (n
);
830 print_buffer(stream
, sizeof(UInt32
), &n
);
833 // Returns base16 value of input hexadecimal character.
834 // If the input character is not valid hex, output error flag is set.
837 hexToValue(char c
, char *error
)
839 static const char digits
[] = "0123456789abcdef";
840 char *p
= strchr(digits
, tolower(c
));
841 if (p
) { return p
- digits
; }
842 if (error
) { *error
= 1; }
849 return hexToValue(c
, NULL
);
852 // Returns true if we can convert the supplied hex string to data,
853 // with pointer to the allocated data and its length as optional output.
854 // This function will zero-pad an odd number of nibbles in hexString,
855 // e.g. an input of 'FFF' is treated as 0x0FFF.
856 // If outData is supplied, caller must free returned value.
859 convertHex(const char *hexString
, uint8_t **outData
, size_t *outLength
)
861 if (!hexString
) { return false; }
863 size_t num_nibbles
= strlen(hexString
);
864 uint8_t zero_pad
= ((num_nibbles
% 2) != 0) ? 1 : 0;
865 size_t num_bytes
= (num_nibbles
/ 2) + zero_pad
;
866 uint8_t *data
= calloc(num_bytes
, sizeof(*data
));
869 data
[0] = hexToValue(hexString
[0], &error
);
871 for (size_t n
= zero_pad
; n
< num_bytes
; n
++) {
872 data
[n
] = hexToValue(hexString
[2*n
], &error
) << 4 | hexToValue(hexString
[2*n
+1], &error
);
881 memset(data
, 0, num_bytes
);
885 *outLength
= num_bytes
;
891 fromHex(const char *hexDigits
, CSSM_DATA
*data
)
893 size_t bytes
= strlen(hexDigits
) / 2; // (discards malformed odd end)
894 if (bytes
> data
->Length
)
896 // length(bytes); // (will assert if we try to grow it)
898 for (n
= 0; n
< bytes
; n
++) {
899 data
->Data
[n
] = (uint8
)(hexValue(hexDigits
[2*n
]) << 4 | hexValue(hexDigits
[2*n
+1]));
903 CFDataRef CF_RETURNS_RETAINED
904 cfFromHex(CFStringRef hex
) {
905 // behavior is undefined if you pass in a non-hex string. Don't do that.
909 GetCStringFromCFString(hex
, &chex
, &len
);
914 size_t bytes
= len
/2;
915 CFMutableDataRef bin
= CFDataCreateMutable(kCFAllocatorDefault
, bytes
);
916 CFDataIncreaseLength(bin
, bytes
);
918 if(!bin
|| (size_t) CFDataGetLength(bin
) != bytes
) {
923 UInt8
* data
= CFDataGetMutableBytePtr(bin
);
924 for(size_t i
= 0; i
< bytes
; i
++) {
925 data
[i
] = (uint8
)(hexValue(chex
[2*i
]) << 4 | hexValue(chex
[2*i
+1]));
931 CFStringRef CF_RETURNS_RETAINED
cfToHex(CFDataRef bin
) {
932 size_t len
= CFDataGetLength(bin
) * 2;
933 CFMutableStringRef str
= CFStringCreateMutable(NULL
, len
);
935 static const char* digits
[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
937 const uint8_t* data
= CFDataGetBytePtr(bin
);
938 for (CFIndex i
= 0; i
< CFDataGetLength(bin
); i
++) {
939 CFStringAppendCString(str
, digits
[data
[i
] >> 4], 1);
940 CFStringAppendCString(str
, digits
[data
[i
] & 0xf], 1);
946 safe_CFRelease(void *cfTypeRefPtr
)
948 CFTypeRef
*obj
= (CFTypeRef
*)cfTypeRefPtr
;
957 GetCStringFromCFString(CFStringRef cfstring
, char** cstr
, size_t* len
) {
958 CFIndex strLen
= CFStringGetLength(cfstring
);
959 CFIndex bufLen
= 1 + CFStringGetMaximumSizeForEncoding(strLen
, kCFStringEncodingUTF8
);
960 *cstr
= (char *)malloc(bufLen
);
961 if (!CFStringGetCString(cfstring
, *cstr
, bufLen
-1, kCFStringEncodingUTF8
)) {
964 // Handle non-8-bit characters.
965 *len
= strnlen(*cstr
, strLen
);
968 CFDictionaryRef CF_RETURNS_RETAINED
makeCFDictionaryFromData(CFDataRef data
)
971 CFPropertyListRef plist
= CFPropertyListCreateFromXMLData(NULL
, data
, kCFPropertyListImmutable
, NULL
);
972 if (plist
&& CFGetTypeID(plist
) != CFDictionaryGetTypeID()) {
973 safe_CFRelease(&plist
);
976 return (CFDictionaryRef
) plist
;
982 void print_partition_id_list(FILE* stream
, CFStringRef description
) {
983 CFDataRef binary
= NULL
;
984 CFDictionaryRef partitionList
= NULL
;
985 CFArrayRef partitionIDs
= NULL
;
990 binary
= cfFromHex(description
);
994 partitionList
= makeCFDictionaryFromData(binary
);
999 partitionIDs
= CFDictionaryGetValue(partitionList
, CFSTR("Partitions"));
1004 for(CFIndex i
= 0; i
< CFArrayGetCount(partitionIDs
); i
++) {
1005 CFStringRef s
= CFArrayGetValueAtIndex(partitionIDs
, i
);
1011 fprintf(stream
, ", ");
1013 print_cfstring(stream
, s
);
1018 fprintf(stream
, "invalid partition ID: ");
1019 print_cfstring(stream
, description
);
1021 // don't release partitionIDs; it's an element of partitionList
1022 safe_CFRelease(&binary
);
1023 safe_CFRelease(&partitionList
);
1029 * map a 6-bit binary value to a printable character.
1032 unsigned char bintoasc
[] =
1033 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1036 * map 6 bits to a printing char
1038 #define ENC(c) (bintoasc[((c) & 0x3f)])
1043 * map one group of up to 3 bytes at inp to 4 bytes at outp.
1044 * Count is number of valid bytes in *inp; if less than 3, the
1045 * 1 or two extras must be zeros.
1048 encChunk(const unsigned char *inp
,
1049 unsigned char *outp
,
1052 unsigned char c1
, c2
, c3
, c4
;
1055 c2
= ((inp
[0] << 4) & 0x30) | ((inp
[1] >> 4) & 0xf);
1056 c3
= ((inp
[1] << 2) & 0x3c) | ((inp
[2] >> 6) & 0x3);
1074 static unsigned char *
1075 malloc_enc64_with_lines(const unsigned char *inbuf
,
1080 unsigned outTextLen
;
1081 unsigned len
; // to malloc, liberal
1082 unsigned olen
= 0; // actual output size
1083 unsigned char *outbuf
;
1084 unsigned char endbuf
[3];
1086 unsigned char *outp
;
1090 outTextLen
= ((((unsigned)inlen
) + 2) / 3) * 4;
1093 * linelen must be 0 mod 4 for this to work; round up...
1095 if((linelen
& 0x03) != 0) {
1096 linelen
= (linelen
+ 3) & 0xfffffffc;
1098 numLines
= (outTextLen
+ ((unsigned)linelen
) - 1)/ linelen
;
1105 * Total output size = encoded text size plus one newline per
1106 * line of output, plus trailing NULL. We always generate newlines
1107 * as \n; when decoding, we tolerate \r\n (Microsoft) or \n.
1109 len
= outTextLen
+ (2 * numLines
) + 1;
1110 outbuf
= (unsigned char*)malloc(len
);
1116 for(i
=0; i
<3; i
++) {
1118 endbuf
[i
] = inbuf
[i
];
1124 encChunk(endbuf
, outp
, inlen
);
1128 encChunk(inbuf
, outp
, 3);
1135 if((linelen
!= 0) && (thisLine
>= linelen
) && inlen
) {
1137 * last trailing newline added below
1138 * Note we don't split 4-byte output chunks over newlines
1152 print_buffer_pem(FILE *stream
, const char *headerString
, size_t length
, const void *data
)
1158 fprintf(stream
, "-----BEGIN %s-----\n", headerString
);
1159 buf
= malloc_enc64_with_lines(data
, length
, 64, &bufLen
);
1160 fwrite(buf
, bufLen
, 1, stream
);
1163 fprintf(stream
, "-----END %s-----\n", headerString
);
1167 prompt_password(const char* keychainName
) {
1168 const char *fmt
= "password to unlock %s: ";
1169 const char *name
= keychainName
? keychainName
: "default";
1170 char *prompt
= malloc(strlen(fmt
) + strlen(name
));
1171 sprintf(prompt
, fmt
, name
);
1172 char *password
= getpass(prompt
);