Skip to content

Instantly share code, notes, and snippets.

@joebarker87
Created February 13, 2014 03:38
Show Gist options
  • Select an option

  • Save joebarker87/8969346 to your computer and use it in GitHub Desktop.

Select an option

Save joebarker87/8969346 to your computer and use it in GitHub Desktop.

Revisions

  1. joebarker87 created this gist Feb 13, 2014.
    2,233 changes: 2,233 additions & 0 deletions post_hpn_keychain_patch.diff
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,2233 @@
    diff --git a/Makefile.in b/Makefile.in
    index 8fded74..5279aba 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@

    @@ -95,6 +96,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

    +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@
    @@ -129,6 +132,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 $<
    @@ -142,8 +146,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)
    @@ -151,11 +155,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)
    @@ -267,7 +271,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 d51318b..b74476c 100644
    --- a/auth-pam.c
    +++ b/auth-pam.c
    @@ -791,10 +791,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 775786b..48a580a 100644
    --- a/authfd.c
    +++ b/authfd.c
    @@ -689,6 +689,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 b75e501..02be253 100644
    --- a/config.h.in
    +++ b/config.h.in
    @@ -77,6 +77,18 @@
    /* 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

    diff --git a/configure.ac b/configure.ac
    index 4a1b503..a1ef284 100644
    --- a/configure.ac
    +++ b/configure.ac
    @@ -4550,10 +4550,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 27c1a5e..bd9f2ec 100644
    --- a/readconf.c
    +++ b/readconf.c
    @@ -137,9 +137,13 @@ typedef enum {
    oHashKnownHosts,
    oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
    oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication,
    - oKexAlgorithms, oIPQoS, oRequestTTY, oTcpRcvBufPoll, oTcpRcvBuf,
    - oHPNDisabled, oHPNBufferSize, oNoneSwitch, oNoneEnabled,
    - oIgnoreUnknown, oIgnoredUnknownOption, oDeprecated, oUnsupported
    + oKexAlgorithms, oIPQoS, oRequestTTY, oTcpRcvBufPoll, oTcpRcvBuf,
    + oHPNDisabled, oHPNBufferSize, oNoneSwitch, oNoneEnabled,
    + oIgnoreUnknown,
    +#ifdef __APPLE_KEYCHAIN__
    + oAskPassGUI,
    +#endif
    + oIgnoredUnknownOption, oDeprecated, oUnsupported
    } OpCodes;

    /* Textual representations of the tokens. */
    @@ -199,7 +203,7 @@ static struct {
    { "globalknownhostsfile", oGlobalKnownHostsFile },
    { "globalknownhostsfile2", oDeprecated },
    { "userknownhostsfile", oUserKnownHostsFile },
    - { "userknownhostsfile2", oDeprecated },
    + { "userknownhostsfile2", oDeprecated },
    { "connectionattempts", oConnectionAttempts },
    { "batchmode", oBatchMode },
    { "checkhostip", oCheckHostIP },
    @@ -257,7 +261,9 @@ static struct {
    { "hpndisabled", oHPNDisabled },
    { "hpnbuffersize", oHPNBufferSize },
    { "ignoreunknown", oIgnoreUnknown },
    -
    +#ifdef __APPLE_KEYCHAIN__
    + { "askpassgui", oAskPassGUI },
    +#endif
    { NULL, oBadOption }
    };

    @@ -466,7 +472,7 @@ parse_flag:
    case oForwardX11Trusted:
    intptr = &options->forward_x11_trusted;
    goto parse_flag;
    -
    +
    case oForwardX11Timeout:
    intptr = &options->forward_x11_timeout;
    goto parse_time;
    @@ -550,7 +556,7 @@ parse_flag:
    case oNoneEnabled:
    intptr = &options->none_enabled;
    goto parse_flag;
    -
    +
    /* we check to see if the command comes from the */
    /* command line or not. If it does then enable it */
    /* otherwise fail. NONE should never be a default configuration */
    @@ -1113,6 +1119,12 @@ parse_int:
    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);
    @@ -1281,6 +1293,9 @@ initialize_options(Options * options)
    options->tcp_rcv_buf_poll = -1;
    options->tcp_rcv_buf = -1;
    options->ignored_unknown = NULL;
    +#ifdef __APPLE_KEYCHAIN__
    + options->ask_pass_gui = -1;
    +#endif
    }

    /*
    @@ -1458,6 +1473,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
    /* 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 */
    diff --git a/readconf.h b/readconf.h
    index 23c3cbb..a8a3310 100644
    --- a/readconf.h
    +++ b/readconf.h
    @@ -145,6 +145,10 @@ typedef struct {
    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
    diff --git a/scp.1 b/scp.1
    index c83012c..1346041 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 21f22a6..132d89a 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,19 +779,36 @@ source(int argc, char **argv)
    int fd = -1, haderr, indx;
    char *last, *name, buf[16384], 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;
    @@ -846,6 +891,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 +1012,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 +1161,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 +1290,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 +1374,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 798deef..4d0c8c9 100644
    --- a/servconf.c
    +++ b/servconf.c
    @@ -169,7 +169,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)
    @@ -252,7 +252,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)
    @@ -301,9 +301,9 @@ fill_default_server_options(ServerOptions *options)
    options->permit_tun = SSH_TUNMODE_NO;
    if (options->zero_knowledge_password_authentication == -1)
    options->zero_knowledge_password_authentication = 0;
    - if (options->none_enabled == -1)
    + if (options->none_enabled == -1)
    options->none_enabled = 0;
    - if (options->hpn_disabled == -1)
    + if (options->hpn_disabled == -1)
    options->hpn_disabled = 0;

    if (options->hpn_buffer_size == -1) {
    @@ -316,13 +316,13 @@ fill_default_server_options(ServerOptions *options)
    /*create a socket but don't connect it */
    /* we use that the get the rcv socket size */
    sock = socket(AF_INET, SOCK_STREAM, 0);
    - getsockopt(sock, SOL_SOCKET, SO_RCVBUF,
    + getsockopt(sock, SOL_SOCKET, SO_RCVBUF,
    &socksize, &socksizelen);
    close(sock);
    options->hpn_buffer_size = socksize;
    debug ("HPN Buffer Size: %d", options->hpn_buffer_size);
    -
    - }
    +
    + }
    } else {
    /* we have to do this incase the user sets both values in a contradictory */
    /* manner. hpn_disabled overrrides hpn_buffer_size*/
    @@ -677,7 +677,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 2316821..d062b7d 100644
    --- a/session.c
    +++ b/session.c
    @@ -2084,8 +2084,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);
    @@ -2331,9 +2333,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 bcd1e73..551acd5 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 44846b6..058a4cb 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
    @@ -118,6 +118,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 5e8166f..7620730 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;
    @@ -96,12 +97,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);
    @@ -164,7 +177,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;
    @@ -201,11 +214,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();
    @@ -221,8 +239,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);
    @@ -258,7 +279,7 @@ add_file(AuthenticationConnection *ac, const char *filename, int key_only)
    certpath, filename);
    key_free(cert);
    goto out;
    - }
    + }

    /* Graft with private bits */
    if (key_to_certified(private, key_cert_is_legacy(cert)) != 0) {
    @@ -376,13 +397,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;
    @@ -404,6 +425,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
    @@ -414,6 +440,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();
    @@ -430,7 +457,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;
    @@ -469,6 +496,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;
    @@ -500,7 +534,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++;
    @@ -509,7 +543,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 c3b1172..0e76e1f 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"
    @@ -790,6 +795,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
    @@ -882,6 +942,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);
    @@ -1122,7 +1185,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;
    @@ -1156,7 +1223,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)
    @@ -1166,6 +1237,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();
    @@ -1192,7 +1268,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) {
    @@ -1248,6 +1328,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");
    @@ -1269,6 +1396,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
    @@ -1341,6 +1476,7 @@ skip:
    pkcs11_init(0);
    #endif
    new_socket(AUTH_SOCKET, sock);
    +skip2:
    if (ac > 0)
    parent_alive_interval = 10;
    idtab_init();
    @@ -1350,6 +1486,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 5e0b2d2..f6a41f9 100644
    --- a/ssh-keysign.8
    +++ b/ssh-keysign.8
    @@ -71,6 +71,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 d285e23..3bb5dcf 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 c4adfb3..f1de09b 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"
    @@ -1378,6 +1379,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 c48b987..6c6ac5d 100644
    --- a/sshd.0
    +++ b/sshd.0
    @@ -622,8 +622,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 b0c7ab6..17148d7 100644
    --- a/sshd.8
    +++ b/sshd.8
    @@ -956,10 +956,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 eec1093..3481f98 100644
    --- a/sshd.c
    +++ b/sshd.c
    @@ -2121,6 +2121,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);
    @@ -2128,12 +2134,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 7f10c11..2f91d86 100644
    --- a/sshd_config
    +++ b/sshd_config
    @@ -34,7 +34,7 @@

    # Logging
    # obsoletes QuietMode and FascistLogging
    -#SyslogFacility AUTH
    +SyslogFacility AUTHPRIV
    #LogLevel INFO

    # Authentication:
    @@ -67,8 +67,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
    @@ -93,7 +94,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 5f1df7b..ec78a1f 100644
    --- a/sshd_config.0
    +++ b/sshd_config.0
    @@ -507,7 +507,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
    @@ -709,7 +709,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 3abac6c..d0d4d45 100644
    --- a/sshd_config.5
    +++ b/sshd_config.5
    @@ -856,7 +856,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.
    @@ -1183,7 +1183,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