1/* 2 * Copyright (c) 2003-2009 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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 11 * file. 12 * 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. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 * 23 * keychain_utilities.c 24 */ 25 26#include "keychain_utilities.h" 27#include "security.h" 28 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> 34#include <stdio.h> 35#include <stdlib.h> 36#include <sys/param.h> 37#include <libkern/OSByteOrder.h> 38 39#include "readline.h" 40 41// SecTrustedApplicationValidateWithPath 42#include <Security/SecTrustedApplicationPriv.h> 43 44 45void check_obsolete_keychain(const char *kcName) 46{ 47 if(kcName == NULL) { 48 return; 49 } 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"); 61 } 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"); 71 } 72} 73 74SecKeychainRef 75keychain_open(const char *name) 76{ 77 SecKeychainRef keychain = NULL; 78 OSStatus result; 79 80 check_obsolete_keychain(name); 81 if (name && name[0] != '/') 82 { 83 CFArrayRef dynamic = NULL; 84 result = SecKeychainCopyDomainSearchList( 85 kSecPreferencesDomainDynamic, &dynamic); 86 if (result) 87 { 88 sec_error("SecKeychainCopyDomainSearchList %s: %s", 89 name, sec_errstr(result)); 90 return NULL; 91 } 92 else 93 { 94 uint32_t i; 95 uint32_t count = dynamic ? CFArrayGetCount(dynamic) : 0; 96 97 for (i = 0; i < count; ++i) 98 { 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); 104 if (result) 105 { 106 sec_error("SecKeychainGetPath %s: %s", 107 name, sec_errstr(result)); 108 return NULL; 109 } 110 if (!strncmp(pathName, name, ioPathLength)) 111 { 112 CFRetain(keychain); 113 CFRelease(dynamic); 114 return keychain; 115 } 116 } 117 CFRelease(dynamic); 118 } 119 } 120 121 result = SecKeychainOpen(name, &keychain); 122 if (result) 123 { 124 sec_error("SecKeychainOpen %s: %s", name, sec_errstr(result)); 125 } 126 127 return keychain; 128} 129 130CFTypeRef 131keychain_create_array(int argc, char * const *argv) 132{ 133 if (argc == 0) 134 return NULL; 135 else if (argc == 1) 136 return keychain_open(argv[0]); 137 else 138 { 139 CFMutableArrayRef keychains = CFArrayCreateMutable(NULL, argc, &kCFTypeArrayCallBacks); 140 int ix; 141 for (ix = 0; ix < argc; ++ix) 142 { 143 SecKeychainRef keychain = keychain_open(argv[ix]); 144 if (keychain) 145 { 146 CFArrayAppendValue(keychains, keychain); 147 CFRelease(keychain); 148 } 149 } 150 151 return keychains; 152 } 153} 154 155int 156parse_fourcharcode(const char *name, UInt32 *code) 157{ 158 UInt32 cc = 0; 159 int len = (name) ? strlen(name) : 0; 160 161 // error check the name 162 if (len != 4) 163 { 164 fprintf(stderr, "Error: four-character types must be exactly 4 characters long.\n"); 165 if (len == 3) { 166 fprintf(stderr, "(Try \"%s \" instead of \"%s\")\n", name, name); 167 } 168 return 1; 169 } 170 171 int i; 172 for (i = 0; i < 4; ++i) 173 { 174 cc = (cc << 8) | name[i]; 175 } 176 177 *code = cc; // note: this is in host byte order, suitable for passing to APIs 178 179 return 0; 180} 181 182int 183print_keychain_name(FILE *stream, SecKeychainRef keychain) 184{ 185 int result = 0; 186 char pathName[MAXPATHLEN]; 187 UInt32 ioPathLength = sizeof(pathName); 188 OSStatus status = SecKeychainGetPath(keychain, &ioPathLength, pathName); 189 if (status) 190 { 191 sec_perror("SecKeychainGetPath", status); 192 result = 1; 193 goto loser; 194 } 195 196 print_buffer(stream, ioPathLength, pathName); 197 198loser: 199 return result; 200} 201 202static void 203print_cfdata(FILE *stream, CFDataRef data) 204{ 205 if (data) 206 return print_buffer(stream, CFDataGetLength(data), CFDataGetBytePtr(data)); 207 else 208 fprintf(stream, "<NULL>"); 209} 210 211static void 212print_cfstring(FILE *stream, CFStringRef string) 213{ 214 if (!string) 215 fprintf(stream, "<NULL>"); 216 else 217 { 218 const char *utf8 = CFStringGetCStringPtr(string, kCFStringEncodingUTF8); 219 if (utf8) 220 fprintf(stream, "%s", utf8); 221 else 222 { 223 CFRange rangeToProcess = CFRangeMake(0, CFStringGetLength(string)); 224 while (rangeToProcess.length > 0) 225 { 226 UInt8 localBuffer[256]; 227 CFIndex usedBufferLength; 228 CFIndex numChars = CFStringGetBytes(string, rangeToProcess, 229 kCFStringEncodingUTF8, '?', FALSE, localBuffer, 230 sizeof(localBuffer), &usedBufferLength); 231 if (numChars == 0) 232 break; // Failed to convert anything... 233 234 fprintf(stream, "%.*s", (int)usedBufferLength, localBuffer); 235 rangeToProcess.location += numChars; 236 rangeToProcess.length -= numChars; 237 } 238 } 239 } 240} 241 242static int 243print_access(FILE *stream, SecAccessRef access, Boolean interactive) 244{ 245 CFArrayRef aclList = NULL; 246 CFIndex aclix, aclCount; 247 int result = 0; 248 OSStatus status; 249 250 status = SecAccessCopyACLList(access, &aclList); 251 if (status) 252 { 253 sec_perror("SecAccessCopyACLList", status); 254 result = 1; 255 goto loser; 256 } 257 258 aclCount = CFArrayGetCount(aclList); 259 fprintf(stream, "access: %lu entries\n", aclCount); 260 for (aclix = 0; aclix < aclCount; ++aclix) 261 { 262 CFArrayRef applicationList = NULL; 263 CFStringRef description = NULL; 264 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector = {}; 265 CFIndex appix, appCount; 266 267 SecACLRef acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, aclix); 268 CSSM_ACL_AUTHORIZATION_TAG tags[64]; // Pick some upper limit 269 uint32 tagix, tagCount = sizeof(tags) / sizeof(*tags); 270 status = SecACLGetAuthorizations(acl, tags, &tagCount); 271 if (status) 272 { 273 sec_perror("SecACLGetAuthorizations", status); 274 result = 1; 275 goto loser; 276 } 277 278 fprintf(stream, " entry %lu:\n authorizations (%lu):", aclix, 279 (unsigned long)tagCount); 280 for (tagix = 0; tagix < tagCount; ++tagix) 281 { 282 CSSM_ACL_AUTHORIZATION_TAG tag = tags[tagix]; 283 switch (tag) 284 { 285 case CSSM_ACL_AUTHORIZATION_ANY: 286 fputs(" any", stream); 287 break; 288 case CSSM_ACL_AUTHORIZATION_LOGIN: 289 fputs(" login", stream); 290 break; 291 case CSSM_ACL_AUTHORIZATION_GENKEY: 292 fputs(" genkey", stream); 293 break; 294 case CSSM_ACL_AUTHORIZATION_DELETE: 295 fputs(" delete", stream); 296 break; 297 case CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED: 298 fputs(" export_wrapped", stream); 299 break; 300 case CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR: 301 fputs(" export_clear", stream); 302 break; 303 case CSSM_ACL_AUTHORIZATION_IMPORT_WRAPPED: 304 fputs(" import_wrapped", stream); 305 break; 306 case CSSM_ACL_AUTHORIZATION_IMPORT_CLEAR: 307 fputs(" import_clear", stream); 308 break; 309 case CSSM_ACL_AUTHORIZATION_SIGN: 310 fputs(" sign", stream); 311 break; 312 case CSSM_ACL_AUTHORIZATION_ENCRYPT: 313 fputs(" encrypt", stream); 314 break; 315 case CSSM_ACL_AUTHORIZATION_DECRYPT: 316 fputs(" decrypt", stream); 317 break; 318 case CSSM_ACL_AUTHORIZATION_MAC: 319 fputs(" mac", stream); 320 break; 321 case CSSM_ACL_AUTHORIZATION_DERIVE: 322 fputs(" derive", stream); 323 break; 324 case CSSM_ACL_AUTHORIZATION_DBS_CREATE: 325 fputs(" dbs_create", stream); 326 break; 327 case CSSM_ACL_AUTHORIZATION_DBS_DELETE: 328 fputs(" dbs_delete", stream); 329 break; 330 case CSSM_ACL_AUTHORIZATION_DB_READ: 331 fputs(" db_read", stream); 332 break; 333 case CSSM_ACL_AUTHORIZATION_DB_INSERT: 334 fputs(" db_insert", stream); 335 break; 336 case CSSM_ACL_AUTHORIZATION_DB_MODIFY: 337 fputs(" db_modify", stream); 338 break; 339 case CSSM_ACL_AUTHORIZATION_DB_DELETE: 340 fputs(" db_delete", stream); 341 break; 342 case CSSM_ACL_AUTHORIZATION_CHANGE_ACL: 343 fputs(" change_acl", stream); 344 break; 345 case CSSM_ACL_AUTHORIZATION_CHANGE_OWNER: 346 fputs(" change_owner", stream); 347 break; 348 default: 349 fprintf(stream, " tag=%lu", (unsigned long)tag); 350 break; 351 } 352 } 353 fputc('\n', stream); 354 355 status = SecACLCopySimpleContents(acl, &applicationList, &description, &promptSelector); 356 if (status) 357 { 358 sec_perror("SecACLCopySimpleContents", status); 359 continue; 360 } 361 362 if (promptSelector.flags & CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE) 363 fputs(" require-password\n", stream); 364 else 365 fputs(" don't-require-password\n", stream); 366 367 fputs(" description: ", stream); 368 print_cfstring(stream, description); 369 fputc('\n', stream); 370 371 if (applicationList) 372 { 373 appCount = CFArrayGetCount(applicationList); 374 fprintf(stream, " applications (%lu):\n", appCount); 375 } 376 else 377 { 378 appCount = 0; 379 fprintf(stream, " applications: <null>\n"); 380 } 381 382 for (appix = 0; appix < appCount; ++appix) 383 { 384 const UInt8* bytes; 385 SecTrustedApplicationRef app = (SecTrustedApplicationRef)CFArrayGetValueAtIndex(applicationList, appix); 386 CFDataRef data = NULL; 387 fprintf(stream, " %lu: ", appix); 388 status = SecTrustedApplicationCopyData(app, &data); 389 if (status) 390 { 391 sec_perror("SecTrustedApplicationCopyData", status); 392 continue; 393 } 394 395 bytes = CFDataGetBytePtr(data); 396 if (bytes && bytes[0] == 0x2f) { 397 fprintf(stream, "%s", (const char *)bytes); 398 if ((status = SecTrustedApplicationValidateWithPath(app, (const char *)bytes)) == noErr) { 399 fprintf(stream, " (OK)"); 400 } else { 401 fprintf(stream, " (status %d)", (int)status); 402 } 403 fprintf(stream, "\n"); 404 } else { 405 print_cfdata(stream, data); 406 fputc('\n', stream); 407 } 408 if (data) 409 CFRelease(data); 410 } 411 412 if (applicationList) 413 CFRelease(applicationList); 414 415 if (description) 416 CFRelease(description); 417 418 if (interactive) 419 { 420 char buffer[10] = {}; 421 fprintf(stderr, "Remove this acl? "); 422 if (readline(buffer, sizeof(buffer)) && buffer[0] == 'y') 423 { 424 fprintf(stderr, "removing acl\n"); 425 status = SecACLRemove(acl); 426 if (status) 427 { 428 sec_perror("SecACLRemove", status); 429 continue; 430 } 431 } 432 } 433 } 434 435loser: 436 if (aclList) 437 CFRelease(aclList); 438 439 return result; 440} 441 442int 443print_keychain_item_attributes(FILE *stream, SecKeychainItemRef item, Boolean show_data, Boolean show_raw_data, Boolean show_acl, Boolean interactive) 444{ 445 int result = 0; 446 unsigned int ix; 447 OSStatus status; 448 SecKeychainRef keychain = NULL; 449 SecAccessRef access = NULL; 450 SecItemClass itemClass = 0; 451 UInt32 itemID; 452 SecKeychainAttributeList *attrList = NULL; 453 SecKeychainAttributeInfo *info = NULL; 454 UInt32 length = 0; 455 void *data = NULL; 456 457 status = SecKeychainItemCopyKeychain(item, &keychain); 458 if (status) 459 { 460 sec_perror("SecKeychainItemCopyKeychain", status); 461 result = 1; 462 goto loser; 463 } 464 465 fputs("keychain: ", stream); 466 result = print_keychain_name(stream, keychain); 467 fputc('\n', stream); 468 if (result) 469 goto loser; 470 471 /* First find out the item class. */ 472 status = SecKeychainItemCopyAttributesAndData(item, NULL, &itemClass, NULL, NULL, NULL); 473 if (status) 474 { 475 sec_perror("SecKeychainItemCopyAttributesAndData", status); 476 result = 1; 477 goto loser; 478 } 479 480 fputs("class: ", stream); 481 char buffer[4]; 482 buffer[3] = itemClass & 0xFF; 483 buffer[2] = (itemClass >> 8) & 0xFF; 484 buffer[1] = (itemClass >> 16) & 0xFF; 485 buffer[0] = (itemClass >> 24) & 0xFF; 486 487 print_buffer(stream, 4, buffer); 488 fputs("\nattributes:\n", stream); 489 490 switch (itemClass) 491 { 492 case kSecInternetPasswordItemClass: 493 itemID = CSSM_DL_DB_RECORD_INTERNET_PASSWORD; 494 break; 495 case kSecGenericPasswordItemClass: 496 itemID = CSSM_DL_DB_RECORD_GENERIC_PASSWORD; 497 break; 498 case 'ashp': /* kSecAppleSharePasswordItemClass */ 499 itemID = CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD; 500 break; 501 default: 502 itemID = itemClass; 503 break; 504 } 505 506 /* Now get the AttributeInfo for it. */ 507 status = SecKeychainAttributeInfoForItemID(keychain, itemID, &info); 508 if (status) 509 { 510 sec_perror("SecKeychainAttributeInfoForItemID", status); 511 result = 1; 512 goto loser; 513 } 514 515 status = SecKeychainItemCopyAttributesAndData(item, info, &itemClass, &attrList, 516 show_data ? &length : NULL, 517 show_data ? &data : NULL); 518 if (status) 519 { 520 sec_perror("SecKeychainItemCopyAttributesAndData", status); 521 result = 1; 522 goto loser; 523 } 524 525 if (info->count != attrList->count) 526 { 527 sec_error("info count: %ld != attribute count: %ld", info->count, attrList->count); 528 result = 1; 529 goto loser; 530 } 531 532 for (ix = 0; ix < info->count; ++ix) 533 { 534 UInt32 tag = info->tag[ix]; 535 UInt32 format = info->format[ix]; 536 SecKeychainAttribute *attribute = &attrList->attr[ix]; 537 if (tag != attribute->tag) 538 { 539 sec_error("attribute %d of %ld info tag: %ld != attribute tag: %ld", ix, info->count, tag, attribute->tag); 540 result = 1; 541 goto loser; 542 } 543 544 fputs(" ", stream); 545 print_uint32(stream, tag); 546 switch (format) 547 { 548 case CSSM_DB_ATTRIBUTE_FORMAT_STRING: 549 fputs("<string>", stream); 550 break; 551 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32: 552 fputs("<sint32>", stream); 553 break; 554 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32: 555 fputs("<uint32>", stream); 556 break; 557 case CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM: 558 fputs("<bignum>", stream); 559 break; 560 case CSSM_DB_ATTRIBUTE_FORMAT_REAL: 561 fputs("<real>", stream); 562 break; 563 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE: 564 fputs("<timedate>", stream); 565 break; 566 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB: 567 fputs("<blob>", stream); 568 break; 569 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32: 570 fputs("<uint32>", stream); 571 break; 572 case CSSM_DB_ATTRIBUTE_FORMAT_COMPLEX: 573 fputs("<complex>", stream); 574 break; 575 default: 576 fprintf(stream, "<format: %d>", (int)format); 577 break; 578 } 579 fputs("=", stream); 580 if (!attribute->length && !attribute->data) 581 fputs("<NULL>", stream); 582 else 583 { switch (format) 584 { 585 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32: 586 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32: 587 { 588 print_uint32(stream, *(UInt32*) attribute->data); 589 break; 590 } 591 592 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32: 593 { 594 int n = attribute->length / sizeof(UInt32); 595 UInt32* ptr = (UInt32*) attribute->data; 596 597 while (n--) 598 { 599 print_uint32(stream, *ptr++); 600 } 601 } 602 break; 603 604 default: 605 { 606 print_buffer(stream, attribute->length, attribute->data); 607 } 608 break; 609 } 610 } 611 fputc('\n', stream); 612 } 613 614 if (show_data) 615 { 616 fputs("data:\n", stream); 617 print_buffer(stream, length, data); 618 fputc('\n', stream); 619 } 620 621 if (show_raw_data) 622 { 623 CSSM_DL_DB_HANDLE dldbHandle = {}; 624 const CSSM_DB_UNIQUE_RECORD *uniqueRecordID = NULL; 625 CSSM_DATA data = {}; 626 status = SecKeychainItemGetDLDBHandle(item, &dldbHandle); 627 if (status) 628 { 629 sec_perror("SecKeychainItemGetDLDBHandle", status); 630 result = 1; 631 goto loser; 632 } 633 634 status = SecKeychainItemGetUniqueRecordID(item, &uniqueRecordID); 635 if (status) 636 { 637 sec_perror("SecKeychainItemGetUniqueRecordID", status); 638 result = 1; 639 goto loser; 640 } 641 642 status = CSSM_DL_DataGetFromUniqueRecordId(dldbHandle, uniqueRecordID, NULL, &data); 643 if (status) 644 { 645 sec_perror("CSSM_DL_DataGetFromUniqueRecordId", status); 646 result = 1; 647 goto loser; 648 } 649 650 fputs("raw data:\n", stream); 651 print_buffer(stream, data.Length, data.Data); 652 fputc('\n', stream); 653 654 /* @@@ Hmm which allocators should we use here? */ 655 free(data.Data); 656 } 657 658 if (show_acl) 659 { 660 status = SecKeychainItemCopyAccess(item, &access); 661 if (status == errSecNoAccessForItem) 662 fprintf(stream, "no access control for this item\n"); 663 else 664 { 665 if (status) 666 { 667 sec_perror("SecKeychainItemCopyAccess", status); 668 result = 1; 669 goto loser; 670 } 671 672 result = print_access(stream, access, interactive); 673 if (result) 674 goto loser; 675 676 if (interactive) 677 { 678 char buffer[10] = {}; 679 fprintf(stderr, "Update access? "); 680 if (readline(buffer, sizeof(buffer)) && buffer[0] == 'y') 681 { 682 fprintf(stderr, "Updating access\n"); 683 status = SecKeychainItemSetAccess(item, access); 684 if (status) 685 { 686 sec_perror("SecKeychainItemSetAccess", status); 687 result = 1; 688 goto loser; 689 } 690 } 691 } 692 } 693 } 694 695loser: 696 if (access) 697 CFRelease(access); 698 699 if (attrList) 700 { 701 status = SecKeychainItemFreeAttributesAndData(attrList, data); 702 if (status) 703 sec_perror("SecKeychainItemFreeAttributesAndData", status); 704 } 705 706 if (info) 707 { 708 status = SecKeychainFreeAttributeInfo(info); 709 if (status) 710 sec_perror("SecKeychainFreeAttributeInfo", status); 711 } 712 713 if (keychain) 714 CFRelease(keychain); 715 716 return result; 717} 718 719static void 720print_buffer_hex(FILE *stream, UInt32 length, const void *data) 721{ 722 uint8 *p = (uint8 *) data; 723 while (length--) 724 { 725 int ch = *p++; 726 fprintf(stream, "%02X", ch); 727 } 728} 729 730static void 731print_buffer_ascii(FILE *stream, UInt32 length, const void *data) 732{ 733 uint8 *p = (uint8 *) data; 734 while (length--) 735 { 736 int ch = *p++; 737 if (ch >= ' ' && ch <= '~' && ch != '\\') 738 { 739 fputc(ch, stream); 740 } 741 else 742 { 743 fputc('\\', stream); 744 fputc('0' + ((ch >> 6) & 7), stream); 745 fputc('0' + ((ch >> 3) & 7), stream); 746 fputc('0' + ((ch >> 0) & 7), stream); 747 } 748 } 749} 750 751void 752print_buffer(FILE *stream, UInt32 length, const void *data) 753{ 754 uint8 *p = (uint8 *) data; 755 Boolean hex = FALSE; 756 Boolean ascii = FALSE; 757 UInt32 ix; 758 for (ix = 0; ix < length; ++ix) 759 { 760 int ch = *p++; 761 if (ch >= ' ' && ch <= '~' && ch != '\\') 762 ascii = TRUE; 763 else 764 hex = TRUE; 765 } 766 767 if (hex) 768 { 769 fputc('0', stream); 770 fputc('x', stream); 771 print_buffer_hex(stream, length, data); 772 if (ascii) 773 fputc(' ', stream); 774 fputc(' ', stream); 775 } 776 if (ascii) 777 { 778 fputc('"', stream); 779 print_buffer_ascii(stream, length, data); 780 fputc('"', stream); 781 } 782} 783 784void 785print_uint32(FILE *stream, uint32 n) 786{ 787 n = OSSwapHostToBigInt32 (n); 788 print_buffer(stream, sizeof(UInt32), &n); 789} 790 791unsigned char 792hexValue(char c) 793{ 794 static const char digits[] = "0123456789abcdef"; 795 char *p; 796 if (p = strchr(digits, tolower(c))) 797 return p - digits; 798 else 799 return 0; 800} 801 802void 803fromHex(const char *hexDigits, CSSM_DATA *data) 804{ 805 size_t bytes = strlen(hexDigits) / 2; // (discards malformed odd end) 806 if (bytes > data->Length) 807 return; 808 // length(bytes); // (will assert if we try to grow it) 809 size_t n; 810 for (n = 0; n < bytes; n++) { 811 data->Data[n] = (uint8)(hexValue(hexDigits[2*n]) << 4 | hexValue(hexDigits[2*n+1])); 812 } 813} 814 815void 816safe_CFRelease(void *cfTypeRefPtr) 817{ 818 CFTypeRef *obj = (CFTypeRef *)cfTypeRefPtr; 819 if (obj && *obj) { 820 CFRelease(*obj); 821 *obj = NULL; 822 } 823} 824 825/* 826 * map a 6-bit binary value to a printable character. 827 */ 828static const 829unsigned char bintoasc[] = 830 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 831 832/* 833 * map 6 bits to a printing char 834 */ 835#define ENC(c) (bintoasc[((c) & 0x3f)]) 836 837#define PAD '=' 838 839/* 840 * map one group of up to 3 bytes at inp to 4 bytes at outp. 841 * Count is number of valid bytes in *inp; if less than 3, the 842 * 1 or two extras must be zeros. 843 */ 844static void 845encChunk(const unsigned char *inp, 846 unsigned char *outp, 847 int count) 848{ 849 unsigned char c1, c2, c3, c4; 850 851 c1 = *inp >> 2; 852 c2 = ((inp[0] << 4) & 0x30) | ((inp[1] >> 4) & 0xf); 853 c3 = ((inp[1] << 2) & 0x3c) | ((inp[2] >> 6) & 0x3); 854 c4 = inp[2] & 0x3f; 855 *outp++ = ENC(c1); 856 *outp++ = ENC(c2); 857 if (count == 1) { 858 *outp++ = PAD; 859 *outp = PAD; 860 } else { 861 *outp++ = ENC(c3); 862 if (count == 2) { 863 *outp = PAD; 864 } 865 else { 866 *outp = ENC(c4); 867 } 868 } 869} 870 871static unsigned char * 872malloc_enc64_with_lines(const unsigned char *inbuf, 873 unsigned inlen, 874 unsigned linelen, 875 unsigned *outlen) 876{ 877 unsigned outTextLen; 878 unsigned len; // to malloc, liberal 879 unsigned olen = 0; // actual output size 880 unsigned char *outbuf; 881 unsigned char endbuf[3]; 882 unsigned i; 883 unsigned char *outp; 884 unsigned numLines; 885 unsigned thisLine; 886 887 outTextLen = ((inlen + 2) / 3) * 4; 888 if(linelen) { 889 /* 890 * linelen must be 0 mod 4 for this to work; round up... 891 */ 892 if((linelen & 0x03) != 0) { 893 linelen = (linelen + 3) & 0xfffffffc; 894 } 895 numLines = (outTextLen + linelen - 1)/ linelen; 896 } 897 else { 898 numLines = 1; 899 } 900 901 /* 902 * Total output size = encoded text size plus one newline per 903 * line of output, plus trailing NULL. We always generate newlines 904 * as \n; when decoding, we tolerate \r\n (Microsoft) or \n. 905 */ 906 len = outTextLen + (2 * numLines) + 1; 907 outbuf = (unsigned char*)malloc(len); 908 outp = outbuf; 909 thisLine = 0; 910 911 while(inlen) { 912 if(inlen < 3) { 913 for(i=0; i<3; i++) { 914 if(i < inlen) { 915 endbuf[i] = inbuf[i]; 916 } 917 else { 918 endbuf[i] = 0; 919 } 920 } 921 encChunk(endbuf, outp, inlen); 922 inlen = 0; 923 } 924 else { 925 encChunk(inbuf, outp, 3); 926 inlen -= 3; 927 inbuf += 3; 928 } 929 outp += 4; 930 thisLine += 4; 931 olen += 4; 932 if((linelen != 0) && (thisLine >= linelen) && inlen) { 933 /* 934 * last trailing newline added below 935 * Note we don't split 4-byte output chunks over newlines 936 */ 937 *outp++ = '\n'; 938 olen++; 939 thisLine = 0; 940 } 941 } 942 *outp++ = '\n'; 943 olen += 1; 944 *outlen = olen; 945 return outbuf; 946} 947 948void 949print_buffer_pem(FILE *stream, const char *headerString, UInt32 length, const void *data) 950{ 951 unsigned char *buf; 952 unsigned bufLen; 953 954 if (headerString) 955 fprintf(stream, "-----BEGIN %s-----\n", headerString); 956 buf = malloc_enc64_with_lines(data, length, 64, &bufLen); 957 fwrite(buf, bufLen, 1, stream); 958 free(buf); 959 if (headerString) 960 fprintf(stream, "-----END %s-----\n", headerString); 961} 962