diff options
Diffstat (limited to 'crypto/openssh/ssh-agent.c')
-rw-r--r-- | crypto/openssh/ssh-agent.c | 136 |
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); |