/*
* 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;
};