Forked from leonklingele/0001-apple-keychain-integration-other-changes.patch
Created
December 9, 2020 18:01
-
-
Save rdp/790f9e3c4c4d8217a144c94e0ca9d74a to your computer and use it in GitHub Desktop.
openssh-{7.7,7.8,7.9,8.0,8.1,8.2,8.3,8.4}p1 patches for storing SSH passphrases in the OS X keychain. https://github.com/leonklingele/homebrew-dupes
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
From 1f00753130f44c0e29c34d95957f6eac2b2dcf45 Mon Sep 17 00:00:00 2001 | |
From: Leon Klingele <[email protected]> | |
Date: Tue, 29 Sep 2020 18:21:13 +0200 | |
Subject: [PATCH 1/2] apple-keychain-integration-other-changes | |
--- | |
Makefile.in | 16 +- | |
audit-bsm.c | 7 +- | |
auth.c | 2 +- | |
authfd.c | 25 ++ | |
authfd.h | 6 + | |
configure.ac | 30 +++ | |
groupaccess.c | 50 +++- | |
groupaccess.h | 2 +- | |
keychain.c | 695 ++++++++++++++++++++++++++++++++++++++++++++++++++ | |
keychain.h | 45 ++++ | |
readconf.c | 19 ++ | |
readconf.h | 4 + | |
servconf.c | 2 +- | |
session.c | 4 + | |
ssh-add.1 | 12 +- | |
ssh-add.c | 62 ++++- | |
ssh-agent.c | 139 ++++++++++ | |
ssh-keysign.8 | 3 + | |
sshconnect2.c | 5 + | |
sshd.c | 11 +- | |
20 files changed, 1110 insertions(+), 29 deletions(-) | |
create mode 100644 keychain.c | |
create mode 100644 keychain.h | |
diff --git a/Makefile.in b/Makefile.in | |
index acfb919..3277d5d 100644 | |
--- a/Makefile.in | |
+++ b/Makefile.in | |
@@ -60,6 +60,7 @@ SED=@SED@ | |
XAUTH_PATH=@XAUTH_PATH@ | |
LDFLAGS=-L. -Lopenbsd-compat/ @LDFLAGS@ | |
LDFLAGS_NOPIE=-L. -Lopenbsd-compat/ @LDFLAGS_NOPIE@ | |
+KEYCHAIN_LDFLAGS=@KEYCHAIN_LDFLAGS@ | |
EXEEXT=@EXEEXT@ | |
MANFMT=@MANFMT@ | |
MKDIR_P=@MKDIR_P@ | |
@@ -150,6 +151,8 @@ SFTPSERVER_OBJS=sftp-common.o sftp-server.o sftp-server-main.o | |
SFTP_OBJS= sftp.o sftp-client.o sftp-common.o sftp-glob.o progressmeter.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 ssh-sk-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 ssh-sk-helper.8 sshd_config.5 ssh_config.5 | |
MANTYPE = @MANTYPE@ | |
@@ -185,6 +188,7 @@ all: configure-check $(CONFIGFILES) $(MANPAGES) $(TARGETS) | |
$(LIBSSH_OBJS): Makefile.in config.h | |
$(SSHOBJS): Makefile.in config.h | |
$(SSHDOBJS): Makefile.in config.h | |
+$(KEYCHAINOBJS): Makefile.in config.h | |
configure-check: $(srcdir)/configure | |
$(srcdir)/configure: configure.ac $(srcdir)/m4/*.m4 | |
@@ -203,8 +207,8 @@ libssh.a: $(LIBSSH_OBJS) | |
$(AR) rv $@ $(LIBSSH_OBJS) | |
$(RANLIB) $@ | |
-ssh$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHOBJS) | |
- $(LD) -o $@ $(SSHOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(GSSLIBS) | |
+ssh$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHOBJS) $(KEYCHAINOBJS) | |
+ $(LD) -o $@ $(SSHOBJS) $(KEYCHAINOBJS) $(LDFLAGS) $(KEYCHAIN_LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(GSSLIBS) | |
sshd$(EXEEXT): libssh.a $(LIBCOMPAT) $(SSHDOBJS) | |
$(LD) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHDLIBS) $(LIBS) $(GSSLIBS) $(K5LIBS) | |
@@ -212,11 +216,11 @@ sshd$(EXEEXT): libssh.a $(LIBCOMPAT) $(SSHDOBJS) | |
scp$(EXEEXT): $(LIBCOMPAT) libssh.a $(SCP_OBJS) | |
$(LD) -o $@ $(SCP_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
-ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHADD_OBJS) | |
- $(LD) -o $@ $(SSHADD_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
+ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHADD_OBJS) $(KEYCHAINOBJS) | |
+ $(LD) -o $@ $(SSHADD_OBJS) $(KEYCHAINOBJS) $(LDFLAGS) $(KEYCHAIN_LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
-ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHAGENT_OBJS) | |
- $(LD) -o $@ $(SSHAGENT_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
+ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHAGENT_OBJS) $(KEYCHAINOBJS) | |
+ $(LD) -o $@ $(SSHAGENT_OBJS) $(KEYCHAINOBJS) $(LDFLAGS) $(KEYCHAIN_LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
ssh-keygen$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHKEYGEN_OBJS) | |
$(LD) -o $@ $(SSHKEYGEN_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) | |
diff --git a/audit-bsm.c b/audit-bsm.c | |
index 0ba16c7..9953625 100644 | |
--- a/audit-bsm.c | |
+++ b/audit-bsm.c | |
@@ -260,7 +260,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.c b/auth.c | |
index 9a5498b..efa737c 100644 | |
--- a/auth.c | |
+++ b/auth.c | |
@@ -226,7 +226,7 @@ allowed_user(struct ssh *ssh, 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 8288ef2..238fa8c 100644 | |
--- a/authfd.c | |
+++ b/authfd.c | |
@@ -177,6 +177,31 @@ ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply) | |
return 0; | |
} | |
+/* | |
+ * Adds identities using passphrases stored in the keychain. This call is not | |
+ * meant to be used by normal applications. | |
+ */ | |
+int | |
+ssh_add_from_keychain(int sock) | |
+{ | |
+ int r; | |
+ struct sshbuf *msg; | |
+ int type; | |
+ | |
+ if ((msg = sshbuf_new()) == NULL) | |
+ return SSH_ERR_ALLOC_FAIL; | |
+ if ((r = sshbuf_put_u8(msg, SSH_AGENTC_ADD_FROM_KEYCHAIN)) != 0) | |
+ goto out; | |
+ if ((r = ssh_request_reply(sock, msg, msg)) != 0) | |
+ goto out; | |
+ if ((r = sshbuf_get_u8(msg, &type)) != 0) | |
+ goto out; | |
+ r = decode_reply(type); | |
+ out: | |
+ sshbuf_free(msg); | |
+ return r; | |
+} | |
+ | |
/* | |
* Closes the agent socket if it should be closed (depends on how it was | |
* obtained). The argument must have been returned by | |
diff --git a/authfd.h b/authfd.h | |
index 4fbf82f..bcc5c1e 100644 | |
--- a/authfd.h | |
+++ b/authfd.h | |
@@ -43,6 +43,9 @@ int ssh_agent_sign(int sock, const struct sshkey *key, | |
u_char **sigp, size_t *lenp, | |
const u_char *data, size_t datalen, const char *alg, u_int compat); | |
+int | |
+ssh_add_from_keychain(int sock); | |
+ | |
/* Messages for the authentication agent connection. */ | |
#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1 | |
#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2 | |
@@ -76,6 +79,9 @@ int ssh_agent_sign(int sock, const struct sshkey *key, | |
#define SSH2_AGENTC_ADD_ID_CONSTRAINED 25 | |
#define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26 | |
+/* keychain */ | |
+#define SSH_AGENTC_ADD_FROM_KEYCHAIN 27 | |
+ | |
#define SSH_AGENT_CONSTRAIN_LIFETIME 1 | |
#define SSH_AGENT_CONSTRAIN_CONFIRM 2 | |
#define SSH_AGENT_CONSTRAIN_MAXSIGN 3 | |
diff --git a/configure.ac b/configure.ac | |
index 7005a50..c3da33c 100644 | |
--- a/configure.ac | |
+++ b/configure.ac | |
@@ -5440,10 +5440,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 80d3019..de7f57d 100644 | |
--- a/groupaccess.c | |
+++ b/groupaccess.c | |
@@ -34,40 +34,68 @@ | |
#include <string.h> | |
#include <limits.h> | |
+#ifdef __APPLE_MEMBERSHIP__ | |
+#include <membership.h> | |
+#endif | |
+ | |
#include "xmalloc.h" | |
#include "groupaccess.h" | |
#include "match.h" | |
#include "log.h" | |
+#ifdef __APPLE_MEMBERSHIP__ | |
+// SPI for 5235093 | |
+int32_t getgrouplist_2(const char *, gid_t, gid_t **); | |
+int32_t getgroupcount(const char *, gid_t); | |
+#endif | |
+ | |
static int ngroups; | |
static char **groups_byname; | |
+#ifdef __APPLE_MEMBERSHIP__ | |
+uuid_t u_uuid; | |
+#endif | |
/* | |
* Initialize group access list for user with primary (base) and | |
* supplementary groups. Return the number of groups in the list. | |
*/ | |
int | |
-ga_init(const char *user, gid_t base) | |
+ga_init(struct passwd *pw) | |
{ | |
- gid_t *groups_bygid; | |
+ gid_t *groups_bygid = NULL; | |
int i, j, retry = 0; | |
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)); | |
- while (getgrouplist(user, base, groups_bygid, &ngroups) == -1) { | |
+#else | |
+ if (-1 == (ngroups = getgrouplist_2(pw->pw_name, pw->pw_gid, | |
+ &groups_bygid))) { | |
+ logit("getgrouplist_2 failed"); | |
+ return 0; | |
+ } | |
+#endif | |
+#ifndef __APPLE_MEMBERSHIP__ | |
+ while (getgrouplist(pw->pw_name, pw->pw_gid, groups_bygid, &ngroups) == -1) { | |
if (retry++ > 0) | |
fatal("getgrouplist: groups list too small"); | |
groups_bygid = xreallocarray(groups_bygid, ngroups, | |
sizeof(*groups_bygid)); | |
} | |
+#endif | |
groups_byname = xcalloc(ngroups, sizeof(*groups_byname)); | |
for (i = 0, j = 0; i < ngroups; i++) | |
@@ -80,16 +108,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..5968c93 | |
--- /dev/null | |
+++ b/keychain.c | |
@@ -0,0 +1,695 @@ | |
+/* | |
+ * 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 "sshkey.h" | |
+#include "authfd.h" | |
+#include "authfile.h" | |
+ | |
+#if defined(__APPLE_KEYCHAIN__) | |
+ | |
+#include <CoreFoundation/CoreFoundation.h> | |
+#include <Security/Security.h> | |
+ | |
+/* Our Security/SecPassword.h is not yet API, so I will define the constants that I am using here. */ | |
+int kSecPasswordGet = 1<<0; // Get password from keychain or user | |
+int kSecPasswordSet = 1<<1; // Set password (passed in if kSecPasswordGet not set, otherwise from user) | |
+int kSecPasswordFail = 1<<2; // Wrong password (ignore item in keychain and flag error) | |
+OSStatus SecGenericPasswordCreate(SecKeychainAttributeList *searchAttrList, SecKeychainAttributeList *itemAttrList, SecPasswordRef *itemRef); | |
+OSStatus SecPasswordAction(SecPasswordRef itemRef, CFTypeRef message, UInt32 flags, UInt32 *length, const void **data); | |
+OSStatus SecPasswordSetInitialAccess(SecPasswordRef itemRef, SecAccessRef accessRef); | |
+ | |
+#endif | |
+ | |
+/* | |
+ * Platform-specific helper functions. | |
+ */ | |
+ | |
+#if defined(__APPLE_KEYCHAIN__) | |
+ | |
+static int get_boolean_preference(const char *key, int default_value, | |
+ int foreground) | |
+{ | |
+ int value = default_value; | |
+ CFStringRef keyRef = NULL; | |
+ CFPropertyListRef valueRef = NULL; | |
+ | |
+ keyRef = CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8); | |
+ if (keyRef != NULL) | |
+ valueRef = CFPreferencesCopyAppValue(keyRef, | |
+ CFSTR("org.openbsd.openssh")); | |
+ if (valueRef != NULL) | |
+ if (CFGetTypeID(valueRef) == CFBooleanGetTypeID()) | |
+ value = CFBooleanGetValue(valueRef); | |
+ else if (foreground) | |
+ fprintf(stderr, "Ignoring nonboolean %s preference.\n", key); | |
+ | |
+ if (keyRef) | |
+ CFRelease(keyRef); | |
+ if (valueRef) | |
+ CFRelease(valueRef); | |
+ | |
+ return value; | |
+} | |
+ | |
+#endif | |
+ | |
+/* | |
+ * Store the passphrase for a given identity in the keychain. | |
+ */ | |
+void | |
+store_in_keychain(const char *filename, const char *passphrase) | |
+{ | |
+ | |
+#if defined(__APPLE_KEYCHAIN__) | |
+ | |
+ /* | |
+ * store_in_keychain | |
+ * Mac OS X implementation | |
+ */ | |
+ | |
+ CFStringRef cfstr_relative_filename = NULL; | |
+ CFURLRef cfurl_relative_filename = NULL, cfurl_filename = NULL; | |
+ CFStringRef cfstr_filename = NULL; | |
+ CFDataRef cfdata_filename = NULL; | |
+ CFIndex filename_len; | |
+ UInt8 *label = NULL; | |
+ UInt8 *utf8_filename; | |
+ OSStatus rv; | |
+ SecKeychainItemRef itemRef = NULL; | |
+ SecTrustedApplicationRef apps[] = {NULL, NULL, NULL}; | |
+ CFArrayRef trustedlist = NULL; | |
+ SecAccessRef initialAccess = NULL; | |
+ | |
+ /* Bail out if KeychainIntegration preference is -bool NO */ | |
+ if (get_boolean_preference("KeychainIntegration", 1, 1) == 0) { | |
+ fprintf(stderr, "Keychain integration is disabled.\n"); | |
+ goto err; | |
+ } | |
+ | |
+ /* Interpret filename with the correct encoding. */ | |
+ if ((cfstr_relative_filename = | |
+ CFStringCreateWithFileSystemRepresentation(NULL, filename)) == NULL) | |
+ { | |
+ fprintf(stderr, "CFStringCreateWithFileSystemRepresentation failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfurl_relative_filename = CFURLCreateWithFileSystemPath(NULL, | |
+ cfstr_relative_filename, kCFURLPOSIXPathStyle, false)) == NULL) { | |
+ fprintf(stderr, "CFURLCreateWithFileSystemPath failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfurl_filename = CFURLCopyAbsoluteURL(cfurl_relative_filename)) == | |
+ NULL) { | |
+ fprintf(stderr, "CFURLCopyAbsoluteURL failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfstr_filename = CFURLCopyFileSystemPath(cfurl_filename, | |
+ kCFURLPOSIXPathStyle)) == NULL) { | |
+ fprintf(stderr, "CFURLCopyFileSystemPath failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfdata_filename = CFStringCreateExternalRepresentation(NULL, | |
+ cfstr_filename, kCFStringEncodingUTF8, 0)) == NULL) { | |
+ fprintf(stderr, "CFStringCreateExternalRepresentation failed\n"); | |
+ goto err; | |
+ } | |
+ filename_len = CFDataGetLength(cfdata_filename); | |
+ if ((label = xmalloc(filename_len + 5)) == NULL) { | |
+ fprintf(stderr, "xmalloc failed\n"); | |
+ goto err; | |
+ } | |
+ memcpy(label, "SSH: ", 5); | |
+ utf8_filename = label + 5; | |
+ CFDataGetBytes(cfdata_filename, CFRangeMake(0, filename_len), | |
+ utf8_filename); | |
+ | |
+ /* Check if we already have this passphrase. */ | |
+ rv = SecKeychainFindGenericPassword(NULL, 3, "SSH", filename_len, | |
+ (char *)utf8_filename, NULL, NULL, &itemRef); | |
+ if (rv == errSecItemNotFound) { | |
+ /* Add a new keychain item. */ | |
+ SecKeychainAttribute attrs[] = { | |
+ {kSecLabelItemAttr, filename_len + 5, label}, | |
+ {kSecServiceItemAttr, 3, "SSH"}, | |
+ {kSecAccountItemAttr, filename_len, utf8_filename} | |
+ }; | |
+ SecKeychainAttributeList attrList = | |
+ {sizeof(attrs) / sizeof(attrs[0]), attrs}; | |
+ if (SecTrustedApplicationCreateFromPath("/usr/bin/ssh-agent", | |
+ &apps[0]) != noErr || | |
+ SecTrustedApplicationCreateFromPath("/usr/bin/ssh-add", | |
+ &apps[1]) != noErr || | |
+ SecTrustedApplicationCreateFromPath("/usr/bin/ssh", | |
+ &apps[2]) != noErr) { | |
+ fprintf(stderr, "SecTrustedApplicationCreateFromPath failed\n"); | |
+ goto err; | |
+ } | |
+ if ((trustedlist = CFArrayCreate(NULL, (const void **)apps, | |
+ sizeof(apps) / sizeof(apps[0]), &kCFTypeArrayCallBacks)) == | |
+ NULL) { | |
+ fprintf(stderr, "CFArrayCreate failed\n"); | |
+ goto err; | |
+ } | |
+ if (SecAccessCreate(cfstr_filename, trustedlist, | |
+ &initialAccess) != noErr) { | |
+ fprintf(stderr, "SecAccessCreate failed\n"); | |
+ goto err; | |
+ } | |
+ if (SecKeychainItemCreateFromContent( | |
+ kSecGenericPasswordItemClass, &attrList, strlen(passphrase), | |
+ passphrase, NULL, initialAccess, NULL) == noErr) | |
+ fprintf(stderr, "Passphrase stored in keychain: %s\n", filename); | |
+ else | |
+ fprintf(stderr, "Could not create keychain item\n"); | |
+ } else if (rv == noErr) { | |
+ /* Update an existing keychain item. */ | |
+ if (SecKeychainItemModifyAttributesAndData(itemRef, NULL, | |
+ strlen(passphrase), passphrase) == noErr) | |
+ fprintf(stderr, "Passphrase updated in keychain: %s\n", filename); | |
+ else | |
+ fprintf(stderr, "Could not modify keychain item\n"); | |
+ } else | |
+ fprintf(stderr, "Could not access keychain\n"); | |
+ | |
+err: /* Clean up. */ | |
+ if (cfstr_relative_filename) | |
+ CFRelease(cfstr_relative_filename); | |
+ if (cfurl_relative_filename) | |
+ CFRelease(cfurl_relative_filename); | |
+ if (cfurl_filename) | |
+ CFRelease(cfurl_filename); | |
+ if (cfstr_filename) | |
+ CFRelease(cfstr_filename); | |
+ if (cfdata_filename) | |
+ CFRelease(cfdata_filename); | |
+ if (label) | |
+ free(label); | |
+ if (itemRef) | |
+ CFRelease(itemRef); | |
+ if (apps[0]) | |
+ CFRelease(apps[0]); | |
+ if (apps[1]) | |
+ CFRelease(apps[1]); | |
+ if (apps[2]) | |
+ CFRelease(apps[2]); | |
+ if (trustedlist) | |
+ CFRelease(trustedlist); | |
+ if (initialAccess) | |
+ CFRelease(initialAccess); | |
+ | |
+#else | |
+ | |
+ /* | |
+ * store_in_keychain | |
+ * no keychain implementation | |
+ */ | |
+ | |
+ fprintf(stderr, "Keychain is not available on this system\n"); | |
+ | |
+#endif | |
+ | |
+} | |
+ | |
+/* | |
+ * Remove the passphrase for a given identity from the keychain. | |
+ */ | |
+void | |
+remove_from_keychain(const char *filename) | |
+{ | |
+ | |
+#if defined(__APPLE_KEYCHAIN__) | |
+ | |
+ /* | |
+ * remove_from_keychain | |
+ * Mac OS X implementation | |
+ */ | |
+ | |
+ CFStringRef cfstr_relative_filename = NULL; | |
+ CFURLRef cfurl_relative_filename = NULL, cfurl_filename = NULL; | |
+ CFStringRef cfstr_filename = NULL; | |
+ CFDataRef cfdata_filename = NULL; | |
+ CFIndex filename_len; | |
+ const UInt8 *utf8_filename; | |
+ OSStatus rv; | |
+ SecKeychainItemRef itemRef = NULL; | |
+ | |
+ /* Bail out if KeychainIntegration preference is -bool NO */ | |
+ if (get_boolean_preference("KeychainIntegration", 1, 1) == 0) { | |
+ fprintf(stderr, "Keychain integration is disabled.\n"); | |
+ goto err; | |
+ } | |
+ | |
+ /* Interpret filename with the correct encoding. */ | |
+ if ((cfstr_relative_filename = | |
+ CFStringCreateWithFileSystemRepresentation(NULL, filename)) == NULL) | |
+ { | |
+ fprintf(stderr, "CFStringCreateWithFileSystemRepresentation failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfurl_relative_filename = CFURLCreateWithFileSystemPath(NULL, | |
+ cfstr_relative_filename, kCFURLPOSIXPathStyle, false)) == NULL) { | |
+ fprintf(stderr, "CFURLCreateWithFileSystemPath failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfurl_filename = CFURLCopyAbsoluteURL(cfurl_relative_filename)) == | |
+ NULL) { | |
+ fprintf(stderr, "CFURLCopyAbsoluteURL failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfstr_filename = CFURLCopyFileSystemPath(cfurl_filename, | |
+ kCFURLPOSIXPathStyle)) == NULL) { | |
+ fprintf(stderr, "CFURLCopyFileSystemPath failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfdata_filename = CFStringCreateExternalRepresentation(NULL, | |
+ cfstr_filename, kCFStringEncodingUTF8, 0)) == NULL) { | |
+ fprintf(stderr, "CFStringCreateExternalRepresentation failed\n"); | |
+ goto err; | |
+ } | |
+ filename_len = CFDataGetLength(cfdata_filename); | |
+ utf8_filename = CFDataGetBytePtr(cfdata_filename); | |
+ | |
+ /* Check if we already have this passphrase. */ | |
+ rv = SecKeychainFindGenericPassword(NULL, 3, "SSH", filename_len, | |
+ (const char *)utf8_filename, NULL, NULL, &itemRef); | |
+ if (rv == noErr) { | |
+ /* Remove the passphrase from the keychain. */ | |
+ if (SecKeychainItemDelete(itemRef) == noErr) | |
+ fprintf(stderr, "Passphrase removed from keychain: %s\n", filename); | |
+ else | |
+ fprintf(stderr, "Could not remove keychain item\n"); | |
+ } else if (rv != errSecItemNotFound) | |
+ fprintf(stderr, "Could not access keychain\n"); | |
+ | |
+err: /* Clean up. */ | |
+ if (cfstr_relative_filename) | |
+ CFRelease(cfstr_relative_filename); | |
+ if (cfurl_relative_filename) | |
+ CFRelease(cfurl_relative_filename); | |
+ if (cfurl_filename) | |
+ CFRelease(cfurl_filename); | |
+ if (cfstr_filename) | |
+ CFRelease(cfstr_filename); | |
+ if (cfdata_filename) | |
+ CFRelease(cfdata_filename); | |
+ if (itemRef) | |
+ CFRelease(itemRef); | |
+ | |
+#else | |
+ | |
+ /* | |
+ * remove_from_keychain | |
+ * no keychain implementation | |
+ */ | |
+ | |
+ fprintf(stderr, "Keychain is not available on this system\n"); | |
+ | |
+#endif | |
+ | |
+} | |
+ | |
+/* | |
+ * Add identities to ssh-agent using passphrases stored in the keychain. | |
+ * Returns zero on success and nonzero on failure. | |
+ * add_identity is a callback into ssh-agent. It takes a filename and a | |
+ * passphrase, and attempts to add the identity to the agent. It returns | |
+ * zero on success and nonzero on failure. | |
+ */ | |
+int | |
+add_identities_using_keychain(int (*add_identity)(const char *, const char *)) | |
+{ | |
+ | |
+#if defined(__APPLE_KEYCHAIN__) | |
+ | |
+ /* | |
+ * add_identities_using_keychain | |
+ * Mac OS X implementation | |
+ */ | |
+ | |
+ OSStatus rv; | |
+ SecKeychainSearchRef searchRef; | |
+ SecKeychainItemRef itemRef; | |
+ UInt32 length; | |
+ void *data; | |
+ CFIndex maxsize; | |
+ | |
+ /* Bail out if KeychainIntegration preference is -bool NO */ | |
+ if (get_boolean_preference("KeychainIntegration", 1, 0) == 0) | |
+ return 0; | |
+ | |
+ /* Search for SSH passphrases in the keychain */ | |
+ SecKeychainAttribute attrs[] = { | |
+ {kSecServiceItemAttr, 3, "SSH"} | |
+ }; | |
+ SecKeychainAttributeList attrList = | |
+ {sizeof(attrs) / sizeof(attrs[0]), attrs}; | |
+ if ((rv = SecKeychainSearchCreateFromAttributes(NULL, | |
+ kSecGenericPasswordItemClass, &attrList, &searchRef)) != noErr) | |
+ return 0; | |
+ | |
+ /* Iterate through the search results. */ | |
+ while ((rv = SecKeychainSearchCopyNext(searchRef, &itemRef)) == noErr) { | |
+ UInt32 tag = kSecAccountItemAttr; | |
+ UInt32 format = kSecFormatUnknown; | |
+ SecKeychainAttributeInfo info = {1, &tag, &format}; | |
+ SecKeychainAttributeList *itemAttrList = NULL; | |
+ CFStringRef cfstr_filename = NULL; | |
+ char *filename = NULL; | |
+ char *passphrase = NULL; | |
+ | |
+ /* Retrieve filename and passphrase. */ | |
+ if ((rv = SecKeychainItemCopyAttributesAndData(itemRef, &info, | |
+ NULL, &itemAttrList, &length, &data)) != noErr) | |
+ goto err; | |
+ if (itemAttrList->count != 1) | |
+ goto err; | |
+ cfstr_filename = CFStringCreateWithBytes(NULL, | |
+ itemAttrList->attr->data, itemAttrList->attr->length, | |
+ kCFStringEncodingUTF8, true); | |
+ maxsize = CFStringGetMaximumSizeOfFileSystemRepresentation( | |
+ cfstr_filename); | |
+ if ((filename = xmalloc(maxsize)) == NULL) | |
+ goto err; | |
+ if (CFStringGetFileSystemRepresentation(cfstr_filename, | |
+ filename, maxsize) == false) | |
+ goto err; | |
+ if ((passphrase = xmalloc(length + 1)) == NULL) | |
+ goto err; | |
+ memcpy(passphrase, data, length); | |
+ passphrase[length] = '\0'; | |
+ | |
+ /* Add the identity. */ | |
+ add_identity(filename, passphrase); | |
+ | |
+err: /* Clean up. */ | |
+ if (itemRef) | |
+ CFRelease(itemRef); | |
+ if (cfstr_filename) | |
+ CFRelease(cfstr_filename); | |
+ if (filename) | |
+ free(filename); | |
+ if (passphrase) | |
+ free(passphrase); | |
+ if (itemAttrList) | |
+ SecKeychainItemFreeAttributesAndData(itemAttrList, | |
+ data); | |
+ } | |
+ | |
+ CFRelease(searchRef); | |
+ | |
+ return 0; | |
+ | |
+#else | |
+ | |
+ /* | |
+ * add_identities_using_keychain | |
+ * no implementation | |
+ */ | |
+ | |
+ return 1; | |
+ | |
+#endif | |
+ | |
+} | |
+ | |
+/* | |
+ * Prompt the user for a key's passphrase. The user will be offered the option | |
+ * of storing the passphrase in their keychain. Returns the passphrase | |
+ * (which the caller is responsible for freeing), or NULL if this function | |
+ * fails or is not implemented. If this function is not implemented, ssh will | |
+ * fall back on the standard read_passphrase function, and the user will need | |
+ * to use ssh-add -K to add their keys to the keychain. | |
+ */ | |
+char * | |
+keychain_read_passphrase(const char *filename, int oAskPassGUI) | |
+{ | |
+ | |
+#if defined(__APPLE_KEYCHAIN__) | |
+ | |
+ /* | |
+ * keychain_read_passphrase | |
+ * Mac OS X implementation | |
+ */ | |
+ | |
+ CFStringRef cfstr_relative_filename = NULL; | |
+ CFURLRef cfurl_relative_filename = NULL, cfurl_filename = NULL; | |
+ CFStringRef cfstr_filename = NULL; | |
+ CFDataRef cfdata_filename = NULL; | |
+ CFIndex filename_len; | |
+ UInt8 *label = NULL; | |
+ UInt8 *utf8_filename; | |
+ SecPasswordRef passRef = NULL; | |
+ SecTrustedApplicationRef apps[] = {NULL, NULL, NULL}; | |
+ CFArrayRef trustedlist = NULL; | |
+ SecAccessRef initialAccess = NULL; | |
+ CFURLRef path = NULL; | |
+ CFStringRef pathFinal = NULL; | |
+ CFURLRef bundle_url = NULL; | |
+ CFBundleRef bundle = NULL; | |
+ CFStringRef promptTemplate = NULL, prompt = NULL; | |
+ UInt32 length; | |
+ const void *data; | |
+ int sock = -1; | |
+ char *result = NULL; | |
+ | |
+ /* Bail out if KeychainIntegration preference is -bool NO */ | |
+ if (get_boolean_preference("KeychainIntegration", 1, 1) == 0) | |
+ goto err; | |
+ | |
+ /* Bail out if the user set AskPassGUI preference to -bool NO */ | |
+ if (get_boolean_preference("AskPassGUI", 1, 1) == 0 || oAskPassGUI == 0) | |
+ goto err; | |
+ | |
+ /* Bail out if we can't communicate with ssh-agent */ | |
+ if ((ssh_get_authentication_socket(&sock)) != 0) | |
+ goto err; | |
+ | |
+ /* Interpret filename with the correct encoding. */ | |
+ if ((cfstr_relative_filename = | |
+ CFStringCreateWithFileSystemRepresentation(NULL, filename)) == NULL) | |
+ { | |
+ fprintf(stderr, "CFStringCreateWithFileSystemRepresentation failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfurl_relative_filename = CFURLCreateWithFileSystemPath(NULL, | |
+ cfstr_relative_filename, kCFURLPOSIXPathStyle, false)) == NULL) { | |
+ fprintf(stderr, "CFURLCreateWithFileSystemPath failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfurl_filename = CFURLCopyAbsoluteURL(cfurl_relative_filename)) == | |
+ NULL) { | |
+ fprintf(stderr, "CFURLCopyAbsoluteURL failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfstr_filename = CFURLCopyFileSystemPath(cfurl_filename, | |
+ kCFURLPOSIXPathStyle)) == NULL) { | |
+ fprintf(stderr, "CFURLCopyFileSystemPath failed\n"); | |
+ goto err; | |
+ } | |
+ if ((cfdata_filename = CFStringCreateExternalRepresentation(NULL, | |
+ cfstr_filename, kCFStringEncodingUTF8, 0)) == NULL) { | |
+ fprintf(stderr, "CFStringCreateExternalRepresentation failed\n"); | |
+ goto err; | |
+ } | |
+ filename_len = CFDataGetLength(cfdata_filename); | |
+ if ((label = xmalloc(filename_len + 5)) == NULL) { | |
+ fprintf(stderr, "xmalloc failed\n"); | |
+ goto err; | |
+ } | |
+ memcpy(label, "SSH: ", 5); | |
+ utf8_filename = label + 5; | |
+ CFDataGetBytes(cfdata_filename, CFRangeMake(0, filename_len), | |
+ utf8_filename); | |
+ | |
+ /* Build a SecPasswordRef. */ | |
+ SecKeychainAttribute searchAttrs[] = { | |
+ {kSecServiceItemAttr, 3, "SSH"}, | |
+ {kSecAccountItemAttr, filename_len, utf8_filename} | |
+ }; | |
+ SecKeychainAttributeList searchAttrList = | |
+ {sizeof(searchAttrs) / sizeof(searchAttrs[0]), searchAttrs}; | |
+ SecKeychainAttribute attrs[] = { | |
+ {kSecLabelItemAttr, filename_len + 5, label}, | |
+ {kSecServiceItemAttr, 3, "SSH"}, | |
+ {kSecAccountItemAttr, filename_len, utf8_filename} | |
+ }; | |
+ SecKeychainAttributeList attrList = | |
+ {sizeof(attrs) / sizeof(attrs[0]), attrs}; | |
+ if (SecGenericPasswordCreate(&searchAttrList, &attrList, &passRef) != | |
+ noErr) { | |
+ fprintf(stderr, "SecGenericPasswordCreate failed\n"); | |
+ goto err; | |
+ } | |
+ if (SecTrustedApplicationCreateFromPath("/usr/bin/ssh-agent", &apps[0]) | |
+ != noErr || | |
+ SecTrustedApplicationCreateFromPath("/usr/bin/ssh-add", &apps[1]) | |
+ != noErr || | |
+ SecTrustedApplicationCreateFromPath("/usr/bin/ssh", &apps[2]) | |
+ != noErr) { | |
+ fprintf(stderr, "SecTrustedApplicationCreateFromPath failed\n"); | |
+ goto err; | |
+ } | |
+ if ((trustedlist = CFArrayCreate(NULL, (const void **)apps, | |
+ sizeof(apps) / sizeof(apps[0]), &kCFTypeArrayCallBacks)) == NULL) { | |
+ fprintf(stderr, "CFArrayCreate failed\n"); | |
+ goto err; | |
+ } | |
+ if (SecAccessCreate(cfstr_filename, trustedlist, &initialAccess) | |
+ != noErr) { | |
+ fprintf(stderr, "SecAccessCreate failed\n"); | |
+ goto err; | |
+ } | |
+ if (SecPasswordSetInitialAccess(passRef, initialAccess) != noErr) { | |
+ fprintf(stderr, "SecPasswordSetInitialAccess failed\n"); | |
+ goto err; | |
+ } | |
+ | |
+ /* Request the passphrase from the user. */ | |
+ if ((path = CFURLCreateFromFileSystemRepresentation(NULL, | |
+ (UInt8 *)filename, strlen(filename), false)) == NULL) { | |
+ fprintf(stderr, "CFURLCreateFromFileSystemRepresentation failed\n"); | |
+ goto err; | |
+ } | |
+ if ((pathFinal = CFURLCopyLastPathComponent(path)) == NULL) { | |
+ fprintf(stderr, "CFURLCopyLastPathComponent failed\n"); | |
+ goto err; | |
+ } | |
+ if (!((bundle_url = CFURLCreateWithFileSystemPath(NULL, | |
+ CFSTR("/System/Library/CoreServices/"), kCFURLPOSIXPathStyle, true)) | |
+ != NULL && (bundle = CFBundleCreate(NULL, bundle_url)) != NULL && | |
+ (promptTemplate = CFCopyLocalizedStringFromTableInBundle( | |
+ CFSTR("Enter your password for the SSH key \"%@\"."), | |
+ CFSTR("OpenSSH"), bundle, "Text of the dialog asking the user for" | |
+ "their passphrase. The %@ will be replaced with the filename of a" | |
+ "specific key.")) != NULL) && | |
+ (promptTemplate = CFStringCreateCopy(NULL, | |
+ CFSTR("Enter your password for the SSH key \"%@\"."))) == NULL) { | |
+ fprintf(stderr, "CFStringCreateCopy failed\n"); | |
+ goto err; | |
+ } | |
+ if ((prompt = CFStringCreateWithFormat(NULL, NULL, promptTemplate, | |
+ pathFinal)) == NULL) { | |
+ fprintf(stderr, "CFStringCreateWithFormat failed\n"); | |
+ goto err; | |
+ } | |
+ switch (SecPasswordAction(passRef, prompt, | |
+ kSecPasswordGet|kSecPasswordFail, &length, &data)) { | |
+ case noErr: | |
+ result = xmalloc(length + 1); | |
+ memcpy(result, data, length); | |
+ result[length] = '\0'; | |
+ | |
+ /* Save password in keychain if requested. */ | |
+ if (noErr != SecPasswordAction(passRef, CFSTR(""), kSecPasswordSet, &length, &data)) | |
+ fprintf(stderr, "Saving password to keychain failed\n"); | |
+ | |
+ /* Add password to agent. */ | |
+ char *comment = NULL; | |
+ struct sshkey *private; | |
+ | |
+ if (sshkey_load_private(filename, result, &private, &comment) != 0) | |
+ break; | |
+ if (ssh_add_identity_constrained(sock, private, comment, 0, 0, 0, NULL)) | |
+ fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); | |
+ else | |
+ fprintf(stderr, "Could not add identity: %s\n", filename); | |
+ free(comment); | |
+ sshkey_free(private); | |
+ break; | |
+ case errAuthorizationCanceled: | |
+ result = xmalloc(1); | |
+ *result = '\0'; | |
+ break; | |
+ default: | |
+ goto err; | |
+ } | |
+ | |
+err: /* Clean up. */ | |
+ if (cfstr_relative_filename) | |
+ CFRelease(cfstr_relative_filename); | |
+ if (cfurl_relative_filename) | |
+ CFRelease(cfurl_relative_filename); | |
+ if (cfurl_filename) | |
+ CFRelease(cfurl_filename); | |
+ if (cfstr_filename) | |
+ CFRelease(cfstr_filename); | |
+ if (cfdata_filename) | |
+ CFRelease(cfdata_filename); | |
+ if (label) | |
+ free(label); | |
+ if (passRef) | |
+ CFRelease(passRef); | |
+ if (apps[0]) | |
+ CFRelease(apps[0]); | |
+ if (apps[1]) | |
+ CFRelease(apps[1]); | |
+ if (apps[2]) | |
+ CFRelease(apps[2]); | |
+ if (trustedlist) | |
+ CFRelease(trustedlist); | |
+ if (initialAccess) | |
+ CFRelease(initialAccess); | |
+ if (path) | |
+ CFRelease(path); | |
+ if (pathFinal) | |
+ CFRelease(pathFinal); | |
+ if (bundle_url) | |
+ CFRelease(bundle_url); | |
+ if (bundle) | |
+ CFRelease(bundle); | |
+ if (promptTemplate) | |
+ CFRelease(promptTemplate); | |
+ if (prompt) | |
+ CFRelease(prompt); | |
+ if (sock != -1) | |
+ ssh_close_authentication_socket(sock); | |
+ | |
+ return result; | |
+ | |
+#else | |
+ | |
+ /* | |
+ * keychain_read_passphrase | |
+ * no implementation | |
+ */ | |
+ | |
+ return NULL; | |
+ | |
+#endif | |
+ | |
+} | |
diff --git a/keychain.h b/keychain.h | |
new file mode 100644 | |
index 0000000..9bb8c22 | |
--- /dev/null | |
+++ b/keychain.h | |
@@ -0,0 +1,45 @@ | |
+/* | |
+ * Copyright (c) 2007 Apple Inc. All rights reserved. | |
+ * | |
+ * @APPLE_BSD_LICENSE_HEADER_START@ | |
+ * | |
+ * Redistribution and use in source and binary forms, with or without | |
+ * modification, are permitted provided that the following conditions | |
+ * are met: | |
+ * | |
+ * 1. Redistributions of source code must retain the above copyright | |
+ * notice, this list of conditions and the following disclaimer. | |
+ * 2. Redistributions in binary form must reproduce the above copyright | |
+ * notice, this list of conditions and the following disclaimer in the | |
+ * documentation and/or other materials provided with the distribution. | |
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of its | |
+ * contributors may be used to endorse or promote products derived from | |
+ * this software without specific prior written permission. | |
+ * | |
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
+ * | |
+ * @APPLE_BSD_LICENSE_HEADER_END@ | |
+ */ | |
+ | |
+/* | |
+ * KEYCHAIN indicates that keychain functionality is present. | |
+ * KEYCHAIN_* indicates the implementation to use, and implies KEYCHAIN. | |
+ */ | |
+#if defined(__APPLE_KEYCHAIN__) | |
+#define KEYCHAIN | |
+#endif | |
+ | |
+void store_in_keychain(const char *filename, const char *passphrase); | |
+void remove_from_keychain(const char *filename); | |
+int add_identities_using_keychain( | |
+ int (*add_identity)(const char *, const char *)); | |
+char *keychain_read_passphrase(const char *filename, int oAskPassGUI); | |
diff --git a/readconf.c b/readconf.c | |
index 554efd7..29093f3 100644 | |
--- a/readconf.c | |
+++ b/readconf.c | |
@@ -169,6 +169,9 @@ typedef enum { | |
oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, | |
oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, | |
oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ oAskPassGUI, | |
+#endif | |
oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys, | |
oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes, | |
oPubkeyAcceptedKeyTypes, oCASignatureAlgorithms, oProxyJump, | |
@@ -310,6 +313,9 @@ static struct { | |
{ "ignoreunknown", oIgnoreUnknown }, | |
{ "proxyjump", oProxyJump }, | |
{ "securitykeyprovider", oSecurityKeyProvider }, | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ { "askpassgui", oAskPassGUI }, | |
+#endif | |
{ NULL, oBadOption } | |
}; | |
@@ -1698,6 +1704,12 @@ parse_keytypes: | |
charptr = &options->ignored_unknown; | |
goto parse_string; | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ case oAskPassGUI: | |
+ intptr = &options->ask_pass_gui; | |
+ goto parse_flag; | |
+#endif | |
+ | |
case oProxyUseFdpass: | |
intptr = &options->proxy_use_fdpass; | |
goto parse_flag; | |
@@ -2052,6 +2064,9 @@ initialize_options(Options * options) | |
options->request_tty = -1; | |
options->proxy_use_fdpass = -1; | |
options->ignored_unknown = NULL; | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ options->ask_pass_gui = -1; | |
+#endif | |
options->num_canonical_domains = 0; | |
options->num_permitted_cnames = 0; | |
options->canonicalize_max_dots = -1; | |
@@ -2230,6 +2245,10 @@ fill_default_options(Options * options) | |
options->ip_qos_bulk = IPTOS_DSCP_CS1; | |
if (options->request_tty == -1) | |
options->request_tty = REQUEST_TTY_AUTO; | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ if (options->ask_pass_gui == -1) | |
+ options->ask_pass_gui = 1; | |
+#endif | |
if (options->proxy_use_fdpass == -1) | |
options->proxy_use_fdpass = 0; | |
if (options->canonicalize_max_dots == -1) | |
diff --git a/readconf.h b/readconf.h | |
index d6a1555..8e61ef3 100644 | |
--- a/readconf.h | |
+++ b/readconf.h | |
@@ -169,6 +169,10 @@ typedef struct { | |
char *jump_extra; | |
char *ignored_unknown; /* Pattern list of unknown tokens to ignore */ | |
+ | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ int ask_pass_gui; | |
+#endif | |
} Options; | |
#define SSH_CANONICALISE_NO 0 | |
diff --git a/servconf.c b/servconf.c | |
index f08e374..91573ba 100644 | |
--- a/servconf.c | |
+++ b/servconf.c | |
@@ -1012,7 +1012,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 27ca8a1..e342b72 100644 | |
--- a/session.c | |
+++ b/session.c | |
@@ -1952,8 +1952,10 @@ session_pty_req(struct ssh *ssh, Session *s) | |
if ((r = sshpkt_get_end(ssh)) != 0) | |
sshpkt_fatal(ssh, r, "%s: parse packet", __func__); | |
+#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); | |
@@ -2288,9 +2290,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.1 b/ssh-add.1 | |
index 2786df5..fc0a4d6 100644 | |
--- a/ssh-add.1 | |
+++ b/ssh-add.1 | |
@@ -43,7 +43,7 @@ | |
.Nd adds private key identities to the OpenSSH authentication agent | |
.Sh SYNOPSIS | |
.Nm ssh-add | |
-.Op Fl cDdKkLlqvXx | |
+.Op Fl cDdkKALlqvXx | |
.Op Fl E Ar fingerprint_hash | |
.Op Fl S Ar provider | |
.Op Fl t Ar life | |
@@ -129,7 +129,7 @@ The default is | |
.It Fl e Ar pkcs11 | |
Remove keys provided by the PKCS#11 shared library | |
.Ar pkcs11 . | |
-.It Fl K | |
+.It Fl F | |
Load resident keys from a FIDO authenticator. | |
.It Fl k | |
When loading keys into or deleting keys from the agent, process plain private | |
@@ -139,6 +139,14 @@ 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 K | |
+When adding identities, each passphrase will also be stored in the user's keychain. When removing identities | |
+with | |
+.Fl d , | |
+each passphrase will be removed from it. | |
+.It Fl A | |
+Add identities to the agent using any passphrase stored in the user's keychain. | |
+X keychain. | |
.It Fl q | |
Be quiet after a successful operation. | |
.It Fl S Ar provider | |
diff --git a/ssh-add.c b/ssh-add.c | |
index 936dc21..d1ace26 100644 | |
--- a/ssh-add.c | |
+++ b/ssh-add.c | |
@@ -68,6 +68,7 @@ | |
#include "digest.h" | |
#include "ssh-sk.h" | |
#include "sk-api.h" | |
+#include "keychain.h" | |
/* argv0 */ | |
extern char *__progname; | |
@@ -160,7 +161,17 @@ delete_stdin(int agent_fd, int qflag) | |
} | |
static int | |
-delete_file(int agent_fd, const char *filename, int key_only, int qflag) | |
+add_from_keychain(int sock) | |
+{ | |
+ if (ssh_add_from_keychain(sock) == 0) | |
+ return -1; | |
+ | |
+ fprintf(stderr, "Added keychain identities.\n"); | |
+ return 0; | |
+} | |
+ | |
+static int | |
+delete_file(int agent_fd, int keychain, const char *filename, int key_only, int qflag) | |
{ | |
struct sshkey *public, *cert = NULL; | |
char *certpath = NULL, *comment = NULL; | |
@@ -169,6 +180,9 @@ delete_file(int agent_fd, const char *filename, int key_only, int qflag) | |
if (strcmp(filename, "-") == 0) | |
return delete_stdin(agent_fd, qflag); | |
+ if (keychain) | |
+ remove_from_keychain(filename); | |
+ | |
if ((r = sshkey_load_public(filename, &public, &comment)) != 0) { | |
printf("Bad key file %s: %s\n", filename, ssh_err(r)); | |
return -1; | |
@@ -231,7 +245,7 @@ delete_all(int agent_fd, int qflag) | |
} | |
static int | |
-add_file(int agent_fd, const char *filename, int key_only, int qflag, | |
+add_file(int agent_fd, int keychain, const char *filename, int key_only, int qflag, | |
const char *skprovider) | |
{ | |
struct sshkey *private, *cert; | |
@@ -277,6 +291,10 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag, | |
filename, ssh_err(r)); | |
goto fail_load; | |
} | |
+ | |
+ if (keychain && private != NULL) | |
+ store_in_keychain(filename, ""); | |
+ | |
/* try last */ | |
if (private == NULL && pass != NULL) { | |
if ((r = sshkey_parse_private_fileblob(keyblob, pass, &private, | |
@@ -285,6 +303,8 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag, | |
filename, ssh_err(r)); | |
goto fail_load; | |
} | |
+ if (keychain && private != NULL) | |
+ store_in_keychain(filename, pass); | |
} | |
if (private == NULL) { | |
/* clear passphrase since it did not work */ | |
@@ -296,8 +316,13 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag, | |
if (strcmp(pass, "") == 0) | |
goto fail_load; | |
if ((r = sshkey_parse_private_fileblob(keyblob, pass, | |
- &private, &comment)) == 0) | |
+ &private, &comment)) == 0) { | |
+ if (private != NULL) { | |
+ if (keychain) | |
+ store_in_keychain(filename, pass); | |
+ } | |
break; | |
+ } | |
else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) { | |
fprintf(stderr, | |
"Error loading key \"%s\": %s\n", | |
@@ -631,14 +656,14 @@ load_resident_keys(int agent_fd, const char *skprovider, int qflag) | |
} | |
static int | |
-do_file(int agent_fd, int deleting, int key_only, char *file, int qflag, | |
+do_file(int agent_fd, int deleting, int keychain, int key_only, char *file, int qflag, | |
const char *skprovider) | |
{ | |
if (deleting) { | |
- if (delete_file(agent_fd, file, key_only, qflag) == -1) | |
+ if (delete_file(agent_fd, keychain, file, key_only, qflag) == -1) | |
return -1; | |
} else { | |
- if (add_file(agent_fd, file, key_only, qflag, skprovider) == -1) | |
+ if (add_file(agent_fd, keychain, file, key_only, qflag, skprovider) == -1) | |
return -1; | |
} | |
return 0; | |
@@ -657,6 +682,11 @@ usage(void) | |
" ssh-add -e pkcs11\n" | |
" ssh-add -T pubkey ...\n" | |
); | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ fprintf(stderr, " -A Add all identities stored in your keychain.\n"); | |
+ fprintf(stderr, " -K Store passphrases in your keychain.\n"); | |
+ fprintf(stderr, " With -d, remove passphrases from your keychain.\n"); | |
+#endif | |
} | |
int | |
@@ -668,6 +698,7 @@ main(int argc, char **argv) | |
char *pkcs11provider = NULL, *skprovider = NULL; | |
int r, i, ch, deleting = 0, ret = 0, key_only = 0, do_download = 0; | |
int xflag = 0, lflag = 0, Dflag = 0, qflag = 0, Tflag = 0; | |
+ int keychain = 0; | |
SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; | |
LogLevel log_level = SYSLOG_LEVEL_INFO; | |
@@ -696,7 +727,11 @@ main(int argc, char **argv) | |
skprovider = getenv("SSH_SK_PROVIDER"); | |
- while ((ch = getopt(argc, argv, "vkKlLcdDTxXE:e:M:m:qs:S:t:")) != -1) { | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ while ((ch = getopt(argc, argv, "vkKFAlLcdDTxXE:e:M:m:qs:S:t:")) != -1) { | |
+#else | |
+ while ((ch = getopt(argc, argv, "vkFlLcdDTxXE:e:M:m:qs:S:t:")) != -1) { | |
+#endif | |
switch (ch) { | |
case 'v': | |
if (log_level == SYSLOG_LEVEL_INFO) | |
@@ -712,7 +747,16 @@ main(int argc, char **argv) | |
case 'k': | |
key_only = 1; | |
break; | |
+#ifdef __APPLE_KEYCHAIN__ | |
case 'K': | |
+ keychain = 1; | |
+ break; | |
+ case 'A': | |
+ if (add_from_keychain(agent_fd) == -1) | |
+ ret = 1; | |
+ goto done; | |
+#endif | |
+ case 'F': | |
do_download = 1; | |
break; | |
case 'l': | |
@@ -846,7 +890,7 @@ main(int argc, char **argv) | |
default_files[i]); | |
if (stat(buf, &st) == -1) | |
continue; | |
- if (do_file(agent_fd, deleting, key_only, buf, | |
+ if (do_file(agent_fd, deleting, keychain, key_only, buf, | |
qflag, skprovider) == -1) | |
ret = 1; | |
else | |
@@ -856,7 +900,7 @@ main(int argc, char **argv) | |
ret = 1; | |
} else { | |
for (i = 0; i < argc; i++) { | |
- if (do_file(agent_fd, deleting, key_only, | |
+ if (do_file(agent_fd, deleting, keychain, key_only, | |
argv[i], qflag, skprovider) == -1) | |
ret = 1; | |
} | |
diff --git a/ssh-agent.c b/ssh-agent.c | |
index e1fd1f3..ee80f87 100644 | |
--- a/ssh-agent.c | |
+++ b/ssh-agent.c | |
@@ -74,6 +74,9 @@ | |
#ifdef HAVE_UTIL_H | |
# include <util.h> | |
#endif | |
+#ifdef __APPLE_LAUNCHD__ | |
+#include <launch.h> | |
+#endif | |
#include "xmalloc.h" | |
#include "ssh.h" | |
@@ -81,6 +84,7 @@ | |
#include "sshbuf.h" | |
#include "sshkey.h" | |
#include "authfd.h" | |
+#include "authfile.h" | |
#include "compat.h" | |
#include "log.h" | |
#include "misc.h" | |
@@ -92,6 +96,7 @@ | |
#include "pathnames.h" | |
#include "ssh-pkcs11.h" | |
#include "sk-api.h" | |
+#include "keychain.h" | |
#ifndef DEFAULT_ALLOWED_PROVIDERS | |
# define DEFAULT_ALLOWED_PROVIDERS "/usr/lib*/*,/usr/local/lib*/*" | |
@@ -852,6 +857,60 @@ send: | |
} | |
#endif /* ENABLE_PKCS11 */ | |
+static int | |
+add_identity_callback(const char *filename, const char *passphrase) | |
+{ | |
+ struct sshkey *k; | |
+ int r; | |
+ | |
+ if ((r = sshkey_load_private(filename, passphrase, &k, NULL)) != NULL) | |
+ return r; | |
+ switch (k->type) { | |
+ case KEY_RSA: | |
+ if (RSA_blinding_on(k->rsa, NULL) != 1) { | |
+ sshkey_free(k); | |
+ return 1; | |
+ } | |
+ break; | |
+ } | |
+ if (lookup_identity(k) == NULL) { | |
+ Identity *id = xmalloc(sizeof(Identity)); | |
+ id->key = k; | |
+ id->comment = xstrdup(filename); | |
+ if (id->comment == NULL) { | |
+ sshkey_free(k); | |
+ return 1; | |
+ } | |
+ id->death = 0; | |
+ id->confirm = 0; | |
+ TAILQ_INSERT_TAIL(&idtab->idlist, id, next); | |
+ idtab->nentries++; | |
+ } else { | |
+ sshkey_free(k); | |
+ return 1; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void | |
+process_add_from_keychain(SocketEntry *e) | |
+{ | |
+ int success; | |
+ | |
+ success = add_identities_using_keychain(&add_identity_callback); | |
+ | |
+ /* e will be NULL when ssh-agent adds keys on its own at startup */ | |
+ if (e) { | |
+ int r; | |
+ | |
+ if ((r = sshbuf_put_u32(e->output, 1)) != 0 || | |
+ (r = sshbuf_put_u8(e->output, success ? | |
+ SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE)) != 0) | |
+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); | |
+ } | |
+} | |
+ | |
/* | |
* dispatch incoming message. | |
* returns 1 on success, 0 for incomplete messages or -1 on error. | |
@@ -946,6 +1005,9 @@ process_message(u_int socknum) | |
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); | |
@@ -1272,7 +1334,11 @@ usage(void) | |
int | |
main(int ac, char **av) | |
{ | |
+#ifdef __APPLE_LAUNCHD__ | |
+ int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0, s_flag = 0, l_flag = 0; | |
+#else | |
int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0, s_flag = 0; | |
+#endif | |
int sock, fd, ch, result, saved_errno; | |
char *shell, *format, *pidstr, *agentsocket = NULL; | |
#ifdef HAVE_SETRLIMIT | |
@@ -1306,7 +1372,11 @@ main(int ac, char **av) | |
__progname = ssh_get_progname(av[0]); | |
seed_rng(); | |
+#ifdef __APPLE_LAUNCHD__ | |
+ while ((ch = getopt(ac, av, "cDdklsE:a:P:t:")) != -1) { | |
+#else | |
while ((ch = getopt(ac, av, "cDdksE:a:O:P:t:")) != -1) { | |
+#endif | |
switch (ch) { | |
case 'E': | |
fingerprint_hash = ssh_digest_alg_by_name(optarg); | |
@@ -1332,6 +1402,11 @@ main(int ac, char **av) | |
fatal("-P option already specified"); | |
allowed_providers = xstrdup(optarg); | |
break; | |
+#ifdef __APPLE_LAUNCHD__ | |
+ case 'l': | |
+ l_flag++; | |
+ break; | |
+#endif | |
case 's': | |
if (c_flag) | |
usage(); | |
@@ -1363,7 +1438,11 @@ main(int ac, char **av) | |
ac -= optind; | |
av += optind; | |
+#ifdef __APPLE_LAUNCHD__ | |
+ if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag || l_flag)) | |
+#else | |
if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag)) | |
+#endif | |
usage(); | |
if (allowed_providers == NULL) | |
@@ -1434,6 +1513,53 @@ main(int ac, char **av) | |
* Create socket early so it will exist before command gets run from | |
* the parent. | |
*/ | |
+#ifdef __APPLE_LAUNCHD__ | |
+ if (l_flag) { | |
+ launch_data_t resp, msg, tmp; | |
+ size_t listeners_i; | |
+ | |
+ msg = launch_data_new_string(LAUNCH_KEY_CHECKIN); | |
+ | |
+ resp = launch_msg(msg); | |
+ | |
+ if (NULL == resp) { | |
+ perror("launch_msg"); | |
+ exit(1); | |
+ } | |
+ launch_data_free(msg); | |
+ switch (launch_data_get_type(resp)) { | |
+ case LAUNCH_DATA_ERRNO: | |
+ errno = launch_data_get_errno(resp); | |
+ perror("launch_msg response"); | |
+ exit(1); | |
+ case LAUNCH_DATA_DICTIONARY: | |
+ break; | |
+ default: | |
+ fprintf(stderr, "launch_msg unknown response"); | |
+ exit(1); | |
+ } | |
+ tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS); | |
+ | |
+ if (NULL == tmp) { | |
+ fprintf(stderr, "no sockets\n"); | |
+ exit(1); | |
+ } | |
+ | |
+ tmp = launch_data_dict_lookup(tmp, "Listeners"); | |
+ | |
+ if (NULL == tmp) { | |
+ fprintf(stderr, "no known listeners\n"); | |
+ exit(1); | |
+ } | |
+ | |
+ for (listeners_i = 0; listeners_i < launch_data_array_get_count(tmp); listeners_i++) { | |
+ launch_data_t obj_at_ind = launch_data_array_get_index(tmp, listeners_i); | |
+ new_socket(AUTH_SOCKET, launch_data_get_fd(obj_at_ind)); | |
+ } | |
+ | |
+ launch_data_free(resp); | |
+ } else { | |
+#endif | |
prev_mask = umask(0177); | |
sock = unix_listener(socket_name, SSH_LISTEN_BACKLOG, 0); | |
if (sock < 0) { | |
@@ -1442,6 +1568,14 @@ main(int ac, char **av) | |
cleanup_exit(1); | |
} | |
umask(prev_mask); | |
+#ifdef __APPLE_LAUNCHD__ | |
+ } | |
+#endif | |
+ | |
+#ifdef __APPLE_LAUNCHD__ | |
+ if (l_flag) | |
+ goto skip2; | |
+#endif | |
/* | |
* Fork, and have the parent execute the command, if any, or present | |
@@ -1519,6 +1653,7 @@ skip: | |
pkcs11_init(0); | |
#endif | |
new_socket(AUTH_SOCKET, sock); | |
+skip2: | |
if (ac > 0) | |
parent_alive_interval = 10; | |
idtab_init(); | |
@@ -1531,6 +1666,10 @@ skip: | |
fatal("%s: pledge: %s", __progname, strerror(errno)); | |
platform_pledge_agent(); | |
+#ifdef KEYCHAIN | |
+ process_add_from_keychain(NULL); | |
+#endif | |
+ | |
while (1) { | |
prepare_poll(&pfd, &npfd, &timeout, maxfds); | |
result = poll(pfd, npfd, timeout); | |
diff --git a/ssh-keysign.8 b/ssh-keysign.8 | |
index 73b6239..0406ba0 100644 | |
--- a/ssh-keysign.8 | |
+++ b/ssh-keysign.8 | |
@@ -72,6 +72,9 @@ accessible to others. | |
Since they are readable only by root, | |
.Nm | |
must be set-uid root if host-based authentication is used. | |
+Note that | |
+.Nm | |
+is not set-uid by default on Mac OS X. | |
.Pp | |
.It Pa /etc/ssh/ssh_host_dsa_key-cert.pub | |
.It Pa /etc/ssh/ssh_host_ecdsa_key-cert.pub | |
diff --git a/sshconnect2.c b/sshconnect2.c | |
index f64aae6..f318d8a 100644 | |
--- a/sshconnect2.c | |
+++ b/sshconnect2.c | |
@@ -72,6 +72,7 @@ | |
#include "hostfile.h" | |
#include "ssherr.h" | |
#include "utf8.h" | |
+#include "keychain.h" | |
#include "ssh-sk.h" | |
#include "sk-api.h" | |
@@ -1523,6 +1524,10 @@ load_identity_file(Identity *id) | |
if (i == 0) | |
passphrase = ""; | |
else { | |
+#ifdef __APPLE_KEYCHAIN__ | |
+ passphrase = keychain_read_passphrase(id->filename, options.ask_pass_gui); | |
+ if (passphrase == NULL) | |
+#endif | |
passphrase = read_passphrase(prompt, 0); | |
if (*passphrase == '\0') { | |
debug2("no passphrase given, try next key"); | |
diff --git a/sshd.c b/sshd.c | |
index 8aa7f3d..d8304c0 100644 | |
--- a/sshd.c | |
+++ b/sshd.c | |
@@ -2272,6 +2272,12 @@ main(int ac, char **av) | |
audit_event(ssh, SSH_AUTH_SUCCESS); | |
#endif | |
+#ifdef USE_PAM | |
+ if (options.use_pam) { | |
+ do_pam_setcred(1); | |
+ do_pam_session(ssh); | |
+ } | |
+#endif | |
#ifdef GSSAPI | |
if (options.gss_authentication) { | |
temporarily_use_uid(authctxt->pw); | |
@@ -2279,12 +2285,7 @@ main(int ac, char **av) | |
restore_uid(); | |
} | |
#endif | |
-#ifdef USE_PAM | |
- if (options.use_pam) { | |
- do_pam_setcred(1); | |
do_pam_session(ssh); | |
- } | |
-#endif | |
/* | |
* In privilege separation, we fork another child and prepare | |
-- | |
2.28.0 | |
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
From 2e38110d47432b28854f9e41c5f41a2e7523c501 Mon Sep 17 00:00:00 2001 | |
From: Leon Klingele <[email protected]> | |
Date: Tue, 29 Sep 2020 18:22:20 +0200 | |
Subject: [PATCH 2/2] apple-sandbox-named-external | |
--- | |
sandbox-darwin.c | 8 ++++++++ | |
sshd.c | 7 +++++++ | |
2 files changed, 15 insertions(+) | |
diff --git a/sandbox-darwin.c b/sandbox-darwin.c | |
index 59b4d28..0639f63 100644 | |
--- a/sandbox-darwin.c | |
+++ b/sandbox-darwin.c | |
@@ -63,8 +63,16 @@ ssh_sandbox_child(struct ssh_sandbox *box) | |
struct rlimit rl_zero; | |
debug3("%s: starting Darwin sandbox", __func__); | |
+#ifdef __APPLE_SANDBOX_NAMED_EXTERNAL__ | |
+#ifndef SANDBOX_NAMED_EXTERNAL | |
+#define SANDBOX_NAMED_EXTERNAL (0x3) | |
+#endif | |
+ if (sandbox_init("@PREFIX@/share/openssh/org.openssh.sshd.sb", | |
+ SANDBOX_NAMED_EXTERNAL, &errmsg) == -1) | |
+#else | |
if (sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED, | |
&errmsg) == -1) | |
+#endif | |
fatal("%s: sandbox_init: %s", __func__, errmsg); | |
/* | |
diff --git a/sshd.c b/sshd.c | |
index d8304c0..11fa6e5 100644 | |
--- a/sshd.c | |
+++ b/sshd.c | |
@@ -538,10 +538,17 @@ privsep_preauth(struct ssh *ssh) | |
/* Arrange for logging to be sent to the monitor */ | |
set_log_handler(mm_log_handler, pmonitor); | |
+#ifdef __APPLE_SANDBOX_NAMED_EXTERNAL__ | |
+ /* We need to do this before we chroot() so we can read sshd.sb */ | |
+ if (box != NULL) | |
+ ssh_sandbox_child(box); | |
+#endif | |
privsep_preauth_child(); | |
setproctitle("%s", "[net]"); | |
+#ifndef __APPLE_SANDBOX_NAMED_EXTERNAL__ | |
if (box != NULL) | |
ssh_sandbox_child(box); | |
+#endif | |
return 0; | |
} | |
-- | |
2.28.0 | |
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
;; Copyright (c) 2008 Apple Inc. All Rights reserved. | |
;; | |
;; sshd - profile for privilege separated children | |
;; | |
;; WARNING: The sandbox rules in this file currently constitute | |
;; Apple System Private Interface and are subject to change at any time and | |
;; without notice. | |
;; | |
(version 1) | |
(deny default) | |
(allow file-chroot) | |
(allow file-read-metadata (literal "/var")) | |
(allow sysctl-read) | |
(allow mach-per-user-lookup) | |
(allow mach-lookup | |
(global-name "com.apple.system.notification_center") | |
(global-name "com.apple.system.opendirectoryd.libinfo") | |
(global-name "com.apple.system.opendirectoryd.libinfo") ;; duplicate name as a work-around for 19978803 | |
(global-name "com.apple.system.logger")) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment