ability to select which identity to forward when using "ForwardAgent" ?

Damien Miller djm at mindrot.org
Sun Oct 4 23:35:06 AEDT 2020


On Sun, 4 Oct 2020, Pablo Escobar wrote:

> Hi,
> 
> I usually have around 10 identities loaded in my local ssh-agent and when I
> use the "ForwardAgent" option all them are forwarded to the remote server,
> which is not ideal. I usually only need to forward one (or two) of the
> identities and I would like to be able to choose which one(s) to forward.
> 
> Looking for solutions it seems that the only option is to create a new
> ssh-agent, add the required identities and then do the forwarding as
> described in https://serverfault.com/a/1012678 but this is not very
> convenient for daily usage mainly when I need to connect to many different
> servers and all my private keys are password protected.
> 
> I have also found an external tool to do it (
> https://github.com/tiwe-de/ssh-agent-filter ) but this tool doesn't seem to
> be actively maintained and a native openssh functionality would be
> preferred.
> 
> Ideally it would be great to be able to add something like this to my
> ~/.ssh/config ( option "IdentitiesToForward" in this example doesn't exist
> and it's what I am missing)
> 
> Host myserver
>     Hostname myserver.com
>     IdentityFile ~/.ssh/id_ed25519
>     ForwardAgent yes
>     IdentitiesToForward ~/.ssh/id_ed25519,~/.ssh/id_rsa
> 
> Do you think this feature or any alternative providing similar
> functionality could be added to openssh?

Yes, I have been working on better control over what gets forwarded but
not quite what you have here. Generally, I don't want to implement in
ssh fine-grained control over which keys are offered for agent forwarding
because that would force ssh into a much more trusted role in agent key
handling than it currently occupies.

Instead, I have some work-in-progress patches that let ssh-add mark a key
as "local only". These keys may be used for authentication by ssh but are
never forwarded. This lets users separate the two commingled roles of the
agent: 1) a handy place way to use private keys without having to type the
passphrase over and over and 2) a repository of keys that you want to
forward to remote hosts.

Patches are attached but be warned that are likely incomplete :)

-d
-------------- next part --------------
From b6cb3abb87b7a6acdd626abd13c90e3b3cea5de2 Mon Sep 17 00:00:00 2001
From: Damien Miller <djm at mindrot.org>
Date: Thu, 10 Sep 2020 11:57:11 +1000
Subject: [PATCH 1/4] ssh-agent forwarded socket restrictions

This makes ssh-agent listen on a new $SSH_AUTH_SOCK_LOCAL socket in
addition to the usual $SSH_AUTH_SOCK. The original socket loses the
ability to add smartcard or FIDO keys.

Makes ssh-add prefer $SSH_AUTH_SOCK_LOCAL if present.

This makes forwarded agents by default lose the ability to add new
smartcard or FIDO keys to a remote agent. "ssh-agent -L" will get
back the old behaviour.
---
 ssh-add.1   |   8 ++--
 ssh-add.c   |  10 ++++-
 ssh-agent.1 |  29 ++++++++++++-
 ssh-agent.c | 123 ++++++++++++++++++++++++++++++++++++++++------------
 ssh.h       |   6 +++
 5 files changed, 144 insertions(+), 32 deletions(-)

diff --git a/ssh-add.1 b/ssh-add.1
index 2786df5..2f1793a 100644
--- a/ssh-add.1
+++ b/ssh-add.1
@@ -215,10 +215,12 @@ then the askpass program will be used for all passphrase input regardless
 of whether
 .Ev DISPLAY
 is set.
-.It Ev SSH_AUTH_SOCK
-Identifies the path of a
+.It Ev SSH_AUTH_SOCK | Ev SSH_AUTH_SOCK_LOCAL
+Identifies paths to
 .Ux Ns -domain
-socket used to communicate with the agent.
+sockets used to communicate with the agent.
+.Ev SSH_AUTH_SOCK_LOCAL
+is preferred if available.
 .It Ev SSH_SK_PROVIDER
 Specifies a path to a library that will be used when loading any
 FIDO authenticator-hosted keys, overriding the default of using
diff --git a/ssh-add.c b/ssh-add.c
index 0ce989f..6ce1142 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -658,6 +658,7 @@ main(int argc, char **argv)
 	extern char *optarg;
 	extern int optind;
 	int agent_fd;
+	const char *sock_path;
 	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;
@@ -674,8 +675,15 @@ main(int argc, char **argv)
 
 	setvbuf(stdout, NULL, _IOLBF, 0);
 
+	if ((sock_path = getenv(SSH_AUTHSOCKET_LOCAL_ENV_NAME)) == NULL &&
+	    (sock_path = getenv(SSH_AUTHSOCKET_ENV_NAME)) == NULL) {
+		fprintf(stderr, "No $%s or $%s socket found\n",
+		    SSH_AUTHSOCKET_ENV_NAME, SSH_AUTHSOCKET_LOCAL_ENV_NAME);
+		exit(2);
+	}
+
 	/* First, get a connection to the authentication agent. */
-	switch (r = ssh_get_authentication_socket(&agent_fd)) {
+	switch (r = ssh_get_authentication_socket_path(sock_path, &agent_fd)) {
 	case 0:
 		break;
 	case SSH_ERR_AGENT_NOT_PRESENT:
diff --git a/ssh-agent.1 b/ssh-agent.1
index 2cf4616..c7e37fa 100644
--- a/ssh-agent.1
+++ b/ssh-agent.1
@@ -43,7 +43,7 @@
 .Sh SYNOPSIS
 .Nm ssh-agent
 .Op Fl c | s
-.Op Fl \&Dd
+.Op Fl \&DdL
 .Op Fl a Ar bind_address
 .Op Fl E Ar fingerprint_hash
 .Op Fl P Ar allowed_providers
@@ -102,6 +102,16 @@ The default is
 Kill the current agent (given by the
 .Ev SSH_AGENT_PID
 environment variable).
+.It Fl L
+Makes
+.Nm
+listen on only the primary
+.Ev SSH_AUTH_SOCK
+instead of both that and
+.Ev SSH_AUTH_SOCK_LOCAL .
+This flag will also remove the default restrictions on adding smartcard or
+FIDO key by the main
+.Ev SSH_AUTH_SOCK .
 .It Fl P Ar allowed_providers
 Specify a pattern-list of acceptable paths for PKCS#11 provider and FIDO
 authenticator middleware shared libraries that may be used with the
@@ -203,8 +213,25 @@ When
 starts, it creates a
 .Ux Ns -domain
 socket and stores its pathname in this variable.
+This socket is used for communication with
+.Nm
+by
+.Xr ssh-add 1 ,
+.Xr ssh 1
+and other tools.
 It is accessible only to the current user,
 but is easily abused by root or another instance of the same user.
+.It Ev SSH_AUTH_SOCK_LOCAL
+Is another
+.Ux Ns -domain
+socket
+started by
+.Nm .
+It is intended to be used only locally and never forwarded by
+.Xr ssh 1 .
+This socket is permitted to perform additional operations, such as adding
+FIDO or smartcard keys that is by default restricted on the primary
+.Ev SSH_AUTH_SOCK .
 .El
 .Sh FILES
 .Bl -tag -width Ds
diff --git a/ssh-agent.c b/ssh-agent.c
index 86d771e..152b8d9 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -91,7 +91,9 @@
 typedef enum {
 	AUTH_UNUSED,
 	AUTH_SOCKET,
-	AUTH_CONNECTION
+	AUTH_SOCKET_LOCAL,
+	AUTH_CONNECTION,
+	AUTH_CONNECTION_LOCAL,
 } sock_type;
 
 typedef struct {
@@ -132,9 +134,10 @@ time_t parent_alive_interval = 0;
 /* pid of process for which cleanup_socket is applicable */
 pid_t cleanup_pid = 0;
 
-/* pathname and directory for AUTH_SOCKET */
-char socket_name[PATH_MAX];
-char socket_dir[PATH_MAX];
+/* pathname and directory for AUTH_SOCKET and AUTH_SOCKET_LOCAL */
+static char socket_name[PATH_MAX];
+static char localsocket_name[PATH_MAX];
+static char socket_dir[PATH_MAX];
 
 /* Pattern-list of allowed PKCS#11/Security key paths */
 static char *allowed_providers;
@@ -495,7 +498,7 @@ reaper(void)
 }
 
 static void
-process_add_identity(SocketEntry *e)
+process_add_identity(SocketEntry *e, int islocal)
 {
 	Identity *id;
 	int success = 0, confirm = 0;
@@ -586,6 +589,11 @@ process_add_identity(SocketEntry *e)
 			free(sk_provider);
 			goto send;
 		}
+		if (!islocal) {
+			error("Refusing add FIDO key from forwarded agent");
+			free(sk_provider);
+			goto send;
+		}
 		if (strcasecmp(sk_provider, "internal") == 0) {
 			debug("%s: internal provider", __func__);
 		} else {
@@ -712,7 +720,7 @@ no_identities(SocketEntry *e)
 
 #ifdef ENABLE_PKCS11
 static void
-process_add_smartcard_key(SocketEntry *e)
+process_add_smartcard_key(SocketEntry *e, int islocal)
 {
 	char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX];
 	char **comments = NULL;
@@ -751,6 +759,10 @@ process_add_smartcard_key(SocketEntry *e)
 			goto send;
 		}
 	}
+	if (!islocal) {
+		error("Refusing add of smartcard key from forwarded agent");
+		goto send;
+	}
 	if (realpath(provider, canonical_provider) == NULL) {
 		verbose("failed PKCS#11 add of \"%.100s\": realpath: %s",
 		    provider, strerror(errno));
@@ -843,7 +855,7 @@ send:
  * returns 1 on success, 0 for incomplete messages or -1 on error.
  */
 static int
-process_message(u_int socknum)
+process_message(u_int socknum, int islocal)
 {
 	u_int msg_len;
 	u_char type;
@@ -915,7 +927,7 @@ process_message(u_int socknum)
 		break;
 	case SSH2_AGENTC_ADD_IDENTITY:
 	case SSH2_AGENTC_ADD_ID_CONSTRAINED:
-		process_add_identity(e);
+		process_add_identity(e, islocal);
 		break;
 	case SSH2_AGENTC_REMOVE_IDENTITY:
 		process_remove_identity(e);
@@ -926,7 +938,7 @@ process_message(u_int socknum)
 #ifdef ENABLE_PKCS11
 	case SSH_AGENTC_ADD_SMARTCARD_KEY:
 	case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED:
-		process_add_smartcard_key(e);
+		process_add_smartcard_key(e, islocal);
 		break;
 	case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
 		process_remove_smartcard_key(e);
@@ -981,7 +993,7 @@ new_socket(sock_type type, int fd)
 }
 
 static int
-handle_socket_read(u_int socknum)
+handle_socket_read(u_int socknum, int islocal)
 {
 	struct sockaddr_un sunaddr;
 	socklen_t slen;
@@ -992,7 +1004,8 @@ handle_socket_read(u_int socknum)
 	slen = sizeof(sunaddr);
 	fd = accept(sockets[socknum].fd, (struct sockaddr *)&sunaddr, &slen);
 	if (fd == -1) {
-		error("accept from AUTH_SOCKET: %s", strerror(errno));
+		error("accept from %s auth socket: %s",
+		    islocal ? "local" : "remote", strerror(errno));
 		return -1;
 	}
 	if (getpeereid(fd, &euid, &egid) == -1) {
@@ -1006,12 +1019,12 @@ handle_socket_read(u_int socknum)
 		close(fd);
 		return -1;
 	}
-	new_socket(AUTH_CONNECTION, fd);
+	new_socket(islocal ? AUTH_CONNECTION_LOCAL : AUTH_CONNECTION, fd);
 	return 0;
 }
 
 static int
-handle_conn_read(u_int socknum)
+handle_conn_read(u_int socknum, int islocal)
 {
 	char buf[AGENT_RBUF_LEN];
 	ssize_t len;
@@ -1031,7 +1044,7 @@ handle_conn_read(u_int socknum)
 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
 	explicit_bzero(buf, sizeof(buf));
 	for (;;) {
-		if ((r = process_message(socknum)) == -1)
+		if ((r = process_message(socknum, islocal)) == -1)
 			return -1;
 		else if (r == 0)
 			break;
@@ -1069,15 +1082,22 @@ after_poll(struct pollfd *pfd, size_t npfd, u_int maxfds)
 {
 	size_t i;
 	u_int socknum, activefds = npfd;
+	SocketEntry *s;
 
 	for (i = 0; i < npfd; i++) {
 		if (pfd[i].revents == 0)
 			continue;
 		/* Find sockets entry */
 		for (socknum = 0; socknum < sockets_alloc; socknum++) {
-			if (sockets[socknum].type != AUTH_SOCKET &&
-			    sockets[socknum].type != AUTH_CONNECTION)
+			switch (sockets[socknum].type) {
+			case AUTH_SOCKET:
+			case AUTH_SOCKET_LOCAL:
+			case AUTH_CONNECTION:
+			case AUTH_CONNECTION_LOCAL:
+				break;
+			default:
 				continue;
+			}
 			if (pfd[i].fd == sockets[socknum].fd)
 				break;
 		}
@@ -1086,8 +1106,10 @@ after_poll(struct pollfd *pfd, size_t npfd, u_int maxfds)
 			continue;
 		}
 		/* Process events */
-		switch (sockets[socknum].type) {
+		s = sockets + socknum;
+		switch (s->type) {
 		case AUTH_SOCKET:
+		case AUTH_SOCKET_LOCAL:
 			if ((pfd[i].revents & (POLLIN|POLLERR)) == 0)
 				break;
 			if (npfd > maxfds) {
@@ -1095,12 +1117,15 @@ after_poll(struct pollfd *pfd, size_t npfd, u_int maxfds)
 				    "skipping accept", activefds, maxfds);
 				break;
 			}
-			if (handle_socket_read(socknum) == 0)
+			if (handle_socket_read(socknum,
+			    s->type == AUTH_SOCKET_LOCAL) == 0)
 				activefds++;
 			break;
 		case AUTH_CONNECTION:
+		case AUTH_CONNECTION_LOCAL:
 			if ((pfd[i].revents & (POLLIN|POLLERR)) != 0 &&
-			    handle_conn_read(socknum) != 0) {
+			    handle_conn_read(socknum,
+			    s->type == AUTH_CONNECTION_LOCAL) != 0) {
 				goto close_sock;
 			}
 			if ((pfd[i].revents & (POLLOUT|POLLHUP)) != 0 &&
@@ -1131,7 +1156,9 @@ prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp, u_int maxfds)
 	for (i = 0; i < sockets_alloc; i++) {
 		switch (sockets[i].type) {
 		case AUTH_SOCKET:
+		case AUTH_SOCKET_LOCAL:
 		case AUTH_CONNECTION:
+		case AUTH_CONNECTION_LOCAL:
 			npfd++;
 			break;
 		case AUTH_UNUSED:
@@ -1150,6 +1177,7 @@ prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp, u_int maxfds)
 	for (i = j = 0; i < sockets_alloc; i++) {
 		switch (sockets[i].type) {
 		case AUTH_SOCKET:
+		case AUTH_SOCKET_LOCAL:
 			if (npfd > maxfds) {
 				debug3("out of fds (active %zu >= limit %u); "
 				    "skipping arming listener", npfd, maxfds);
@@ -1161,6 +1189,7 @@ prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp, u_int maxfds)
 			j++;
 			break;
 		case AUTH_CONNECTION:
+		case AUTH_CONNECTION_LOCAL:
 			pfd[j].fd = sockets[i].fd;
 			pfd[j].revents = 0;
 			/*
@@ -1207,6 +1236,8 @@ cleanup_socket(void)
 	debug("%s: cleanup", __func__);
 	if (socket_name[0])
 		unlink(socket_name);
+	if (localsocket_name[0])
+		unlink(localsocket_name);
 	if (socket_dir[0])
 		rmdir(socket_dir);
 }
@@ -1247,9 +1278,9 @@ static void
 usage(void)
 {
 	fprintf(stderr,
-	    "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n"
+	    "usage: ssh-agent [-c | -s] [-DdL] [-a bind_address] [-E fingerprint_hash]\n"
 	    "                 [-P allowed_providers] [-t life]\n"
-	    "       ssh-agent [-a bind_address] [-E fingerprint_hash] [-P allowed_providers]\n"
+	    "       ssh-agent [-DdL] [-a bind_address] [-E fingerprint_hash] [-P allowed_providers]\n"
 	    "                 [-t life] command [arg ...]\n"
 	    "       ssh-agent [-c | -s] -k\n");
 	exit(1);
@@ -1259,7 +1290,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;
+	int L_flag = 0;
+	int sock, localsock = -1, ch, result, saved_errno;
 	char *shell, *format, *pidstr, *agentsocket = NULL;
 	struct rlimit rlim;
 	extern int optind;
@@ -1287,7 +1319,7 @@ main(int ac, char **av)
 	OpenSSL_add_all_algorithms();
 #endif
 
-	while ((ch = getopt(ac, av, "cDdksE:a:O:P:t:")) != -1) {
+	while ((ch = getopt(ac, av, "cDdkLsE:a:O:P:t:")) != -1) {
 		switch (ch) {
 		case 'E':
 			fingerprint_hash = ssh_digest_alg_by_name(optarg);
@@ -1302,6 +1334,9 @@ main(int ac, char **av)
 		case 'k':
 			k_flag++;
 			break;
+		case 'L':
+			L_flag = 1;
+			break;
 		case 'O':
 			if (strcmp(optarg, "no-restrict-websafe") == 0)
 				restrict_websafe  = 0;
@@ -1403,12 +1438,20 @@ main(int ac, char **av)
 			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);
+		if (!L_flag) {
+			snprintf(localsocket_name, sizeof localsocket_name,
+			    "%s/agent.local.%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 (!L_flag) {
+			snprintf(localsocket_name, sizeof localsocket_name,
+			    "%s.local", agentsocket);
+		}
 	}
 
 	/*
@@ -1422,6 +1465,14 @@ main(int ac, char **av)
 		*socket_name = '\0'; /* Don't unlink any existing file */
 		cleanup_exit(1);
 	}
+	if (*localsocket_name != '\0') {
+		localsock = unix_listener(localsocket_name,
+		    SSH_LISTEN_BACKLOG, 0);
+		if (localsock < 0) {
+			*localsocket_name = '\0';
+			cleanup_exit(1);
+		}
+	}
 	umask(prev_mask);
 
 	/*
@@ -1435,6 +1486,10 @@ main(int ac, char **av)
 		format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
 		printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
 		    SSH_AUTHSOCKET_ENV_NAME);
+		if (*localsocket_name != '\0') {
+			printf(format, SSH_AUTHSOCKET_LOCAL_ENV_NAME,
+			    localsocket_name, SSH_AUTHSOCKET_LOCAL_ENV_NAME);
+		}
 		printf("echo Agent pid %ld;\n", (long)parent_pid);
 		fflush(stdout);
 		goto skip;
@@ -1451,13 +1506,21 @@ main(int ac, char **av)
 			format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
 			printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
 			    SSH_AUTHSOCKET_ENV_NAME);
+			if (*localsocket_name != '\0') {
+				printf(format, SSH_AUTHSOCKET_LOCAL_ENV_NAME,
+				    localsocket_name,
+				    SSH_AUTHSOCKET_LOCAL_ENV_NAME);
+			}
 			printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
 			    SSH_AGENTPID_ENV_NAME);
 			printf("echo Agent pid %ld;\n", (long)pid);
 			exit(0);
 		}
 		if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 ||
-		    setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) {
+		    setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1 ||
+		    (*localsocket_name != '\0' &&
+		    setenv(SSH_AUTHSOCKET_LOCAL_ENV_NAME,
+		    localsocket_name, 1) == -1)) {
 			perror("setenv");
 			exit(1);
 		}
@@ -1491,7 +1554,13 @@ skip:
 #ifdef ENABLE_PKCS11
 	pkcs11_init(0);
 #endif
-	new_socket(AUTH_SOCKET, sock);
+	if (localsock != -1) {
+		new_socket(AUTH_SOCKET_LOCAL, localsock);
+		new_socket(AUTH_SOCKET, sock);
+	} else {
+		new_socket(AUTH_SOCKET_LOCAL, sock);
+	}
+
 	if (ac > 0)
 		parent_alive_interval = 10;
 	idtab_init();
diff --git a/ssh.h b/ssh.h
index e40f04a..146cecc 100644
--- a/ssh.h
+++ b/ssh.h
@@ -62,6 +62,12 @@
  */
 #define SSH_AUTHSOCKET_ENV_NAME "SSH_AUTH_SOCK"
 
+/*
+ * Name of the environment variable containing the pathname of the
+ * local authentication socket.
+ */
+#define SSH_AUTHSOCKET_LOCAL_ENV_NAME "SSH_AUTH_SOCK_LOCAL"
+
 /*
  * Environment variable for overwriting the default location of askpass
  */
-- 
2.28.0

-------------- next part --------------
From 378a659110b144dde0fd35d8dc0186f533f5947a Mon Sep 17 00:00:00 2001
From: Damien Miller <djm at mindrot.org>
Date: Thu, 10 Sep 2020 12:05:17 +1000
Subject: [PATCH 2/4] add ssh_get_authentication_socket_local()

This prefers the SSH_AUTH_SOCK_LOCAL socket, but will fall back
---
 authfd.c  | 13 +++++++++++++
 authfd.h  |  1 +
 ssh-add.c | 10 +---------
 3 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/authfd.c b/authfd.c
index ab5b2b7..861b9f5 100644
--- a/authfd.c
+++ b/authfd.c
@@ -132,6 +132,19 @@ ssh_get_authentication_socket(int *fdp)
 	return ssh_get_authentication_socket_path(authsocket, fdp);
 }
 
+/* Opens the authentication socket, preferring the privileged local socket */
+int
+ssh_get_authentication_socket_local(int *fdp)
+{
+	const char *authsocket;
+
+	if (fdp != NULL)
+		*fdp = -1;
+	if ((authsocket = getenv(SSH_AUTHSOCKET_LOCAL_ENV_NAME)) != NULL)
+		return ssh_get_authentication_socket_path(authsocket, fdp);
+	return ssh_get_authentication_socket(fdp);
+}
+
 /* Communicate with agent: send request and read reply */
 static int
 ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply)
diff --git a/authfd.h b/authfd.h
index 4fbf82f..fc81ecc 100644
--- a/authfd.h
+++ b/authfd.h
@@ -24,6 +24,7 @@ struct ssh_identitylist {
 };
 
 int	ssh_get_authentication_socket(int *fdp);
+int	ssh_get_authentication_socket_local(int *fdp);
 int	ssh_get_authentication_socket_path(const char *authsocket, int *fdp);
 void	ssh_close_authentication_socket(int sock);
 
diff --git a/ssh-add.c b/ssh-add.c
index 6ce1142..4e112dc 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -658,7 +658,6 @@ main(int argc, char **argv)
 	extern char *optarg;
 	extern int optind;
 	int agent_fd;
-	const char *sock_path;
 	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;
@@ -675,15 +674,8 @@ main(int argc, char **argv)
 
 	setvbuf(stdout, NULL, _IOLBF, 0);
 
-	if ((sock_path = getenv(SSH_AUTHSOCKET_LOCAL_ENV_NAME)) == NULL &&
-	    (sock_path = getenv(SSH_AUTHSOCKET_ENV_NAME)) == NULL) {
-		fprintf(stderr, "No $%s or $%s socket found\n",
-		    SSH_AUTHSOCKET_ENV_NAME, SSH_AUTHSOCKET_LOCAL_ENV_NAME);
-		exit(2);
-	}
-
 	/* First, get a connection to the authentication agent. */
-	switch (r = ssh_get_authentication_socket_path(sock_path, &agent_fd)) {
+	switch (r = ssh_get_authentication_socket_local(&agent_fd)) {
 	case 0:
 		break;
 	case SSH_ERR_AGENT_NOT_PRESENT:
-- 
2.28.0

-------------- next part --------------
From 16fbc4aeff8f896995c16134d892326e7872fd83 Mon Sep 17 00:00:00 2001
From: Damien Miller <djm at mindrot.org>
Date: Thu, 10 Sep 2020 12:08:43 +1000
Subject: [PATCH 3/4] prefer local agent socket for non-forwarding cases

---
 ssh-keygen.c  | 4 ++--
 sshconnect.c  | 2 +-
 sshconnect2.c | 4 ++--
 sshd.c        | 6 +++---
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/ssh-keygen.c b/ssh-keygen.c
index 703a5fb..fcc0b3e 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -1749,7 +1749,7 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent,
 		if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0)
 			fatal("Cannot load CA public key %s: %s",
 			    tmp, ssh_err(r));
-		if ((r = ssh_get_authentication_socket(&agent_fd)) != 0)
+		if ((r = ssh_get_authentication_socket_local(&agent_fd)) != 0)
 			fatal("Cannot use public key for CA signature: %s",
 			    ssh_err(r));
 		if ((r = ssh_fetch_identitylist(agent_fd, &agent_ids)) != 0)
@@ -2623,7 +2623,7 @@ sig_sign(const char *keypath, const char *sig_namespace, int argc, char **argv)
 		goto done;
 	}
 
-	if ((r = ssh_get_authentication_socket(&agent_fd)) != 0)
+	if ((r = ssh_get_authentication_socket_local(&agent_fd)) != 0)
 		debug("Couldn't get agent socket: %s", ssh_err(r));
 	else {
 		if ((r = ssh_agent_has_key(agent_fd, pubkey)) == 0)
diff --git a/sshconnect.c b/sshconnect.c
index f9323eb..00c4267 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -1400,7 +1400,7 @@ maybe_add_key_to_agent(const char *authfile, struct sshkey *private,
 	if (options.add_keys_to_agent == 0)
 		return;
 
-	if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) {
+	if ((r = ssh_get_authentication_socket_local(&auth_sock)) != 0) {
 		debug3("no authentication agent, not adding key");
 		return;
 	}
diff --git a/sshconnect2.c b/sshconnect2.c
index 2aca328..d7697c8 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1674,9 +1674,9 @@ pubkey_prepare(Authctxt *authctxt)
 		TAILQ_INSERT_TAIL(preferred, id, next);
 	}
 	/* list of keys supported by the agent */
-	if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) {
+	if ((r = ssh_get_authentication_socket_local(&agent_fd)) != 0) {
 		if (r != SSH_ERR_AGENT_NOT_PRESENT)
-			debug("%s: ssh_get_authentication_socket: %s",
+			debug("%s: ssh_get_authentication_socket_local: %s",
 			    __func__, ssh_err(r));
 	} else if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) {
 		if (r != SSH_ERR_AGENT_NO_IDENTITIES)
diff --git a/sshd.c b/sshd.c
index f944c7e..5088dee 100644
--- a/sshd.c
+++ b/sshd.c
@@ -465,7 +465,7 @@ privsep_preauth(struct ssh *ssh)
 
 		pmonitor->m_pid = pid;
 		if (have_agent) {
-			r = ssh_get_authentication_socket(&auth_sock);
+			r = ssh_get_authentication_socket_local(&auth_sock);
 			if (r != 0) {
 				error("Could not get agent socket: %s",
 				    ssh_err(r));
@@ -1685,7 +1685,7 @@ main(int ac, char **av)
 		if (strcmp(options.host_key_agent, SSH_AUTHSOCKET_ENV_NAME))
 			setenv(SSH_AUTHSOCKET_ENV_NAME,
 			    options.host_key_agent, 1);
-		if ((r = ssh_get_authentication_socket(NULL)) == 0)
+		if ((r = ssh_get_authentication_socket_local(NULL)) == 0)
 			have_agent = 1;
 		else
 			error("Could not connect to agent \"%s\": %s",
@@ -2066,7 +2066,7 @@ main(int ac, char **av)
 		if (privsep_preauth(ssh) == 1)
 			goto authenticated;
 	} else if (have_agent) {
-		if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) {
+		if ((r = ssh_get_authentication_socket_local(&auth_sock)) != 0) {
 			error("Unable to get agent socket: %s", ssh_err(r));
 			have_agent = 0;
 		}
-- 
2.28.0

-------------- next part --------------
From 0552c5c3c552bc18240eca9c77ae2e272df053d4 Mon Sep 17 00:00:00 2001
From: Damien Miller <djm at mindrot.org>
Date: Thu, 10 Sep 2020 13:10:53 +1000
Subject: [PATCH 4/4] implement unforwardable agent keys

This adds a new a do-not-forward at openssh.com key constraint that tells
the agent to only offer the key over the SSH_AUTH_SOCK_LOCAL socket
and never by the default SSH_AUTH_SOCK. This restriction may be set
when adding keys using "ssh-add -r key [...]"

ssh will only ever forward SSH_AUTH_SOCK unless explicitly instructed
so, unless the user overrides something, restricted keys will not be
usable off the host running ssh-agent.
---
 authfd.c     | 19 ++++++++----
 authfd.h     |  4 +--
 ssh-add.c    | 38 ++++++++++++++----------
 ssh-agent.c  | 83 ++++++++++++++++++++++++++++++++++++++--------------
 ssh.1        |  8 +++--
 sshconnect.c |  3 +-
 6 files changed, 107 insertions(+), 48 deletions(-)

diff --git a/authfd.c b/authfd.c
index 861b9f5..f1177d0 100644
--- a/authfd.c
+++ b/authfd.c
@@ -450,7 +450,7 @@ ssh_agent_sign(int sock, const struct sshkey *key,
 
 static int
 encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign,
-    const char *provider)
+    const char *provider, u_int local)
 {
 	int r;
 
@@ -476,6 +476,13 @@ encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign,
 		    (r = sshbuf_put_cstring(m, provider)) != 0)
 			goto out;
 	}
+	if (local != 0) {
+		if ((r = sshbuf_put_u8(m,
+		    SSH_AGENT_CONSTRAIN_EXTENSION)) != 0 ||
+		    (r = sshbuf_put_cstring(m,
+		    "do-not-forward at openssh.com")) != 0)
+			goto out;
+	}
 	r = 0;
  out:
 	return r;
@@ -488,10 +495,10 @@ encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign,
 int
 ssh_add_identity_constrained(int sock, struct sshkey *key,
     const char *comment, u_int life, u_int confirm, u_int maxsign,
-    const char *provider)
+    const char *provider, u_int local)
 {
 	struct sshbuf *msg;
-	int r, constrained = (life || confirm || maxsign || provider);
+	int r, constrained = (life || confirm || maxsign || provider || local);
 	u_char type;
 
 	if ((msg = sshbuf_new()) == NULL)
@@ -529,7 +536,7 @@ ssh_add_identity_constrained(int sock, struct sshkey *key,
 	}
 	if (constrained &&
 	    (r = encode_constraints(msg, life, confirm, maxsign,
-	    provider)) != 0)
+	    provider, local)) != 0)
 		goto out;
 	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
 		goto out;
@@ -585,7 +592,7 @@ ssh_remove_identity(int sock, const struct sshkey *key)
  */
 int
 ssh_update_card(int sock, int add, const char *reader_id, const char *pin,
-    u_int life, u_int confirm)
+    u_int life, u_int confirm, u_int local)
 {
 	struct sshbuf *msg;
 	int r, constrained = (life || confirm);
@@ -605,7 +612,7 @@ ssh_update_card(int sock, int add, const char *reader_id, const char *pin,
 	    (r = sshbuf_put_cstring(msg, pin)) != 0)
 		goto out;
 	if (constrained &&
-	    (r = encode_constraints(msg, life, confirm, 0, NULL)) != 0)
+	    (r = encode_constraints(msg, life, confirm, 0, NULL, local)) != 0)
 		goto out;
 	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
 		goto out;
diff --git a/authfd.h b/authfd.h
index fc81ecc..6888350 100644
--- a/authfd.h
+++ b/authfd.h
@@ -33,11 +33,11 @@ int	ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp);
 void	ssh_free_identitylist(struct ssh_identitylist *idl);
 int	ssh_add_identity_constrained(int sock, struct sshkey *key,
 	    const char *comment, u_int life, u_int confirm, u_int maxsign,
-	    const char *provider);
+	    const char *provider, u_int local);
 int	ssh_agent_has_key(int sock, const struct sshkey *key);
 int	ssh_remove_identity(int sock, const struct sshkey *key);
 int	ssh_update_card(int sock, int add, const char *reader_id,
-	    const char *pin, u_int life, u_int confirm);
+	    const char *pin, u_int life, u_int confirm, u_int local);
 int	ssh_remove_all_identities(int sock, int version);
 
 int	ssh_agent_sign(int sock, const struct sshkey *key,
diff --git a/ssh-add.c b/ssh-add.c
index 4e112dc..aae0ad7 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -225,7 +225,7 @@ delete_all(int agent_fd, int qflag)
 
 static int
 add_file(int agent_fd, const char *filename, int key_only, int qflag,
-    const char *skprovider)
+    int localonly, const char *skprovider)
 {
 	struct sshkey *private, *cert;
 	char *comment = NULL;
@@ -359,7 +359,7 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag,
 	}
 
 	if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
-	    lifetime, confirm, maxsign, skprovider)) == 0) {
+	    lifetime, confirm, maxsign, skprovider, localonly)) == 0) {
 		ret = 0;
 		if (!qflag) {
 			fprintf(stderr, "Identity added: %s (%s)\n",
@@ -412,7 +412,7 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag,
 	sshkey_free(cert);
 
 	if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
-	    lifetime, confirm, maxsign, skprovider)) != 0) {
+	    lifetime, confirm, maxsign, skprovider, localonly)) != 0) {
 		error("Certificate %s (%s) add failed: %s", certpath,
 		    private->cert->key_id, ssh_err(r));
 		goto out;
@@ -440,7 +440,7 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag,
 }
 
 static int
-update_card(int agent_fd, int add, const char *id, int qflag)
+update_card(int agent_fd, int add, const char *id, int qflag, int rflag)
 {
 	char *pin = NULL;
 	int r, ret = -1;
@@ -452,7 +452,7 @@ update_card(int agent_fd, int add, const char *id, int qflag)
 	}
 
 	if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin,
-	    lifetime, confirm)) == 0) {
+	    lifetime, confirm, rflag)) == 0) {
 		ret = 0;
 		if (!qflag) {
 			fprintf(stderr, "Card %s: %s\n",
@@ -575,7 +575,7 @@ lock_agent(int agent_fd, int lock)
 }
 
 static int
-load_resident_keys(int agent_fd, const char *skprovider, int qflag)
+load_resident_keys(int agent_fd, const char *skprovider, int qflag, int rflag)
 {
 	struct sshkey **keys;
 	size_t nkeys, i;
@@ -593,7 +593,7 @@ load_resident_keys(int agent_fd, const char *skprovider, int qflag)
 		    fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
 			fatal("%s: sshkey_fingerprint failed", __func__);
 		if ((r = ssh_add_identity_constrained(agent_fd, keys[i], "",
-		    lifetime, confirm, maxsign, skprovider)) != 0) {
+		    lifetime, confirm, maxsign, skprovider, rflag)) != 0) {
 			error("Unable to add key %s %s",
 			    sshkey_type(keys[i]), fp);
 			free(fp);
@@ -625,13 +625,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,
-    const char *skprovider)
+    int rflag, const char *skprovider)
 {
 	if (deleting) {
 		if (delete_file(agent_fd, file, key_only, qflag) == -1)
 			return -1;
 	} else {
-		if (add_file(agent_fd, file, key_only, qflag, skprovider) == -1)
+		if (add_file(agent_fd, file, key_only, qflag, rflag,
+		    skprovider) == -1)
 			return -1;
 	}
 	return 0;
@@ -660,7 +661,7 @@ main(int argc, char **argv)
 	int agent_fd;
 	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 xflag = 0, lflag = 0, Dflag = 0, qflag = 0, rflag = 0, Tflag = 0;
 	SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
 	LogLevel log_level = SYSLOG_LEVEL_INFO;
 
@@ -689,7 +690,7 @@ main(int argc, char **argv)
 
 	skprovider = getenv("SSH_SK_PROVIDER");
 
-	while ((ch = getopt(argc, argv, "vkKlLcdDTxXE:e:M:m:qs:S:t:")) != -1) {
+	while ((ch = getopt(argc, argv, "cDdKkLlrTvXxE:e:M:m:qS:s:t:")) != -1) {
 		switch (ch) {
 		case 'v':
 			if (log_level == SYSLOG_LEVEL_INFO)
@@ -755,6 +756,9 @@ main(int argc, char **argv)
 			deleting = 1;
 			pkcs11provider = optarg;
 			break;
+		case 'r':
+			rflag = 1;
+			break;
 		case 't':
 			if ((lifetime = convtime(optarg)) == -1 ||
 			    lifetime < 0 || (u_long)lifetime > UINT32_MAX) {
@@ -792,6 +796,10 @@ main(int argc, char **argv)
 			ret = 1;
 		goto done;
 	}
+	if (rflag && getenv(SSH_AUTHSOCKET_LOCAL_ENV_NAME) == NULL) {
+		fatal("Local-only restriction requested by no $%s agent "
+		    "socket present", SSH_AUTHSOCKET_LOCAL_ENV_NAME);
+	}
 
 	if (skprovider == NULL)
 		skprovider = "internal";
@@ -808,14 +816,14 @@ main(int argc, char **argv)
 	}
 	if (pkcs11provider != NULL) {
 		if (update_card(agent_fd, !deleting, pkcs11provider,
-		    qflag) == -1)
+		    qflag, rflag) == -1)
 			ret = 1;
 		goto done;
 	}
 	if (do_download) {
 		if (skprovider == NULL)
 			fatal("Cannot download keys without provider");
-		if (load_resident_keys(agent_fd, skprovider, qflag) != 0)
+		if (load_resident_keys(agent_fd, skprovider, qflag, rflag) != 0)
 			ret = 1;
 		goto done;
 	}
@@ -838,7 +846,7 @@ main(int argc, char **argv)
 			if (stat(buf, &st) == -1)
 				continue;
 			if (do_file(agent_fd, deleting, key_only, buf,
-			    qflag, skprovider) == -1)
+			    qflag, rflag, skprovider) == -1)
 				ret = 1;
 			else
 				count++;
@@ -848,7 +856,7 @@ main(int argc, char **argv)
 	} else {
 		for (i = 0; i < argc; i++) {
 			if (do_file(agent_fd, deleting, key_only,
-			    argv[i], qflag, skprovider) == -1)
+			    argv[i], qflag, rflag, skprovider) == -1)
 				ret = 1;
 		}
 	}
diff --git a/ssh-agent.c b/ssh-agent.c
index 152b8d9..f69c67b 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -114,6 +114,7 @@ typedef struct identity {
 	char *provider;
 	time_t death;
 	u_int confirm;
+	int localonly;
 	char *sk_provider;
 } Identity;
 
@@ -232,18 +233,35 @@ send_status(SocketEntry *e, int success)
 
 /* send list of supported public keys to 'client' */
 static void
-process_request_identities(SocketEntry *e)
+process_request_identities(SocketEntry *e, int islocal)
 {
 	Identity *id;
 	struct sshbuf *msg;
 	int r;
+	u_int ntotal = 0, nentries = 0;
 
 	if ((msg = sshbuf_new()) == NULL)
 		fatal("%s: sshbuf_new failed", __func__);
+
+	/* count visible keys */
+	TAILQ_FOREACH(id, &idtab->idlist, next) {
+		ntotal++;
+		if (id->localonly && !islocal)
+			continue;
+		nentries++;
+	}
+	debug2("%s: start local=%d, returning %u/%u", __func__, islocal,
+	    nentries, ntotal);
+
+	/* reply header */
 	if ((r = sshbuf_put_u8(msg, SSH2_AGENT_IDENTITIES_ANSWER)) != 0 ||
-	    (r = sshbuf_put_u32(msg, idtab->nentries)) != 0)
+	    (r = sshbuf_put_u32(msg, nentries)) != 0)
 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
+	/* append visible keys */
 	TAILQ_FOREACH(id, &idtab->idlist, next) {
+		if (id->localonly && !islocal)
+			continue;
 		if ((r = sshkey_puts_opts(id->key, msg, SSHKEY_SERIALIZE_INFO))
 		     != 0 ||
 		    (r = sshbuf_put_cstring(msg, id->comment)) != 0) {
@@ -351,7 +369,7 @@ check_websafe_message_contents(struct sshkey *key,
 
 /* ssh2 only */
 static void
-process_sign_request2(SocketEntry *e)
+process_sign_request2(SocketEntry *e, int islocal)
 {
 	const u_char *data;
 	u_char *signature = NULL;
@@ -372,11 +390,15 @@ process_sign_request2(SocketEntry *e)
 		error("%s: couldn't parse request: %s", __func__, ssh_err(r));
 		goto send;
 	}
-
 	if ((id = lookup_identity(key)) == NULL) {
 		verbose("%s: %s key not found", __func__, sshkey_type(key));
 		goto send;
 	}
+	if (id->localonly && !islocal) {
+		verbose("%s: attempt to use local %s key via remote agent",
+		    __func__, sshkey_type(key));
+		goto send;
+	}
 	if (id->confirm && confirm_key(id) != 0) {
 		verbose("%s: user refused key", __func__);
 		goto send;
@@ -425,7 +447,7 @@ process_sign_request2(SocketEntry *e)
 
 /* shared */
 static void
-process_remove_identity(SocketEntry *e)
+process_remove_identity(SocketEntry *e, int islocal)
 {
 	int r, success = 0;
 	struct sshkey *key = NULL;
@@ -439,6 +461,11 @@ process_remove_identity(SocketEntry *e)
 		debug("%s: key not found", __func__);
 		goto done;
 	}
+	if (id->localonly && !islocal) {
+		verbose("%s: attempt to remove local %s key via remote agent",
+		    __func__, sshkey_type(key));
+		goto done;
+	}
 	/* We have this key, free it. */
 	if (idtab->nentries < 1)
 		fatal("%s: internal error: nentries %d",
@@ -501,9 +528,9 @@ static void
 process_add_identity(SocketEntry *e, int islocal)
 {
 	Identity *id;
-	int success = 0, confirm = 0;
+	int success = 0, confirm = 0, localonly = 0;
 	u_int seconds = 0, maxsign;
-	char *fp, *comment = NULL, *ext_name = NULL, *sk_provider = NULL;
+	char *fp = NULL, *comment = NULL, *ext_name = NULL, *sk_provider = NULL;
 	char canonical_provider[PATH_MAX];
 	time_t death = 0;
 	struct sshkey *k = NULL;
@@ -564,6 +591,13 @@ process_add_identity(SocketEntry *e, int islocal)
 					    __func__, ext_name, ssh_err(r));
 					goto err;
 				}
+			} else if (strcmp(ext_name, "do-not-forward at openssh.com") == 0) {
+				if (!islocal) {
+					error("Refusing add local-only key on "
+					    "forwarded connection");
+					goto send;
+				}
+				localonly = 1;
 			} else {
 				error("%s: unsupported constraint \"%s\"",
 				    __func__, ext_name);
@@ -574,11 +608,8 @@ process_add_identity(SocketEntry *e, int islocal)
 		default:
 			error("%s: Unknown constraint %d", __func__, ctype);
  err:
-			free(sk_provider);
 			free(ext_name);
 			sshbuf_reset(e->request);
-			free(comment);
-			sshkey_free(k);
 			goto send;
 		}
 	}
@@ -586,12 +617,10 @@ process_add_identity(SocketEntry *e, int islocal)
 		if (!sshkey_is_sk(k)) {
 			error("Cannot add provider: %s is not an "
 			    "authenticator-hosted key", sshkey_type(k));
-			free(sk_provider);
 			goto send;
 		}
 		if (!islocal) {
 			error("Refusing add FIDO key from forwarded agent");
-			free(sk_provider);
 			goto send;
 		}
 		if (strcasecmp(sk_provider, "internal") == 0) {
@@ -601,7 +630,6 @@ process_add_identity(SocketEntry *e, int islocal)
 				verbose("failed provider \"%.100s\": "
 				    "realpath: %s", sk_provider,
 				    strerror(errno));
-				free(sk_provider);
 				goto send;
 			}
 			free(sk_provider);
@@ -610,7 +638,6 @@ process_add_identity(SocketEntry *e, int islocal)
 			    allowed_providers, 0) != 1) {
 				error("Refusing add key: "
 				    "provider %s not allowed", sk_provider);
-				free(sk_provider);
 				goto send;
 			}
 		}
@@ -638,16 +665,26 @@ process_add_identity(SocketEntry *e, int islocal)
 	id->comment = comment;
 	id->death = death;
 	id->confirm = confirm;
+	id->localonly = localonly;
 	id->sk_provider = sk_provider;
 
 	if ((fp = sshkey_fingerprint(k, SSH_FP_HASH_DEFAULT,
 	    SSH_FP_DEFAULT)) == NULL)
 		fatal("%s: sshkey_fingerprint failed", __func__);
 	debug("%s: add %s %s \"%.100s\" (life: %u) (confirm: %u) "
-	    "(provider: %s)", __func__, sshkey_ssh_name(k), fp, comment,
-	    seconds, confirm, sk_provider == NULL ? "none" : sk_provider);
-	free(fp);
+	    "(local: %s) (provider: %s)", __func__, sshkey_ssh_name(k), fp,
+	    comment, seconds, confirm, localonly ? "YES" : "NO",
+	    sk_provider == NULL ? "none" : sk_provider);
+
+	/* transferred */
+	k = NULL;
+	comment = NULL;
+	sk_provider = NULL;
 send:
+	free(fp);
+	sshkey_free(k);
+	free(comment);
+	free(sk_provider);
 	send_status(e, success);
 }
 
@@ -809,7 +846,7 @@ send:
 }
 
 static void
-process_remove_smartcard_key(SocketEntry *e)
+process_remove_smartcard_key(SocketEntry *e, int islocal)
 {
 	char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX];
 	int r, success = 0;
@@ -831,6 +868,8 @@ process_remove_smartcard_key(SocketEntry *e)
 	debug("%s: remove %.100s", __func__, canonical_provider);
 	for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) {
 		nxt = TAILQ_NEXT(id, next);
+		if (id->localonly && !islocal)
+			continue;
 		/* Skip file--based keys */
 		if (id->provider == NULL)
 			continue;
@@ -920,17 +959,17 @@ process_message(u_int socknum, int islocal)
 		break;
 	/* ssh2 */
 	case SSH2_AGENTC_SIGN_REQUEST:
-		process_sign_request2(e);
+		process_sign_request2(e, islocal);
 		break;
 	case SSH2_AGENTC_REQUEST_IDENTITIES:
-		process_request_identities(e);
+		process_request_identities(e, islocal);
 		break;
 	case SSH2_AGENTC_ADD_IDENTITY:
 	case SSH2_AGENTC_ADD_ID_CONSTRAINED:
 		process_add_identity(e, islocal);
 		break;
 	case SSH2_AGENTC_REMOVE_IDENTITY:
-		process_remove_identity(e);
+		process_remove_identity(e, islocal);
 		break;
 	case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
 		process_remove_all_identities(e);
@@ -941,7 +980,7 @@ process_message(u_int socknum, int islocal)
 		process_add_smartcard_key(e, islocal);
 		break;
 	case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
-		process_remove_smartcard_key(e);
+		process_remove_smartcard_key(e, islocal);
 		break;
 #endif /* ENABLE_PKCS11 */
 	default:
diff --git a/ssh.1 b/ssh.1
index 5553178..9cb3ec8 100644
--- a/ssh.1
+++ b/ssh.1
@@ -1428,10 +1428,14 @@ then the askpass program will be used for all passphrase input regardless
 of whether
 .Ev DISPLAY
 is set.
-.It Ev SSH_AUTH_SOCK
-Identifies the path of a
+.It Ev SSH_AUTH_SOCK | Ev SSH_AUTH_SOCK_LOCAL
+Identifies paths of a
 .Ux Ns -domain
 socket used to communicate with the agent.
+.Ev SSH_AUTH_SOCK_LOCAL
+is preferred for user authentication.
+.Ev SSH_AUTH_SOCK
+is used for forwarding.
 .It Ev SSH_CONNECTION
 Identifies the client and server ends of the connection.
 The variable contains
diff --git a/sshconnect.c b/sshconnect.c
index 00c4267..b9274d6 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -1416,7 +1416,8 @@ maybe_add_key_to_agent(const char *authfile, struct sshkey *private,
 	if ((r = ssh_add_identity_constrained(auth_sock, private,
 	    comment == NULL ? authfile : comment,
 	    options.add_keys_to_agent_lifespan,
-	    (options.add_keys_to_agent == 3), 0, skprovider)) == 0)
+	    (options.add_keys_to_agent == 3), 0, skprovider,
+	    getenv(SSH_AUTHSOCKET_LOCAL_ENV_NAME) == NULL)) == 0)
 		debug("identity added to agent: %s", authfile);
 	else
 		debug("could not add identity to agent: %s (%d)", authfile, r);
-- 
2.28.0



More information about the openssh-unix-dev mailing list