Created
January 1, 2015 23:03
-
-
Save jbergler/ebaa38fcdda2e15f25a1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- a/Makefile.in.old | |
+++ b/Makefile.in | |
@@ -59,6 +59,7 @@ | |
ENT=@ENT@ | |
XAUTH_PATH=@XAUTH_PATH@ | |
LDFLAGS=-L. -Lopenbsd-compat/ @LDFLAGS@ | |
+KEYCHAIN_LDFLAGS=@KEYCHAIN_LDFLAGS@ | |
EXEEXT=@EXEEXT@ | |
MANFMT=@MANFMT@ | |
@@ -108,6 +109,8 @@ | |
sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ | |
sandbox-seccomp-filter.o sandbox-capsicum.o | |
+KEYCHAINOBJS=keychain.o | |
+ | |
MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-keysign.8.out ssh-pkcs11-helper.8.out sshd_config.5.out ssh_config.5.out | |
MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-keysign.8 ssh-pkcs11-helper.8 sshd_config.5 ssh_config.5 | |
MANTYPE = @MANTYPE@ | |
@@ -143,6 +146,7 @@ | |
$(LIBSSH_OBJS): Makefile.in config.h | |
$(SSHOBJS): Makefile.in config.h | |
$(SSHDOBJS): Makefile.in config.h | |
+$(KEYCHAINOBJS): Makefile.in config.h | |
.c.o: | |
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ | |
@@ -156,8 +160,8 @@ | |
$(AR) rv $@ $(LIBSSH_OBJS) | |
$(RANLIB) $@ | |
-ssh$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHOBJS) | |
- $(LD) -o $@ $(SSHOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHLIBS) $(LIBS) $(GSSLIBS) | |
+ssh$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHOBJS) $(KEYCHAINOBJS) | |
+ $(LD) -o $@ $(SSHOBJS) $(KEYCHAINOBJS) $(LDFLAGS) $(KEYCHAIN_LDFLAGS) -lssh -lopenbsd-compat $(SSHLIBS) $(LIBS) $(GSSLIBS) | |
sshd$(EXEEXT): libssh.a $(LIBCOMPAT) $(SSHDOBJS) | |
$(LD) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHDLIBS) $(LIBS) $(GSSLIBS) $(K5LIBS) | |
@@ -165,11 +169,11 @@ | |
scp$(EXEEXT): $(LIBCOMPAT) libssh.a scp.o progressmeter.o | |
$(LD) -o $@ scp.o progressmeter.o bufaux.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
-ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-add.o | |
- $(LD) -o $@ ssh-add.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
+ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-add.o $(KEYCHAINOBJS) | |
+ $(LD) -o $@ ssh-add.o $(KEYCHAINOBJS) $(LDFLAGS) $(KEYCHAIN_LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
-ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-agent.o ssh-pkcs11-client.o | |
- $(LD) -o $@ ssh-agent.o ssh-pkcs11-client.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
+ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-agent.o ssh-pkcs11-client.o $(KEYCHAINOBJS) | |
+ $(LD) -o $@ ssh-agent.o ssh-pkcs11-client.o $(KEYCHAINOBJS) $(LDFLAGS) $(KEYCHAIN_LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
ssh-keygen$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keygen.o | |
$(LD) -o $@ ssh-keygen.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
@@ -293,7 +297,7 @@ | |
$(INSTALL) -m 0755 $(STRIP_OPT) ssh-keygen$(EXEEXT) $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT) | |
$(INSTALL) -m 0755 $(STRIP_OPT) ssh-keyscan$(EXEEXT) $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT) | |
$(INSTALL) -m 0755 $(STRIP_OPT) sshd$(EXEEXT) $(DESTDIR)$(sbindir)/sshd$(EXEEXT) | |
- $(INSTALL) -m 4711 $(STRIP_OPT) ssh-keysign$(EXEEXT) $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) | |
+ $(INSTALL) -m 0711 $(STRIP_OPT) ssh-keysign$(EXEEXT) $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) | |
$(INSTALL) -m 0755 $(STRIP_OPT) ssh-pkcs11-helper$(EXEEXT) $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT) | |
$(INSTALL) -m 0755 $(STRIP_OPT) sftp$(EXEEXT) $(DESTDIR)$(bindir)/sftp$(EXEEXT) | |
$(INSTALL) -m 0755 $(STRIP_OPT) sftp-server$(EXEEXT) $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) | |
--- a/audit-bsm.c.old | |
+++ b/audit-bsm.c | |
@@ -263,7 +263,12 @@ | |
pid_t pid = getpid(); | |
AuditInfoTermID tid = ssh_bsm_tid; | |
- if (the_authctxt != NULL && the_authctxt->valid) { | |
+ if (the_authctxt == NULL) { | |
+ error("BSM audit: audit record internal error (NULL ctxt)"); | |
+ abort(); | |
+ } | |
+ | |
+ if (the_authctxt->valid) { | |
uid = the_authctxt->pw->pw_uid; | |
gid = the_authctxt->pw->pw_gid; | |
} | |
--- a/auth-pam.c.old | |
+++ b/auth-pam.c | |
@@ -793,10 +793,11 @@ | |
free(msg); | |
return (0); | |
} | |
- error("PAM: %s for %s%.100s from %.100s", msg, | |
+ error("PAM: %s for %s%.100s from %.100s via %s", msg, | |
sshpam_authctxt->valid ? "" : "illegal user ", | |
sshpam_authctxt->user, | |
- get_remote_name_or_ip(utmp_len, options.use_dns)); | |
+ get_remote_name_or_ip(utmp_len, options.use_dns), | |
+ get_local_ipaddr(packet_get_connection_in())); | |
/* FALLTHROUGH */ | |
default: | |
*num = 0; | |
--- a/auth.c.old | |
+++ b/auth.c | |
@@ -211,7 +211,7 @@ | |
} | |
if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { | |
/* Get the user's group access list (primary and supplementary) */ | |
- if (ga_init(pw->pw_name, pw->pw_gid) == 0) { | |
+ if (ga_init(pw) == 0) { | |
logit("User %.100s from %.100s not allowed because " | |
"not in any group", pw->pw_name, hostname); | |
return 0; | |
--- a/authfd.c.old | |
+++ b/authfd.c | |
@@ -650,6 +650,29 @@ | |
return decode_reply(type); | |
} | |
+/* | |
+ * Adds identities using passphrases stored in the keychain. This call is not | |
+ * meant to be used by normal applications. | |
+ */ | |
+ | |
+int | |
+ssh_add_from_keychain(AuthenticationConnection *auth) | |
+{ | |
+ Buffer msg; | |
+ int type; | |
+ | |
+ buffer_init(&msg); | |
+ buffer_put_char(&msg, SSH_AGENTC_ADD_FROM_KEYCHAIN); | |
+ | |
+ if (ssh_request_reply(auth, &msg, &msg) == 0) { | |
+ buffer_free(&msg); | |
+ return 0; | |
+ } | |
+ type = buffer_get_char(&msg); | |
+ buffer_free(&msg); | |
+ return decode_reply(type); | |
+} | |
+ | |
int | |
decode_reply(int type) | |
{ | |
--- a/authfd.h.old | |
+++ b/authfd.h | |
@@ -49,6 +49,9 @@ | |
#define SSH2_AGENTC_ADD_ID_CONSTRAINED 25 | |
#define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26 | |
+/* keychain */ | |
+#define SSH_AGENTC_ADD_FROM_KEYCHAIN 27 | |
+ | |
#define SSH_AGENT_CONSTRAIN_LIFETIME 1 | |
#define SSH_AGENT_CONSTRAIN_CONFIRM 2 | |
--- a/config.h.in.old | |
+++ b/config.h.in | |
@@ -81,6 +81,18 @@ | |
/* FreeBSD strnvis argument order is swapped compared to OpenBSD */ | |
#undef BROKEN_STRNVIS | |
+/* platform uses an in-memory credentials cache */ | |
+#undef USE_CCAPI | |
+ | |
+/* platform has a Security Authorization Session API */ | |
+#undef USE_SECURITY_SESSION_API | |
+ | |
+/* Define to 1 if you have the `copyfile' function. */ | |
+#undef HAVE_COPYFILE | |
+ | |
+/* Define to 1 if you have the <copyfile.h> header file. */ | |
+#undef HAVE_COPYFILE_H | |
+ | |
/* tcgetattr with ICANON may hang */ | |
#undef BROKEN_TCGETATTR_ICANON | |
--- a/configure.ac.old | |
+++ b/configure.ac | |
@@ -4766,10 +4766,40 @@ | |
#endif | |
]) | |
+dnl Keychain support | |
+AC_ARG_WITH(keychain, | |
+ [ --with-keychain=apple Use Mac OS X Keychain], | |
+ [ | |
+ case "$withval" in | |
+ apple|no) | |
+ KEYCHAIN=$withval | |
+ ;; | |
+ *) | |
+ AC_MSG_ERROR(invalid keychain type: $withval) | |
+ ;; | |
+ esac | |
+ ] | |
+) | |
+if test ! -z "$KEYCHAIN" -a "$KEYCHAIN" != "no"; then | |
+ case "$KEYCHAIN" in | |
+ apple) | |
+ AC_CHECK_HEADERS(Security/Security.h, [ | |
+ CPPFLAGS="$CPPFLAGS -D__APPLE_KEYCHAIN__" | |
+ KEYCHAIN_LDFLAGS="-framework Security -framework CoreFoundation" | |
+ AC_SUBST(KEYCHAIN_LDFLAGS) | |
+ ], | |
+ AC_MSG_WARN([Security framework not found. Disabling Mac OS X Keychain support.])) | |
+ ;; | |
+ esac | |
+fi | |
+ | |
dnl Adding -Werror to CFLAGS early prevents configure tests from running. | |
dnl Add now. | |
CFLAGS="$CFLAGS $werror_flags" | |
+AC_CHECK_FUNCS(copyfile) | |
+AC_CHECK_HEADERS(copyfile.h) | |
+ | |
if test "x$ac_cv_func_getaddrinfo" != "xyes" ; then | |
TEST_SSH_IPV6=no | |
else | |
--- a/groupaccess.c.old | |
+++ b/groupaccess.c | |
@@ -34,38 +34,67 @@ | |
#include <stdlib.h> | |
#include <string.h> | |
+#ifdef __APPLE_MEMBERSHIP__ | |
+#include <membership.h> | |
+#endif | |
+ | |
#include "xmalloc.h" | |
#include "groupaccess.h" | |
#include "match.h" | |
#include "log.h" | |
+#ifdef __APPLE_MEMBERSHIP__ | |
+// SPI for 5235093 | |
+int32_t getgrouplist_2(const char *, gid_t, gid_t **); | |
+int32_t getgroupcount(const char *, gid_t); | |
+#endif | |
+ | |
static int ngroups; | |
static char **groups_byname; | |
+#ifdef __APPLE_MEMBERSHIP__ | |
+uuid_t u_uuid; | |
+#endif | |
/* | |
* Initialize group access list for user with primary (base) and | |
* supplementary groups. Return the number of groups in the list. | |
*/ | |
int | |
-ga_init(const char *user, gid_t base) | |
+ga_init(struct passwd *pw) | |
{ | |
- gid_t *groups_bygid; | |
+ gid_t *groups_bygid = NULL; | |
int i, j; | |
struct group *gr; | |
+#ifdef __APPLE_MEMBERSHIP__ | |
+ if (0 != mbr_uid_to_uuid(pw->pw_uid, u_uuid)) | |
+ return 0; | |
+#endif | |
+ | |
if (ngroups > 0) | |
ga_free(); | |
+#ifndef __APPLE_MEMBERSHIP__ | |
ngroups = NGROUPS_MAX; | |
#if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX) | |
ngroups = MAX(NGROUPS_MAX, sysconf(_SC_NGROUPS_MAX)); | |
-#endif | |
- | |
+#endif | |
groups_bygid = xcalloc(ngroups, sizeof(*groups_bygid)); | |
+#else | |
+ if (-1 == (ngroups = getgrouplist_2(pw->pw_name, pw->pw_gid, | |
+ &groups_bygid))) { | |
+ logit("getgrouplist_2 failed"); | |
+ return 0; | |
+ } | |
+#endif | |
groups_byname = xcalloc(ngroups, sizeof(*groups_byname)); | |
- | |
- if (getgrouplist(user, base, groups_bygid, &ngroups) == -1) | |
- logit("getgrouplist: groups list too small"); | |
+#ifndef __APPLE_MEMBERSHIP__ | |
+ if (getgrouplist(pw->pw_name, pw->pw_gid, groups_bygid, &ngroups) == -1) { | |
+ logit("getgrouplist: groups list too small"); | |
+ free(groups_bygid); | |
+ return 0; | |
+ } | |
+#endif | |
for (i = 0, j = 0; i < ngroups; i++) | |
if ((gr = getgrgid(groups_bygid[i])) != NULL) | |
groups_byname[j++] = xstrdup(gr->gr_name); | |
@@ -76,16 +105,32 @@ | |
/* | |
* Return 1 if one of user's groups is contained in groups. | |
* Return 0 otherwise. Use match_pattern() for string comparison. | |
+ * Use mbr_check_membership() for membership checking on Mac OS X. | |
*/ | |
int | |
ga_match(char * const *groups, int n) | |
{ | |
+#ifdef __APPLE_MEMBERSHIP__ | |
+ int i, ismember = 0; | |
+ uuid_t g_uuid; | |
+ struct group *grp; | |
+ | |
+ for (i = 0; i < n; i++) { | |
+ if ((grp = getgrnam(groups[i])) == NULL || | |
+ (mbr_gid_to_uuid(grp->gr_gid, g_uuid) != 0) || | |
+ (mbr_check_membership(u_uuid, g_uuid, &ismember) != 0)) | |
+ return 0; | |
+ if (ismember) | |
+ return 1; | |
+ } | |
+#else | |
int i, j; | |
for (i = 0; i < ngroups; i++) | |
for (j = 0; j < n; j++) | |
if (match_pattern(groups_byname[i], groups[j])) | |
return 1; | |
+#endif | |
return 0; | |
} | |
--- a/groupaccess.h.old | |
+++ b/groupaccess.h | |
@@ -27,7 +27,7 @@ | |
#ifndef GROUPACCESS_H | |
#define GROUPACCESS_H | |
-int ga_init(const char *, gid_t); | |
+int ga_init(struct passwd *); | |
int ga_match(char * const *, int); | |
int ga_match_pattern_list(const char *); | |
void ga_free(void); | |
--- a/keychain.c.old 1970-01-01 01:00:00.000000000 +0100 | |
+++ b/keychain.c | |
@@ -0,0 +1,694 @@ | |
+/* | |
+ * 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 <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> | |
+ | |
+/* Our Security/SecPassword.h is not yet API, so I will define the constants that I am using here. */ | |
+int kSecPasswordGet = 1<<0; // Get password from keychain or user | |
+int kSecPasswordSet = 1<<1; // Set password (passed in if kSecPasswordGet not set, otherwise from user) | |
+int kSecPasswordFail = 1<<2; // Wrong password (ignore item in keychain and flag error) | |
+OSStatus SecGenericPasswordCreate(SecKeychainAttributeList *searchAttrList, SecKeychainAttributeList *itemAttrList, SecPasswordRef *itemRef); | |
+OSStatus SecPasswordAction(SecPasswordRef itemRef, CFTypeRef message, UInt32 flags, UInt32 *length, const void **data); | |
+OSStatus SecPasswordSetInitialAccess(SecPasswordRef itemRef, SecAccessRef accessRef); | |
+ | |
+#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) | |
+ free(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) | |
+ free(filename); | |
+ if (passphrase) | |
+ free(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 freeing), 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); | |
+ free(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) | |
+ free(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 | |
+ | |
+} | |
--- a/keychain.h.old 1970-01-01 01:00:00.000000000 +0100 | |
+++ b/keychain.h | |
@@ -0,0 +1,45 @@ | |
+/* | |
+ * 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@ | |
+ */ | |
+ | |
+/* | |
+ * KEYCHAIN indicates that keychain functionality is present. | |
+ * KEYCHAIN_* indicates the implementation to use, and implies KEYCHAIN. | |
+ */ | |
+#if defined(__APPLE_KEYCHAIN__) | |
+#define KEYCHAIN | |
+#endif | |
+ | |
+void store_in_keychain(const char *filename, const char *passphrase); | |
+void remove_from_keychain(const char *filename); | |
+int add_identities_using_keychain( | |
+ int (*add_identity)(const char *, const char *)); | |
+char *keychain_read_passphrase(const char *filename, int oAskPassGUI); | |
--- a/readconf.c.old | |
+++ b/readconf.c | |
@@ -150,6 +150,9 @@ | |
oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, | |
oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, | |
oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ oAskPassGUI, | |
+#endif | |
oStreamLocalBindMask, oStreamLocalBindUnlink, | |
oIgnoredUnknownOption, oDeprecated, oUnsupported | |
} OpCodes; | |
@@ -266,6 +269,9 @@ | |
{ "streamlocalbindmask", oStreamLocalBindMask }, | |
{ "streamlocalbindunlink", oStreamLocalBindUnlink }, | |
{ "ignoreunknown", oIgnoreUnknown }, | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ { "askpassgui", oAskPassGUI }, | |
+#endif | |
{ NULL, oBadOption } | |
}; | |
@@ -1358,6 +1364,12 @@ | |
charptr = &options->ignored_unknown; | |
goto parse_string; | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ case oAskPassGUI: | |
+ intptr = &options->ask_pass_gui; | |
+ goto parse_flag; | |
+#endif | |
+ | |
case oProxyUseFdpass: | |
intptr = &options->proxy_use_fdpass; | |
goto parse_flag; | |
@@ -1604,6 +1616,9 @@ | |
options->request_tty = -1; | |
options->proxy_use_fdpass = -1; | |
options->ignored_unknown = NULL; | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ options->ask_pass_gui = -1; | |
+#endif | |
options->num_canonical_domains = 0; | |
options->num_permitted_cnames = 0; | |
options->canonicalize_max_dots = -1; | |
@@ -1778,6 +1793,10 @@ | |
options->ip_qos_bulk = IPTOS_THROUGHPUT; | |
if (options->request_tty == -1) | |
options->request_tty = REQUEST_TTY_AUTO; | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ if (options->ask_pass_gui == -1) | |
+ options->ask_pass_gui = 1; | |
+#endif | |
if (options->proxy_use_fdpass == -1) | |
options->proxy_use_fdpass = 0; | |
if (options->canonicalize_max_dots == -1) | |
--- a/readconf.h.old | |
+++ b/readconf.h | |
@@ -145,6 +145,10 @@ | |
struct allowed_cname permitted_cnames[MAX_CANON_DOMAINS]; | |
char *ignored_unknown; /* Pattern list of unknown tokens to ignore */ | |
+ | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ int ask_pass_gui; | |
+#endif | |
} Options; | |
#define SSH_CANONICALISE_NO 0 | |
--- a/scp.1.old | |
+++ b/scp.1 | |
@@ -19,7 +19,7 @@ | |
.Sh SYNOPSIS | |
.Nm scp | |
.Bk -words | |
-.Op Fl 12346BCpqrv | |
+.Op Fl 12346BCEpqrv | |
.Op Fl c Ar cipher | |
.Op Fl F Ar ssh_config | |
.Op Fl i Ar identity_file | |
@@ -95,6 +95,8 @@ | |
flag to | |
.Xr ssh 1 | |
to enable compression. | |
+.It Fl E | |
+Preserves extended attributes, resource forks, and ACLs. Requires both ends to be running Mac OS X 10.4 or later. | |
.It Fl c Ar cipher | |
Selects the cipher to use for encrypting the data transfer. | |
This option is directly passed to | |
--- a/scp.c.old | |
+++ b/scp.c | |
@@ -78,6 +78,9 @@ | |
#ifdef HAVE_SYS_STAT_H | |
# include <sys/stat.h> | |
#endif | |
+#ifdef __APPLE_XSAN__ | |
+#include <sys/mount.h> | |
+#endif | |
#ifdef HAVE_POLL_H | |
#include <poll.h> | |
#else | |
@@ -114,6 +117,11 @@ | |
#include "misc.h" | |
#include "progressmeter.h" | |
+#ifdef HAVE_COPYFILE_H | |
+#include <libgen.h> | |
+#include <copyfile.h> | |
+#endif | |
+ | |
extern char *__progname; | |
#define COPY_BUFLEN 16384 | |
@@ -150,6 +158,12 @@ | |
/* This is used to store the pid of ssh_program */ | |
pid_t do_cmd_pid = -1; | |
+#ifdef HAVE_COPYFILE | |
+int copy_xattr = 0; | |
+int md_flag = 0; | |
+#endif | |
+ | |
+ | |
static void | |
killchild(int signo) | |
{ | |
@@ -395,7 +409,11 @@ | |
addargs(&args, "-oClearAllForwardings=yes"); | |
fflag = tflag = 0; | |
+#if HAVE_COPYFILE | |
+ while ((ch = getopt(argc, argv, "dfl:prtvBCEc:i:P:q12346S:o:F:")) != -1) | |
+#else | |
while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1) | |
+#endif | |
switch (ch) { | |
/* User-visible flags. */ | |
case '1': | |
@@ -456,6 +474,11 @@ | |
showprogress = 0; | |
break; | |
+#ifdef HAVE_COPYFILE | |
+ case 'E': | |
+ copy_xattr = 1; | |
+ break; | |
+#endif | |
/* Server options. */ | |
case 'd': | |
targetshouldbedirectory = 1; | |
@@ -505,7 +528,12 @@ | |
remin = remout = -1; | |
do_cmd_pid = -1; | |
/* Command to be executed on remote system using "ssh". */ | |
+#if HAVE_COPYFILE | |
+ (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s%s", | |
+ copy_xattr ? " -E" : "", | |
+#else | |
(void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", | |
+#endif | |
verbose_mode ? " -v" : "", | |
iamrecursive ? " -r" : "", pflag ? " -p" : "", | |
targetshouldbedirectory ? " -d" : ""); | |
@@ -751,6 +779,10 @@ | |
int fd = -1, haderr, indx; | |
char *last, *name, buf[2048], encname[MAXPATHLEN]; | |
int len; | |
+#if HAVE_COPYFILE | |
+ char md_name[MAXPATHLEN]; | |
+ char *md_tmp; | |
+#endif | |
for (indx = 0; indx < argc; ++indx) { | |
name = argv[indx]; | |
@@ -758,12 +790,26 @@ | |
len = strlen(name); | |
while (len > 1 && name[len-1] == '/') | |
name[--len] = '\0'; | |
+#if HAVE_COPYFILE | |
+md_next: | |
+ statbytes = 0; | |
+ if (md_flag) { | |
+ fd = open(md_tmp, O_RDONLY, 0); | |
+ unlink(md_tmp); | |
+ free(md_tmp); | |
+ if (fd < 0) | |
+ goto syserr; | |
+ } else { | |
+#endif | |
if ((fd = open(name, O_RDONLY|O_NONBLOCK, 0)) < 0) | |
goto syserr; | |
if (strchr(name, '\n') != NULL) { | |
strnvis(encname, name, sizeof(encname), VIS_NL); | |
name = encname; | |
} | |
+#if HAVE_COPYFILE | |
+ } | |
+#endif | |
if (fstat(fd, &stb) < 0) { | |
syserr: run_err("%s: %s", name, strerror(errno)); | |
goto next; | |
@@ -850,6 +896,36 @@ | |
else | |
run_err("%s: %s", name, strerror(haderr)); | |
(void) response(); | |
+#ifdef HAVE_COPYFILE | |
+ if (copy_xattr && md_flag == 0) | |
+ { | |
+ if (!copyfile(name, NULL, 0, | |
+ COPYFILE_ACL | COPYFILE_XATTR | COPYFILE_CHECK)) | |
+ continue; | |
+ | |
+ /* | |
+ * this file will hold the actual metadata | |
+ * to be transferred | |
+ */ | |
+ md_tmp = strdup("/tmp/scp.md.XXXXXX"); | |
+ md_tmp = mktemp(md_tmp); | |
+ | |
+ if(copyfile(name, md_tmp, 0, | |
+ COPYFILE_ACL | COPYFILE_XATTR | COPYFILE_PACK) == 0) | |
+ { | |
+ /* | |
+ * this is the fake name to display | |
+ */ | |
+ snprintf(md_name, sizeof md_name, "%s/._%s", dirname(name), basename(name)); | |
+ name = md_name; | |
+ md_flag = 1; | |
+ if (verbose_mode) | |
+ fprintf(stderr, "copyfile(%s, %s, PACK)\n", name, md_tmp); | |
+ goto md_next; | |
+ } | |
+ } else | |
+ md_flag = 0; | |
+#endif | |
} | |
} | |
@@ -941,6 +1017,10 @@ | |
if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) | |
targisdir = 1; | |
for (first = 1;; first = 0) { | |
+#if HAVE_COPYFILE | |
+ char md_src[MAXPATHLEN]; | |
+ char md_dst[MAXPATHLEN]; | |
+#endif | |
cp = buf; | |
if (atomicio(read, remin, cp, 1) != 1) | |
return; | |
@@ -1086,10 +1166,51 @@ | |
} | |
omode = mode; | |
mode |= S_IWUSR; | |
+ | |
+#if HAVE_COPYFILE | |
+ if (copy_xattr && !strncmp(basename(curfile), "._", 2)) | |
+ { | |
+ int mdfd; | |
+ if (targisdir) | |
+ { | |
+ snprintf(md_src, sizeof md_src, "%s.XXXXXX", np); | |
+ snprintf(md_dst, sizeof md_dst, "%s/%s", | |
+ dirname(np), basename(np) + 2); | |
+ if((mdfd = mkstemp(md_src)) < 0) | |
+ continue; | |
+ } | |
+ else | |
+ { | |
+ snprintf(md_src, sizeof md_src, "%s/._%s.XXXXXX", | |
+ dirname(np), basename(np)); | |
+ snprintf(md_dst, sizeof md_dst, "%s", np); | |
+ if((mdfd = mkstemp(md_src)) < 0) | |
+ continue; | |
+ } | |
+ if (mdfd >= 0) | |
+ close(mdfd); | |
+ np = md_src; | |
+ } | |
+#endif | |
if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { | |
bad: run_err("%s: %s", np, strerror(errno)); | |
continue; | |
} | |
+#ifdef __APPLE_XSAN__ | |
+ { | |
+ /* | |
+ * Pre-allocate blocks for the destination file. | |
+ */ | |
+ fstore_t fst; | |
+ | |
+ fst.fst_flags = 0; | |
+ fst.fst_posmode = F_PEOFPOSMODE; | |
+ fst.fst_offset = 0; | |
+ fst.fst_length = size; | |
+ | |
+ (void) fcntl(ofd, F_PREALLOCATE, &fst); | |
+ } | |
+#endif /* __APPLE_XSAN__ */ | |
(void) atomicio(vwrite, remout, "", 1); | |
if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { | |
(void) close(ofd); | |
@@ -1174,6 +1295,29 @@ | |
wrerrno = errno; | |
} | |
(void) response(); | |
+#ifdef HAVE_COPYFILE | |
+ if (copy_xattr && strncmp(basename(np), "._", 2) == 0) | |
+ { | |
+ if (verbose_mode) | |
+ fprintf(stderr, "copyfile(%s, %s, UNPACK)\n", md_src, md_dst); | |
+ if(!copyfile(md_src, md_dst, 0, | |
+ COPYFILE_ACL | COPYFILE_XATTR | COPYFILE_UNPACK) < 0) | |
+ { | |
+ snprintf(md_dst, sizeof md_dst, "%s/._%s", | |
+ dirname(md_dst), basename(md_dst)); | |
+ rename(md_src, md_dst); | |
+ } else | |
+ unlink(md_src); | |
+ if (setimes && wrerr == NO) { | |
+ setimes = 0; | |
+ if (utimes(md_dst, tv) < 0) { | |
+ run_err("%s: set times: %s", | |
+ np, strerror(errno)); | |
+ wrerr = DISPLAYED; | |
+ } | |
+ } | |
+ } else | |
+#endif | |
if (setimes && wrerr == NO) { | |
setimes = 0; | |
if (utimes(np, tv) < 0) { | |
@@ -1235,7 +1379,11 @@ | |
usage(void) | |
{ | |
(void) fprintf(stderr, | |
+#if HAVE_COPYFILE | |
+ "usage: scp [-12346BCEpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" | |
+#else | |
"usage: scp [-12346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" | |
+#endif | |
" [-l limit] [-o ssh_option] [-P port] [-S program]\n" | |
" [[user@]host1:]file1 ... [[user@]host2:]file2\n"); | |
exit(1); | |
--- a/servconf.c.old | |
+++ b/servconf.c | |
@@ -253,7 +253,7 @@ | |
if (options->gss_cleanup_creds == -1) | |
options->gss_cleanup_creds = 1; | |
if (options->password_authentication == -1) | |
- options->password_authentication = 1; | |
+ options->password_authentication = 0; | |
if (options->kbd_interactive_authentication == -1) | |
options->kbd_interactive_authentication = 0; | |
if (options->challenge_response_authentication == -1) | |
@@ -639,7 +639,7 @@ | |
if ((pw = getpwnam(user)) == NULL) { | |
debug("Can't match group at line %d because user %.100s does " | |
"not exist", line, user); | |
- } else if (ga_init(pw->pw_name, pw->pw_gid) == 0) { | |
+ } else if (ga_init(pw) == 0) { | |
debug("Can't Match group because user %.100s not in any group " | |
"at line %d", user, line); | |
} else if (ga_match_pattern_list(grps) != 1) { | |
--- a/session.c.old | |
+++ b/session.c | |
@@ -2113,8 +2113,10 @@ | |
n_bytes = packet_remaining(); | |
tty_parse_modes(s->ttyfd, &n_bytes); | |
+#ifndef __APPLE_PRIVPTY__ | |
if (!use_privsep) | |
pty_setowner(s->pw, s->tty); | |
+#endif | |
/* Set window size from the packet. */ | |
pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); | |
@@ -2354,9 +2356,11 @@ | |
if (s->pid != 0) | |
record_logout(s->pid, s->tty, s->pw->pw_name); | |
+#ifndef __APPLE_PRIVPTY__ | |
/* Release the pseudo-tty. */ | |
if (getuid() == 0) | |
pty_release(s->tty); | |
+#endif | |
/* | |
* Close the server side of the socket pairs. We must do this after | |
--- a/ssh-add.0.old | |
+++ b/ssh-add.0 | |
@@ -4,7 +4,7 @@ | |
ssh-add - adds private key identities to the authentication agent | |
SYNOPSIS | |
- ssh-add [-cDdkLlXx] [-t life] [file ...] | |
+ ssh-add [-cDdkKLlXx] [-t life] [file ...] | |
ssh-add -s pkcs11 | |
ssh-add -e pkcs11 | |
@@ -55,6 +55,13 @@ | |
-l Lists fingerprints of all identities currently represented by the | |
agent. | |
+ -m Add identities to the agent using any passphrases stored in your | |
+ Mac OS X keychain. | |
+ | |
+ -M When adding identities, each passphrase will also be stored in | |
+ your Mac OS X keychain. When removing identities with -d, each | |
+ passphrase will be removed from your Mac OS X keychain. | |
+ | |
-s pkcs11 | |
Add keys provided by the PKCS#11 shared library pkcs11. | |
--- a/ssh-add.1.old | |
+++ b/ssh-add.1 | |
@@ -43,7 +43,7 @@ | |
.Nd adds private key identities to the authentication agent | |
.Sh SYNOPSIS | |
.Nm ssh-add | |
-.Op Fl cDdkLlXx | |
+.Op Fl cDdkLlMmXx | |
.Op Fl t Ar life | |
.Op Ar | |
.Nm ssh-add | |
@@ -119,6 +119,13 @@ | |
by the agent. | |
.It Fl l | |
Lists fingerprints of all identities currently represented by the agent. | |
+.It Fl m | |
+Add identities to the agent using any passphrases stored in your Mac OS | |
+X keychain. | |
+.It Fl M | |
+When adding identities, each passphrase will also be stored in your Mac OS | |
+Xkeychain. When removing identities with -d, each passphrase will be removed | |
+from your Mac OS X keychain. | |
.It Fl s Ar pkcs11 | |
Add keys provided by the PKCS#11 shared library | |
.Ar pkcs11 . | |
--- a/ssh-add.c.old | |
+++ b/ssh-add.c | |
@@ -63,6 +63,7 @@ | |
#include "pathnames.h" | |
#include "misc.h" | |
#include "ssherr.h" | |
+#include "keychain.h" | |
/* argv0 */ | |
extern char *__progname; | |
@@ -98,12 +99,24 @@ | |
} | |
static int | |
-delete_file(AuthenticationConnection *ac, const char *filename, int key_only) | |
+add_from_keychain(AuthenticationConnection *ac) | |
+{ | |
+ if (ssh_add_from_keychain(ac) == 0) | |
+ return -1; | |
+ | |
+ fprintf(stderr, "Added keychain identities.\n"); | |
+ return 0; | |
+} | |
+ | |
+static int | |
+delete_file(AuthenticationConnection *ac, int keychain, const char *filename, int key_only) | |
{ | |
Key *public = NULL, *cert = NULL; | |
char *certpath = NULL, *comment = NULL; | |
int ret = -1; | |
+ if (keychain) | |
+ remove_from_keychain(filename); | |
public = key_load_public(filename, &comment); | |
if (public == NULL) { | |
printf("Bad key file %s\n", filename); | |
@@ -166,7 +179,7 @@ | |
} | |
static int | |
-add_file(AuthenticationConnection *ac, const char *filename, int key_only) | |
+add_file(AuthenticationConnection *ac, int keychain, const char *filename, int key_only) | |
{ | |
Key *private, *cert; | |
char *comment = NULL; | |
@@ -205,12 +218,16 @@ | |
if ((r = sshkey_parse_private_fileblob(&keyblob, "", filename, | |
&private, &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) | |
fatal("Cannot parse %s: %s", filename, ssh_err(r)); | |
+ if (keychain && private != NULL) | |
+ store_in_keychain(filename, ""); | |
/* try last */ | |
if (private == NULL && pass != NULL) { | |
if ((r = sshkey_parse_private_fileblob(&keyblob, pass, filename, | |
&private, &comment)) != 0 && | |
r != SSH_ERR_KEY_WRONG_PASSPHRASE) | |
fatal("Cannot parse %s: %s", filename, ssh_err(r)); | |
+ if (keychain && private != NULL) | |
+ store_in_keychain(filename, pass); | |
} | |
if (comment == NULL) | |
comment = xstrdup(filename); | |
@@ -232,8 +249,11 @@ | |
r != SSH_ERR_KEY_WRONG_PASSPHRASE) | |
fatal("Cannot parse %s: %s", | |
filename, ssh_err(r)); | |
- if (private != NULL) | |
+ if (private != NULL) { | |
+ if (keychain) | |
+ store_in_keychain(filename, pass); | |
break; | |
+ } | |
clear_pass(); | |
snprintf(msg, sizeof msg, | |
"Bad passphrase, try again for %.200s: ", comment); | |
@@ -390,13 +410,13 @@ | |
} | |
static int | |
-do_file(AuthenticationConnection *ac, int deleting, int key_only, char *file) | |
+do_file(AuthenticationConnection *ac, int deleting, int keychain, int key_only, char *file) | |
{ | |
if (deleting) { | |
- if (delete_file(ac, file, key_only) == -1) | |
+ if (delete_file(ac, keychain, file, key_only) == -1) | |
return -1; | |
} else { | |
- if (add_file(ac, file, key_only) == -1) | |
+ if (add_file(ac, keychain, file, key_only) == -1) | |
return -1; | |
} | |
return 0; | |
@@ -418,6 +438,11 @@ | |
fprintf(stderr, " -X Unlock agent.\n"); | |
fprintf(stderr, " -s pkcs11 Add keys from PKCS#11 provider.\n"); | |
fprintf(stderr, " -e pkcs11 Remove keys provided by PKCS#11 provider.\n"); | |
+#ifdef KEYCHAIN | |
+ fprintf(stderr, " -m Add all identities stored in your Mac OS X keychain.\n"); | |
+ fprintf(stderr, " -M Store passphrases in your Mac OS X keychain.\n"); | |
+ fprintf(stderr, " With -d, remove passphrases from your Mac OS X keychain.\n"); | |
+#endif | |
} | |
int | |
@@ -428,6 +453,7 @@ | |
AuthenticationConnection *ac = NULL; | |
char *pkcs11provider = NULL; | |
int i, ch, deleting = 0, ret = 0, key_only = 0; | |
+ int keychain = 0; | |
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ | |
sanitise_stdfd(); | |
@@ -446,7 +472,7 @@ | |
"Could not open a connection to your authentication agent.\n"); | |
exit(2); | |
} | |
- while ((ch = getopt(argc, argv, "klLcdDxXe:s:t:")) != -1) { | |
+ while ((ch = getopt(argc, argv, "kKlLcdDxXmMe:s:t:")) != -1) { | |
switch (ch) { | |
case 'k': | |
key_only = 1; | |
@@ -485,6 +511,13 @@ | |
goto done; | |
} | |
break; | |
+ case 'm': | |
+ if (add_from_keychain(ac) == -1) | |
+ ret = 1; | |
+ goto done; | |
+ case 'M': | |
+ keychain = 1; | |
+ break; | |
default: | |
usage(); | |
ret = 1; | |
@@ -516,7 +549,7 @@ | |
default_files[i]); | |
if (stat(buf, &st) < 0) | |
continue; | |
- if (do_file(ac, deleting, key_only, buf) == -1) | |
+ if (do_file(ac, deleting, keychain, key_only, buf) == -1) | |
ret = 1; | |
else | |
count++; | |
@@ -525,7 +558,7 @@ | |
ret = 1; | |
} else { | |
for (i = 0; i < argc; i++) { | |
- if (do_file(ac, deleting, key_only, argv[i]) == -1) | |
+ if (do_file(ac, deleting, keychain, key_only, argv[i]) == -1) | |
ret = 1; | |
} | |
} | |
--- a/ssh-agent.c.old | |
+++ b/ssh-agent.c | |
@@ -66,6 +66,9 @@ | |
#include <time.h> | |
#include <string.h> | |
#include <unistd.h> | |
+#ifdef __APPLE_LAUNCHD__ | |
+#include <launch.h> | |
+#endif | |
#include "xmalloc.h" | |
#include "ssh.h" | |
@@ -73,10 +76,12 @@ | |
#include "buffer.h" | |
#include "key.h" | |
#include "authfd.h" | |
+#include "authfile.h" | |
#include "compat.h" | |
#include "log.h" | |
#include "misc.h" | |
#include "digest.h" | |
+#include "keychain.h" | |
#ifdef ENABLE_PKCS11 | |
#include "ssh-pkcs11.h" | |
@@ -701,6 +706,61 @@ | |
} | |
#endif /* ENABLE_PKCS11 */ | |
+static int | |
+add_identity_callback(const char *filename, const char *passphrase) | |
+{ | |
+ Key *k; | |
+ int version; | |
+ Idtab *tab; | |
+ | |
+ if ((k = key_load_private(filename, passphrase, NULL)) == NULL) | |
+ return 1; | |
+ switch (k->type) { | |
+ case KEY_RSA: | |
+ case KEY_RSA1: | |
+ if (RSA_blinding_on(k->rsa, NULL) != 1) { | |
+ key_free(k); | |
+ return 1; | |
+ } | |
+ break; | |
+ } | |
+ version = k->type == KEY_RSA1 ? 1 : 2; | |
+ tab = idtab_lookup(version); | |
+ if (lookup_identity(k, version) == NULL) { | |
+ Identity *id = xmalloc(sizeof(Identity)); | |
+ id->key = k; | |
+ id->comment = xstrdup(filename); | |
+ if (id->comment == NULL) { | |
+ key_free(k); | |
+ return 1; | |
+ } | |
+ id->death = 0; | |
+ id->confirm = 0; | |
+ TAILQ_INSERT_TAIL(&tab->idlist, id, next); | |
+ tab->nentries++; | |
+ } else { | |
+ key_free(k); | |
+ return 1; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void | |
+process_add_from_keychain(SocketEntry *e) | |
+{ | |
+ int result; | |
+ | |
+ result = add_identities_using_keychain(&add_identity_callback); | |
+ | |
+ /* e will be NULL when ssh-agent adds keys on its own at startup */ | |
+ if (e) { | |
+ buffer_put_int(&e->output, 1); | |
+ buffer_put_char(&e->output, | |
+ result ? SSH_AGENT_FAILURE : SSH_AGENT_SUCCESS); | |
+ } | |
+} | |
+ | |
/* dispatch incoming messages */ | |
static void | |
@@ -795,6 +855,9 @@ | |
process_remove_smartcard_key(e); | |
break; | |
#endif /* ENABLE_PKCS11 */ | |
+ case SSH_AGENTC_ADD_FROM_KEYCHAIN: | |
+ process_add_from_keychain(e); | |
+ break; | |
default: | |
/* Unknown message. Respond with failure. */ | |
error("Unknown message %d", type); | |
@@ -1034,7 +1097,11 @@ | |
int | |
main(int ac, char **av) | |
{ | |
+#ifdef __APPLE_LAUNCHD__ | |
+ int c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0, l_flag = 0; | |
+#else | |
int c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0; | |
+#endif | |
int sock, fd, ch, result, saved_errno; | |
u_int nalloc; | |
char *shell, *format, *pidstr, *agentsocket = NULL; | |
@@ -1069,7 +1136,11 @@ | |
__progname = ssh_get_progname(av[0]); | |
seed_rng(); | |
+#ifdef __APPLE_LAUNCHD__ | |
+ while ((ch = getopt(ac, av, "cdklsa:t:")) != -1) { | |
+#else | |
while ((ch = getopt(ac, av, "cdksa:t:")) != -1) { | |
+#endif | |
switch (ch) { | |
case 'c': | |
if (s_flag) | |
@@ -1079,6 +1150,11 @@ | |
case 'k': | |
k_flag++; | |
break; | |
+#ifdef __APPLE_LAUNCHD__ | |
+ case 'l': | |
+ l_flag++; | |
+ break; | |
+#endif | |
case 's': | |
if (c_flag) | |
usage(); | |
@@ -1105,7 +1181,11 @@ | |
ac -= optind; | |
av += optind; | |
+#ifdef __APPPLE_LAUNCHD__ | |
+ if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || l_flag)) | |
+#else | |
if (ac > 0 && (c_flag || k_flag || s_flag || d_flag)) | |
+#endif | |
usage(); | |
if (ac == 0 && !c_flag && !s_flag) { | |
@@ -1161,6 +1241,53 @@ | |
* Create socket early so it will exist before command gets run from | |
* the parent. | |
*/ | |
+#ifdef __APPLE_LAUNCHD__ | |
+ if (l_flag) { | |
+ launch_data_t resp, msg, tmp; | |
+ size_t listeners_i; | |
+ | |
+ msg = launch_data_new_string(LAUNCH_KEY_CHECKIN); | |
+ | |
+ resp = launch_msg(msg); | |
+ | |
+ if (NULL == resp) { | |
+ perror("launch_msg"); | |
+ exit(1); | |
+ } | |
+ launch_data_free(msg); | |
+ switch (launch_data_get_type(resp)) { | |
+ case LAUNCH_DATA_ERRNO: | |
+ errno = launch_data_get_errno(resp); | |
+ perror("launch_msg response"); | |
+ exit(1); | |
+ case LAUNCH_DATA_DICTIONARY: | |
+ break; | |
+ default: | |
+ fprintf(stderr, "launch_msg unknown response"); | |
+ exit(1); | |
+ } | |
+ tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS); | |
+ | |
+ if (NULL == tmp) { | |
+ fprintf(stderr, "no sockets\n"); | |
+ exit(1); | |
+ } | |
+ | |
+ tmp = launch_data_dict_lookup(tmp, "Listeners"); | |
+ | |
+ if (NULL == tmp) { | |
+ fprintf(stderr, "no known listeners\n"); | |
+ exit(1); | |
+ } | |
+ | |
+ for (listeners_i = 0; listeners_i < launch_data_array_get_count(tmp); listeners_i++) { | |
+ launch_data_t obj_at_ind = launch_data_array_get_index(tmp, listeners_i); | |
+ new_socket(AUTH_SOCKET, launch_data_get_fd(obj_at_ind)); | |
+ } | |
+ | |
+ launch_data_free(resp); | |
+ } else { | |
+#endif | |
prev_mask = umask(0177); | |
sock = unix_listener(socket_name, SSH_LISTEN_BACKLOG, 0); | |
if (sock < 0) { | |
@@ -1169,6 +1296,14 @@ | |
cleanup_exit(1); | |
} | |
umask(prev_mask); | |
+#ifdef __APPLE_LAUNCHD__ | |
+ } | |
+#endif | |
+ | |
+#ifdef __APPLE_LAUNCHD__ | |
+ if (l_flag) | |
+ goto skip2; | |
+#endif | |
/* | |
* Fork, and have the parent execute the command, if any, or present | |
@@ -1243,6 +1378,7 @@ | |
pkcs11_init(0); | |
#endif | |
new_socket(AUTH_SOCKET, sock); | |
+skip2: | |
if (ac > 0) | |
parent_alive_interval = 10; | |
idtab_init(); | |
@@ -1252,6 +1388,10 @@ | |
signal(SIGTERM, cleanup_handler); | |
nalloc = 0; | |
+#ifdef KEYCHAIN | |
+ process_add_from_keychain(NULL); | |
+#endif | |
+ | |
while (1) { | |
prepare_select(&readsetp, &writesetp, &max_fd, &nalloc, &tvp); | |
result = select(max_fd + 1, readsetp, writesetp, NULL, tvp); | |
--- a/ssh-keysign.8.old | |
+++ b/ssh-keysign.8 | |
@@ -72,6 +72,9 @@ | |
Since they are readable only by root, | |
.Nm | |
must be set-uid root if host-based authentication is used. | |
+Note that | |
+.Nm | |
+is not set-uid by default on Mac OS X. | |
.Pp | |
.It Pa /etc/ssh/ssh_host_dsa_key-cert.pub | |
.It Pa /etc/ssh/ssh_host_ecdsa_key-cert.pub | |
--- a/sshconnect1.c.old | |
+++ b/sshconnect1.c | |
@@ -47,6 +47,7 @@ | |
#include "hostfile.h" | |
#include "auth.h" | |
#include "digest.h" | |
+#include "keychain.h" | |
/* Session id for the current session. */ | |
u_char session_id[16]; | |
@@ -262,6 +263,10 @@ | |
snprintf(buf, sizeof(buf), | |
"Enter passphrase for RSA key '%.100s': ", comment); | |
for (i = 0; i < options.number_of_password_prompts; i++) { | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ passphrase = keychain_read_passphrase(comment, options.ask_pass_gui); | |
+ if (passphrase == NULL) | |
+#endif | |
passphrase = read_passphrase(buf, 0); | |
if (strcmp(passphrase, "") != 0) { | |
private = key_load_private_type(KEY_RSA1, | |
--- a/sshconnect2.c.old | |
+++ b/sshconnect2.c | |
@@ -70,6 +70,7 @@ | |
#include "pathnames.h" | |
#include "uidswap.h" | |
#include "hostfile.h" | |
+#include "keychain.h" | |
#ifdef GSSAPI | |
#include "ssh-gss.h" | |
@@ -1122,6 +1123,10 @@ | |
snprintf(prompt, sizeof prompt, | |
"Enter passphrase for key '%.100s': ", filename); | |
for (i = 0; i < options.number_of_password_prompts; i++) { | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ passphrase = keychain_read_passphrase(filename, options.ask_pass_gui); | |
+ if (passphrase == NULL) | |
+#endif | |
passphrase = read_passphrase(prompt, 0); | |
if (strcmp(passphrase, "") != 0) { | |
private = key_load_private_type(KEY_UNSPEC, | |
--- a/sshd.0.old | |
+++ b/sshd.0 | |
@@ -621,8 +621,7 @@ | |
SEE ALSO | |
scp(1), sftp(1), ssh(1), ssh-add(1), ssh-agent(1), ssh-keygen(1), | |
- ssh-keyscan(1), chroot(2), login.conf(5), moduli(5), sshd_config(5), | |
- inetd(8), sftp-server(8) | |
+ ssh-keyscan(1), chroot(2), sshd_config(5), sftp-server(8) | |
AUTHORS | |
OpenSSH is a derivative of the original and free ssh 1.2.12 release by | |
--- a/sshd.8.old | |
+++ b/sshd.8 | |
@@ -954,10 +954,7 @@ | |
.Xr ssh-keygen 1 , | |
.Xr ssh-keyscan 1 , | |
.Xr chroot 2 , | |
-.Xr login.conf 5 , | |
-.Xr moduli 5 , | |
.Xr sshd_config 5 , | |
-.Xr inetd 8 , | |
.Xr sftp-server 8 | |
.Sh AUTHORS | |
OpenSSH is a derivative of the original and free | |
--- a/sshd.c.old | |
+++ b/sshd.c | |
@@ -2144,6 +2144,12 @@ | |
audit_event(SSH_AUTH_SUCCESS); | |
#endif | |
+#ifdef USE_PAM | |
+ if (options.use_pam) { | |
+ do_pam_setcred(1); | |
+ do_pam_session(); | |
+ } | |
+#endif | |
#ifdef GSSAPI | |
if (options.gss_authentication) { | |
temporarily_use_uid(authctxt->pw); | |
@@ -2151,12 +2157,6 @@ | |
restore_uid(); | |
} | |
#endif | |
-#ifdef USE_PAM | |
- if (options.use_pam) { | |
- do_pam_setcred(1); | |
- do_pam_session(); | |
- } | |
-#endif | |
/* | |
* In privilege separation, we fork another child and prepare | |
--- a/sshd_config.old | |
+++ b/sshd_config | |
@@ -35,7 +35,7 @@ | |
# Logging | |
# obsoletes QuietMode and FascistLogging | |
-#SyslogFacility AUTH | |
+SyslogFacility AUTHPRIV | |
#LogLevel INFO | |
# Authentication: | |
@@ -68,8 +68,9 @@ | |
# Don't read the user's ~/.rhosts and ~/.shosts files | |
#IgnoreRhosts yes | |
-# To disable tunneled clear text passwords, change to no here! | |
-#PasswordAuthentication yes | |
+# To disable tunneled clear text passwords, change to no here! Also, | |
+# remember to set the UsePAM setting to 'no'. | |
+#PasswordAuthentication no | |
#PermitEmptyPasswords no | |
# Change to no to disable s/key passwords | |
@@ -94,7 +95,10 @@ | |
# If you just want the PAM account and session checks to run without | |
# PAM authentication, then enable this but set PasswordAuthentication | |
# and ChallengeResponseAuthentication to 'no'. | |
-#UsePAM no | |
+# Also, PAM will deny null passwords by default. If you need to allow | |
+# null passwords, add the " nullok" option to the end of the | |
+# securityserver.so line in /etc/pam.d/sshd. | |
+#UsePAM yes | |
#AllowAgentForwarding yes | |
#AllowTcpForwarding yes | |
--- a/sshd_config.0.old | |
+++ b/sshd_config.0 | |
@@ -571,7 +571,7 @@ | |
PasswordAuthentication | |
Specifies whether password authentication is allowed. The | |
- default is ``yes''. | |
+ default is ``no''. | |
PermitEmptyPasswords | |
When password authentication is allowed, it specifies whether the | |
@@ -802,7 +802,7 @@ | |
either PasswordAuthentication or ChallengeResponseAuthentication. | |
If UsePAM is enabled, you will not be able to run sshd(8) as a | |
- non-root user. The default is ``no''. | |
+ non-root user. The default is ``yes''. | |
UsePrivilegeSeparation | |
Specifies whether sshd(8) separates privileges by creating an | |
--- a/sshd_config.5.old | |
+++ b/sshd_config.5 | |
@@ -977,7 +977,7 @@ | |
.It Cm PasswordAuthentication | |
Specifies whether password authentication is allowed. | |
The default is | |
-.Dq yes . | |
+.Dq no . | |
.It Cm PermitEmptyPasswords | |
When password authentication is allowed, it specifies whether the | |
server allows login to accounts with empty password strings. | |
@@ -1343,7 +1343,7 @@ | |
.Xr sshd 8 | |
as a non-root user. | |
The default is | |
-.Dq no . | |
+.Dq yes . | |
.It Cm UsePrivilegeSeparation | |
Specifies whether | |
.Xr sshd 8 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment