/* * LocalShell like RTTY LoginSh, only local, instead of remote. * * Must be setuid-root for the chown's to work. * * %Z%%M% %I% %H% %T% * */ #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"; main(argc, argv) int argc; char **argv; { int argn; /* in [0 .. argc] */ char **argp; /* pointer to elements of argv */ int cleanFlag = 1; int consoleFlag = 0; char *ptyName = 0; char *ttyName = 0; int child = -1; int exitStatus = 0; 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; default: goto usage; }; }; if (ptyName == 0 || ttyName == 0) { goto usage; }; /* fprintf(stderr, "forking\n"); */ child = fork(); switch (child) { default: /* we are the parent */ close(0); close(1); close(2); exitStatus = BabySit(); /* fprintf(stderr, "shutting down pty\n"); */ Shutdown(ptyName, ttyName); break; case 0: /* we are the child */ /* fprintf(stderr, "setting up pty\n"); */ Setup(ptyName, ttyName, consoleFlag); /* fprintf(stderr, "becoming ourselves\n"); */ BecomeOurselves(); /* fprintf(stderr, "doing a shell\n"); */ exitStatus = DoShell(argp, cleanFlag); 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); } BecomeOurselves() { setuid(getuid()); }; #define DEFAULT←SHELL "/bin/sh" DoShell(argp, cleanFlag) char **argp; int cleanFlag; { char *myHome; char *myShell; char *minusName; char *shellTail; int t; char *getenv(); char *rindex(); char *Concat(); void CleanEnvironment(); myHome = getenv("HOME"); /* fprintf(stderr, "myHome = %s\n", myHome); */ 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"); /* fprintf(stderr, "myShell = %s\n", myShell); */ if(myShell == NULL) { myShell = DEFAULT←SHELL; }; shellTail = rindex(myShell, '/'); shellTail = ((shellTail != NULL) ? shellTail+1 : myShell); minusName = Concat("-", shellTail); if (cleanFlag) CleanEnvironment(); /* fprintf(stderr, "execlp(%s, %s, 0)\n", myShell, minusName); */ 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; }; /* * utmp entry management */ #define UTMPNAME "/etc/utmp" WriteUtmp(slot, line, name, host) int slot; /* slot in utmp file, -1 ==> erase prev slot */ char *line; /* line name (NULL okay if slot == -1) */ char *name; /* user name (NULL okay if slot == -1) */ char *host; /* host name (NULL okay if slot == -1) */ { static int oldSlot = -1; static struct utmp utmpEntry; int f; char *rindex(); /* fprintf(stderr, "writing utmp slot %d\n", slot); */ if( slot >= 0 ) { char *temp = rindex(line, '/'); strncpy( utmpEntry.ut←line, ((temp != NULL) ? temp+1 : line), (sizeof utmpEntry.ut←line) ); strncpy( utmpEntry.ut←name, ((name != NULL) ? name : ""), (sizeof utmpEntry.ut←name) ); strncpy( utmpEntry.ut←host, ((host != NULL) ? host : ""), (sizeof utmpEntry.ut←host) ); oldSlot = slot; } else { if( oldSlot < 0 ) return; strncpy( utmpEntry.ut←name, "", (sizeof utmpEntry.ut←name) ); strncpy( utmpEntry.ut←host, "", (sizeof utmpEntry.ut←host) ); slot = oldSlot; oldSlot = -1; } 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); /* fprintf(stderr, "utmp %d written\n", slot); */ } Setup(ptyName, ttyName, consoleFlag) char *ptyName; char *ttyName; int consoleFlag; { int fileDescriptor; struct termios terminalParameters; int gotParameters = FALSE; char *getenv(); /* fprintf(stderr, "Setup(%s, %s, %d) begun\n", ptyName, ttyName, consoleFlag); */ 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(-1, NULL, NULL, NULL); /* erase any prev entry */ WriteUtmp(ttyslot(), ttyName, getenv("USER"), "PCedar"); fileDescriptor = open(ttyName, O←RDWR); if (fileDescriptor < 0) { perror("Can't open tty"); exit(1); }; (void) dup2(fileDescriptor, 0); (void) dup2(fileDescriptor, 1); (void) dup2(fileDescriptor, 2); (void) setpgrp(0, getpid()); /* take this out when Alan does it in XR←Spawn. */ { int mypgroup = getpgrp(); (void) ioctl(fileDescriptor, TIOCSPGRP, &mypgroup); }; close(fileDescriptor); if(gotParameters) { ioctl(0, TCSETSF, &terminalParameters); }; if (consoleFlag) { ioctl(0, TIOCCONS, 0); }; /* fprintf(stderr, "Setup tty opened, doing chowns\n"); */ { /* 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"); }; }; /* fprintf(stderr, "Setup(%s, %s, %d) done\n", ptyName, ttyName, consoleFlag); */ } /* * Release named pty/tty pair. */ Shutdown(ptyName, ttyName) char *ptyName; char *ttyName; { /* fprintf(stderr, "Shutdown(%s, %s) begun\n", ptyName, 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(-1, NULL, NULL, NULL); /* fprintf(stderr, "Shutdown(%s, %s) done\n", ptyName, ttyName); */ } int BabySit() { union wait status; int waitResult; int result = 0; /* fprintf(stderr, "baby sitting\n"); */ waitResult = wait(&status); switch (waitResult) { case -1: result = errno; break; default: result = status.w←retcode; break; }; 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) { /* fprintf(stderr, "name = %s *dirty = %s", name, *dirty); */ for ((dp = *dirty, np = name); (*dp && *np && *dp == *np); (dp += 1, np += 1)) { continue; }; if ((*dp != '=') || (*np != '\0')) { /* fprintf(stderr, " clean"); */ *clean = *dirty; clean += 1; }; /* fprintf(stderr, "\n"); */ }; *clean = 0; };