2 * Unix Pageant, more or less similar to ssh-agent.
11 #include <sys/types.h>
15 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
21 SockAddr unix_sock_addr(const char *path);
22 Socket new_unix_listener(SockAddr listenaddr, Plug plug);
24 void fatalbox(char *p, ...)
27 fprintf(stderr, "FATAL ERROR: ");
29 vfprintf(stderr, p, ap);
34 void modalfatalbox(char *p, ...)
37 fprintf(stderr, "FATAL ERROR: ");
39 vfprintf(stderr, p, ap);
44 void nonfatal(char *p, ...)
47 fprintf(stderr, "ERROR: ");
49 vfprintf(stderr, p, ap);
53 void connection_fatal(void *frontend, char *p, ...)
56 fprintf(stderr, "FATAL ERROR: ");
58 vfprintf(stderr, p, ap);
63 void cmdline_error(char *p, ...)
66 fprintf(stderr, "pageant: ");
68 vfprintf(stderr, p, ap);
74 FILE *pageant_logfp = NULL;
75 void pageant_log(void *ctx, const char *fmt, va_list ap)
80 fprintf(pageant_logfp, "pageant: ");
81 vfprintf(pageant_logfp, fmt, ap);
82 fprintf(pageant_logfp, "\n");
86 * In Pageant our selects are synchronous, so these functions are
89 int uxsel_input_add(int fd, int rwx) { return 0; }
90 void uxsel_input_remove(int id) { }
95 void logevent(void *frontend, const char *string) {}
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 old_keyfile_warning(void) {}
105 void timer_change_notify(unsigned long next) {}
108 * Short description of parameters.
110 static void usage(void)
112 printf("Pageant: SSH agent\n");
118 static void version(void)
120 printf("pageant: %s\n", ver);
124 void keylist_update(void)
126 /* Nothing needs doing in Unix Pageant */
129 #define PAGEANT_DIR_PREFIX "/tmp/pageant"
131 const char *const appname = "Pageant";
135 char *platform_get_x_display(void) {
136 return dupstr(getenv("DISPLAY"));
138 int sshfwd_write(struct ssh_channel *c, char *data, int len) { return 0; }
139 void sshfwd_write_eof(struct ssh_channel *c) { /* FIXME: notify main loop instead */ exit(0); }
140 void sshfwd_unclean_close(struct ssh_channel *c, const char *err) { /* FIXME: notify main loop instead */ exit(1); }
141 void sshfwd_unthrottle(struct ssh_channel *c, int bufsize) {}
142 Conf *sshfwd_get_conf(struct ssh_channel *c) { return conf; }
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) {}
149 static void x11_log(Plug p, int type, SockAddr addr, int port,
150 const char *error_msg, int error_code) {}
151 static int x11_closing(Plug plug, const char *error_msg, int error_code,
152 int calling_back) { /* FIXME: notify main loop instead */ exit(0); }
153 static int x11_receive(Plug plug, int urgent, char *data, int len) { return 0; }
154 static void x11_sent(Plug plug, int bufsize) {}
155 struct X11Connection {
156 const struct plug_function_table *fn;
160 void pageant_print_env(int pid)
162 printf("SSH_AUTH_SOCK=%s; export SSH_AUTH_SOCK;\n"
163 "SSH_AGENT_PID=%d; export SSH_AGENT_PID;\n",
164 socketname, (int)pid);
167 void pageant_fork_and_print_env(void)
173 } else if (pid != 0) {
174 pageant_print_env(pid);
179 * Having forked off, we now daemonise ourselves as best we can.
180 * It's good practice in general to setsid() ourself out of any
181 * process group we didn't want to be part of, and to chdir("/")
182 * to avoid holding any directories open that we don't need in
183 * case someone wants to umount them; also, we should definitely
184 * close standard output (because it will very likely be pointing
185 * at a pipe from which some parent process is trying to read our
186 * environment variable dump, so if we hold open another copy of
187 * it then that process will never finish reading). We close
188 * standard input too on general principles, but not standard
189 * error, since we might need to shout a panicky error message
192 if (chdir("/") < 0) {
193 /* should there be an error condition, nothing we can do about
203 void sigchld(int signum)
205 if (write(signalpipe[1], "x", 1) <= 0)
206 /* not much we can do about it */;
209 int main(int argc, char **argv)
213 int i, fdcount, fdsize, fdstate;
216 char *username, *socketdir;
218 struct pageant_listen_state *pl;
221 LIFE_UNSPEC, LIFE_X11, LIFE_DEBUG, LIFE_PERM, LIFE_EXEC
222 } life = LIFE_UNSPEC;
223 const char *display = NULL;
224 int doing_opts = TRUE;
225 char **exec_args = NULL;
226 int termination_pid = -1;
229 fdcount = fdsize = 0;
233 * Process the command line.
237 if (*p == '-' && doing_opts) {
238 if (!strcmp(p, "-V") || !strcmp(p, "--version")) {
240 } else if (!strcmp(p, "--help")) {
243 } else if (!strcmp(p, "-v")) {
244 pageant_logfp = stderr;
245 } else if (!strcmp(p, "-X")) {
247 } else if (!strcmp(p, "--debug")) {
249 } else if (!strcmp(p, "--permanent")) {
251 } else if (!strcmp(p, "--exec")) {
253 } else if (!strcmp(p, "--")) {
257 if (life == LIFE_EXEC) {
259 break; /* everything else is now args to the exec command */
261 fprintf(stderr, "pageant: unexpected argument '%s'\n", p);
270 if (life == LIFE_UNSPEC) {
271 fprintf(stderr, "pageant: expected a lifetime option\n");
274 if (life == LIFE_EXEC && !exec_args) {
275 fprintf(stderr, "pageant: expected a command with --exec\n");
280 * Block SIGPIPE, so that we'll get EPIPE individually on
281 * particular network connections that go wrong.
283 putty_signal(SIGPIPE, SIG_IGN);
289 * Set up a listening socket and run Pageant on it.
291 username = get_username();
292 socketdir = dupprintf("%s.%s", PAGEANT_DIR_PREFIX, username);
294 assert(*socketdir == '/');
295 if ((err = make_dir_and_check_ours(socketdir)) != NULL) {
296 fprintf(stderr, "pageant: %s: %s\n", socketdir, err);
299 socketname = dupprintf("%s/pageant.%d", socketdir, (int)getpid());
302 conf_set_int(conf, CONF_proxy_type, PROXY_NONE);
305 * Lifetime preparations.
307 signalpipe[0] = signalpipe[1] = -1;
308 if (life == LIFE_X11) {
309 struct X11Display *disp;
313 struct X11Connection *conn;
315 static const struct plug_function_table fn_table = {
324 display = getenv("DISPLAY");
326 fprintf(stderr, "pageant: no DISPLAY for -X mode\n");
329 disp = x11_setup_display(display, conf);
331 conn = snew(struct X11Connection);
332 conn->fn = &fn_table;
333 s = new_connection(sk_addr_dup(disp->addr),
334 disp->realhost, disp->port,
335 0, 1, 0, 0, (Plug)conn, conf);
336 if ((err = sk_socket_error(s)) != NULL) {
337 fprintf(stderr, "pageant: unable to connect to X server: %s", err);
340 greeting = x11_make_greeting('B', 11, 0, disp->localauthproto,
342 disp->localauthdatalen,
343 NULL, 0, &greetinglen);
344 sk_write(s, greeting, greetinglen);
345 smemclr(greeting, greetinglen);
348 pageant_fork_and_print_env();
349 } else if (life == LIFE_PERM) {
350 pageant_fork_and_print_env();
351 } else if (life == LIFE_DEBUG) {
352 pageant_print_env(getpid());
353 pageant_logfp = stdout;
354 } else if (life == LIFE_EXEC) {
360 * Set up the pipe we'll use to tell us about SIGCHLD.
362 if (pipe(signalpipe) < 0) {
366 putty_signal(SIGCHLD, sigchld);
372 } else if (pid == 0) {
373 setenv("SSH_AUTH_SOCK", socketname, TRUE);
374 setenv("SSH_AGENT_PID", dupprintf("%d", (int)agentpid), TRUE);
375 execvp(exec_args[0], exec_args);
379 termination_pid = pid;
384 pl = pageant_listener_new(NULL, pageant_logfp ? pageant_log : NULL);
385 sock = new_unix_listener(unix_sock_addr(socketname), (Plug)pl);
386 if ((err = sk_socket_error(sock)) != NULL) {
387 fprintf(stderr, "pageant: %s: %s\n", socketname, err);
390 pageant_listener_got_socket(pl, sock);
392 now = GETTICKCOUNT();
395 fd_set rset, wset, xset;
406 if (signalpipe[0] >= 0) {
407 FD_SET_MAX(signalpipe[0], maxfd, rset);
410 /* Count the currently active fds. */
412 for (fd = first_fd(&fdstate, &rwx); fd >= 0;
413 fd = next_fd(&fdstate, &rwx)) i++;
415 /* Expand the fdlist buffer if necessary. */
418 fdlist = sresize(fdlist, fdsize, int);
422 * Add all currently open fds to the select sets, and store
423 * them in fdlist as well.
426 for (fd = first_fd(&fdstate, &rwx); fd >= 0;
427 fd = next_fd(&fdstate, &rwx)) {
428 fdlist[fdcount++] = fd;
430 FD_SET_MAX(fd, maxfd, rset);
432 FD_SET_MAX(fd, maxfd, wset);
434 FD_SET_MAX(fd, maxfd, xset);
437 if (toplevel_callback_pending()) {
441 ret = select(maxfd, &rset, &wset, &xset, &tv);
442 } else if (run_timers(now, &next)) {
448 now = GETTICKCOUNT();
449 if (now - then > next - then)
453 tv.tv_sec = ticks / 1000;
454 tv.tv_usec = ticks % 1000 * 1000;
455 ret = select(maxfd, &rset, &wset, &xset, &tv);
459 now = GETTICKCOUNT();
461 ret = select(maxfd, &rset, &wset, &xset, NULL);
464 if (ret < 0 && errno == EINTR)
472 for (i = 0; i < fdcount; i++) {
475 * We must process exceptional notifications before
476 * ordinary readability ones, or we may go straight
477 * past the urgent marker.
479 if (FD_ISSET(fd, &xset))
480 select_result(fd, 4);
481 if (FD_ISSET(fd, &rset))
482 select_result(fd, 1);
483 if (FD_ISSET(fd, &wset))
484 select_result(fd, 2);
487 if (signalpipe[0] >= 0 && FD_ISSET(signalpipe[0], &rset)) {
489 if (read(signalpipe[0], c, 1) <= 0)
491 /* ignore its value; it'll be `x' */
495 pid = waitpid(-1, &status, WNOHANG);
498 if (pid == termination_pid)
503 run_toplevel_callbacks();