-
-
Save kogent/8792974 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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