source: trunk/third/vte/gnome-pty-helper/gnome-pty-helper.c @ 22386

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