diff options
-rw-r--r-- | usr.bin/ssh/ssh-agent.1 | 61 | ||||
-rw-r--r-- | usr.bin/ssh/ssh-agent.c | 177 | ||||
-rw-r--r-- | usr.bin/ssh/ssh.h | 6 |
3 files changed, 194 insertions, 50 deletions
diff --git a/usr.bin/ssh/ssh-agent.1 b/usr.bin/ssh/ssh-agent.1 index 286c9d94d61..8b9504fa5f5 100644 --- a/usr.bin/ssh/ssh-agent.1 +++ b/usr.bin/ssh/ssh-agent.1 @@ -1,16 +1,16 @@ +.\" $OpenBSD: ssh-agent.1,v 1.7 1999/10/28 08:43:10 markus Exp $ +.\" .\" -*- nroff -*- .\" .\" ssh-agent.1 .\" .\" Author: Tatu Ylonen <ylo@cs.hut.fi> -.\" +pp.\" .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland .\" All rights reserved .\" .\" Created: Sat Apr 23 20:10:43 1995 ylo .\" -.\" $Id: ssh-agent.1,v 1.6 1999/10/17 00:31:06 deraadt Exp $ -.\" .Dd September 25, 1999 .Dt SSH-AGENT 1 .Os @@ -19,22 +19,47 @@ .Nd authentication agent .Sh SYNOPSIS .Nm ssh-agent +.Op Fl c Li | Fl s +.Op Fl k +.Oo .Ar command +.Op Ar args ... +.Oc .Sh DESCRIPTION .Nm is a program to hold authentication private keys. The idea is that .Nm is started in the beginning of an X-session or a login session, and -all other windows or programs are started as children of the ssh-agent -program (the -.Ar command -normally starts X or is the user shell). Programs started under -the agent inherit a connection to the agent, and the agent is -automatically used for RSA authentication when logging to other +all other windows or programs are started as clients to the ssh-agent +program. Through use of environment variables the agent can be located +and automatically used for RSA authentication when logging in to other machines using .Xr ssh 1 . .Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl c +Generate C-shell commands on +.Dv stdout . +This is the default if +.Ev SHELL +looks like it's a csh style of shell. +.It Fl s +Generate Bourne shell commands on +.Dv stdout . +This is the default if +.Ev SHELL +does not look like it's a csh style of shell. +.It Fl k +Kill the current agent (given by the +.Ev SSH_AGENT_PID +environment variable). +.El +.Pp +If a commandline is given, this is executed as a subprocess of the agent. +When the command dies, so does the agent. +.Pp The agent initially does not have any private keys. Keys are added using .Xr ssh-add 1 . @@ -58,9 +83,19 @@ However, the connection to the agent is forwarded over SSH remote logins, and the user can thus use the privileges given by the identities anywhere in the network in a secure way. .Pp -A connection to the agent is inherited by child programs: +There are two main ways to get an agent setup: Either you let the agent +start a new subcommand into which some environment variables are exported, or +you let the agent print the needed shell commands (either +.Xr sh 1 +or +.Xr csh 1 +syntax can be generated) which can be evalled in the calling shell. +Later +.Xr ssh 1 +look at these variables and use them to establish a connection to the agent. +.Pp A unix-domain socket is created -.Pq Pa /tmp/ssh-XXXX/agent.<pid> , +.Pq Pa /tmp/ssh-XXXXXXXX/agent.<pid> , and the name of this socket is stored in the .Ev SSH_AUTH_SOCK environment @@ -68,6 +103,10 @@ variable. The socket is made accessible only to the current user. This method is easily abused by root or another instance of the same user. .Pp +The +.Ev SSH_AGENT_PID +environment variable holds the agent's PID. +.Pp The agent exits automatically when the command given on the command line terminates. .Sh FILES diff --git a/usr.bin/ssh/ssh-agent.c b/usr.bin/ssh/ssh-agent.c index fbf02e3e875..d6f48740ca7 100644 --- a/usr.bin/ssh/ssh-agent.c +++ b/usr.bin/ssh/ssh-agent.c @@ -1,3 +1,5 @@ +/* $OpenBSD: ssh-agent.c,v 1.15 1999/10/28 08:43:10 markus Exp $ */ + /* ssh-agent.c @@ -14,7 +16,7 @@ The authentication agent program. */ #include "includes.h" -RCSID("$Id: ssh-agent.c,v 1.14 1999/10/27 23:34:53 markus Exp $"); +RCSID("$OpenBSD: ssh-agent.c,v 1.15 1999/10/28 08:43:10 markus Exp $"); #include "ssh.h" #include "rsa.h" @@ -476,17 +478,39 @@ check_parent_exists(int sig) alarm(10); } -void cleanup_socket(void) { +void +cleanup_socket(void) +{ remove(socket_name); rmdir(socket_dir); } +void +cleanup_exit(int i) +{ + cleanup_socket(); + exit(i); +} + +void +usage() +{ + extern char *__progname; + + fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION); + fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n", + __progname); + exit(1); +} + int main(int ac, char **av) { fd_set readset, writeset; - int sock; + int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch; struct sockaddr_un sunaddr; + pid_t pid; + char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid]; /* check if RSA support exists */ if (rsa_alive() == 0) { @@ -497,11 +521,66 @@ main(int ac, char **av) exit(1); } - if (ac < 2) + while ((ch = getopt(ac, av, "cks")) != -1) { - fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION); - fprintf(stderr, "Usage: %s command\n", av[0]); - exit(1); + switch (ch) + { + case 'c': + if (s_flag) + usage(); + c_flag++; + break; + case 'k': + k_flag++; + break; + case 's': + if (c_flag) + usage(); + s_flag++; + break; + default: + usage(); + } + } + ac -= optind; + av += optind; + + if (ac > 0 && (c_flag || k_flag || s_flag)) + usage(); + + if (ac == 0 && !c_flag && !k_flag && !s_flag) + { + shell = getenv("SHELL"); + if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0) + c_flag = 1; + } + + if (k_flag) + { + pidstr = getenv(SSH_AGENTPID_ENV_NAME); + if (pidstr == NULL) + { + fprintf(stderr, "%s not set, cannot kill agent\n", + SSH_AGENTPID_ENV_NAME); + exit(1); + } + pid = atoi(pidstr); + if (pid < 1) /* XXX PID_MAX check too */ + { + fprintf(stderr, "%s=\"%s\", which is not a good PID\n", + SSH_AGENTPID_ENV_NAME, pidstr); + exit(1); + } + if (kill(pid, SIGTERM) == -1) + { + perror("kill"); + exit(1); + } + format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; + printf(format, SSH_AUTHSOCKET_ENV_NAME); + printf(format, SSH_AGENTPID_ENV_NAME); + printf("echo Agent pid %d killed;\n", pid); + exit(0); } parent_pid = getpid(); @@ -512,38 +591,16 @@ main(int ac, char **av) perror("mkdtemp: private socket dir"); exit(1); } - snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir, parent_pid); - - /* Fork, and have the parent execute the command. The child continues as - the authentication agent. */ - if (fork() != 0) - { /* Parent - execute the given command. */ - setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1); - execvp(av[1], av + 1); - perror(av[1]); - exit(1); - } - - if (atexit(cleanup_socket) < 0) { - perror("atexit"); - cleanup_socket(); - exit(1); - } - - /* Create a new session and process group */ - if (setsid() < 0) { - perror("setsid failed"); - exit(1); - } - - /* Ignore if a client dies while we are sending a reply */ - signal(SIGPIPE, SIG_IGN); + snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir, + parent_pid); + /* Create socket early so it will exist before command gets run from + the parent. */ sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { perror("socket"); - exit(1); + cleanup_exit(1); } memset(&sunaddr, 0, sizeof(sunaddr)); sunaddr.sun_family = AF_UNIX; @@ -551,18 +608,63 @@ main(int ac, char **av) if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { perror("bind"); - exit(1); + cleanup_exit(1); } if (listen(sock, 5) < 0) { perror("listen"); + cleanup_exit(1); + } + + /* Fork, and have the parent execute the command, if any, or present the + socket data. The child continues as the authentication agent. */ + pid = fork(); + if (pid == -1) + { + perror("fork"); exit(1); } + if (pid != 0) + { /* Parent - execute the given command. */ + close(sock); + snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid); + if (ac == 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(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, + SSH_AGENTPID_ENV_NAME); + printf("echo Agent pid %d;\n", pid); + exit(0); + } + + setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1); + setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1); + execvp(av[0], av); + perror(av[0]); + exit(1); + } + + close(0); + close(1); + close(2); + + if (ac == 0 && setsid() == -1) + cleanup_exit(1); + + if (atexit(cleanup_socket) < 0) + cleanup_exit(1); + new_socket(AUTH_SOCKET, sock); - signal(SIGALRM, check_parent_exists); - alarm(10); + if (ac > 0) + { + signal(SIGALRM, check_parent_exists); + alarm(10); + } signal(SIGINT, SIG_IGN); + signal(SIGPIPE, SIG_IGN); while (1) { FD_ZERO(&readset); @@ -572,7 +674,6 @@ main(int ac, char **av) { if (errno == EINTR) continue; - perror("select"); exit(1); } after_select(&readset, &writeset); diff --git a/usr.bin/ssh/ssh.h b/usr.bin/ssh/ssh.h index 099ad20db79..e30f74d2fc6 100644 --- a/usr.bin/ssh/ssh.h +++ b/usr.bin/ssh/ssh.h @@ -13,7 +13,7 @@ Generic header file for ssh. */ -/* RCSID("$Id: ssh.h,v 1.14 1999/10/25 20:41:55 markus Exp $"); */ +/* RCSID("$Id: ssh.h,v 1.15 1999/10/28 08:43:10 markus Exp $"); */ #ifndef SSH_H #define SSH_H @@ -120,6 +120,10 @@ only by root, whereas ssh_config should be world-readable. */ authentication socket. */ #define SSH_AUTHSOCKET_ENV_NAME "SSH_AUTH_SOCK" +/* Name of the environment variable containing the pathname of the + authentication socket. */ +#define SSH_AGENTPID_ENV_NAME "SSH_AGENT_PID" + /* Force host key length and server key length to differ by at least this many bits. This is to make double encryption with rsaref work. */ #define SSH_KEY_BITS_RESERVED 128 |