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
);