Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leonklingele/afdb457f1b378b42bdbfdbc5fd59f55c to your computer and use it in GitHub Desktop.
Save leonklingele/afdb457f1b378b42bdbfdbc5fd59f55c to your computer and use it in GitHub Desktop.
Homebrew openssh-7.5p1 patches
From 32a85e260f85fc820fd026637ad38e2230d2887f Mon Sep 17 00:00:00 2001
From: Leon Klingele <git@leonklingele.de>
Date: Tue, 21 Mar 2017 22:37:56 +0100
Subject: [PATCH 1/3] 0001-apple-keychain-integration-other-changes.patch
---
Makefile.in | 18 +-
audit-bsm.c | 7 +-
auth-pam.c | 5 +-
auth.c | 2 +-
authfd.c | 23 ++
authfd.h | 6 +
config.h.in | 12 +
configure.ac | 30 +++
groupaccess.c | 57 ++++-
groupaccess.h | 2 +-
keychain.c | 694 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
keychain.h | 45 ++++
readconf.c | 19 ++
readconf.h | 4 +
scp.1 | 4 +-
scp.c | 148 +++++++++++++
servconf.c | 4 +-
session.c | 4 +
ssh-add.0 | 9 +-
ssh-add.1 | 9 +-
ssh-add.c | 56 ++++-
ssh-agent.c | 141 ++++++++++++
ssh-keysign.8 | 3 +
sshconnect1.c | 5 +
sshconnect2.c | 5 +
sshd.0 | 3 +-
sshd.8 | 3 -
sshd.c | 12 +-
sshd_config | 12 +-
sshd_config.0 | 4 +-
sshd_config.5 | 4 +-
31 files changed, 1299 insertions(+), 51 deletions(-)
create mode 100644 keychain.c
create mode 100644 keychain.h
diff --git a/Makefile.in b/Makefile.in
index 5870e9e..48a13db 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -59,6 +59,7 @@ SED=@SED@
ENT=@ENT@
XAUTH_PATH=@XAUTH_PATH@
LDFLAGS=-L. -Lopenbsd-compat/ @LDFLAGS@
+KEYCHAIN_LDFLAGS=@KEYCHAIN_LDFLAGS@
EXEEXT=@EXEEXT@
MANFMT=@MANFMT@
@@ -112,6 +113,8 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \
sandbox-seccomp-filter.o sandbox-capsicum.o sandbox-pledge.o \
sandbox-solaris.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@
@@ -147,6 +150,7 @@ all: $(CONFIGFILES) $(MANPAGES) $(TARGETS)
$(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 $@
@@ -160,8 +164,8 @@ libssh.a: $(LIBSSH_OBJS)
$(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)
@@ -169,11 +173,11 @@ sshd$(EXEEXT): libssh.a $(LIBCOMPAT) $(SSHDOBJS)
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)
@@ -324,7 +328,7 @@ install-files:
$(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)
diff --git a/audit-bsm.c b/audit-bsm.c
index f8e0bea..b91a775 100644
--- a/audit-bsm.c
+++ b/audit-bsm.c
@@ -261,7 +261,12 @@ bsm_audit_record(int typ, char *string, au_event_t event_no)
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;
}
diff --git a/auth-pam.c b/auth-pam.c
index bc8e5e0..103b6cb 100644
--- a/auth-pam.c
+++ b/auth-pam.c
@@ -798,10 +798,11 @@ sshpam_query(void *ctx, char **name, char **info,
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,
- auth_get_canonical_hostname(ssh, options.use_dns));
+ auth_get_canonical_hostname(ssh, options.use_dns),
+ get_local_ipaddr(ssh_packet_get_connection_in(ssh)));
/* FALLTHROUGH */
default:
*num = 0;
diff --git a/auth.c b/auth.c
index 6ee6116..95bb13a 100644
--- a/auth.c
+++ b/auth.c
@@ -226,7 +226,7 @@ allowed_user(struct passwd * pw)
}
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;
diff --git a/authfd.c b/authfd.c
index a634bcb..fe018c1 100644
--- a/authfd.c
+++ b/authfd.c
@@ -165,6 +165,29 @@ ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply)
}
/*
+ * Adds identities using passphrases stored in the keychain. This call is not
+ * meant to be used by normal applications.
+ */
+
+int
+ssh_add_from_keychain(int agent_fd)
+{
+ Buffer msg;
+ int type;
+
+ buffer_init(&msg);
+ buffer_put_char(&msg, SSH_AGENTC_ADD_FROM_KEYCHAIN);
+
+ if (ssh_request_reply(agent_fd, &msg, &msg) == 0) {
+ buffer_free(&msg);
+ return 0;
+ }
+ type = buffer_get_char(&msg);
+ buffer_free(&msg);
+ return decode_reply(type);
+}
+
+/*
* Closes the agent socket if it should be closed (depends on how it was
* obtained). The argument must have been returned by
* ssh_get_authentication_socket().
diff --git a/authfd.h b/authfd.h
index 4b417e3..33ce33e 100644
--- a/authfd.h
+++ b/authfd.h
@@ -43,6 +43,9 @@ int ssh_agent_sign(int sock, struct sshkey *key,
u_char **sigp, size_t *lenp,
const u_char *data, size_t datalen, const char *alg, u_int compat);
+int
+ssh_add_from_keychain(int agent_fd);
+
/* Messages for the authentication agent connection. */
#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1
#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2
@@ -76,6 +79,9 @@ int ssh_agent_sign(int sock, struct sshkey *key,
#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
diff --git a/config.h.in b/config.h.in
index b65420e..1ec0ab8 100644
--- a/config.h.in
+++ b/config.h.in
@@ -78,6 +78,18 @@
/* strnvis detected broken */
#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
diff --git a/configure.ac b/configure.ac
index c2878e3..c47412d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5029,10 +5029,40 @@ AC_CHECK_MEMBER([struct utmp.ut_line], [], [
#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
diff --git a/groupaccess.c b/groupaccess.c
index 2518c84..f5e8c36 100644
--- a/groupaccess.c
+++ b/groupaccess.c
@@ -34,38 +34,67 @@
#include <string.h>
#include <limits.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
-
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 @@ ga_init(const char *user, gid_t base)
/*
* 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;
}
diff --git a/groupaccess.h b/groupaccess.h
index 000578e..ddea117 100644
--- a/groupaccess.h
+++ 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);
diff --git a/keychain.c b/keychain.c
new file mode 100644
index 0000000..51d6ad9
--- /dev/null
+++ 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;
+ int sock = -1;
+ 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 ((ssh_get_authentication_socket(&sock)) != 0)
+ 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(sock, 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 (sock != -1)
+ ssh_close_authentication_socket(sock);
+
+ return result;
+
+#else
+
+ /*
+ * keychain_read_passphrase
+ * no implementation
+ */
+
+ return NULL;
+
+#endif
+
+}
diff --git a/keychain.h b/keychain.h
new file mode 100644
index 0000000..9bb8c22
--- /dev/null
+++ 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);
diff --git a/readconf.c b/readconf.c
index 9d59493..48f1ab6 100644
--- a/readconf.c
+++ b/readconf.c
@@ -168,6 +168,9 @@ typedef enum {
oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass,
oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
+#ifdef __APPLE_KEYCHAIN__
+ oAskPassGUI,
+#endif
oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes,
oPubkeyAcceptedKeyTypes, oProxyJump,
@@ -308,6 +311,9 @@ static struct {
{ "pubkeyacceptedkeytypes", oPubkeyAcceptedKeyTypes },
{ "ignoreunknown", oIgnoreUnknown },
{ "proxyjump", oProxyJump },
+#ifdef __APPLE_KEYCHAIN__
+ { "askpassgui", oAskPassGUI },
+#endif
{ NULL, oBadOption }
};
@@ -1562,6 +1568,12 @@ parse_keytypes:
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;
@@ -1869,6 +1881,9 @@ initialize_options(Options * options)
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;
@@ -2054,6 +2069,10 @@ fill_default_options(Options * options)
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)
diff --git a/readconf.h b/readconf.h
index cef55f7..913c938 100644
--- a/readconf.h
+++ b/readconf.h
@@ -169,6 +169,10 @@ typedef struct {
char *jump_extra;
char *ignored_unknown; /* Pattern list of unknown tokens to ignore */
+
+#ifdef __APPLE_KEYCHAIN__
+ int ask_pass_gui;
+#endif
} Options;
#define SSH_CANONICALISE_NO 0
diff --git a/scp.1 b/scp.1
index 4ae8777..3535932 100644
--- a/scp.1
+++ 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 @@ Passes the
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
diff --git a/scp.c b/scp.c
index b4db851..7eeb9e3 100644
--- a/scp.c
+++ b/scp.c
@@ -77,6 +77,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
@@ -116,6 +119,11 @@
#include "progressmeter.h"
#include "utf8.h"
+#ifdef HAVE_COPYFILE_H
+#include <libgen.h>
+#include <copyfile.h>
+#endif
+
extern char *__progname;
#define COPY_BUFLEN 16384
@@ -152,6 +160,12 @@ char *ssh_program = _PATH_SSH_PROGRAM;
/* 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)
{
@@ -399,7 +413,11 @@ main(int argc, char **argv)
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':
@@ -460,6 +478,11 @@ main(int argc, char **argv)
showprogress = 0;
break;
+#ifdef HAVE_COPYFILE
+ case 'E':
+ copy_xattr = 1;
+ break;
+#endif
/* Server options. */
case 'd':
targetshouldbedirectory = 1;
@@ -519,7 +542,12 @@ main(int argc, char **argv)
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" : "");
@@ -765,6 +793,10 @@ source(int argc, char **argv)
int fd = -1, haderr, indx;
char *last, *name, buf[2048], encname[PATH_MAX];
int len;
+#if HAVE_COPYFILE
+ char md_name[MAXPATHLEN];
+ char *md_tmp;
+#endif
for (indx = 0; indx < argc; ++indx) {
name = argv[indx];
@@ -772,12 +804,26 @@ source(int argc, char **argv)
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;
@@ -861,6 +907,36 @@ next: if (fd != -1) {
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
if (showprogress)
stop_progress_meter();
}
@@ -954,6 +1030,10 @@ sink(int argc, char **argv)
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;
@@ -1102,10 +1182,51 @@ sink(int argc, char **argv)
}
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);
@@ -1188,6 +1309,29 @@ bad: run_err("%s: %s", np, strerror(errno));
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 (showprogress)
stop_progress_meter();
if (setimes && wrerr == NO) {
@@ -1256,7 +1400,11 @@ void
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);
diff --git a/servconf.c b/servconf.c
index 56b8316..0c2680c 100644
--- a/servconf.c
+++ b/servconf.c
@@ -272,7 +272,7 @@ fill_default_server_options(ServerOptions *options)
if (options->gss_strict_acceptor == -1)
options->gss_strict_acceptor = 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)
@@ -752,7 +752,7 @@ match_cfg_line_group(const char *grps, int line, const char *user)
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) {
diff --git a/session.c b/session.c
index a08aa69..43142f1 100644
--- a/session.c
+++ b/session.c
@@ -1902,8 +1902,10 @@ session_pty_req(Session *s)
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);
@@ -2147,9 +2149,11 @@ session_pty_cleanup2(Session *s)
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
diff --git a/ssh-add.0 b/ssh-add.0
index 706bfe6..429e4e7 100644
--- a/ssh-add.0
+++ b/ssh-add.0
@@ -4,7 +4,7 @@ NAME
ssh-add M-bM-^@M-^S adds private key identities to the authentication agent
SYNOPSIS
- ssh-add [-cDdkLlXx] [-E fingerprint_hash] [-t life] [file ...]
+ ssh-add [-cDdkKLlXx] [-E fingerprint_hash] [-t life] [file ...]
ssh-add -s pkcs11
ssh-add -e pkcs11
@@ -60,6 +60,13 @@ DESCRIPTION
-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.
diff --git a/ssh-add.1 b/ssh-add.1
index f02b595..d53b956 100644
--- a/ssh-add.1
+++ 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 E Ar fingerprint_hash
.Op Fl t Ar life
.Op Ar
@@ -127,6 +127,13 @@ Lists public key parameters of all identities currently represented
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 .
diff --git a/ssh-add.c b/ssh-add.c
index fb9a53e..0febac0 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -65,6 +65,7 @@
#include "misc.h"
#include "ssherr.h"
#include "digest.h"
+#include "keychain.h"
/* argv0 */
extern char *__progname;
@@ -106,12 +107,25 @@ clear_pass(void)
}
static int
-delete_file(int agent_fd, const char *filename, int key_only)
+add_from_keychain(int agent_fd)
+{
+ if (ssh_add_from_keychain(agent_fd) == 0)
+ return -1;
+
+ fprintf(stderr, "Added keychain identities.\n");
+ return 0;
+}
+
+static int
+delete_file(int agent_fd, int keychain, const char *filename, int key_only)
{
struct sshkey *public, *cert = NULL;
char *certpath = NULL, *comment = NULL;
int r, ret = -1;
+ if (keychain)
+ remove_from_keychain(filename);
+
if ((r = sshkey_load_public(filename, &public, &comment)) != 0) {
printf("Bad key file %s: %s\n", filename, ssh_err(r));
return -1;
@@ -178,7 +192,7 @@ delete_all(int agent_fd)
}
static int
-add_file(int agent_fd, const char *filename, int key_only)
+add_file(int agent_fd, int keychain, const char *filename, int key_only)
{
struct sshkey *private, *cert;
char *comment = NULL;
@@ -222,6 +236,10 @@ add_file(int agent_fd, const char *filename, int key_only)
filename, ssh_err(r));
goto fail_load;
}
+
+ if (keychain && private != NULL)
+ store_in_keychain(filename, "");
+
/* try last */
if (private == NULL && pass != NULL) {
if ((r = sshkey_parse_private_fileblob(keyblob, pass, &private,
@@ -230,6 +248,8 @@ add_file(int agent_fd, const char *filename, int key_only)
filename, ssh_err(r));
goto fail_load;
}
+ if (keychain && private != NULL)
+ store_in_keychain(filename, pass);
}
if (private == NULL) {
/* clear passphrase since it did not work */
@@ -241,8 +261,13 @@ add_file(int agent_fd, const char *filename, int key_only)
if (strcmp(pass, "") == 0)
goto fail_load;
if ((r = sshkey_parse_private_fileblob(keyblob, pass,
- &private, &comment)) == 0)
+ &private, &comment)) == 0) {
+ if (private != NULL) {
+ if (keychain)
+ store_in_keychain(filename, pass);
+ }
break;
+ }
else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) {
fprintf(stderr,
"Error loading key \"%s\": %s\n",
@@ -440,13 +465,13 @@ lock_agent(int agent_fd, int lock)
}
static int
-do_file(int agent_fd, int deleting, int key_only, char *file)
+do_file(int agent_fd, int deleting, int keychain, int key_only, char *file)
{
if (deleting) {
- if (delete_file(agent_fd, file, key_only) == -1)
+ if (delete_file(agent_fd, keychain, file, key_only) == -1)
return -1;
} else {
- if (add_file(agent_fd, file, key_only) == -1)
+ if (add_file(agent_fd, keychain, file, key_only) == -1)
return -1;
}
return 0;
@@ -469,6 +494,11 @@ usage(void)
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
@@ -480,6 +510,7 @@ main(int argc, char **argv)
char *pkcs11provider = NULL;
int r, i, ch, deleting = 0, ret = 0, key_only = 0;
int xflag = 0, lflag = 0, Dflag = 0;
+ int keychain = 0;
ssh_malloc_init(); /* must be called before any mallocs */
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
@@ -507,7 +538,7 @@ main(int argc, char **argv)
exit(2);
}
- while ((ch = getopt(argc, argv, "klLcdDxXE:e:s:t:")) != -1) {
+ while ((ch = getopt(argc, argv, "kKlLcdDxXmME:e:s:t:")) != -1) {
switch (ch) {
case 'E':
fingerprint_hash = ssh_digest_alg_by_name(optarg);
@@ -552,6 +583,13 @@ main(int argc, char **argv)
goto done;
}
break;
+ case 'm':
+ if (add_from_keychain(agent_fd) == -1)
+ ret = 1;
+ goto done;
+ case 'M':
+ keychain = 1;
+ break;
default:
usage();
ret = 1;
@@ -600,7 +638,7 @@ main(int argc, char **argv)
default_files[i]);
if (stat(buf, &st) < 0)
continue;
- if (do_file(agent_fd, deleting, key_only, buf) == -1)
+ if (do_file(agent_fd, deleting, keychain, key_only, buf) == -1)
ret = 1;
else
count++;
@@ -609,7 +647,7 @@ main(int argc, char **argv)
ret = 1;
} else {
for (i = 0; i < argc; i++) {
- if (do_file(agent_fd, deleting, key_only,
+ if (do_file(agent_fd, deleting, keychain, key_only,
argv[i]) == -1)
ret = 1;
}
diff --git a/ssh-agent.c b/ssh-agent.c
index b987562..0fdc1fe 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -70,6 +70,9 @@
#ifdef HAVE_UTIL_H
# include <util.h>
#endif
+#ifdef __APPLE_LAUNCHD__
+#include <launch.h>
+#endif
#include "xmalloc.h"
#include "ssh.h"
@@ -77,12 +80,15 @@
#include "sshbuf.h"
#include "sshkey.h"
#include "authfd.h"
+#include "authfile.h"
#include "compat.h"
#include "log.h"
#include "misc.h"
#include "digest.h"
#include "ssherr.h"
#include "match.h"
+#include "keychain.h"
+#include "key.h"
#ifdef ENABLE_PKCS11
#include "ssh-pkcs11.h"
@@ -863,6 +869,61 @@ send:
}
#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
@@ -957,6 +1018,9 @@ process_message(SocketEntry *e)
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);
@@ -1207,7 +1271,11 @@ usage(void)
int
main(int ac, char **av)
{
+#ifdef __APPLE_LAUNCHD__
+ int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0, s_flag = 0, l_flag = 0;
+#else
int c_flag = 0, d_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;
@@ -1240,7 +1308,11 @@ main(int ac, char **av)
__progname = ssh_get_progname(av[0]);
seed_rng();
+#ifdef __APPLE_LAUNCHD__
+ while ((ch = getopt(ac, av, "cDdklsE:a:P:t:")) != -1) {
+#else
while ((ch = getopt(ac, av, "cDdksE:a:P:t:")) != -1) {
+#endif
switch (ch) {
case 'E':
fingerprint_hash = ssh_digest_alg_by_name(optarg);
@@ -1260,6 +1332,11 @@ main(int ac, char **av)
fatal("-P option already specified");
pkcs11_whitelist = xstrdup(optarg);
break;
+#ifdef __APPLE_LAUNCHD__
+ case 'l':
+ l_flag++;
+ break;
+#endif
case 's':
if (c_flag)
usage();
@@ -1291,7 +1368,11 @@ main(int ac, char **av)
ac -= optind;
av += optind;
+#ifdef __APPPLE_LAUNCHD__
+ if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag || l_flag))
+#else
if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag))
+#endif
usage();
if (pkcs11_whitelist == NULL)
@@ -1350,6 +1431,53 @@ main(int ac, char **av)
* 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) {
@@ -1358,6 +1486,14 @@ main(int ac, char **av)
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
@@ -1435,6 +1571,7 @@ skip:
pkcs11_init(0);
#endif
new_socket(AUTH_SOCKET, sock);
+skip2:
if (ac > 0)
parent_alive_interval = 10;
idtab_init();
@@ -1448,6 +1585,10 @@ skip:
fatal("%s: pledge: %s", __progname, strerror(errno));
platform_pledge_agent();
+#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);
diff --git a/ssh-keysign.8 b/ssh-keysign.8
index 19b0dbc..5cacf70 100644
--- a/ssh-keysign.8
+++ b/ssh-keysign.8
@@ -72,6 +72,9 @@ accessible to others.
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
diff --git a/sshconnect1.c b/sshconnect1.c
index dc00b4c..64d192c 100644
--- a/sshconnect1.c
+++ b/sshconnect1.c
@@ -51,6 +51,7 @@
#include "auth.h"
#include "digest.h"
#include "ssherr.h"
+#include "keychain.h"
/* Session id for the current session. */
u_char session_id[16];
@@ -274,6 +275,10 @@ try_rsa_authentication(int idx)
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,
diff --git a/sshconnect2.c b/sshconnect2.c
index f8a54be..66092df 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -72,6 +72,7 @@
#include "hostfile.h"
#include "ssherr.h"
#include "utf8.h"
+#include "keychain.h"
#ifdef GSSAPI
#include "ssh-gss.h"
@@ -1244,6 +1245,10 @@ load_identity_file(Identity *id)
if (i == 0)
passphrase = "";
else {
+#ifdef __APPLE_KEYCHAIN__
+ passphrase = keychain_read_passphrase(id->filename, options.ask_pass_gui);
+ if (passphrase == NULL)
+#endif
passphrase = read_passphrase(prompt, 0);
if (*passphrase == '\0') {
debug2("no passphrase given, try next key");
diff --git a/sshd.0 b/sshd.0
index 6cd5f03..ceb29b7 100644
--- a/sshd.0
+++ b/sshd.0
@@ -612,8 +612,7 @@ FILES
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
diff --git a/sshd.8 b/sshd.8
index 7725a69..114c1d1 100644
--- a/sshd.8
+++ b/sshd.8
@@ -929,10 +929,7 @@ The content of this file is not sensitive; it can be world-readable.
.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
diff --git a/sshd.c b/sshd.c
index 010a2c3..12f988a 100644
--- a/sshd.c
+++ b/sshd.c
@@ -2060,6 +2060,12 @@ main(int ac, char **av)
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);
@@ -2067,12 +2073,6 @@ main(int ac, char **av)
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
diff --git a/sshd_config b/sshd_config
index 4eb2e02..e55fe4b 100644
--- a/sshd_config
+++ b/sshd_config
@@ -24,7 +24,7 @@
#RekeyLimit default none
# Logging
-#SyslogFacility AUTH
+SyslogFacility AUTHPRIV
#LogLevel INFO
# Authentication:
@@ -54,8 +54,9 @@ AuthorizedKeysFile .ssh/authorized_keys
# 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
@@ -80,7 +81,10 @@ AuthorizedKeysFile .ssh/authorized_keys
# 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
diff --git a/sshd_config.0 b/sshd_config.0
index b0160aa..757ce51 100644
--- a/sshd_config.0
+++ b/sshd_config.0
@@ -672,7 +672,7 @@ DESCRIPTION
PasswordAuthentication
Specifies whether password authentication is allowed. The
- default is yes.
+ default is no.
PermitEmptyPasswords
When password authentication is allowed, it specifies whether the
@@ -902,7 +902,7 @@ DESCRIPTION
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.
VersionAddendum
Optionally specifies additional text to append to the SSH
diff --git a/sshd_config.5 b/sshd_config.5
index ac6ccc7..e208fc8 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -1136,7 +1136,7 @@ are refused if the number of unauthenticated connections reaches full (60).
.It Cm PasswordAuthentication
Specifies whether password authentication is allowed.
The default is
-.Cm yes .
+.Cm no .
.It Cm PermitEmptyPasswords
When password authentication is allowed, it specifies whether the
server allows login to accounts with empty password strings.
@@ -1493,7 +1493,7 @@ is enabled, you will not be able to run
.Xr sshd 8
as a non-root user.
The default is
-.Cm no .
+.Cm yes .
.It Cm VersionAddendum
Optionally specifies additional text to append to the SSH protocol banner
sent by the server upon connection.
--
2.12.1
From 8dea1d383891d81020225c6f83f0697c39704561 Mon Sep 17 00:00:00 2001
From: Leon Klingele <git@leonklingele.de>
Date: Tue, 21 Mar 2017 22:38:12 +0100
Subject: [PATCH 2/3] 0002-apple-sandbox-named-external.patch
---
sshd.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/sshd.c b/sshd.c
index 12f988a..088fa4d 100644
--- a/sshd.c
+++ b/sshd.c
@@ -619,10 +619,17 @@ privsep_preauth(Authctxt *authctxt)
/* Arrange for logging to be sent to the monitor */
set_log_handler(mm_log_handler, pmonitor);
+#ifdef __APPLE_SANDBOX_NAMED_EXTERNAL__
+ /* We need to do this before we chroot() so we can read sshd.sb */
+ if (box != NULL)
+ ssh_sandbox_child(box);
+#endif
privsep_preauth_child();
setproctitle("%s", "[net]");
+#ifndef __APPLE_SANDBOX_NAMED_EXTERNAL__
if (box != NULL)
ssh_sandbox_child(box);
+#endif
return 0;
}
--
2.12.1
From fc0bde10e9f3bb27b4cb3c28038f8cdad95fea23 Mon Sep 17 00:00:00 2001
From: Leon Klingele <git@leonklingele.de>
Date: Tue, 21 Mar 2017 22:38:50 +0100
Subject: [PATCH 3/3] 0003-launchd.patch
---
clientloop.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/clientloop.c b/clientloop.c
index 0648162..e208c8d 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -315,6 +315,11 @@ client_x11_get_proto(const char *display, const char *xauth_path,
struct stat st;
u_int now, x11_timeout_real;
+#if __APPLE__
+ // TODO(leon): Combine with channels.c's is_path_to_xsocket
+ int is_path_to_socket = 0;
+#endif /* __APPLE__ */
+
*_proto = proto;
*_data = data;
proto[0] = data[0] = xauthfile[0] = xauthdir[0] = '\0';
@@ -331,6 +336,34 @@ client_x11_get_proto(const char *display, const char *xauth_path,
}
if (xauth_path != NULL) {
+#if __APPLE__
+ {
+ /*
+ * If using launchd socket, remove the screen number from the end
+ * of $DISPLAY. is_path_to_socket is used later in this function
+ * to determine if an error should be displayed.
+ */
+ char path[PATH_MAX];
+ struct stat sbuf;
+
+ strlcpy(path, display, sizeof(path));
+ if (0 == stat(path, &sbuf)) {
+ is_path_to_socket = 1;
+ } else {
+ char *dot = strrchr(path, '.');
+ if (dot) {
+ *dot = '\0';
+ /* screen = atoi(dot + 1); */
+ if (0 == stat(path, &sbuf)) {
+ is_path_to_socket = 1;
+ debug("x11_get_proto: $DISPLAY is launchd, removing screennum");
+ setenv("DISPLAY", path, 1);
+ }
+ }
+ }
+ }
+#endif /* __APPLE__ */
+
/*
* Handle FamilyLocal case where $DISPLAY does
* not match an authorization entry. For this we
@@ -441,6 +474,9 @@ client_x11_get_proto(const char *display, const char *xauth_path,
u_int8_t rnd[16];
u_int i;
+#if __APPLE__
+ if (!is_path_to_socket)
+#endif /* __APPLE__ */
logit("Warning: No xauth data; "
"using fake authentication data for X11 forwarding.");
strlcpy(proto, SSH_X11_PROTO, sizeof proto);
--
2.12.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment