aboutsummaryrefslogtreecommitdiff
path: root/crypto/openssh/ssh-agent.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/openssh/ssh-agent.c')
-rw-r--r--crypto/openssh/ssh-agent.c136
1 files changed, 101 insertions, 35 deletions
diff --git a/crypto/openssh/ssh-agent.c b/crypto/openssh/ssh-agent.c
index 5ea283ddaf29..bae8810d18e4 100644
--- a/crypto/openssh/ssh-agent.c
+++ b/crypto/openssh/ssh-agent.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-agent.c,v 1.306 2024/03/09 05:12:13 djm Exp $ */
+/* $OpenBSD: ssh-agent.c,v 1.310 2025/02/18 08:02:48 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -94,6 +94,9 @@
#ifndef DEFAULT_ALLOWED_PROVIDERS
# define DEFAULT_ALLOWED_PROVIDERS "/usr/lib*/*,/usr/local/lib*/*"
#endif
+#ifndef DEFAULT_WEBSAFE_ALLOWLIST
+# define DEFAULT_WEBSAFE_ALLOWLIST "ssh:*"
+#endif
/* Maximum accepted message length */
#define AGENT_MAX_LEN (256*1024)
@@ -162,7 +165,8 @@ int max_fd = 0;
pid_t parent_pid = -1;
time_t parent_alive_interval = 0;
-sig_atomic_t signalled = 0;
+static sig_atomic_t signalled_exit;
+static sig_atomic_t signalled_keydrop;
/* pid of process for which cleanup_socket is applicable */
pid_t cleanup_pid = 0;
@@ -197,6 +201,7 @@ static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
/* Refuse signing of non-SSH messages for web-origin FIDO keys */
static int restrict_websafe = 1;
+static char *websafe_allowlist;
/*
* Client connection count; incremented in new_socket() and decremented in
@@ -942,7 +947,8 @@ process_sign_request2(SocketEntry *e)
}
if (sshkey_is_sk(id->key)) {
if (restrict_websafe &&
- strncmp(id->key->sk_application, "ssh:", 4) != 0 &&
+ match_pattern_list(id->key->sk_application,
+ websafe_allowlist, 0) != 1 &&
!check_websafe_message_contents(key, data)) {
/* error already logged */
goto send;
@@ -1039,7 +1045,7 @@ process_remove_identity(SocketEntry *e)
}
static void
-process_remove_all_identities(SocketEntry *e)
+remove_all_identities(void)
{
Identity *id;
@@ -1053,6 +1059,12 @@ process_remove_all_identities(SocketEntry *e)
/* Mark that there are no identities. */
idtab->nentries = 0;
+}
+
+static void
+process_remove_all_identities(SocketEntry *e)
+{
+ remove_all_identities();
/* Send success. */
send_status(e, 1);
@@ -1725,6 +1737,10 @@ process_ext_session_bind(SocketEntry *e)
error_fr(r, "parse");
goto out;
}
+ if (sshbuf_len(sid) > AGENT_MAX_SID_LEN) {
+ error_f("session ID too long");
+ goto out;
+ }
if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT,
SSH_FP_DEFAULT)) == NULL)
fatal_f("fingerprint failed");
@@ -2187,7 +2203,13 @@ cleanup_exit(int i)
static void
cleanup_handler(int sig)
{
- signalled = sig;
+ signalled_exit = sig;
+}
+
+static void
+keydrop_handler(int sig)
+{
+ signalled_keydrop = sig;
}
static void
@@ -2220,8 +2242,10 @@ 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, *fdstr, *pidstr, *agentsocket = NULL;
+ const char *errstr = NULL;
+ const char *ccp;
#ifdef HAVE_SETRLIMIT
struct rlimit rlim;
#endif
@@ -2275,7 +2299,12 @@ main(int ac, char **av)
restrict_websafe = 0;
else if (strcmp(optarg, "allow-remote-pkcs11") == 0)
remote_add_provider = 1;
- else
+ else if ((ccp = strprefix(optarg,
+ "websafe-allow=", 0)) != NULL) {
+ if (websafe_allowlist != NULL)
+ fatal("websafe-allow already set");
+ websafe_allowlist = xstrdup(ccp);
+ } else
fatal("Unknown -O option");
break;
case 'P':
@@ -2322,6 +2351,8 @@ main(int ac, char **av)
if (allowed_providers == NULL)
allowed_providers = xstrdup(DEFAULT_ALLOWED_PROVIDERS);
+ if (websafe_allowlist == NULL)
+ websafe_allowlist = xstrdup(DEFAULT_WEBSAFE_ALLOWLIST);
if (ac == 0 && !c_flag && !s_flag) {
shell = getenv("SHELL");
@@ -2330,8 +2361,6 @@ main(int ac, char **av)
c_flag = 1;
}
if (k_flag) {
- const char *errstr = NULL;
-
pidstr = getenv(SSH_AGENTPID_ENV_NAME);
if (pidstr == NULL) {
fprintf(stderr, "%s not set, cannot kill agent\n",
@@ -2369,33 +2398,59 @@ 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);
+ /* Has the socket been provided via socket activation? */
+ if (agentsocket == NULL && ac == 0 && (d_flag || D_flag) &&
+ (pidstr = getenv("LISTEN_PID")) != NULL &&
+ (fdstr = getenv("LISTEN_FDS")) != NULL) {
+ if (strcmp(fdstr, "1") != 0) {
+ fatal("unexpected LISTEN_FDS contents "
+ "(want: \"1\" got\"%s\"", fdstr);
}
- snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir,
+ if (fcntl(3, F_GETFL) == -1)
+ fatal("LISTEN_FDS set but fd 3 unavailable");
+ pid = (int)strtonum(pidstr, 1, INT_MAX, &errstr);
+ if (errstr != NULL)
+ fatal("invalid LISTEN_PID: %s", errstr);
+ if (pid != getpid())
+ fatal("bad LISTEN_PID: %d vs pid %d", pid, getpid());
+ debug("using socket activation on fd=3");
+ sock = 3;
+ }
+
+ /* Otherwise, create private directory for agent socket */
+ if (sock == -1) {
+ if (agentsocket == NULL) {
+ 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);
- } else {
- /* Try to use specified agent socket */
- socket_dir[0] = '\0';
- strlcpy(socket_name, agentsocket, sizeof socket_name);
+ } else {
+ /* Try to use specified agent socket */
+ socket_dir[0] = '\0';
+ strlcpy(socket_name, agentsocket, sizeof socket_name);
+ }
}
+ closefrom(sock == -1 ? STDERR_FILENO + 1 : sock + 1);
+
/*
* 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 == -1) {
+ 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 existing file */
+ cleanup_exit(1);
+ }
+ umask(prev_mask);
}
- umask(prev_mask);
/*
* Fork, and have the parent execute the command, if any, or present
@@ -2405,11 +2460,14 @@ main(int ac, char **av)
log_init(__progname,
d_flag ? SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_INFO,
SYSLOG_FACILITY_AUTH, 1);
- format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
- printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
- SSH_AUTHSOCKET_ENV_NAME);
- printf("echo Agent pid %ld;\n", (long)parent_pid);
- fflush(stdout);
+ if (socket_name[0] != '\0') {
+ format = c_flag ?
+ "setenv %s %s;\n" : "%s=%s; export %s;\n";
+ printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
+ SSH_AUTHSOCKET_ENV_NAME);
+ printf("echo Agent pid %ld;\n", (long)parent_pid);
+ fflush(stdout);
+ }
goto skip;
}
pid = fork();
@@ -2474,11 +2532,13 @@ skip:
ssh_signal(SIGINT, (d_flag | D_flag) ? cleanup_handler : SIG_IGN);
ssh_signal(SIGHUP, cleanup_handler);
ssh_signal(SIGTERM, cleanup_handler);
+ ssh_signal(SIGUSR1, keydrop_handler);
sigemptyset(&nsigset);
sigaddset(&nsigset, SIGINT);
sigaddset(&nsigset, SIGHUP);
sigaddset(&nsigset, SIGTERM);
+ sigaddset(&nsigset, SIGUSR1);
if (pledge("stdio rpath cpath unix id proc exec", NULL) == -1)
fatal("%s: pledge: %s", __progname, strerror(errno));
@@ -2486,10 +2546,16 @@ skip:
while (1) {
sigprocmask(SIG_BLOCK, &nsigset, &osigset);
- if (signalled != 0) {
- logit("exiting on signal %d", (int)signalled);
+ if (signalled_exit != 0) {
+ logit("exiting on signal %d", (int)signalled_exit);
cleanup_exit(2);
}
+ if (signalled_keydrop) {
+ logit("signal %d received; removing all keys",
+ signalled_keydrop);
+ remove_all_identities();
+ signalled_keydrop = 0;
+ }
ptimeout_init(&timeout);
prepare_poll(&pfd, &npfd, &timeout, maxfds);
result = ppoll(pfd, npfd, ptimeout_get_tsp(&timeout), &osigset);