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 logevent(void *frontend, const char *string) {}
97 void random_save_seed(void) {}
98 void random_destroy_seed(void) {}
99 void noise_ultralight(unsigned long data) {}
100 char *platform_default_s(const char *name) { return NULL; }
101 int platform_default_i(const char *name, int def) { return def; }
102 FontSpec *platform_default_fontspec(const char *name) { return fontspec_new(""); }
103 Filename *platform_default_filename(const char *name) { return filename_from_str(""); }
104 char *x_get_default(const char *key) { return NULL; }
105 void old_keyfile_warning(void) {}
106 void timer_change_notify(unsigned long next) {}
109 * Short description of parameters.
111 static void usage(void)
113 printf("Pageant: SSH agent\n");
119 static void version(void)
121 printf("pageant: %s\n", ver);
125 void keylist_update(void)
127 /* Nothing needs doing in Unix Pageant */
130 #define PAGEANT_DIR_PREFIX "/tmp/pageant"
132 const char *const appname = "Pageant";
134 char *platform_get_x_display(void) {
135 return dupstr(getenv("DISPLAY"));
138 static int time_to_die = FALSE;
140 /* Stub functions to permit linking against x11fwd.c. These never get
141 * used, because in LIFE_X11 mode we connect to the X server using a
142 * straightforward Socket and don't try to create an ersatz SSH
144 int sshfwd_write(struct ssh_channel *c, char *data, int len) { return 0; }
145 void sshfwd_write_eof(struct ssh_channel *c) { }
146 void sshfwd_unclean_close(struct ssh_channel *c, const char *err) { }
147 void sshfwd_unthrottle(struct ssh_channel *c, int bufsize) {}
148 Conf *sshfwd_get_conf(struct ssh_channel *c) { return NULL; }
149 void sshfwd_x11_sharing_handover(struct ssh_channel *c,
150 void *share_cs, void *share_chan,
151 const char *peer_addr, int peer_port,
152 int endian, int protomajor, int protominor,
153 const void *initial_data, int initial_len) {}
154 void sshfwd_x11_is_local(struct ssh_channel *c) {}
157 * These functions are part of the plug for our connection to the X
158 * display, so they do get called. They needn't actually do anything,
159 * except that x11_closing has to signal back to the main loop that
160 * it's time to terminate.
162 static void x11_log(Plug p, int type, SockAddr addr, int port,
163 const char *error_msg, int error_code) {}
164 static int x11_receive(Plug plug, int urgent, char *data, int len) {return 0;}
165 static void x11_sent(Plug plug, int bufsize) {}
166 static int x11_closing(Plug plug, const char *error_msg, int error_code,
172 struct X11Connection {
173 const struct plug_function_table *fn;
177 void pageant_print_env(int pid)
179 printf("SSH_AUTH_SOCK=%s; export SSH_AUTH_SOCK;\n"
180 "SSH_AGENT_PID=%d; export SSH_AGENT_PID;\n",
181 socketname, (int)pid);
184 void pageant_fork_and_print_env(int retain_tty)
190 } else if (pid != 0) {
191 pageant_print_env(pid);
196 * Having forked off, we now daemonise ourselves as best we can.
197 * It's good practice in general to setsid() ourself out of any
198 * process group we didn't want to be part of, and to chdir("/")
199 * to avoid holding any directories open that we don't need in
200 * case someone wants to umount them; also, we should definitely
201 * close standard output (because it will very likely be pointing
202 * at a pipe from which some parent process is trying to read our
203 * environment variable dump, so if we hold open another copy of
204 * it then that process will never finish reading). We close
205 * standard input too on general principles, but not standard
206 * error, since we might need to shout a panicky error message
209 if (chdir("/") < 0) {
210 /* should there be an error condition, nothing we can do about
216 /* Get out of our previous process group, to avoid being
217 * blasted by passing signals. But keep our controlling tty,
218 * so we can keep checking to see if we still have one. */
221 /* Do that, but also leave our entire session and detach from
222 * the controlling tty (if any). */
229 void sigchld(int signum)
231 if (write(signalpipe[1], "x", 1) <= 0)
232 /* not much we can do about it */;
235 #define TTY_LIFE_POLL_INTERVAL (TICKSPERSEC * 30)
236 void *dummy_timer_ctx;
237 static void tty_life_timer(void *ctx, unsigned long now)
239 schedule_timer(TTY_LIFE_POLL_INTERVAL, tty_life_timer, &dummy_timer_ctx);
242 int main(int argc, char **argv)
246 int i, fdcount, fdsize, fdstate;
249 char *username, *socketdir;
251 struct pageant_listen_state *pl;
254 LIFE_UNSPEC, LIFE_X11, LIFE_TTY, LIFE_DEBUG, LIFE_PERM, LIFE_EXEC
255 } life = LIFE_UNSPEC;
256 const char *display = NULL;
257 int doing_opts = TRUE;
258 char **exec_args = NULL;
259 int termination_pid = -1;
263 fdcount = fdsize = 0;
267 * Process the command line.
271 if (*p == '-' && doing_opts) {
272 if (!strcmp(p, "-V") || !strcmp(p, "--version")) {
274 } else if (!strcmp(p, "--help")) {
277 } else if (!strcmp(p, "-v")) {
278 pageant_logfp = stderr;
279 } else if (!strcmp(p, "-X")) {
281 } else if (!strcmp(p, "-T")) {
283 } else if (!strcmp(p, "--debug")) {
285 } else if (!strcmp(p, "--permanent")) {
287 } else if (!strcmp(p, "--exec")) {
289 } else if (!strcmp(p, "--")) {
293 if (life == LIFE_EXEC) {
295 break; /* everything else is now args to the exec command */
297 fprintf(stderr, "pageant: unexpected argument '%s'\n", p);
306 if (life == LIFE_UNSPEC) {
307 fprintf(stderr, "pageant: expected a lifetime option\n");
310 if (life == LIFE_EXEC && !exec_args) {
311 fprintf(stderr, "pageant: expected a command with --exec\n");
316 * Block SIGPIPE, so that we'll get EPIPE individually on
317 * particular network connections that go wrong.
319 putty_signal(SIGPIPE, SIG_IGN);
325 * Set up a listening socket and run Pageant on it.
327 username = get_username();
328 socketdir = dupprintf("%s.%s", PAGEANT_DIR_PREFIX, username);
330 assert(*socketdir == '/');
331 if ((err = make_dir_and_check_ours(socketdir)) != NULL) {
332 fprintf(stderr, "pageant: %s: %s\n", socketdir, err);
335 socketname = dupprintf("%s/pageant.%d", socketdir, (int)getpid());
337 pl = pageant_listener_new();
338 sock = new_unix_listener(unix_sock_addr(socketname), (Plug)pl);
339 if ((err = sk_socket_error(sock)) != NULL) {
340 fprintf(stderr, "pageant: %s: %s\n", socketname, err);
343 pageant_listener_got_socket(pl, sock);
346 conf_set_int(conf, CONF_proxy_type, PROXY_NONE);
349 * Lifetime preparations.
351 signalpipe[0] = signalpipe[1] = -1;
352 if (life == LIFE_X11) {
353 struct X11Display *disp;
357 struct X11Connection *conn;
359 static const struct plug_function_table fn_table = {
368 display = getenv("DISPLAY");
370 fprintf(stderr, "pageant: no DISPLAY for -X mode\n");
373 disp = x11_setup_display(display, conf);
375 conn = snew(struct X11Connection);
376 conn->fn = &fn_table;
377 s = new_connection(sk_addr_dup(disp->addr),
378 disp->realhost, disp->port,
379 0, 1, 0, 0, (Plug)conn, conf);
380 if ((err = sk_socket_error(s)) != NULL) {
381 fprintf(stderr, "pageant: unable to connect to X server: %s", err);
384 greeting = x11_make_greeting('B', 11, 0, disp->localauthproto,
386 disp->localauthdatalen,
387 NULL, 0, &greetinglen);
388 sk_write(s, greeting, greetinglen);
389 smemclr(greeting, greetinglen);
392 pageant_fork_and_print_env(FALSE);
393 } else if (life == LIFE_TTY) {
394 schedule_timer(TTY_LIFE_POLL_INTERVAL,
395 tty_life_timer, &dummy_timer_ctx);
396 pageant_fork_and_print_env(TRUE);
397 } else if (life == LIFE_PERM) {
398 pageant_fork_and_print_env(FALSE);
399 } else if (life == LIFE_DEBUG) {
400 pageant_print_env(getpid());
401 pageant_logfp = stdout;
402 } else if (life == LIFE_EXEC) {
408 * Set up the pipe we'll use to tell us about SIGCHLD.
410 if (pipe(signalpipe) < 0) {
414 putty_signal(SIGCHLD, sigchld);
420 } else if (pid == 0) {
421 setenv("SSH_AUTH_SOCK", socketname, TRUE);
422 setenv("SSH_AGENT_PID", dupprintf("%d", (int)agentpid), TRUE);
423 execvp(exec_args[0], exec_args);
427 termination_pid = pid;
432 * Now we've decided on our logging arrangements, pass them on to
435 pageant_listener_set_logfn(pl, NULL, pageant_logfp ? pageant_log : NULL);
437 now = GETTICKCOUNT();
439 while (!time_to_die) {
440 fd_set rset, wset, xset;
451 if (signalpipe[0] >= 0) {
452 FD_SET_MAX(signalpipe[0], maxfd, rset);
455 /* Count the currently active fds. */
457 for (fd = first_fd(&fdstate, &rwx); fd >= 0;
458 fd = next_fd(&fdstate, &rwx)) i++;
460 /* Expand the fdlist buffer if necessary. */
463 fdlist = sresize(fdlist, fdsize, int);
467 * Add all currently open fds to the select sets, and store
468 * them in fdlist as well.
471 for (fd = first_fd(&fdstate, &rwx); fd >= 0;
472 fd = next_fd(&fdstate, &rwx)) {
473 fdlist[fdcount++] = fd;
475 FD_SET_MAX(fd, maxfd, rset);
477 FD_SET_MAX(fd, maxfd, wset);
479 FD_SET_MAX(fd, maxfd, xset);
482 if (toplevel_callback_pending()) {
486 ret = select(maxfd, &rset, &wset, &xset, &tv);
487 } else if (run_timers(now, &next)) {
493 now = GETTICKCOUNT();
494 if (now - then > next - then)
498 tv.tv_sec = ticks / 1000;
499 tv.tv_usec = ticks % 1000 * 1000;
500 ret = select(maxfd, &rset, &wset, &xset, &tv);
504 now = GETTICKCOUNT();
506 ret = select(maxfd, &rset, &wset, &xset, NULL);
509 if (ret < 0 && errno == EINTR)
517 if (life == LIFE_TTY) {
519 * Every time we wake up (whether it was due to tty_timer
520 * elapsing or for any other reason), poll to see if we
521 * still have a controlling terminal. If we don't, then
522 * our containing tty session has ended, so it's time to
523 * clean up and leave.
525 int fd = open("/dev/tty", O_RDONLY);
527 if (errno != ENXIO) {
528 perror("/dev/tty: open");
538 for (i = 0; i < fdcount; i++) {
541 * We must process exceptional notifications before
542 * ordinary readability ones, or we may go straight
543 * past the urgent marker.
545 if (FD_ISSET(fd, &xset))
546 select_result(fd, 4);
547 if (FD_ISSET(fd, &rset))
548 select_result(fd, 1);
549 if (FD_ISSET(fd, &wset))
550 select_result(fd, 2);
553 if (signalpipe[0] >= 0 && FD_ISSET(signalpipe[0], &rset)) {
555 if (read(signalpipe[0], c, 1) <= 0)
557 /* ignore its value; it'll be `x' */
561 pid = waitpid(-1, &status, WNOHANG);
564 if (pid == termination_pid)
569 run_toplevel_callbacks();
573 * When we come here, we're terminating, and should clean up our
574 * Unix socket file if possible.
576 if (unlink(socketname) < 0) {
577 fprintf(stderr, "pageant: %s: %s\n", socketname, strerror(errno));