[PATCH] ssh-agent: add systemd socket-based activation

Ronan Pigott ronan at rjp.ie
Sat Jun 17 12:19:54 AEST 2023


This adds support for systemd socket-based activation in the ssh-agent.
When using socket activation, the -a flag value must match the socket
path provided by systemd, as a sanity check. Support for this feature is
enabled by the --with-systemd configure flag.

---
Something tells me upstream would not be interested in this patch, but
as it may be useful on linux, I'm submitting it here.

 Makefile.in  |  3 ++-
 configure.ac | 25 ++++++++++++++++++++++++
 ssh-agent.c  | 54 +++++++++++++++++++++++++++++++++++++++++++---------
 3 files changed, 72 insertions(+), 10 deletions(-)

diff --git a/Makefile.in b/Makefile.in
index 70287f51fb81..9bace646fecf 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -53,6 +53,7 @@ CHANNELLIBS=@CHANNELLIBS@
 K5LIBS=@K5LIBS@
 GSSLIBS=@GSSLIBS@
 SSHDLIBS=@SSHDLIBS@
+AGENTLIBS=@AGENTLIBS@
 LIBEDIT=@LIBEDIT@
 LIBFIDO2=@LIBFIDO2@
 AR=@AR@
@@ -216,7 +217,7 @@ ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHADD_OBJS)
 	$(LD) -o $@ $(SSHADD_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(CHANNELLIBS)
 
 ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHAGENT_OBJS)
-	$(LD) -o $@ $(SSHAGENT_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(CHANNELLIBS)
+	$(LD) -o $@ $(SSHAGENT_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(AGENTLIBS) $(LIBS) $(CHANNELLIBS)
 
 ssh-keygen$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHKEYGEN_OBJS)
 	$(LD) -o $@ $(SSHKEYGEN_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(CHANNELLIBS)
diff --git a/configure.ac b/configure.ac
index 07893e870659..d12b6e9c0588 100644
--- a/configure.ac
+++ b/configure.ac
@@ -147,6 +147,16 @@ else
 	AC_MSG_RESULT([no])
 fi
 
+systemd=no
+AC_ARG_WITH([systemd],
+	[  --with-systemd          Enable use of systemd socket-based activation ],
+	[  if test "x$withval" = "xyes" ; then
+		systemd=yes
+		AC_DEFINE([WITH_SYSTEMD], [1], [enable systemd socket-based activation])
+	   fi
+	]
+)
+
 use_stack_protector=1
 use_toolchain_hardening=1
 AC_ARG_WITH([stackprotect],
@@ -3376,6 +3386,18 @@ AC_CHECK_LIB([crypt], [crypt], [
 AC_CHECK_FUNCS([crypt])
 LIBS="$saved_LIBS"
 
+if test "x$systemd" == "xyes" ; then
+	# Check for sd_listen_fds in libsystemd for socket activation
+	saved_LIBS="$LIBS"
+	AC_CHECK_LIB([systemd], [sd_listen_fds], [
+		LIBS="-lsystemd $LIBS"
+		AGENTLIBS="-lsystemd $AGENTLIBS"
+	])
+	AC_CHECK_FUNCS([sd_listen_fds])
+	LIBS="$saved_LIBS"
+	AC_SUBST([AGENTLIBS])
+fi
+
 # Check for PAM libs
 PAM_MSG="no"
 AC_ARG_WITH([pam],
@@ -5632,6 +5654,9 @@ fi
 if test ! -z "${SSHDLIBS}"; then
 echo "         +for sshd: ${SSHDLIBS}"
 fi
+if test ! -z "${AGENTLIBS}"; then
+echo "    +for ssh-agent: ${AGENTLIBS}"
+fi
 
 echo ""
 
diff --git a/ssh-agent.c b/ssh-agent.c
index c72518ba3537..eb3a8b022590 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -69,6 +69,9 @@
 #include <stdlib.h>
 #include <time.h>
 #include <string.h>
+#ifdef WITH_SYSTEMD
+# include <systemd/sd-daemon.h>
+#endif
 #include <unistd.h>
 #ifdef HAVE_UTIL_H
 # include <util.h>
@@ -166,6 +169,11 @@ pid_t cleanup_pid = 0;
 char socket_name[PATH_MAX];
 char socket_dir[PATH_MAX];
 
+#ifdef WITH_SYSTEMD
+/* tracks whether the active AUTH_SOCKET was passed to us by a third party */
+int external_socket = 0;
+#endif
+
 /* Pattern-list of allowed PKCS#11/Security key paths */
 static char *allowed_providers;
 
@@ -1946,6 +1954,10 @@ cleanup_socket(void)
 {
 	if (cleanup_pid != 0 && getpid() != cleanup_pid)
 		return;
+#ifdef WITH_SYSTEMD
+	if (external_socket)
+		return;
+#endif
 	debug_f("cleanup");
 	if (socket_name[0])
 		unlink(socket_name);
@@ -2000,7 +2012,7 @@ int
 main(int ac, char **av)
 {
 	int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0, s_flag = 0;
-	int sock, ch, result, saved_errno;
+	int sock = 0, ch, result, saved_errno;
 	char *shell, *format, *pidstr, *agentsocket = NULL;
 #ifdef HAVE_SETRLIMIT
 	struct rlimit rlim;
@@ -2015,6 +2027,9 @@ main(int ac, char **av)
 	struct pollfd *pfd = NULL;
 	size_t npfd = 0;
 	u_int maxfds;
+#ifdef WITH_SYSTEMD
+	int nfds = 0;
+#endif
 
 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
 	sanitise_stdfd();
@@ -2142,6 +2157,25 @@ main(int ac, char **av)
 
 	parent_pid = getpid();
 
+#ifdef WITH_SYSTEMD
+	nfds = sd_listen_fds(1);
+	if (nfds > 0) {
+		sock = SD_LISTEN_FDS_START;
+		if (agentsocket == NULL) {
+			fprintf(stderr, "%s not set, cannot use socket-activation",
+				SSH_AUTHSOCKET_ENV_NAME);
+			exit(1);
+		} else if (sd_is_socket_unix(sock, SOCK_STREAM, 1, agentsocket, 0) <= 0) {
+			fprintf(stderr, "Unexpected auth sock received from systemd. Expected %s\n", agentsocket);
+			exit(1);
+		} else if (nfds > 1) {
+			fprintf(stderr, "too many fds received from systemd (%d)\n", nfds);
+			exit(1);
+		}
+		strlcpy(socket_name, agentsocket, sizeof socket_name);
+		external_socket = 1;
+	}
+#endif
 	if (agentsocket == NULL) {
 		/* Create private directory for agent socket */
 		mktemp_proto(socket_dir, sizeof(socket_dir));
@@ -2150,7 +2184,7 @@ main(int ac, char **av)
 			exit(1);
 		}
 		snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir,
-		    (long)parent_pid);
+			(long)parent_pid);
 	} else {
 		/* Try to use specified agent socket */
 		socket_dir[0] = '\0';
@@ -2161,14 +2195,16 @@ main(int ac, char **av)
 	 * Create socket early so it will exist before command gets run from
 	 * the parent.
 	 */
-	prev_mask = umask(0177);
-	sock = unix_listener(socket_name, SSH_LISTEN_BACKLOG, 0);
-	if (sock < 0) {
-		/* XXX - unix_listener() calls error() not perror() */
-		*socket_name = '\0'; /* Don't unlink any existing file */
-		cleanup_exit(1);
+	if (sock == 0) {
+		prev_mask = umask(0177);
+		sock = unix_listener(socket_name, SSH_LISTEN_BACKLOG, 0);
+		if (sock < 0) {
+			/* XXX - unix_listener() calls error() not perror() */
+			*socket_name = '\0'; /* Don't unlink any existing file */
+			cleanup_exit(1);
+		}
+		umask(prev_mask);
 	}
-	umask(prev_mask);
 
 	/*
 	 * Fork, and have the parent execute the command, if any, or present
-- 
2.41.0



More information about the openssh-unix-dev mailing list