/*	@(#)su.c	2.1		*/
/*
 *	su [-] [name [arg ...]] change userid, `-' changes environment
 *	if SULOG is defined, all attemts to su to uid 0 are logged there.
 *	if CONSOLE is defined, all successful attempts are also logged there.
 */
#include <stdio.h>
#include <pwd.h>
#include <time.h>
#include <signal.h>
#define SULOG	"/usr/adm/sulog"
#define PATH	"PATH=:/bin:/usr/bin"
#define SUPATH	"PATH=/bin:/etc:/usr/bin"
#define ELIM 128
long time();
void pause();
struct	passwd *pwd, *getpwnam();
struct	tm *localtime();
char	*malloc(), *strcpy();
char	*getpass(), *ttyname(), *strrchr();
char	*shell = "/bin/sh";
char	su[16] = "su";
char	homedir[64] = "HOME=";
char	logname[20] = "LOGNAME=";
char	*path = PATH;
char	*supath = SUPATH;
char	*envinit[ELIM];
extern	char **environ;
char *ttyn;

main(argc, argv)
char	**argv;
{
	char *nptr, *password;
	char	*pshell = shell;
	int badsw = 0;
	int eflag = 0;
	int uid, gid;
	char *dir, *shprog, *name;

	if (argc > 1 && *argv[1] == '-') {
		eflag++;
		argv++;
		argc--;
	}
	nptr = (argc > 1)? argv[1]: "root";
	if((pwd = getpwnam(nptr)) == NULL) {
		fprintf(stderr,"Unknown id: %s\n",nptr);
		exit(1);
	}
	uid = pwd->pw_uid;
	gid = pwd->pw_gid;
	dir = strcpy(malloc(strlen(pwd->pw_dir)+1),pwd->pw_dir);
	shprog = strcpy(malloc(strlen(pwd->pw_shell)+1),pwd->pw_shell);
	name = strcpy(malloc(strlen(pwd->pw_name)+1),pwd->pw_name);
	if((ttyn=ttyname(0))==NULL)
		if((ttyn=ttyname(1))==NULL)
			if((ttyn=ttyname(2))==NULL)
				ttyn="/dev/tty??";
	if(pwd->pw_passwd[0] == '\0' || getuid() == 0 )
		goto ok;
	password = getpass("Password:");
	if(badsw || (strcmp(pwd->pw_passwd, crypt(password, pwd->pw_passwd)) != 0)) {
#ifdef SULOG
		log(SULOG, nptr, 0);
#endif
		fprintf(stderr,"Sorry\n");
		exit(2);
	}
ok:
	endpwent();
#ifdef SULOG
		log(SULOG, nptr, 1);
#endif
	if((setgid(gid) != 0) || (setuid(uid) != 0)) {
		printf("Invalid ID\n");
		exit(2);
	}
	if (eflag) {
		strcat(homedir, dir);
		strcat(logname, name);
		envinit[2] = logname;
		chdir(dir);
		envinit[0] = homedir;
		if (uid == 0)
			envinit[1] = supath;
		else	envinit[1] = path;
		envinit[3] = NULL;
		environ = envinit;
		strcpy(su, "-su");
	}

	if (uid == 0)
	{
#ifdef CONSOLE
		if(strcmp(ttyn, CONSOLE) != 0) {
			signal(SIGALRM, to);
			alarm(30);
			log(CONSOLE, nptr, 1);
			alarm(0);
		}
#endif
		if (!eflag) envalt();
	}
	if (argc > 2) {
		if(shprog[0] != '\0') {
			fprintf(stderr,"Non-standard shell - denied\n");
			exit(2);
		}
		argv[1] = su;
		execv(shell, &argv[1]);
	} else {
		if(shprog[0] != '\0') {
			pshell = shprog;
			strcpy(su, eflag ? "-" : "");
			strcat(su, strrchr(pshell,'/') + 1);
		}
		execl(pshell, su, 0);
	}
	fprintf(stderr,"No shell\n");
	exit(3);
}

envalt()
{
int i, pset=0, j=0;
char **eptr=environ;

	for(i=0;i<130;i++)
	{
		if(*eptr == (char *)0) break;
		if(strncmp(*eptr,"PATH=",5)==0)
		{
			envinit[i-j]=supath;
			pset++;
		}
		else if(strncmp(*eptr,"PS1=",4)==0)
			j++;
		else
  			envinit[i-j] = *eptr;

		eptr++;
	}

	if(!pset)
	{
		i = (i < ELIM-2) ? i : ELIM-2;
		envinit[i++]=supath;
	}
	envinit[((i < ELIM-1) ? i : ELIM-1)]=(char *)0;

	environ = envinit;
}
log(where, towho, how)
char *where, *towho;
int how;
{
	FILE *logf;
	long now;
	struct tm *tmp;

	now = time(0);
	tmp = localtime(&now);
	if((logf=fopen(where,"a")) == NULL) return;
	fprintf(logf,"SU %.2d/%.2d %.2d:%.2d %c %s %s-%s\n",
		tmp->tm_mon+1,tmp->tm_mday,tmp->tm_hour,tmp->tm_min,
		how?'+':'-',(strrchr(ttyn,'/')+1),cuserid((char *)0),towho);
	fclose(logf);
}
void to(){}
