/* * LocalShell like RTTY LoginSh, only local, instead of remote. * * Must be setuid-root for the chown's to work. * * Peter B. Kessler, September 26, 1990 9:10:17 pm PDT * */ static char sccsid[] = "@(#)LocalShell.c 1.2 10/4/90 13:10:33"; #include <sys/types.h> #include <errno.h> #include <lastlog.h> #include <signal.h> #include <stdio.h> #include <sysexits.h> #include <sys/file.h> #include <sys/stat.h> #include <utmp.h> #include <sys/wait.h> #include <sys/termios.h> #define FALSE 0 #define TRUE 1 char myName[] = "LocalShell"; FILE *debugStream; main(argc, argv) int argc; char **argv; { int argn; /* in [0 .. argc] */ char **argp; /* pointer to elements of argv */ int consoleFlag = 0; int cleanFlag = 1; char *ptyName = 0; char *ttyName = 0; char *termEnvItem = 0; int child = -1; int exitStatus = 0; void showState(); for (argn = 1, argp = argv+1; argn < argc; argn += 1, argp += 1) { if (argp == 0 || *argp == 0 || (*argp)[0] != '-' || (*argp)[1] == '\0') { break; }; switch ((*argp)[1]) { case 'C': consoleFlag = 1; break; case 'p': argp += 1; argn += 1; ptyName = *argp; break; case 't': argp += 1; argn += 1; ttyName = *argp; break; case 'e': cleanFlag = 0; break; case 'f': cleanFlag = 0; argp += 1; argn += 1; termEnvItem = *argp; break; default: goto usage; }; }; if (ptyName == 0 || ttyName == 0) { goto usage; }; { debugStream = fopen("/dev/console", "a"); if (debugStream == NULL) { fprintf(stderr, "couldn't get console, errno = %d\n", errno); exit(1); }; }; showState(debugStream, "Before:"); { close(0); close(1); close(2); } child = fork(); switch (child) { default: /* we are the parent */ exitStatus = BabySit(); Shutdown(ptyName, ttyName); break; case 0: /* we are the child */ showState(debugStream, "Child, before setsid:"); { /* try to make ourselves a new session/pgrp */ int result; result = setpgrp(0, 0); if (result < 0) { fprintf(debugStream, "setpgrp(0, 0) returns %d errno = %d\n", result, errno); }; }; showState(debugStream, "Child, after setsid:"); Setup(ptyName, ttyName, consoleFlag); showState(debugStream, "Child, after Setup:"); BecomeOurselves(); exitStatus = DoShell(argp, cleanFlag, termEnvItem); break; case -1: /* something went wrong */ perror("can't fork."); exitStatus = 1; break; }; exit(exitStatus); usage: fprintf(stderr, "usage is: %s -p ptyName -t ttyName [-C]\n", myName); exit(1); } /* * wait for child to return. */ int BabySit() { union wait status; int exited = FALSE; int waitResult; int result = 0; while (! exited) { waitResult = wait(&status); switch (waitResult) { case -1: result = errno; exited = TRUE; /* might as well be */ break; default: exited = (WIFEXITED(status) || WIFSIGNALED(status)); result = status.w←retcode; break; }; }; return result; }; /* * Release named pty/tty pair. */ Shutdown(ptyName, ttyName) char *ptyName; char *ttyName; { /* must be suid-root here */ if (chown(ttyName, 0, 0) != 0) { perror("Couldn't chown tty"); }; if (chmod(ttyName, 0666) != 0) { perror("Couldn't chmod tty"); }; if (chown(ptyName, 0, 0) != 0) { perror("Couldn't chown pty"); }; if (chmod(ptyName, 0666) != 0) { perror("Couldn't chmod pty"); }; WriteUtmp(ttyName, "", ""); } Setup(ptyName, ttyName, consoleFlag) char *ptyName; char *ttyName; int consoleFlag; { int fileDescriptor; struct termios terminalParameters; int gotParameters = FALSE; char *getenv(); { fileDescriptor = open("/dev/tty", O←RDWR, 0666); if (fileDescriptor >= 0) { ioctl(fileDescriptor, TCGETS, &terminalParameters); gotParameters = TRUE; (void) ioctl(fileDescriptor, TIOCNOTTY, (char *) 0); close(fileDescriptor); }; }; WriteUtmp(ttyName, getenv("USER"), "PCedar"); fileDescriptor = open(ttyName, O←RDWR); if (fileDescriptor < 0) { fprintf(debugStream, "Can't open(%s)\n", ttyName); exit(1); }; { int result = dup2(fileDescriptor, 0); if (result < 0) { fprintf(debugStream, "open(ttyName, O←RDWR) returns %d errno = %d\n", result, errno); }; }; { int result = dup2(fileDescriptor, 1); if (result < 0) { fprintf(debugStream, "dup2(fileDescriptor, 1) returns %d errno = %d\n", result, errno); }; }; { int result = dup2(fileDescriptor, 2); if (result < 0) { fprintf(debugStream, "dup2(fileDescriptor, 2) returns %d errno = %d\n", result, errno); }; }; if (fileDescriptor > 2) { close(fileDescriptor); }; if(gotParameters) { ioctl(0, TCSETSF, &terminalParameters); }; if (consoleFlag) { ioctl(0, TIOCCONS, 0); }; { /* must be suid-root here */ int myuid = getuid(); int mygid = getgid(); if (chown(ptyName, myuid, mygid) != 0) { perror("Couldn't chown pty"); }; if (chmod(ptyName, 0600) != 0) { perror("Couldn't chmod pty"); }; if (chown(ttyName, myuid, mygid) != 0) { perror("Couldn't chown tty"); }; if (chmod(ttyName, 0622) != 0) { perror("Couldn't chmod tty"); }; }; } /* * utmp entry management */ #define UTMPNAME "/etc/utmp" WriteUtmp(ttyName, name, host) char *ttyName; /* ttyName name */ char *name; /* user name */ char *host; /* host name */ { int slot; struct utmp utmpEntry; int f; char *rindex(); { int fileDescriptor = open(ttyName, O←RDWR); if (fileDescriptor < 0) { fprintf(debugStream, "WriteUtmp: Can't open(%s)\n", ttyName); exit(1); }; slot = ttyslot(); if (slot < 0) { fprintf(debugStream, "WriteUtmp: ttyslot() => -1\n"); exit(1); }; close(fileDescriptor); }; { char *temp = rindex(ttyName, '/'); strncpy( utmpEntry.ut←line, ((temp != NULL) ? temp+1 : ttyName), (sizeof utmpEntry.ut←line)); strncpy(utmpEntry.ut←name, name, (sizeof utmpEntry.ut←name)); strncpy(utmpEntry.ut←host, host, (sizeof utmpEntry.ut←host)); } time( &utmpEntry.ut←time ); if((f = open(UTMPNAME, O←WRONLY)) < 0) return; lseek(f, (long)(slot*(sizeof utmpEntry)), 0); write(f, (char *)(&utmpEntry), (sizeof utmpEntry)); close(f); } BecomeOurselves() { setuid(getuid()); }; #define DEFAULT←SHELL "/bin/sh" DoShell(argp, cleanFlag, termEnvItem) char **argp; int cleanFlag; char *termEnvItem; { char *myShell; char *minusName; char *getenv(); char *rindex(); char *Concat(); void CleanEnvironment(); { char *myHome = getenv("HOME"); if (myHome == NULL) { printf("No HOME\n"); exit(1); /*??*/ } if (chdir(myHome) < 0) { printf("Can't chdir to %s\n", myHome); /* exit(1); ??*/ } }; myShell = getenv("SHELL"); if (myShell == NULL) { myShell = DEFAULT←SHELL; }; { char *shellTail; shellTail = rindex(myShell, '/'); shellTail = ((shellTail != NULL) ? shellTail+1 : myShell); minusName = Concat("-", shellTail); }; if (termEnvItem !=0) { Resetenv("TERM", termEnvItem); } if (cleanFlag) { CleanEnvironment(); } execlp(myShell, minusName, (char *)0); perror(myShell); printf("Can't exec %s\n", myShell); exit(1); } /* * string concatenation routine -- should be in library */ char * Concat(this, that) char *this; char *that; { int length = 0; char *result = 0; char *calloc(); length = strlen(this) + strlen(that) + 1; result = calloc(length, 1); if(result != NULL) { strcat(result, this); strcat(result, that); } return result; }; void CleanEnvironment() { void Unsetenv(); Unsetenv("TERM"); Unsetenv("TERMCAP"); }; extern char **environ; void Unsetenv(name) char *name; { char **clean = environ; char **dirty = environ; char *dp; char *np; for (dirty = environ; *dirty != 0; dirty += 1) { for ((dp = *dirty, np = name); (*dp && *np && *dp == *np); (dp += 1, np += 1)) { continue; }; if ((*dp != '=') || (*np != '\0')) { *clean = *dirty; clean += 1; }; }; *clean = 0; }; Resetenv(name,new) char *name, *new; { char **dirty = environ; char *dp; char *np; for (dirty = environ; *dirty != 0; dirty++) { for ((dp = *dirty, np = name); (*dp && *np && *dp == *np); (dp++, np++)) ; if ((*dp == '=') && (*np == '\0')) { *dirty = new; return; }; }; }; #define VERBOSE 0 void showState(stream, prompt) char *prompt; FILE *stream; { int fileDescriptor; if (VERBOSE) { fprintf(stream, "%s\n", prompt); fileDescriptor = open("/dev/tty", O←RDWR, 0666); if (fileDescriptor < 0) { fprintf(stream, " I don't have a /dev/tty. errno = %d\n", errno); } else { fprintf(stream, " I have a /dev/tty\n"); close(fileDescriptor); }; fprintf(stream, " my pid = %d\n", getpid()); fprintf(stream, " my pgrp = %d\n", getpgrp(0)); { int pgrp = -1; (void) ioctl(0, TIOCGPGRP, &pgrp); fprintf(stream, " 0's pgrp = %d\n", pgrp); }; }; };