]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/pty.c
Major destabilisation, phase 1. In this phase I've moved (I think)
[PuTTY.git] / unix / pty.c
1 #define _XOPEN_SOURCE
2 #define _XOPEN_SOURCE_EXTENDED
3 #include <features.h>
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <signal.h>
10 #include <fcntl.h>
11 #include <termios.h>
12 #include <grp.h>
13 #include <utmp.h>
14 #include <pwd.h>
15 #include <time.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <sys/ioctl.h>
19
20 #include "putty.h"
21 #include "terminal.h"
22
23 #ifndef FALSE
24 #define FALSE 0
25 #endif
26 #ifndef TRUE
27 #define TRUE 1
28 #endif
29
30 #ifndef UTMP_FILE
31 #define UTMP_FILE "/var/run/utmp"
32 #endif
33 #ifndef WTMP_FILE
34 #define WTMP_FILE "/var/log/wtmp"
35 #endif
36 #ifndef LASTLOG_FILE
37 #ifdef _PATH_LASTLOG
38 #define LASTLOG_FILE _PATH_LASTLOG
39 #else
40 #define LASTLOG_FILE "/var/log/lastlog"
41 #endif
42 #endif
43
44 /*
45  * Set up a default for vaguely sane systems. The idea is that if
46  * OMIT_UTMP is not defined, then at least one of the symbols which
47  * enable particular forms of utmp processing should be, if only so
48  * that a link error can warn you that you should have defined
49  * OMIT_UTMP if you didn't want any. Currently HAVE_PUTUTLINE is
50  * the only such symbol.
51  */
52 #ifndef OMIT_UTMP
53 #if !defined HAVE_PUTUTLINE
54 #define HAVE_PUTUTLINE
55 #endif
56 #endif
57
58 int pty_master_fd;
59 static char pty_name[FILENAME_MAX];
60 static int pty_stamped_utmp = 0;
61 static int pty_child_pid;
62 static int pty_utmp_helper_pid, pty_utmp_helper_pipe;
63 static sig_atomic_t pty_child_dead;
64 #ifndef OMIT_UTMP
65 static struct utmp utmp_entry;
66 #endif
67 char **pty_argv;
68
69 int pty_child_is_dead(void)
70 {
71     return pty_child_dead;
72 }
73
74 static void pty_size(void);
75
76 static void setup_utmp(char *ttyname, char *location)
77 {
78 #ifndef OMIT_UTMP
79 #ifdef HAVE_LASTLOG
80     struct lastlog lastlog_entry;
81     FILE *lastlog;
82 #endif
83     struct passwd *pw;
84     FILE *wtmp;
85
86     pw = getpwuid(getuid());
87     memset(&utmp_entry, 0, sizeof(utmp_entry));
88     utmp_entry.ut_type = USER_PROCESS;
89     utmp_entry.ut_pid = getpid();
90     strncpy(utmp_entry.ut_line, ttyname+5, lenof(utmp_entry.ut_line));
91     strncpy(utmp_entry.ut_id, ttyname+8, lenof(utmp_entry.ut_id));
92     strncpy(utmp_entry.ut_user, pw->pw_name, lenof(utmp_entry.ut_user));
93     strncpy(utmp_entry.ut_host, location, lenof(utmp_entry.ut_host));
94     time(&utmp_entry.ut_time);
95
96 #if defined HAVE_PUTUTLINE
97     utmpname(UTMP_FILE);
98     setutent();
99     pututline(&utmp_entry);
100     endutent();
101 #endif
102
103     if ((wtmp = fopen(WTMP_FILE, "a")) != NULL) {
104         fwrite(&utmp_entry, 1, sizeof(utmp_entry), wtmp);
105         fclose(wtmp);
106     }
107
108 #ifdef HAVE_LASTLOG
109     memset(&lastlog_entry, 0, sizeof(lastlog_entry));
110     strncpy(lastlog_entry.ll_line, ttyname+5, lenof(lastlog_entry.ll_line));
111     strncpy(lastlog_entry.ll_host, location, lenof(lastlog_entry.ll_host));
112     time(&lastlog_entry.ll_time);
113     if ((lastlog = fopen(LASTLOG_FILE, "r+")) != NULL) {
114         fseek(lastlog, sizeof(lastlog_entry) * getuid(), SEEK_SET);
115         fwrite(&lastlog_entry, 1, sizeof(lastlog_entry), lastlog);
116         fclose(lastlog);
117     }
118 #endif
119
120     pty_stamped_utmp = 1;
121
122 #endif
123 }
124
125 static void cleanup_utmp(void)
126 {
127 #ifndef OMIT_UTMP
128     FILE *wtmp;
129
130     if (!pty_stamped_utmp)
131         return;
132
133     utmp_entry.ut_type = DEAD_PROCESS;
134     memset(utmp_entry.ut_user, 0, lenof(utmp_entry.ut_user));
135     time(&utmp_entry.ut_time);
136
137     if ((wtmp = fopen(WTMP_FILE, "a")) != NULL) {
138         fwrite(&utmp_entry, 1, sizeof(utmp_entry), wtmp);
139         fclose(wtmp);
140     }
141
142     memset(utmp_entry.ut_line, 0, lenof(utmp_entry.ut_line));
143     utmp_entry.ut_time = 0;
144
145 #if defined HAVE_PUTUTLINE
146     utmpname(UTMP_FILE);
147     setutent();
148     pututline(&utmp_entry);
149     endutent();
150 #endif
151
152     pty_stamped_utmp = 0;              /* ensure we never double-cleanup */
153 #endif
154 }
155
156 static void sigchld_handler(int signum)
157 {
158     pid_t pid;
159     int status;
160     pid = waitpid(-1, &status, WNOHANG);
161     if (pid == pty_child_pid && (WIFEXITED(status) || WIFSIGNALED(status)))
162         pty_child_dead = TRUE;  
163 }
164
165 static void fatal_sig_handler(int signum)
166 {
167     signal(signum, SIG_DFL);
168     cleanup_utmp();
169     setuid(getuid());
170     raise(signum);
171 }
172
173 static void pty_open_master(void)
174 {
175 #ifdef BSD_PTYS
176     const char chars1[] = "pqrstuvwxyz";
177     const char chars2[] = "0123456789abcdef";
178     const char *p1, *p2;
179     char master_name[20];
180     struct group *gp;
181
182     for (p1 = chars1; *p1; p1++)
183         for (p2 = chars2; *p2; p2++) {
184             sprintf(master_name, "/dev/pty%c%c", *p1, *p2);
185             pty_master_fd = open(master_name, O_RDWR);
186             if (pty_master_fd >= 0) {
187                 if (geteuid() == 0 ||
188                     access(master_name, R_OK | W_OK) == 0)
189                     goto got_one;
190                 close(pty_master_fd);
191             }
192         }
193
194     /* If we get here, we couldn't get a tty at all. */
195     fprintf(stderr, "pterm: unable to open a pseudo-terminal device\n");
196     exit(1);
197
198     got_one:
199     strcpy(pty_name, master_name);
200     pty_name[5] = 't';                 /* /dev/ptyXX -> /dev/ttyXX */
201
202     /* We need to chown/chmod the /dev/ttyXX device. */
203     gp = getgrnam("tty");
204     chown(pty_name, getuid(), gp ? gp->gr_gid : -1);
205     chmod(pty_name, 0600);
206 #else
207     pty_master_fd = open("/dev/ptmx", O_RDWR);
208
209     if (pty_master_fd < 0) {
210         perror("/dev/ptmx: open");
211         exit(1);
212     }
213
214     if (grantpt(pty_master_fd) < 0) {
215         perror("grantpt");
216         exit(1);
217     }
218     
219     if (unlockpt(pty_master_fd) < 0) {
220         perror("unlockpt");
221         exit(1);
222     }
223
224     pty_name[FILENAME_MAX-1] = '\0';
225     strncpy(pty_name, ptsname(pty_master_fd), FILENAME_MAX-1);
226 #endif
227 }
228
229 /*
230  * Pre-initialisation. This is here to get around the fact that GTK
231  * doesn't like being run in setuid/setgid programs (probably
232  * sensibly). So before we initialise GTK - and therefore before we
233  * even process the command line - we check to see if we're running
234  * set[ug]id. If so, we open our pty master _now_, chown it as
235  * necessary, and drop privileges. We can always close it again
236  * later. If we're potentially going to be doing utmp as well, we
237  * also fork off a utmp helper process and communicate with it by
238  * means of a pipe; the utmp helper will keep privileges in order
239  * to clean up utmp when we exit (i.e. when its end of our pipe
240  * closes).
241  */
242 void pty_pre_init(void)
243 {
244     pid_t pid;
245     int pipefd[2];
246
247     pty_master_fd = -1;
248
249     if (geteuid() != getuid() || getegid() != getgid()) {
250         pty_open_master();
251     }
252
253 #ifndef OMIT_UTMP
254     /*
255      * Fork off the utmp helper.
256      */
257     if (pipe(pipefd) < 0) {
258         perror("pterm: pipe");
259         exit(1);
260     }
261     pid = fork();
262     if (pid < 0) {
263         perror("pterm: fork");
264         exit(1);
265     } else if (pid == 0) {
266         char display[128], buffer[128];
267         int dlen, ret;
268
269         close(pipefd[1]);
270         /*
271          * Now sit here until we receive a display name from the
272          * other end of the pipe, and then stamp utmp. Unstamp utmp
273          * again, and exit, when the pipe closes.
274          */
275
276         dlen = 0;
277         while (1) {
278             
279             ret = read(pipefd[0], buffer, lenof(buffer));
280             if (ret <= 0) {
281                 cleanup_utmp();
282                 exit(0);
283             } else if (!pty_stamped_utmp) {
284                 if (dlen < lenof(display))
285                     memcpy(display+dlen, buffer,
286                            min(ret, lenof(display)-dlen));
287                 if (buffer[ret-1] == '\0') {
288                     /*
289                      * Now we have a display name. NUL-terminate
290                      * it, and stamp utmp.
291                      */
292                     display[lenof(display)-1] = '\0';
293                     /*
294                      * Trap as many fatal signals as we can in the
295                      * hope of having the best possible chance to
296                      * clean up utmp before termination. We are
297                      * unfortunately unprotected against SIGKILL,
298                      * but that's life.
299                      */
300                     signal(SIGHUP, fatal_sig_handler);
301                     signal(SIGINT, fatal_sig_handler);
302                     signal(SIGQUIT, fatal_sig_handler);
303                     signal(SIGILL, fatal_sig_handler);
304                     signal(SIGABRT, fatal_sig_handler);
305                     signal(SIGFPE, fatal_sig_handler);
306                     signal(SIGPIPE, fatal_sig_handler);
307                     signal(SIGALRM, fatal_sig_handler);
308                     signal(SIGTERM, fatal_sig_handler);
309                     signal(SIGSEGV, fatal_sig_handler);
310                     signal(SIGUSR1, fatal_sig_handler);
311                     signal(SIGUSR2, fatal_sig_handler);
312 #ifdef SIGBUS
313                     signal(SIGBUS, fatal_sig_handler);
314 #endif
315 #ifdef SIGPOLL
316                     signal(SIGPOLL, fatal_sig_handler);
317 #endif
318 #ifdef SIGPROF
319                     signal(SIGPROF, fatal_sig_handler);
320 #endif
321 #ifdef SIGSYS
322                     signal(SIGSYS, fatal_sig_handler);
323 #endif
324 #ifdef SIGTRAP
325                     signal(SIGTRAP, fatal_sig_handler);
326 #endif
327 #ifdef SIGVTALRM
328                     signal(SIGVTALRM, fatal_sig_handler);
329 #endif
330 #ifdef SIGXCPU
331                     signal(SIGXCPU, fatal_sig_handler);
332 #endif
333 #ifdef SIGXFSZ
334                     signal(SIGXFSZ, fatal_sig_handler);
335 #endif
336 #ifdef SIGIO
337                     signal(SIGIO, fatal_sig_handler);
338 #endif
339                     /* Also clean up utmp on normal exit. */
340                     atexit(cleanup_utmp);
341                     setup_utmp(pty_name, display);
342                 }
343             }
344         }
345     } else {
346         close(pipefd[0]);
347         pty_utmp_helper_pid = pid;
348         pty_utmp_helper_pipe = pipefd[1];
349         signal(SIGCHLD, sigchld_handler);
350     }
351 #endif
352
353     /* Drop privs. */
354     {
355         int gid = getgid(), uid = getuid();
356 #ifndef HAVE_NO_SETRESUID
357         int setresgid(gid_t, gid_t, gid_t);
358         int setresuid(uid_t, uid_t, uid_t);
359         setresgid(gid, gid, gid);
360         setresuid(uid, uid, uid);
361 #else
362         setgid(getgid());
363         setuid(getuid());
364 #endif
365     }
366 }
367
368 /*
369  * Called to set up the pty.
370  * 
371  * Returns an error message, or NULL on success.
372  *
373  * Also places the canonical host name into `realhost'. It must be
374  * freed by the caller.
375  */
376 static char *pty_init(void *frontend,
377                       char *host, int port, char **realhost, int nodelay)
378 {
379     int slavefd;
380     pid_t pid, pgrp;
381
382     if (pty_master_fd < 0)
383         pty_open_master();
384
385     /*
386      * Set the backspace character to be whichever of ^H and ^? is
387      * specified by bksp_is_delete.
388      */
389     {
390         struct termios attrs;
391         tcgetattr(pty_master_fd, &attrs);
392         attrs.c_cc[VERASE] = cfg.bksp_is_delete ? '\177' : '\010';
393         tcsetattr(pty_master_fd, TCSANOW, &attrs);
394     }
395
396     /*
397      * Stamp utmp (that is, tell the utmp helper process to do so),
398      * or not.
399      */
400     if (!cfg.stamp_utmp)
401         close(pty_utmp_helper_pipe);   /* just let the child process die */
402     else {
403         char *location = get_x_display();
404         int len = strlen(location)+1, pos = 0;   /* +1 to include NUL */
405         while (pos < len) {
406             int ret = write(pty_utmp_helper_pipe, location+pos, len - pos);
407             if (ret < 0) {
408                 perror("pterm: writing to utmp helper process");
409                 close(pty_utmp_helper_pipe);   /* arrgh, just give up */
410                 break;
411             }
412             pos += ret;
413         }
414     }
415
416     /*
417      * Fork and execute the command.
418      */
419     pid = fork();
420     if (pid < 0) {
421         perror("fork");
422         exit(1);
423     }
424
425     if (pid == 0) {
426         int i;
427         /*
428          * We are the child.
429          */
430
431         slavefd = open(pty_name, O_RDWR);
432         if (slavefd < 0) {
433             perror("slave pty: open");
434             exit(1);
435         }
436
437         close(pty_master_fd);
438         fcntl(slavefd, F_SETFD, 0);    /* don't close on exec */
439         dup2(slavefd, 0);
440         dup2(slavefd, 1);
441         dup2(slavefd, 2);
442         setsid();
443         ioctl(slavefd, TIOCSCTTY, 1);
444         pgrp = getpid();
445         tcsetpgrp(slavefd, pgrp);
446         setpgrp();
447         close(open(pty_name, O_WRONLY, 0));
448         setpgrp();
449         /* Close everything _else_, for tidiness. */
450         for (i = 3; i < 1024; i++)
451             close(i);
452         {
453             char term_env_var[10 + sizeof(cfg.termtype)];
454             sprintf(term_env_var, "TERM=%s", cfg.termtype);
455             putenv(term_env_var);
456         }
457         /*
458          * SIGINT and SIGQUIT may have been set to ignored by our
459          * parent, particularly by things like sh -c 'pterm &' and
460          * some window managers. Reverse this for our child process.
461          */
462         signal(SIGINT, SIG_DFL);
463         signal(SIGQUIT, SIG_DFL);
464         if (pty_argv)
465             execvp(pty_argv[0], pty_argv);
466         else {
467             char *shell = getenv("SHELL");
468             char *shellname;
469             if (cfg.login_shell) {
470                 char *p = strrchr(shell, '/');
471                 shellname = smalloc(2+strlen(shell));
472                 p = p ? p+1 : shell;
473                 sprintf(shellname, "-%s", p);
474             } else
475                 shellname = shell;
476             execl(getenv("SHELL"), shellname, NULL);
477         }
478
479         /*
480          * If we're here, exec has gone badly foom.
481          */
482         perror("exec");
483         exit(127);
484     } else {
485         close(slavefd);
486         pty_child_pid = pid;
487         pty_child_dead = FALSE;
488         signal(SIGCHLD, sigchld_handler);
489     }
490
491     return NULL;
492 }
493
494 /*
495  * Called to send data down the pty.
496  */
497 static int pty_send(char *buf, int len)
498 {
499     while (len > 0) {
500         int ret = write(pty_master_fd, buf, len);
501         if (ret < 0) {
502             perror("write pty master");
503             exit(1);
504         }
505         buf += ret;
506         len -= ret;
507     }
508     return 0;
509 }
510
511 /*
512  * Called to query the current socket sendability status.
513  */
514 static int pty_sendbuffer(void)
515 {
516     return 0;
517 }
518
519 /*
520  * Called to set the size of the window
521  */
522 static void pty_size(void)
523 {
524     struct winsize size;
525
526     size.ws_row = (unsigned short)term->rows;
527     size.ws_col = (unsigned short)term->cols;
528     size.ws_xpixel = (unsigned short) term->cols * font_dimension(0);
529     size.ws_ypixel = (unsigned short) term->rows * font_dimension(1);
530     ioctl(pty_master_fd, TIOCSWINSZ, (void *)&size);
531     return;
532 }
533
534 /*
535  * Send special codes.
536  */
537 static void pty_special(Telnet_Special code)
538 {
539     /* Do nothing! */
540     return;
541 }
542
543 static Socket pty_socket(void)
544 {
545     return NULL;                       /* shouldn't ever be needed */
546 }
547
548 static int pty_sendok(void)
549 {
550     return 1;
551 }
552
553 static void pty_unthrottle(int backlog)
554 {
555     /* do nothing */
556 }
557
558 static int pty_ldisc(int option)
559 {
560     return 0;                          /* neither editing nor echoing */
561 }
562
563 static int pty_exitcode(void)
564 {
565     /* Shouldn't ever be required */
566     return 0;
567 }
568
569 Backend pty_backend = {
570     pty_init,
571     pty_send,
572     pty_sendbuffer,
573     pty_size,
574     pty_special,
575     pty_socket,
576     pty_exitcode,
577     pty_sendok,
578     pty_ldisc,
579     pty_unthrottle,
580     1
581 };