keychain_find.c
1 /* 2 * Copyright (c) 2003-2010,2012-2019 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_cssm.h" 30 #include "security_tool.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/SecACL.h> 38 #include <Security/SecItem.h> 39 #include <Security/SecItemPriv.h> 40 #include <Security/SecKeychainItem.h> 41 #include <Security/SecKeychainItemPriv.h> 42 #include <Security/SecKeychainSearch.h> 43 #include <Security/SecCertificate.h> 44 #include <CommonCrypto/CommonDigest.h> 45 #include <CoreFoundation/CFString.h> 46 #include <ctype.h> 47 #include <utilities/SecCFRelease.h> 48 49 // SecDigestGetData, SecKeychainSearchCreateForCertificateByEmail, SecCertificateFindByEmail 50 #include <Security/SecCertificatePriv.h> 51 52 Boolean gDeleteIt = 0; 53 54 // find_first_generic_password 55 // 56 // Returns a SecKeychainItemRef for the first item 57 // which matches the specified attributes. Caller is 58 // responsible for releasing the item (with CFRelease). 59 // 60 SecKeychainItemRef 61 find_first_generic_password(CFTypeRef keychainOrArray, 62 FourCharCode itemCreator, 63 FourCharCode itemType, 64 const char *kind, 65 const char *value, 66 const char *comment, 67 const char *label, 68 const char *serviceName, 69 const char *accountName) 70 { 71 OSStatus status = noErr; 72 SecKeychainSearchRef searchRef = NULL; 73 SecKeychainItemRef itemRef = NULL; 74 75 SecKeychainAttribute attrs[8]; // maximum number of searchable attributes 76 SecKeychainAttributeList attrList = { 0, attrs }; 77 78 // the primary key for a generic password item (i.e. the combination of 79 // attributes which determine whether the item is unique) consists of: 80 // { kSecAccountItemAttr, kSecServiceItemAttr } 81 // 82 // if we have a primary key, we don't need to search on other attributes 83 // (and we don't want to, if non-primary attributes are being updated) 84 Boolean primaryKey = (accountName && serviceName); 85 86 // build the attribute list for searching 87 if ((UInt32)itemCreator != 0 && !primaryKey) { 88 attrs[attrList.count].tag = kSecCreatorItemAttr; 89 attrs[attrList.count].length = sizeof(FourCharCode); 90 attrs[attrList.count].data = (FourCharCode *)&itemCreator; 91 attrList.count++; 92 } 93 if ((UInt32)itemType != 0 && !primaryKey) { 94 attrs[attrList.count].tag = kSecTypeItemAttr; 95 attrs[attrList.count].length = sizeof(FourCharCode); 96 attrs[attrList.count].data = (FourCharCode *)&itemType; 97 attrList.count++; 98 } 99 if (kind != NULL && !primaryKey) { 100 attrs[attrList.count].tag = kSecDescriptionItemAttr; 101 attrs[attrList.count].length = (UInt32) strlen(kind); 102 attrs[attrList.count].data = (void*)kind; 103 attrList.count++; 104 } 105 if (value != NULL && !primaryKey) { 106 attrs[attrList.count].tag = kSecGenericItemAttr; 107 attrs[attrList.count].length = (UInt32) strlen(value); 108 attrs[attrList.count].data = (void*)value; 109 attrList.count++; 110 } 111 if (comment != NULL && !primaryKey) { 112 attrs[attrList.count].tag = kSecCommentItemAttr; 113 attrs[attrList.count].length = (UInt32) strlen(comment); 114 attrs[attrList.count].data = (void*)comment; 115 attrList.count++; 116 } 117 if (label != NULL && !primaryKey) { 118 attrs[attrList.count].tag = kSecLabelItemAttr; 119 attrs[attrList.count].length = (UInt32) strlen(label); 120 attrs[attrList.count].data = (void*)label; 121 attrList.count++; 122 } 123 if (serviceName != NULL) { 124 attrs[attrList.count].tag = kSecServiceItemAttr; 125 attrs[attrList.count].length = (UInt32) strlen(serviceName); 126 attrs[attrList.count].data = (void*)serviceName; 127 attrList.count++; 128 } 129 if (accountName != NULL) { 130 attrs[attrList.count].tag = kSecAccountItemAttr; 131 attrs[attrList.count].length = (UInt32) strlen(accountName); 132 attrs[attrList.count].data = (void*)accountName; 133 attrList.count++; 134 } 135 136 status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecGenericPasswordItemClass, &attrList, &searchRef); 137 if (status) { 138 sec_perror("SecKeychainSearchCreateFromAttributes", status); 139 goto cleanup; 140 } 141 // we're only interested in the first match, if there is a match at all 142 status = SecKeychainSearchCopyNext(searchRef, &itemRef); 143 if (status) { 144 itemRef = NULL; 145 } 146 147 cleanup: 148 if (searchRef) 149 CFRelease(searchRef); 150 151 return itemRef; 152 } 153 154 // find_first_internet_password 155 // 156 // Returns a SecKeychainItemRef for the first item 157 // which matches the specified attributes. Caller is 158 // responsible for releasing the item (with CFRelease). 159 // 160 SecKeychainItemRef 161 find_first_internet_password(CFTypeRef keychainOrArray, 162 FourCharCode itemCreator, 163 FourCharCode itemType, 164 const char *kind, 165 const char *comment, 166 const char *label, 167 const char *serverName, 168 const char *securityDomain, 169 const char *accountName, 170 const char *path, 171 UInt16 port, 172 SecProtocolType protocol, 173 SecAuthenticationType authenticationType) 174 { 175 OSStatus status = noErr; 176 SecKeychainSearchRef searchRef = NULL; 177 SecKeychainItemRef itemRef = NULL; 178 179 SecKeychainAttribute attrs[12]; // maximum number of searchable attributes 180 SecKeychainAttributeList attrList = { 0, attrs }; 181 182 // the primary key for an internet password item (i.e. the combination of 183 // attributes which determine whether the item is unique) consists of: 184 // { kSecAccountItemAttr, kSecSecurityDomainItemAttr, kSecServerItemAttr, 185 // kSecProtocolItemAttr, kSecAuthenticationTypeItemAttr, 186 // kSecPortItemAttr, kSecPathItemAttr } 187 // 188 // if we have a primary key, we don't need to search on other attributes. 189 // (and we don't want to, if non-primary attributes are being updated) 190 Boolean primaryKey = (accountName && securityDomain && serverName && 191 protocol && authenticationType && port && path); 192 193 // build the attribute list for searching 194 if ((UInt32)itemCreator != 0 && !primaryKey) { 195 attrs[attrList.count].tag = kSecCreatorItemAttr; 196 attrs[attrList.count].length = sizeof(FourCharCode); 197 attrs[attrList.count].data = (FourCharCode *)&itemCreator; 198 attrList.count++; 199 } 200 if ((UInt32)itemType != 0 && !primaryKey) { 201 attrs[attrList.count].tag = kSecTypeItemAttr; 202 attrs[attrList.count].length = sizeof(FourCharCode); 203 attrs[attrList.count].data = (FourCharCode *)&itemType; 204 attrList.count++; 205 } 206 if (kind != NULL && !primaryKey) { 207 attrs[attrList.count].tag = kSecDescriptionItemAttr; 208 attrs[attrList.count].length = (UInt32) strlen(kind); 209 attrs[attrList.count].data = (void*)kind; 210 attrList.count++; 211 } 212 if (comment != NULL && !primaryKey) { 213 attrs[attrList.count].tag = kSecCommentItemAttr; 214 attrs[attrList.count].length = (UInt32) strlen(comment); 215 attrs[attrList.count].data = (void*)comment; 216 attrList.count++; 217 } 218 if (label != NULL && !primaryKey) { 219 attrs[attrList.count].tag = kSecLabelItemAttr; 220 attrs[attrList.count].length = (UInt32) strlen(label); 221 attrs[attrList.count].data = (void*)label; 222 attrList.count++; 223 } 224 if (serverName != NULL) { 225 attrs[attrList.count].tag = kSecServerItemAttr; 226 attrs[attrList.count].length = (UInt32) strlen(serverName); 227 attrs[attrList.count].data = (void*)serverName; 228 attrList.count++; 229 } 230 if (securityDomain != NULL) { 231 attrs[attrList.count].tag = kSecSecurityDomainItemAttr; 232 attrs[attrList.count].length = (UInt32) strlen(securityDomain); 233 attrs[attrList.count].data = (void *)securityDomain; 234 attrList.count++; 235 } 236 if (accountName != NULL) { 237 attrs[attrList.count].tag = kSecAccountItemAttr; 238 attrs[attrList.count].length = (UInt32) strlen(accountName); 239 attrs[attrList.count].data = (void *)accountName; 240 attrList.count++; 241 } 242 if (path != NULL) { 243 attrs[attrList.count].tag = kSecPathItemAttr; 244 attrs[attrList.count].length = (UInt32) strlen(path); 245 attrs[attrList.count].data = (void *)path; 246 attrList.count++; 247 } 248 if (port != 0) { 249 attrs[attrList.count].tag = kSecPortItemAttr; 250 attrs[attrList.count].length = sizeof(UInt16); 251 attrs[attrList.count].data = (UInt16 *)&port; 252 attrList.count++; 253 } 254 if ((UInt32)protocol != 0) { 255 attrs[attrList.count].tag = kSecProtocolItemAttr; 256 attrs[attrList.count].length = sizeof(SecProtocolType); 257 attrs[attrList.count].data = (SecProtocolType *)&protocol; 258 attrList.count++; 259 } 260 if ((UInt32)authenticationType != 0) { 261 attrs[attrList.count].tag = kSecAuthenticationTypeItemAttr; 262 attrs[attrList.count].length = sizeof(SecAuthenticationType); 263 attrs[attrList.count].data = (SecAuthenticationType *)&authenticationType; 264 attrList.count++; 265 } 266 267 status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecInternetPasswordItemClass, &attrList, &searchRef); 268 if (status) { 269 sec_perror("SecKeychainSearchCreateFromAttributes", status); 270 goto cleanup; 271 } 272 // we're only interested in the first match, if there is a match at all 273 status = SecKeychainSearchCopyNext(searchRef, &itemRef); 274 if (status) { 275 itemRef = NULL; 276 } 277 278 cleanup: 279 if (searchRef) 280 CFRelease(searchRef); 281 282 return itemRef; 283 } 284 285 // find_unique_certificate 286 // 287 // Returns a SecKeychainItemRef for the certificate 288 // in the specified keychain (or keychain list) 289 // which is a unique match for either the specified name 290 // or SHA-1/SHA-2 hash. If more than one match exists, the 291 // certificate is not unique and none are returned. Caller is 292 // responsible for releasing the item (with CFRelease). 293 // 294 SecKeychainItemRef 295 find_unique_certificate(CFTypeRef keychainOrArray, 296 const char *name, 297 const char *hash) 298 { 299 OSStatus status = noErr; 300 SecKeychainSearchRef searchRef = NULL; 301 SecKeychainItemRef uniqueItemRef = NULL; 302 303 status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecCertificateItemClass, NULL, &searchRef); 304 if (status) { 305 return uniqueItemRef; 306 } 307 308 // check input hash string and convert to data 309 CFDataRef hashData = NULL; 310 CFIndex hashLength = 0; 311 if (hash) { 312 CSSM_DATA hashCssmData = { 0, NULL }; 313 hashCssmData.Length = (CSSM_SIZE)strlen(hash)/2; 314 hashCssmData.Data = (uint8 *)malloc(hashCssmData.Length); 315 fromHex(hash, &hashCssmData); 316 hashLength = (CFIndex)hashCssmData.Length; 317 hashData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)hashCssmData.Data, hashLength); 318 free(hashCssmData.Data); 319 } 320 321 // filter candidates against the hash (or the name, if no hash provided) 322 CFStringRef matchRef = (name) ? CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8) : NULL; 323 Boolean exactMatch = FALSE; 324 SecKeychainItemRef candidate = NULL; 325 326 while (SecKeychainSearchCopyNext(searchRef, &candidate) == noErr) { 327 SecCertificateRef cert = (SecCertificateRef)candidate; 328 if (hashData) { 329 CFDataRef certHash = NULL; 330 if (hashLength == CC_SHA1_DIGEST_LENGTH) { 331 certHash = SecCertificateGetSHA1Digest(cert); 332 if (certHash) { CFRetain(certHash); } 333 } else if (hashLength == CC_SHA256_DIGEST_LENGTH) { 334 certHash = SecCertificateCopySHA256Digest(cert); 335 } 336 if (!certHash) { 337 safe_CFRelease(&candidate); 338 continue; // no hash, so no match is possible 339 } else if (CFEqual(certHash, hashData)) { 340 exactMatch = TRUE; 341 } 342 safe_CFRelease(&certHash); 343 if (exactMatch) { 344 uniqueItemRef = candidate; // currently retained 345 break; // we're done - can't get more exact than this 346 } 347 } else { 348 // copy certificate name 349 CFStringRef nameRef = NULL; 350 if ((SecCertificateCopyCommonName(cert, &nameRef) != noErr) || nameRef == NULL) { 351 safe_CFRelease(&candidate); 352 continue; // no name, so no match is possible 353 } 354 CFIndex nameLen = CFStringGetLength(nameRef); 355 CFIndex bufLen = 1 + CFStringGetMaximumSizeForEncoding(nameLen, kCFStringEncodingUTF8); 356 char *nameBuf = (char *)malloc(bufLen); 357 if (!CFStringGetCString(nameRef, nameBuf, bufLen-1, kCFStringEncodingUTF8)) 358 nameBuf[0]=0; 359 360 CFRange find = { kCFNotFound, 0 }; 361 if (nameRef && matchRef) 362 find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral); 363 Boolean isExact = (find.location == 0 && find.length == nameLen); 364 if (find.location == kCFNotFound) { 365 free(nameBuf); 366 safe_CFRelease(&nameRef); 367 safe_CFRelease(&candidate); 368 continue; // no match 369 } 370 if (uniqueItemRef) { // got two matches 371 if (exactMatch && !isExact) { // prior is better; ignore this one 372 free(nameBuf); 373 safe_CFRelease(&nameRef); 374 safe_CFRelease(&candidate); 375 continue; 376 } 377 if (exactMatch == isExact) { // same class of match 378 if (CFEqual(uniqueItemRef, candidate)) { // same certificate 379 free(nameBuf); 380 safe_CFRelease(&nameRef); 381 safe_CFRelease(&candidate); 382 continue; 383 } 384 // ambiguity - must fail 385 sec_error("\"%s\" is ambiguous, matches more than one certificate", name); 386 free(nameBuf); 387 safe_CFRelease(&nameRef); 388 safe_CFRelease(&candidate); 389 safe_CFRelease(&uniqueItemRef); 390 break; 391 } 392 safe_CFRelease(&uniqueItemRef); // about to replace with this one 393 } 394 uniqueItemRef = candidate; // currently retained 395 exactMatch = isExact; 396 free(nameBuf); 397 safe_CFRelease(&nameRef); 398 } 399 } 400 401 safe_CFRelease(&searchRef); 402 safe_CFRelease(&matchRef); 403 safe_CFRelease(&hashData); 404 405 return uniqueItemRef; 406 } 407 408 static OSStatus 409 do_password_item_printing(SecKeychainItemRef itemRef, 410 Boolean get_password, 411 Boolean password_stdout) 412 { 413 OSStatus result = noErr; 414 void *passwordData = NULL; 415 UInt32 passwordLength = 0; 416 417 if(get_password) { 418 result = SecKeychainItemCopyContent(itemRef, NULL, NULL, &passwordLength, &passwordData); 419 if(result != noErr) return result; 420 } 421 if(!password_stdout) { 422 print_keychain_item_attributes(stdout, itemRef, FALSE, FALSE, FALSE, FALSE); 423 if(get_password) { 424 fputs("password: ", stderr); 425 print_buffer(stderr, passwordLength, passwordData); 426 fputc('\n', stderr); 427 } 428 } else { 429 uint8_t *password = (uint8_t *) passwordData; 430 int doHex = 0; 431 for(uint32_t i=0; i<passwordLength; i++) if(!isprint(password[i])) doHex = 1; 432 if(doHex) { 433 for(uint32_t i=0; i<passwordLength; i++) printf("%02x", password[i]); 434 } else { 435 for(uint32_t i=0; i<passwordLength; i++) putchar(password[i]); 436 } 437 putchar('\n'); 438 } 439 440 if (passwordData) SecKeychainItemFreeContent(NULL, passwordData); 441 return noErr; 442 443 } 444 445 static int 446 do_keychain_find_generic_password(CFTypeRef keychainOrArray, 447 FourCharCode itemCreator, 448 FourCharCode itemType, 449 const char *kind, 450 const char *value, 451 const char *comment, 452 const char *label, 453 const char *serviceName, 454 const char *accountName, 455 Boolean get_password, 456 Boolean password_stdout) 457 { 458 OSStatus result = noErr; 459 SecKeychainItemRef itemRef = NULL; 460 461 itemRef = find_first_generic_password(keychainOrArray, 462 itemCreator, 463 itemType, 464 kind, 465 value, 466 comment, 467 label, 468 serviceName, 469 accountName); 470 471 if(itemRef) { 472 result = do_password_item_printing(itemRef, get_password, password_stdout); 473 } else { 474 result = errSecItemNotFound; 475 sec_perror("SecKeychainSearchCopyNext", result); 476 } 477 478 if (itemRef) CFRelease(itemRef); 479 480 return result; 481 } 482 483 static int 484 do_keychain_delete_generic_password(CFTypeRef keychainOrArray, 485 FourCharCode itemCreator, 486 FourCharCode itemType, 487 const char *kind, 488 const char *value, 489 const char *comment, 490 const char *label, 491 const char *serviceName, 492 const char *accountName) 493 { 494 OSStatus result = noErr; 495 SecKeychainItemRef itemRef = NULL; 496 void *passwordData = NULL; 497 498 if (!itemCreator && !itemType && !kind && !value && !comment && !label && !serviceName && !accountName) { 499 return SHOW_USAGE_MESSAGE; 500 } 501 502 itemRef = find_first_generic_password(keychainOrArray, 503 itemCreator, 504 itemType, 505 kind, 506 value, 507 comment, 508 label, 509 serviceName, 510 accountName); 511 if (!itemRef) { 512 result = errSecItemNotFound; 513 sec_perror("SecKeychainSearchCopyNext", result); 514 goto cleanup; 515 } 516 517 print_keychain_item_attributes(stdout, itemRef, FALSE, FALSE, FALSE, FALSE); 518 519 result = SecKeychainItemDelete(itemRef); 520 521 fputs("password has been deleted.\n", stderr); 522 523 cleanup: 524 if (passwordData) 525 SecKeychainItemFreeContent(NULL, passwordData); 526 if (itemRef) 527 CFRelease(itemRef); 528 529 return result; 530 } 531 532 static int 533 do_keychain_find_internet_password(CFTypeRef keychainOrArray, 534 FourCharCode itemCreator, 535 FourCharCode itemType, 536 const char *kind, 537 const char *comment, 538 const char *label, 539 const char *serverName, 540 const char *securityDomain, 541 const char *accountName, 542 const char *path, 543 UInt16 port, 544 SecProtocolType protocol, 545 SecAuthenticationType authenticationType, 546 Boolean get_password, 547 Boolean password_stdout) 548 { 549 OSStatus result = noErr; 550 SecKeychainItemRef itemRef = NULL; 551 552 itemRef = find_first_internet_password(keychainOrArray, 553 itemCreator, 554 itemType, 555 kind, 556 comment, 557 label, 558 serverName, 559 securityDomain, 560 accountName, 561 path, 562 port, 563 protocol, 564 authenticationType); 565 if(itemRef) { 566 result = do_password_item_printing(itemRef, get_password, password_stdout); 567 } else { 568 result = errSecItemNotFound; 569 sec_perror("SecKeychainSearchCopyNext", result); 570 } 571 572 return result; 573 } 574 575 static int 576 do_keychain_delete_internet_password(CFTypeRef keychainOrArray, 577 FourCharCode itemCreator, 578 FourCharCode itemType, 579 const char *kind, 580 const char *comment, 581 const char *label, 582 const char *serverName, 583 const char *securityDomain, 584 const char *accountName, 585 const char *path, 586 UInt16 port, 587 SecProtocolType protocol, 588 SecAuthenticationType authenticationType) 589 { 590 OSStatus result = noErr; 591 SecKeychainItemRef itemRef = NULL; 592 void *passwordData = NULL; 593 594 if (!itemCreator && !itemType && !kind && !comment && !label && !serverName && !securityDomain && !accountName && !path && !port && !protocol && !authenticationType) { 595 return SHOW_USAGE_MESSAGE; 596 } 597 598 itemRef = find_first_internet_password(keychainOrArray, 599 itemCreator, 600 itemType, 601 kind, 602 comment, 603 label, 604 serverName, 605 securityDomain, 606 accountName, 607 path, 608 port, 609 protocol, 610 authenticationType); 611 if (!itemRef) { 612 result = errSecItemNotFound; 613 sec_perror("SecKeychainSearchCopyNext", result); 614 goto cleanup; 615 } 616 617 print_keychain_item_attributes(stdout, itemRef, FALSE, FALSE, FALSE, FALSE); 618 619 result = SecKeychainItemDelete(itemRef); 620 621 fputs("password has been deleted.\n", stderr); 622 623 cleanup: 624 if (passwordData) 625 SecKeychainItemFreeContent(NULL, passwordData); 626 if (itemRef) 627 CFRelease(itemRef); 628 629 return result; 630 } 631 632 static int 633 do_keychain_find_certificate(CFTypeRef keychainOrArray, 634 const char *name, 635 const char *emailAddress, 636 Boolean print_hash, 637 Boolean output_pem, 638 Boolean find_all, 639 Boolean print_email) 640 { 641 OSStatus result = noErr; 642 SecCertificateRef certificateRef = NULL; 643 SecKeychainSearchRef searchRef = NULL; 644 CFStringRef matchRef = (name) ? CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8) : NULL; 645 646 if (find_all && emailAddress) { 647 result = SecKeychainSearchCreateForCertificateByEmail(keychainOrArray, emailAddress, &searchRef); 648 if (result) { 649 sec_perror("SecKeychainSearchCreateForCertificateByEmail", result); 650 goto cleanup; 651 } 652 } else { 653 result = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecCertificateItemClass, NULL, &searchRef); 654 if (result) { 655 sec_perror("SecKeychainSearchCreateFromAttributes", result); 656 goto cleanup; 657 } 658 } 659 660 do 661 { 662 if (find_all) { 663 SecKeychainItemRef itemRef = NULL; 664 result = SecKeychainSearchCopyNext(searchRef, &itemRef); 665 if (result == errSecItemNotFound) { 666 result = 0; 667 break; 668 } 669 else if (result) { 670 sec_perror("SecKeychainSearchCopyNext", result); 671 goto cleanup; 672 } 673 674 if (!emailAddress && name) { 675 // match name in common name 676 CFStringRef nameRef = NULL; 677 if (SecCertificateCopyCommonName((SecCertificateRef)itemRef, &nameRef) != noErr) { 678 safe_CFRelease(&itemRef); 679 continue; // no name, so no match is possible 680 } 681 CFRange find = { kCFNotFound, 0 }; 682 if (nameRef && matchRef) 683 find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral); 684 if (find.location == kCFNotFound) { 685 safe_CFRelease(&nameRef); 686 safe_CFRelease(&itemRef); 687 continue; // no match 688 } 689 safe_CFRelease(&nameRef); 690 } 691 safe_CFRelease(&certificateRef); 692 certificateRef = (SecCertificateRef) itemRef; 693 } 694 else { // only want the first match 695 if (emailAddress) { 696 result = SecCertificateFindByEmail(keychainOrArray, emailAddress, &certificateRef); 697 if (result) { 698 sec_perror("SecCertificateFindByEmail", result); 699 goto cleanup; 700 } 701 } else { 702 SecKeychainItemRef itemRef = NULL; 703 while ((result = SecKeychainSearchCopyNext(searchRef, &itemRef)) != errSecItemNotFound) { 704 if (name) { 705 // match name in common name 706 CFStringRef nameRef = NULL; 707 if (SecCertificateCopyCommonName((SecCertificateRef)itemRef, &nameRef) != noErr) { 708 safe_CFRelease(&itemRef); 709 continue; // no name, so no match is possible 710 } 711 CFRange find = { kCFNotFound, 0 }; 712 if (nameRef && matchRef) 713 find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral); 714 if (find.location == kCFNotFound) { 715 safe_CFRelease(&nameRef); 716 safe_CFRelease(&itemRef); 717 continue; // no match 718 } 719 safe_CFRelease(&nameRef); 720 } 721 break; // we have a match! 722 } 723 if (result == errSecItemNotFound) { 724 sec_perror("SecKeychainSearchCopyNext", result); 725 goto cleanup; 726 } 727 certificateRef = (SecCertificateRef) itemRef; 728 } 729 } 730 731 // process the found certificate 732 733 if (print_hash) { 734 unsigned char *p; 735 CFIndex i, count; 736 CFDataRef sha256Digest = SecCertificateCopySHA256Digest(certificateRef); 737 if (sha256Digest != NULL && 738 (count = (CFIndex)CFDataGetLength(sha256Digest)) == CC_SHA256_DIGEST_LENGTH && 739 (p = (unsigned char *)CFDataGetBytePtr(sha256Digest)) != NULL) { 740 // print SHA-256 hash value 741 fprintf(stdout, "SHA-256 hash: "); 742 for (i=0; i<count; i++) { fprintf(stdout, "%02X", p[i]); } 743 fprintf(stdout, "\n"); 744 } 745 safe_CFRelease(&sha256Digest); 746 CFDataRef sha1Digest = SecCertificateGetSHA1Digest(certificateRef); 747 if (sha1Digest != NULL && 748 (count = (CFIndex)CFDataGetLength(sha1Digest)) == CC_SHA1_DIGEST_LENGTH && 749 (p = (unsigned char *)CFDataGetBytePtr(sha1Digest)) != NULL) { 750 // print SHA-1 hash value for compatibility 751 fprintf(stdout, "SHA-1 hash: "); 752 for (i=0; i<count; i++) { fprintf(stdout, "%02X", p[i]); } 753 fprintf(stdout, "\n"); 754 } 755 } 756 757 if (print_email) 758 { 759 CFArrayRef emailAddresses = NULL; 760 CFIndex ix, count; 761 result = SecCertificateCopyEmailAddresses(certificateRef, &emailAddresses); 762 if (result) 763 { 764 sec_perror("SecCertificateCopyEmailAddresses", result); 765 goto cleanup; 766 } 767 768 count = CFArrayGetCount(emailAddresses); 769 fputs("email addresses: ", stdout); 770 for (ix = 0; ix < count; ++ix) 771 { 772 CFStringRef emailAddress = (CFStringRef)CFArrayGetValueAtIndex(emailAddresses, ix); 773 const char *addr; 774 char buffer[256]; 775 776 if (ix) 777 fputs(", ", stdout); 778 779 addr = CFStringGetCStringPtr(emailAddress, kCFStringEncodingUTF8); 780 if (!addr) 781 { 782 if (CFStringGetCString(emailAddress, buffer, sizeof(buffer), kCFStringEncodingUTF8)) 783 addr = buffer; 784 } 785 786 fprintf(stdout, "%s", addr); 787 } 788 fputc('\n', stdout); 789 790 CFRelease(emailAddresses); 791 } 792 793 if (output_pem) 794 { 795 CSSM_DATA certData = {}; 796 result = SecCertificateGetData(certificateRef, &certData); 797 if (result) 798 { 799 sec_perror("SecCertificateGetData", result); 800 goto cleanup; 801 } 802 803 print_buffer_pem(stdout, "CERTIFICATE", certData.Length, certData.Data); 804 } 805 else 806 { 807 print_keychain_item_attributes(stdout, (SecKeychainItemRef)certificateRef, FALSE, FALSE, FALSE, FALSE); 808 } 809 } while (find_all); 810 811 cleanup: 812 safe_CFRelease(&searchRef); 813 safe_CFRelease(&certificateRef); 814 safe_CFRelease(&matchRef); 815 816 return result; 817 } 818 819 int 820 keychain_delete_internet_password(int argc, char * const *argv) 821 { 822 char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL; 823 char *kind = NULL, *label = NULL, *comment = NULL; 824 FourCharCode itemCreator = 0, itemType = 0; 825 UInt16 port = 0; 826 SecProtocolType protocol = 0; 827 SecAuthenticationType authenticationType = 0; 828 CFTypeRef keychainOrArray = NULL; 829 int ch, result = 0; 830 831 /* 832 * " -a Match \"account\" string\n" 833 * " -c Match \"creator\" (four-character code)\n" 834 * " -C Match \"type\" (four-character code)\n" 835 * " -d Match \"securityDomain\" string\n" 836 * " -D Match \"kind\" string\n" 837 * " -j Match \"comment\" string\n" 838 * " -l Match \"label\" string\n" 839 * " -p Match \"path\" string\n" 840 * " -P Match port number\n" 841 * " -r Match \"protocol\" (four-character code)\n" 842 * " -s Match \"server\" string\n" 843 * " -t Match \"authenticationType\" (four-character code)\n" 844 */ 845 846 while ((ch = getopt(argc, argv, "ha:c:C:d:D:hgj:l:p:P:r:s:t:")) != -1) 847 { 848 switch (ch) 849 { 850 case 'a': 851 accountName = optarg; 852 break; 853 case 'c': 854 result = parse_fourcharcode(optarg, &itemCreator); 855 if (result) goto cleanup; 856 break; 857 case 'C': 858 result = parse_fourcharcode(optarg, &itemType); 859 if (result) goto cleanup; 860 break; 861 case 'd': 862 securityDomain = optarg; 863 break; 864 case 'D': 865 kind = optarg; 866 break; 867 case 'j': 868 comment = optarg; 869 break; 870 case 'l': 871 label = optarg; 872 break; 873 case 'p': 874 path = optarg; 875 break; 876 case 'P': 877 port = atoi(optarg); 878 break; 879 case 'r': 880 result = parse_fourcharcode(optarg, &protocol); 881 if (result) goto cleanup; 882 break; 883 case 's': 884 serverName = optarg; 885 break; 886 case 't': 887 result = parse_fourcharcode(optarg, &authenticationType); 888 if (result) goto cleanup; 889 break; 890 case '?': 891 default: 892 result = 2; /* @@@ Return 2 triggers usage message. */ 893 goto cleanup; 894 } 895 } 896 897 argc -= optind; 898 argv += optind; 899 900 keychainOrArray = keychain_create_array(argc, argv); 901 902 result = do_keychain_delete_internet_password(keychainOrArray, 903 itemCreator, 904 itemType, 905 kind, 906 comment, 907 label, 908 serverName, 909 securityDomain, 910 accountName, 911 path, 912 port, 913 protocol, 914 authenticationType); 915 cleanup: 916 if (keychainOrArray) 917 CFRelease(keychainOrArray); 918 919 return result; 920 } 921 922 int 923 keychain_find_internet_password(int argc, char * const *argv) 924 { 925 char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL; 926 char *kind = NULL, *label = NULL, *comment = NULL; 927 FourCharCode itemCreator = 0, itemType = 0; 928 UInt16 port = 0; 929 SecProtocolType protocol = 0; 930 SecAuthenticationType authenticationType = 0; 931 CFTypeRef keychainOrArray = NULL; 932 int ch, result = 0; 933 Boolean get_password = FALSE; 934 Boolean password_stdout = FALSE; 935 936 /* 937 * " -a Match \"account\" string\n" 938 * " -c Match \"creator\" (four-character code)\n" 939 * " -C Match \"type\" (four-character code)\n" 940 * " -d Match \"securityDomain\" string\n" 941 * " -D Match \"kind\" string\n" 942 * " -j Match \"comment\" string\n" 943 * " -l Match \"label\" string\n" 944 * " -p Match \"path\" string\n" 945 * " -P Match port number\n" 946 * " -r Match \"protocol\" (four-character code)\n" 947 * " -s Match \"server\" string\n" 948 * " -t Match \"authenticationType\" (four-character code)\n" 949 * " -g Display the password for the item found\n" 950 * " -w Display the password(only) for the item(s) found\n" 951 */ 952 953 while ((ch = getopt(argc, argv, "ha:c:C:d:D:hgj:l:p:P:r:s:wt:")) != -1) 954 { 955 switch (ch) 956 { 957 case 'a': 958 accountName = optarg; 959 break; 960 case 'c': 961 result = parse_fourcharcode(optarg, &itemCreator); 962 if (result) goto cleanup; 963 break; 964 case 'C': 965 result = parse_fourcharcode(optarg, &itemType); 966 if (result) goto cleanup; 967 break; 968 case 'd': 969 securityDomain = optarg; 970 break; 971 case 'D': 972 kind = optarg; 973 break; 974 case 'j': 975 comment = optarg; 976 break; 977 case 'l': 978 label = optarg; 979 break; 980 case 'g': 981 get_password = TRUE; 982 break; 983 case 'p': 984 path = optarg; 985 break; 986 case 'P': 987 port = atoi(optarg); 988 break; 989 case 'r': 990 result = parse_fourcharcode(optarg, &protocol); 991 if (result) goto cleanup; 992 break; 993 case 's': 994 serverName = optarg; 995 break; 996 case 'w': 997 get_password = TRUE; 998 password_stdout = TRUE; 999 break; 1000 case 't': 1001 result = parse_fourcharcode(optarg, &authenticationType); 1002 if (result) goto cleanup; 1003 /* auth type attribute is special */ 1004 authenticationType = OSSwapHostToBigInt32(authenticationType); 1005 break; 1006 case '?': 1007 default: 1008 result = 2; /* @@@ Return 2 triggers usage message. */ 1009 goto cleanup; 1010 } 1011 } 1012 1013 argc -= optind; 1014 argv += optind; 1015 1016 keychainOrArray = keychain_create_array(argc, argv); 1017 1018 result = do_keychain_find_internet_password(keychainOrArray, 1019 itemCreator, 1020 itemType, 1021 kind, 1022 comment, 1023 label, 1024 serverName, 1025 securityDomain, 1026 accountName, 1027 path, 1028 port, 1029 protocol, 1030 authenticationType, 1031 get_password, 1032 password_stdout); 1033 cleanup: 1034 if (keychainOrArray) 1035 CFRelease(keychainOrArray); 1036 1037 return result; 1038 } 1039 1040 int 1041 keychain_delete_generic_password(int argc, char * const *argv) 1042 { 1043 char *serviceName = NULL, *accountName = NULL; 1044 char *kind = NULL, *label = NULL, *value = NULL, *comment = NULL; 1045 FourCharCode itemCreator = 0, itemType = 0; 1046 CFTypeRef keychainOrArray = nil; 1047 int ch, result = 0; 1048 1049 /* 1050 * " -a Match \"account\" string\n" 1051 * " -c Match \"creator\" (four-character code)\n" 1052 * " -C Match \"type\" (four-character code)\n" 1053 * " -D Match \"kind\" string\n" 1054 * " -G Match \"value\" string (generic attribute)\n" 1055 * " -j Match \"comment\" string\n" 1056 * " -l Match \"label\" string\n" 1057 * " -s Match \"service\" string\n" 1058 */ 1059 1060 while ((ch = getopt(argc, argv, "ha:c:C:D:G:j:l:s:g")) != -1) 1061 { 1062 switch (ch) 1063 { 1064 case 'a': 1065 accountName = optarg; 1066 break; 1067 case 'c': 1068 result = parse_fourcharcode(optarg, &itemCreator); 1069 if (result) goto cleanup; 1070 break; 1071 case 'C': 1072 result = parse_fourcharcode(optarg, &itemType); 1073 if (result) goto cleanup; 1074 break; 1075 case 'D': 1076 kind = optarg; 1077 break; 1078 case 'G': 1079 value = optarg; 1080 break; 1081 case 'j': 1082 comment = optarg; 1083 break; 1084 case 'l': 1085 label = optarg; 1086 break; 1087 case 's': 1088 serviceName = optarg; 1089 break; 1090 case '?': 1091 default: 1092 result = 2; /* @@@ Return 2 triggers usage message. */ 1093 goto cleanup; 1094 } 1095 } 1096 1097 argc -= optind; 1098 argv += optind; 1099 1100 keychainOrArray = keychain_create_array(argc, argv); 1101 1102 result = do_keychain_delete_generic_password(keychainOrArray, 1103 itemCreator, 1104 itemType, 1105 kind, 1106 value, 1107 comment, 1108 label, 1109 serviceName, 1110 accountName); 1111 cleanup: 1112 if (keychainOrArray) 1113 CFRelease(keychainOrArray); 1114 1115 return result; 1116 } 1117 1118 int 1119 keychain_find_generic_password(int argc, char * const *argv) 1120 { 1121 char *serviceName = NULL, *accountName = NULL; 1122 char *kind = NULL, *label = NULL, *value = NULL, *comment = NULL; 1123 FourCharCode itemCreator = 0, itemType = 0; 1124 CFTypeRef keychainOrArray = nil; 1125 int ch, result = 0; 1126 Boolean get_password = FALSE; 1127 Boolean password_stdout = FALSE; 1128 1129 /* 1130 * " -a Match \"account\" string\n" 1131 * " -c Match \"creator\" (four-character code)\n" 1132 * " -C Match \"type\" (four-character code)\n" 1133 * " -D Match \"kind\" string\n" 1134 * " -G Match \"value\" string (generic attribute)\n" 1135 * " -j Match \"comment\" string\n" 1136 * " -l Match \"label\" string\n" 1137 * " -s Match \"service\" string\n" 1138 * " -g Display the password for the item(s) found\n" 1139 * " -w Display the password(only) for the item(s) found\n" 1140 */ 1141 1142 while ((ch = getopt(argc, argv, "ha:c:C:D:G:j:l:s:wg")) != -1) 1143 { 1144 switch (ch) 1145 { 1146 case 'a': 1147 accountName = optarg; 1148 break; 1149 case 'c': 1150 result = parse_fourcharcode(optarg, &itemCreator); 1151 if (result) goto cleanup; 1152 break; 1153 case 'C': 1154 result = parse_fourcharcode(optarg, &itemType); 1155 if (result) goto cleanup; 1156 break; 1157 case 'D': 1158 kind = optarg; 1159 break; 1160 case 'G': 1161 value = optarg; 1162 break; 1163 case 'j': 1164 comment = optarg; 1165 break; 1166 case 'l': 1167 label = optarg; 1168 break; 1169 case 's': 1170 serviceName = optarg; 1171 break; 1172 case 'w': 1173 password_stdout = TRUE; 1174 get_password = TRUE; 1175 break; 1176 case 'g': 1177 get_password = TRUE; 1178 break; 1179 case '?': 1180 default: 1181 result = 2; /* @@@ Return 2 triggers usage message. */ 1182 goto cleanup; 1183 } 1184 } 1185 1186 argc -= optind; 1187 argv += optind; 1188 1189 keychainOrArray = keychain_create_array(argc, argv); 1190 1191 result = do_keychain_find_generic_password(keychainOrArray, 1192 itemCreator, 1193 itemType, 1194 kind, 1195 value, 1196 comment, 1197 label, 1198 serviceName, 1199 accountName, 1200 get_password, 1201 password_stdout); 1202 cleanup: 1203 if (keychainOrArray) 1204 CFRelease(keychainOrArray); 1205 1206 return result; 1207 } 1208 1209 #define SetKeyToString(dict, key, arg) \ 1210 { \ 1211 CFStringRef str = CFStringCreateWithCStringNoCopy(NULL, arg, kCFStringEncodingUTF8, kCFAllocatorNull); \ 1212 CFDictionarySetValue(dict, key, str); \ 1213 CFReleaseNull(str); \ 1214 } 1215 1216 int 1217 keychain_find_key(int argc, char * const *argv) { 1218 /* 1219 * " -a Match \"application label\" string\n" 1220 * " -c Match \"creator\" (four-character code)\n" 1221 * " -d Match keys that can decrypt\n" 1222 * " -D Match \"description\" string\n" 1223 * " -e Match keys that can encrypt\n" 1224 * " -j Match \"comment\" string\n" 1225 * " -l Match \"label\" string\n" 1226 * " -r Match keys that can derive\n" 1227 * " -s Match keys that can sign\n" 1228 * " -t Type of key to find: one of \"symmetric\", \"public\", or \"private\"\n" 1229 * " -u Match keys that can unwrap\n" 1230 * " -v Match keys that can verify\n" 1231 * " -w Match keys that can wrap\n" 1232 */ 1233 1234 CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1235 CFDictionarySetValue(query, kSecClass, kSecClassKey); 1236 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll); 1237 1238 CFTypeRef results = NULL; 1239 1240 int ch, result = 0; 1241 while ((ch = getopt(argc, argv, "a:c:dD:ej:l:rst:uvw")) != -1) 1242 { 1243 switch (ch) 1244 { 1245 case 'a': 1246 SetKeyToString(query, kSecAttrApplicationLabel, optarg); 1247 break; 1248 case 'c': 1249 SetKeyToString(query, kSecAttrCreator, optarg); 1250 break; 1251 case 'd': 1252 CFDictionarySetValue(query, kSecAttrCanDecrypt, kCFBooleanTrue); 1253 break; 1254 case 'D': 1255 SetKeyToString(query, kSecAttrDescription, optarg); 1256 break; 1257 case 'e': 1258 CFDictionarySetValue(query, kSecAttrCanEncrypt, kCFBooleanTrue); 1259 break; 1260 case 'j': 1261 SetKeyToString(query, kSecAttrComment, optarg); 1262 break; 1263 case 'l': 1264 SetKeyToString(query, kSecAttrLabel, optarg); 1265 break; 1266 case 'r': 1267 CFDictionarySetValue(query, kSecAttrCanDerive, kCFBooleanTrue); 1268 break; 1269 case 's': 1270 CFDictionarySetValue(query, kSecAttrCanSign, kCFBooleanTrue); 1271 break; 1272 case 't': 1273 if(strcmp(optarg, "symmetric") == 0) { 1274 CFDictionarySetValue(query, kSecAttrKeyClass, kSecAttrKeyClassSymmetric); 1275 } else if(strcmp(optarg, "public") == 0) { 1276 CFDictionarySetValue(query, kSecAttrKeyClass, kSecAttrKeyClassPublic); 1277 } else if(strcmp(optarg, "private") == 0) { 1278 CFDictionarySetValue(query, kSecAttrKeyClass, kSecAttrKeyClassPrivate); 1279 } else { 1280 result = 2; 1281 goto cleanup; 1282 } 1283 break; 1284 case 'u': 1285 CFDictionarySetValue(query, kSecAttrCanUnwrap, kCFBooleanTrue); 1286 break; 1287 case 'v': 1288 CFDictionarySetValue(query, kSecAttrCanVerify, kCFBooleanTrue); 1289 break; 1290 case 'w': 1291 CFDictionarySetValue(query, kSecAttrCanWrap, kCFBooleanTrue); 1292 break; 1293 case '?': 1294 default: 1295 result = 2; 1296 goto cleanup; 1297 } 1298 } 1299 1300 argc -= optind; 1301 argv += optind; 1302 1303 CFTypeRef keychainOrArray = keychain_create_array(argc, argv); 1304 1305 if(keychainOrArray && CFGetTypeID(keychainOrArray) == CFArrayGetTypeID()) { 1306 CFDictionarySetValue(query, kSecMatchSearchList, keychainOrArray); 1307 } else if(keychainOrArray) { 1308 // if it's not an array (but is something), it's a keychain. Put it in an array. 1309 CFMutableArrayRef searchList = (CFMutableArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 1, &kCFTypeArrayCallBacks); 1310 CFArrayAppendValue((CFMutableArrayRef)searchList, keychainOrArray); 1311 CFDictionarySetValue(query, kSecMatchSearchList, searchList); 1312 CFRelease(searchList); 1313 } 1314 CFReleaseNull(keychainOrArray); 1315 1316 OSStatus status = SecItemCopyMatching(query, &results); 1317 if(status) { 1318 sec_perror("SecItemCopyMatching", status); 1319 result = 1; 1320 goto cleanup; 1321 } 1322 1323 if (CFGetTypeID(results) == CFArrayGetTypeID()) { 1324 for(int i = 0; i < CFArrayGetCount(results); i++) { 1325 SecKeychainItemRef item = (SecKeychainItemRef) CFArrayGetValueAtIndex(results, i); 1326 1327 print_keychain_item_attributes(stdout, item, FALSE, FALSE, FALSE, FALSE); 1328 } 1329 } 1330 1331 cleanup: 1332 safe_CFRelease(&results); 1333 safe_CFRelease(&query); 1334 return result; 1335 } 1336 1337 // Declare here to use later. 1338 int keychain_set_partition_list(SecKeychainRef kc, CFDictionaryRef query, CFStringRef password, CFStringRef partitionlist); 1339 int keychain_parse_args_and_set_partition_list(int argc, char * const *argv, CFMutableDictionaryRef query, CFStringRef partitionidsinput, CFStringRef password); 1340 1341 int keychain_set_internet_password_partition_list(int argc, char * const *argv) { 1342 /* 1343 * " -a Match \"account\" string\n" 1344 * " -c Match \"creator\" (four-character code)\n" 1345 * " -C Match \"type\" (four-character code)\n" 1346 * " -d Match \"securityDomain\" string\n" 1347 * " -D Match \"kind\" string\n" 1348 * " -j Match \"comment\" string\n" 1349 * " -l Match \"label\" string\n" 1350 * " -p Match \"path\" string\n" 1351 * " -P Match port number\n" 1352 * " -r Match \"protocol\" (four-character code)\n" 1353 * " -s Match \"server\" string\n" 1354 * " -t Match \"authenticationType\" (four-character code)\n" 1355 * " -S Comma-separated list of allowed partition IDs" 1356 * " -k password for keychain" 1357 */ 1358 1359 CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1360 CFDictionarySetValue(query, kSecClass, kSecClassInternetPassword); 1361 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll); 1362 1363 CFStringRef partitionidsinput = NULL; 1364 CFStringRef password = NULL; 1365 1366 int ch, result = 0; 1367 while ((ch = getopt(argc, argv, "a:c:C:d:D:j:l:p:P:r:s:S:t:k:")) != -1) 1368 { 1369 switch (ch) 1370 { 1371 case 'a': 1372 SetKeyToString(query, kSecAttrAccount, optarg); 1373 break; 1374 case 'c': 1375 SetKeyToString(query, kSecAttrCreator, optarg); 1376 break; 1377 case 'C': 1378 SetKeyToString(query, kSecAttrType, optarg); 1379 break; 1380 case 'd': 1381 SetKeyToString(query, kSecAttrSecurityDomain, optarg); 1382 break; 1383 case 'D': 1384 SetKeyToString(query, kSecAttrDescription, optarg); 1385 break; 1386 case 'j': 1387 SetKeyToString(query, kSecAttrComment, optarg); 1388 break; 1389 case 'l': 1390 SetKeyToString(query, kSecAttrLabel, optarg); 1391 break; 1392 case 'p': 1393 SetKeyToString(query, kSecAttrPath, optarg); 1394 break; 1395 case 'P': 1396 SetKeyToString(query, kSecAttrPort, optarg); 1397 break; 1398 case 'r': 1399 SetKeyToString(query, kSecAttrProtocol, optarg); 1400 break; 1401 case 's': 1402 SetKeyToString(query, kSecAttrServer, optarg); 1403 break; 1404 case 't': 1405 SetKeyToString(query, kSecAttrAuthenticationType, optarg); 1406 break; 1407 case 'S': 1408 CFReleaseNull(partitionidsinput); 1409 partitionidsinput = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull); 1410 break; 1411 case 'k': 1412 CFReleaseNull(password); 1413 password = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull); 1414 break; 1415 case '?': 1416 default: 1417 result = 2; 1418 goto cleanup; 1419 } 1420 } 1421 1422 argc -= optind; 1423 argv += optind; 1424 1425 result = keychain_parse_args_and_set_partition_list(argc, argv, query, partitionidsinput, password); 1426 1427 cleanup: 1428 safe_CFRelease(&password); 1429 safe_CFRelease(&partitionidsinput); 1430 safe_CFRelease(&query); 1431 return result; 1432 } 1433 1434 int 1435 keychain_set_generic_password_partition_list(int argc, char * const *argv) { 1436 CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1437 1438 // -a Match \"account\" string 1439 // -c Match \"creator\" (four-character code) 1440 // -C Match \"type\" (four-character code) 1441 // -D Match \"kind\" string 1442 // -G Match \"value\" string (generic attribute) 1443 // -j Match \"comment\" string 1444 // -l Match \"label\" string 1445 // -s Match \"service\" string 1446 // -S Comma-separated list of allowed partition IDs 1447 // -k password for keychain 1448 1449 CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword); 1450 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll); 1451 1452 CFStringRef partitionidsinput = NULL; 1453 CFStringRef password = NULL; 1454 1455 int ch, result = 0; 1456 while ((ch = getopt(argc, argv, ":a:c:C:D:G:j:l:s:S:k:")) != -1) 1457 { 1458 switch (ch) 1459 { 1460 case 'a': 1461 SetKeyToString(query, kSecAttrAccount, optarg); 1462 break; 1463 case 'c': 1464 SetKeyToString(query, kSecAttrCreator, optarg); 1465 break; 1466 case 'C': 1467 SetKeyToString(query, kSecAttrType, optarg); 1468 break; 1469 case 'D': 1470 SetKeyToString(query, kSecAttrDescription, optarg); 1471 break; 1472 case 'G': 1473 SetKeyToString(query, kSecAttrGeneric, optarg); 1474 break; 1475 case 'j': 1476 SetKeyToString(query, kSecAttrComment, optarg); 1477 break; 1478 case 'l': 1479 SetKeyToString(query, kSecAttrLabel, optarg); 1480 break; 1481 case 's': 1482 SetKeyToString(query, kSecAttrService, optarg); 1483 break; 1484 case 'S': 1485 CFReleaseNull(partitionidsinput); 1486 partitionidsinput = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull); 1487 break; 1488 case 'k': 1489 CFReleaseNull(password); 1490 password = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull); 1491 break; 1492 case '?': 1493 case ':': 1494 // They supplied the -k but with no data 1495 // Leaving it null will cause prompt below 1496 if (optopt == 'k') { 1497 break; 1498 } 1499 result = 2; 1500 goto cleanup; /* @@@ Return 2 triggers usage message. */ 1501 default: 1502 result = 2; 1503 goto cleanup; 1504 } 1505 } 1506 1507 argc -= optind; 1508 argv += optind; 1509 1510 result = keychain_parse_args_and_set_partition_list(argc, argv, query, partitionidsinput, password); 1511 1512 cleanup: 1513 safe_CFRelease(&password); 1514 safe_CFRelease(&partitionidsinput); 1515 safe_CFRelease(&query); 1516 return result; 1517 } 1518 1519 int 1520 keychain_set_key_partition_list(int argc, char * const *argv) { 1521 /* 1522 * " -a Match \"application label\" string\n" 1523 * " -c Match \"creator\" (four-character code)\n" 1524 * " -d Match keys that can decrypt\n" 1525 * " -D Match \"description\" string\n" 1526 * " -e Match keys that can encrypt\n" 1527 * " -j Match \"comment\" string\n" 1528 * " -l Match \"label\" string\n" 1529 * " -r Match keys that can derive\n" 1530 * " -s Match keys that can sign\n" 1531 * " -t Type of key to find: one of \"symmetric\", \"public\", or \"private\"\n" 1532 * " -u Match keys that can unwrap\n" 1533 * " -v Match keys that can verify\n" 1534 * " -w Match keys that can wrap\n" 1535 * " -S Comma-separated list of allowed partition IDs 1536 * " -k password for keychain (required) 1537 */ 1538 1539 CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1540 CFDictionarySetValue(query, kSecClass, kSecClassKey); 1541 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll); 1542 1543 CFStringRef partitionidsinput = NULL; 1544 CFStringRef password = NULL; 1545 1546 int ch, result = 0; 1547 while ((ch = getopt(argc, argv, ":a:c:dD:ej:k:l:rsS:t:uvw")) != -1) 1548 { 1549 switch (ch) 1550 { 1551 case 'a': 1552 SetKeyToString(query, kSecAttrApplicationLabel, optarg); 1553 break; 1554 case 'c': 1555 SetKeyToString(query, kSecAttrCreator, optarg); 1556 break; 1557 case 'd': 1558 SetKeyToString(query, kSecAttrCanDecrypt, optarg); 1559 CFDictionarySetValue(query, kSecAttrCanDecrypt, kCFBooleanTrue); 1560 break; 1561 case 'D': 1562 SetKeyToString(query, kSecAttrDescription, optarg); 1563 break; 1564 case 'e': 1565 CFDictionarySetValue(query, kSecAttrCanEncrypt, kCFBooleanTrue); 1566 break; 1567 case 'j': 1568 SetKeyToString(query, kSecAttrComment, optarg); 1569 break; 1570 case 'l': 1571 SetKeyToString(query, kSecAttrLabel, optarg); 1572 break; 1573 case 'r': 1574 CFDictionarySetValue(query, kSecAttrCanDerive, kCFBooleanTrue); 1575 case 's': 1576 CFDictionarySetValue(query, kSecAttrCanSign, kCFBooleanTrue); 1577 break; 1578 case 't': 1579 if(strcmp(optarg, "symmetric") == 0) { 1580 CFDictionarySetValue(query, kSecAttrKeyClass, kSecAttrKeyClassSymmetric); 1581 } else if(strcmp(optarg, "public") == 0) { 1582 CFDictionarySetValue(query, kSecAttrKeyClass, kSecAttrKeyClassPublic); 1583 } else if(strcmp(optarg, "private") == 0) { 1584 CFDictionarySetValue(query, kSecAttrKeyClass, kSecAttrKeyClassPrivate); 1585 } else { 1586 result = 2; 1587 goto cleanup; 1588 } 1589 break; 1590 case 'u': 1591 CFDictionarySetValue(query, kSecAttrCanUnwrap, kCFBooleanTrue); 1592 break; 1593 case 'v': 1594 CFDictionarySetValue(query, kSecAttrCanVerify, kCFBooleanTrue); 1595 break; 1596 case 'w': 1597 CFDictionarySetValue(query, kSecAttrCanWrap, kCFBooleanTrue); 1598 break; 1599 case 'S': 1600 CFReleaseNull(partitionidsinput); 1601 partitionidsinput = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull); 1602 break; 1603 case 'k': 1604 CFReleaseNull(password); 1605 password = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull); 1606 break; 1607 case '?': 1608 case ':': 1609 // They supplied the -k but with no data 1610 // Leaving it null will cause prompt below 1611 if (optopt == 'k') { 1612 break; 1613 } 1614 result = 2; 1615 goto cleanup; /* @@@ Return 2 triggers usage message. */ 1616 default: 1617 result = 2; 1618 goto cleanup; 1619 } 1620 } 1621 1622 argc -= optind; 1623 argv += optind; 1624 1625 result = keychain_parse_args_and_set_partition_list(argc, argv, query, partitionidsinput, password); 1626 1627 cleanup: 1628 CFReleaseNull(partitionidsinput); 1629 CFReleaseNull(password); 1630 safe_CFRelease(&query); 1631 return result; 1632 } 1633 1634 1635 int keychain_parse_args_and_set_partition_list(int argc, char * const *argv, CFMutableDictionaryRef query, CFStringRef partitionidsinput, CFStringRef password) { 1636 int result = 0; 1637 const char *keychainName = NULL; 1638 SecKeychainRef kc = NULL; 1639 CFStringRef localPassword = NULL; 1640 1641 // if we were given a keychain, use it 1642 if (argc == 1) 1643 { 1644 keychainName = argv[0]; 1645 if (*keychainName == '\0') 1646 { 1647 result = 2; 1648 goto cleanup; 1649 } 1650 1651 kc = keychain_open(keychainName); 1652 if(!kc) { 1653 sec_error("couldn't open \"%s\"", keychainName); 1654 result = 1; 1655 goto cleanup; 1656 } 1657 1658 CFMutableArrayRef searchList = (CFMutableArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 1, &kCFTypeArrayCallBacks); 1659 CFArrayAppendValue((CFMutableArrayRef)searchList, kc); 1660 CFDictionarySetValue(query, kSecMatchSearchList, searchList); 1661 } else if (argc != 0) { 1662 result = 2; 1663 goto cleanup; 1664 } 1665 1666 if(!partitionidsinput) { 1667 result = 2; 1668 goto cleanup; 1669 } 1670 1671 if(!password) { 1672 char* cpassword = prompt_password(keychainName); 1673 if (!cpassword) { 1674 result = -1; 1675 goto cleanup; 1676 } 1677 localPassword = CFStringCreateWithCString(NULL, cpassword, kCFStringEncodingUTF8); 1678 password = localPassword; 1679 free(cpassword); 1680 } 1681 1682 result = keychain_set_partition_list(kc, query, password, partitionidsinput); 1683 1684 cleanup: 1685 CFReleaseNull(localPassword); 1686 return result; 1687 } 1688 1689 1690 int keychain_set_partition_list(SecKeychainRef kc, CFDictionaryRef query, CFStringRef password, CFStringRef partitionlist) { 1691 int result = 0; 1692 1693 char *passwordBuf = NULL; 1694 size_t passwordLen; 1695 GetCStringFromCFString(password, &passwordBuf, &passwordLen); 1696 1697 OSStatus status; 1698 CFTypeRef results = NULL; 1699 1700 // Unlock the keychain with the given password, since we'll be fetching ACLs 1701 status = SecKeychainUnlock(kc, (UInt32) passwordLen, passwordBuf, true); 1702 if(status) { 1703 sec_perror("SecKeychainUnlock", status); 1704 result = 1; 1705 goto cleanup; 1706 } 1707 1708 status = SecItemCopyMatching(query, &results); 1709 if(status) { 1710 sec_perror("SecItemCopyMatching", status); 1711 result = 1; 1712 goto cleanup; 1713 } 1714 1715 if(!results) { 1716 result = 0; 1717 goto cleanup; 1718 } 1719 1720 if (CFGetTypeID(results) == CFArrayGetTypeID()) { 1721 for(int i = 0; i < CFArrayGetCount(results); i++) { 1722 SecKeychainItemRef item = (SecKeychainItemRef) CFArrayGetValueAtIndex(results, i); 1723 SecAccessRef access = NULL; 1724 1725 do_password_item_printing(item, false, false); 1726 1727 status = SecKeychainItemCopyAccess(item, &access); 1728 if (status == errSecNoAccessForItem) { 1729 continue; 1730 } 1731 if(status) { 1732 sec_perror("SecKeychainItemCopyAccess", status); 1733 result = 1; 1734 goto cleanup; 1735 } 1736 1737 CFArrayRef aclList = NULL; 1738 status = SecAccessCopyACLList(access, &aclList); 1739 if (status) 1740 { 1741 sec_perror("SecAccessCopyACLList", status); 1742 result = 1; 1743 goto cleanup; 1744 } 1745 1746 CFIndex size = CFArrayGetCount(aclList); 1747 for(CFIndex i = 0; i < size; i++) { 1748 SecACLRef acl = (SecACLRef) CFArrayGetValueAtIndex(aclList, i); 1749 CSSM_ACL_AUTHORIZATION_TAG tags[64]; // Pick some upper limit 1750 uint32 tagix, tagCount = sizeof(tags) / sizeof(*tags); 1751 status = SecACLGetAuthorizations(acl, tags, &tagCount); 1752 1753 if (status) 1754 { 1755 sec_perror("SecACLGetAuthorizations", status); 1756 result = 1; 1757 goto cleanup; 1758 } 1759 1760 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector = {}; 1761 1762 for (tagix = 0; tagix < tagCount; ++tagix) 1763 { 1764 CSSM_ACL_AUTHORIZATION_TAG tag = tags[tagix]; 1765 if(tag == CSSM_ACL_AUTHORIZATION_PARTITION_ID) { 1766 1767 CFArrayRef applicationList; 1768 CFStringRef promptDescription; 1769 1770 status = SecACLCopySimpleContents(acl, &applicationList, &promptDescription, &promptSelector); 1771 if(status) { 1772 sec_perror("SecACLCopySimpleContents", status); 1773 result = 1; 1774 goto cleanup; 1775 } 1776 1777 CFArrayRef partitionIDs = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, partitionlist, CFSTR(",")); 1778 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1779 CFDictionarySetValue(dict, CFSTR("Partitions"), partitionIDs); 1780 CFDataRef xml = CFPropertyListCreateXMLData(NULL, dict); 1781 CFStringRef xmlstr = cfToHex(xml); 1782 1783 SecACLSetSimpleContents(acl, applicationList, xmlstr, &promptSelector); 1784 1785 safe_CFRelease(&xmlstr); 1786 safe_CFRelease(&xml); 1787 safe_CFRelease(&dict); 1788 safe_CFRelease(&partitionIDs); 1789 } 1790 } 1791 } 1792 1793 status = SecKeychainItemSetAccessWithPassword(item, access, (UInt32) passwordLen, passwordBuf); 1794 if(status) { 1795 sec_perror("SecKeychainItemSetAccessWithPassword", status); 1796 result = 1; 1797 goto cleanup; 1798 } 1799 } 1800 } 1801 1802 result = 0; 1803 1804 cleanup: 1805 if(passwordBuf) { 1806 free(passwordBuf); 1807 } 1808 safe_CFRelease(&results); 1809 1810 return result; 1811 } 1812 1813 1814 1815 int 1816 keychain_find_certificate(int argc, char * const *argv) 1817 { 1818 char *emailAddress = NULL; 1819 char *name = NULL; 1820 int ch, result = 0; 1821 CFTypeRef keychainOrArray = nil; 1822 Boolean output_pem = FALSE; 1823 Boolean find_all = FALSE; 1824 Boolean print_hash = FALSE; 1825 Boolean print_email = FALSE; 1826 1827 while ((ch = getopt(argc, argv, "hac:e:mpZ")) != -1) 1828 { 1829 switch (ch) 1830 { 1831 case 'a': 1832 find_all = TRUE; 1833 break; 1834 case 'c': 1835 name = optarg; 1836 break; 1837 case 'e': 1838 emailAddress = optarg; 1839 break; 1840 case 'm': 1841 print_email = TRUE; 1842 break; 1843 case 'p': 1844 output_pem = TRUE; 1845 break; 1846 case 'Z': 1847 print_hash = TRUE; 1848 break; 1849 case '?': 1850 default: 1851 result = 2; /* @@@ Return 2 triggers usage message. */ 1852 goto cleanup; 1853 } 1854 } 1855 1856 argc -= optind; 1857 argv += optind; 1858 1859 keychainOrArray = keychain_create_array(argc, argv); 1860 1861 result = do_keychain_find_certificate(keychainOrArray, name, emailAddress, print_hash, output_pem, find_all, print_email); 1862 1863 cleanup: 1864 if (keychainOrArray) 1865 CFRelease(keychainOrArray); 1866 1867 return result; 1868 } 1869 1870 1871 static int 1872 do_keychain_dump_class(FILE *stream, CFTypeRef keychainOrArray, SecItemClass itemClass, Boolean show_data, Boolean show_raw_data, Boolean show_acl, Boolean interactive) 1873 { 1874 SecKeychainItemRef item; 1875 SecKeychainSearchRef search = NULL; 1876 int result = 0; 1877 OSStatus status; 1878 1879 status = SecKeychainSearchCreateFromAttributes(keychainOrArray, itemClass, NULL, &search); 1880 if (status) 1881 { 1882 sec_perror("SecKeychainSearchCreateFromAttributes", status); 1883 result = 1; 1884 goto cleanup; 1885 } 1886 1887 while ((status = SecKeychainSearchCopyNext(search, &item)) == 0) 1888 { 1889 print_keychain_item_attributes(stream, item, show_data, show_raw_data, show_acl, interactive); 1890 CFRelease(item); 1891 } 1892 1893 if (status != errSecItemNotFound) 1894 { 1895 sec_perror("SecKeychainSearchCopyNext", status); 1896 result = 1; 1897 goto cleanup; 1898 } 1899 1900 cleanup: 1901 if (search) 1902 CFRelease(search); 1903 1904 return result; 1905 } 1906 1907 static int 1908 do_keychain_dump(FILE *stream, CFTypeRef keychainOrArray, Boolean show_data, Boolean show_raw_data, Boolean show_acl, Boolean interactive) 1909 { 1910 return do_keychain_dump_class(stream, keychainOrArray, CSSM_DL_DB_RECORD_ANY, show_data, show_raw_data, show_acl, interactive); 1911 } 1912 1913 int 1914 keychain_dump(int argc, char * const *argv) 1915 { 1916 int ch, result = 0; 1917 Boolean show_data = FALSE, show_raw_data = FALSE, show_acl = FALSE, interactive = FALSE; 1918 CFTypeRef keychainOrArray = NULL; 1919 const char *outputFilename = NULL; 1920 FILE *output; 1921 1922 while ((ch = getopt(argc, argv, "adhiro:")) != -1) 1923 { 1924 switch (ch) 1925 { 1926 case 'a': 1927 show_acl = TRUE; 1928 break; 1929 case 'd': 1930 show_data = TRUE; 1931 break; 1932 case 'i': 1933 show_acl = TRUE; 1934 interactive = TRUE; 1935 break; 1936 case 'r': 1937 show_raw_data = TRUE; 1938 break; 1939 case 'o': 1940 outputFilename = optarg; 1941 break; 1942 case '?': 1943 default: 1944 return SHOW_USAGE_MESSAGE; 1945 } 1946 } 1947 1948 argc -= optind; 1949 argv += optind; 1950 1951 keychainOrArray = keychain_create_array(argc, argv); 1952 1953 if (outputFilename) 1954 output = fopen(outputFilename, "w"); 1955 else 1956 output = stdout; 1957 1958 result = do_keychain_dump(output, keychainOrArray, show_data, show_raw_data, show_acl, interactive); 1959 1960 if (outputFilename) 1961 fclose(output); 1962 1963 if (keychainOrArray) 1964 CFRelease(keychainOrArray); 1965 1966 return result; 1967 }