2 * Copyright (c) 2003-2009,2012,2014 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"
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>
41 // SecTrustedApplicationValidateWithPath
42 #include <Security/SecTrustedApplicationPriv.h>
45 void check_obsolete_keychain(const char *kcName
)
50 if(!strcmp(kcName
, "/System/Library/Keychains/X509Anchors")) {
51 fprintf(stderr
, "***************************************************************\n");
52 fprintf(stderr
, " WARNING\n");
53 fprintf(stderr
, "\n");
54 fprintf(stderr
, "The keychain you are accessing, X509Anchors, is no longer\n");
55 fprintf(stderr
, "used by Mac OS X as the system root certificate store.\n");
56 fprintf(stderr
, "Please read the security man page for information on the \n");
57 fprintf(stderr
, "add-trusted-cert command. New system root certificates should\n");
58 fprintf(stderr
, "be added to the Admin Trust Settings domain and to the \n");
59 fprintf(stderr
, "System keychain in /Library/Keychains.\n");
60 fprintf(stderr
, "***************************************************************\n");
62 else if(!strcmp(kcName
, "/System/Library/Keychains/X509Certificates")) {
63 fprintf(stderr
, "***************************************************************\n");
64 fprintf(stderr
, " WARNING\n");
65 fprintf(stderr
, "\n");
66 fprintf(stderr
, "The keychain you are accessing, X509Certificates, is no longer\n");
67 fprintf(stderr
, "used by Mac OS X as the system intermediate certificate\n");
68 fprintf(stderr
, "store. New system intermediate certificates should be added\n");
69 fprintf(stderr
, "to the System keychain in /Library/Keychains.\n");
70 fprintf(stderr
, "***************************************************************\n");
75 keychain_open(const char *name
)
77 SecKeychainRef keychain
= NULL
;
80 check_obsolete_keychain(name
);
81 if (name
&& name
[0] != '/')
83 CFArrayRef dynamic
= NULL
;
84 result
= SecKeychainCopyDomainSearchList(
85 kSecPreferencesDomainDynamic
, &dynamic
);
88 sec_error("SecKeychainCopyDomainSearchList %s: %s",
89 name
, sec_errstr(result
));
95 uint32_t count
= dynamic
? CFArrayGetCount(dynamic
) : 0;
97 for (i
= 0; i
< count
; ++i
)
99 char pathName
[MAXPATHLEN
];
100 UInt32 ioPathLength
= sizeof(pathName
);
101 bzero(pathName
, ioPathLength
);
102 keychain
= (SecKeychainRef
)CFArrayGetValueAtIndex(dynamic
, i
);
103 result
= SecKeychainGetPath(keychain
, &ioPathLength
, pathName
);
106 sec_error("SecKeychainGetPath %s: %s",
107 name
, sec_errstr(result
));
110 if (!strncmp(pathName
, name
, ioPathLength
))
121 result
= SecKeychainOpen(name
, &keychain
);
124 sec_error("SecKeychainOpen %s: %s", name
, sec_errstr(result
));
131 keychain_create_array(int argc
, char * const *argv
)
136 return keychain_open(argv
[0]);
139 CFMutableArrayRef keychains
= CFArrayCreateMutable(NULL
, argc
, &kCFTypeArrayCallBacks
);
141 for (ix
= 0; ix
< argc
; ++ix
)
143 SecKeychainRef keychain
= keychain_open(argv
[ix
]);
146 CFArrayAppendValue(keychains
, keychain
);
156 parse_fourcharcode(const char *name
, UInt32
*code
)
159 int len
= (name
) ? strlen(name
) : 0;
161 // error check the name
164 fprintf(stderr
, "Error: four-character types must be exactly 4 characters long.\n");
166 fprintf(stderr
, "(Try \"%s \" instead of \"%s\")\n", name
, name
);
172 for (i
= 0; i
< 4; ++i
)
174 cc
= (cc
<< 8) | name
[i
];
177 *code
= cc
; // note: this is in host byte order, suitable for passing to APIs
183 print_keychain_name(FILE *stream
, SecKeychainRef keychain
)
186 char pathName
[MAXPATHLEN
];
187 UInt32 ioPathLength
= sizeof(pathName
);
188 OSStatus status
= SecKeychainGetPath(keychain
, &ioPathLength
, pathName
);
191 sec_perror("SecKeychainGetPath", status
);
196 print_buffer(stream
, ioPathLength
, pathName
);
203 print_keychain_version(FILE* stream
, SecKeychainRef keychain
)
207 OSStatus status
= SecKeychainGetKeychainVersion(keychain
, &version
);
209 sec_perror("SecKeychainGetKeychainVersion", status
);
214 fprintf(stream
, "%d", version
);
221 print_cfdata(FILE *stream
, CFDataRef data
)
224 return print_buffer(stream
, CFDataGetLength(data
), CFDataGetBytePtr(data
));
226 fprintf(stream
, "<NULL>");
230 print_cfstring(FILE *stream
, CFStringRef string
)
233 fprintf(stream
, "<NULL>");
236 const char *utf8
= CFStringGetCStringPtr(string
, kCFStringEncodingUTF8
);
238 fprintf(stream
, "%s", utf8
);
241 CFRange rangeToProcess
= CFRangeMake(0, CFStringGetLength(string
));
242 while (rangeToProcess
.length
> 0)
244 UInt8 localBuffer
[256];
245 CFIndex usedBufferLength
;
246 CFIndex numChars
= CFStringGetBytes(string
, rangeToProcess
,
247 kCFStringEncodingUTF8
, '?', FALSE
, localBuffer
,
248 sizeof(localBuffer
), &usedBufferLength
);
250 break; // Failed to convert anything...
252 fprintf(stream
, "%.*s", (int)usedBufferLength
, localBuffer
);
253 rangeToProcess
.location
+= numChars
;
254 rangeToProcess
.length
-= numChars
;
261 print_access(FILE *stream
, SecAccessRef access
, Boolean interactive
)
263 CFArrayRef aclList
= NULL
;
264 CFIndex aclix
, aclCount
;
268 status
= SecAccessCopyACLList(access
, &aclList
);
271 sec_perror("SecAccessCopyACLList", status
);
276 aclCount
= CFArrayGetCount(aclList
);
277 fprintf(stream
, "access: %lu entries\n", aclCount
);
278 for (aclix
= 0; aclix
< aclCount
; ++aclix
)
280 CFArrayRef applicationList
= NULL
;
281 CFStringRef description
= NULL
;
282 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector
= {};
283 CFIndex appix
, appCount
;
285 SecACLRef acl
= (SecACLRef
)CFArrayGetValueAtIndex(aclList
, aclix
);
286 CSSM_ACL_AUTHORIZATION_TAG tags
[64]; // Pick some upper limit
287 uint32 tagix
, tagCount
= sizeof(tags
) / sizeof(*tags
);
288 status
= SecACLGetAuthorizations(acl
, tags
, &tagCount
);
291 sec_perror("SecACLGetAuthorizations", status
);
296 fprintf(stream
, " entry %lu:\n authorizations (%lu):", aclix
,
297 (unsigned long)tagCount
);
298 bool printPartitionIDList
= false;
299 for (tagix
= 0; tagix
< tagCount
; ++tagix
)
301 CSSM_ACL_AUTHORIZATION_TAG tag
= tags
[tagix
];
304 case CSSM_ACL_AUTHORIZATION_ANY
:
305 fputs(" any", stream
);
307 case CSSM_ACL_AUTHORIZATION_LOGIN
:
308 fputs(" login", stream
);
310 case CSSM_ACL_AUTHORIZATION_GENKEY
:
311 fputs(" genkey", stream
);
313 case CSSM_ACL_AUTHORIZATION_DELETE
:
314 fputs(" delete", stream
);
316 case CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED
:
317 fputs(" export_wrapped", stream
);
319 case CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR
:
320 fputs(" export_clear", stream
);
322 case CSSM_ACL_AUTHORIZATION_IMPORT_WRAPPED
:
323 fputs(" import_wrapped", stream
);
325 case CSSM_ACL_AUTHORIZATION_IMPORT_CLEAR
:
326 fputs(" import_clear", stream
);
328 case CSSM_ACL_AUTHORIZATION_SIGN
:
329 fputs(" sign", stream
);
331 case CSSM_ACL_AUTHORIZATION_ENCRYPT
:
332 fputs(" encrypt", stream
);
334 case CSSM_ACL_AUTHORIZATION_DECRYPT
:
335 fputs(" decrypt", stream
);
337 case CSSM_ACL_AUTHORIZATION_MAC
:
338 fputs(" mac", stream
);
340 case CSSM_ACL_AUTHORIZATION_DERIVE
:
341 fputs(" derive", stream
);
343 case CSSM_ACL_AUTHORIZATION_DBS_CREATE
:
344 fputs(" dbs_create", stream
);
346 case CSSM_ACL_AUTHORIZATION_DBS_DELETE
:
347 fputs(" dbs_delete", stream
);
349 case CSSM_ACL_AUTHORIZATION_DB_READ
:
350 fputs(" db_read", stream
);
352 case CSSM_ACL_AUTHORIZATION_DB_INSERT
:
353 fputs(" db_insert", stream
);
355 case CSSM_ACL_AUTHORIZATION_DB_MODIFY
:
356 fputs(" db_modify", stream
);
358 case CSSM_ACL_AUTHORIZATION_DB_DELETE
:
359 fputs(" db_delete", stream
);
361 case CSSM_ACL_AUTHORIZATION_CHANGE_ACL
:
362 fputs(" change_acl", stream
);
364 case CSSM_ACL_AUTHORIZATION_CHANGE_OWNER
:
365 fputs(" change_owner", stream
);
367 case CSSM_ACL_AUTHORIZATION_INTEGRITY
:
368 fputs(" integrity", stream
);
370 case CSSM_ACL_AUTHORIZATION_PARTITION_ID
:
371 fputs(" partition_id", stream
);
372 printPartitionIDList
= true;
375 fprintf(stream
, " tag=%lu", (unsigned long)tag
);
381 status
= SecACLCopySimpleContents(acl
, &applicationList
, &description
, &promptSelector
);
384 sec_perror("SecACLCopySimpleContents", status
);
388 if (promptSelector
.flags
& CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE
)
389 fputs(" require-password\n", stream
);
391 fputs(" don't-require-password\n", stream
);
393 fputs(" description: ", stream
);
394 // special case for Partition IDs
395 if(printPartitionIDList
) {
396 print_partition_id_list(stream
, description
);
398 print_cfstring(stream
, description
);
404 appCount
= CFArrayGetCount(applicationList
);
405 fprintf(stream
, " applications (%lu):\n", appCount
);
410 fprintf(stream
, " applications: <null>\n");
413 for (appix
= 0; appix
< appCount
; ++appix
)
416 SecTrustedApplicationRef app
= (SecTrustedApplicationRef
)CFArrayGetValueAtIndex(applicationList
, appix
);
417 CFDataRef data
= NULL
;
418 fprintf(stream
, " %lu: ", appix
);
419 status
= SecTrustedApplicationCopyData(app
, &data
);
422 sec_perror("SecTrustedApplicationCopyData", status
);
426 bytes
= CFDataGetBytePtr(data
);
427 if (bytes
&& bytes
[0] == 0x2f) {
428 fprintf(stream
, "%s", (const char *)bytes
);
429 if ((status
= SecTrustedApplicationValidateWithPath(app
, (const char *)bytes
)) == noErr
) {
430 fprintf(stream
, " (OK)");
432 fprintf(stream
, " (status %d)", (int)status
);
434 fprintf(stream
, "\n");
436 print_cfdata(stream
, data
);
444 CFRelease(applicationList
);
447 CFRelease(description
);
451 char buffer
[10] = {};
452 fprintf(stderr
, "Remove this acl? ");
453 if (readline(buffer
, sizeof(buffer
)) && buffer
[0] == 'y')
455 fprintf(stderr
, "removing acl\n");
456 status
= SecACLRemove(acl
);
459 sec_perror("SecACLRemove", status
);
474 print_keychain_item_attributes(FILE *stream
, SecKeychainItemRef item
, Boolean show_data
, Boolean show_raw_data
, Boolean show_acl
, Boolean interactive
)
479 SecKeychainRef keychain
= NULL
;
480 SecAccessRef access
= NULL
;
481 SecItemClass itemClass
= 0;
483 SecKeychainAttributeList
*attrList
= NULL
;
484 SecKeychainAttributeInfo
*info
= NULL
;
488 status
= SecKeychainItemCopyKeychain(item
, &keychain
);
491 sec_perror("SecKeychainItemCopyKeychain", status
);
496 fputs("keychain: ", stream
);
497 result
= print_keychain_name(stream
, keychain
);
501 fputs("version: ", stream
);
502 result
= print_keychain_version(stream
, keychain
);
507 /* First find out the item class. */
508 status
= SecKeychainItemCopyAttributesAndData(item
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
511 sec_perror("SecKeychainItemCopyAttributesAndData", status
);
516 fputs("class: ", stream
);
518 buffer
[3] = itemClass
& 0xFF;
519 buffer
[2] = (itemClass
>> 8) & 0xFF;
520 buffer
[1] = (itemClass
>> 16) & 0xFF;
521 buffer
[0] = (itemClass
>> 24) & 0xFF;
523 print_buffer(stream
, 4, buffer
);
524 fputs("\nattributes:\n", stream
);
528 case kSecInternetPasswordItemClass
:
529 itemID
= CSSM_DL_DB_RECORD_INTERNET_PASSWORD
;
531 case kSecGenericPasswordItemClass
:
532 itemID
= CSSM_DL_DB_RECORD_GENERIC_PASSWORD
;
534 case 'ashp': /* kSecAppleSharePasswordItemClass */
535 itemID
= CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD
;
542 /* Now get the AttributeInfo for it. */
543 status
= SecKeychainAttributeInfoForItemID(keychain
, itemID
, &info
);
546 sec_perror("SecKeychainAttributeInfoForItemID", status
);
551 status
= SecKeychainItemCopyAttributesAndData(item
, info
, &itemClass
, &attrList
,
552 show_data
? &length
: NULL
,
553 show_data
? &data
: NULL
);
556 sec_perror("SecKeychainItemCopyAttributesAndData", status
);
561 if (info
->count
!= attrList
->count
)
563 sec_error("info count: %ld != attribute count: %ld", info
->count
, attrList
->count
);
568 for (ix
= 0; ix
< info
->count
; ++ix
)
570 UInt32 tag
= info
->tag
[ix
];
571 UInt32 format
= info
->format
[ix
];
572 SecKeychainAttribute
*attribute
= &attrList
->attr
[ix
];
573 if (tag
!= attribute
->tag
)
575 sec_error("attribute %d of %ld info tag: %ld != attribute tag: %ld", ix
, info
->count
, tag
, attribute
->tag
);
581 print_uint32(stream
, tag
);
584 case CSSM_DB_ATTRIBUTE_FORMAT_STRING
:
585 fputs("<string>", stream
);
587 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32
:
588 fputs("<sint32>", stream
);
590 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32
:
591 fputs("<uint32>", stream
);
593 case CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM
:
594 fputs("<bignum>", stream
);
596 case CSSM_DB_ATTRIBUTE_FORMAT_REAL
:
597 fputs("<real>", stream
);
599 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
:
600 fputs("<timedate>", stream
);
602 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB
:
603 fputs("<blob>", stream
);
605 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
:
606 fputs("<uint32>", stream
);
608 case CSSM_DB_ATTRIBUTE_FORMAT_COMPLEX
:
609 fputs("<complex>", stream
);
612 fprintf(stream
, "<format: %d>", (int)format
);
616 if (!attribute
->length
&& !attribute
->data
)
617 fputs("<NULL>", stream
);
621 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32
:
622 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32
:
624 print_uint32(stream
, *(UInt32
*) attribute
->data
);
628 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
:
630 int n
= attribute
->length
/ sizeof(UInt32
);
631 UInt32
* ptr
= (UInt32
*) attribute
->data
;
635 print_uint32(stream
, *ptr
++);
642 print_buffer(stream
, attribute
->length
, attribute
->data
);
652 fputs("data:\n", stream
);
653 print_buffer(stream
, length
, data
);
659 CSSM_DL_DB_HANDLE dldbHandle
= {};
660 const CSSM_DB_UNIQUE_RECORD
*uniqueRecordID
= NULL
;
662 status
= SecKeychainItemGetDLDBHandle(item
, &dldbHandle
);
665 sec_perror("SecKeychainItemGetDLDBHandle", status
);
670 status
= SecKeychainItemGetUniqueRecordID(item
, &uniqueRecordID
);
673 sec_perror("SecKeychainItemGetUniqueRecordID", status
);
678 status
= CSSM_DL_DataGetFromUniqueRecordId(dldbHandle
, uniqueRecordID
, NULL
, &data
);
681 sec_perror("CSSM_DL_DataGetFromUniqueRecordId", status
);
686 fputs("raw data:\n", stream
);
687 print_buffer(stream
, data
.Length
, data
.Data
);
690 /* @@@ Hmm which allocators should we use here? */
696 status
= SecKeychainItemCopyAccess(item
, &access
);
697 if (status
== errSecNoAccessForItem
)
698 fprintf(stream
, "no access control for this item\n");
703 sec_perror("SecKeychainItemCopyAccess", status
);
708 result
= print_access(stream
, access
, interactive
);
714 char buffer
[10] = {};
715 fprintf(stderr
, "Update access? ");
716 if (readline(buffer
, sizeof(buffer
)) && buffer
[0] == 'y')
718 fprintf(stderr
, "Updating access\n");
719 status
= SecKeychainItemSetAccess(item
, access
);
722 sec_perror("SecKeychainItemSetAccess", status
);
737 status
= SecKeychainItemFreeAttributesAndData(attrList
, data
);
739 sec_perror("SecKeychainItemFreeAttributesAndData", status
);
744 status
= SecKeychainFreeAttributeInfo(info
);
746 sec_perror("SecKeychainFreeAttributeInfo", status
);
756 print_buffer_hex(FILE *stream
, UInt32 length
, const void *data
)
758 uint8
*p
= (uint8
*) data
;
762 fprintf(stream
, "%02X", ch
);
767 print_buffer_ascii(FILE *stream
, UInt32 length
, const void *data
)
769 uint8
*p
= (uint8
*) data
;
773 if (ch
>= ' ' && ch
<= '~' && ch
!= '\\')
780 fputc('0' + ((ch
>> 6) & 7), stream
);
781 fputc('0' + ((ch
>> 3) & 7), stream
);
782 fputc('0' + ((ch
>> 0) & 7), stream
);
788 print_buffer(FILE *stream
, UInt32 length
, const void *data
)
790 uint8
*p
= (uint8
*) data
;
792 Boolean ascii
= FALSE
;
794 for (ix
= 0; ix
< length
; ++ix
)
797 if (ch
>= ' ' && ch
<= '~' && ch
!= '\\')
807 print_buffer_hex(stream
, length
, data
);
815 print_buffer_ascii(stream
, length
, data
);
821 print_uint32(FILE *stream
, uint32 n
)
823 n
= OSSwapHostToBigInt32 (n
);
824 print_buffer(stream
, sizeof(UInt32
), &n
);
830 static const char digits
[] = "0123456789abcdef";
832 if ((p
= strchr(digits
, tolower(c
))))
839 fromHex(const char *hexDigits
, CSSM_DATA
*data
)
841 size_t bytes
= strlen(hexDigits
) / 2; // (discards malformed odd end)
842 if (bytes
> data
->Length
)
844 // length(bytes); // (will assert if we try to grow it)
846 for (n
= 0; n
< bytes
; n
++) {
847 data
->Data
[n
] = (uint8
)(hexValue(hexDigits
[2*n
]) << 4 | hexValue(hexDigits
[2*n
+1]));
852 cfFromHex(CFStringRef hex
) {
853 // behavior is undefined if you pass in a non-hex string. Don't do that.
857 GetCStringFromCFString(hex
, &chex
, &len
);
862 size_t bytes
= len
/2;
863 CFMutableDataRef bin
= CFDataCreateMutable(kCFAllocatorDefault
, bytes
);
864 CFDataIncreaseLength(bin
, bytes
);
866 if(!bin
|| CFDataGetLength(bin
) != bytes
) {
871 UInt8
* data
= CFDataGetMutableBytePtr(bin
);
872 for(size_t i
= 0; i
< bytes
; i
++) {
873 data
[i
] = (uint8
)(hexValue(chex
[2*i
]) << 4 | hexValue(chex
[2*i
+1]));
879 CFStringRef
cfToHex(CFDataRef bin
) {
880 size_t len
= CFDataGetLength(bin
) * 2;
881 CFMutableStringRef str
= CFStringCreateMutable(NULL
, len
);
883 static const char* digits
[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
885 const uint8_t* data
= CFDataGetBytePtr(bin
);
886 for (size_t i
= 0; i
< CFDataGetLength(bin
); i
++) {
887 CFStringAppendCString(str
, digits
[data
[i
] >> 4], 1);
888 CFStringAppendCString(str
, digits
[data
[i
] & 0xf], 1);
894 safe_CFRelease(void *cfTypeRefPtr
)
896 CFTypeRef
*obj
= (CFTypeRef
*)cfTypeRefPtr
;
905 GetCStringFromCFString(CFStringRef cfstring
, char** cstr
, size_t* len
) {
906 CFIndex strLen
= CFStringGetLength(cfstring
);
907 CFIndex bufLen
= 1 + CFStringGetMaximumSizeForEncoding(strLen
, kCFStringEncodingUTF8
);
908 *cstr
= (char *)malloc(bufLen
);
909 if (!CFStringGetCString(cfstring
, *cstr
, bufLen
-1, kCFStringEncodingUTF8
)) {
912 // Handle non-8-bit characters.
913 *len
= strnlen(*cstr
, strLen
);
916 CFDictionaryRef
makeCFDictionaryFromData(CFDataRef data
)
919 CFPropertyListRef plist
= CFPropertyListCreateFromXMLData(NULL
, data
, kCFPropertyListImmutable
, NULL
);
920 if (plist
&& CFGetTypeID(plist
) != CFDictionaryGetTypeID()) {
921 safe_CFRelease(&plist
);
924 return (CFDictionaryRef
) plist
;
930 void print_partition_id_list(FILE* stream
, CFStringRef description
) {
931 CFDataRef binary
= NULL
;
932 CFDictionaryRef partitionList
= NULL
;
933 CFArrayRef partitionIDs
= NULL
;
938 binary
= cfFromHex(description
);
942 partitionList
= makeCFDictionaryFromData(binary
);
947 partitionIDs
= CFDictionaryGetValue(partitionList
, CFSTR("Partitions"));
952 for(size_t i
= 0; i
< CFArrayGetCount(partitionIDs
); i
++) {
953 CFStringRef s
= CFArrayGetValueAtIndex(partitionIDs
, i
);
959 fprintf(stream
, ", ");
961 print_cfstring(stream
, s
);
966 fprintf(stream
, "invalid partition ID: ");
967 print_cfstring(stream
, description
);
969 // don't release partitionIDs; it's an element of partitionList
970 safe_CFRelease(&binary
);
971 safe_CFRelease(&partitionList
);
977 * map a 6-bit binary value to a printable character.
980 unsigned char bintoasc
[] =
981 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
984 * map 6 bits to a printing char
986 #define ENC(c) (bintoasc[((c) & 0x3f)])
991 * map one group of up to 3 bytes at inp to 4 bytes at outp.
992 * Count is number of valid bytes in *inp; if less than 3, the
993 * 1 or two extras must be zeros.
996 encChunk(const unsigned char *inp
,
1000 unsigned char c1
, c2
, c3
, c4
;
1003 c2
= ((inp
[0] << 4) & 0x30) | ((inp
[1] >> 4) & 0xf);
1004 c3
= ((inp
[1] << 2) & 0x3c) | ((inp
[2] >> 6) & 0x3);
1022 static unsigned char *
1023 malloc_enc64_with_lines(const unsigned char *inbuf
,
1028 unsigned outTextLen
;
1029 unsigned len
; // to malloc, liberal
1030 unsigned olen
= 0; // actual output size
1031 unsigned char *outbuf
;
1032 unsigned char endbuf
[3];
1034 unsigned char *outp
;
1038 outTextLen
= ((inlen
+ 2) / 3) * 4;
1041 * linelen must be 0 mod 4 for this to work; round up...
1043 if((linelen
& 0x03) != 0) {
1044 linelen
= (linelen
+ 3) & 0xfffffffc;
1046 numLines
= (outTextLen
+ linelen
- 1)/ linelen
;
1053 * Total output size = encoded text size plus one newline per
1054 * line of output, plus trailing NULL. We always generate newlines
1055 * as \n; when decoding, we tolerate \r\n (Microsoft) or \n.
1057 len
= outTextLen
+ (2 * numLines
) + 1;
1058 outbuf
= (unsigned char*)malloc(len
);
1064 for(i
=0; i
<3; i
++) {
1066 endbuf
[i
] = inbuf
[i
];
1072 encChunk(endbuf
, outp
, inlen
);
1076 encChunk(inbuf
, outp
, 3);
1083 if((linelen
!= 0) && (thisLine
>= linelen
) && inlen
) {
1085 * last trailing newline added below
1086 * Note we don't split 4-byte output chunks over newlines
1100 print_buffer_pem(FILE *stream
, const char *headerString
, UInt32 length
, const void *data
)
1106 fprintf(stream
, "-----BEGIN %s-----\n", headerString
);
1107 buf
= malloc_enc64_with_lines(data
, length
, 64, &bufLen
);
1108 fwrite(buf
, bufLen
, 1, stream
);
1111 fprintf(stream
, "-----END %s-----\n", headerString
);
1115 prompt_password(const char* keychainName
) {
1116 const char *fmt
= "password to unlock %s: ";
1117 const char *name
= keychainName
? keychainName
: "default";
1118 char *prompt
= malloc(strlen(fmt
) + strlen(name
));
1119 sprintf(prompt
, fmt
, name
);
1120 char *password
= getpass(prompt
);