Bug#1068416: ssh-agent: improve systemd user session integration

2024-04-05 Thread Daniel Kahn Gillmor
On Thu 2024-04-04 15:28:34 -0400, Daniel Kahn Gillmor wrote:
> ssh-agent is a critical piece of infrastructure for my workflow, and i
> want it better integrated with my user session, which is managed by
> systemd's per-user login manager (`systemd --user`).

I'm attaching an updated set of patches.  These replace the earlier set
of patches. In particular, it irons out the following bugs:

- the systemd socket-activation now sets up the systemd activation
   environment, but doesn't try to use
   dbus-update-activation-environment, as i found that trying to talk to
   dbus during early login wasn't reliable.

- the socket is set up with mode 0600

- the socket is actually installed with the debian package

- properly initialize `sock` variable in ssh-agent to -1, so that
  "ssh-agent " works properly again.

I'd be happy to hear some feedback if anyone else wants to try it.

  --dkg

From b3051efa59b6a3802f2b10048f35640cf8d2011c Mon Sep 17 00:00:00 2001
From: Daniel Kahn Gillmor 
Date: Thu, 4 Apr 2024 13:09:10 -0400
Subject: [PATCH 1/2] ssh-agent: automatically detect when socket-activated via
 systemd

Socket activation for the ssh-agent is useful for several reasons:

- the systemd user session can choose and reserve the socket before
  the session has fully started.

- systemd can inject SSH_AUTH_SOCK directly into the dbus and systemd
  environment, even before the agent is started.

- the agent will be started lazily, on-demand.  This consumes no
  system resources (not even a pid) if the user never tries to talk to
  the agent, and the agent will inherit the systemd service activation
  environment when it is first accessed (for example, if it is first
  accessed from a Wayland session, $WAYLAND_DISPLAY will be set; if it
  is first accessed from an X11 session, $DISPLAY will be set).

We only enter this mode if the following conditions are true:

 - One of the -D or -d flags are given, and no command arguments are
   present (ssh-agent when supervised by systemd must be run in the
   foreground and not as a subprocess), and

 - The environment variable conditions described in
   sd_listen_fds(3) are met, and only a single socket is provided.
---
 ssh-agent.1 |  8 
 ssh-agent.c | 57 ++---
 2 files changed, 45 insertions(+), 20 deletions(-)

diff --git a/ssh-agent.1 b/ssh-agent.1
index 1ab7d729d..0ce0cd781 100644
--- a/ssh-agent.1
+++ b/ssh-agent.1
@@ -247,6 +247,14 @@ starts, it creates a
 socket and stores its pathname in this variable.
 It is accessible only to the current user,
 but is easily abused by root or another instance of the same user.
+.It Ev SD_LISTEN_FDS
+When
+.Nm
+starts, if no socket path has been specified, but this variable and the
+accompanying SD_LISTEN_PID are set as described in
+.Xr sd_listen_fds 3 ,
+.Nm
+uses the socket passed in by the systemd supervisor.
 .El
 .Pp
 In Debian,
diff --git a/ssh-agent.c b/ssh-agent.c
index d35741a86..54a8978cc 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -2193,8 +2193,8 @@ 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;
-	char *shell, *format, *pidstr, *agentsocket = NULL;
+	int sock = -1, ch, result, saved_errno;
+	char *shell, *format, *pidstr, *listen, *agentsocket = NULL;
 #ifdef HAVE_SETRLIMIT
 	struct rlimit rlim;
 #endif
@@ -2339,14 +2339,27 @@ main(int ac, char **av)
 	parent_pid = getpid();
 
 	if (agentsocket == NULL) {
-		/* Create private directory for agent socket */
-		mktemp_proto(socket_dir, sizeof(socket_dir));
-		if (mkdtemp(socket_dir) == NULL) {
-			perror("mkdtemp: private socket dir");
-			exit(1);
+		/* Check if we are socket-activated with a single socket, as
+		 * described in sd_listen_fds(3) */
+		if ((D_flag || d_flag) &&
+			(listen = getenv("LISTEN_PID")) &&
+			strtol(listen, NULL, 10) == (long)parent_pid &&
+			(listen = getenv("LISTEN_FDS")) &&
+			strcmp(listen, "1") == 0) {
+			socket_name[0] = '\0';
+			socket_dir[0] = '\0';
+			/* this is SD_LISTEN_FDS_START */
+			sock = 3;
+		} else {
+			/* Create private directory for agent socket */
+			mktemp_proto(socket_dir, sizeof(socket_dir));
+			if (mkdtemp(socket_dir) == NULL) {
+perror("mkdtemp: private socket dir");
+exit(1);
+			}
+			snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir,
+	 (long)parent_pid);
 		}
-		snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir,
-		(long)parent_pid);
 	} else {
 		/* Try to use specified agent socket */
 		socket_dir[0] = '\0';
@@ -2357,14 +2370,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);
+		prev_mask = 

Bug#1068416: ssh-agent: improve systemd user session integration

2024-04-04 Thread Daniel Kahn Gillmor
Package: openssh-client
Version: 1:9.7p1-4
Severity: wishlist
X-Debbugs-Cc: Daniel Kahn Gillmor 
Tags: patch

Hi Debian OpenSSH maintainers!

ssh-agent is a critical piece of infrastructure for my workflow, and i
want it better integrated with my user session, which is managed by
systemd's per-user login manager (`systemd --user`).

It seems to me that the ideal scenario is to use a socket-activated
ssh-agent at a fixed location (in $XDG_RUNTIME_DIR).

The attached series of patches do this, by:

- patching ssh-agent.c to enable socket-activation when run in the
  foreground and the environment matches that described in
  sd_listen_fds(3)

- Adding an ssh-agent.socket file to debian/systemd/, so that the user
  session manager creates the socket and updates the dbus and systemd
  activation environments to point to the socket.  This systemd unit
  should be automatically activated at user login.

- Updating debian's ssh-agent.service unit to simply run ssh-agent in
  the foreground, accepting the distributed socket as its listening
  port.  I think this makes /usr/lib/openssh/agent-launch superfluous;
  maybe it can be removed.

- making `ssh-agent -k` Do What I Mean™ when $SSH_AGENT_PID is unset --
  it just tries to run `systemctl --user stop ssh-agent`, which has the
  same semantics.

Happy to get any feedback on this proposed change.

  --dkg

-- System Information:
Debian Release: trixie/sid
  APT prefers testing-debug
  APT policy: (500, 'testing-debug'), (500, 'testing'), (500, 'stable'), (500, 
'oldstable'), (200, 'unstable-debug'), (200, 'unstable'), (1, 
'experimental-debug'), (1, 'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 6.6.15-amd64 (SMP w/4 CPU threads; PREEMPT)
Kernel taint flags: TAINT_FIRMWARE_WORKAROUND
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)

Versions of packages openssh-client depends on:
ii  adduser   3.137
ii  libc6 2.37-15
ii  libedit2  3.1-20230828-1
ii  libfido2-11.14.0-1
ii  libgssapi-krb5-2  1.20.1-5+b1
ii  libselinux1   3.5-2
ii  libssl3t643.1.5-1.1
ii  passwd1:4.13+dfsg1-4
ii  zlib1g1:1.3.dfsg-3+b1

Versions of packages openssh-client recommends:
ii  xauth  1:1.1.2-1

Versions of packages openssh-client suggests:
pn  keychain 
pn  libpam-ssh   
ii  monkeysphere 0.44-1
ii  ssh-askpass-gnome [ssh-askpass]  1:9.7p1-4

-- no debconf information
From c5fc76aab1b956c74b2a46b45532f5802f7a1c1a Mon Sep 17 00:00:00 2001
From: Daniel Kahn Gillmor 
Date: Thu, 4 Apr 2024 13:09:10 -0400
Subject: [PATCH 1/3] ssh-agent: automatically detect when socket-activated via
 systemd

Socket activation for the ssh-agent is useful for several reasons:

- the systemd user session can choose and reserve the socket before
  the session has fully started.

- systemd can inject SSH_AUTH_SOCK directly into the dbus and systemd
  environment, even before the agent is started.

- the agent will be started lazily, on-demand.  This consumes no
  system resources (not even a pid) if the user never tries to talk to
  the agent, and the agent will inherit the systemd service activation
  environment when it is first accessed (for example, if it is first
  accessed from a Wayland session, $WAYLAND_DISPLAY will be set; if it
  is first accessed from an X11 session, $DISPLAY will be set).

We only enter this mode if the following conditions are true:

 - One of the -D or -d flags are given, and no command arguments are
   present (ssh-agent when supervised by systemd must be run in the
   foreground and not as a subprocess), and

 - The environment variable conditions described in
   sd_listen_fds(3) are met, and only a single socket is provided.
---
 ssh-agent.1 |  8 
 ssh-agent.c | 55 +++--
 2 files changed, 44 insertions(+), 19 deletions(-)

diff --git a/ssh-agent.1 b/ssh-agent.1
index 1ab7d729d..0ce0cd781 100644
--- a/ssh-agent.1
+++ b/ssh-agent.1
@@ -247,6 +247,14 @@ starts, it creates a
 socket and stores its pathname in this variable.
 It is accessible only to the current user,
 but is easily abused by root or another instance of the same user.
+.It Ev SD_LISTEN_FDS
+When
+.Nm
+starts, if no socket path has been specified, but this variable and the
+accompanying SD_LISTEN_PID are set as described in
+.Xr sd_listen_fds 3 ,
+.Nm
+uses the socket passed in by the systemd supervisor.
 .El
 .Pp
 In Debian,
diff --git a/ssh-agent.c b/ssh-agent.c
index d35741a86..47472e069 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -2194,7 +2194,7 @@ 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;
-	char *shell, *format, *pidstr, *agentsocket = NULL;
+	char *shell, *format, *pidstr, *listen, *agentsocket = NULL;
 #ifdef