Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save kogent/8792974 to your computer and use it in GitHub Desktop.
Save kogent/8792974 to your computer and use it in GitHub Desktop.
# HG changeset patch
# User Sean Farley <[email protected]>
# Date 1382624667 -28800
# Thu Oct 24 22:24:27 2013 +0800
# Node ID dd6d51b7e12be5fab94a8779e890c5558e4d4001
# Parent 86a3bc5c8ff689a291e86950a3d8fd327f42b870
partial import
wiggled scp
wiggled readconf
wiggled readconf.c
diff --git a/Makefile.in b/Makefile.in
--- a/Makefile.in
+++ b/Makefile.in
@@ -56,10 +56,11 @@
PERL=@PERL@
SED=@SED@
ENT=@ENT@
XAUTH_PATH=@XAUTH_PATH@
LDFLAGS=-L. -Lopenbsd-compat/ @LDFLAGS@
+KEYCHAIN_LDFLAGS=@KEYCHAIN_LDFLAGS@
EXEEXT=@EXEEXT@
MANFMT=@MANFMT@
TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT)
@@ -93,10 +94,12 @@
sftp-server.o sftp-common.o \
roaming_common.o roaming_serv.o \
sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
sandbox-seccomp-filter.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@
CONFIGFILES=sshd_config.out ssh_config.out moduli.out
@@ -127,10 +130,11 @@
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 $<
LIBCOMPAT=openbsd-compat/libopenbsd-compat.a
@@ -140,24 +144,24 @@
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)
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)
ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keysign.o roaming_dummy.o readconf.o
@@ -265,11 +269,11 @@
$(INSTALL) -m 0755 $(STRIP_OPT) ssh-add$(EXEEXT) $(DESTDIR)$(bindir)/ssh-add$(EXEEXT)
$(INSTALL) -m 0755 $(STRIP_OPT) ssh-agent$(EXEEXT) $(DESTDIR)$(bindir)/ssh-agent$(EXEEXT)
$(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)
$(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1
$(INSTALL) -m 644 scp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1
diff --git a/audit-bsm.c b/audit-bsm.c
--- a/audit-bsm.c
+++ b/audit-bsm.c
@@ -261,11 +261,16 @@
uid_t uid = -1;
gid_t gid = -1;
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;
}
rc = (typ == 0) ? 0 : -1;
diff --git a/auth-pam.c b/auth-pam.c
--- a/auth-pam.c
+++ b/auth-pam.c
@@ -789,14 +789,15 @@
**echo_on = 0;
ctxt->pam_done = 1;
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;
**echo_on = 0;
free(msg);
diff --git a/auth.c b/auth.c
--- a/auth.c
+++ b/auth.c
@@ -209,11 +209,11 @@
return 0;
}
}
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
--- a/authfd.c
+++ b/authfd.c
@@ -687,10 +687,33 @@
type = buffer_get_char(&msg);
buffer_free(&msg);
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)
{
switch (type) {
case SSH_AGENT_FAILURE:
diff --git a/authfd.h b/authfd.h
--- a/authfd.h
+++ b/authfd.h
@@ -47,10 +47,13 @@
/* add key with constraints */
#define SSH_AGENTC_ADD_RSA_ID_CONSTRAINED 24
#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
/* extended failure messages */
#define SSH2_AGENT_FAILURE 30
diff --git a/config.h.in b/config.h.in
--- a/config.h.in
+++ b/config.h.in
@@ -75,10 +75,22 @@
#undef BROKEN_SNPRINTF
/* FreeBSD strnvis does not do what we need */
#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
/* updwtmpx is broken (if present) */
#undef BROKEN_UPDWTMPX
diff --git a/configure.ac b/configure.ac
--- a/configure.ac
+++ b/configure.ac
@@ -4548,14 +4548,44 @@
#ifdef HAVE_LASTLOG_H
#include <lastlog.h>
#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
TEST_SSH_IPV6=yes
fi
diff --git a/groupaccess.c b/groupaccess.c
--- a/groupaccess.c
+++ b/groupaccess.c
@@ -32,62 +32,107 @@
#include <unistd.h>
#include <stdarg.h>
#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
+ 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_bygid = xcalloc(ngroups, sizeof(*groups_bygid));
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);
free(groups_bygid);
return (ngroups = j);
}
/*
* 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;
}
/*
* Return 1 if one of user's groups matches group_pattern list.
diff --git a/groupaccess.h b/groupaccess.h
--- a/groupaccess.h
+++ b/groupaccess.h
@@ -25,11 +25,11 @@
*/
#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);
#endif
diff --git a/keychain.c b/keychain.c
new file mode 100644
--- /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
--- /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
--- a/readconf.c
+++ b/readconf.c
@@ -136,10 +136,13 @@
oSendEnv, oControlPath, oControlMaster, oControlPersist,
oHashKnownHosts,
oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication,
oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown,
+#ifdef __APPLE_KEYCHAIN__
+ oAskPassGUI,
+#endif
oIgnoredUnknownOption, oDeprecated, oUnsupported
} OpCodes;
/* Textual representations of the tokens. */
@@ -248,11 +251,13 @@
#endif
{ "kexalgorithms", oKexAlgorithms },
{ "ipqos", oIPQoS },
{ "requesttty", oRequestTTY },
{ "ignoreunknown", oIgnoreUnknown },
-
+#ifdef __APPLE_KEYCHAIN__
+ { "askpassgui", oAskPassGUI },
+#endif
{ NULL, oBadOption }
};
/*
* Adds a local TCP/IP port forward to options. Never returns if there is an
@@ -1070,10 +1075,16 @@
case oIgnoreUnknown:
charptr = &options->ignored_unknown;
goto parse_string;
+#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);
return 0;
@@ -1232,10 +1243,13 @@
options->zero_knowledge_password_authentication = -1;
options->ip_qos_interactive = -1;
options->ip_qos_bulk = -1;
options->request_tty = -1;
options->ignored_unknown = NULL;
+#ifdef __APPLE_KEYCHAIN__
+ options->ask_pass_gui = -1;
+#endif
}
/*
* Called after processing other sources of option data, this fills those
* options for which no value has been specified with their default values.
@@ -1383,10 +1397,14 @@
options->ip_qos_interactive = IPTOS_LOWDELAY;
if (options->ip_qos_bulk == -1)
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
/* 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
--- a/readconf.h
+++ b/readconf.h
@@ -137,10 +137,14 @@
int use_roaming;
int request_tty;
char *ignored_unknown; /* Pattern list of unknown tokens to ignore */
+
+#ifdef __APPLE_KEYCHAIN__
+ int ask_pass_gui;
+#endif
} Options;
#define SSHCTL_MASTER_NO 0
#define SSHCTL_MASTER_YES 1
#define SSHCTL_MASTER_AUTO 2
diff --git a/scp.1 b/scp.1
--- a/scp.1
+++ b/scp.1
@@ -17,11 +17,11 @@
.Nm scp
.Nd secure copy (remote file copy program)
.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
.Op Fl l Ar limit
.Op Fl o Ar ssh_option
@@ -95,10 +95,12 @@
Passes the
.Fl C
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
.Xr ssh 1 .
.It Fl F Ar ssh_config
diff --git a/scp.c b/scp.c
--- a/scp.c
+++ b/scp.c
@@ -76,10 +76,13 @@
#include <sys/types.h>
#include <sys/param.h>
#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
# ifdef HAVE_SYS_POLL_H
# include <sys/poll.h>
@@ -112,10 +115,15 @@
#include "pathnames.h"
#include "log.h"
#include "misc.h"
#include "progressmeter.h"
+#ifdef HAVE_COPYFILE_H
+#include <libgen.h>
+#include <copyfile.h>
+#endif
+
extern char *__progname;
#define COPY_BUFLEN 16384
int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout);
@@ -148,10 +156,16 @@
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)
{
if (do_cmd_pid > 1) {
kill(do_cmd_pid, signo ? signo : SIGTERM);
@@ -393,11 +407,15 @@
addargs(&args, "-oForwardAgent=no");
addargs(&args, "-oPermitLocalCommand=no");
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':
case '2':
case '4':
@@ -454,10 +472,15 @@
addargs(&args, "-q");
addargs(&remote_remote_args, "-q");
showprogress = 0;
break;
+#ifdef HAVE_COPYFILE
+ case 'E':
+ copy_xattr = 1;
+ break;
+#endif
/* Server options. */
case 'd':
targetshouldbedirectory = 1;
break;
case 'f': /* "from" */
@@ -503,11 +526,16 @@
targetshouldbedirectory = 1;
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" : "");
(void) signal(SIGPIPE, lostconn);
@@ -749,23 +777,41 @@
off_t i, statbytes;
size_t amt;
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];
statbytes = 0;
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;
}
if (stb.st_size < 0) {
@@ -844,10 +890,40 @@
if (!haderr)
(void) atomicio(vwrite, remout, "", 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
}
}
void
rsource(char *name, struct stat *statp)
@@ -935,10 +1011,14 @@
(void) atomicio(vwrite, remout, "", 1);
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;
if (*cp++ == '\n')
SCREWUP("unexpected <newline>");
@@ -1080,14 +1160,55 @@
free(vect[0]);
continue;
}
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);
continue;
}
@@ -1168,10 +1289,33 @@
if (close(ofd) == -1) {
wrerr = YES;
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) {
run_err("%s: set times: %s",
np, strerror(errno));
@@ -1229,11 +1373,15 @@
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
--- a/servconf.c
+++ b/servconf.c
@@ -158,11 +158,11 @@
void
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)
options->protocol = SSH_PROTO_2;
if (options->num_host_key_files == 0) {
@@ -241,11 +241,11 @@
if (options->gss_authentication == -1)
options->gss_authentication = 0;
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)
options->challenge_response_authentication = 1;
if (options->permit_empty_passwd == -1)
@@ -621,11 +621,11 @@
goto out;
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) {
debug("user %.100s does not match group list %.100s at line %d",
user, grps, line);
diff --git a/session.c b/session.c
--- a/session.c
+++ b/session.c
@@ -2081,12 +2081,14 @@
/* for SSH1 the tty modes length is not given */
if (!compat20)
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);
packet_check_eom();
@@ -2322,13 +2324,15 @@
/* Record that the user has logged out. */
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
* the pty cleanup, so that another process doesn't get this pty
* while we're still cleaning up.
diff --git a/ssh-add.0 b/ssh-add.0
--- a/ssh-add.0
+++ b/ssh-add.0
@@ -2,11 +2,11 @@
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
DESCRIPTION
ssh-add adds private key identities to the authentication agent,
@@ -53,10 +53,17 @@
represented by the agent.
-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.
-t life
Set a maximum lifetime when adding identities to an agent. The
diff --git a/ssh-add.1 b/ssh-add.1
--- a/ssh-add.1
+++ b/ssh-add.1
@@ -41,11 +41,11 @@
.Sh NAME
.Nm ssh-add
.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
.Fl s Ar pkcs11
.Nm ssh-add
@@ -116,10 +116,17 @@
.It Fl L
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 .
.It Fl t Ar life
Set a maximum lifetime when adding identities to an agent.
diff --git a/ssh-add.c b/ssh-add.c
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -60,10 +60,11 @@
#include "buffer.h"
#include "authfd.h"
#include "authfile.h"
#include "pathnames.h"
#include "misc.h"
+#include "keychain.h"
/* argv0 */
extern char *__progname;
/* Default files to add */
@@ -94,16 +95,28 @@
pass = NULL;
}
}
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);
return -1;
}
@@ -162,11 +175,11 @@
return ret;
}
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;
char msg[1024], *certpath = NULL;
int fd, perms_ok, ret = -1;
@@ -199,15 +212,20 @@
}
close(fd);
/* 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();
snprintf(msg, sizeof msg, "Enter passphrase for %.200s: ",
comment);
@@ -219,12 +237,15 @@
buffer_free(&keyblob);
return -1;
}
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);
}
}
@@ -374,17 +395,17 @@
free(p1);
return (ret);
}
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;
}
@@ -402,20 +423,26 @@
fprintf(stderr, " -D Delete all identities.\n");
fprintf(stderr, " -x Lock agent.\n");
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
main(int argc, char **argv)
{
extern char *optarg;
extern int optind;
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();
__progname = ssh_get_progname(argv[0]);
@@ -428,11 +455,11 @@
if (ac == NULL) {
fprintf(stderr,
"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, "kKlLcdDxXe:s:t:")) != -1) {
switch (ch) {
case 'k':
key_only = 1;
break;
case 'l':
@@ -467,10 +494,17 @@
fprintf(stderr, "Invalid lifetime\n");
ret = 1;
goto done;
}
break;
+ case 'm':
+ if (add_from_keychain(ac) == -1)
+ ret = 1;
+ goto done;
+ case 'M':
+ keychain = 1;
+ break;
default:
usage();
ret = 1;
goto done;
}
@@ -498,20 +532,20 @@
for (i = 0; default_files[i]; i++) {
snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir,
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++;
}
if (count == 0)
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;
}
}
clear_pass();
diff --git a/ssh-agent.c b/ssh-agent.c
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -63,20 +63,25 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
+#ifdef __APPLE_LAUNCHD__
+#include <launch.h>
+#endif
#include "xmalloc.h"
#include "ssh.h"
#include "rsa.h"
#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"
#endif
@@ -788,10 +793,65 @@
buffer_put_char(&e->output,
success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
}
#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
process_message(SocketEntry *e)
{
@@ -880,10 +940,13 @@
break;
case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
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);
buffer_clear(&e->request);
buffer_put_int(&e->output, 1);
@@ -1120,11 +1183,15 @@
}
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;
fd_set *readsetp = NULL, *writesetp = NULL;
struct sockaddr_un sunaddr;
@@ -1154,20 +1221,29 @@
OpenSSL_add_all_algorithms();
__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)
usage();
c_flag++;
break;
case 'k':
k_flag++;
break;
+#ifdef __APPLE_LAUNCHD__
+ case 'l':
+ l_flag++;
+ break;
+#endif
case 's':
if (c_flag)
usage();
s_flag++;
break;
@@ -1190,11 +1266,15 @@
}
}
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) {
shell = getenv("SHELL");
if (shell != NULL && (len = strlen(shell)) > 2 &&
@@ -1246,10 +1326,57 @@
/*
* 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");
*socket_name = '\0'; /* Don't unlink any existing file */
cleanup_exit(1);
@@ -1267,10 +1394,18 @@
umask(prev_mask);
if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
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
* the socket data. The child continues as the authentication agent.
*/
@@ -1339,19 +1474,24 @@
#ifdef ENABLE_PKCS11
pkcs11_init(0);
#endif
new_socket(AUTH_SOCKET, sock);
+skip2:
if (ac > 0)
parent_alive_interval = 10;
idtab_init();
signal(SIGPIPE, SIG_IGN);
signal(SIGINT, d_flag ? cleanup_handler : SIG_IGN);
signal(SIGHUP, cleanup_handler);
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);
saved_errno = errno;
if (parent_alive_interval != 0)
diff --git a/ssh-keysign.8 b/ssh-keysign.8
--- a/ssh-keysign.8
+++ b/ssh-keysign.8
@@ -69,10 +69,13 @@
They should be owned by root, readable only by root, and not
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
.It Pa /etc/ssh/ssh_host_rsa_key-cert.pub
If these files exist they are assumed to contain public certificate
diff --git a/sshconnect1.c b/sshconnect1.c
--- a/sshconnect1.c
+++ b/sshconnect1.c
@@ -45,10 +45,11 @@
#include "authfile.h"
#include "misc.h"
#include "canohost.h"
#include "hostfile.h"
#include "auth.h"
+#include "keychain.h"
/* Session id for the current session. */
u_char session_id[16];
u_int supported_authentications = 0;
@@ -258,10 +259,14 @@
&perm_ok);
if (private == NULL && !options.batch_mode && perm_ok) {
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,
authfile, passphrase, NULL, NULL);
quit = 0;
diff --git a/sshconnect2.c b/sshconnect2.c
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -70,10 +70,11 @@
#include "pathnames.h"
#include "uidswap.h"
#include "hostfile.h"
#include "schnorr.h"
#include "jpake.h"
+#include "keychain.h"
#ifdef GSSAPI
#include "ssh-gss.h"
#endif
@@ -1331,10 +1332,14 @@
if (options.batch_mode)
return NULL;
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,
filename, passphrase, NULL, NULL);
quit = 0;
diff --git a/sshd.0 b/sshd.0
--- a/sshd.0
+++ b/sshd.0
@@ -620,12 +620,12 @@
The content of this file is not sensitive; it can be world-
readable.
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
Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo
de Raadt and Dug Song removed many bugs, re-added newer features and
diff --git a/sshd.8 b/sshd.8
--- a/sshd.8
+++ b/sshd.8
@@ -954,14 +954,11 @@
.Xr ssh-agent 1 ,
.Xr ssh-keygen 1 ,
.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
ssh 1.2.12 release by Tatu Ylonen.
Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos,
diff --git a/sshd.c b/sshd.c
--- a/sshd.c
+++ b/sshd.c
@@ -2106,23 +2106,23 @@
#ifdef SSH_AUDIT_EVENTS
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);
ssh_gssapi_storecreds();
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
* file descriptor passing.
*/
diff --git a/sshd_config b/sshd_config
--- a/sshd_config
+++ b/sshd_config
@@ -32,11 +32,11 @@
# Ciphers and keying
#RekeyLimit default none
# Logging
# obsoletes QuietMode and FascistLogging
-#SyslogFacility AUTH
+SyslogFacility AUTHPRIV
#LogLevel INFO
# Authentication:
#LoginGraceTime 2m
@@ -65,12 +65,13 @@
# RhostsRSAAuthentication and HostbasedAuthentication
#IgnoreUserKnownHosts no
# 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
#ChallengeResponseAuthentication yes
@@ -91,11 +92,14 @@
# PAM authentication via ChallengeResponseAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# 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
#GatewayPorts no
#X11Forwarding no
diff --git a/sshd_config.0 b/sshd_config.0
--- a/sshd_config.0
+++ b/sshd_config.0
@@ -505,11 +505,11 @@
increases linearly and all connection attempts are refused if the
number of unauthenticated connections reaches ``full'' (60).
PasswordAuthentication
Specifies whether password authentication is allowed. The
- default is ``yes''.
+ default is ``no''.
PermitEmptyPasswords
When password authentication is allowed, it specifies whether the
server allows login to accounts with empty password strings. The
default is ``no''.
@@ -707,11 +707,11 @@
Because PAM challenge-response authentication usually serves an
equivalent role to password authentication, you should disable
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
unprivileged child process to deal with incoming network traffic.
After successful authentication, another process will be created
diff --git a/sshd_config.5 b/sshd_config.5
--- 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
diff --git a/ssh-add.c b/ssh-add.c
index 63ce720..f665aba 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -434,7 +434,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, "klLcdDxXmMe:s:t:")) != -1) {
switch (ch) {
case 'k':
key_only = 1;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment