Skip to content

Instantly share code, notes, and snippets.

@kruton
Created February 12, 2014 07:31
Show Gist options
  • Save kruton/8951373 to your computer and use it in GitHub Desktop.
Save kruton/8951373 to your computer and use it in GitHub Desktop.
OpenSSH 6.5p1 Apple Keychain patch
From ca4ab3b9f7568055ee769ed0e2ea6a82dc64ad56 Mon Sep 17 00:00:00 2001
From: Kenny Root <[email protected]>
Date: Tue, 11 Feb 2014 21:48:58 -0800
Subject: [PATCH] OpenSSH 6.5p1: Apple Keychain patch
Original commit had this information:
HG changeset patch
User Sean Farley <[email protected]>
Date 1382624667 -28800
Thu Oct 24 22:24:27 2013 +0800
---
Makefile.in | 18 +-
audit-bsm.c | 7 +-
auth-pam.c | 5 +-
auth.c | 2 +-
authfd.c | 23 ++
authfd.h | 3 +
config.h.in | 12 +
configure.ac | 30 +++
groupaccess.c | 59 ++++-
groupaccess.h | 2 +-
keychain.c | 694 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
keychain.h | 45 ++++
readconf.c | 22 +-
readconf.h | 4 +
scp.1 | 4 +-
scp.c | 148 +++++++++++++
servconf.c | 6 +-
session.c | 4 +
ssh-add.0 | 9 +-
ssh-add.1 | 9 +-
ssh-add.c | 54 ++++-
ssh-agent.c | 140 ++++++++++++
ssh-keysign.8 | 3 +
sshconnect1.c | 5 +
sshconnect2.c | 5 +
sshd.0 | 4 +-
sshd.8 | 3 -
sshd.c | 12 +-
sshd_config | 12 +-
sshd_config.0 | 4 +-
sshd_config.5 | 4 +-
31 files changed, 1297 insertions(+), 55 deletions(-)
create mode 100644 keychain.c
create mode 100644 keychain.h
diff --git a/Makefile.in b/Makefile.in
index a8aa127..e938fb9 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -58,6 +58,7 @@ SED=@SED@
ENT=@ENT@
XAUTH_PATH=@XAUTH_PATH@
LDFLAGS=-L. -Lopenbsd-compat/ @LDFLAGS@
+KEYCHAIN_LDFLAGS=@KEYCHAIN_LDFLAGS@
EXEEXT=@EXEEXT@
MANFMT=@MANFMT@
@@ -98,6 +99,8 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \
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@
@@ -133,6 +136,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 $<
@@ -146,8 +150,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)
@@ -155,11 +159,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)
@@ -271,7 +275,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 6135591..4711b25 100644
--- a/audit-bsm.c
+++ b/audit-bsm.c
@@ -263,7 +263,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 d789bad..814c893 100644
--- a/auth-pam.c
+++ b/auth-pam.c
@@ -793,10 +793,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,
- 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;
diff --git a/auth.c b/auth.c
index 9a36f1d..1ea002c 100644
--- a/auth.c
+++ b/auth.c
@@ -211,7 +211,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 f963690..f8d4a16 100644
--- a/authfd.c
+++ b/authfd.c
@@ -638,6 +638,29 @@ ssh_remove_all_identities(AuthenticationConnection *auth, int version)
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)
{
diff --git a/authfd.h b/authfd.h
index 2582a27..7b786fe 100644
--- a/authfd.h
+++ 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
diff --git a/config.h.in b/config.h.in
index 075c619..a16ba86 100644
--- a/config.h.in
+++ 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
diff --git a/configure.ac b/configure.ac
index dfd32cd..049cbe5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4779,10 +4779,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 1eab10b..7934d9d 100644
--- a/groupaccess.c
+++ 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 @@ 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..5c2d1ff
--- /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;
+ 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
+
+}
diff --git a/keychain.h b/keychain.h
new file mode 100644
index 0000000..3ab1a6b
--- /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 9c7e73d..3e18ccb 100644
--- a/readconf.c
+++ b/readconf.c
@@ -148,6 +148,9 @@ typedef enum {
oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass,
oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
+#ifdef __APPLE_KEYCHAIN__
+ oAskPassGUI,
+#endif
oIgnoredUnknownOption, oDeprecated, oUnsupported
} OpCodes;
@@ -267,7 +270,9 @@ static struct {
{ "canonicalizemaxdots", oCanonicalizeMaxDots },
{ "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs },
{ "ignoreunknown", oIgnoreUnknown },
-
+#ifdef __APPLE_KEYCHAIN__
+ { "askpassgui", oAskPassGUI },
+#endif
{ NULL, oBadOption }
};
@@ -1392,6 +1397,12 @@ parse_int:
intptr = &options->canonicalize_fallback_local;
goto parse_flag;
+#ifdef __APPLE_KEYCHAIN__
+ case oAskPassGUI:
+ intptr = &options->ask_pass_gui;
+ goto parse_flag;
+#endif
+
case oDeprecated:
debug("%s line %d: Deprecated option \"%s\"",
filename, linenum, keyword);
@@ -1560,6 +1571,9 @@ initialize_options(Options * options)
options->canonicalize_max_dots = -1;
options->canonicalize_fallback_local = -1;
options->canonicalize_hostname = -1;
+#ifdef __APPLE_KEYCHAIN__
+ options->ask_pass_gui = -1;
+#endif
}
/*
@@ -1713,6 +1727,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)
@@ -1731,6 +1749,8 @@ fill_default_options(Options * options)
CLEAR_ON_NONE(options->local_command);
CLEAR_ON_NONE(options->proxy_command);
CLEAR_ON_NONE(options->control_path);
+ /* options->local_command should not be set by default */
+ /* options->proxy_command should not be set by default */
/* options->user will be set in the main program if appropriate */
/* options->hostname will be set in the main program if appropriate */
/* options->host_key_alias should not be set by default */
diff --git a/readconf.h b/readconf.h
index 2d7ea9f..ceffad2 100644
--- a/readconf.h
+++ b/readconf.h
@@ -155,6 +155,10 @@ typedef struct {
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
diff --git a/scp.1 b/scp.1
index 3b67cff..2cf06b4 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
@@ -97,6 +97,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 18d3b1d..c0c4a22 100644
--- a/scp.c
+++ 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 @@ 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)
{
@@ -395,7 +409,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':
@@ -456,6 +474,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;
@@ -505,7 +528,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" : "");
@@ -751,6 +779,10 @@ source(int argc, char **argv)
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 @@ 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;
@@ -846,6 +892,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
}
}
@@ -937,6 +1013,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;
@@ -1082,10 +1162,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);
@@ -1170,6 +1291,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 (setimes && wrerr == NO) {
setimes = 0;
if (utimes(np, tv) < 0) {
@@ -1231,7 +1375,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 9bcd05b..25d02c6 100644
--- a/servconf.c
+++ b/servconf.c
@@ -161,7 +161,7 @@ fill_default_server_options(ServerOptions *options)
{
/* Portable-specific options */
if (options->use_pam == -1)
- options->use_pam = 0;
+ options->use_pam = 1;
/* Standard Options */
if (options->protocol == SSH_PROTO_UNKNOWN)
@@ -248,7 +248,7 @@ fill_default_server_options(ServerOptions *options)
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)
@@ -629,7 +629,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 12dd9ab..582a3c8 100644
--- a/session.c
+++ b/session.c
@@ -2111,8 +2111,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);
@@ -2352,9 +2354,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 8d10f7f..e385250 100644
--- a/ssh-add.0
+++ b/ssh-add.0
@@ -4,7 +4,7 @@ NAME
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 @@ 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 4812448..8bc39f4 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 t Ar life
.Op Ar
.Nm ssh-add
@@ -119,6 +119,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 63ce720..ba03706 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -62,6 +62,7 @@
#include "authfile.h"
#include "pathnames.h"
#include "misc.h"
+#include "keychain.h"
/* argv0 */
extern char *__progname;
@@ -97,12 +98,24 @@ clear_pass(void)
}
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);
@@ -165,7 +178,7 @@ delete_all(AuthenticationConnection *ac)
}
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;
@@ -202,11 +215,16 @@ add_file(AuthenticationConnection *ac, const char *filename, int key_only)
/* At first, try empty passphrase */
private = key_parse_private(&keyblob, filename, "", &comment);
+ if (keychain && private != NULL)
+ store_in_keychain(filename, "");
if (comment == NULL)
comment = xstrdup(filename);
/* try last */
- if (private == NULL && pass != NULL)
+ if (private == NULL && pass != NULL) {
private = key_parse_private(&keyblob, filename, pass, NULL);
+ if (keychain && private != NULL)
+ store_in_keychain(filename, pass);
+ }
if (private == NULL) {
/* clear passphrase since it did not work */
clear_pass();
@@ -222,8 +240,11 @@ add_file(AuthenticationConnection *ac, const char *filename, int key_only)
}
private = key_parse_private(&keyblob, filename, pass,
&comment);
- 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);
@@ -380,13 +401,13 @@ lock_agent(AuthenticationConnection *ac, int lock)
}
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;
@@ -408,6 +429,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
@@ -418,6 +444,7 @@ main(int argc, char **argv)
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();
@@ -434,7 +461,7 @@ main(int argc, char **argv)
"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;
@@ -473,6 +500,13 @@ main(int argc, char **argv)
goto done;
}
break;
+ case 'm':
+ if (add_from_keychain(ac) == -1)
+ ret = 1;
+ goto done;
+ case 'M':
+ keychain = 1;
+ break;
default:
usage();
ret = 1;
@@ -504,7 +538,7 @@ main(int argc, char **argv)
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++;
@@ -513,7 +547,7 @@ main(int argc, char **argv)
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;
}
}
diff --git a/ssh-agent.c b/ssh-agent.c
index 95117e0..43a55bc 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -65,6 +65,9 @@
#include <time.h>
#include <string.h>
#include <unistd.h>
+#ifdef __APPLE_LAUNCHD__
+#include <launch.h>
+#endif
#include "xmalloc.h"
#include "ssh.h"
@@ -72,9 +75,11 @@
#include "buffer.h"
#include "key.h"
#include "authfd.h"
+#include "authfile.h"
#include "compat.h"
#include "log.h"
#include "misc.h"
+#include "keychain.h"
#ifdef ENABLE_PKCS11
#include "ssh-pkcs11.h"
@@ -682,6 +687,61 @@ process_remove_smartcard_key(SocketEntry *e)
}
#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
@@ -774,6 +834,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);
@@ -1014,7 +1077,11 @@ usage(void)
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;
@@ -1048,7 +1115,11 @@ main(int ac, char **av)
__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)
@@ -1058,6 +1129,11 @@ main(int ac, char **av)
case 'k':
k_flag++;
break;
+#ifdef __APPLE_LAUNCHD__
+ case 'l':
+ l_flag++;
+ break;
+#endif
case 's':
if (c_flag)
usage();
@@ -1084,7 +1160,11 @@ main(int ac, char **av)
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) {
@@ -1140,6 +1220,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
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket");
@@ -1161,6 +1288,14 @@ main(int ac, char **av)
perror("listen");
cleanup_exit(1);
}
+#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
@@ -1233,6 +1368,7 @@ skip:
pkcs11_init(0);
#endif
new_socket(AUTH_SOCKET, sock);
+skip2:
if (ac > 0)
parent_alive_interval = 10;
idtab_init();
@@ -1242,6 +1378,10 @@ skip:
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);
diff --git a/ssh-keysign.8 b/ssh-keysign.8
index 69d0829..eba9255 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 7bd6cb0..de90f7c 100644
--- a/sshconnect1.c
+++ b/sshconnect1.c
@@ -47,6 +47,7 @@
#include "canohost.h"
#include "hostfile.h"
#include "auth.h"
+#include "keychain.h"
/* Session id for the current session. */
u_char session_id[16];
@@ -260,6 +261,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 8acffc5..fdf775d 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -72,6 +72,7 @@
#include "hostfile.h"
#include "schnorr.h"
#include "jpake.h"
+#include "keychain.h"
#ifdef GSSAPI
#include "ssh-gss.h"
@@ -1335,6 +1336,10 @@ load_identity_file(char *filename, int userprovided)
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,
diff --git a/sshd.0 b/sshd.0
index 154009c..f2432cd 100644
--- a/sshd.0
+++ b/sshd.0
@@ -625,8 +625,8 @@ FILES
SEE ALSO
scp(1), sftp(1), ssh(1), ssh-add(1), ssh-agent(1), ssh-keygen(1),
- ssh-keyscan(1), chroot(2), hosts_access(5), login.conf(5), moduli(5),
- sshd_config(5), inetd(8), sftp-server(8)
+ ssh-keyscan(1), chroot(2), hosts_access(5), 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 e6a900b..bf8cca8 100644
--- a/sshd.8
+++ b/sshd.8
@@ -961,10 +961,7 @@ The content of this file is not sensitive; it can be world-readable.
.Xr ssh-keyscan 1 ,
.Xr chroot 2 ,
.Xr hosts_access 5 ,
-.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 25380c9..296dcea 100644
--- a/sshd.c
+++ b/sshd.c
@@ -2124,6 +2124,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);
@@ -2131,12 +2137,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 e9045bc..9e1be7a 100644
--- a/sshd_config
+++ b/sshd_config
@@ -35,7 +35,7 @@
# Logging
# obsoletes QuietMode and FascistLogging
-#SyslogFacility AUTH
+SyslogFacility AUTHPRIV
#LogLevel INFO
# Authentication:
@@ -68,8 +68,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
@@ -94,7 +95,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 5962b02..a765487 100644
--- a/sshd_config.0
+++ b/sshd_config.0
@@ -517,7 +517,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
@@ -723,7 +723,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''.
UsePrivilegeSeparation
Specifies whether sshd(8) separates privileges by creating an
diff --git a/sshd_config.5 b/sshd_config.5
index 3b21ea6..a9dda92 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -871,7 +871,7 @@ are refused if the number of unauthenticated connections reaches
.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.
@@ -1204,7 +1204,7 @@ is enabled, you will not be able to run
.Xr sshd 8
as a non-root user.
The default is
-.Dq no .
+.Dq yes .
.It Cm UsePrivilegeSeparation
Specifies whether
.Xr sshd 8
--
1.8.5.4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment