2 * Unix Pageant, more or less similar to ssh-agent.
11 #include <sys/types.h>
16 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
22 SockAddr unix_sock_addr(const char *path);
23 Socket new_unix_listener(SockAddr listenaddr, Plug plug);
25 void fatalbox(char *p, ...)
28 fprintf(stderr, "FATAL ERROR: ");
30 vfprintf(stderr, p, ap);
35 void modalfatalbox(char *p, ...)
38 fprintf(stderr, "FATAL ERROR: ");
40 vfprintf(stderr, p, ap);
45 void nonfatal(char *p, ...)
48 fprintf(stderr, "ERROR: ");
50 vfprintf(stderr, p, ap);
54 void connection_fatal(void *frontend, char *p, ...)
57 fprintf(stderr, "FATAL ERROR: ");
59 vfprintf(stderr, p, ap);
64 void cmdline_error(char *p, ...)
67 fprintf(stderr, "pageant: ");
69 vfprintf(stderr, p, ap);
75 FILE *pageant_logfp = NULL;
76 void pageant_log(void *ctx, const char *fmt, va_list ap)
81 fprintf(pageant_logfp, "pageant: ");
82 vfprintf(pageant_logfp, fmt, ap);
83 fprintf(pageant_logfp, "\n");
87 * In Pageant our selects are synchronous, so these functions are
90 int uxsel_input_add(int fd, int rwx) { return 0; }
91 void uxsel_input_remove(int id) { }
96 void random_save_seed(void) {}
97 void random_destroy_seed(void) {}
98 void noise_ultralight(unsigned long data) {}
99 char *platform_default_s(const char *name) { return NULL; }
100 int platform_default_i(const char *name, int def) { return def; }
101 FontSpec *platform_default_fontspec(const char *name) { return fontspec_new(""); }
102 Filename *platform_default_filename(const char *name) { return filename_from_str(""); }
103 char *x_get_default(const char *key) { return NULL; }
104 void log_eventlog(void *handle, const char *event) {}
107 * Short description of parameters.
109 static void usage(void)
111 printf("Pageant: SSH agent\n");
117 static void version(void)
119 printf("pageant: %s\n", ver);
123 void keylist_update(void)
125 /* Nothing needs doing in Unix Pageant */
128 #define PAGEANT_DIR_PREFIX "/tmp/pageant"
130 const char *const appname = "Pageant";
132 static int time_to_die = FALSE;
134 /* Stub functions to permit linking against x11fwd.c. These never get
135 * used, because in LIFE_X11 mode we connect to the X server using a
136 * straightforward Socket and don't try to create an ersatz SSH
138 int sshfwd_write(struct ssh_channel *c, char *data, int len) { return 0; }
139 void sshfwd_write_eof(struct ssh_channel *c) { }
140 void sshfwd_unclean_close(struct ssh_channel *c, const char *err) { }
141 void sshfwd_unthrottle(struct ssh_channel *c, int bufsize) {}
142 Conf *sshfwd_get_conf(struct ssh_channel *c) { return NULL; }
143 void sshfwd_x11_sharing_handover(struct ssh_channel *c,
144 void *share_cs, void *share_chan,
145 const char *peer_addr, int peer_port,
146 int endian, int protomajor, int protominor,
147 const void *initial_data, int initial_len) {}
148 void sshfwd_x11_is_local(struct ssh_channel *c) {}
151 * These functions are part of the plug for our connection to the X
152 * display, so they do get called. They needn't actually do anything,
153 * except that x11_closing has to signal back to the main loop that
154 * it's time to terminate.
156 static void x11_log(Plug p, int type, SockAddr addr, int port,
157 const char *error_msg, int error_code) {}
158 static int x11_receive(Plug plug, int urgent, char *data, int len) {return 0;}
159 static void x11_sent(Plug plug, int bufsize) {}
160 static int x11_closing(Plug plug, const char *error_msg, int error_code,
166 struct X11Connection {
167 const struct plug_function_table *fn;
171 void pageant_print_env(int pid)
173 printf("SSH_AUTH_SOCK=%s; export SSH_AUTH_SOCK;\n"
174 "SSH_AGENT_PID=%d; export SSH_AGENT_PID;\n",
175 socketname, (int)pid);
178 void pageant_fork_and_print_env(int retain_tty)
184 } else if (pid != 0) {
185 pageant_print_env(pid);
190 * Having forked off, we now daemonise ourselves as best we can.
191 * It's good practice in general to setsid() ourself out of any
192 * process group we didn't want to be part of, and to chdir("/")
193 * to avoid holding any directories open that we don't need in
194 * case someone wants to umount them; also, we should definitely
195 * close standard output (because it will very likely be pointing
196 * at a pipe from which some parent process is trying to read our
197 * environment variable dump, so if we hold open another copy of
198 * it then that process will never finish reading). We close
199 * standard input too on general principles, but not standard
200 * error, since we might need to shout a panicky error message
203 if (chdir("/") < 0) {
204 /* should there be an error condition, nothing we can do about
210 /* Get out of our previous process group, to avoid being
211 * blasted by passing signals. But keep our controlling tty,
212 * so we can keep checking to see if we still have one. */
215 /* Do that, but also leave our entire session and detach from
216 * the controlling tty (if any). */
223 void sigchld(int signum)
225 if (write(signalpipe[1], "x", 1) <= 0)
226 /* not much we can do about it */;
229 #define TTY_LIFE_POLL_INTERVAL (TICKSPERSEC * 30)
230 void *dummy_timer_ctx;
231 static void tty_life_timer(void *ctx, unsigned long now)
233 schedule_timer(TTY_LIFE_POLL_INTERVAL, tty_life_timer, &dummy_timer_ctx);
240 KEYACT_CLIENT_DEL_ALL,
242 KEYACT_CLIENT_LIST_FULL,
244 struct cmdline_key_action {
245 struct cmdline_key_action *next;
247 const char *filename;
250 int is_agent_action(keyact action)
252 return action == KEYACT_AGENT_LOAD;
255 struct cmdline_key_action *keyact_head = NULL, *keyact_tail = NULL;
257 void add_keyact(keyact action, const char *filename)
259 struct cmdline_key_action *a = snew(struct cmdline_key_action);
261 a->filename = filename;
264 keyact_tail->next = a;
270 char **exec_args = NULL;
272 LIFE_UNSPEC, LIFE_X11, LIFE_TTY, LIFE_DEBUG, LIFE_PERM, LIFE_EXEC
273 } life = LIFE_UNSPEC;
274 const char *display = NULL;
276 static char *askpass(const char *comment)
278 prompts_t *p = new_prompts(NULL);
282 * FIXME: if we don't have a terminal, and have to do this by X11,
283 * there's a big missing piece.
286 p->to_server = FALSE;
287 p->name = dupstr("Pageant passphrase prompt");
289 dupprintf("Enter passphrase to load key '%s': ", comment),
291 ret = console_get_userpass_input(p, NULL, 0);
295 perror("pageant: unable to read passphrase");
299 char *passphrase = dupstr(p->prompts[0]->result);
305 static int unix_add_keyfile(const char *filename_str)
307 Filename *filename = filename_from_str(filename_str);
314 * Try without a passphrase.
316 status = pageant_add_keyfile(filename, NULL, &err);
317 if (status == PAGEANT_ACTION_OK) {
319 } else if (status == PAGEANT_ACTION_FAILURE) {
320 fprintf(stderr, "pageant: %s: %s\n", filename_str, err);
327 * And now try prompting for a passphrase.
330 char *passphrase = askpass(err);
335 status = pageant_add_keyfile(filename, passphrase, &err);
337 smemclr(passphrase, strlen(passphrase));
341 if (status == PAGEANT_ACTION_OK) {
343 } else if (status == PAGEANT_ACTION_FAILURE) {
344 fprintf(stderr, "pageant: %s: %s\n", filename_str, err);
353 filename_free(filename);
357 void key_list_callback(void *ctx, const char *fingerprint, const char *comment)
359 printf("%s %s\n", fingerprint, comment);
362 void run_client(void)
364 const struct cmdline_key_action *act;
368 if (!agent_exists()) {
369 fprintf(stderr, "pageant: no agent running to talk to\n");
373 for (act = keyact_head; act; act = act->next) {
374 switch (act->action) {
375 case KEYACT_CLIENT_ADD:
376 if (!unix_add_keyfile(act->filename))
379 case KEYACT_CLIENT_LIST:
380 if (pageant_enum_keys(key_list_callback, NULL, &retstr) ==
381 PAGEANT_ACTION_FAILURE) {
382 fprintf(stderr, "pageant: listing keys: %s\n", retstr);
387 case KEYACT_CLIENT_DEL:
388 case KEYACT_CLIENT_DEL_ALL:
389 case KEYACT_CLIENT_LIST_FULL:
390 fprintf(stderr, "NYI\n");
394 assert(0 && "Invalid client action found");
405 char *username, *socketdir;
406 struct pageant_listen_state *pl;
411 int i, fdcount, fdsize, fdstate;
412 int termination_pid = -1;
415 const struct cmdline_key_action *act;
418 fdcount = fdsize = 0;
423 * Start by loading any keys provided on the command line.
425 for (act = keyact_head; act; act = act->next) {
426 assert(act->action == KEYACT_AGENT_LOAD);
427 if (!unix_add_keyfile(act->filename))
434 * Set up a listening socket and run Pageant on it.
436 username = get_username();
437 socketdir = dupprintf("%s.%s", PAGEANT_DIR_PREFIX, username);
439 assert(*socketdir == '/');
440 if ((err = make_dir_and_check_ours(socketdir)) != NULL) {
441 fprintf(stderr, "pageant: %s: %s\n", socketdir, err);
444 socketname = dupprintf("%s/pageant.%d", socketdir, (int)getpid());
445 pl = pageant_listener_new();
446 sock = new_unix_listener(unix_sock_addr(socketname), (Plug)pl);
447 if ((err = sk_socket_error(sock)) != NULL) {
448 fprintf(stderr, "pageant: %s: %s\n", socketname, err);
451 pageant_listener_got_socket(pl, sock);
454 conf_set_int(conf, CONF_proxy_type, PROXY_NONE);
457 * Lifetime preparations.
459 signalpipe[0] = signalpipe[1] = -1;
460 if (life == LIFE_X11) {
461 struct X11Display *disp;
465 struct X11Connection *conn;
467 static const struct plug_function_table fn_table = {
476 display = getenv("DISPLAY");
478 fprintf(stderr, "pageant: no DISPLAY for -X mode\n");
481 disp = x11_setup_display(display, conf);
483 conn = snew(struct X11Connection);
484 conn->fn = &fn_table;
485 s = new_connection(sk_addr_dup(disp->addr),
486 disp->realhost, disp->port,
487 0, 1, 0, 0, (Plug)conn, conf);
488 if ((err = sk_socket_error(s)) != NULL) {
489 fprintf(stderr, "pageant: unable to connect to X server: %s", err);
492 greeting = x11_make_greeting('B', 11, 0, disp->localauthproto,
494 disp->localauthdatalen,
495 NULL, 0, &greetinglen);
496 sk_write(s, greeting, greetinglen);
497 smemclr(greeting, greetinglen);
500 pageant_fork_and_print_env(FALSE);
501 } else if (life == LIFE_TTY) {
502 schedule_timer(TTY_LIFE_POLL_INTERVAL,
503 tty_life_timer, &dummy_timer_ctx);
504 pageant_fork_and_print_env(TRUE);
505 } else if (life == LIFE_PERM) {
506 pageant_fork_and_print_env(FALSE);
507 } else if (life == LIFE_DEBUG) {
508 pageant_print_env(getpid());
509 pageant_logfp = stdout;
510 } else if (life == LIFE_EXEC) {
516 * Set up the pipe we'll use to tell us about SIGCHLD.
518 if (pipe(signalpipe) < 0) {
522 putty_signal(SIGCHLD, sigchld);
528 } else if (pid == 0) {
529 setenv("SSH_AUTH_SOCK", socketname, TRUE);
530 setenv("SSH_AGENT_PID", dupprintf("%d", (int)agentpid), TRUE);
531 execvp(exec_args[0], exec_args);
535 termination_pid = pid;
540 * Now we've decided on our logging arrangements, pass them on to
543 pageant_listener_set_logfn(pl, NULL, pageant_logfp ? pageant_log : NULL);
545 now = GETTICKCOUNT();
547 while (!time_to_die) {
548 fd_set rset, wset, xset;
559 if (signalpipe[0] >= 0) {
560 FD_SET_MAX(signalpipe[0], maxfd, rset);
563 /* Count the currently active fds. */
565 for (fd = first_fd(&fdstate, &rwx); fd >= 0;
566 fd = next_fd(&fdstate, &rwx)) i++;
568 /* Expand the fdlist buffer if necessary. */
571 fdlist = sresize(fdlist, fdsize, int);
575 * Add all currently open fds to the select sets, and store
576 * them in fdlist as well.
579 for (fd = first_fd(&fdstate, &rwx); fd >= 0;
580 fd = next_fd(&fdstate, &rwx)) {
581 fdlist[fdcount++] = fd;
583 FD_SET_MAX(fd, maxfd, rset);
585 FD_SET_MAX(fd, maxfd, wset);
587 FD_SET_MAX(fd, maxfd, xset);
590 if (toplevel_callback_pending()) {
594 ret = select(maxfd, &rset, &wset, &xset, &tv);
595 } else if (run_timers(now, &next)) {
601 now = GETTICKCOUNT();
602 if (now - then > next - then)
606 tv.tv_sec = ticks / 1000;
607 tv.tv_usec = ticks % 1000 * 1000;
608 ret = select(maxfd, &rset, &wset, &xset, &tv);
612 now = GETTICKCOUNT();
614 ret = select(maxfd, &rset, &wset, &xset, NULL);
617 if (ret < 0 && errno == EINTR)
625 if (life == LIFE_TTY) {
627 * Every time we wake up (whether it was due to tty_timer
628 * elapsing or for any other reason), poll to see if we
629 * still have a controlling terminal. If we don't, then
630 * our containing tty session has ended, so it's time to
631 * clean up and leave.
633 int fd = open("/dev/tty", O_RDONLY);
635 if (errno != ENXIO) {
636 perror("/dev/tty: open");
646 for (i = 0; i < fdcount; i++) {
649 * We must process exceptional notifications before
650 * ordinary readability ones, or we may go straight
651 * past the urgent marker.
653 if (FD_ISSET(fd, &xset))
654 select_result(fd, 4);
655 if (FD_ISSET(fd, &rset))
656 select_result(fd, 1);
657 if (FD_ISSET(fd, &wset))
658 select_result(fd, 2);
661 if (signalpipe[0] >= 0 && FD_ISSET(signalpipe[0], &rset)) {
663 if (read(signalpipe[0], c, 1) <= 0)
665 /* ignore its value; it'll be `x' */
669 pid = waitpid(-1, &status, WNOHANG);
672 if (pid == termination_pid)
677 run_toplevel_callbacks();
681 * When we come here, we're terminating, and should clean up our
682 * Unix socket file if possible.
684 if (unlink(socketname) < 0) {
685 fprintf(stderr, "pageant: %s: %s\n", socketname, strerror(errno));
690 int main(int argc, char **argv)
692 int doing_opts = TRUE;
693 keyact curr_keyact = KEYACT_AGENT_LOAD;
696 * Process the command line.
700 if (*p == '-' && doing_opts) {
701 if (!strcmp(p, "-V") || !strcmp(p, "--version")) {
703 } else if (!strcmp(p, "--help")) {
706 } else if (!strcmp(p, "-v")) {
707 pageant_logfp = stderr;
708 } else if (!strcmp(p, "-a")) {
709 curr_keyact = KEYACT_CLIENT_ADD;
710 } else if (!strcmp(p, "-d")) {
711 curr_keyact = KEYACT_CLIENT_DEL;
712 } else if (!strcmp(p, "-D")) {
713 add_keyact(KEYACT_CLIENT_DEL_ALL, NULL);
714 } else if (!strcmp(p, "-l")) {
715 add_keyact(KEYACT_CLIENT_LIST, NULL);
716 } else if (!strcmp(p, "-L")) {
717 add_keyact(KEYACT_CLIENT_LIST_FULL, NULL);
718 } else if (!strcmp(p, "-X")) {
720 } else if (!strcmp(p, "-T")) {
722 } else if (!strcmp(p, "--debug")) {
724 } else if (!strcmp(p, "--permanent")) {
726 } else if (!strcmp(p, "--exec")) {
728 /* Now all subsequent arguments go to the exec command. */
731 argc = 0; /* force end of option processing */
733 fprintf(stderr, "pageant: expected a command "
737 } else if (!strcmp(p, "--")) {
742 * Non-option arguments (apart from those after --exec,
743 * which are treated specially above) are interpreted as
744 * the names of private key files to either add or delete
747 add_keyact(curr_keyact, p);
751 if (life == LIFE_EXEC && !exec_args) {
752 fprintf(stderr, "pageant: expected a command with --exec\n");
757 * Block SIGPIPE, so that we'll get EPIPE individually on
758 * particular network connections that go wrong.
760 putty_signal(SIGPIPE, SIG_IGN);
766 * Now distinguish our two main running modes. Either we're
767 * actually starting up an agent, in which case we should have a
768 * lifetime mode, and no key actions of KEYACT_CLIENT_* type; or
769 * else we're contacting an existing agent to add or remove keys,
770 * in which case we should have no lifetime mode, and no key
771 * actions of KEYACT_AGENT_* type.
774 int has_agent_actions = FALSE;
775 int has_client_actions = FALSE;
776 int has_lifetime = FALSE;
777 const struct cmdline_key_action *act;
779 for (act = keyact_head; act; act = act->next) {
780 if (is_agent_action(act->action))
781 has_agent_actions = TRUE;
783 has_client_actions = TRUE;
785 if (life != LIFE_UNSPEC)
788 if (has_lifetime && has_client_actions) {
789 fprintf(stderr, "pageant: client key actions (-a, -d, -D, -l, -L)"
790 " do not go with an agent lifetime option\n");
793 if (!has_lifetime && has_agent_actions) {
794 fprintf(stderr, "pageant: expected an agent lifetime option with"
795 " bare key file arguments\n");
798 if (!has_lifetime && !has_client_actions) {
799 fprintf(stderr, "pageant: expected an agent lifetime option"
800 " or a client key action\n");
806 } else if (has_client_actions) {