#include "includes.h"
#include <stdio.h>
#include <string.h>
#include "xmalloc.h"
#include "key.h"
#include "authfd.h"
#include "authfile.h"
#if defined(__APPLE_KEYCHAIN__)
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
kSecPasswordGet = 1<<0; kSecPasswordSet = 1<<1; kSecPasswordFail = 1<<2;
#endif
#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
void
store_in_keychain(const char *filename, const char *passphrase)
{
#if defined(__APPLE_KEYCHAIN__)
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;
if (get_boolean_preference("KeychainIntegration", 1, 1) == 0) {
fprintf(stderr, "Keychain integration is disabled.\n");
goto err;
}
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);
rv = SecKeychainFindGenericPassword(NULL, 3, "SSH", filename_len,
(char *)utf8_filename, NULL, NULL, &itemRef);
if (rv == errSecItemNotFound) {
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) {
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:
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
fprintf(stderr, "Keychain is not available on this system\n");
#endif
}
void
remove_from_keychain(const char *filename)
{
#if defined(__APPLE_KEYCHAIN__)
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;
if (get_boolean_preference("KeychainIntegration", 1, 1) == 0) {
fprintf(stderr, "Keychain integration is disabled.\n");
goto err;
}
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);
rv = SecKeychainFindGenericPassword(NULL, 3, "SSH", filename_len,
(const char *)utf8_filename, NULL, NULL, &itemRef);
if (rv == noErr) {
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:
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
fprintf(stderr, "Keychain is not available on this system\n");
#endif
}
int
add_identities_using_keychain(int (*add_identity)(const char *, const char *))
{
#if defined(__APPLE_KEYCHAIN__)
OSStatus rv;
SecKeychainSearchRef searchRef;
SecKeychainItemRef itemRef;
UInt32 length;
void *data;
CFIndex maxsize;
if (get_boolean_preference("KeychainIntegration", 1, 0) == 0)
return 0;
SecKeychainAttribute attrs[] = {
{kSecServiceItemAttr, 3, "SSH"}
};
SecKeychainAttributeList attrList =
{sizeof(attrs) / sizeof(attrs[0]), attrs};
if ((rv = SecKeychainSearchCreateFromAttributes(NULL,
kSecGenericPasswordItemClass, &attrList, &searchRef)) != noErr)
return 0;
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;
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_identity(filename, passphrase);
err:
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
return 1;
#endif
}
char *
keychain_read_passphrase(const char *filename, int oAskPassGUI)
{
#if defined(__APPLE_KEYCHAIN__)
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;
if (get_boolean_preference("KeychainIntegration", 1, 1) == 0)
goto err;
if (get_boolean_preference("AskPassGUI", 1, 1) == 0 || oAskPassGUI == 0)
goto err;
if ((ac = ssh_get_authentication_connection()) == NULL)
goto err;
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);
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;
}
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';
if (noErr != SecPasswordAction(passRef, CFSTR(""), kSecPasswordSet, &length, &data))
fprintf(stderr, "Saving password to keychain failed\n");
char *comment = NULL;
Key *private = key_load_private(filename, result, &comment);
if (NULL == private)
break;
if (ssh_add_identity(ac, private, comment))
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:
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
return NULL;
#endif
}