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"
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>
39 #include "readline_cssm.h"
41 // SecTrustedApplicationValidateWithPath
42 #include <Security/SecTrustedApplicationPriv.h>
43 #include <Security/SecKeychainPriv.h>
46 void check_obsolete_keychain(const char *kcName
)
51 if(!strcmp(kcName
, "/System/Library/Keychains/X509Anchors")) {
52 fprintf(stderr
, "***************************************************************\n");
53 fprintf(stderr
, " WARNING\n");
54 fprintf(stderr
, "\n");
55 fprintf(stderr
, "The keychain you are accessing, X509Anchors, is no longer\n");
56 fprintf(stderr
, "used by Mac OS X as the system root certificate store.\n");
57 fprintf(stderr
, "Please read the security man page for information on the \n");
58 fprintf(stderr
, "add-trusted-cert command. New system root certificates should\n");
59 fprintf(stderr
, "be added to the Admin Trust Settings domain and to the \n");
60 fprintf(stderr
, "System keychain in /Library/Keychains.\n");
61 fprintf(stderr
, "***************************************************************\n");
63 else if(!strcmp(kcName
, "/System/Library/Keychains/X509Certificates")) {
64 fprintf(stderr
, "***************************************************************\n");
65 fprintf(stderr
, " WARNING\n");
66 fprintf(stderr
, "\n");
67 fprintf(stderr
, "The keychain you are accessing, X509Certificates, is no longer\n");
68 fprintf(stderr
, "used by Mac OS X as the system intermediate certificate\n");
69 fprintf(stderr
, "store. New system intermediate certificates should be added\n");
70 fprintf(stderr
, "to the System keychain in /Library/Keychains.\n");
71 fprintf(stderr
, "***************************************************************\n");
76 keychain_open(const char *name
)
78 SecKeychainRef keychain
= NULL
;
81 check_obsolete_keychain(name
);
82 if (name
&& name
[0] != '/')
84 CFArrayRef dynamic
= NULL
;
85 result
= SecKeychainCopyDomainSearchList(
86 kSecPreferencesDomainDynamic
, &dynamic
);
89 sec_error("SecKeychainCopyDomainSearchList %s: %s",
90 name
, sec_errstr(result
));
96 CFIndex count
= dynamic
? CFArrayGetCount(dynamic
) : 0;
98 for (i
= 0; i
< count
; ++i
)
100 char pathName
[MAXPATHLEN
];
101 UInt32 ioPathLength
= sizeof(pathName
);
102 bzero(pathName
, ioPathLength
);
103 keychain
= (SecKeychainRef
)CFArrayGetValueAtIndex(dynamic
, i
);
104 result
= SecKeychainGetPath(keychain
, &ioPathLength
, pathName
);
107 sec_error("SecKeychainGetPath %s: %s",
108 name
, sec_errstr(result
));
111 if (!strncmp(pathName
, name
, ioPathLength
))
122 result
= SecKeychainOpen(name
, &keychain
);
125 sec_error("SecKeychainOpen %s: %s", name
, sec_errstr(result
));
132 keychain_create_array(int argc
, char * const *argv
)
137 return keychain_open(argv
[0]);
140 CFMutableArrayRef keychains
= CFArrayCreateMutable(NULL
, argc
, &kCFTypeArrayCallBacks
);
142 for (ix
= 0; ix
< argc
; ++ix
)
144 SecKeychainRef keychain
= keychain_open(argv
[ix
]);
147 CFArrayAppendValue(keychains
, keychain
);
157 parse_fourcharcode(const char *name
, UInt32
*code
)
160 size_t len
= (name
) ? strlen(name
) : 0;
162 // error check the name
165 fprintf(stderr
, "Error: four-character types must be exactly 4 characters long.\n");
167 fprintf(stderr
, "(Try \"%s \" instead of \"%s\")\n", name
, name
);
173 for (i
= 0; i
< 4; ++i
)
175 cc
= (cc
<< 8) | name
[i
];
178 *code
= cc
; // note: this is in host byte order, suitable for passing to APIs
184 print_keychain_name(FILE *stream
, SecKeychainRef keychain
)
187 char pathName
[MAXPATHLEN
];
188 UInt32 ioPathLength
= sizeof(pathName
);
189 OSStatus status
= SecKeychainGetPath(keychain
, &ioPathLength
, pathName
);
192 sec_perror("SecKeychainGetPath", status
);
197 print_buffer(stream
, ioPathLength
, pathName
);
204 print_keychain_version(FILE* stream
, SecKeychainRef keychain
)
208 OSStatus status
= SecKeychainGetKeychainVersion(keychain
, &version
);
210 sec_perror("SecKeychainGetKeychainVersion", status
);
215 fprintf(stream
, "%d", (uint32_t) version
);
222 print_cfdata(FILE *stream
, CFDataRef data
)
225 return print_buffer(stream
, CFDataGetLength(data
), CFDataGetBytePtr(data
));
227 fprintf(stream
, "<NULL>");
231 print_cfstring(FILE *stream
, CFStringRef string
)
234 fprintf(stream
, "<NULL>");
237 const char *utf8
= CFStringGetCStringPtr(string
, kCFStringEncodingUTF8
);
239 fprintf(stream
, "%s", utf8
);
242 CFRange rangeToProcess
= CFRangeMake(0, CFStringGetLength(string
));
243 while (rangeToProcess
.length
> 0)
245 UInt8 localBuffer
[256];
246 CFIndex usedBufferLength
;
247 CFIndex numChars
= CFStringGetBytes(string
, rangeToProcess
,
248 kCFStringEncodingUTF8
, '?', FALSE
, localBuffer
,
249 sizeof(localBuffer
), &usedBufferLength
);
251 break; // Failed to convert anything...
253 fprintf(stream
, "%.*s", (int)usedBufferLength
, localBuffer
);
254 rangeToProcess
.location
+= numChars
;
255 rangeToProcess
.length
-= numChars
;
262 print_access(FILE *stream
, SecAccessRef access
, Boolean interactive
)
264 CFArrayRef aclList
= NULL
;
265 CFIndex aclix
, aclCount
;
269 status
= SecAccessCopyACLList(access
, &aclList
);
272 sec_perror("SecAccessCopyACLList", status
);
277 aclCount
= CFArrayGetCount(aclList
);
278 fprintf(stream
, "access: %lu entries\n", aclCount
);
279 for (aclix
= 0; aclix
< aclCount
; ++aclix
)
281 CFArrayRef applicationList
= NULL
;
282 CFStringRef description
= NULL
;
283 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector
= {};
284 CFIndex appix
, appCount
;
286 SecACLRef acl
= (SecACLRef
)CFArrayGetValueAtIndex(aclList
, aclix
);
287 CSSM_ACL_AUTHORIZATION_TAG tags
[64]; // Pick some upper limit
288 uint32 tagix
, tagCount
= sizeof(tags
) / sizeof(*tags
);
289 status
= SecACLGetAuthorizations(acl
, tags
, &tagCount
);
292 sec_perror("SecACLGetAuthorizations", status
);
297 fprintf(stream
, " entry %lu:\n authorizations (%lu):", aclix
,
298 (unsigned long)tagCount
);
299 bool printPartitionIDList
= false;
300 for (tagix
= 0; tagix
< tagCount
; ++tagix
)
302 CSSM_ACL_AUTHORIZATION_TAG tag
= tags
[tagix
];
305 case CSSM_ACL_AUTHORIZATION_ANY
:
306 fputs(" any", stream
);
308 case CSSM_ACL_AUTHORIZATION_LOGIN
:
309 fputs(" login", stream
);
311 case CSSM_ACL_AUTHORIZATION_GENKEY
:
312 fputs(" genkey", stream
);
314 case CSSM_ACL_AUTHORIZATION_DELETE
:
315 fputs(" delete", stream
);
317 case CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED
:
318 fputs(" export_wrapped", stream
);
320 case CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR
:
321 fputs(" export_clear", stream
);
323 case CSSM_ACL_AUTHORIZATION_IMPORT_WRAPPED
:
324 fputs(" import_wrapped", stream
);
326 case CSSM_ACL_AUTHORIZATION_IMPORT_CLEAR
:
327 fputs(" import_clear", stream
);
329 case CSSM_ACL_AUTHORIZATION_SIGN
:
330 fputs(" sign", stream
);
332 case CSSM_ACL_AUTHORIZATION_ENCRYPT
:
333 fputs(" encrypt", stream
);
335 case CSSM_ACL_AUTHORIZATION_DECRYPT
:
336 fputs(" decrypt", stream
);
338 case CSSM_ACL_AUTHORIZATION_MAC
:
339 fputs(" mac", stream
);
341 case CSSM_ACL_AUTHORIZATION_DERIVE
:
342 fputs(" derive", stream
);
344 case CSSM_ACL_AUTHORIZATION_DBS_CREATE
:
345 fputs(" dbs_create", stream
);
347 case CSSM_ACL_AUTHORIZATION_DBS_DELETE
:
348 fputs(" dbs_delete", stream
);
350 case CSSM_ACL_AUTHORIZATION_DB_READ
:
351 fputs(" db_read", stream
);
353 case CSSM_ACL_AUTHORIZATION_DB_INSERT
:
354 fputs(" db_insert", stream
);
356 case CSSM_ACL_AUTHORIZATION_DB_MODIFY
:
357 fputs(" db_modify", stream
);
359 case CSSM_ACL_AUTHORIZATION_DB_DELETE
:
360 fputs(" db_delete", stream
);
362 case CSSM_ACL_AUTHORIZATION_CHANGE_ACL
:
363 fputs(" change_acl", stream
);
365 case CSSM_ACL_AUTHORIZATION_CHANGE_OWNER
:
366 fputs(" change_owner", stream
);
368 case CSSM_ACL_AUTHORIZATION_INTEGRITY
:
369 fputs(" integrity", stream
);
371 case CSSM_ACL_AUTHORIZATION_PARTITION_ID
:
372 fputs(" partition_id", stream
);
373 printPartitionIDList
= true;
376 fprintf(stream
, " tag=%lu", (unsigned long)tag
);
382 status
= SecACLCopySimpleContents(acl
, &applicationList
, &description
, &promptSelector
);
385 sec_perror("SecACLCopySimpleContents", status
);
389 if (promptSelector
.flags
& CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE
)
390 fputs(" require-password\n", stream
);
392 fputs(" don't-require-password\n", stream
);
394 fputs(" description: ", stream
);
395 // special case for Partition IDs
396 if(printPartitionIDList
) {
397 print_partition_id_list(stream
, description
);
399 print_cfstring(stream
, description
);
405 appCount
= CFArrayGetCount(applicationList
);
406 fprintf(stream
, " applications (%lu):\n", appCount
);
411 fprintf(stream
, " applications: <null>\n");
414 for (appix
= 0; appix
< appCount
; ++appix
)
417 SecTrustedApplicationRef app
= (SecTrustedApplicationRef
)CFArrayGetValueAtIndex(applicationList
, appix
);
418 CFDataRef data
= NULL
;
419 fprintf(stream
, " %lu: ", appix
);
420 status
= SecTrustedApplicationCopyData(app
, &data
);
423 sec_perror("SecTrustedApplicationCopyData", status
);
427 bytes
= CFDataGetBytePtr(data
);
428 if (bytes
&& bytes
[0] == 0x2f) {
429 fprintf(stream
, "%s", (const char *)bytes
);
430 if ((status
= SecTrustedApplicationValidateWithPath(app
, (const char *)bytes
)) == noErr
) {
431 fprintf(stream
, " (OK)");
433 fprintf(stream
, " (status %d)", (int)status
);
435 fprintf(stream
, "\n");
437 print_cfdata(stream
, data
);
445 CFRelease(applicationList
);
448 CFRelease(description
);
452 char buffer
[10] = {};
453 fprintf(stderr
, "Remove this acl? ");
454 if (readline(buffer
, sizeof(buffer
)) && buffer
[0] == 'y')
456 fprintf(stderr
, "removing acl\n");
457 status
= SecACLRemove(acl
);
460 sec_perror("SecACLRemove", status
);
475 print_keychain_item_attributes(FILE *stream
, SecKeychainItemRef item
, Boolean show_data
, Boolean show_raw_data
, Boolean show_acl
, Boolean interactive
)
480 SecKeychainRef keychain
= NULL
;
481 SecAccessRef access
= NULL
;
482 SecItemClass itemClass
= 0;
484 SecKeychainAttributeList
*attrList
= NULL
;
485 SecKeychainAttributeInfo
*info
= NULL
;
489 status
= SecKeychainItemCopyKeychain(item
, &keychain
);
492 sec_perror("SecKeychainItemCopyKeychain", status
);
497 fputs("keychain: ", stream
);
498 result
= print_keychain_name(stream
, keychain
);
502 fputs("version: ", stream
);
503 result
= print_keychain_version(stream
, keychain
);
508 /* First find out the item class. */
509 status
= SecKeychainItemCopyAttributesAndData(item
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
512 sec_perror("SecKeychainItemCopyAttributesAndData", status
);
517 fputs("class: ", stream
);
519 buffer
[3] = itemClass
& 0xFF;
520 buffer
[2] = (itemClass
>> 8) & 0xFF;
521 buffer
[1] = (itemClass
>> 16) & 0xFF;
522 buffer
[0] = (itemClass
>> 24) & 0xFF;
524 print_buffer(stream
, 4, buffer
);
525 fputs("\nattributes:\n", stream
);
529 case kSecInternetPasswordItemClass
:
530 itemID
= CSSM_DL_DB_RECORD_INTERNET_PASSWORD
;
532 case kSecGenericPasswordItemClass
:
533 itemID
= CSSM_DL_DB_RECORD_GENERIC_PASSWORD
;
535 case 'ashp': /* kSecAppleSharePasswordItemClass */
536 itemID
= CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD
;
543 /* Now get the AttributeInfo for it. */
544 status
= SecKeychainAttributeInfoForItemID(keychain
, itemID
, &info
);
547 sec_perror("SecKeychainAttributeInfoForItemID", status
);
552 status
= SecKeychainItemCopyAttributesAndData(item
, info
, &itemClass
, &attrList
,
553 show_data
? &length
: NULL
,
554 show_data
? &data
: NULL
);
557 sec_perror("SecKeychainItemCopyAttributesAndData", status
);
562 if (info
->count
!= attrList
->count
)
564 sec_error("info count: %ld != attribute count: %ld", info
->count
, attrList
->count
);
569 for (ix
= 0; ix
< info
->count
; ++ix
)
571 UInt32 tag
= info
->tag
[ix
];
572 UInt32 format
= info
->format
[ix
];
573 SecKeychainAttribute
*attribute
= &attrList
->attr
[ix
];
574 if (tag
!= attribute
->tag
)
576 sec_error("attribute %d of %ld info tag: %ld != attribute tag: %ld", ix
, info
->count
, tag
, attribute
->tag
);
582 print_uint32(stream
, tag
);
585 case CSSM_DB_ATTRIBUTE_FORMAT_STRING
:
586 fputs("<string>", stream
);
588 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32
:
589 fputs("<sint32>", stream
);
591 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32
:
592 fputs("<uint32>", stream
);
594 case CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM
:
595 fputs("<bignum>", stream
);
597 case CSSM_DB_ATTRIBUTE_FORMAT_REAL
:
598 fputs("<real>", stream
);
600 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
:
601 fputs("<timedate>", stream
);
603 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB
:
604 fputs("<blob>", stream
);
606 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
:
607 fputs("<uint32>", stream
);
609 case CSSM_DB_ATTRIBUTE_FORMAT_COMPLEX
:
610 fputs("<complex>", stream
);
613 fprintf(stream
, "<format: %d>", (int)format
);
617 if (!attribute
->length
&& !attribute
->data
)
618 fputs("<NULL>", stream
);
622 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32
:
623 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32
:
625 print_uint32(stream
, *(UInt32
*) attribute
->data
);
629 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
:
631 int n
= attribute
->length
/ sizeof(UInt32
);
632 UInt32
* ptr
= (UInt32
*) attribute
->data
;
636 print_uint32(stream
, *ptr
++);
643 print_buffer(stream
, attribute
->length
, attribute
->data
);
653 fputs("data:\n", stream
);
654 print_buffer(stream
, length
, data
);
660 CSSM_DL_DB_HANDLE dldbHandle
= {};
661 const CSSM_DB_UNIQUE_RECORD
*uniqueRecordID
= NULL
;
663 status
= SecKeychainItemGetDLDBHandle(item
, &dldbHandle
);
666 sec_perror("SecKeychainItemGetDLDBHandle", status
);
671 status
= SecKeychainItemGetUniqueRecordID(item
, &uniqueRecordID
);
674 sec_perror("SecKeychainItemGetUniqueRecordID", status
);
679 status
= CSSM_DL_DataGetFromUniqueRecordId(dldbHandle
, uniqueRecordID
, NULL
, &data
);
682 sec_perror("CSSM_DL_DataGetFromUniqueRecordId", status
);
687 fputs("raw data:\n", stream
);
688 print_buffer(stream
, data
.Length
, data
.Data
);
691 /* @@@ Hmm which allocators should we use here? */
697 status
= SecKeychainItemCopyAccess(item
, &access
);
698 if (status
== errSecNoAccessForItem
)
699 fprintf(stream
, "no access control for this item\n");
704 sec_perror("SecKeychainItemCopyAccess", status
);
709 result
= print_access(stream
, access
, interactive
);
715 char buffer
[10] = {};
716 fprintf(stderr
, "Update access? ");
717 if (readline(buffer
, sizeof(buffer
)) && buffer
[0] == 'y')
719 fprintf(stderr
, "Updating access\n");
720 status
= SecKeychainItemSetAccess(item
, access
);
723 sec_perror("SecKeychainItemSetAccess", status
);
738 status
= SecKeychainItemFreeAttributesAndData(attrList
, data
);
740 sec_perror("SecKeychainItemFreeAttributesAndData", status
);
745 status
= SecKeychainFreeAttributeInfo(info
);
747 sec_perror("SecKeychainFreeAttributeInfo", status
);
757 print_buffer_hex(FILE *stream
, size_t length
, const void *data
)
759 uint8
*p
= (uint8
*) data
;
763 fprintf(stream
, "%02X", ch
);
768 print_buffer_ascii(FILE *stream
, size_t length
, const void *data
)
770 uint8
*p
= (uint8
*) data
;
774 if (ch
>= ' ' && ch
<= '~' && ch
!= '\\')
781 fputc('0' + ((ch
>> 6) & 7), stream
);
782 fputc('0' + ((ch
>> 3) & 7), stream
);
783 fputc('0' + ((ch
>> 0) & 7), stream
);
789 print_buffer(FILE *stream
, size_t length
, const void *data
)
791 uint8
*p
= (uint8
*) data
;
793 Boolean ascii
= FALSE
;
795 for (ix
= 0; ix
< length
; ++ix
)
798 if (ch
>= ' ' && ch
<= '~' && ch
!= '\\')
808 print_buffer_hex(stream
, length
, data
);
816 print_buffer_ascii(stream
, length
, data
);
822 print_uint32(FILE *stream
, uint32 n
)
824 n
= OSSwapHostToBigInt32 (n
);
825 print_buffer(stream
, sizeof(UInt32
), &n
);
831 static const char digits
[] = "0123456789abcdef";
833 if ((p
= strchr(digits
, tolower(c
))))
840 fromHex(const char *hexDigits
, CSSM_DATA
*data
)
842 size_t bytes
= strlen(hexDigits
) / 2; // (discards malformed odd end)
843 if (bytes
> data
->Length
)
845 // length(bytes); // (will assert if we try to grow it)
847 for (n
= 0; n
< bytes
; n
++) {
848 data
->Data
[n
] = (uint8
)(hexValue(hexDigits
[2*n
]) << 4 | hexValue(hexDigits
[2*n
+1]));
853 cfFromHex(CFStringRef hex
) {
854 // behavior is undefined if you pass in a non-hex string. Don't do that.
858 GetCStringFromCFString(hex
, &chex
, &len
);
863 size_t bytes
= len
/2;
864 CFMutableDataRef bin
= CFDataCreateMutable(kCFAllocatorDefault
, bytes
);
865 CFDataIncreaseLength(bin
, bytes
);
867 if(!bin
|| (size_t) CFDataGetLength(bin
) != bytes
) {
872 UInt8
* data
= CFDataGetMutableBytePtr(bin
);
873 for(size_t i
= 0; i
< bytes
; i
++) {
874 data
[i
] = (uint8
)(hexValue(chex
[2*i
]) << 4 | hexValue(chex
[2*i
+1]));
880 CFStringRef
cfToHex(CFDataRef bin
) {
881 size_t len
= CFDataGetLength(bin
) * 2;
882 CFMutableStringRef str
= CFStringCreateMutable(NULL
, len
);
884 static const char* digits
[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
886 const uint8_t* data
= CFDataGetBytePtr(bin
);
887 for (CFIndex i
= 0; i
< CFDataGetLength(bin
); i
++) {
888 CFStringAppendCString(str
, digits
[data
[i
] >> 4], 1);
889 CFStringAppendCString(str
, digits
[data
[i
] & 0xf], 1);
895 safe_CFRelease(void *cfTypeRefPtr
)
897 CFTypeRef
*obj
= (CFTypeRef
*)cfTypeRefPtr
;
906 GetCStringFromCFString(CFStringRef cfstring
, char** cstr
, size_t* len
) {
907 CFIndex strLen
= CFStringGetLength(cfstring
);
908 CFIndex bufLen
= 1 + CFStringGetMaximumSizeForEncoding(strLen
, kCFStringEncodingUTF8
);
909 *cstr
= (char *)malloc(bufLen
);
910 if (!CFStringGetCString(cfstring
, *cstr
, bufLen
-1, kCFStringEncodingUTF8
)) {
913 // Handle non-8-bit characters.
914 *len
= strnlen(*cstr
, strLen
);
917 CFDictionaryRef
makeCFDictionaryFromData(CFDataRef data
)
920 CFPropertyListRef plist
= CFPropertyListCreateFromXMLData(NULL
, data
, kCFPropertyListImmutable
, NULL
);
921 if (plist
&& CFGetTypeID(plist
) != CFDictionaryGetTypeID()) {
922 safe_CFRelease(&plist
);
925 return (CFDictionaryRef
) plist
;
931 void print_partition_id_list(FILE* stream
, CFStringRef description
) {
932 CFDataRef binary
= NULL
;
933 CFDictionaryRef partitionList
= NULL
;
934 CFArrayRef partitionIDs
= NULL
;
939 binary
= cfFromHex(description
);
943 partitionList
= makeCFDictionaryFromData(binary
);
948 partitionIDs
= CFDictionaryGetValue(partitionList
, CFSTR("Partitions"));
953 for(CFIndex i
= 0; i
< CFArrayGetCount(partitionIDs
); i
++) {
954 CFStringRef s
= CFArrayGetValueAtIndex(partitionIDs
, i
);
960 fprintf(stream
, ", ");
962 print_cfstring(stream
, s
);
967 fprintf(stream
, "invalid partition ID: ");
968 print_cfstring(stream
, description
);
970 // don't release partitionIDs; it's an element of partitionList
971 safe_CFRelease(&binary
);
972 safe_CFRelease(&partitionList
);
978 * map a 6-bit binary value to a printable character.
981 unsigned char bintoasc
[] =
982 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
985 * map 6 bits to a printing char
987 #define ENC(c) (bintoasc[((c) & 0x3f)])
992 * map one group of up to 3 bytes at inp to 4 bytes at outp.
993 * Count is number of valid bytes in *inp; if less than 3, the
994 * 1 or two extras must be zeros.
997 encChunk(const unsigned char *inp
,
1001 unsigned char c1
, c2
, c3
, c4
;
1004 c2
= ((inp
[0] << 4) & 0x30) | ((inp
[1] >> 4) & 0xf);
1005 c3
= ((inp
[1] << 2) & 0x3c) | ((inp
[2] >> 6) & 0x3);
1023 static unsigned char *
1024 malloc_enc64_with_lines(const unsigned char *inbuf
,
1029 unsigned outTextLen
;
1030 unsigned len
; // to malloc, liberal
1031 unsigned olen
= 0; // actual output size
1032 unsigned char *outbuf
;
1033 unsigned char endbuf
[3];
1035 unsigned char *outp
;
1039 outTextLen
= ((((unsigned)inlen
) + 2) / 3) * 4;
1042 * linelen must be 0 mod 4 for this to work; round up...
1044 if((linelen
& 0x03) != 0) {
1045 linelen
= (linelen
+ 3) & 0xfffffffc;
1047 numLines
= (outTextLen
+ ((unsigned)linelen
) - 1)/ linelen
;
1054 * Total output size = encoded text size plus one newline per
1055 * line of output, plus trailing NULL. We always generate newlines
1056 * as \n; when decoding, we tolerate \r\n (Microsoft) or \n.
1058 len
= outTextLen
+ (2 * numLines
) + 1;
1059 outbuf
= (unsigned char*)malloc(len
);
1065 for(i
=0; i
<3; i
++) {
1067 endbuf
[i
] = inbuf
[i
];
1073 encChunk(endbuf
, outp
, inlen
);
1077 encChunk(inbuf
, outp
, 3);
1084 if((linelen
!= 0) && (thisLine
>= linelen
) && inlen
) {
1086 * last trailing newline added below
1087 * Note we don't split 4-byte output chunks over newlines
1101 print_buffer_pem(FILE *stream
, const char *headerString
, size_t length
, const void *data
)
1107 fprintf(stream
, "-----BEGIN %s-----\n", headerString
);
1108 buf
= malloc_enc64_with_lines(data
, length
, 64, &bufLen
);
1109 fwrite(buf
, bufLen
, 1, stream
);
1112 fprintf(stream
, "-----END %s-----\n", headerString
);
1116 prompt_password(const char* keychainName
) {
1117 const char *fmt
= "password to unlock %s: ";
1118 const char *name
= keychainName
? keychainName
: "default";
1119 char *prompt
= malloc(strlen(fmt
) + strlen(name
));
1120 sprintf(prompt
, fmt
, name
);
1121 char *password
= getpass(prompt
);