]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/uxpgnt.c
eb7f035937f3cc0b3fdd0b3ff2905589ffb95226
[PuTTY.git] / unix / uxpgnt.c
1 /*
2  * Unix Pageant, more or less similar to ssh-agent.
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <errno.h>
8 #include <assert.h>
9 #include <signal.h>
10
11 #include <sys/types.h>
12 #include <sys/wait.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15
16 #define PUTTY_DO_GLOBALS               /* actually _define_ globals */
17 #include "putty.h"
18 #include "ssh.h"
19 #include "misc.h"
20 #include "pageant.h"
21
22 SockAddr unix_sock_addr(const char *path);
23 Socket new_unix_listener(SockAddr listenaddr, Plug plug);
24
25 void fatalbox(char *p, ...)
26 {
27     va_list ap;
28     fprintf(stderr, "FATAL ERROR: ");
29     va_start(ap, p);
30     vfprintf(stderr, p, ap);
31     va_end(ap);
32     fputc('\n', stderr);
33     exit(1);
34 }
35 void modalfatalbox(char *p, ...)
36 {
37     va_list ap;
38     fprintf(stderr, "FATAL ERROR: ");
39     va_start(ap, p);
40     vfprintf(stderr, p, ap);
41     va_end(ap);
42     fputc('\n', stderr);
43     exit(1);
44 }
45 void nonfatal(char *p, ...)
46 {
47     va_list ap;
48     fprintf(stderr, "ERROR: ");
49     va_start(ap, p);
50     vfprintf(stderr, p, ap);
51     va_end(ap);
52     fputc('\n', stderr);
53 }
54 void connection_fatal(void *frontend, char *p, ...)
55 {
56     va_list ap;
57     fprintf(stderr, "FATAL ERROR: ");
58     va_start(ap, p);
59     vfprintf(stderr, p, ap);
60     va_end(ap);
61     fputc('\n', stderr);
62     exit(1);
63 }
64 void cmdline_error(char *p, ...)
65 {
66     va_list ap;
67     fprintf(stderr, "pageant: ");
68     va_start(ap, p);
69     vfprintf(stderr, p, ap);
70     va_end(ap);
71     fputc('\n', stderr);
72     exit(1);
73 }
74
75 FILE *pageant_logfp = NULL;
76 void pageant_log(void *ctx, const char *fmt, va_list ap)
77 {
78     if (!pageant_logfp)
79         return;
80
81     fprintf(pageant_logfp, "pageant: ");
82     vfprintf(pageant_logfp, fmt, ap);
83     fprintf(pageant_logfp, "\n");
84 }
85
86 /*
87  * In Pageant our selects are synchronous, so these functions are
88  * empty stubs.
89  */
90 int uxsel_input_add(int fd, int rwx) { return 0; }
91 void uxsel_input_remove(int id) { }
92
93 /*
94  * More stubs.
95  */
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) {}
107
108 /*
109  * Short description of parameters.
110  */
111 static void usage(void)
112 {
113     printf("Pageant: SSH agent\n");
114     printf("%s\n", ver);
115     printf("FIXME\n");
116     exit(1);
117 }
118
119 static void version(void)
120 {
121     printf("pageant: %s\n", ver);
122     exit(1);
123 }
124
125 void keylist_update(void)
126 {
127     /* Nothing needs doing in Unix Pageant */
128 }
129
130 #define PAGEANT_DIR_PREFIX "/tmp/pageant"
131
132 const char *const appname = "Pageant";
133
134 char *platform_get_x_display(void) {
135     return dupstr(getenv("DISPLAY"));
136 }
137
138 static int time_to_die = FALSE;
139
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
143  * forwarding too. */
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) {}
155
156 /*
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.
161  */
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,
167                        int calling_back)
168 {
169     time_to_die = TRUE;
170     return 1;
171 }
172 struct X11Connection {
173     const struct plug_function_table *fn;
174 };
175
176 char *socketname;
177 void pageant_print_env(int pid)
178 {
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);
182 }
183
184 void pageant_fork_and_print_env(int retain_tty)
185 {
186     pid_t pid = fork();
187     if (pid == -1) {
188         perror("fork");
189         exit(1);
190     } else if (pid != 0) {
191         pageant_print_env(pid);
192         exit(0);
193     }
194
195     /*
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
207      * down that one.
208      */
209     if (chdir("/") < 0) {
210         /* should there be an error condition, nothing we can do about
211          * it anyway */
212     }
213     close(0);
214     close(1);
215     if (retain_tty) {
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. */
219         setpgrp();
220     } else {
221         /* Do that, but also leave our entire session and detach from
222          * the controlling tty (if any). */
223         setsid();
224     }
225 }
226
227 int signalpipe[2];
228
229 void sigchld(int signum)
230 {
231     if (write(signalpipe[1], "x", 1) <= 0)
232         /* not much we can do about it */;
233 }
234
235 #define TTY_LIFE_POLL_INTERVAL (TICKSPERSEC * 30)
236 void *dummy_timer_ctx;
237 static void tty_life_timer(void *ctx, unsigned long now)
238 {
239     schedule_timer(TTY_LIFE_POLL_INTERVAL, tty_life_timer, &dummy_timer_ctx);
240 }
241
242 int main(int argc, char **argv)
243 {
244     int *fdlist;
245     int fd;
246     int i, fdcount, fdsize, fdstate;
247     int errors;
248     unsigned long now;
249     char *username, *socketdir;
250     const char *err;
251     struct pageant_listen_state *pl;
252     Socket sock;
253     enum {
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;
260     Conf *conf;
261
262     fdlist = NULL;
263     fdcount = fdsize = 0;
264     errors = FALSE;
265
266     /*
267      * Process the command line.
268      */
269     while (--argc) {
270         char *p = *++argv;
271         if (*p == '-' && doing_opts) {
272             if (!strcmp(p, "-V") || !strcmp(p, "--version")) {
273                 version();
274             } else if (!strcmp(p, "--help")) {
275                 usage();
276                 exit(0);
277             } else if (!strcmp(p, "-v")) {
278                 pageant_logfp = stderr;
279             } else if (!strcmp(p, "-X")) {
280                 life = LIFE_X11;
281             } else if (!strcmp(p, "-T")) {
282                 life = LIFE_TTY;
283             } else if (!strcmp(p, "--debug")) {
284                 life = LIFE_DEBUG;
285             } else if (!strcmp(p, "--permanent")) {
286                 life = LIFE_PERM;
287             } else if (!strcmp(p, "--exec")) {
288                 life = LIFE_EXEC;
289             } else if (!strcmp(p, "--")) {
290                 doing_opts = FALSE;
291             }
292         } else {
293             if (life == LIFE_EXEC) {
294                 exec_args = argv;
295                 break; /* everything else is now args to the exec command */
296             } else {
297                 fprintf(stderr, "pageant: unexpected argument '%s'\n", p);
298                 exit(1);
299             }
300         }
301     }
302
303     if (errors)
304         return 1;
305
306     if (life == LIFE_UNSPEC) {
307         fprintf(stderr, "pageant: expected a lifetime option\n");
308         exit(1);
309     }
310     if (life == LIFE_EXEC && !exec_args) {
311         fprintf(stderr, "pageant: expected a command with --exec\n");
312         exit(1);
313     }
314
315     /*
316      * Block SIGPIPE, so that we'll get EPIPE individually on
317      * particular network connections that go wrong.
318      */
319     putty_signal(SIGPIPE, SIG_IGN);
320
321     sk_init();
322     uxsel_init();
323
324     /*
325      * Set up a listening socket and run Pageant on it.
326      */
327     username = get_username();
328     socketdir = dupprintf("%s.%s", PAGEANT_DIR_PREFIX, username);
329     sfree(username);
330     assert(*socketdir == '/');
331     if ((err = make_dir_and_check_ours(socketdir)) != NULL) {
332         fprintf(stderr, "pageant: %s: %s\n", socketdir, err);
333         exit(1);
334     }
335     socketname = dupprintf("%s/pageant.%d", socketdir, (int)getpid());
336     pageant_init();
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);
341         exit(1);
342     }
343     pageant_listener_got_socket(pl, sock);
344
345     conf = conf_new();
346     conf_set_int(conf, CONF_proxy_type, PROXY_NONE);
347
348     /*
349      * Lifetime preparations.
350      */
351     signalpipe[0] = signalpipe[1] = -1;
352     if (life == LIFE_X11) {
353         struct X11Display *disp;
354         void *greeting;
355         int greetinglen;
356         Socket s;
357         struct X11Connection *conn;
358
359         static const struct plug_function_table fn_table = {
360             x11_log,
361             x11_closing,
362             x11_receive,
363             x11_sent,
364             NULL
365         };
366
367         if (!display)
368             display = getenv("DISPLAY");
369         if (!display) {
370             fprintf(stderr, "pageant: no DISPLAY for -X mode\n");
371             exit(1);
372         }
373         disp = x11_setup_display(display, conf);
374
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);
382             exit(1);
383         }
384         greeting = x11_make_greeting('B', 11, 0, disp->localauthproto,
385                                      disp->localauthdata,
386                                      disp->localauthdatalen,
387                                      NULL, 0, &greetinglen);
388         sk_write(s, greeting, greetinglen);
389         smemclr(greeting, greetinglen);
390         sfree(greeting);
391
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) {
403         pid_t agentpid, pid;
404
405         agentpid = getpid();
406
407         /*
408          * Set up the pipe we'll use to tell us about SIGCHLD.
409          */
410         if (pipe(signalpipe) < 0) {
411             perror("pipe");
412             exit(1);
413         }
414         putty_signal(SIGCHLD, sigchld);
415
416         pid = fork();
417         if (pid < 0) {
418             perror("fork");
419             exit(1);
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);
424             perror("exec");
425             _exit(127);
426         } else {
427             termination_pid = pid;
428         }
429     }
430
431     /*
432      * Now we've decided on our logging arrangements, pass them on to
433      * pageant.c.
434      */
435     pageant_listener_set_logfn(pl, NULL, pageant_logfp ? pageant_log : NULL);
436
437     now = GETTICKCOUNT();
438
439     while (!time_to_die) {
440         fd_set rset, wset, xset;
441         int maxfd;
442         int rwx;
443         int ret;
444         unsigned long next;
445
446         FD_ZERO(&rset);
447         FD_ZERO(&wset);
448         FD_ZERO(&xset);
449         maxfd = 0;
450
451         if (signalpipe[0] >= 0) {
452             FD_SET_MAX(signalpipe[0], maxfd, rset);
453         }
454
455         /* Count the currently active fds. */
456         i = 0;
457         for (fd = first_fd(&fdstate, &rwx); fd >= 0;
458              fd = next_fd(&fdstate, &rwx)) i++;
459
460         /* Expand the fdlist buffer if necessary. */
461         if (i > fdsize) {
462             fdsize = i + 16;
463             fdlist = sresize(fdlist, fdsize, int);
464         }
465
466         /*
467          * Add all currently open fds to the select sets, and store
468          * them in fdlist as well.
469          */
470         fdcount = 0;
471         for (fd = first_fd(&fdstate, &rwx); fd >= 0;
472              fd = next_fd(&fdstate, &rwx)) {
473             fdlist[fdcount++] = fd;
474             if (rwx & 1)
475                 FD_SET_MAX(fd, maxfd, rset);
476             if (rwx & 2)
477                 FD_SET_MAX(fd, maxfd, wset);
478             if (rwx & 4)
479                 FD_SET_MAX(fd, maxfd, xset);
480         }
481
482         if (toplevel_callback_pending()) {
483             struct timeval tv;
484             tv.tv_sec = 0;
485             tv.tv_usec = 0;
486             ret = select(maxfd, &rset, &wset, &xset, &tv);
487         } else if (run_timers(now, &next)) {
488             unsigned long then;
489             long ticks;
490             struct timeval tv;
491
492             then = now;
493             now = GETTICKCOUNT();
494             if (now - then > next - then)
495                 ticks = 0;
496             else
497                 ticks = next - now;
498             tv.tv_sec = ticks / 1000;
499             tv.tv_usec = ticks % 1000 * 1000;
500             ret = select(maxfd, &rset, &wset, &xset, &tv);
501             if (ret == 0)
502                 now = next;
503             else
504                 now = GETTICKCOUNT();
505         } else {
506             ret = select(maxfd, &rset, &wset, &xset, NULL);
507         }
508
509         if (ret < 0 && errno == EINTR)
510             continue;
511
512         if (ret < 0) {
513             perror("select");
514             exit(1);
515         }
516
517         if (life == LIFE_TTY) {
518             /*
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.
524              */
525             int fd = open("/dev/tty", O_RDONLY);
526             if (fd < 0) {
527                 if (errno != ENXIO) {
528                     perror("/dev/tty: open");
529                     exit(1);
530                 }
531                 time_to_die = TRUE;
532                 break;
533             } else {
534                 close(fd);
535             }
536         }
537
538         for (i = 0; i < fdcount; i++) {
539             fd = fdlist[i];
540             /*
541              * We must process exceptional notifications before
542              * ordinary readability ones, or we may go straight
543              * past the urgent marker.
544              */
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);
551         }
552
553         if (signalpipe[0] >= 0 && FD_ISSET(signalpipe[0], &rset)) {
554             char c[1];
555             if (read(signalpipe[0], c, 1) <= 0)
556                 /* ignore error */;
557             /* ignore its value; it'll be `x' */
558             while (1) {
559                 int status;
560                 pid_t pid;
561                 pid = waitpid(-1, &status, WNOHANG);
562                 if (pid <= 0)
563                     break;
564                 if (pid == termination_pid)
565                     time_to_die = TRUE;
566             }
567         }
568
569         run_toplevel_callbacks();
570     }
571
572     /*
573      * When we come here, we're terminating, and should clean up our
574      * Unix socket file if possible.
575      */
576     if (unlink(socketname) < 0) {
577         fprintf(stderr, "pageant: %s: %s\n", socketname, strerror(errno));
578         exit(1);
579     }
580
581     return 0;
582 }