/* * Copyright (c) 2007 Apple Inc. All rights reserved. * * @APPLE_BSD_LICENSE_HEADER_START@ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Inc. ("Apple") nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @APPLE_BSD_LICENSE_HEADER_END@ */ #include "includes.h" #include #include #include "xmalloc.h" #include "key.h" #include "authfd.h" #include "authfile.h" #if defined(__APPLE_KEYCHAIN__) #include #include /* Our Security/SecPassword.h is not yet API, so I will define the constants that I am using here. */ enum SEC_PASSWORD_OPTS { kSecPasswordGet = 1<<0, // Get password from keychain or user kSecPasswordSet = 1<<1, // Set password (passed in if kSecPasswordGet not set, otherwise from user) kSecPasswordFail = 1<<2, // Wrong password (ignore item in keychain and flag error) }; #endif /* * Platform-specific helper functions. */ #if defined(__APPLE_KEYCHAIN__) static int get_boolean_preference(const char *key, int default_value, int foreground) { int value = default_value; CFStringRef keyRef = NULL; CFPropertyListRef valueRef = NULL; keyRef = CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8); if (keyRef != NULL) valueRef = CFPreferencesCopyAppValue(keyRef, CFSTR("org.openbsd.openssh")); if (valueRef != NULL) if (CFGetTypeID(valueRef) == CFBooleanGetTypeID()) value = CFBooleanGetValue(valueRef); else if (foreground) fprintf(stderr, "Ignoring nonboolean %s preference.\n", key); if (keyRef) CFRelease(keyRef); if (valueRef) CFRelease(valueRef); return value; } #endif /* * Store the passphrase for a given identity in the keychain. */ void store_in_keychain(const char *filename, const char *passphrase) { #if defined(__APPLE_KEYCHAIN__) /* * store_in_keychain * Mac OS X implementation */ CFStringRef cfstr_relative_filename = NULL; CFURLRef cfurl_relative_filename = NULL, cfurl_filename = NULL; CFStringRef cfstr_filename = NULL; CFDataRef cfdata_filename = NULL; CFIndex filename_len; UInt8 *label = NULL; UInt8 *utf8_filename; OSStatus rv; SecKeychainItemRef itemRef = NULL; SecTrustedApplicationRef apps[] = {NULL, NULL, NULL}; CFArrayRef trustedlist = NULL; SecAccessRef initialAccess = NULL; /* Bail out if KeychainIntegration preference is -bool NO */ if (get_boolean_preference("KeychainIntegration", 1, 1) == 0) { fprintf(stderr, "Keychain integration is disabled.\n"); goto err; } /* Interpret filename with the correct encoding. */ if ((cfstr_relative_filename = CFStringCreateWithFileSystemRepresentation(NULL, filename)) == NULL) { fprintf(stderr, "CFStringCreateWithFileSystemRepresentation failed\n"); goto err; } if ((cfurl_relative_filename = CFURLCreateWithFileSystemPath(NULL, cfstr_relative_filename, kCFURLPOSIXPathStyle, false)) == NULL) { fprintf(stderr, "CFURLCreateWithFileSystemPath failed\n"); goto err; } if ((cfurl_filename = CFURLCopyAbsoluteURL(cfurl_relative_filename)) == NULL) { fprintf(stderr, "CFURLCopyAbsoluteURL failed\n"); goto err; } if ((cfstr_filename = CFURLCopyFileSystemPath(cfurl_filename, kCFURLPOSIXPathStyle)) == NULL) { fprintf(stderr, "CFURLCopyFileSystemPath failed\n"); goto err; } if ((cfdata_filename = CFStringCreateExternalRepresentation(NULL, cfstr_filename, kCFStringEncodingUTF8, 0)) == NULL) { fprintf(stderr, "CFStringCreateExternalRepresentation failed\n"); goto err; } filename_len = CFDataGetLength(cfdata_filename); if ((label = xmalloc(filename_len + 5)) == NULL) { fprintf(stderr, "xmalloc failed\n"); goto err; } memcpy(label, "SSH: ", 5); utf8_filename = label + 5; CFDataGetBytes(cfdata_filename, CFRangeMake(0, filename_len), utf8_filename); /* Check if we already have this passphrase. */ rv = SecKeychainFindGenericPassword(NULL, 3, "SSH", filename_len, (char *)utf8_filename, NULL, NULL, &itemRef); if (rv == errSecItemNotFound) { /* Add a new keychain item. */ SecKeychainAttribute attrs[] = { {kSecLabelItemAttr, filename_len + 5, label}, {kSecServiceItemAttr, 3, "SSH"}, {kSecAccountItemAttr, filename_len, utf8_filename} }; SecKeychainAttributeList attrList = {sizeof(attrs) / sizeof(attrs[0]), attrs}; if (SecTrustedApplicationCreateFromPath("/usr/bin/ssh-agent", &apps[0]) != noErr || SecTrustedApplicationCreateFromPath("/usr/bin/ssh-add", &apps[1]) != noErr || SecTrustedApplicationCreateFromPath("/usr/bin/ssh", &apps[2]) != noErr) { fprintf(stderr, "SecTrustedApplicationCreateFromPath failed\n"); goto err; } if ((trustedlist = CFArrayCreate(NULL, (const void **)apps, sizeof(apps) / sizeof(apps[0]), &kCFTypeArrayCallBacks)) == NULL) { fprintf(stderr, "CFArrayCreate failed\n"); goto err; } if (SecAccessCreate(cfstr_filename, trustedlist, &initialAccess) != noErr) { fprintf(stderr, "SecAccessCreate failed\n"); goto err; } if (SecKeychainItemCreateFromContent( kSecGenericPasswordItemClass, &attrList, strlen(passphrase), passphrase, NULL, initialAccess, NULL) == noErr) fprintf(stderr, "Passphrase stored in keychain: %s\n", filename); else fprintf(stderr, "Could not create keychain item\n"); } else if (rv == noErr) { /* Update an existing keychain item. */ if (SecKeychainItemModifyAttributesAndData(itemRef, NULL, strlen(passphrase), passphrase) == noErr) fprintf(stderr, "Passphrase updated in keychain: %s\n", filename); else fprintf(stderr, "Could not modify keychain item\n"); } else fprintf(stderr, "Could not access keychain\n"); err: /* Clean up. */ if (cfstr_relative_filename) CFRelease(cfstr_relative_filename); if (cfurl_relative_filename) CFRelease(cfurl_relative_filename); if (cfurl_filename) CFRelease(cfurl_filename); if (cfstr_filename) CFRelease(cfstr_filename); if (cfdata_filename) CFRelease(cfdata_filename); if (label) xfree(label); if (itemRef) CFRelease(itemRef); if (apps[0]) CFRelease(apps[0]); if (apps[1]) CFRelease(apps[1]); if (apps[2]) CFRelease(apps[2]); if (trustedlist) CFRelease(trustedlist); if (initialAccess) CFRelease(initialAccess); #else /* * store_in_keychain * no keychain implementation */ fprintf(stderr, "Keychain is not available on this system\n"); #endif } /* * Remove the passphrase for a given identity from the keychain. */ void remove_from_keychain(const char *filename) { #if defined(__APPLE_KEYCHAIN__) /* * remove_from_keychain * Mac OS X implementation */ CFStringRef cfstr_relative_filename = NULL; CFURLRef cfurl_relative_filename = NULL, cfurl_filename = NULL; CFStringRef cfstr_filename = NULL; CFDataRef cfdata_filename = NULL; CFIndex filename_len; const UInt8 *utf8_filename; OSStatus rv; SecKeychainItemRef itemRef = NULL; /* Bail out if KeychainIntegration preference is -bool NO */ if (get_boolean_preference("KeychainIntegration", 1, 1) == 0) { fprintf(stderr, "Keychain integration is disabled.\n"); goto err; } /* Interpret filename with the correct encoding. */ if ((cfstr_relative_filename = CFStringCreateWithFileSystemRepresentation(NULL, filename)) == NULL) { fprintf(stderr, "CFStringCreateWithFileSystemRepresentation failed\n"); goto err; } if ((cfurl_relative_filename = CFURLCreateWithFileSystemPath(NULL, cfstr_relative_filename, kCFURLPOSIXPathStyle, false)) == NULL) { fprintf(stderr, "CFURLCreateWithFileSystemPath failed\n"); goto err; } if ((cfurl_filename = CFURLCopyAbsoluteURL(cfurl_relative_filename)) == NULL) { fprintf(stderr, "CFURLCopyAbsoluteURL failed\n"); goto err; } if ((cfstr_filename = CFURLCopyFileSystemPath(cfurl_filename, kCFURLPOSIXPathStyle)) == NULL) { fprintf(stderr, "CFURLCopyFileSystemPath failed\n"); goto err; } if ((cfdata_filename = CFStringCreateExternalRepresentation(NULL, cfstr_filename, kCFStringEncodingUTF8, 0)) == NULL) { fprintf(stderr, "CFStringCreateExternalRepresentation failed\n"); goto err; } filename_len = CFDataGetLength(cfdata_filename); utf8_filename = CFDataGetBytePtr(cfdata_filename); /* Check if we already have this passphrase. */ rv = SecKeychainFindGenericPassword(NULL, 3, "SSH", filename_len, (const char *)utf8_filename, NULL, NULL, &itemRef); if (rv == noErr) { /* Remove the passphrase from the keychain. */ if (SecKeychainItemDelete(itemRef) == noErr) fprintf(stderr, "Passphrase removed from keychain: %s\n", filename); else fprintf(stderr, "Could not remove keychain item\n"); } else if (rv != errSecItemNotFound) fprintf(stderr, "Could not access keychain\n"); err: /* Clean up. */ if (cfstr_relative_filename) CFRelease(cfstr_relative_filename); if (cfurl_relative_filename) CFRelease(cfurl_relative_filename); if (cfurl_filename) CFRelease(cfurl_filename); if (cfstr_filename) CFRelease(cfstr_filename); if (cfdata_filename) CFRelease(cfdata_filename); if (itemRef) CFRelease(itemRef); #else /* * remove_from_keychain * no keychain implementation */ fprintf(stderr, "Keychain is not available on this system\n"); #endif } /* * Add identities to ssh-agent using passphrases stored in the keychain. * Returns zero on success and nonzero on failure. * add_identity is a callback into ssh-agent. It takes a filename and a * passphrase, and attempts to add the identity to the agent. It returns * zero on success and nonzero on failure. */ int add_identities_using_keychain(int (*add_identity)(const char *, const char *)) { #if defined(__APPLE_KEYCHAIN__) /* * add_identities_using_keychain * Mac OS X implementation */ OSStatus rv; SecKeychainSearchRef searchRef; SecKeychainItemRef itemRef; UInt32 length; void *data; CFIndex maxsize; /* Bail out if KeychainIntegration preference is -bool NO */ if (get_boolean_preference("KeychainIntegration", 1, 0) == 0) return 0; /* Search for SSH passphrases in the keychain */ SecKeychainAttribute attrs[] = { {kSecServiceItemAttr, 3, "SSH"} }; SecKeychainAttributeList attrList = {sizeof(attrs) / sizeof(attrs[0]), attrs}; if ((rv = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attrList, &searchRef)) != noErr) return 0; /* Iterate through the search results. */ while ((rv = SecKeychainSearchCopyNext(searchRef, &itemRef)) == noErr) { UInt32 tag = kSecAccountItemAttr; UInt32 format = kSecFormatUnknown; SecKeychainAttributeInfo info = {1, &tag, &format}; SecKeychainAttributeList *itemAttrList = NULL; CFStringRef cfstr_filename = NULL; char *filename = NULL; char *passphrase = NULL; /* Retrieve filename and passphrase. */ if ((rv = SecKeychainItemCopyAttributesAndData(itemRef, &info, NULL, &itemAttrList, &length, &data)) != noErr) goto err; if (itemAttrList->count != 1) goto err; cfstr_filename = CFStringCreateWithBytes(NULL, itemAttrList->attr->data, itemAttrList->attr->length, kCFStringEncodingUTF8, true); maxsize = CFStringGetMaximumSizeOfFileSystemRepresentation( cfstr_filename); if ((filename = xmalloc(maxsize)) == NULL) goto err; if (CFStringGetFileSystemRepresentation(cfstr_filename, filename, maxsize) == false) goto err; if ((passphrase = xmalloc(length + 1)) == NULL) goto err; memcpy(passphrase, data, length); passphrase[length] = '\0'; /* Add the identity. */ add_identity(filename, passphrase); err: /* Clean up. */ if (itemRef) CFRelease(itemRef); if (cfstr_filename) CFRelease(cfstr_filename); if (filename) xfree(filename); if (passphrase) xfree(passphrase); if (itemAttrList) SecKeychainItemFreeAttributesAndData(itemAttrList, data); } CFRelease(searchRef); return 0; #else /* * add_identities_using_keychain * no implementation */ return 1; #endif } /* * Prompt the user for a key's passphrase. The user will be offered the option * of storing the passphrase in their keychain. Returns the passphrase * (which the caller is responsible for xfreeing), or NULL if this function * fails or is not implemented. If this function is not implemented, ssh will * fall back on the standard read_passphrase function, and the user will need * to use ssh-add -K to add their keys to the keychain. */ char * keychain_read_passphrase(const char *filename, int oAskPassGUI) { #if defined(__APPLE_KEYCHAIN__) /* * keychain_read_passphrase * Mac OS X implementation */ CFStringRef cfstr_relative_filename = NULL; CFURLRef cfurl_relative_filename = NULL, cfurl_filename = NULL; CFStringRef cfstr_filename = NULL; CFDataRef cfdata_filename = NULL; CFIndex filename_len; UInt8 *label = NULL; UInt8 *utf8_filename; SecPasswordRef passRef = NULL; SecTrustedApplicationRef apps[] = {NULL, NULL, NULL}; CFArrayRef trustedlist = NULL; SecAccessRef initialAccess = NULL; CFURLRef path = NULL; CFStringRef pathFinal = NULL; CFURLRef bundle_url = NULL; CFBundleRef bundle = NULL; CFStringRef promptTemplate = NULL, prompt = NULL; UInt32 length; const void *data; AuthenticationConnection *ac = NULL; char *result = NULL; /* Bail out if KeychainIntegration preference is -bool NO */ if (get_boolean_preference("KeychainIntegration", 1, 1) == 0) goto err; /* Bail out if the user set AskPassGUI preference to -bool NO */ if (get_boolean_preference("AskPassGUI", 1, 1) == 0 || oAskPassGUI == 0) goto err; /* Bail out if we can't communicate with ssh-agent */ if ((ac = ssh_get_authentication_connection()) == NULL) goto err; /* Interpret filename with the correct encoding. */ if ((cfstr_relative_filename = CFStringCreateWithFileSystemRepresentation(NULL, filename)) == NULL) { fprintf(stderr, "CFStringCreateWithFileSystemRepresentation failed\n"); goto err; } if ((cfurl_relative_filename = CFURLCreateWithFileSystemPath(NULL, cfstr_relative_filename, kCFURLPOSIXPathStyle, false)) == NULL) { fprintf(stderr, "CFURLCreateWithFileSystemPath failed\n"); goto err; } if ((cfurl_filename = CFURLCopyAbsoluteURL(cfurl_relative_filename)) == NULL) { fprintf(stderr, "CFURLCopyAbsoluteURL failed\n"); goto err; } if ((cfstr_filename = CFURLCopyFileSystemPath(cfurl_filename, kCFURLPOSIXPathStyle)) == NULL) { fprintf(stderr, "CFURLCopyFileSystemPath failed\n"); goto err; } if ((cfdata_filename = CFStringCreateExternalRepresentation(NULL, cfstr_filename, kCFStringEncodingUTF8, 0)) == NULL) { fprintf(stderr, "CFStringCreateExternalRepresentation failed\n"); goto err; } filename_len = CFDataGetLength(cfdata_filename); if ((label = xmalloc(filename_len + 5)) == NULL) { fprintf(stderr, "xmalloc failed\n"); goto err; } memcpy(label, "SSH: ", 5); utf8_filename = label + 5; CFDataGetBytes(cfdata_filename, CFRangeMake(0, filename_len), utf8_filename); /* Build a SecPasswordRef. */ SecKeychainAttribute searchAttrs[] = { {kSecServiceItemAttr, 3, "SSH"}, {kSecAccountItemAttr, filename_len, utf8_filename} }; SecKeychainAttributeList searchAttrList = {sizeof(searchAttrs) / sizeof(searchAttrs[0]), searchAttrs}; SecKeychainAttribute attrs[] = { {kSecLabelItemAttr, filename_len + 5, label}, {kSecServiceItemAttr, 3, "SSH"}, {kSecAccountItemAttr, filename_len, utf8_filename} }; SecKeychainAttributeList attrList = {sizeof(attrs) / sizeof(attrs[0]), attrs}; if (SecGenericPasswordCreate(&searchAttrList, &attrList, &passRef) != noErr) { fprintf(stderr, "SecGenericPasswordCreate failed\n"); goto err; } if (SecTrustedApplicationCreateFromPath("/usr/bin/ssh-agent", &apps[0]) != noErr || SecTrustedApplicationCreateFromPath("/usr/bin/ssh-add", &apps[1]) != noErr || SecTrustedApplicationCreateFromPath("/usr/bin/ssh", &apps[2]) != noErr) { fprintf(stderr, "SecTrustedApplicationCreateFromPath failed\n"); goto err; } if ((trustedlist = CFArrayCreate(NULL, (const void **)apps, sizeof(apps) / sizeof(apps[0]), &kCFTypeArrayCallBacks)) == NULL) { fprintf(stderr, "CFArrayCreate failed\n"); goto err; } if (SecAccessCreate(cfstr_filename, trustedlist, &initialAccess) != noErr) { fprintf(stderr, "SecAccessCreate failed\n"); goto err; } if (SecPasswordSetInitialAccess(passRef, initialAccess) != noErr) { fprintf(stderr, "SecPasswordSetInitialAccess failed\n"); goto err; } /* Request the passphrase from the user. */ if ((path = CFURLCreateFromFileSystemRepresentation(NULL, (UInt8 *)filename, strlen(filename), false)) == NULL) { fprintf(stderr, "CFURLCreateFromFileSystemRepresentation failed\n"); goto err; } if ((pathFinal = CFURLCopyLastPathComponent(path)) == NULL) { fprintf(stderr, "CFURLCopyLastPathComponent failed\n"); goto err; } if (!((bundle_url = CFURLCreateWithFileSystemPath(NULL, CFSTR("/System/Library/CoreServices/"), kCFURLPOSIXPathStyle, true)) != NULL && (bundle = CFBundleCreate(NULL, bundle_url)) != NULL && (promptTemplate = CFCopyLocalizedStringFromTableInBundle( CFSTR("Enter your password for the SSH key \"%@\"."), CFSTR("OpenSSH"), bundle, "Text of the dialog asking the user for" "their passphrase. The %@ will be replaced with the filename of a" "specific key.")) != NULL) && (promptTemplate = CFStringCreateCopy(NULL, CFSTR("Enter your password for the SSH key \"%@\"."))) == NULL) { fprintf(stderr, "CFStringCreateCopy failed\n"); goto err; } if ((prompt = CFStringCreateWithFormat(NULL, NULL, promptTemplate, pathFinal)) == NULL) { fprintf(stderr, "CFStringCreateWithFormat failed\n"); goto err; } switch (SecPasswordAction(passRef, prompt, kSecPasswordGet|kSecPasswordFail, &length, &data)) { case noErr: result = xmalloc(length + 1); memcpy(result, data, length); result[length] = '\0'; /* Save password in keychain if requested. */ if (noErr != SecPasswordAction(passRef, CFSTR(""), kSecPasswordSet, &length, &data)) fprintf(stderr, "Saving password to keychain failed\n"); /* Add password to agent. */ char *comment = NULL; Key *private = key_load_private(filename, result, &comment); if (NULL == private) break; if (ssh_add_identity_constrained(ac, private, comment, 0, 0)) fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); else fprintf(stderr, "Could not add identity: %s\n", filename); xfree(comment); key_free(private); break; case errAuthorizationCanceled: result = xmalloc(1); *result = '\0'; break; default: goto err; } err: /* Clean up. */ if (cfstr_relative_filename) CFRelease(cfstr_relative_filename); if (cfurl_relative_filename) CFRelease(cfurl_relative_filename); if (cfurl_filename) CFRelease(cfurl_filename); if (cfstr_filename) CFRelease(cfstr_filename); if (cfdata_filename) CFRelease(cfdata_filename); if (label) xfree(label); if (passRef) CFRelease(passRef); if (apps[0]) CFRelease(apps[0]); if (apps[1]) CFRelease(apps[1]); if (apps[2]) CFRelease(apps[2]); if (trustedlist) CFRelease(trustedlist); if (initialAccess) CFRelease(initialAccess); if (path) CFRelease(path); if (pathFinal) CFRelease(pathFinal); if (bundle_url) CFRelease(bundle_url); if (bundle) CFRelease(bundle); if (promptTemplate) CFRelease(promptTemplate); if (prompt) CFRelease(prompt); if (ac) ssh_close_authentication_connection(ac); return result; #else /* * keychain_read_passphrase * no implementation */ return NULL; #endif } #if defined(__APPLE_KEYCHAIN__) volatile sig_atomic_t keychain_thread_active = 0; OSStatus keychain_lock_callback(SecKeychainEvent event, SecKeychainCallbackInfo *info, void *context) { SecKeychainRef login_keychain = NULL; OSStatus retval = noErr; /* Only care about login keychain */ retval = SecKeychainCopyDefault(&login_keychain); if (retval != noErr) { debug("keychain_lock_callback: Unable to get login keychain, doing nothing."); goto cleanup; } if (!CFEqual(info->keychain, login_keychain)) { goto cleanup; } AuthenticationConnection *ac = ssh_get_authentication_connection(); if (NULL == ac) { error("keychain_lock_callback: Unable to get authentication connection."); goto cleanup; } /* Silently remove all identitites */ debug("keychain_lock_callback: Removing all identities."); if (0 != ssh_remove_all_identities(ac, 1)) debug("keychain_lock_callback: Failed to remove all v1 identities."); if (0 != ssh_remove_all_identities(ac, 2)) debug("keychain_lock_callback: Failed to remove all v2 identities."); ssh_close_authentication_connection(ac); cleanup: if (login_keychain) CFRelease(login_keychain); return errSecSuccess; } OSStatus keychain_unlock_callback(SecKeychainEvent event, SecKeychainCallbackInfo *info, void *context) { OSStatus ret = errSecSuccess; Boolean state = false; SecKeychainRef login_keychain = NULL; /* Only care about login keychain */ ret = SecKeychainCopyDefault(&login_keychain); if (ret != noErr) { debug("keychain_lock_callback: Unable to get login keychain."); goto cleanup; } if (!CFEqual(info->keychain, login_keychain)) { goto cleanup; } /* No user interaction for keychain actions */ ret = SecKeychainGetUserInteractionAllowed(&state); if (errSecSuccess != ret) debug("keychain_unlock_callback: Unable to determine if user interaction is allowed."); if (state) { debug("keychain_unlock_callback: Temporarily denying user interaction."); ret = SecKeychainSetUserInteractionAllowed(false); if (errSecSuccess != ret) error("Keychain unlocked callback: Unable deny user interaction."); } /* Silently add all identities from keychain */ debug("keychain_unlock_callback: Adding all identities from keychain, no user interaction."); AuthenticationConnection *ac = ssh_get_authentication_connection(); if (NULL == ac) { error("keychain_unlock_callback: Unable to get authentication connection."); goto cleanup; } ssh_add_from_keychain(ac); ssh_close_authentication_connection(ac); /* Set user interaction state back */ if (state) { debug("keychain_unlock_callback: Restoring user interaction."); ret = SecKeychainSetUserInteractionAllowed(state); if (errSecSuccess != ret) error("keychain_unlock_callback: Unable to restore user interaction."); } cleanup: if (login_keychain) CFRelease(login_keychain); return errSecSuccess; } void keychain_thread_timer_callback(CFRunLoopTimerRef timer, void *info) { /* Will get here every kCFAbsoluteTimeIntervalSince1904 seconds. */ } void* keychain_thread_main(void *msg) { OSStatus ret; CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1904, kCFAbsoluteTimeIntervalSince1904, 0, 0, keychain_thread_timer_callback, NULL); if (NULL == timer) error("keychain_thread_main: Cannot create timer for runloop."); CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode); ret = SecKeychainAddCallback(&keychain_lock_callback, kSecLockEventMask, NULL); if (errSecSuccess != ret) error("keychain_thread_main: Unable to add keychain lock callback."); SecKeychainAddCallback(&keychain_unlock_callback, kSecUnlockEventMask, NULL); if (errSecSuccess != ret) error("keychain_thread_main: Unable to add keychain unlock callback."); CFRunLoopRun(); /* NEVER REACHED */ return NULL; } /* Start the keychain thread. */ void keychain_thread_init() { if (!keychain_thread_active) { int ret; pthread_t thread; keychain_thread_active = 1; ret = pthread_create(&thread, NULL, &keychain_thread_main, (void*)"keychain-notification-thread"); if (0 != ret) error("keychain_thread_init: pthread_create failed for keychain notification thread."); } } #endif