1/* 2 * Copyright (c) 2007 Apple Inc. All rights reserved. 3 * 4 * @APPLE_BSD_LICENSE_HEADER_START@ 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of Apple Inc. ("Apple") nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * @APPLE_BSD_LICENSE_HEADER_END@ 31 */ 32 33#include "includes.h" 34 35#include <stdio.h> 36#include <string.h> 37 38#include "xmalloc.h" 39#include "key.h" 40#include "authfd.h" 41#include "authfile.h" 42 43#if defined(__APPLE_KEYCHAIN__) 44 45#include <CoreFoundation/CoreFoundation.h> 46#include <Security/Security.h> 47 48/* Our Security/SecPassword.h is not yet API, so I will define the constants that I am using here. */ 49enum SEC_PASSWORD_OPTS { 50kSecPasswordGet = 1<<0, // Get password from keychain or user 51kSecPasswordSet = 1<<1, // Set password (passed in if kSecPasswordGet not set, otherwise from user) 52kSecPasswordFail = 1<<2, // Wrong password (ignore item in keychain and flag error) 53}; 54 55#endif 56 57/* 58 * Platform-specific helper functions. 59 */ 60 61#if defined(__APPLE_KEYCHAIN__) 62 63static int get_boolean_preference(const char *key, int default_value, 64 int foreground) 65{ 66 int value = default_value; 67 CFStringRef keyRef = NULL; 68 CFPropertyListRef valueRef = NULL; 69 70 keyRef = CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8); 71 if (keyRef != NULL) 72 valueRef = CFPreferencesCopyAppValue(keyRef, 73 CFSTR("org.openbsd.openssh")); 74 if (valueRef != NULL) 75 if (CFGetTypeID(valueRef) == CFBooleanGetTypeID()) 76 value = CFBooleanGetValue(valueRef); 77 else if (foreground) 78 fprintf(stderr, "Ignoring nonboolean %s preference.\n", key); 79 80 if (keyRef) 81 CFRelease(keyRef); 82 if (valueRef) 83 CFRelease(valueRef); 84 85 return value; 86} 87 88#endif 89 90/* 91 * Store the passphrase for a given identity in the keychain. 92 */ 93void 94store_in_keychain(const char *filename, const char *passphrase) 95{ 96 97#if defined(__APPLE_KEYCHAIN__) 98 99 /* 100 * store_in_keychain 101 * Mac OS X implementation 102 */ 103 104 CFStringRef cfstr_relative_filename = NULL; 105 CFURLRef cfurl_relative_filename = NULL, cfurl_filename = NULL; 106 CFStringRef cfstr_filename = NULL; 107 CFDataRef cfdata_filename = NULL; 108 CFIndex filename_len; 109 UInt8 *label = NULL; 110 UInt8 *utf8_filename; 111 OSStatus rv; 112 SecKeychainItemRef itemRef = NULL; 113 SecTrustedApplicationRef apps[] = {NULL, NULL, NULL}; 114 CFArrayRef trustedlist = NULL; 115 SecAccessRef initialAccess = NULL; 116 117 /* Bail out if KeychainIntegration preference is -bool NO */ 118 if (get_boolean_preference("KeychainIntegration", 1, 1) == 0) { 119 fprintf(stderr, "Keychain integration is disabled.\n"); 120 goto err; 121 } 122 123 /* Interpret filename with the correct encoding. */ 124 if ((cfstr_relative_filename = 125 CFStringCreateWithFileSystemRepresentation(NULL, filename)) == NULL) 126 { 127 fprintf(stderr, "CFStringCreateWithFileSystemRepresentation failed\n"); 128 goto err; 129 } 130 if ((cfurl_relative_filename = CFURLCreateWithFileSystemPath(NULL, 131 cfstr_relative_filename, kCFURLPOSIXPathStyle, false)) == NULL) { 132 fprintf(stderr, "CFURLCreateWithFileSystemPath failed\n"); 133 goto err; 134 } 135 if ((cfurl_filename = CFURLCopyAbsoluteURL(cfurl_relative_filename)) == 136 NULL) { 137 fprintf(stderr, "CFURLCopyAbsoluteURL failed\n"); 138 goto err; 139 } 140 if ((cfstr_filename = CFURLCopyFileSystemPath(cfurl_filename, 141 kCFURLPOSIXPathStyle)) == NULL) { 142 fprintf(stderr, "CFURLCopyFileSystemPath failed\n"); 143 goto err; 144 } 145 if ((cfdata_filename = CFStringCreateExternalRepresentation(NULL, 146 cfstr_filename, kCFStringEncodingUTF8, 0)) == NULL) { 147 fprintf(stderr, "CFStringCreateExternalRepresentation failed\n"); 148 goto err; 149 } 150 filename_len = CFDataGetLength(cfdata_filename); 151 if ((label = xmalloc(filename_len + 5)) == NULL) { 152 fprintf(stderr, "xmalloc failed\n"); 153 goto err; 154 } 155 memcpy(label, "SSH: ", 5); 156 utf8_filename = label + 5; 157 CFDataGetBytes(cfdata_filename, CFRangeMake(0, filename_len), 158 utf8_filename); 159 160 /* Check if we already have this passphrase. */ 161 rv = SecKeychainFindGenericPassword(NULL, 3, "SSH", filename_len, 162 (char *)utf8_filename, NULL, NULL, &itemRef); 163 if (rv == errSecItemNotFound) { 164 /* Add a new keychain item. */ 165 SecKeychainAttribute attrs[] = { 166 {kSecLabelItemAttr, filename_len + 5, label}, 167 {kSecServiceItemAttr, 3, "SSH"}, 168 {kSecAccountItemAttr, filename_len, utf8_filename} 169 }; 170 SecKeychainAttributeList attrList = 171 {sizeof(attrs) / sizeof(attrs[0]), attrs}; 172 if (SecTrustedApplicationCreateFromPath("/usr/bin/ssh-agent", 173 &apps[0]) != noErr || 174 SecTrustedApplicationCreateFromPath("/usr/bin/ssh-add", 175 &apps[1]) != noErr || 176 SecTrustedApplicationCreateFromPath("/usr/bin/ssh", 177 &apps[2]) != noErr) { 178 fprintf(stderr, "SecTrustedApplicationCreateFromPath failed\n"); 179 goto err; 180 } 181 if ((trustedlist = CFArrayCreate(NULL, (const void **)apps, 182 sizeof(apps) / sizeof(apps[0]), &kCFTypeArrayCallBacks)) == 183 NULL) { 184 fprintf(stderr, "CFArrayCreate failed\n"); 185 goto err; 186 } 187 if (SecAccessCreate(cfstr_filename, trustedlist, 188 &initialAccess) != noErr) { 189 fprintf(stderr, "SecAccessCreate failed\n"); 190 goto err; 191 } 192 if (SecKeychainItemCreateFromContent( 193 kSecGenericPasswordItemClass, &attrList, strlen(passphrase), 194 passphrase, NULL, initialAccess, NULL) == noErr) 195 fprintf(stderr, "Passphrase stored in keychain: %s\n", filename); 196 else 197 fprintf(stderr, "Could not create keychain item\n"); 198 } else if (rv == noErr) { 199 /* Update an existing keychain item. */ 200 if (SecKeychainItemModifyAttributesAndData(itemRef, NULL, 201 strlen(passphrase), passphrase) == noErr) 202 fprintf(stderr, "Passphrase updated in keychain: %s\n", filename); 203 else 204 fprintf(stderr, "Could not modify keychain item\n"); 205 } else 206 fprintf(stderr, "Could not access keychain\n"); 207 208err: /* Clean up. */ 209 if (cfstr_relative_filename) 210 CFRelease(cfstr_relative_filename); 211 if (cfurl_relative_filename) 212 CFRelease(cfurl_relative_filename); 213 if (cfurl_filename) 214 CFRelease(cfurl_filename); 215 if (cfstr_filename) 216 CFRelease(cfstr_filename); 217 if (cfdata_filename) 218 CFRelease(cfdata_filename); 219 if (label) 220 xfree(label); 221 if (itemRef) 222 CFRelease(itemRef); 223 if (apps[0]) 224 CFRelease(apps[0]); 225 if (apps[1]) 226 CFRelease(apps[1]); 227 if (apps[2]) 228 CFRelease(apps[2]); 229 if (trustedlist) 230 CFRelease(trustedlist); 231 if (initialAccess) 232 CFRelease(initialAccess); 233 234#else 235 236 /* 237 * store_in_keychain 238 * no keychain implementation 239 */ 240 241 fprintf(stderr, "Keychain is not available on this system\n"); 242 243#endif 244 245} 246 247/* 248 * Remove the passphrase for a given identity from the keychain. 249 */ 250void 251remove_from_keychain(const char *filename) 252{ 253 254#if defined(__APPLE_KEYCHAIN__) 255 256 /* 257 * remove_from_keychain 258 * Mac OS X implementation 259 */ 260 261 CFStringRef cfstr_relative_filename = NULL; 262 CFURLRef cfurl_relative_filename = NULL, cfurl_filename = NULL; 263 CFStringRef cfstr_filename = NULL; 264 CFDataRef cfdata_filename = NULL; 265 CFIndex filename_len; 266 const UInt8 *utf8_filename; 267 OSStatus rv; 268 SecKeychainItemRef itemRef = NULL; 269 270 /* Bail out if KeychainIntegration preference is -bool NO */ 271 if (get_boolean_preference("KeychainIntegration", 1, 1) == 0) { 272 fprintf(stderr, "Keychain integration is disabled.\n"); 273 goto err; 274 } 275 276 /* Interpret filename with the correct encoding. */ 277 if ((cfstr_relative_filename = 278 CFStringCreateWithFileSystemRepresentation(NULL, filename)) == NULL) 279 { 280 fprintf(stderr, "CFStringCreateWithFileSystemRepresentation failed\n"); 281 goto err; 282 } 283 if ((cfurl_relative_filename = CFURLCreateWithFileSystemPath(NULL, 284 cfstr_relative_filename, kCFURLPOSIXPathStyle, false)) == NULL) { 285 fprintf(stderr, "CFURLCreateWithFileSystemPath failed\n"); 286 goto err; 287 } 288 if ((cfurl_filename = CFURLCopyAbsoluteURL(cfurl_relative_filename)) == 289 NULL) { 290 fprintf(stderr, "CFURLCopyAbsoluteURL failed\n"); 291 goto err; 292 } 293 if ((cfstr_filename = CFURLCopyFileSystemPath(cfurl_filename, 294 kCFURLPOSIXPathStyle)) == NULL) { 295 fprintf(stderr, "CFURLCopyFileSystemPath failed\n"); 296 goto err; 297 } 298 if ((cfdata_filename = CFStringCreateExternalRepresentation(NULL, 299 cfstr_filename, kCFStringEncodingUTF8, 0)) == NULL) { 300 fprintf(stderr, "CFStringCreateExternalRepresentation failed\n"); 301 goto err; 302 } 303 filename_len = CFDataGetLength(cfdata_filename); 304 utf8_filename = CFDataGetBytePtr(cfdata_filename); 305 306 /* Check if we already have this passphrase. */ 307 rv = SecKeychainFindGenericPassword(NULL, 3, "SSH", filename_len, 308 (const char *)utf8_filename, NULL, NULL, &itemRef); 309 if (rv == noErr) { 310 /* Remove the passphrase from the keychain. */ 311 if (SecKeychainItemDelete(itemRef) == noErr) 312 fprintf(stderr, "Passphrase removed from keychain: %s\n", filename); 313 else 314 fprintf(stderr, "Could not remove keychain item\n"); 315 } else if (rv != errSecItemNotFound) 316 fprintf(stderr, "Could not access keychain\n"); 317 318err: /* Clean up. */ 319 if (cfstr_relative_filename) 320 CFRelease(cfstr_relative_filename); 321 if (cfurl_relative_filename) 322 CFRelease(cfurl_relative_filename); 323 if (cfurl_filename) 324 CFRelease(cfurl_filename); 325 if (cfstr_filename) 326 CFRelease(cfstr_filename); 327 if (cfdata_filename) 328 CFRelease(cfdata_filename); 329 if (itemRef) 330 CFRelease(itemRef); 331 332#else 333 334 /* 335 * remove_from_keychain 336 * no keychain implementation 337 */ 338 339 fprintf(stderr, "Keychain is not available on this system\n"); 340 341#endif 342 343} 344 345/* 346 * Add identities to ssh-agent using passphrases stored in the keychain. 347 * Returns zero on success and nonzero on failure. 348 * add_identity is a callback into ssh-agent. It takes a filename and a 349 * passphrase, and attempts to add the identity to the agent. It returns 350 * zero on success and nonzero on failure. 351 */ 352int 353add_identities_using_keychain(int (*add_identity)(const char *, const char *)) 354{ 355 356#if defined(__APPLE_KEYCHAIN__) 357 358 /* 359 * add_identities_using_keychain 360 * Mac OS X implementation 361 */ 362 363 OSStatus rv; 364 SecKeychainSearchRef searchRef; 365 SecKeychainItemRef itemRef; 366 UInt32 length; 367 void *data; 368 CFIndex maxsize; 369 370 /* Bail out if KeychainIntegration preference is -bool NO */ 371 if (get_boolean_preference("KeychainIntegration", 1, 0) == 0) 372 return 0; 373 374 /* Search for SSH passphrases in the keychain */ 375 SecKeychainAttribute attrs[] = { 376 {kSecServiceItemAttr, 3, "SSH"} 377 }; 378 SecKeychainAttributeList attrList = 379 {sizeof(attrs) / sizeof(attrs[0]), attrs}; 380 if ((rv = SecKeychainSearchCreateFromAttributes(NULL, 381 kSecGenericPasswordItemClass, &attrList, &searchRef)) != noErr) 382 return 0; 383 384 /* Iterate through the search results. */ 385 while ((rv = SecKeychainSearchCopyNext(searchRef, &itemRef)) == noErr) { 386 UInt32 tag = kSecAccountItemAttr; 387 UInt32 format = kSecFormatUnknown; 388 SecKeychainAttributeInfo info = {1, &tag, &format}; 389 SecKeychainAttributeList *itemAttrList = NULL; 390 CFStringRef cfstr_filename = NULL; 391 char *filename = NULL; 392 char *passphrase = NULL; 393 394 /* Retrieve filename and passphrase. */ 395 if ((rv = SecKeychainItemCopyAttributesAndData(itemRef, &info, 396 NULL, &itemAttrList, &length, &data)) != noErr) 397 goto err; 398 if (itemAttrList->count != 1) 399 goto err; 400 cfstr_filename = CFStringCreateWithBytes(NULL, 401 itemAttrList->attr->data, itemAttrList->attr->length, 402 kCFStringEncodingUTF8, true); 403 maxsize = CFStringGetMaximumSizeOfFileSystemRepresentation( 404 cfstr_filename); 405 if ((filename = xmalloc(maxsize)) == NULL) 406 goto err; 407 if (CFStringGetFileSystemRepresentation(cfstr_filename, 408 filename, maxsize) == false) 409 goto err; 410 if ((passphrase = xmalloc(length + 1)) == NULL) 411 goto err; 412 memcpy(passphrase, data, length); 413 passphrase[length] = '\0'; 414 415 /* Add the identity. */ 416 add_identity(filename, passphrase); 417 418err: /* Clean up. */ 419 if (itemRef) 420 CFRelease(itemRef); 421 if (cfstr_filename) 422 CFRelease(cfstr_filename); 423 if (filename) 424 xfree(filename); 425 if (passphrase) 426 xfree(passphrase); 427 if (itemAttrList) 428 SecKeychainItemFreeAttributesAndData(itemAttrList, 429 data); 430 } 431 432 CFRelease(searchRef); 433 434 return 0; 435 436#else 437 438 /* 439 * add_identities_using_keychain 440 * no implementation 441 */ 442 443 return 1; 444 445#endif 446 447} 448 449/* 450 * Prompt the user for a key's passphrase. The user will be offered the option 451 * of storing the passphrase in their keychain. Returns the passphrase 452 * (which the caller is responsible for xfreeing), or NULL if this function 453 * fails or is not implemented. If this function is not implemented, ssh will 454 * fall back on the standard read_passphrase function, and the user will need 455 * to use ssh-add -K to add their keys to the keychain. 456 */ 457char * 458keychain_read_passphrase(const char *filename, int oAskPassGUI) 459{ 460 461#if defined(__APPLE_KEYCHAIN__) 462 463 /* 464 * keychain_read_passphrase 465 * Mac OS X implementation 466 */ 467 468 CFStringRef cfstr_relative_filename = NULL; 469 CFURLRef cfurl_relative_filename = NULL, cfurl_filename = NULL; 470 CFStringRef cfstr_filename = NULL; 471 CFDataRef cfdata_filename = NULL; 472 CFIndex filename_len; 473 UInt8 *label = NULL; 474 UInt8 *utf8_filename; 475 SecPasswordRef passRef = NULL; 476 SecTrustedApplicationRef apps[] = {NULL, NULL, NULL}; 477 CFArrayRef trustedlist = NULL; 478 SecAccessRef initialAccess = NULL; 479 CFURLRef path = NULL; 480 CFStringRef pathFinal = NULL; 481 CFURLRef bundle_url = NULL; 482 CFBundleRef bundle = NULL; 483 CFStringRef promptTemplate = NULL, prompt = NULL; 484 UInt32 length; 485 const void *data; 486 AuthenticationConnection *ac = NULL; 487 char *result = NULL; 488 489 /* Bail out if KeychainIntegration preference is -bool NO */ 490 if (get_boolean_preference("KeychainIntegration", 1, 1) == 0) 491 goto err; 492 493 /* Bail out if the user set AskPassGUI preference to -bool NO */ 494 if (get_boolean_preference("AskPassGUI", 1, 1) == 0 || oAskPassGUI == 0) 495 goto err; 496 497 /* Bail out if we can't communicate with ssh-agent */ 498 if ((ac = ssh_get_authentication_connection()) == NULL) 499 goto err; 500 501 /* Interpret filename with the correct encoding. */ 502 if ((cfstr_relative_filename = 503 CFStringCreateWithFileSystemRepresentation(NULL, filename)) == NULL) 504 { 505 fprintf(stderr, "CFStringCreateWithFileSystemRepresentation failed\n"); 506 goto err; 507 } 508 if ((cfurl_relative_filename = CFURLCreateWithFileSystemPath(NULL, 509 cfstr_relative_filename, kCFURLPOSIXPathStyle, false)) == NULL) { 510 fprintf(stderr, "CFURLCreateWithFileSystemPath failed\n"); 511 goto err; 512 } 513 if ((cfurl_filename = CFURLCopyAbsoluteURL(cfurl_relative_filename)) == 514 NULL) { 515 fprintf(stderr, "CFURLCopyAbsoluteURL failed\n"); 516 goto err; 517 } 518 if ((cfstr_filename = CFURLCopyFileSystemPath(cfurl_filename, 519 kCFURLPOSIXPathStyle)) == NULL) { 520 fprintf(stderr, "CFURLCopyFileSystemPath failed\n"); 521 goto err; 522 } 523 if ((cfdata_filename = CFStringCreateExternalRepresentation(NULL, 524 cfstr_filename, kCFStringEncodingUTF8, 0)) == NULL) { 525 fprintf(stderr, "CFStringCreateExternalRepresentation failed\n"); 526 goto err; 527 } 528 filename_len = CFDataGetLength(cfdata_filename); 529 if ((label = xmalloc(filename_len + 5)) == NULL) { 530 fprintf(stderr, "xmalloc failed\n"); 531 goto err; 532 } 533 memcpy(label, "SSH: ", 5); 534 utf8_filename = label + 5; 535 CFDataGetBytes(cfdata_filename, CFRangeMake(0, filename_len), 536 utf8_filename); 537 538 /* Build a SecPasswordRef. */ 539 SecKeychainAttribute searchAttrs[] = { 540 {kSecServiceItemAttr, 3, "SSH"}, 541 {kSecAccountItemAttr, filename_len, utf8_filename} 542 }; 543 SecKeychainAttributeList searchAttrList = 544 {sizeof(searchAttrs) / sizeof(searchAttrs[0]), searchAttrs}; 545 SecKeychainAttribute attrs[] = { 546 {kSecLabelItemAttr, filename_len + 5, label}, 547 {kSecServiceItemAttr, 3, "SSH"}, 548 {kSecAccountItemAttr, filename_len, utf8_filename} 549 }; 550 SecKeychainAttributeList attrList = 551 {sizeof(attrs) / sizeof(attrs[0]), attrs}; 552 if (SecGenericPasswordCreate(&searchAttrList, &attrList, &passRef) != 553 noErr) { 554 fprintf(stderr, "SecGenericPasswordCreate failed\n"); 555 goto err; 556 } 557 if (SecTrustedApplicationCreateFromPath("/usr/bin/ssh-agent", &apps[0]) 558 != noErr || 559 SecTrustedApplicationCreateFromPath("/usr/bin/ssh-add", &apps[1]) 560 != noErr || 561 SecTrustedApplicationCreateFromPath("/usr/bin/ssh", &apps[2]) 562 != noErr) { 563 fprintf(stderr, "SecTrustedApplicationCreateFromPath failed\n"); 564 goto err; 565 } 566 if ((trustedlist = CFArrayCreate(NULL, (const void **)apps, 567 sizeof(apps) / sizeof(apps[0]), &kCFTypeArrayCallBacks)) == NULL) { 568 fprintf(stderr, "CFArrayCreate failed\n"); 569 goto err; 570 } 571 if (SecAccessCreate(cfstr_filename, trustedlist, &initialAccess) 572 != noErr) { 573 fprintf(stderr, "SecAccessCreate failed\n"); 574 goto err; 575 } 576 if (SecPasswordSetInitialAccess(passRef, initialAccess) != noErr) { 577 fprintf(stderr, "SecPasswordSetInitialAccess failed\n"); 578 goto err; 579 } 580 581 /* Request the passphrase from the user. */ 582 if ((path = CFURLCreateFromFileSystemRepresentation(NULL, 583 (UInt8 *)filename, strlen(filename), false)) == NULL) { 584 fprintf(stderr, "CFURLCreateFromFileSystemRepresentation failed\n"); 585 goto err; 586 } 587 if ((pathFinal = CFURLCopyLastPathComponent(path)) == NULL) { 588 fprintf(stderr, "CFURLCopyLastPathComponent failed\n"); 589 goto err; 590 } 591 if (!((bundle_url = CFURLCreateWithFileSystemPath(NULL, 592 CFSTR("/System/Library/CoreServices/"), kCFURLPOSIXPathStyle, true)) 593 != NULL && (bundle = CFBundleCreate(NULL, bundle_url)) != NULL && 594 (promptTemplate = CFCopyLocalizedStringFromTableInBundle( 595 CFSTR("Enter your password for the SSH key \"%@\"."), 596 CFSTR("OpenSSH"), bundle, "Text of the dialog asking the user for" 597 "their passphrase. The %@ will be replaced with the filename of a" 598 "specific key.")) != NULL) && 599 (promptTemplate = CFStringCreateCopy(NULL, 600 CFSTR("Enter your password for the SSH key \"%@\"."))) == NULL) { 601 fprintf(stderr, "CFStringCreateCopy failed\n"); 602 goto err; 603 } 604 if ((prompt = CFStringCreateWithFormat(NULL, NULL, promptTemplate, 605 pathFinal)) == NULL) { 606 fprintf(stderr, "CFStringCreateWithFormat failed\n"); 607 goto err; 608 } 609 switch (SecPasswordAction(passRef, prompt, 610 kSecPasswordGet|kSecPasswordFail, &length, &data)) { 611 case noErr: 612 result = xmalloc(length + 1); 613 memcpy(result, data, length); 614 result[length] = '\0'; 615 616 /* Save password in keychain if requested. */ 617 if (noErr != SecPasswordAction(passRef, CFSTR(""), kSecPasswordSet, &length, &data)) 618 fprintf(stderr, "Saving password to keychain failed\n"); 619 620 /* Add password to agent. */ 621 char *comment = NULL; 622 Key *private = key_load_private(filename, result, &comment); 623 if (NULL == private) 624 break; 625 if (ssh_add_identity_constrained(ac, private, comment, 0, 0)) 626 fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); 627 else 628 fprintf(stderr, "Could not add identity: %s\n", filename); 629 xfree(comment); 630 key_free(private); 631 break; 632 case errAuthorizationCanceled: 633 result = xmalloc(1); 634 *result = '\0'; 635 break; 636 default: 637 goto err; 638 } 639 640err: /* Clean up. */ 641 if (cfstr_relative_filename) 642 CFRelease(cfstr_relative_filename); 643 if (cfurl_relative_filename) 644 CFRelease(cfurl_relative_filename); 645 if (cfurl_filename) 646 CFRelease(cfurl_filename); 647 if (cfstr_filename) 648 CFRelease(cfstr_filename); 649 if (cfdata_filename) 650 CFRelease(cfdata_filename); 651 if (label) 652 xfree(label); 653 if (passRef) 654 CFRelease(passRef); 655 if (apps[0]) 656 CFRelease(apps[0]); 657 if (apps[1]) 658 CFRelease(apps[1]); 659 if (apps[2]) 660 CFRelease(apps[2]); 661 if (trustedlist) 662 CFRelease(trustedlist); 663 if (initialAccess) 664 CFRelease(initialAccess); 665 if (path) 666 CFRelease(path); 667 if (pathFinal) 668 CFRelease(pathFinal); 669 if (bundle_url) 670 CFRelease(bundle_url); 671 if (bundle) 672 CFRelease(bundle); 673 if (promptTemplate) 674 CFRelease(promptTemplate); 675 if (prompt) 676 CFRelease(prompt); 677 if (ac) 678 ssh_close_authentication_connection(ac); 679 680 return result; 681 682#else 683 684 /* 685 * keychain_read_passphrase 686 * no implementation 687 */ 688 689 return NULL; 690 691#endif 692 693} 694 695#if defined(__APPLE_KEYCHAIN__) 696volatile sig_atomic_t keychain_thread_active = 0; 697 698OSStatus 699keychain_lock_callback(SecKeychainEvent event, SecKeychainCallbackInfo *info, void *context) 700{ 701 SecKeychainRef login_keychain = NULL; 702 OSStatus retval = noErr; 703 704 /* Only care about login keychain */ 705 retval = SecKeychainCopyDefault(&login_keychain); 706 if (retval != noErr) { 707 debug("keychain_lock_callback: Unable to get login keychain, doing nothing."); 708 goto cleanup; 709 } 710 if (!CFEqual(info->keychain, login_keychain)) { 711 goto cleanup; 712 } 713 714 AuthenticationConnection *ac = ssh_get_authentication_connection(); 715 if (NULL == ac) { 716 error("keychain_lock_callback: Unable to get authentication connection."); 717 goto cleanup; 718 } 719 720 /* Silently remove all identitites */ 721 debug("keychain_lock_callback: Removing all identities."); 722 if (0 != ssh_remove_all_identities(ac, 1)) 723 debug("keychain_lock_callback: Failed to remove all v1 identities."); 724 725 if (0 != ssh_remove_all_identities(ac, 2)) 726 debug("keychain_lock_callback: Failed to remove all v2 identities."); 727 728 ssh_close_authentication_connection(ac); 729 730cleanup: 731 if (login_keychain) 732 CFRelease(login_keychain); 733 734 return errSecSuccess; 735} 736 737OSStatus 738keychain_unlock_callback(SecKeychainEvent event, SecKeychainCallbackInfo *info, void *context) 739{ 740 OSStatus ret = errSecSuccess; 741 Boolean state = false; 742 SecKeychainRef login_keychain = NULL; 743 744 /* Only care about login keychain */ 745 ret = SecKeychainCopyDefault(&login_keychain); 746 if (ret != noErr) { 747 debug("keychain_lock_callback: Unable to get login keychain."); 748 goto cleanup; 749 } 750 if (!CFEqual(info->keychain, login_keychain)) { 751 goto cleanup; 752 } 753 754 /* No user interaction for keychain actions */ 755 ret = SecKeychainGetUserInteractionAllowed(&state); 756 if (errSecSuccess != ret) 757 debug("keychain_unlock_callback: Unable to determine if user interaction is allowed."); 758 759 if (state) { 760 debug("keychain_unlock_callback: Temporarily denying user interaction."); 761 ret = SecKeychainSetUserInteractionAllowed(false); 762 if (errSecSuccess != ret) 763 error("Keychain unlocked callback: Unable deny user interaction."); 764 } 765 766 /* Silently add all identities from keychain */ 767 debug("keychain_unlock_callback: Adding all identities from keychain, no user interaction."); 768 AuthenticationConnection *ac = ssh_get_authentication_connection(); 769 if (NULL == ac) { 770 error("keychain_unlock_callback: Unable to get authentication connection."); 771 goto cleanup; 772 } 773 ssh_add_from_keychain(ac); 774 ssh_close_authentication_connection(ac); 775 776 /* Set user interaction state back */ 777 if (state) { 778 debug("keychain_unlock_callback: Restoring user interaction."); 779 ret = SecKeychainSetUserInteractionAllowed(state); 780 if (errSecSuccess != ret) 781 error("keychain_unlock_callback: Unable to restore user interaction."); 782 } 783 784cleanup: 785 if (login_keychain) 786 CFRelease(login_keychain); 787 788 return errSecSuccess; 789} 790 791void 792keychain_thread_timer_callback(CFRunLoopTimerRef timer, void *info) 793{ 794 /* Will get here every kCFAbsoluteTimeIntervalSince1904 seconds. */ 795} 796 797void* 798keychain_thread_main(void *msg) 799{ 800 OSStatus ret; 801 802 CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, 803 CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1904, 804 kCFAbsoluteTimeIntervalSince1904, 805 0, 0, keychain_thread_timer_callback, NULL); 806 if (NULL == timer) 807 error("keychain_thread_main: Cannot create timer for runloop."); 808 809 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode); 810 811 ret = SecKeychainAddCallback(&keychain_lock_callback, kSecLockEventMask, NULL); 812 if (errSecSuccess != ret) 813 error("keychain_thread_main: Unable to add keychain lock callback."); 814 815 SecKeychainAddCallback(&keychain_unlock_callback, kSecUnlockEventMask, NULL); 816 if (errSecSuccess != ret) 817 error("keychain_thread_main: Unable to add keychain unlock callback."); 818 819 CFRunLoopRun(); 820 /* NEVER REACHED */ 821 822 return NULL; 823} 824 825/* Start the keychain thread. */ 826void 827keychain_thread_init() 828{ 829 if (!keychain_thread_active) { 830 int ret; 831 pthread_t thread; 832 833 keychain_thread_active = 1; 834 ret = pthread_create(&thread, NULL, &keychain_thread_main, (void*)"keychain-notification-thread"); 835 if (0 != ret) 836 error("keychain_thread_init: pthread_create failed for keychain notification thread."); 837 } 838} 839 840#endif 841