Updated Spanish translation
[vte.git] / gnome-pty-helper / gnome-pty-helper.c
1 /*
2  * gnome-pty.c:  Helper setuid application used to open a pseudo-
3  * terminal, set the permissions, ownership and record user login
4  * information
5  *
6  * Author:
7  *    Miguel de Icaza (miguel@gnu.org)
8  *
9  * Parent application talks to us via a couple of sockets that are strategically
10  * placed on file descriptors 0 and 1 (STDIN_FILENO and STDOUT_FILENO).
11  *
12  * We use the STDIN_FILENO to read and write the protocol information and we use
13  * the STDOUT_FILENO to pass the file descriptors (we need two different file
14  * descriptors as using a socket for both data transfers and file descriptor
15  * passing crashes some BSD kernels according to Theo de Raadt)
16  *
17  * A sample protocol is used:
18  *
19  * OPEN_PTY             => 1 <tag> <master-pty-fd> <slave-pty-fd>
20  *                      => 0
21  *
22  * CLOSE_PTY  <tag>     => void
23  *
24  * <tag> is a pointer.  If tag is NULL, then the ptys were not allocated.
25  * ptys are passed using file descriptor passing on the stdin file descriptor
26  *
27  * We use as little as possible external libraries.
28  */
29 #include <config.h>
30
31 /* Use this to pull SCM_RIGHTS definition on IRIX */
32 #if defined(irix) || defined (__irix__) || defined(sgi) || defined (__sgi__)
33 #    define _XOPEN_SOURCE 1
34 extern char *strdup(const char *);
35 #endif
36
37 #include <sys/types.h>
38 #include <sys/time.h>
39 #include <sys/resource.h>
40 #include <sys/stat.h>
41 #include <limits.h>
42 #include <unistd.h>
43 #include <string.h>
44 #include <signal.h>
45 #include <sys/param.h>
46 #include <fcntl.h>
47 #include <termios.h>
48 #include <errno.h>
49 #include <termios.h>
50 #include <pwd.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <stdio.h>
54 #include <utmp.h>
55 #include <grp.h>
56 #include "gnome-pty.h"
57 #include "gnome-login-support.h"
58
59 /* For PATH_MAX on BSD-like systems. */
60 #ifdef HAVE_SYS_SYSLIMITS_H
61 #include <sys/syslimits.h>
62 #endif
63
64 static struct passwd *pwent;
65 static char login_name_buffer [48];
66 static char *login_name, *display_name;
67
68 struct pty_info {
69         char   *login_name;
70         struct pty_info *next;
71         char   *line;
72         void   *data;
73         char   utmp, wtmp, lastlog;
74 };
75
76 typedef struct pty_info pty_info;
77
78 static pty_info *pty_list;
79
80 #ifdef HAVE_SENDMSG
81 #include <sys/socket.h>
82 #include <sys/uio.h>
83
84 #ifdef HAVE_SYS_UN_H /* Linux libc5 */
85 #include <sys/un.h>
86 #endif
87
88 #ifndef CMSG_DATA /* Linux libc5 */
89 /* Ancillary data object manipulation macros.  */
90 #if !defined __STRICT_ANSI__ && defined __GNUC__ && __GNUC__ >= 2
91 # define CMSG_DATA(cmsg) ((cmsg)->cmsg_data)
92 #else
93 # define CMSG_DATA(cmsg) ((unsigned char *) ((struct cmsghdr *) (cmsg) + 1))
94 #endif
95 #endif /* CMSG_DATA */
96
97 /* Solaris doesn't define these */
98 #ifndef CMSG_ALIGN
99 #define CMSG_ALIGN(len) (((len) + sizeof (size_t) - 1) & (size_t) ~(sizeof (size_t) - 1))
100 #endif
101 #ifndef CMSG_SPACE
102 #define CMSG_SPACE(len) (CMSG_ALIGN (len) + CMSG_ALIGN (sizeof (struct cmsghdr)))
103 #endif
104 #ifndef CMSG_LEN
105 #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len))
106 #endif
107
108 static int
109 pass_fd (int client_fd, int fd)
110 {
111         struct iovec  iov[1];
112         struct msghdr msg;
113         char          buf [1];
114         char    cmsgbuf[CMSG_SPACE(sizeof(int))];
115         struct  cmsghdr *cmptr;
116         int *fdptr;
117
118         iov [0].iov_base = buf;
119         iov [0].iov_len  = 1;
120
121         msg.msg_iov        = iov;
122         msg.msg_iovlen     = 1;
123         msg.msg_name       = NULL;
124         msg.msg_namelen    = 0;
125         msg.msg_control    = (caddr_t) cmsgbuf;
126         msg.msg_controllen = sizeof(cmsgbuf);
127
128         cmptr = CMSG_FIRSTHDR(&msg);
129         cmptr->cmsg_level = SOL_SOCKET;
130         cmptr->cmsg_type  = SCM_RIGHTS;
131         cmptr->cmsg_len   = CMSG_LEN(sizeof(int));
132         fdptr = (int *) CMSG_DATA(cmptr);
133         memcpy (fdptr, &fd, sizeof(int));
134         if (sendmsg (client_fd, &msg, 0) != 1)
135                 return -1;
136
137         return 0;
138 }
139
140 #elif defined(__sgi) && !defined(HAVE_SENDMSG)
141
142 /*
143  * IRIX 6.2 is like 4.3BSD; it will not have HAVE_SENDMSG set,
144  * because msghdr used msg_accrights and msg_accrightslen rather
145  * than the newer msg_control and msg_controllen fields configure
146  * checks.  The SVR4 code below doesn't work because pipe()
147  * semantics are controlled by the svr3pipe systune variable,
148  * which defaults to uni-directional pipes.  Also sending
149  * file descriptors through pipes isn't implemented.
150  */
151
152 #include <sys/socket.h>
153 #include <sys/uio.h>
154
155 static int
156 pass_fd (int client_fd, int fd)
157 {
158   struct iovec  iov[1];
159   struct msghdr msg;
160   char          buf [1];
161
162   iov [0].iov_base = buf;
163   iov [0].iov_len  = 1;
164
165   msg.msg_iov        = iov;
166   msg.msg_iovlen     = 1;
167   msg.msg_name       = NULL;
168   msg.msg_namelen    = 0;
169   msg.msg_accrights    = (caddr_t) &fd;
170   msg.msg_accrightslen = sizeof(fd);
171
172   if (sendmsg (client_fd, &msg, 0) != 1)
173     return -1;
174
175   return 0;
176 }
177
178 #else
179 #include <stropts.h>
180 #ifdef I_SENDFD
181
182 int
183 pass_fd (int client_fd, int fd)
184 {
185         if (ioctl (client_fd, I_SENDFD, fd) < 0)
186                 return -1;
187         return 0;
188 }
189 #endif
190 #endif
191
192 static void
193 pty_free (pty_info *pi)
194 {
195         free (pi);
196 }
197
198 static void
199 pty_remove (pty_info *pi)
200 {
201         pty_info *l, *last;
202
203         last = (void *) 0;
204
205         for (l = pty_list; l; l = l->next) {
206                 if (l == pi) {
207                         if (last == (void *) 0)
208                                 pty_list = pi->next;
209                         else
210                                 last->next = pi->next;
211                         free (pi->line);
212                         free (pi->login_name);
213                         pty_free (pi);
214                         return;
215                 }
216                 last = l;
217         }
218
219         exit (1);
220 }
221
222 static void
223 shutdown_pty (pty_info *pi)
224 {
225         if (pi->utmp || pi->wtmp || pi->lastlog)
226                 if (pi->data)
227                         write_logout_record (pi->login_name, pi->data, pi->utmp, pi->wtmp);
228
229         pty_remove (pi);
230 }
231
232 static void
233 shutdown_helper (void)
234 {
235         pty_info *pi;
236
237         for (pi = pty_list; pi; pi = pty_list)
238                 shutdown_pty (pi);
239 }
240
241 static pty_info *
242 pty_add (int utmp, int wtmp, int lastlog, char *line, char *login_name)
243 {
244         pty_info *pi = malloc (sizeof (pty_info));
245
246         if (pi == NULL) {
247                 shutdown_helper ();
248                 exit (1);
249         }
250
251         memset (pi, 0, sizeof (pty_info));
252
253         if (strncmp (line, "/dev/", 5))
254                 pi->line = strdup (line);
255         else
256                 pi->line = strdup (line+5);
257
258         if (pi->line == NULL) {
259                 shutdown_helper ();
260                 exit (1);
261         }
262
263         pi->next = pty_list;
264         pi->utmp = utmp;
265         pi->wtmp = wtmp;
266         pi->lastlog = lastlog;
267         pi->login_name = strdup (login_name);
268
269         pty_list = pi;
270
271         return pi;
272 }
273
274 static struct termios*
275 init_term_with_defaults(struct termios* term)
276 {
277         /*
278          *      openpty assumes POSIX termios so this should be portable.
279          *      Don't change this to a structure init - POSIX doesn't say
280          *      anything about field order.
281          */
282         memset(term, 0, sizeof(struct termios));
283
284         term->c_iflag = 0
285 #ifdef BRKINT
286           | BRKINT
287 #endif
288 #ifdef ICRNL
289           | ICRNL
290 #endif
291 #ifdef IMAXBEL
292           | IMAXBEL
293 #endif
294 #ifdef IXON
295           | IXON
296 #endif
297 #ifdef IXANY
298           | IXANY
299 #endif
300           ;
301         term->c_oflag = 0
302 #ifdef OPOST
303           | OPOST
304 #endif
305 #ifdef ONLCR
306           | ONLCR
307 #endif
308 #ifdef NL0
309           | NL0
310 #endif
311 #ifdef CR0
312           | CR0
313 #endif
314 #ifdef TAB0
315           | TAB0
316 #endif
317 #ifdef BS0
318           | BS0
319 #endif
320 #ifdef VT0
321           | VT0
322 #endif
323 #ifdef FF0
324           | FF0
325 #endif
326           ;
327         term->c_cflag = 0
328 #ifdef CREAD
329           | CREAD
330 #endif
331 #ifdef CS8
332           | CS8
333 #endif
334 #ifdef HUPCL
335           | HUPCL
336 #endif
337           ;
338 #ifdef EXTB
339         cfsetispeed(term, EXTB);
340         cfsetospeed(term, EXTB);
341 #else
342 #   ifdef B38400
343         cfsetispeed(term, B38400);
344         cfsetospeed(term, B38400);
345 #   else
346 #       ifdef B9600
347         cfsetispeed(term, B9600);
348         cfsetospeed(term, B9600);
349 #       endif
350 #   endif
351 #endif /* EXTB */
352
353         term->c_lflag = 0
354 #ifdef ECHO
355           | ECHO
356 #endif
357 #ifdef ICANON
358           | ICANON
359 #endif
360 #ifdef ISIG
361           | ISIG
362 #endif
363 #ifdef IEXTEN
364           | IEXTEN
365 #endif
366 #ifdef ECHOE
367           | ECHOE
368 #endif
369 #ifdef ECHOKE
370           | ECHOKE
371 #endif
372 #ifdef ECHOK
373           | ECHOK
374 #endif
375 #ifdef ECHOCTL
376           | ECHOCTL
377 #endif
378           ;
379
380 #ifdef N_TTY
381         /* should really be a check for c_line, but maybe this is good enough */
382         term->c_line = N_TTY;
383 #endif
384
385         /* These two may overlap so set them first */
386         /* That setup means, that read() will be blocked until  */
387         /* at least 1 symbol will be read.                      */
388         term->c_cc[VMIN]  = 1;
389         term->c_cc[VTIME] = 0;
390
391         /*
392          * Now set the characters. This is of course a religious matter
393          * but we use the defaults, with erase bound to the key gnome-terminal
394          * maps.
395          *
396          * These are the ones set by "stty sane".
397          */
398
399         term->c_cc[VINTR]  = 'C'-64;
400         term->c_cc[VQUIT]  = '\\'-64;
401         term->c_cc[VERASE] = 127;
402         term->c_cc[VKILL]  = 'U'-64;
403         term->c_cc[VEOF]   = 'D'-64;
404 #ifdef VSWTC
405         term->c_cc[VSWTC]  = 255;
406 #endif
407         term->c_cc[VSTART] = 'Q'-64;
408         term->c_cc[VSTOP]  = 'S'-64;
409         term->c_cc[VSUSP]  = 'Z'-64;
410         term->c_cc[VEOL]   = 255;
411
412         /*
413          *      Extended stuff.
414          */
415
416 #ifdef VREPRINT
417         term->c_cc[VREPRINT] = 'R'-64;
418 #endif
419 #ifdef VSTATUS
420         term->c_cc[VSTATUS]  = 'T'-64;
421 #endif
422 #ifdef VDISCARD
423         term->c_cc[VDISCARD] = 'O'-64;
424 #endif
425 #ifdef VWERASE
426         term->c_cc[VWERASE]  = 'W'-64;
427 #endif
428 #ifdef VLNEXT
429         term->c_cc[VLNEXT]   = 'V'-64;
430 #endif
431 #ifdef VDSUSP
432         term->c_cc[VDSUSP]   = 'Y'-64;
433 #endif
434 #ifdef VEOL2
435         term->c_cc[VEOL2]    = 255;
436 #endif
437     return term;
438 }
439
440 static int
441 open_ptys (int utmp, int wtmp, int lastlog)
442 {
443         const char *term_name;
444         int status, master_pty, slave_pty;
445         pty_info *p;
446         int result;
447         uid_t savedUid;
448         gid_t savedGid;
449         struct group *group_info;
450         struct termios term;
451
452         /* Initialize term */
453         init_term_with_defaults(&term);
454
455         /* root privileges */
456         savedUid = geteuid();
457         savedGid = getegid();
458
459         /* drop privileges to the user level */
460 #if defined(HAVE_SETEUID)
461         seteuid (pwent->pw_uid);
462         setegid (pwent->pw_gid);
463 #elif defined(HAVE_SETREUID)
464         setreuid (savedUid, pwent->pw_uid);
465         setregid (savedGid, pwent->pw_gid);
466 #else
467 #error "No means to drop privileges! Huge security risk! Won't compile."
468 #endif
469         /* Open pty with privileges of the user */
470         status = openpty (&master_pty, &slave_pty, NULL, &term, NULL);
471
472         /* Restore saved privileges to root */
473 #ifdef HAVE_SETEUID
474         seteuid (savedUid);
475         setegid (savedGid);
476 #elif defined(HAVE_SETREUID)
477         setreuid (pwent->pw_uid, savedUid);
478         setregid (pwent->pw_gid, savedGid);
479 #else
480 #error "No means to raise privileges! Huge security risk! Won't compile."
481 #endif
482         /* openpty() failed, reject request */
483         if (status == -1 || (term_name = ttyname(slave_pty)) == NULL) {
484                 result = 0;
485                 n_write (STDIN_FILENO, &result, sizeof (result));
486                 return 0;
487         }
488
489         /* a bit tricky, we re-do the part of the openpty()  */
490         /* that required root privileges, and, hence, failed */
491         group_info = getgrnam ("tty");
492         fchown (slave_pty, getuid (), group_info ? group_info->gr_gid : -1);
493         fchmod (slave_pty, S_IRUSR | S_IWUSR | S_IWGRP);
494         /* It's too late to call revoke at this time... */
495         /* revoke(term_name); */
496
497         /* add pty to the list of allocated by us */
498         p = pty_add (utmp, wtmp, lastlog, term_name, login_name);
499         result = 1;
500
501         if (n_write (STDIN_FILENO, &result, sizeof (result)) != sizeof (result) ||
502             n_write (STDIN_FILENO, &p, sizeof (p)) != sizeof (p) ||
503             pass_fd (STDOUT_FILENO, master_pty)  == -1 ||
504             pass_fd (STDOUT_FILENO, slave_pty)   == -1) {
505                 exit (0);
506         }
507
508         if (utmp || wtmp || lastlog) {
509                 p->data = write_login_record (login_name, display_name,
510                                               term_name, utmp, wtmp, lastlog);
511         }
512
513         close (master_pty);
514         close (slave_pty);
515
516         return 1;
517 }
518
519 static void
520 close_pty_pair (void *tag)
521 {
522         pty_info *pi;
523
524         for (pi = pty_list; pi; pi = pi->next) {
525                 if (tag == pi) {
526                         shutdown_pty (pi);
527                         break;
528                 }
529         }
530 }
531
532 #define MB (1024*1024)
533
534 struct {
535         int limit;
536         int value;
537 } sensible_limits [] = {
538         { RLIMIT_CPU,    120 },
539         { RLIMIT_FSIZE,  1 * MB },
540         { RLIMIT_DATA,   1 * MB },
541         { RLIMIT_STACK,  1 * MB },
542 #ifdef RLIMIT_AS
543         { RLIMIT_AS,     1 * MB },
544 #endif
545         { RLIMIT_NOFILE, 10 },
546 #ifdef RLIMIT_NPROC
547         { RLIMIT_NPROC,  5 },
548 #endif
549         { -1, -1 }
550 };
551
552 static void
553 sanity_checks (void)
554 {
555         int stderr_fd;
556         int i, open_max;
557         int flag;
558
559         /*
560          * Make sure stdin/stdout are open.  This is a requirement
561          * for our program to work and closes potential security holes.
562          */
563         if ((fcntl (0, F_GETFL, &flag) == -1 && errno == EBADF) ||
564             (fcntl (1, F_GETFL, &flag) == -1 && errno == EBADF)) {
565                 exit (1);
566         }
567
568         /*
569          * File descriptors 0 and 1 have been setup by the parent process
570          * to be used for the protocol exchange and for transfering
571          * file descriptors.
572          *
573          * Make stderr point to a terminal.
574          */
575         if (fcntl (2, F_GETFL, &flag) == -1 && errno == EBADF) {
576                 stderr_fd = open ("/dev/tty", O_RDWR);
577                 if (stderr_fd == -1) {
578                         stderr_fd = open ("/dev/null", O_RDWR);
579                         if (stderr_fd == -1)
580                                 exit (1);
581                 }
582
583                 if (stderr_fd != 2)
584                         while (dup2 (stderr_fd, 2) == -1 && errno == EINTR)
585                                 ;
586         }
587
588         /* Close any file descriptor we do not use */
589         open_max = sysconf (_SC_OPEN_MAX);
590         for (i = 3; i < open_max; i++) {
591                 close (i);
592         }
593
594         /* Check sensible resource limits */
595         for (i = 0; sensible_limits [i].value != -1; i++) {
596                 struct rlimit rlim;
597
598                 if (getrlimit (sensible_limits [i].limit, &rlim) != 0)
599                         continue;
600
601                 if (rlim.rlim_cur != RLIM_INFINITY &&
602                     rlim.rlim_cur < sensible_limits [i].value) {
603                         if (setrlimit (sensible_limits [i].limit, &rlim) != 0) {
604                                 fprintf (stderr, "Living environment not ok\n");
605                                 exit (1);
606                         }
607                 }
608         }
609
610         /* Make sure SIGIO/SIGINT is SIG_IGN */
611         {
612                 struct sigaction sa;
613                 sigset_t sigset;
614
615                 sa.sa_handler = SIG_IGN;
616                 sigemptyset (&sa.sa_mask);
617                 sa.sa_flags = 0;
618
619                 sigemptyset(&sigset);
620                 sigaddset(&sigset, SIGIO);
621                 sigaddset(&sigset, SIGINT);
622                 sigprocmask(SIG_UNBLOCK, &sigset, NULL);
623
624                 sigaction (SIGIO, &sa, NULL);
625                 sigaction (SIGINT, &sa, NULL);
626         }
627 }
628
629 static volatile int done;
630
631 static void
632 exit_handler (int signum)
633 {
634         done = 1;
635 }
636
637
638 int
639 main (int argc, char *argv [])
640 {
641         int res, n;
642         void *tag;
643         GnomePtyOps op;
644         const char *logname;
645
646         sanity_checks ();
647
648         pwent = NULL;
649
650         logname = getenv ("LOGNAME");
651         if (logname != NULL) {
652                 pwent = getpwnam (logname);
653                 if (pwent != NULL && pwent->pw_uid != getuid ()) {
654                         /* LOGNAME is lying, fall back to looking up the uid */
655                         pwent = NULL;
656                 }
657         }
658
659         if (pwent == NULL)
660                 pwent = getpwuid (getuid ());
661
662         if (pwent)
663                 login_name = pwent->pw_name;
664         else {
665                 sprintf (login_name_buffer, "#%u", (unsigned int)getuid ());
666                 login_name = login_name_buffer;
667         }
668
669         /* Change directory so we don't prevent unmounting in case the initial cwd
670          * is on an external device (see bug #574491). 
671          */
672         if (chdir ("/") < 0)
673                 fprintf (stderr, "Failed to chdir to /: %s\n", strerror (errno));
674  
675         display_name = getenv ("DISPLAY");
676         if (!display_name)
677                 display_name = "localhost";
678
679         done = 0;
680
681         /* Make sure we clean up utmp/wtmp even under vncserver */
682         signal (SIGHUP, exit_handler);
683         signal (SIGTERM, exit_handler);
684
685         while (!done) {
686                 res = n_read (STDIN_FILENO, &op, sizeof (op));
687
688                 if (res != sizeof (op)) {
689                         done = 1;
690                         continue;
691                 }
692
693                 switch (op) {
694                 case GNOME_PTY_OPEN_PTY_UTMP:
695                         open_ptys (1, 0, 0);
696                         break;
697
698                 case GNOME_PTY_OPEN_PTY_UWTMP:
699                         open_ptys (1, 1, 0);
700                         break;
701
702                 case GNOME_PTY_OPEN_PTY_WTMP:
703                         open_ptys (0, 1, 0);
704                         break;
705
706                 case GNOME_PTY_OPEN_PTY_LASTLOG:
707                         open_ptys (0, 0, 1);
708                         break;
709
710                 case GNOME_PTY_OPEN_PTY_LASTLOGUTMP:
711                         open_ptys (1, 0, 1);
712                         break;
713
714                 case GNOME_PTY_OPEN_PTY_LASTLOGUWTMP:
715                         open_ptys (1, 1, 1);
716                         break;
717
718                 case GNOME_PTY_OPEN_PTY_LASTLOGWTMP:
719                         open_ptys (0, 1, 1);
720                         break;
721
722                 case GNOME_PTY_OPEN_NO_DB_UPDATE:
723                         open_ptys (0, 0, 0);
724                         break;
725
726                 case GNOME_PTY_RESET_TO_DEFAULTS:
727                         break;
728
729                 case GNOME_PTY_CLOSE_PTY:
730                         n = n_read (STDIN_FILENO, &tag, sizeof (tag));
731                         if (n != sizeof (tag)) {
732                                 shutdown_helper ();
733                                 exit (1);
734                         }
735                         close_pty_pair (tag);
736                         break;
737
738                 case GNOME_PTY_SYNCH:
739                         {
740                                 int result = 0;
741                                 n_write (STDIN_FILENO, &result, 1);
742                         }
743                         break;
744                 }
745
746         }
747
748         shutdown_helper ();
749         return 0;
750 }
751