diff options
author | Damien Miller <djm@mindrot.org> | 2024-12-04 14:01:33 +0100 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2024-12-04 14:01:33 +0100 |
commit | 66e986880b2472fefaad781f10113b138b65ff27 (patch) | |
tree | 518f1b80b72a5a6270eb6bca4617db9b4199fc91 | |
parent | Update readme files to better reflect reality. (diff) | |
download | openssh-66e986880b2472fefaad781f10113b138b65ff27.tar.xz openssh-66e986880b2472fefaad781f10113b138b65ff27.zip |
Support systemd-style socket activation in agent
Adds support for systemd LISTEN_PID/LISTEN_FDS socket activation to
ssh-agent. Activated when these environment variables are set and
the agent is started with the -d or -D option and no socket path
is set.
Based on GHPR502 by Daniel Kahn Gillmor, ok dtucker
-rw-r--r-- | ssh-agent.1 | 26 | ||||
-rw-r--r-- | ssh-agent.c | 82 |
2 files changed, 79 insertions, 29 deletions
diff --git a/ssh-agent.1 b/ssh-agent.1 index 2f5b091ee..062e87bbf 100644 --- a/ssh-agent.1 +++ b/ssh-agent.1 @@ -190,7 +190,7 @@ The agent exits automatically when the command given on the command line terminates. .El .Pp -There are two main ways to get an agent set up. +There are three main ways to get an agent set up. The first is at the start of an X session, where all other windows or programs are started as children of the .Nm @@ -208,11 +208,33 @@ it prints the shell commands required to set its environment variables, which in turn can be evaluated in the calling shell, for example .Cm eval `ssh-agent -s` . .Pp -In both cases, +In both of these cases, .Xr ssh 1 looks at these environment variables and uses them to establish a connection to the agent. .Pp +The third way to run +.Nm +is via socket activation from a supervising process, such as systemd. +In this mode, the supervising process creates the listening socket and +is responsible for starting +.Nm +as needed, and also for communicating the location of the socket listener +to other programs in the user's session. +Socket activation is used when +.Nm +is started with either of the +.Fl d +or +.Fl D +flags, so socket listening address specified by the +.Fl a +flag, and both the +.Ev LISTEN_FDS +and +.Ev LISTEN_PID +environment variables correctly supplied by the supervising process. +.Pp The agent initially does not have any private keys. Keys are added using .Xr ssh-add 1 diff --git a/ssh-agent.c b/ssh-agent.c index 96c25b9d5..48973b2c1 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -2215,8 +2215,9 @@ 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; @@ -2329,8 +2330,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", @@ -2368,33 +2367,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 @@ -2404,11 +2429,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(); |