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