1/* 2 * Copyright (c) 2003-2010 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_find.c 24 */ 25 26#include "keychain_find.h" 27 28#include "keychain_utilities.h" 29#include "readline.h" 30#include "security.h" 31 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> 35#include <unistd.h> 36#include <libkern/OSByteOrder.h> 37#include <Security/SecKeychainItem.h> 38#include <Security/SecKeychainSearch.h> 39#include <Security/SecCertificate.h> 40#include <CoreFoundation/CFString.h> 41#include <ctype.h> 42 43 44// SecDigestGetData, SecKeychainSearchCreateForCertificateByEmail, SecCertificateFindByEmail 45#include <Security/SecCertificatePriv.h> 46 47Boolean gDeleteIt = 0; 48 49// find_first_generic_password 50// 51// Returns a SecKeychainItemRef for the first item 52// which matches the specified attributes. Caller is 53// responsible for releasing the item (with CFRelease). 54// 55SecKeychainItemRef 56find_first_generic_password(CFTypeRef keychainOrArray, 57 FourCharCode itemCreator, 58 FourCharCode itemType, 59 const char *kind, 60 const char *value, 61 const char *comment, 62 const char *label, 63 const char *serviceName, 64 const char *accountName) 65{ 66 OSStatus status = noErr; 67 SecKeychainSearchRef searchRef = NULL; 68 SecKeychainItemRef itemRef = NULL; 69 70 SecKeychainAttribute attrs[8]; // maximum number of searchable attributes 71 SecKeychainAttributeList attrList = { 0, attrs }; 72 73 // the primary key for a generic password item (i.e. the combination of 74 // attributes which determine whether the item is unique) consists of: 75 // { kSecAccountItemAttr, kSecServiceItemAttr } 76 // 77 // if we have a primary key, we don't need to search on other attributes 78 // (and we don't want to, if non-primary attributes are being updated) 79 Boolean primaryKey = (accountName && serviceName); 80 81 // build the attribute list for searching 82 if ((UInt32)itemCreator != 0 && !primaryKey) { 83 attrs[attrList.count].tag = kSecCreatorItemAttr; 84 attrs[attrList.count].length = sizeof(FourCharCode); 85 attrs[attrList.count].data = (FourCharCode *)&itemCreator; 86 attrList.count++; 87 } 88 if ((UInt32)itemType != 0 && !primaryKey) { 89 attrs[attrList.count].tag = kSecTypeItemAttr; 90 attrs[attrList.count].length = sizeof(FourCharCode); 91 attrs[attrList.count].data = (FourCharCode *)&itemType; 92 attrList.count++; 93 } 94 if (kind != NULL && !primaryKey) { 95 attrs[attrList.count].tag = kSecDescriptionItemAttr; 96 attrs[attrList.count].length = strlen(kind); 97 attrs[attrList.count].data = (void*)kind; 98 attrList.count++; 99 } 100 if (value != NULL && !primaryKey) { 101 attrs[attrList.count].tag = kSecGenericItemAttr; 102 attrs[attrList.count].length = strlen(value); 103 attrs[attrList.count].data = (void*)value; 104 attrList.count++; 105 } 106 if (comment != NULL && !primaryKey) { 107 attrs[attrList.count].tag = kSecCommentItemAttr; 108 attrs[attrList.count].length = strlen(comment); 109 attrs[attrList.count].data = (void*)comment; 110 attrList.count++; 111 } 112 if (label != NULL && !primaryKey) { 113 attrs[attrList.count].tag = kSecLabelItemAttr; 114 attrs[attrList.count].length = strlen(label); 115 attrs[attrList.count].data = (void*)label; 116 attrList.count++; 117 } 118 if (serviceName != NULL) { 119 attrs[attrList.count].tag = kSecServiceItemAttr; 120 attrs[attrList.count].length = strlen(serviceName); 121 attrs[attrList.count].data = (void*)serviceName; 122 attrList.count++; 123 } 124 if (accountName != NULL) { 125 attrs[attrList.count].tag = kSecAccountItemAttr; 126 attrs[attrList.count].length = strlen(accountName); 127 attrs[attrList.count].data = (void*)accountName; 128 attrList.count++; 129 } 130 131 status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecGenericPasswordItemClass, &attrList, &searchRef); 132 if (status) { 133 sec_perror("SecKeychainSearchCreateFromAttributes", status); 134 goto cleanup; 135 } 136 // we're only interested in the first match, if there is a match at all 137 status = SecKeychainSearchCopyNext(searchRef, &itemRef); 138 if (status) { 139 itemRef = NULL; 140 } 141 142cleanup: 143 if (searchRef) 144 CFRelease(searchRef); 145 146 return itemRef; 147} 148 149// find_first_internet_password 150// 151// Returns a SecKeychainItemRef for the first item 152// which matches the specified attributes. Caller is 153// responsible for releasing the item (with CFRelease). 154// 155SecKeychainItemRef 156find_first_internet_password(CFTypeRef keychainOrArray, 157 FourCharCode itemCreator, 158 FourCharCode itemType, 159 const char *kind, 160 const char *comment, 161 const char *label, 162 const char *serverName, 163 const char *securityDomain, 164 const char *accountName, 165 const char *path, 166 UInt16 port, 167 SecProtocolType protocol, 168 SecAuthenticationType authenticationType) 169{ 170 OSStatus status = noErr; 171 SecKeychainSearchRef searchRef = NULL; 172 SecKeychainItemRef itemRef = NULL; 173 174 SecKeychainAttribute attrs[12]; // maximum number of searchable attributes 175 SecKeychainAttributeList attrList = { 0, attrs }; 176 177 // the primary key for an internet password item (i.e. the combination of 178 // attributes which determine whether the item is unique) consists of: 179 // { kSecAccountItemAttr, kSecSecurityDomainItemAttr, kSecServerItemAttr, 180 // kSecProtocolItemAttr, kSecAuthenticationTypeItemAttr, 181 // kSecPortItemAttr, kSecPathItemAttr } 182 // 183 // if we have a primary key, we don't need to search on other attributes. 184 // (and we don't want to, if non-primary attributes are being updated) 185 Boolean primaryKey = (accountName && securityDomain && serverName && 186 protocol && authenticationType && port && path); 187 188 // build the attribute list for searching 189 if ((UInt32)itemCreator != 0 && !primaryKey) { 190 attrs[attrList.count].tag = kSecCreatorItemAttr; 191 attrs[attrList.count].length = sizeof(FourCharCode); 192 attrs[attrList.count].data = (FourCharCode *)&itemCreator; 193 attrList.count++; 194 } 195 if ((UInt32)itemType != 0 && !primaryKey) { 196 attrs[attrList.count].tag = kSecTypeItemAttr; 197 attrs[attrList.count].length = sizeof(FourCharCode); 198 attrs[attrList.count].data = (FourCharCode *)&itemType; 199 attrList.count++; 200 } 201 if (kind != NULL && !primaryKey) { 202 attrs[attrList.count].tag = kSecDescriptionItemAttr; 203 attrs[attrList.count].length = strlen(kind); 204 attrs[attrList.count].data = (void*)kind; 205 attrList.count++; 206 } 207 if (comment != NULL && !primaryKey) { 208 attrs[attrList.count].tag = kSecCommentItemAttr; 209 attrs[attrList.count].length = strlen(comment); 210 attrs[attrList.count].data = (void*)comment; 211 attrList.count++; 212 } 213 if (label != NULL && !primaryKey) { 214 attrs[attrList.count].tag = kSecLabelItemAttr; 215 attrs[attrList.count].length = strlen(label); 216 attrs[attrList.count].data = (void*)label; 217 attrList.count++; 218 } 219 if (serverName != NULL) { 220 attrs[attrList.count].tag = kSecServerItemAttr; 221 attrs[attrList.count].length = strlen(serverName); 222 attrs[attrList.count].data = (void*)serverName; 223 attrList.count++; 224 } 225 if (securityDomain != NULL) { 226 attrs[attrList.count].tag = kSecSecurityDomainItemAttr; 227 attrs[attrList.count].length = strlen(securityDomain); 228 attrs[attrList.count].data = (void *)securityDomain; 229 attrList.count++; 230 } 231 if (accountName != NULL) { 232 attrs[attrList.count].tag = kSecAccountItemAttr; 233 attrs[attrList.count].length = strlen(accountName); 234 attrs[attrList.count].data = (void *)accountName; 235 attrList.count++; 236 } 237 if (path != NULL) { 238 attrs[attrList.count].tag = kSecPathItemAttr; 239 attrs[attrList.count].length = strlen(path); 240 attrs[attrList.count].data = (void *)path; 241 attrList.count++; 242 } 243 if (port != 0) { 244 attrs[attrList.count].tag = kSecPortItemAttr; 245 attrs[attrList.count].length = sizeof(UInt16); 246 attrs[attrList.count].data = (UInt16 *)&port; 247 attrList.count++; 248 } 249 if ((UInt32)protocol != 0) { 250 attrs[attrList.count].tag = kSecProtocolItemAttr; 251 attrs[attrList.count].length = sizeof(SecProtocolType); 252 attrs[attrList.count].data = (SecProtocolType *)&protocol; 253 attrList.count++; 254 } 255 if ((UInt32)authenticationType != 0) { 256 attrs[attrList.count].tag = kSecAuthenticationTypeItemAttr; 257 attrs[attrList.count].length = sizeof(SecAuthenticationType); 258 attrs[attrList.count].data = (SecAuthenticationType *)&authenticationType; 259 attrList.count++; 260 } 261 262 status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecInternetPasswordItemClass, &attrList, &searchRef); 263 if (status) { 264 sec_perror("SecKeychainSearchCreateFromAttributes", status); 265 goto cleanup; 266 } 267 // we're only interested in the first match, if there is a match at all 268 status = SecKeychainSearchCopyNext(searchRef, &itemRef); 269 if (status) { 270 itemRef = NULL; 271 } 272 273cleanup: 274 if (searchRef) 275 CFRelease(searchRef); 276 277 return itemRef; 278} 279 280// find_unique_certificate 281// 282// Returns a SecKeychainItemRef for the certificate 283// in the specified keychain (or keychain list) 284// which is a unique match for either the specified name 285// or SHA-1 hash. If more than one match exists, the 286// certificate is not unique and none are returned. Caller is 287// responsible for releasing the item (with CFRelease). 288// 289SecKeychainItemRef 290find_unique_certificate(CFTypeRef keychainOrArray, 291 const char *name, 292 const char *hash) 293{ 294 OSStatus status = noErr; 295 SecKeychainSearchRef searchRef = NULL; 296 SecKeychainItemRef uniqueItemRef = NULL; 297 298 status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecCertificateItemClass, NULL, &searchRef); 299 if (status) { 300 return uniqueItemRef; 301 } 302 303 // check input hash string and convert to data 304 CSSM_DATA hashData = { 0, NULL }; 305 if (hash) { 306 CSSM_SIZE len = strlen(hash)/2; 307 hashData.Length = len; 308 hashData.Data = (uint8 *)malloc(hashData.Length); 309 fromHex(hash, &hashData); 310 } 311 312 // filter candidates against the hash (or the name, if no hash provided) 313 CFStringRef matchRef = (name) ? CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8) : NULL; 314 Boolean exactMatch = FALSE; 315 316 CSSM_DATA certData = { 0, NULL }; 317 SecKeychainItemRef candidate = NULL; 318 319 while (SecKeychainSearchCopyNext(searchRef, &candidate) == noErr) { 320 SecCertificateRef cert = (SecCertificateRef)candidate; 321 if (SecCertificateGetData(cert, &certData) != noErr) { 322 safe_CFRelease(&candidate); 323 continue; 324 } 325 if (hash) { 326 uint8 candidate_sha1_hash[20]; 327 CSSM_DATA digest; 328 digest.Length = sizeof(candidate_sha1_hash); 329 digest.Data = candidate_sha1_hash; 330 if ((SecDigestGetData(CSSM_ALGID_SHA1, &digest, &certData) == CSSM_OK) && 331 (hashData.Length == digest.Length) && 332 (!memcmp(hashData.Data, digest.Data, digest.Length))) { 333 exactMatch = TRUE; 334 uniqueItemRef = candidate; // currently retained 335 break; // we're done - can't get more exact than this 336 } 337 } else { 338 // copy certificate name 339 CFStringRef nameRef = NULL; 340 if ((SecCertificateCopyCommonName(cert, &nameRef) != noErr) || nameRef == NULL) { 341 safe_CFRelease(&candidate); 342 continue; // no name, so no match is possible 343 } 344 CFIndex nameLen = CFStringGetLength(nameRef); 345 CFIndex bufLen = 1 + CFStringGetMaximumSizeForEncoding(nameLen, kCFStringEncodingUTF8); 346 char *nameBuf = (char *)malloc(bufLen); 347 if (!CFStringGetCString(nameRef, nameBuf, bufLen-1, kCFStringEncodingUTF8)) 348 nameBuf[0]=0; 349 350 CFRange find = { kCFNotFound, 0 }; 351 if (nameRef && matchRef) 352 find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral); 353 Boolean isExact = (find.location == 0 && find.length == nameLen); 354 if (find.location == kCFNotFound) { 355 free(nameBuf); 356 safe_CFRelease(&nameRef); 357 safe_CFRelease(&candidate); 358 continue; // no match 359 } 360 if (uniqueItemRef) { // got two matches 361 if (exactMatch && !isExact) { // prior is better; ignore this one 362 free(nameBuf); 363 safe_CFRelease(&nameRef); 364 safe_CFRelease(&candidate); 365 continue; 366 } 367 if (exactMatch == isExact) { // same class of match 368 if (CFEqual(uniqueItemRef, candidate)) { // same certificate 369 free(nameBuf); 370 safe_CFRelease(&nameRef); 371 safe_CFRelease(&candidate); 372 continue; 373 } 374 // ambiguity - must fail 375 sec_error("\"%s\" is ambiguous, matches more than one certificate", name); 376 free(nameBuf); 377 safe_CFRelease(&nameRef); 378 safe_CFRelease(&candidate); 379 safe_CFRelease(&uniqueItemRef); 380 break; 381 } 382 safe_CFRelease(&uniqueItemRef); // about to replace with this one 383 } 384 uniqueItemRef = candidate; // currently retained 385 exactMatch = isExact; 386 free(nameBuf); 387 safe_CFRelease(&nameRef); 388 } 389 } 390 391 safe_CFRelease(&searchRef); 392 safe_CFRelease(&matchRef); 393 if (hashData.Data) { 394 free(hashData.Data); 395 } 396 397 return uniqueItemRef; 398} 399 400static OSStatus 401do_password_item_printing( SecKeychainItemRef itemRef, 402 Boolean get_password, 403 Boolean password_stdout) 404{ 405 OSStatus result = noErr; 406 void *passwordData = NULL; 407 UInt32 passwordLength = 0; 408 409 if(get_password) { 410 result = SecKeychainItemCopyContent(itemRef, NULL, NULL, &passwordLength, &passwordData); 411 if(result != noErr) return result; 412 } 413 if(!password_stdout) { 414 print_keychain_item_attributes(stdout, itemRef, FALSE, FALSE, FALSE, FALSE); 415 if(get_password) { 416 fputs("password: ", stderr); 417 print_buffer(stderr, passwordLength, passwordData); 418 fputc('\n', stderr); 419 } 420 } else { 421 char *password = (char *) passwordData; 422 int doHex = 0; 423 for(int i=0; i<passwordLength; i++) if(!isprint(password[i])) doHex = 1; 424 if(doHex) { 425 for(int i=0; i<passwordLength; i++) printf("%02x", password[i]); 426 } else { 427 for(int i=0; i<passwordLength; i++) putchar(password[i]); 428 } 429 putchar('\n'); 430 } 431 432 if (passwordData) SecKeychainItemFreeContent(NULL, passwordData); 433 return noErr; 434 435} 436 437static int 438do_keychain_find_generic_password(CFTypeRef keychainOrArray, 439 FourCharCode itemCreator, 440 FourCharCode itemType, 441 const char *kind, 442 const char *value, 443 const char *comment, 444 const char *label, 445 const char *serviceName, 446 const char *accountName, 447 Boolean get_password, 448 Boolean password_stdout) 449{ 450 OSStatus result = noErr; 451 SecKeychainItemRef itemRef = NULL; 452 453 itemRef = find_first_generic_password(keychainOrArray, 454 itemCreator, 455 itemType, 456 kind, 457 value, 458 comment, 459 label, 460 serviceName, 461 accountName); 462 463 if(itemRef) { 464 result = do_password_item_printing(itemRef, get_password, password_stdout); 465 } else { 466 result = errSecItemNotFound; 467 sec_perror("SecKeychainSearchCopyNext", result); 468 } 469 470 if (itemRef) CFRelease(itemRef); 471 472 return result; 473} 474 475static int 476do_keychain_delete_generic_password(CFTypeRef keychainOrArray, 477 FourCharCode itemCreator, 478 FourCharCode itemType, 479 const char *kind, 480 const char *value, 481 const char *comment, 482 const char *label, 483 const char *serviceName, 484 const char *accountName) 485{ 486 OSStatus result = noErr; 487 SecKeychainItemRef itemRef = NULL; 488 void *passwordData = NULL; 489 490 itemRef = find_first_generic_password(keychainOrArray, 491 itemCreator, 492 itemType, 493 kind, 494 value, 495 comment, 496 label, 497 serviceName, 498 accountName); 499 if (!itemRef) { 500 result = errSecItemNotFound; 501 sec_perror("SecKeychainSearchCopyNext", result); 502 goto cleanup; 503 } 504 505 print_keychain_item_attributes(stdout, itemRef, FALSE, FALSE, FALSE, FALSE); 506 507 result = SecKeychainItemDelete(itemRef); 508 509 fputs("password has been deleted.\n", stderr); 510 511cleanup: 512 if (passwordData) 513 SecKeychainItemFreeContent(NULL, passwordData); 514 if (itemRef) 515 CFRelease(itemRef); 516 517 return result; 518} 519 520static int 521do_keychain_find_internet_password(CFTypeRef keychainOrArray, 522 FourCharCode itemCreator, 523 FourCharCode itemType, 524 const char *kind, 525 const char *comment, 526 const char *label, 527 const char *serverName, 528 const char *securityDomain, 529 const char *accountName, 530 const char *path, 531 UInt16 port, 532 SecProtocolType protocol, 533 SecAuthenticationType authenticationType, 534 Boolean get_password, 535 Boolean password_stdout) 536{ 537 OSStatus result = noErr; 538 SecKeychainItemRef itemRef = NULL; 539 540 itemRef = find_first_internet_password(keychainOrArray, 541 itemCreator, 542 itemType, 543 kind, 544 comment, 545 label, 546 serverName, 547 securityDomain, 548 accountName, 549 path, 550 port, 551 protocol, 552 authenticationType); 553 if(itemRef) { 554 result = do_password_item_printing(itemRef, get_password, password_stdout); 555 } else { 556 result = errSecItemNotFound; 557 sec_perror("SecKeychainSearchCopyNext", result); 558 } 559 560 return result; 561} 562 563static int 564do_keychain_delete_internet_password(CFTypeRef keychainOrArray, 565 FourCharCode itemCreator, 566 FourCharCode itemType, 567 const char *kind, 568 const char *comment, 569 const char *label, 570 const char *serverName, 571 const char *securityDomain, 572 const char *accountName, 573 const char *path, 574 UInt16 port, 575 SecProtocolType protocol, 576 SecAuthenticationType authenticationType) 577{ 578 OSStatus result = noErr; 579 SecKeychainItemRef itemRef = NULL; 580 void *passwordData = NULL; 581 582 itemRef = find_first_internet_password(keychainOrArray, 583 itemCreator, 584 itemType, 585 kind, 586 comment, 587 label, 588 serverName, 589 securityDomain, 590 accountName, 591 path, 592 port, 593 protocol, 594 authenticationType); 595 if (!itemRef) { 596 result = errSecItemNotFound; 597 sec_perror("SecKeychainSearchCopyNext", result); 598 goto cleanup; 599 } 600 601 print_keychain_item_attributes(stdout, itemRef, FALSE, FALSE, FALSE, FALSE); 602 603 result = SecKeychainItemDelete(itemRef); 604 605 fputs("password has been deleted.\n", stderr); 606 607cleanup: 608 if (passwordData) 609 SecKeychainItemFreeContent(NULL, passwordData); 610 if (itemRef) 611 CFRelease(itemRef); 612 613 return result; 614} 615 616static int 617do_keychain_find_certificate(CFTypeRef keychainOrArray, 618 const char *name, 619 const char *emailAddress, 620 Boolean print_hash, 621 Boolean output_pem, 622 Boolean find_all, 623 Boolean print_email) 624{ 625 OSStatus result = noErr; 626 SecCertificateRef certificateRef = NULL; 627 SecKeychainSearchRef searchRef = NULL; 628 CFStringRef matchRef = (name) ? CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8) : NULL; 629 630 if (find_all && emailAddress) { 631 result = SecKeychainSearchCreateForCertificateByEmail(keychainOrArray, emailAddress, &searchRef); 632 if (result) { 633 sec_perror("SecKeychainSearchCreateForCertificateByEmail", result); 634 goto cleanup; 635 } 636 } else { 637 result = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecCertificateItemClass, NULL, &searchRef); 638 if (result) { 639 sec_perror("SecKeychainSearchCreateFromAttributes", result); 640 goto cleanup; 641 } 642 } 643 644 do 645 { 646 if (find_all) { 647 SecKeychainItemRef itemRef = NULL; 648 result = SecKeychainSearchCopyNext(searchRef, &itemRef); 649 if (result == errSecItemNotFound) { 650 result = 0; 651 break; 652 } 653 else if (result) { 654 sec_perror("SecKeychainSearchCopyNext", result); 655 goto cleanup; 656 } 657 658 if (!emailAddress && name) { 659 // match name in common name 660 CFStringRef nameRef = NULL; 661 if (SecCertificateCopyCommonName((SecCertificateRef)itemRef, &nameRef) != noErr) { 662 safe_CFRelease(&itemRef); 663 continue; // no name, so no match is possible 664 } 665 CFRange find = { kCFNotFound, 0 }; 666 if (nameRef && matchRef) 667 find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral); 668 if (find.location == kCFNotFound) { 669 safe_CFRelease(&nameRef); 670 safe_CFRelease(&itemRef); 671 continue; // no match 672 } 673 safe_CFRelease(&nameRef); 674 } 675 safe_CFRelease(&certificateRef); 676 certificateRef = (SecCertificateRef) itemRef; 677 } 678 else { // only want the first match 679 if (emailAddress) { 680 result = SecCertificateFindByEmail(keychainOrArray, emailAddress, &certificateRef); 681 if (result) { 682 sec_perror("SecCertificateFindByEmail", result); 683 goto cleanup; 684 } 685 } else { 686 SecKeychainItemRef itemRef = NULL; 687 while ((result = SecKeychainSearchCopyNext(searchRef, &itemRef)) != errSecItemNotFound) { 688 if (name) { 689 // match name in common name 690 CFStringRef nameRef = NULL; 691 if (SecCertificateCopyCommonName((SecCertificateRef)itemRef, &nameRef) != noErr) { 692 safe_CFRelease(&itemRef); 693 continue; // no name, so no match is possible 694 } 695 CFRange find = { kCFNotFound, 0 }; 696 if (nameRef && matchRef) 697 find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral); 698 if (find.location == kCFNotFound) { 699 safe_CFRelease(&nameRef); 700 safe_CFRelease(&itemRef); 701 continue; // no match 702 } 703 safe_CFRelease(&nameRef); 704 } 705 break; // we have a match! 706 } 707 if (result == errSecItemNotFound) { 708 sec_perror("SecKeychainSearchCopyNext", result); 709 goto cleanup; 710 } 711 certificateRef = (SecCertificateRef) itemRef; 712 } 713 } 714 715 // process the found certificate 716 717 if (print_hash) { 718 uint8 sha1_hash[20]; 719 CSSM_DATA data; 720 CSSM_DATA digest; 721 digest.Length = sizeof(sha1_hash); 722 digest.Data = sha1_hash; 723 if ((SecCertificateGetData(certificateRef, &data) == noErr) && 724 (SecDigestGetData(CSSM_ALGID_SHA1, &digest, &data) == CSSM_OK)) { 725 unsigned int i; 726 uint32 len = digest.Length; 727 uint8 *cp = digest.Data; 728 fprintf(stdout, "SHA-1 hash: "); 729 for(i=0; i<len; i++) { 730 fprintf(stdout, "%02X", ((unsigned char *)cp)[i]); 731 } 732 fprintf(stdout, "\n"); 733 } 734 } 735 736 if (print_email) 737 { 738 CFArrayRef emailAddresses = NULL; 739 CFIndex ix, count; 740 result = SecCertificateCopyEmailAddresses(certificateRef, &emailAddresses); 741 if (result) 742 { 743 sec_perror("SecCertificateCopyEmailAddresses", result); 744 goto cleanup; 745 } 746 747 count = CFArrayGetCount(emailAddresses); 748 fputs("email addresses: ", stdout); 749 for (ix = 0; ix < count; ++ix) 750 { 751 CFStringRef emailAddress = (CFStringRef)CFArrayGetValueAtIndex(emailAddresses, ix); 752 const char *addr; 753 char buffer[256]; 754 755 if (ix) 756 fputs(", ", stdout); 757 758 addr = CFStringGetCStringPtr(emailAddress, kCFStringEncodingUTF8); 759 if (!addr) 760 { 761 if (CFStringGetCString(emailAddress, buffer, sizeof(buffer), kCFStringEncodingUTF8)) 762 addr = buffer; 763 } 764 765 fprintf(stdout, "%s", addr); 766 } 767 fputc('\n', stdout); 768 769 CFRelease(emailAddresses); 770 } 771 772 if (output_pem) 773 { 774 CSSM_DATA certData = {}; 775 result = SecCertificateGetData(certificateRef, &certData); 776 if (result) 777 { 778 sec_perror("SecCertificateGetData", result); 779 goto cleanup; 780 } 781 782 print_buffer_pem(stdout, "CERTIFICATE", certData.Length, certData.Data); 783 } 784 else 785 { 786 print_keychain_item_attributes(stdout, (SecKeychainItemRef)certificateRef, FALSE, FALSE, FALSE, FALSE); 787 } 788 } while (find_all); 789 790cleanup: 791 safe_CFRelease(&searchRef); 792 safe_CFRelease(&certificateRef); 793 safe_CFRelease(&matchRef); 794 795 return result; 796} 797 798int 799keychain_delete_internet_password(int argc, char * const *argv) 800{ 801 char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL; 802 char *kind = NULL, *label = NULL, *comment = NULL; 803 FourCharCode itemCreator = 0, itemType = 0; 804 UInt16 port = 0; 805 SecProtocolType protocol = 0; 806 SecAuthenticationType authenticationType = 0; 807 CFTypeRef keychainOrArray = NULL; 808 int ch, result = 0; 809 810 /* 811 * " -a Match \"account\" string\n" 812 * " -c Match \"creator\" (four-character code)\n" 813 * " -C Match \"type\" (four-character code)\n" 814 * " -d Match \"securityDomain\" string\n" 815 * " -D Match \"kind\" string\n" 816 * " -j Match \"comment\" string\n" 817 * " -l Match \"label\" string\n" 818 * " -p Match \"path\" string\n" 819 * " -P Match port number\n" 820 * " -r Match \"protocol\" (four-character code)\n" 821 * " -s Match \"server\" string\n" 822 * " -t Match \"authenticationType\" (four-character code)\n" 823 */ 824 825 while ((ch = getopt(argc, argv, "ha:c:C:d:D:hgj:l:p:P:r:s:t:")) != -1) 826 { 827 switch (ch) 828 { 829 case 'a': 830 accountName = optarg; 831 break; 832 case 'c': 833 result = parse_fourcharcode(optarg, &itemCreator); 834 if (result) goto cleanup; 835 break; 836 case 'C': 837 result = parse_fourcharcode(optarg, &itemType); 838 if (result) goto cleanup; 839 break; 840 case 'd': 841 securityDomain = optarg; 842 break; 843 case 'D': 844 kind = optarg; 845 break; 846 case 'j': 847 comment = optarg; 848 break; 849 case 'l': 850 label = optarg; 851 break; 852 case 'p': 853 path = optarg; 854 break; 855 case 'P': 856 port = atoi(optarg); 857 break; 858 case 'r': 859 result = parse_fourcharcode(optarg, &protocol); 860 if (result) goto cleanup; 861 break; 862 case 's': 863 serverName = optarg; 864 break; 865 case 't': 866 result = parse_fourcharcode(optarg, &authenticationType); 867 if (result) goto cleanup; 868 break; 869 case '?': 870 default: 871 result = 2; /* @@@ Return 2 triggers usage message. */ 872 goto cleanup; 873 } 874 } 875 876 argc -= optind; 877 argv += optind; 878 879 keychainOrArray = keychain_create_array(argc, argv); 880 881 result = do_keychain_delete_internet_password(keychainOrArray, 882 itemCreator, 883 itemType, 884 kind, 885 comment, 886 label, 887 serverName, 888 securityDomain, 889 accountName, 890 path, 891 port, 892 protocol, 893 authenticationType); 894cleanup: 895 if (keychainOrArray) 896 CFRelease(keychainOrArray); 897 898 return result; 899} 900 901int 902keychain_find_internet_password(int argc, char * const *argv) 903{ 904 char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL; 905 char *kind = NULL, *label = NULL, *comment = NULL; 906 FourCharCode itemCreator = 0, itemType = 0; 907 UInt16 port = 0; 908 SecProtocolType protocol = 0; 909 SecAuthenticationType authenticationType = 0; 910 CFTypeRef keychainOrArray = NULL; 911 int ch, result = 0; 912 Boolean get_password = FALSE; 913 Boolean password_stdout = FALSE; 914 915 /* 916 * " -a Match \"account\" string\n" 917 * " -c Match \"creator\" (four-character code)\n" 918 * " -C Match \"type\" (four-character code)\n" 919 * " -d Match \"securityDomain\" string\n" 920 * " -D Match \"kind\" string\n" 921 * " -j Match \"comment\" string\n" 922 * " -l Match \"label\" string\n" 923 * " -p Match \"path\" string\n" 924 * " -P Match port number\n" 925 * " -r Match \"protocol\" (four-character code)\n" 926 * " -s Match \"server\" string\n" 927 * " -t Match \"authenticationType\" (four-character code)\n" 928 * " -g Display the password for the item found\n" 929 * " -w Display the password(only) for the item(s) found\n" 930 */ 931 932 while ((ch = getopt(argc, argv, "ha:c:C:d:D:hgj:l:p:P:r:s:wt:")) != -1) 933 { 934 switch (ch) 935 { 936 case 'a': 937 accountName = optarg; 938 break; 939 case 'c': 940 result = parse_fourcharcode(optarg, &itemCreator); 941 if (result) goto cleanup; 942 break; 943 case 'C': 944 result = parse_fourcharcode(optarg, &itemType); 945 if (result) goto cleanup; 946 break; 947 case 'd': 948 securityDomain = optarg; 949 break; 950 case 'D': 951 kind = optarg; 952 break; 953 case 'j': 954 comment = optarg; 955 break; 956 case 'l': 957 label = optarg; 958 break; 959 case 'g': 960 get_password = TRUE; 961 break; 962 case 'p': 963 path = optarg; 964 break; 965 case 'P': 966 port = atoi(optarg); 967 break; 968 case 'r': 969 result = parse_fourcharcode(optarg, &protocol); 970 if (result) goto cleanup; 971 break; 972 case 's': 973 serverName = optarg; 974 break; 975 case 'w': 976 get_password = TRUE; 977 password_stdout = TRUE; 978 break; 979 case 't': 980 result = parse_fourcharcode(optarg, &authenticationType); 981 if (result) goto cleanup; 982 break; 983 case '?': 984 default: 985 result = 2; /* @@@ Return 2 triggers usage message. */ 986 goto cleanup; 987 } 988 } 989 990 argc -= optind; 991 argv += optind; 992 993 keychainOrArray = keychain_create_array(argc, argv); 994 995 result = do_keychain_find_internet_password(keychainOrArray, 996 itemCreator, 997 itemType, 998 kind, 999 comment, 1000 label, 1001 serverName, 1002 securityDomain, 1003 accountName, 1004 path, 1005 port, 1006 protocol, 1007 authenticationType, 1008 get_password, 1009 password_stdout); 1010cleanup: 1011 if (keychainOrArray) 1012 CFRelease(keychainOrArray); 1013 1014 return result; 1015} 1016 1017int 1018keychain_delete_generic_password(int argc, char * const *argv) 1019{ 1020 char *serviceName = NULL, *accountName = NULL; 1021 char *kind = NULL, *label = NULL, *value = NULL, *comment = NULL; 1022 FourCharCode itemCreator = 0, itemType = 0; 1023 CFTypeRef keychainOrArray = nil; 1024 int ch, result = 0; 1025 1026 /* 1027 * " -a Match \"account\" string\n" 1028 * " -c Match \"creator\" (four-character code)\n" 1029 * " -C Match \"type\" (four-character code)\n" 1030 * " -D Match \"kind\" string\n" 1031 * " -G Match \"value\" string (generic attribute)\n" 1032 * " -j Match \"comment\" string\n" 1033 * " -l Match \"label\" string\n" 1034 * " -s Match \"service\" string\n" 1035 */ 1036 1037 while ((ch = getopt(argc, argv, "ha:c:C:D:G:j:l:s:g")) != -1) 1038 { 1039 switch (ch) 1040 { 1041 case 'a': 1042 accountName = optarg; 1043 break; 1044 case 'c': 1045 result = parse_fourcharcode(optarg, &itemCreator); 1046 if (result) goto cleanup; 1047 break; 1048 case 'C': 1049 result = parse_fourcharcode(optarg, &itemType); 1050 if (result) goto cleanup; 1051 break; 1052 case 'D': 1053 kind = optarg; 1054 break; 1055 case 'G': 1056 value = optarg; 1057 break; 1058 case 'j': 1059 comment = optarg; 1060 break; 1061 case 'l': 1062 label = optarg; 1063 break; 1064 case 's': 1065 serviceName = optarg; 1066 break; 1067 case '?': 1068 default: 1069 result = 2; /* @@@ Return 2 triggers usage message. */ 1070 goto cleanup; 1071 } 1072 } 1073 1074 argc -= optind; 1075 argv += optind; 1076 1077 keychainOrArray = keychain_create_array(argc, argv); 1078 1079 result = do_keychain_delete_generic_password(keychainOrArray, 1080 itemCreator, 1081 itemType, 1082 kind, 1083 value, 1084 comment, 1085 label, 1086 serviceName, 1087 accountName); 1088cleanup: 1089 if (keychainOrArray) 1090 CFRelease(keychainOrArray); 1091 1092 return result; 1093} 1094 1095int 1096keychain_find_generic_password(int argc, char * const *argv) 1097{ 1098 char *serviceName = NULL, *accountName = NULL; 1099 char *kind = NULL, *label = NULL, *value = NULL, *comment = NULL; 1100 FourCharCode itemCreator = 0, itemType = 0; 1101 CFTypeRef keychainOrArray = nil; 1102 int ch, result = 0; 1103 Boolean get_password = FALSE; 1104 Boolean password_stdout = FALSE; 1105 1106 /* 1107 * " -a Match \"account\" string\n" 1108 * " -c Match \"creator\" (four-character code)\n" 1109 * " -C Match \"type\" (four-character code)\n" 1110 * " -D Match \"kind\" string\n" 1111 * " -G Match \"value\" string (generic attribute)\n" 1112 * " -j Match \"comment\" string\n" 1113 * " -l Match \"label\" string\n" 1114 * " -s Match \"service\" string\n" 1115 * " -g Display the password for the item(s) found\n" 1116 * " -w Display the password(only) for the item(s) found\n" 1117 */ 1118 1119 while ((ch = getopt(argc, argv, "ha:c:C:D:G:j:l:s:wg")) != -1) 1120 { 1121 switch (ch) 1122 { 1123 case 'a': 1124 accountName = optarg; 1125 break; 1126 case 'c': 1127 result = parse_fourcharcode(optarg, &itemCreator); 1128 if (result) goto cleanup; 1129 break; 1130 case 'C': 1131 result = parse_fourcharcode(optarg, &itemType); 1132 if (result) goto cleanup; 1133 break; 1134 case 'D': 1135 kind = optarg; 1136 break; 1137 case 'G': 1138 value = optarg; 1139 break; 1140 case 'j': 1141 comment = optarg; 1142 break; 1143 case 'l': 1144 label = optarg; 1145 break; 1146 case 's': 1147 serviceName = optarg; 1148 break; 1149 case 'w': 1150 password_stdout = TRUE; 1151 get_password = TRUE; 1152 break; 1153 case 'g': 1154 get_password = TRUE; 1155 break; 1156 case '?': 1157 default: 1158 result = 2; /* @@@ Return 2 triggers usage message. */ 1159 goto cleanup; 1160 } 1161 } 1162 1163 argc -= optind; 1164 argv += optind; 1165 1166 keychainOrArray = keychain_create_array(argc, argv); 1167 1168 result = do_keychain_find_generic_password(keychainOrArray, 1169 itemCreator, 1170 itemType, 1171 kind, 1172 value, 1173 comment, 1174 label, 1175 serviceName, 1176 accountName, 1177 get_password, 1178 password_stdout); 1179cleanup: 1180 if (keychainOrArray) 1181 CFRelease(keychainOrArray); 1182 1183 return result; 1184} 1185 1186 1187int 1188keychain_find_certificate(int argc, char * const *argv) 1189{ 1190 char *emailAddress = NULL; 1191 char *name = NULL; 1192 int ch, result = 0; 1193 CFTypeRef keychainOrArray = nil; 1194 Boolean output_pem = FALSE; 1195 Boolean find_all = FALSE; 1196 Boolean print_hash = FALSE; 1197 Boolean print_email = FALSE; 1198 1199 while ((ch = getopt(argc, argv, "hac:e:mpZ")) != -1) 1200 { 1201 switch (ch) 1202 { 1203 case 'a': 1204 find_all = TRUE; 1205 break; 1206 case 'c': 1207 name = optarg; 1208 break; 1209 case 'e': 1210 emailAddress = optarg; 1211 break; 1212 case 'm': 1213 print_email = TRUE; 1214 break; 1215 case 'p': 1216 output_pem = TRUE; 1217 break; 1218 case 'Z': 1219 print_hash = TRUE; 1220 break; 1221 case '?': 1222 default: 1223 result = 2; /* @@@ Return 2 triggers usage message. */ 1224 goto cleanup; 1225 } 1226 } 1227 1228 argc -= optind; 1229 argv += optind; 1230 1231 keychainOrArray = keychain_create_array(argc, argv); 1232 1233 result = do_keychain_find_certificate(keychainOrArray, name, emailAddress, print_hash, output_pem, find_all, print_email); 1234 1235cleanup: 1236 if (keychainOrArray) 1237 CFRelease(keychainOrArray); 1238 1239 return result; 1240} 1241 1242 1243static int 1244do_keychain_dump_class(FILE *stream, CFTypeRef keychainOrArray, SecItemClass itemClass, Boolean show_data, Boolean show_raw_data, Boolean show_acl, Boolean interactive) 1245{ 1246 SecKeychainItemRef item; 1247 SecKeychainSearchRef search = NULL; 1248 int result = 0; 1249 OSStatus status; 1250 1251 status = SecKeychainSearchCreateFromAttributes(keychainOrArray, itemClass, NULL, &search); 1252 if (status) 1253 { 1254 sec_perror("SecKeychainSearchCreateFromAttributes", status); 1255 result = 1; 1256 goto cleanup; 1257 } 1258 1259 while ((status = SecKeychainSearchCopyNext(search, &item)) == 0) 1260 { 1261 print_keychain_item_attributes(stream, item, show_data, show_raw_data, show_acl, interactive); 1262 CFRelease(item); 1263 } 1264 1265 if (status != errSecItemNotFound) 1266 { 1267 sec_perror("SecKeychainSearchCopyNext", status); 1268 result = 1; 1269 goto cleanup; 1270 } 1271 1272cleanup: 1273 if (search) 1274 CFRelease(search); 1275 1276 return result; 1277} 1278 1279static int 1280do_keychain_dump(FILE *stream, CFTypeRef keychainOrArray, Boolean show_data, Boolean show_raw_data, Boolean show_acl, Boolean interactive) 1281{ 1282 return do_keychain_dump_class(stream, keychainOrArray, CSSM_DL_DB_RECORD_ANY, show_data, show_raw_data, show_acl, interactive); 1283} 1284 1285int 1286keychain_dump(int argc, char * const *argv) 1287{ 1288 int ch, result = 0; 1289 Boolean show_data = FALSE, show_raw_data = FALSE, show_acl = FALSE, interactive = FALSE; 1290 CFTypeRef keychainOrArray = NULL; 1291 const char *outputFilename = NULL; 1292 FILE *output; 1293 1294 while ((ch = getopt(argc, argv, "adhiro:")) != -1) 1295 { 1296 switch (ch) 1297 { 1298 case 'a': 1299 show_acl = TRUE; 1300 break; 1301 case 'd': 1302 show_data = TRUE; 1303 break; 1304 case 'i': 1305 show_acl = TRUE; 1306 interactive = TRUE; 1307 break; 1308 case 'r': 1309 show_raw_data = TRUE; 1310 break; 1311 case 'o': 1312 outputFilename = optarg; 1313 break; 1314 case '?': 1315 default: 1316 return 2; /* @@@ Return 2 triggers usage message. */ 1317 } 1318 } 1319 1320 argc -= optind; 1321 argv += optind; 1322 1323 keychainOrArray = keychain_create_array(argc, argv); 1324 1325 if (outputFilename) 1326 output = fopen(outputFilename, "w"); 1327 else 1328 output = stdout; 1329 1330 result = do_keychain_dump(output, keychainOrArray, show_data, show_raw_data, show_acl, interactive); 1331 1332 if (outputFilename) 1333 fclose(output); 1334 1335 if (keychainOrArray) 1336 CFRelease(keychainOrArray); 1337 1338 return result; 1339} 1340