[openssh-commits] [openssh] 02/04: upstream: Move agent listener sockets from /tmp to under

git+noreply at mindrot.org git+noreply at mindrot.org
Mon May 5 14:59:35 AEST 2025


This is an automated email from the git hooks/post-receive script.

djm pushed a commit to branch master
in repository openssh.

commit 80162f9d7e7eadca4ffd0bd1c015d38cb1821ab6
Author: djm at openbsd.org <djm at openbsd.org>
AuthorDate: Mon May 5 02:48:06 2025 +0000

    upstream: Move agent listener sockets from /tmp to under
    
    ~/.ssh/agent for both ssh-agent(1) and forwarded sockets in sshd(8).
    
    This ensures processes (such as Firefox) that have restricted
    filesystem access that includes /tmp (via unveil(3)) do not have the
    ability to use keys in an agent.
    
    Moving the default directory has the consequence that the OS will no
    longer clean up stale agent sockets, so ssh-agent now gains this
    ability.
    
    To support $HOME on NFS, the socket path includes a truncated hash of
    the hostname. ssh-agent will by default only clean up sockets from
    the same hostname.
    
    ssh-agent gains some new flags: -U suppresses the automatic cleanup
    of stale sockets when it starts. -u forces a cleanup without
    keeping a running agent, -uu forces a cleanup that ignores the
    hostname. -T makes ssh-agent put the socket back in /tmp.
    
    feedback deraadt@ naddy@, doitdoitdoit deraadt@
    
    OpenBSD-Commit-ID: 8383dabd98092fe5498d5f7f15c7d314b03a93e1
---
 Makefile.in |  6 ++---
 hostfile.c  |  2 +-
 misc.c      | 17 ++++++++++++-
 misc.h      |  8 +++++-
 pathnames.h |  9 ++++++-
 session.c   | 34 +++----------------------
 ssh-agent.1 | 32 +++++++++++++++++++----
 ssh-agent.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++-----------
 8 files changed, 136 insertions(+), 57 deletions(-)

diff --git a/Makefile.in b/Makefile.in
index f15ac558a..4550e1795 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -140,7 +140,7 @@ SSHD_SESSION_OBJS=sshd-session.o auth-rhosts.o auth-passwd.o \
 	auth2-gss.o gss-serv.o gss-serv-krb5.o \
 	loginrec.o auth-pam.o auth-shadow.o auth-sia.o \
 	sftp-server.o sftp-common.o \
-	uidswap.o platform-listen.o $(SKOBJS)
+	uidswap.o platform-listen.o misc-agent.o $(SKOBJS)
 
 SSHD_AUTH_OBJS=sshd-auth.o \
 	auth2-methods.o \
@@ -155,7 +155,7 @@ SSHD_AUTH_OBJS=sshd-auth.o \
 	sandbox-null.o sandbox-rlimit.o sandbox-darwin.o \
 	sandbox-seccomp-filter.o sandbox-capsicum.o  sandbox-solaris.o \
 	sftp-server.o sftp-common.o \
-	uidswap.o $(SKOBJS)
+	uidswap.o misc-agent.o $(SKOBJS)
 
 SFTP_CLIENT_OBJS=sftp-common.o sftp-client.o sftp-glob.o
 
@@ -163,7 +163,7 @@ SCP_OBJS=	scp.o progressmeter.o $(SFTP_CLIENT_OBJS)
 
 SSHADD_OBJS=	ssh-add.o $(SKOBJS)
 
-SSHAGENT_OBJS=	ssh-agent.o ssh-pkcs11-client.o $(SKOBJS)
+SSHAGENT_OBJS=	ssh-agent.o ssh-pkcs11-client.o misc-agent.o $(SKOBJS)
 
 SSHKEYGEN_OBJS=	ssh-keygen.o sshsig.o $(SKOBJS)
 
diff --git a/hostfile.c b/hostfile.c
index e941fc450..4b4a0e31e 100644
--- a/hostfile.c
+++ b/hostfile.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: hostfile.c,v 1.97 2025/04/30 05:26:15 djm Exp $ */
+/* $OpenBSD: hostfile.c,v 1.98 2025/05/05 02:48:07 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
diff --git a/misc.c b/misc.c
index dd0bd032a..25465dcd2 100644
--- a/misc.c
+++ b/misc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.c,v 1.198 2024/10/24 03:14:37 djm Exp $ */
+/* $OpenBSD: misc.c,v 1.199 2025/05/05 02:48:06 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  * Copyright (c) 2005-2020 Damien Miller.  All rights reserved.
@@ -3138,3 +3138,18 @@ signal_is_crash(int sig)
 	}
 	return 0;
 }
+
+char *
+get_homedir(void)
+{
+	char *cp;
+	struct passwd *pw;
+
+	if ((cp = getenv("HOME")) != NULL && *cp != '\0')
+		return xstrdup(cp);
+
+	if ((pw = getpwuid(getuid())) != NULL && *pw->pw_dir != '\0')
+		return xstrdup(pw->pw_dir);
+
+	return NULL;
+}
diff --git a/misc.h b/misc.h
index efecdf1ad..a7afa23e8 100644
--- a/misc.h
+++ b/misc.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.h,v 1.110 2024/09/25 01:24:04 djm Exp $ */
+/* $OpenBSD: misc.h,v 1.111 2025/05/05 02:48:06 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
@@ -108,6 +108,7 @@ int	 parse_pattern_interval(const char *, char **, int *);
 int	 path_absolute(const char *);
 int	 stdfd_devnull(int, int, int);
 int	 lib_contains_symbol(const char *, const char *);
+char	*get_homedir(void);
 
 void	 sock_set_v6only(int);
 
@@ -231,6 +232,11 @@ int ptimeout_get_ms(struct timespec *pt);
 struct timespec *ptimeout_get_tsp(struct timespec *pt);
 int ptimeout_isset(struct timespec *pt);
 
+/* misc-agent.c */
+char	*agent_hostname_hash(void);
+int	 agent_listener(const char *, const char *, int *, char **);
+void	 agent_cleanup_stale(const char *, int);
+
 /* readpass.c */
 
 #define RP_ECHO			0x0001
diff --git a/pathnames.h b/pathnames.h
index 1158bec96..e07395cb6 100644
--- a/pathnames.h
+++ b/pathnames.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pathnames.h,v 1.32 2024/05/17 00:30:24 djm Exp $ */
+/* $OpenBSD: pathnames.h,v 1.34 2025/05/05 02:48:06 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
@@ -67,6 +67,13 @@
  */
 #define _PATH_SSH_USER_DIR		".ssh"
 
+
+/*
+ * The directory in which ssh-agent sockets and agent sockets forwarded by
+ * sshd reside. This directory should not be world-readable.
+ */
+#define _PATH_SSH_AGENT_SOCKET_DIR _PATH_SSH_USER_DIR "/agent"
+
 /*
  * Per-user file containing host keys of known hosts.  This file need not be
  * readable by anyone except the user him/herself, though this does not
diff --git a/session.c b/session.c
index 6444c77f3..630e0e6a3 100644
--- a/session.c
+++ b/session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: session.c,v 1.341 2025/04/09 07:00:03 djm Exp $ */
+/* $OpenBSD: session.c,v 1.342 2025/05/05 02:48:06 djm Exp $ */
 /*
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
@@ -175,7 +175,6 @@ static char *auth_info_file = NULL;
 
 /* Name and directory of socket for authentication agent forwarding. */
 static char *auth_sock_name = NULL;
-static char *auth_sock_dir = NULL;
 
 /* removes the agent forwarding socket */
 
@@ -185,7 +184,6 @@ auth_sock_cleanup_proc(struct passwd *pw)
 	if (auth_sock_name != NULL) {
 		temporarily_use_uid(pw);
 		unlink(auth_sock_name);
-		rmdir(auth_sock_dir);
 		auth_sock_name = NULL;
 		restore_uid();
 	}
@@ -205,32 +203,15 @@ auth_input_request_forwarding(struct ssh *ssh, struct passwd * pw)
 	/* Temporarily drop privileged uid for mkdir/bind. */
 	temporarily_use_uid(pw);
 
-	/* Allocate a buffer for the socket name, and format the name. */
-	auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX");
-
-	/* Create private directory for socket */
-	if (mkdtemp(auth_sock_dir) == NULL) {
+	if (agent_listener(pw->pw_dir, "sshd", &sock, &auth_sock_name) != 0) {
+		/* a more detailed error is already logged */
 		ssh_packet_send_debug(ssh, "Agent forwarding disabled: "
-		    "mkdtemp() failed: %.100s", strerror(errno));
+		    "couldn't create listener socket");
 		restore_uid();
-		free(auth_sock_dir);
-		auth_sock_dir = NULL;
 		goto authsock_err;
 	}
-
-	xasprintf(&auth_sock_name, "%s/agent.%ld",
-	    auth_sock_dir, (long) getpid());
-
-	/* Start a Unix listener on auth_sock_name. */
-	sock = unix_listener(auth_sock_name, SSH_LISTEN_BACKLOG, 0);
-
-	/* Restore the privileged uid. */
 	restore_uid();
 
-	/* Check for socket/bind/listen failure. */
-	if (sock < 0)
-		goto authsock_err;
-
 	/* Allocate a channel for the authentication agent socket. */
 	nc = channel_new(ssh, "auth-listener",
 	    SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1,
@@ -241,16 +222,9 @@ auth_input_request_forwarding(struct ssh *ssh, struct passwd * pw)
 
  authsock_err:
 	free(auth_sock_name);
-	if (auth_sock_dir != NULL) {
-		temporarily_use_uid(pw);
-		rmdir(auth_sock_dir);
-		restore_uid();
-		free(auth_sock_dir);
-	}
 	if (sock != -1)
 		close(sock);
 	auth_sock_name = NULL;
-	auth_sock_dir = NULL;
 	return 0;
 }
 
diff --git a/ssh-agent.1 b/ssh-agent.1
index 533ad6d3a..3b515ac42 100644
--- a/ssh-agent.1
+++ b/ssh-agent.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: ssh-agent.1,v 1.82 2025/02/09 18:24:08 schwarze Exp $
+.\" $OpenBSD: ssh-agent.1,v 1.83 2025/05/05 02:48:06 djm Exp $
 .\"
 .\" Author: Tatu Ylonen <ylo at cs.hut.fi>
 .\" Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -34,7 +34,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: February 9 2025 $
+.Dd $Mdocdate: May 5 2025 $
 .Dt SSH-AGENT 1
 .Os
 .Sh NAME
@@ -43,13 +43,14 @@
 .Sh SYNOPSIS
 .Nm ssh-agent
 .Op Fl c | s
-.Op Fl \&Dd
+.Op Fl \&DdTU
 .Op Fl a Ar bind_address
 .Op Fl E Ar fingerprint_hash
 .Op Fl O Ar option
 .Op Fl P Ar allowed_providers
 .Op Fl t Ar life
 .Nm ssh-agent
+.Op Fl TU
 .Op Fl a Ar bind_address
 .Op Fl E Ar fingerprint_hash
 .Op Fl O Ar option
@@ -59,6 +60,8 @@
 .Nm ssh-agent
 .Op Fl c | s
 .Fl k
+.Nm ssh-agent
+.Fl u
 .Sh DESCRIPTION
 .Nm
 is a program to hold private keys used for public key authentication.
@@ -74,8 +77,8 @@ Bind the agent to the
 .Ux Ns -domain
 socket
 .Ar bind_address .
-The default is
-.Pa $TMPDIR/ssh-XXXXXXXXXX/agent.\*(Ltppid\*(Gt .
+The default is to create a socket at a random path matching
+.Pa $HOME/.ssh/agent/s.*
 .It Fl c
 Generate C-shell commands on standard output.
 This is the default if
@@ -173,6 +176,11 @@ Generate Bourne shell commands on standard output.
 This is the default if
 .Ev SHELL
 does not look like it's a csh style of shell.
+.It Fl T
+Bind the agent socket in a randomised subdirectory of the form
+.Pa $TMPDIR/ssh-XXXXXXXXXX/agent.\*(Ltppid\*(Gt ,
+instead of the default behaviour of using a randomised name matching
+.Pa $HOME/.ssh/agent/s.* .
 .It Fl t Ar life
 Set a default value for the maximum lifetime of identities added to the agent.
 The lifetime may be specified in seconds or in a time format specified in
@@ -186,6 +194,20 @@ If a command (and optional arguments) is given,
 this is executed as a subprocess of the agent.
 The agent exits automatically when the command given on the command
 line terminates.
+.It Fl U
+Instructs
+.Nm
+not to clean up stale agent sockets under
+.Pa $HOME/.ssh/agent/ .
+.It Fl u
+Instructs
+.Nm
+to only clean up stale agent sockets under
+.Pa $HOME/.ssh/agent/
+and then exit immediately.
+If this option is given twice,
+.Nm
+will delete stale agent sockets regardless of the host name that created them.
 .El
 .Pp
 There are three main ways to get an agent set up.
diff --git a/ssh-agent.c b/ssh-agent.c
index 55b9f44f4..8a88ef3fd 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-agent.c,v 1.311 2025/04/15 09:22:25 dtucker Exp $ */
+/* $OpenBSD: ssh-agent.c,v 1.312 2025/05/05 02:48:06 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -2208,20 +2208,23 @@ static void
 usage(void)
 {
 	fprintf(stderr,
-	    "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n"
+	    "usage: ssh-agent [-c | -s] [-DdTU] [-a bind_address] [-E fingerprint_hash]\n"
 	    "                 [-O option] [-P allowed_providers] [-t life]\n"
-	    "       ssh-agent [-a bind_address] [-E fingerprint_hash] [-O option]\n"
+	    "       ssh-agent [-TU] [-a bind_address] [-E fingerprint_hash] [-O option]\n"
 	    "                 [-P allowed_providers] [-t life] command [arg ...]\n"
-	    "       ssh-agent [-c | -s] -k\n");
+	    "       ssh-agent [-c | -s] -k\n"
+	    "       ssh-agent -u\n");
 	exit(1);
 }
 
 int
 main(int ac, char **av)
 {
-	int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0, s_flag = 0;
+	int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0;
+	int s_flag = 0, T_flag = 0, u_flag = 0, U_flag = 0;
 	int sock = -1, ch, result, saved_errno;
-	char *shell, *format, *fdstr, *pidstr, *agentsocket = NULL;
+	char *homedir = NULL, *shell, *format, *pidstr, *agentsocket = NULL;
+	char *fdstr;
 	const char *errstr = NULL;
 	const char *ccp;
 #ifdef HAVE_SETRLIMIT
@@ -2256,7 +2259,7 @@ main(int ac, char **av)
 	__progname = ssh_get_progname(av[0]);
 	seed_rng();
 
-	while ((ch = getopt(ac, av, "cDdksE:a:O:P:t:")) != -1) {
+	while ((ch = getopt(ac, av, "cDdksTuUE:a:O:P:t:")) != -1) {
 		switch (ch) {
 		case 'E':
 			fingerprint_hash = ssh_digest_alg_by_name(optarg);
@@ -2313,6 +2316,15 @@ main(int ac, char **av)
 				usage();
 			}
 			break;
+		case 'T':
+			T_flag++;
+			break;
+		case 'u':
+			u_flag++;
+			break;
+		case 'U':
+			U_flag++;
+			break;
 		default:
 			usage();
 		}
@@ -2320,9 +2332,14 @@ main(int ac, char **av)
 	ac -= optind;
 	av += optind;
 
-	if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag))
+	if (ac > 0 &&
+	    (c_flag || k_flag || s_flag || d_flag || D_flag || u_flag))
 		usage();
 
+	log_init(__progname,
+	    d_flag ? SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_INFO,
+	    SYSLOG_FACILITY_AUTH, 1);
+
 	if (allowed_providers == NULL)
 		allowed_providers = xstrdup(DEFAULT_ALLOWED_PROVIDERS);
 	if (websafe_allowlist == NULL)
@@ -2358,6 +2375,14 @@ main(int ac, char **av)
 		printf("echo Agent pid %ld killed;\n", (long)pid);
 		exit(0);
 	}
+	if (u_flag) {
+		if ((homedir = get_homedir()) == NULL)
+			fatal("Couldn't determine home directory");
+		agent_cleanup_stale(homedir, u_flag > 1);
+		printf("Deleted stale agent sockets in ~/%s\n",
+		    _PATH_SSH_AGENT_SOCKET_DIR);
+		exit(0);
+	}
 
 	/*
 	 * Minimum file descriptors:
@@ -2391,22 +2416,52 @@ main(int ac, char **av)
 		sock = 3;
 	}
 
-	/* Otherwise, create private directory for agent socket */
-	if (sock == -1) {
-		if (agentsocket == NULL) {
+	if (sock == -1 && agentsocket == NULL && !T_flag) {
+		/* Default case: ~/.ssh/agent/[socket] */
+		if ((homedir = get_homedir()) == NULL)
+			fatal("Couldn't determine home directory");
+		if (!U_flag)
+			agent_cleanup_stale(homedir, 0);
+		if (agent_listener(homedir, "agent", &sock, &agentsocket) != 0)
+			fatal_f("Couldn't prepare agent socket");
+		if (strlcpy(socket_name, agentsocket,
+		    sizeof(socket_name)) >= sizeof(socket_name)) {
+			fatal_f("Socket path \"%s\" too long",
+			    agentsocket);
+		}
+		free(homedir);
+		free(agentsocket);
+		agentsocket = NULL;
+	} else if (sock == -1) {
+		if (T_flag) {
+			/*
+			 * Create private directory for agent socket
+			 * in $TMPDIR.
+			 */
 			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';
-			strlcpy(socket_name, agentsocket, sizeof socket_name);
+			if (strlcpy(socket_name, agentsocket,
+			   sizeof(socket_name)) >= sizeof(socket_name)) {
+				fatal_f("Socket path \"%s\" too long",
+				    agentsocket);
+			}
 		}
+		/* Listen on socket */
+		prev_mask = umask(0177);
+		if ((sock = unix_listener(socket_name,
+		    SSH_LISTEN_BACKLOG, 0)) < 0) {
+			*socket_name = '\0'; /* Don't unlink existing file */
+			cleanup_exit(1);
+		}
+		umask(prev_mask);
 	}
 
 	closefrom(sock == -1 ? STDERR_FILENO + 1 : sock + 1);

-- 
To stop receiving notification emails like this one, please contact
djm at mindrot.org.


More information about the openssh-commits mailing list