home *** CD-ROM | disk | FTP | other *** search
- /* Copyright 1990, Daniel J. Bernstein. All rights reserved. */
-
- /*
- pty.c: run a program under a pty session
- */
-
- #include <stdio.h>
- extern unsigned short getuid(); /* grrrr */
- extern unsigned short geteuid(); /* grrrr */
- #include "config.h"
- #include "getopt.h"
- #include "err.h"
- #include "pty.h"
- #include "tty.h"
- #include "texts.h"
- #include "sig.h"
- #include "sigler.h"
- #include "master.h"
- #include "slave.h"
- #include "file.h"
- #include "logs.h"
- #include "misc.h"
-
- int flagpcbreak = 0; /* -pc, character-at-a-time */
- int flagpnew = 1; /* -pd, new line discipline---traditionally off to start */
- int flagpecho = 1; /* -pe, echo characters */
- int flagpcrmod = 1; /* -pn, munge carriage returns */
- int flagpraw = 0; /* -pr, raw mode */
- int flagpcrt = 1; /* -ps, screen */
-
- getfreepty(fnmty,fnsty,pty1,pty2)
- register char fnmty[sizeof(DEVMTY)];
- register char fnsty[sizeof(DEVSTY)];
- register char pty1[sizeof(PTY1)];
- register char pty2[sizeof(PTY2)];
- {
- register char *c1;
- register char *c2;
- register char *c1start; /* for ``random'' pty searching */
- register char *c2start;
- int e;
-
- if (flagxrandom)
- {
- c1start = pty1 + (pid % (sizeof(PTY1) - 1));
- c2start = pty2 + ((pid + date) % (sizeof(PTY2) - 1));
- }
- else
- {
- c1start = pty1;
- c2start = pty2;
- }
-
- c1 = c1start;
- do
- {
- fnmty[sizeof(DEVMTY) - 3] = *c1;
- fnmty[sizeof(DEVMTY) - 2] = pty2[0];
- if (!access(fnmty,F_OK))
- {
- c2 = c2start;
- fnsty[sizeof(DEVSTY) - 3] = *c1;
- fnmty[sizeof(DEVMTY) - 2] = fnsty[sizeof(DEVSTY) - 2] = *c2;
- do
- {
- #ifdef DESPERATE_ALARMS
- sig_startring();
- #endif
-
- /* Some other process could come along and mess up our test by opening */
- /* the master side before we do. But in that case they'll get the pty */
- /* anyway, and we'll move on to another possibility without comment. */
- if (flagxchkopen)
- {
- #ifdef DONT_NDELAY
- fdsty = open(fnsty,O_RDWR);
- #else
- fdsty = open(fnsty,O_RDWR | O_NDELAY);
- #endif
- e = errno;
- fdmty = open(fnmty,O_RDWR);
- }
- else
- {
- fdmty = open(fnmty,O_RDWR);
- fdsty = open(fnsty,O_RDWR);
- e = errno;
- }
-
- #ifdef DESPERATE_ALARMS
- sig_stopring();
- #endif
-
- if (fdmty != -1)
- {
- if (flagxskipopen && (fdsty != -1))
- warnerr2("pty: warning: slave %s still in use\n",fnsty);
- else
- {
- if ((fdsty == -1) && (e != EINTR) && (e != EWOULDBLOCK))
- fatalerr2p(6,"pty: fatal: slave %s unopenable",fnsty,e);
- if (flagxchkopen)
- if (fdsty == -1)
- {
- fdsty = open(fnsty,O_RDWR);
- e = errno;
- }
- else
- warnerr2("pty: warning: slave %s still in use\n",fnsty);
- if (fdsty == -1)
- fatalerr2p(6,"pty: fatal: slave %s unopenable",fnsty,e);
- else
- {
- if (flagxchkopen)
- if (fcntl(fdsty,F_SETFL,0) == -1)
- fatalerrp(6,"pty: fatal: can't fcntl pty",e);
- return 0;
- }
- }
- }
-
- if (fdmty != -1) (void) close(fdmty);
- if (fdsty != -1) (void) close(fdsty);
- if (!(*(++c2)))
- c2 = pty2;
- fnmty[sizeof(DEVMTY) - 2] = fnsty[sizeof(DEVSTY) - 2] = *c2;
- }
- while (c2 != c2start);
- }
- if (!(*(++c1)))
- c1 = pty1;
- }
- while (c1 != c1start);
- return -1;
- }
-
- char fnmty[sizeof(DEVMTY)] = DEVMTY;
- char fnsty[sizeof(DEVSTY)] = DEVSTY;
- char pty1[sizeof(PTY1)] = PTY1;
- char pty2[sizeof(PTY2)] = PTY2;
-
- main(argc,argv)
- int argc;
- char *argv[];
- {
- int opt;
- int f;
-
- uid = getuid();
- euid = geteuid();
- pid = getpid();
- pgrp = getpgrp(0);
- date = now();
- setusername();
-
- while ((opt = getopt(argc,argv,"qQvdDe3Ef:FjJsStTp:x:0ACHUVW")) != EOF)
- switch(opt)
- {
- case 'A': fatalinfo(1,ptyauthor);
- case 'C': fatalinfo(1,ptycopyright);
- case 'H': fatalinfo(1,ptyhelp);
- case 'U': fatalinfo(1,ptyusage);
- case 'V': fatalinfo(1,ptyversion);
- case 'W': fatalinfo(1,ptywarranty);
- case '?': fatalinfo(1,ptyusage);
- case 'q': flagquiet = 1; break;
- case 'Q': flagquiet = 0; flagverbose = 0; break;
- case 'v': flagverbose = 1; break;
- case 'd': flagdetached = 1; flagjobctrl = 0; flagttymodes = 0; break;
- case 'D': flagdetached = 0; flagjobctrl = 1; flagttymodes = 1; break;
- case 'e': flagsameerr = 2; break;
- case '3': flagsameerr = 1; break;
- case 'E': flagsameerr = 0; break;
- case 'f': flagfdpass = 1;
- if (sscanf(optarg,"%d",&fdpass) < 1) fatalinfo(1,ptyusage);
- break;
- case 'F': flagfdpass = 0; break;
- case 'j': flagjobctrl = 1; break;
- case 'J': flagjobctrl = 0; break;
- case 's': flagsession = 1; flagxutmp = 1; break;
- case 'S': flagsession = 0; flagxutmp = 0; break;
- case 't': flagttymodes = 1; break;
- case 'T': flagttymodes = 0; break;
- case '0': flagsameerr = 2; flagsession = 0; flagttymodes = 0;
- flagxutmp = 0; flagxwtmp = 0;
- flagpcbreak = 3; flagpraw = 3; flagpecho = 2; flagpnew = 2;
- break;
- case 'p': while (opt = *(optarg++))
- switch(opt)
- {
- case 'c': flagpcbreak = 3; break;
- case 'C': flagpcbreak = 2; break;
- case 'd': flagpnew = 3; break;
- case 'D': flagpnew = 2; break;
- case 'e': flagpecho = 3; break;
- case 'E': flagpecho = 2; break;
- case 'n': flagpcrmod = 3; break;
- case 'N': flagpcrmod = 2; break;
- case 'r': flagpraw = 3; break;
- case 'R': flagpraw = 2; break;
- case 's': flagpcrt = 3; break;
- case 'S': flagpcrt = 2; break;
- case '0': flagpcbreak = 3; flagpraw = 3;
- flagpecho = 2; flagpnew = 2;
- break;
- default: fatalinfo(1,ptyusage); break;
- }
- break;
- case 'x': while (opt = *(optarg++))
- switch(opt)
- {
- case 'c': flagxchown = 1; break;
- case 'C': flagxchown = 0; break;
- case 'u': flagxutmp = 1; break;
- case 'U': flagxutmp = 0; break;
- case 'w': flagxwtmp = 1; break;
- case 'W': flagxwtmp = 0; break;
- case 'x': flagxexcl = 1; break;
- case 'X': flagxexcl = 0; break;
- case 'e': flagxerrwo = 1; break;
- case 'E': flagxerrwo = 0; break;
- case 'n': flagxchkopen = 1; break;
- case 'N': flagxchkopen = 0; break;
- case 'o': flagxskipopen = 1; break;
- case 'O': flagxskipopen = 0; break;
- case 'r': flagxrandom = 1; break;
- case 'R': flagxrandom = 0; break;
- case 's': flagxsetuid = 1; break;
- case 'S': flagxsetuid = 0; break;
- default: fatalinfo(1,ptyusage); break;
- }
- break;
- }
- argv += optind, argc -= optind;
-
- if (!*argv)
- fatalinfo(1,ptyusage);
-
- /* Option forcing. */
- #ifdef NO_UTMP
- if (flagxutmp) if (flagverbose) warnerr2("%s","pty: utmp forced off\n");
- flagxutmp = 0;
- #endif
- #ifdef NO_WTMP
- if (flagxwtmp) if (flagverbose) warnerr2("%s","pty: wtmp forced off\n");
- flagxwtmp = 0;
- #endif
- #ifdef NO_CHOWN
- if (flagxchown) if (flagverbose) warnerr2("%s","pty: chown forced off\n");
- flagxchown = 0;
- #endif
- #ifdef NO_SESSION
- if (flagsession) if (flagverbose) warnerr2("%s","pty: session forced off\n");
- flagsession = 0;
- #endif
- #ifdef MUST_UTMP
- if (flagxutmp) if (flagverbose) warnerr2("%s","pty: utmp forced on\n");
- flagxutmp = 1;
- #endif
- #ifdef MUST_WTMP
- if (flagxwtmp) if (flagverbose) warnerr2("%s","pty: wtmp forced on\n");
- flagxwtmp = 1;
- #endif
- #ifdef MUST_CHOWN
- if (flagxchown) if (flagverbose) warnerr2("%s","pty: chown forced on\n");
- flagxchown = 1;
- #endif
- #ifdef MUST_SESSION
- if (flagsession) if (flagverbose) warnerr2("%s","pty: session forced on\n");
- flagsession = 1;
- #endif
- #ifdef NO_FDPASSING
- if (flagfdpass) if (flagverbose) warnerr2("%s","pty: fd passing forced off\n");
- flagfdpass = 0;
- #endif
-
- /* Option munging. */
- if (flagsession) flagsameerr = 0;
- if (flagdetached) flagttymodes = 0;
- if (flagxskipopen) flagxchkopen = 1;
-
- if (!flagxsetuid)
- {
- (void) setreuid(uid,uid);
- euid = uid;
- }
-
-
- sig_init();
- sig_sethandler(SIGALRM,nothing); sig_handle(SIGALRM);
- sig_default(SIGTTIN);
- sig_default(SIGTTOU);
-
- if (fdpass == -1) /* wow, was this a source of bugs. */
- {
- fdin = 0;
- fdout = 1;
- }
-
- if (flagdetached)
- {
- tty_initmodes(&tmopty,flagpcbreak,flagpnew,flagpecho,
- flagpcrmod,flagpraw,flagpcrt);
- }
- else
- {
- if ((fdtty = tty_getctrl()) == -1)
- fatalerr(2,"pty: fatal: cannot find control terminal; try -d?\n");
- if (tty_getmodes(fdtty,&tmotty) == -1)
- fatalerr(3,"pty: fatal: cannot get current tty modes\n");
- /* XXX: is there a way to recover more gracefully? */
- tty_copymodes(&tmopty,&tmotty);
- tty_mungemodes(&tmopty,flagpcbreak,flagpnew,flagpecho,
- flagpcrmod,flagpraw,flagpcrt);
- tty_copymodes(&tmochartty,&tmotty);
- if (flagttymodes)
- tty_charmode(&tmochartty);
- }
-
- /* XXX: Here would be a good spot to include pty limits, say through */
- /* the file PTYDIR/LIMITS. Lines of the form user group num, saying */
- /* that user in that group is limited to num ptys, with * for all. */
- /* All pty use would have to be logged somewhere. Anyway, with a */
- /* streams-based pty, there wouldn't be much point to limits. */
-
- if (getfreepty(fnmty,fnsty,pty1,pty2) == -1)
- fatalerr(5,"pty: fatal: no ptys available\n");
-
- if (flagverbose)
- warnerr2("pty: successfully opened pty %s\n",fnsty);
-
- if (tty_modifymodes(fdtty,&tmochartty,&tmotty) == -1)
- {
- (void) tty_setmodes(fdtty,&tmotty); /* XXX --- gasp */
- fatalerr(4,"pty: fatal: cannot set modes of original tty\n");
- }
-
- /* In general, BSD systems check MAXUPRC against the effective uid, */
- /* rather than the real uid; and they check it during a fork(). */
- /* The combination of these annoying behaviors means that we have */
- /* to switch uids while forking, hence possibly losing any security */
- /* measures we may have set up before the fork(). Grrrr. */
-
- (void) setreuid(euid,uid);
- if ((f = fork()) == -1)
- {
- (void) tty_modifymodes(fdtty,&tmotty,&tmochartty);
- fatalerr(7,"pty: fatal: cannot fork once\n");
- /* After this, the signaller will handle tty modes. */
- }
- else if (f == 0)
- if ((f = fork()) == -1)
- {
- (void) kill(pid,SIGTERM); /*XXX*/
- fatalerr(7,"pty: fatal: cannot fork twice\n");
- }
- else if (f == 0)
- {
- (void) setreuid(uid,euid);
- slave(fnsty,argv);
- }
- else
- {
- (void) setreuid(uid,euid);
- if (flagsession)
- if (sessdir() == -1)
- fatal(1);
- master(fnsty,f);
- }
- else
- {
- (void) setreuid(uid,euid);
- if (flagsession)
- if (sessdir() == -1)
- {
- fatalerr(8,"pty: fatal: cannot change to session directory\n");
- (void) tty_modifymodes(fdtty,&tmotty,&tmochartty);
- }
- sigler(fnsty,f);
- }
-
- fatal(9); /* just in case */
- /*NOTREACHED*/
- }
-