keychain_add.c
1 /* 2 * Copyright (c) 2003-2009,2011-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_add.c 24 */ 25 26 #include "keychain_add.h" 27 #include "keychain_find.h" 28 #include "readline_cssm.h" 29 #include "security_tool.h" 30 #include "access_utils.h" 31 #include "keychain_utilities.h" 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 #include <AssertMacros.h> 37 #include <libkern/OSByteOrder.h> 38 #include <Security/SecAccess.h> 39 #include <Security/SecCertificate.h> 40 #include <Security/SecKeychain.h> 41 #include <Security/SecKeychainItem.h> 42 #include <Security/SecTrustedApplication.h> 43 44 // SecTrustedApplicationCreateApplicationGroup 45 #include <Security/SecTrustedApplicationPriv.h> 46 47 48 static int 49 do_update_generic_password(const char *keychainName, 50 FourCharCode itemCreator, 51 FourCharCode itemType, 52 const char *kind, 53 const char *value, 54 const char *comment, 55 const char *label, 56 const char *serviceName, 57 const char *accountName, 58 const void *passwordData, 59 uint32_t passwordLength, 60 SecAccessRef access) 61 { 62 OSStatus status; 63 SecKeychainRef keychainRef = NULL; 64 SecKeychainItemRef itemRef = NULL; 65 66 if (keychainName) { 67 keychainRef = keychain_open(keychainName); 68 } 69 itemRef = find_first_generic_password(keychainRef,itemCreator,itemType,kind,value,comment,label,serviceName,accountName); 70 if (keychainRef) { 71 CFRelease(keychainRef); 72 } 73 if (!itemRef) { 74 return errSecItemNotFound; 75 } 76 77 // build list of attributes 78 SecKeychainAttribute attrs[8]; // maximum number of attributes 79 SecKeychainAttributeList attrList = { 0, attrs }; 80 81 if ((UInt32)itemCreator != 0) { 82 attrs[attrList.count].tag = kSecCreatorItemAttr; 83 attrs[attrList.count].length = sizeof(FourCharCode); 84 attrs[attrList.count].data = (FourCharCode *)&itemCreator; 85 attrList.count++; 86 } 87 if ((UInt32)itemType != 0) { 88 attrs[attrList.count].tag = kSecTypeItemAttr; 89 attrs[attrList.count].length = sizeof(FourCharCode); 90 attrs[attrList.count].data = (FourCharCode *)&itemType; 91 attrList.count++; 92 } 93 if (kind != NULL) { 94 attrs[attrList.count].tag = kSecDescriptionItemAttr; 95 attrs[attrList.count].length = (UInt32) strlen(kind); 96 attrs[attrList.count].data = (void *)kind; 97 attrList.count++; 98 } 99 if (value != NULL) { 100 attrs[attrList.count].tag = kSecGenericItemAttr; 101 attrs[attrList.count].length = (UInt32) strlen(value); 102 attrs[attrList.count].data = (void *)value; 103 attrList.count++; 104 } 105 if (comment != NULL) { 106 attrs[attrList.count].tag = kSecCommentItemAttr; 107 attrs[attrList.count].length = (UInt32) strlen(comment); 108 attrs[attrList.count].data = (void *)comment; 109 attrList.count++; 110 } 111 if (label != NULL) { 112 attrs[attrList.count].tag = kSecLabelItemAttr; 113 attrs[attrList.count].length = (UInt32) strlen(label); 114 attrs[attrList.count].data = (void *)label; 115 attrList.count++; 116 } 117 if (serviceName != NULL) { 118 attrs[attrList.count].tag = kSecServiceItemAttr; 119 attrs[attrList.count].length = (UInt32) strlen(serviceName); 120 attrs[attrList.count].data = (void *)serviceName; 121 attrList.count++; 122 } 123 if (accountName != NULL) { 124 attrs[attrList.count].tag = kSecAccountItemAttr; 125 attrs[attrList.count].length = (UInt32) strlen(accountName); 126 attrs[attrList.count].data = (void *)accountName; 127 attrList.count++; 128 } 129 130 // modify attributes and password data, if provided 131 status = SecKeychainItemModifyContent(itemRef, &attrList, passwordLength, passwordData); 132 if (status) { 133 sec_error("SecKeychainItemModifyContent: %s", sec_errstr(status)); 134 } 135 136 // modify access, if provided 137 if (!status && access) { 138 SecAccessRef curAccess = NULL; 139 // for historical reasons, we have to modify the item's existing access reference 140 // (setting the item's access to a freshly created SecAccessRef instance doesn't behave as expected) 141 status = SecKeychainItemCopyAccess(itemRef, &curAccess); 142 if (status) { 143 sec_error("SecKeychainItemCopyAccess: %s", sec_errstr(status)); 144 } else { 145 int result = merge_access(curAccess, access); // make changes to the existing access reference 146 if (result == noErr) { 147 status = SecKeychainItemSetAccess(itemRef, curAccess); // will prompt 148 if (status) { 149 sec_error("SecKeychainItemSetAccess: %s", sec_errstr(status)); 150 } 151 } 152 } 153 if (curAccess) { 154 CFRelease(curAccess); 155 } 156 } 157 158 CFRelease(itemRef); 159 160 return status; 161 } 162 163 static int 164 do_add_generic_password(const char *keychainName, 165 FourCharCode itemCreator, 166 FourCharCode itemType, 167 const char *kind, 168 const char *value, 169 const char *comment, 170 const char *label, 171 const char *serviceName, 172 const char *accountName, 173 const void *passwordData, 174 uint32_t passwordLength, 175 SecAccessRef access, 176 Boolean update) 177 { 178 SecKeychainRef keychain = NULL; 179 OSStatus result = 0; 180 SecKeychainItemRef itemRef = NULL; 181 182 // if update flag is specified, try to find and update an existing item 183 if (update) { 184 result = do_update_generic_password(keychainName,itemCreator,itemType,kind,value, 185 comment,label,serviceName,accountName,passwordData,passwordLength,access); 186 if (result == noErr) 187 return result; 188 } 189 190 if (keychainName) 191 { 192 keychain = keychain_open(keychainName); 193 if (!keychain) 194 { 195 result = 1; 196 goto cleanup; 197 } 198 } 199 200 // set up attribute vector for new item (each attribute consists of {tag, length, pointer}) 201 SecKeychainAttribute attrs[] = { 202 { kSecLabelItemAttr, label ? (UInt32) strlen(label) : 0, (char *)label }, 203 { kSecDescriptionItemAttr, kind ? (UInt32) strlen(kind) : 0, (char *)kind }, 204 { kSecGenericItemAttr, value ? (UInt32) strlen(value) : 0, (char *)value }, 205 { kSecCommentItemAttr, comment ? (UInt32) strlen(comment) : 0, (char *)comment }, 206 { kSecServiceItemAttr, serviceName ? (UInt32) strlen(serviceName) : 0, (char *)serviceName }, 207 { kSecAccountItemAttr, accountName ? (UInt32) strlen(accountName) : 0, (char *)accountName }, 208 { (SecKeychainAttrType)0, sizeof(FourCharCode), NULL }, /* placeholder */ 209 { (SecKeychainAttrType)0, sizeof(FourCharCode), NULL } /* placeholder */ 210 }; 211 SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs }; 212 attributes.count -= 2; 213 214 if (itemCreator != 0) 215 { 216 attrs[attributes.count].tag = kSecCreatorItemAttr; 217 attrs[attributes.count].data = (FourCharCode *)&itemCreator; 218 attributes.count++; 219 } 220 if (itemType != 0) 221 { 222 attrs[attributes.count].tag = kSecTypeItemAttr; 223 attrs[attributes.count].data = (FourCharCode *)&itemType; 224 attributes.count++; 225 } 226 227 result = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, 228 &attributes, 229 passwordLength, 230 passwordData, 231 keychain, 232 access, 233 &itemRef); 234 235 if (result) 236 { 237 sec_error("SecKeychainItemCreateFromContent (%s): %s", keychainName ? keychainName : "<default>", sec_errstr(result)); 238 } 239 240 cleanup: 241 if (itemRef) 242 CFRelease(itemRef); 243 if (keychain) 244 CFRelease(keychain); 245 246 return result; 247 } 248 249 static int 250 do_update_internet_password(const char *keychainName, 251 FourCharCode itemCreator, 252 FourCharCode itemType, 253 const char *kind, 254 const char *comment, 255 const char *label, 256 const char *serverName, 257 const char *securityDomain, 258 const char *accountName, 259 const char *path, 260 UInt16 port, 261 SecProtocolType protocol, 262 SecAuthenticationType authenticationType, 263 const void *passwordData, 264 uint32_t passwordLength, 265 SecAccessRef access) 266 { 267 OSStatus status; 268 SecKeychainRef keychainRef = NULL; 269 SecKeychainItemRef itemRef = NULL; 270 271 if (keychainName) { 272 keychainRef = keychain_open(keychainName); 273 } 274 itemRef = find_first_internet_password(keychainRef,itemCreator,itemType,kind,comment,label,serverName, 275 securityDomain,accountName,path,port,protocol,authenticationType); 276 if (keychainRef) { 277 CFRelease(keychainRef); 278 } 279 if (!itemRef) { 280 return errSecItemNotFound; 281 } 282 283 // build list of attributes 284 SecKeychainAttribute attrs[12]; // maximum number of attributes 285 SecKeychainAttributeList attrList = { 0, attrs }; 286 287 if ((UInt32)itemCreator != 0) { 288 attrs[attrList.count].tag = kSecCreatorItemAttr; 289 attrs[attrList.count].length = sizeof(FourCharCode); 290 attrs[attrList.count].data = (FourCharCode *)&itemCreator; 291 attrList.count++; 292 } 293 if ((UInt32)itemType != 0) { 294 attrs[attrList.count].tag = kSecTypeItemAttr; 295 attrs[attrList.count].length = sizeof(FourCharCode); 296 attrs[attrList.count].data = (FourCharCode *)&itemType; 297 attrList.count++; 298 } 299 if (kind != NULL) { 300 attrs[attrList.count].tag = kSecDescriptionItemAttr; 301 attrs[attrList.count].length = (UInt32) strlen(kind); 302 attrs[attrList.count].data = (void *)kind; 303 attrList.count++; 304 } 305 if (comment != NULL) { 306 attrs[attrList.count].tag = kSecCommentItemAttr; 307 attrs[attrList.count].length = (UInt32) strlen(comment); 308 attrs[attrList.count].data = (void *)comment; 309 attrList.count++; 310 } 311 if (label != NULL) { 312 attrs[attrList.count].tag = kSecLabelItemAttr; 313 attrs[attrList.count].length = (UInt32) strlen(label); 314 attrs[attrList.count].data = (void *)label; 315 attrList.count++; 316 } 317 if (serverName != NULL) { 318 attrs[attrList.count].tag = kSecServerItemAttr; 319 attrs[attrList.count].length = (UInt32) strlen(serverName); 320 attrs[attrList.count].data = (void *)serverName; 321 attrList.count++; 322 } 323 if (securityDomain != NULL) { 324 attrs[attrList.count].tag = kSecSecurityDomainItemAttr; 325 attrs[attrList.count].length = (UInt32) strlen(securityDomain); 326 attrs[attrList.count].data = (void *)securityDomain; 327 attrList.count++; 328 } 329 if (accountName != NULL) { 330 attrs[attrList.count].tag = kSecAccountItemAttr; 331 attrs[attrList.count].length = (UInt32) strlen(accountName); 332 attrs[attrList.count].data = (void *)accountName; 333 attrList.count++; 334 } 335 if (path != NULL) { 336 attrs[attrList.count].tag = kSecPathItemAttr; 337 attrs[attrList.count].length = (UInt32) strlen(path); 338 attrs[attrList.count].data = (void *)path; 339 attrList.count++; 340 } 341 if (port != 0) { 342 attrs[attrList.count].tag = kSecPortItemAttr; 343 attrs[attrList.count].length = sizeof(UInt16); 344 attrs[attrList.count].data = (UInt16 *)&port; 345 attrList.count++; 346 } 347 if ((UInt32)protocol != 0) { 348 attrs[attrList.count].tag = kSecProtocolItemAttr; 349 attrs[attrList.count].length = sizeof(SecProtocolType); 350 attrs[attrList.count].data = (SecProtocolType *)&protocol; 351 attrList.count++; 352 } 353 if ((UInt32)authenticationType != 0) { 354 attrs[attrList.count].tag = kSecAuthenticationTypeItemAttr; 355 attrs[attrList.count].length = sizeof(SecAuthenticationType); 356 attrs[attrList.count].data = (SecAuthenticationType *)&authenticationType; 357 attrList.count++; 358 } 359 360 // modify attributes and password data, if provided 361 status = SecKeychainItemModifyContent(itemRef, &attrList, passwordLength, passwordData); 362 if (status) { 363 sec_error("SecKeychainItemModifyContent: %s", sec_errstr(status)); 364 } 365 366 // modify access, if provided 367 if (!status && access) { 368 status = modify_access(itemRef, access); 369 } 370 371 CFRelease(itemRef); 372 373 return status; 374 } 375 376 static int 377 do_add_internet_password(const char *keychainName, 378 FourCharCode itemCreator, 379 FourCharCode itemType, 380 const char *kind, 381 const char *comment, 382 const char *label, 383 const char *serverName, 384 const char *securityDomain, 385 const char *accountName, 386 const char *path, 387 UInt16 port, 388 SecProtocolType protocol, 389 SecAuthenticationType authenticationType, 390 const void *passwordData, 391 uint32_t passwordLength, 392 SecAccessRef access, 393 Boolean update) 394 { 395 SecKeychainRef keychain = NULL; 396 SecKeychainItemRef itemRef = NULL; 397 OSStatus result = 0; 398 399 // if update flag is specified, try to find and update an existing item 400 if (update) { 401 result = do_update_internet_password(keychainName,itemCreator,itemType,kind,comment,label,serverName, 402 securityDomain,accountName,path,port,protocol,authenticationType, 403 passwordData,passwordLength,access); 404 if (result == noErr) 405 return result; 406 } 407 408 if (keychainName) 409 { 410 keychain = keychain_open(keychainName); 411 if (!keychain) 412 { 413 result = 1; 414 goto cleanup; 415 } 416 } 417 418 // set up attribute vector for new item (each attribute consists of {tag, length, pointer}) 419 SecKeychainAttribute attrs[] = { 420 { kSecLabelItemAttr, label ? (UInt32) strlen(label) : 0, (char *)label }, 421 { kSecDescriptionItemAttr, kind ? (UInt32) strlen(kind) : 0, (char *)kind }, 422 { kSecCommentItemAttr, comment ? (UInt32) strlen(comment) : 0, (char *)comment }, 423 { kSecServerItemAttr, serverName ? (UInt32) strlen(serverName) : 0, (char *)serverName }, 424 { kSecSecurityDomainItemAttr, securityDomain ? (UInt32) strlen(securityDomain) : 0, (char *)securityDomain }, 425 { kSecAccountItemAttr, accountName ? (UInt32) strlen(accountName) : 0, (char *)accountName }, 426 { kSecPathItemAttr, path ? (UInt32) strlen(path) : 0, (char *)path }, 427 { kSecPortItemAttr, sizeof(UInt16), (UInt16 *)&port }, 428 { kSecProtocolItemAttr, sizeof(SecProtocolType), (SecProtocolType *)&protocol }, 429 { kSecAuthenticationTypeItemAttr, sizeof(SecAuthenticationType), (SecAuthenticationType *)&authenticationType }, 430 { (SecKeychainAttrType)0, sizeof(FourCharCode), NULL }, /* placeholder */ 431 { (SecKeychainAttrType)0, sizeof(FourCharCode), NULL } /* placeholder */ 432 }; 433 SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs }; 434 attributes.count -= 2; 435 436 if (itemCreator != 0) 437 { 438 attrs[attributes.count].tag = kSecCreatorItemAttr; 439 attrs[attributes.count].data = (FourCharCode *)&itemCreator; 440 attributes.count++; 441 } 442 if (itemType != 0) 443 { 444 attrs[attributes.count].tag = kSecTypeItemAttr; 445 attrs[attributes.count].data = (FourCharCode *)&itemType; 446 attributes.count++; 447 } 448 449 result = SecKeychainItemCreateFromContent(kSecInternetPasswordItemClass, 450 &attributes, 451 passwordLength, 452 passwordData, 453 keychain, 454 access, 455 &itemRef); 456 457 if (result) 458 { 459 sec_error("SecKeychainAddInternetPassword %s: %s", keychainName ? keychainName : "<NULL>", sec_errstr(result)); 460 } 461 462 cleanup: 463 if (itemRef) 464 CFRelease(itemRef); 465 if (keychain) 466 CFRelease(keychain); 467 468 return result; 469 } 470 471 static int 472 do_add_certificates(const char *keychainName, int argc, char * const *argv) 473 { 474 SecKeychainRef keychain = NULL; 475 int ix, result = 0; 476 477 if (keychainName) 478 { 479 keychain = keychain_open(keychainName); 480 if (!keychain) 481 { 482 result = 1; 483 goto cleanup; 484 } 485 } 486 487 for (ix = 0; ix < argc; ++ix) 488 { 489 CSSM_DATA certData = {}; 490 OSStatus status; 491 SecCertificateRef certificate = NULL; 492 493 if (read_file(argv[ix], &certData)) 494 { 495 result = 1; 496 continue; 497 } 498 499 status = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_UNKNOWN, &certificate); 500 if (status) 501 { 502 sec_perror("SecCertificateCreateFromData", status); 503 result = 1; 504 } 505 else 506 { 507 status = SecCertificateAddToKeychain(certificate, keychain); 508 if (status) 509 { 510 if (status == errSecDuplicateItem) 511 { 512 if (keychainName) 513 sec_error("%s: already in %s", argv[ix], keychainName); 514 else 515 sec_error("%s: already in default keychain", argv[ix]); 516 } 517 else 518 { 519 sec_perror("SecCertificateAddToKeychain", status); 520 } 521 result = 1; 522 } 523 } 524 525 if (certData.Data) 526 free(certData.Data); 527 if (certificate) 528 CFRelease(certificate); 529 } 530 531 cleanup: 532 if (keychain) 533 CFRelease(keychain); 534 535 return result; 536 } 537 538 static bool convertPasswordData(const char *hexString, char **outData, uint32_t *outLength) { 539 size_t length = 0; 540 bool result = convertHex(hexString, (uint8_t **)outData, &length); 541 if (result && outLength) { 542 *outLength = (uint32_t)length & 0xFFFFFFFF; 543 } 544 return result; 545 } 546 547 static bool promptForPasswordData(char **returnedPasswordData) { 548 // Caller must zero memory and free returned value. 549 // Returns true if we have data; false means the user canceled 550 if (!returnedPasswordData) 551 return false; 552 553 bool result = false; 554 char *password = NULL; 555 int tries; 556 557 for (tries = 3; tries-- > 0;) { 558 bool passwordsMatch = false; 559 char *firstpass = NULL; 560 561 password = getpass("password data for new item: "); 562 if (!password) 563 break; 564 565 firstpass = malloc(strlen(password) + 1); 566 strcpy(firstpass, password); 567 password = getpass("retype password for new item: "); 568 passwordsMatch = password && (strcmp(password, firstpass) == 0); 569 memset(firstpass, 0, strlen(firstpass)); 570 free(firstpass); 571 if (!password) 572 break; 573 574 if (passwordsMatch) { 575 result = true; 576 break; 577 } 578 579 fprintf(stderr, "passwords don't match\n"); 580 memset(password, 0, strlen(password)); 581 } 582 583 if (result) { 584 *returnedPasswordData = password; 585 } else { 586 free(password); 587 } 588 return result; 589 } 590 591 int 592 keychain_add_generic_password(int argc, char * const *argv) 593 { 594 char *serviceName = NULL, *passwordData = NULL, *accountName = NULL; 595 char *kind = NULL, *label = NULL, *value = NULL, *comment = NULL; 596 FourCharCode itemCreator = 0, itemType = 0; 597 int ch, result = 0; 598 uint32_t passwordLength = 0; 599 const char *keychainName = NULL; 600 Boolean access_specified = FALSE; 601 Boolean always_allow = FALSE; 602 Boolean update_item = FALSE; 603 SecAccessRef access = NULL; 604 CFMutableArrayRef trusted_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 605 OSStatus status; 606 bool mustFreePasswordData = false; // if we got it via user prompting 607 608 /* 609 * " -a Specify account name (required)\n" 610 * " -c Specify item creator (optional four-character code)\n" 611 * " -C Specify item type (optional four-character code)\n" 612 * " -D Specify kind (default is \"application password\")\n" 613 * " -G Specify generic attribute (optional)\n" 614 * " -j Specify comment string (optional)\n" 615 * " -l Specify label (if omitted, service name is used as default label)\n" 616 * " -s Specify service name (required)\n" 617 * " -p Specify password to be added (legacy option, equivalent to -w)\n" 618 * " -w Specify password to be added\n" 619 * " -X Specify password data to be added as a hexadecimal string\n" 620 * " -A Allow any application to access this item without warning (insecure, not recommended!)\n" 621 * " -T Specify an application which may access this item (multiple -T options are allowed)\n" 622 * " -U Update item if it already exists (if omitted, the item cannot already exist)\n" 623 */ 624 625 while ((ch = getopt(argc, argv, ":a:c:C:D:G:j:l:s:p:w:X:UAT:h")) != -1) 626 { 627 switch (ch) 628 { 629 case 'a': 630 accountName = optarg; 631 break; 632 case 'c': 633 result = parse_fourcharcode(optarg, &itemCreator); 634 if (result) goto cleanup; 635 break; 636 case 'C': 637 result = parse_fourcharcode(optarg, &itemType); 638 if (result) goto cleanup; 639 break; 640 case 'D': 641 kind = optarg; 642 break; 643 case 'G': 644 value = optarg; 645 break; 646 case 'j': 647 comment = optarg; 648 break; 649 case 'l': 650 label = optarg; 651 break; 652 case 's': 653 serviceName = optarg; 654 break; 655 case 'p': 656 case 'w': 657 passwordData = optarg; 658 passwordLength = (passwordData) ? (uint32_t)strlen(passwordData) : 0; 659 break; 660 case 'U': 661 update_item = TRUE; 662 break; 663 case 'A': 664 always_allow = TRUE; 665 access_specified = TRUE; 666 break; 667 case 'T': 668 { 669 if (optarg[0]) 670 { 671 SecTrustedApplicationRef app = NULL; 672 /* check whether the argument specifies an application group */ 673 const char *groupPrefix = "group://"; 674 size_t prefixLen = strlen(groupPrefix); 675 if (strlen(optarg) > prefixLen && !memcmp(optarg, groupPrefix, prefixLen)) { 676 const char *groupName = &optarg[prefixLen]; 677 if ((status = SecTrustedApplicationCreateApplicationGroup(groupName, NULL, &app)) != noErr) { 678 sec_error("SecTrustedApplicationCreateApplicationGroup %s: %s", optarg, sec_errstr(status)); 679 } 680 } else { 681 if ((status = SecTrustedApplicationCreateFromPath(optarg, &app)) != noErr) { 682 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg, sec_errstr(status)); 683 } 684 } 685 686 if (status) { 687 result = 1; 688 goto cleanup; 689 } 690 691 CFArrayAppendValue(trusted_list, app); 692 CFRelease(app); 693 } 694 access_specified = TRUE; 695 break; 696 } 697 case 'X': 698 if (convertPasswordData(optarg, &passwordData, &passwordLength)) { 699 mustFreePasswordData = true; 700 break; 701 } 702 sec_error("Unable to convert password data (-X must specify valid hex digits)"); 703 result = 2; 704 goto cleanup; /* @@@ Return 2 triggers usage message. */ 705 case '?': 706 case ':': 707 // They supplied the -p or -w but with no data, so prompt 708 // This differs from the case where no -p or -w was given, where we set the data to empty 709 if (optopt == 'p' || optopt == 'w') { 710 if (promptForPasswordData(&passwordData)) { 711 passwordLength = (passwordData) ? (uint32_t)strlen(passwordData) : 0; 712 mustFreePasswordData = true; 713 break; 714 } else { 715 result = 1; 716 goto cleanup; /* @@@ Do not trigger usage message, but indicate failure. */ 717 } 718 } 719 result = 2; 720 goto cleanup; /* @@@ Return 2 triggers usage message. */ 721 default: 722 result = 2; 723 goto cleanup; /* @@@ Return 2 triggers usage message. */ 724 } 725 } 726 727 argc -= optind; 728 argv += optind; 729 730 if (!accountName || !serviceName) 731 { 732 result = 2; 733 goto cleanup; 734 } 735 else if (argc > 0) 736 { 737 keychainName = argv[0]; 738 if (argc > 1 || *keychainName == '\0') 739 { 740 result = 2; 741 goto cleanup; 742 } 743 } 744 745 if (access_specified) 746 { 747 const char *accessName = (label) ? label : (serviceName) ? serviceName : (accountName) ? accountName : ""; 748 if ((result = create_access(accessName, always_allow, trusted_list, &access)) != 0) 749 goto cleanup; 750 } 751 752 result = do_add_generic_password(keychainName, 753 itemCreator, 754 itemType, 755 kind, 756 value, 757 comment, 758 (label) ? label : serviceName, 759 serviceName, 760 accountName, 761 passwordData, 762 passwordLength, 763 access, 764 update_item); 765 766 cleanup: 767 if (mustFreePasswordData) { 768 free(passwordData); 769 } 770 if (trusted_list) { 771 CFRelease(trusted_list); 772 } 773 if (access) { 774 CFRelease(access); 775 } 776 777 return result; 778 } 779 780 int 781 keychain_add_internet_password(int argc, char * const *argv) 782 { 783 char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL, *passwordData = NULL; 784 char *kind = NULL, *comment = NULL, *label = NULL; 785 FourCharCode itemCreator = 0, itemType = 0; 786 UInt16 port = 0; 787 SecProtocolType protocol = 0; 788 SecAuthenticationType authenticationType = OSSwapHostToBigInt32('dflt'); 789 int ch, result = 0; 790 uint32_t passwordLength = 0; 791 const char *keychainName = NULL; 792 Boolean access_specified = FALSE; 793 Boolean always_allow = FALSE; 794 Boolean update_item = FALSE; 795 SecAccessRef access = NULL; 796 CFMutableArrayRef trusted_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 797 OSStatus status; 798 bool mustFreePasswordData = false; // if we got it via user prompting 799 800 /* 801 * " -a Specify account name (required)\n" 802 * " -c Specify item creator (optional four-character code)\n" 803 * " -C Specify item type (optional four-character code)\n" 804 * " -d Specify security domain string (optional)\n" 805 * " -D Specify kind (default is \"Internet password\")\n" 806 * " -j Specify comment string (optional)\n" 807 * " -l Specify label (if omitted, server name is used as default label)\n" 808 * " -p Specify path string (optional)\n" 809 * " -P Specify port number (optional)\n" 810 * " -r Specify protocol (optional four-character SecProtocolType, e.g. \"http\", \"ftp \")\n" 811 * " -s Specify server name (required)\n" 812 * " -t Specify authentication type (as a four-character SecAuthenticationType, default is \"dflt\")\n" 813 * " -w Specify password to be added\n" 814 * " -X Specify password data to be added as a hexadecimal string\n" 815 * " -A Allow any application to access this item without warning (insecure, not recommended!)\n" 816 * " -T Specify an application which may access this item (multiple -T options are allowed)\n" 817 * " -U Update item if it already exists (if omitted, the item cannot already exist)\n" 818 */ 819 820 while ((ch = getopt(argc, argv, ":a:c:C:d:D:j:l:p:P:r:s:t:w:X:UAT:h")) != -1) 821 { 822 switch (ch) 823 { 824 case 'a': 825 accountName = optarg; 826 break; 827 case 'c': 828 result = parse_fourcharcode(optarg, &itemCreator); 829 if (result) goto cleanup; 830 break; 831 case 'C': 832 result = parse_fourcharcode(optarg, &itemType); 833 if (result) goto cleanup; 834 break; 835 case 'd': 836 securityDomain = optarg; 837 break; 838 case 'D': 839 kind = optarg; 840 break; 841 case 'j': 842 comment = optarg; 843 break; 844 case 'l': 845 label = optarg; 846 break; 847 case 'p': 848 path = optarg; 849 break; 850 case 'P': 851 port = atoi(optarg); 852 break; 853 case 'r': 854 result = parse_fourcharcode(optarg, &protocol); 855 if (result) goto cleanup; 856 break; 857 case 's': 858 serverName = optarg; 859 break; 860 case 't': 861 result = parse_fourcharcode(optarg, &authenticationType); 862 if (result) goto cleanup; 863 /* auth type attribute is special */ 864 authenticationType = OSSwapHostToBigInt32(authenticationType); 865 break; 866 case 'w': 867 passwordData = optarg; 868 passwordLength = (passwordData) ? (uint32_t)strlen(passwordData) : 0; 869 break; 870 case 'U': 871 update_item = TRUE; 872 break; 873 case 'A': 874 always_allow = TRUE; 875 access_specified = TRUE; 876 break; 877 case 'T': 878 { 879 if (optarg[0]) 880 { 881 SecTrustedApplicationRef app = NULL; 882 /* check whether the argument specifies an application group */ 883 const char *groupPrefix = "group://"; 884 size_t prefixLen = strlen(groupPrefix); 885 if (strlen(optarg) > prefixLen && !memcmp(optarg, groupPrefix, prefixLen)) { 886 const char *groupName = &optarg[prefixLen]; 887 if ((status = SecTrustedApplicationCreateApplicationGroup(groupName, NULL, &app)) != noErr) { 888 sec_error("SecTrustedApplicationCreateApplicationGroup %s: %s", optarg, sec_errstr(status)); 889 } 890 } else { 891 if ((status = SecTrustedApplicationCreateFromPath(optarg, &app)) != noErr) { 892 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg, sec_errstr(status)); 893 } 894 } 895 896 if (status) { 897 result = 1; 898 goto cleanup; 899 } 900 901 CFArrayAppendValue(trusted_list, app); 902 CFRelease(app); 903 } 904 access_specified = TRUE; 905 break; 906 } 907 case 'X': 908 if (convertPasswordData(optarg, &passwordData, &passwordLength)) { 909 mustFreePasswordData = true; 910 break; 911 } 912 sec_error("Unable to convert password data (-X must specify valid hex digits)"); 913 result = 2; 914 goto cleanup; /* @@@ Return 2 triggers usage message. */ 915 case '?': 916 case ':': 917 // They supplied the -p or -w but with no data, so prompt 918 // This differs from the case where no -p or -w was given, where we set the data to empty 919 if (optopt == 'p' || optopt == 'w') { 920 if (promptForPasswordData(&passwordData)) { 921 passwordLength = (passwordData) ? (uint32_t)strlen(passwordData) : 0; 922 mustFreePasswordData = true; 923 break; 924 } else { 925 result = 1; 926 goto cleanup; /* @@@ Do not trigger usage message, but indicate failure. */ 927 } 928 } 929 result = 2; 930 goto cleanup; /* @@@ Return 2 triggers usage message. */ 931 932 default: 933 result = 2; 934 goto cleanup; /* @@@ Return 2 triggers usage message. */ 935 } 936 } 937 938 argc -= optind; 939 argv += optind; 940 941 if (!accountName || !serverName) 942 { 943 result = 2; 944 goto cleanup; 945 } 946 else if (argc > 0) 947 { 948 keychainName = argv[0]; 949 if (argc > 1 || *keychainName == '\0') 950 { 951 result = 2; 952 goto cleanup; 953 } 954 } 955 956 if (access_specified) 957 { 958 const char *accessName = (label) ? label : (serverName) ? serverName : (accountName) ? accountName : ""; 959 if ((result = create_access(accessName, always_allow, trusted_list, &access)) != 0) 960 goto cleanup; 961 } 962 963 result = do_add_internet_password(keychainName, 964 itemCreator, 965 itemType, 966 kind, 967 comment, 968 (label) ? label : serverName, 969 serverName, 970 securityDomain, 971 accountName, 972 path, 973 port, 974 protocol, 975 authenticationType, 976 passwordData, 977 passwordLength, 978 access, 979 update_item); 980 981 cleanup: 982 if (mustFreePasswordData) { 983 free(passwordData); 984 } 985 if (trusted_list) { 986 CFRelease(trusted_list); 987 } 988 if (access) { 989 CFRelease(access); 990 } 991 992 return result; 993 } 994 995 int 996 keychain_add_certificates(int argc, char * const *argv) 997 { 998 int ch, result = 0; 999 const char *keychainName = NULL; 1000 while ((ch = getopt(argc, argv, "hk:")) != -1) 1001 { 1002 switch (ch) 1003 { 1004 case 'k': 1005 keychainName = optarg; 1006 if (*keychainName == '\0') 1007 return SHOW_USAGE_MESSAGE; 1008 break; 1009 case '?': 1010 default: 1011 return SHOW_USAGE_MESSAGE; 1012 } 1013 } 1014 1015 argc -= optind; 1016 argv += optind; 1017 1018 if (argc == 0) 1019 return SHOW_USAGE_MESSAGE; 1020 1021 result = do_add_certificates(keychainName, argc, argv); 1022 1023 return result; 1024 }