#ifdef	ournix
#include "ournix.h"
#endif
#ifndef LINT	/* { */
char sccsID[] = "@(#) login.c V1.4 Copyright, Julian H. Stacey 1987\n" ;
#endif		/* } */

/* Login password program for MS-DOS & Unix
run from msdos autoexec.bat (as last thing to occur)
Author: Julian H. Stacey (reworked, linted, ported, corrected, extended (
	proper password struct, + passwd echo supress)
	inspired by early crude pjc msdos only version.

Running exepack on login.exe causes a "Packed file is corrupt message",
	when logout.exe tries to exec login.exe.
	This is irrespective of whether logout.exe is packed.
*/

#undef	REBOOT
			/* define REBOOT if you want to force the (msdos)
			   machine to reboot on detecting an intruder */
#ifdef unix		/* { */
#define	ANY_PASSWORD	
			/* define this to skip password matching,
			   as unix password is encrypted, we have to skip,
			   until encryption is included in this prog	*/
#endif			/* } */

#define WTMP		"/usr/adm/wtmp"		/* to store login name log. */
#define UTMP		"/etc/utmp"		/* to store who is logged in. */
#define MOTD		"/etc/motd"
#define BANNER		"/etc/banner"
#ifdef unix	/* { */
#define PROFILE		".profile"
#else		/* } { */
#define PROFILE		"profile.dot"
#endif		/* } */

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#ifdef unix	/* { */
#include <pwd.h>	/* needed for msdos, & unix sh_args */
#else		/* } { */
#include "passwd.h" /* needed for msdos, & unix sh_args */
#endif		/* } */

#define MAXLINE 255

char	inbuf[MAXLINE];

struct	passwd	*user;
extern	char	*ctime();
extern	long	time() ;
#ifndef unix
#define getpwnam vsl_getpwnam
#define endpwent vsl_endpwent
#endif
struct	passwd	*getpwnam();
extern	int	endpwent();

/* char strings declared here so they only exist once, to save space */
char	sWTMP[] =	WTMP ;
char	sUTMP[] =	UTMP ;
char	sPROFILE[] =	PROFILE ;
char	sMOTD[] =	MOTD ;
char	sBANNER[] =	BANNER ;
#ifdef DEBUG
char	cant_open[] =	"%s: Cant open %s\n" ;
#endif

int	ARGC ; char	**ARGV ;

#ifndef	MSDOS	/* { */
#define	my_gets(str)	gets(str)
extern	char *gets() ;
#else	/* } { */
#include <conio.h>
	/* effectively gets(str) */
	char *
my_gets(ret_str)
	char	*ret_str ;
	{
	char	*p_str ;
	int	count = MAXLINE ; /* bytes left to store a null term. string */
	int	ch ;

	p_str = ret_str ;
	while (--count)
		{
		if ((ch = (getch() & 0xFF)) != '\r' ) *p_str++ = ch ;
		else break ;
		}
	*p_str = '\0' ;
	putchar('\n') ;
	return(ret_str) ;
	}

#if 0	/* { */
/* from Systems Programming in Microsoft C by Michael Young, Page 49 
	but not compatible with gets() as used in
		if(gets(inbuf) == (char *)0) :
#include <dos.h>
io_raw_mode()
	{ /* place console in raw mode */
	union REGS Reg ;
	Reg.x.ax = 0x4400 ;	/* DOS get device information service.	*/
	Reg.x.bx = 2 ;		/* standard error fd			*/
	int86(0x21,&Reg,&Reg);	/* DOS services interrupt		*/
	Reg.x.ax = 0x4401 ;	/* DOS set device information service.	*/
	Reg.x.bx = 2 ;		/* standard error fd			*/
	Reg.h.dh = 0 ;		/* 0 out DH				*/
	Reg.h.dl |= 0x20 ;	/* Turn on raw mode bit (#5)		*/
	int86(0x21,&Reg,&Reg) ;	/* DOS service interrupt		*/
	}
io_cooked_mode()
	{ /* place console in raw mode */
	union REGS Reg ;
	Reg.x.ax = 0x4400 ;	/* DOS get device information service.	*/
	Reg.x.bx = 2 ;		/* standard error fd			*/
	int86(0x21,&Reg,&Reg);	/* DOS services interrupt		*/
	Reg.x.ax = 0x4401 ;	/* DOS set device information service.	*/
	Reg.x.bx = 2 ;		/* standard error fd			*/
	Reg.x.dx &= 0x00df;	/* turn off raw mode bit and 0 out DH	*/
	int86(0x21,&Reg,&Reg) ;	/* DOS service interrupt		*/
	}
#endif		/* } */
#endif		/* } */

main(argc, argv
#if 0
		, argp
#endif
			)
	int argc ; char **argv;
#if 0
	char **argp;	/* environment */
#endif
	{
	char	*p;
	long	log_time;
	FILE	*banner, *wtmp, *utmp, *profile, *motd ;
	char	prof_bat[100] ;
	int	c ;
#if 0
	char	who_set[30] ;
#endif
	typedef	char	FLAG ;
	FLAG	no_passwd = 0 ;

	ARGV = argv ; ARGC = argc ;
#ifdef	VSL	/* { */
#include	"../../include/vsl.h"
#endif		/* } */

	(void) signal(SIGINT, SIG_IGN);
	/* system("echo on"); */
	/* Display system banner if available */
	if ((banner = fopen(sBANNER,"r" ) ) != (FILE *)0) 
		{
		while ( ( c = getc(banner) ) != EOF ) putchar( c & 0x7f) ;
		(void) fclose(banner);
		}
again: for(;;)
		{
#ifndef unix	/* { */
		printf("Login: ");
		(void) fflush(stdout);
		if (gets(inbuf) == (char *)0) prog_failure();
		if (*inbuf == '\0') continue ;
		for(p = inbuf ; *p != '\0' ; p++) 
			if (*p == '\n') { *p = '\0'; break; }
#else	/* } { */
		/* On Unix, getty gets name, then passes it to login */
		(void) strcpy(inbuf,*(argv + 1)) ;
#endif		/* } */
		/* look user up in password file */
		user = getpwnam(inbuf);
		endpwent();
#ifdef	DEBUG
		fprintf(stderr,"%s name: %s, struct pointer %sset\n",
			*ARGV, inbuf, (user != (struct passwd *)0) ? "": "un" );
#endif
		if ((user != (struct passwd *)0) && (*user->pw_passwd == '\0'))
			no_passwd = (FLAG)1 ;
		break ;
		}

	/* read password, even if an invalid user, to fool crackers */
	if (!no_passwd) for(;;)
		{
		printf("Password: ");
		(void) fflush(stdout);
		echo_set(0);
		if(my_gets(inbuf) == (char *)0)
			{
			echo_set(1);
			prog_failure();
			}
		echo_set(1);
		if (*inbuf == '\0') continue ;
		for(p = inbuf ; *p ; p++) 
			if(*p == '\n') { *p = '\0'; break; }
#ifdef	DEBUG
		fprintf(stderr,"pwd: |%s|, struct pointer %sset\n",
			inbuf, (user != (struct passwd *)0) ? "": "un" );
#endif
		if ( (user == (struct passwd *)0) /* no such user */
#ifndef ANY_PASSWORD/* { */
			|| (strcmp(inbuf, user->pw_passwd) != 0) /* bad pwd */
#endif			/* } */
			)
			{
			/* sleep(3) ; to fool crackers */
			fprintf(stderr,"Login incorrect.\r\n");
			goto again ;
			}
		break ;
		} /* end of password */

	/* now log user */

	(void) time(&log_time) ;
	if ((wtmp = fopen(sWTMP,"a" )) == (FILE *)0) 
		{
#ifdef DEBUG
		fprintf(stderr, cant_open,*ARGV,sWTMP);
		perror(*ARGV);
#endif
		}
	else	{
		fprintf(wtmp,"%s\tlogin\t%s", user->pw_name, ctime(&log_time) );
		(void) fclose(wtmp);
		}
	if ((utmp = fopen(sUTMP,"w" ) ) == (FILE *)0) 
		{
#ifdef DEBUG
		fprintf(stderr,cant_open,*ARGV,sUTMP);
		perror(*ARGV);
#endif
		}
	else	{
		fprintf(utmp,"%s\t%s", user->pw_name , ctime(&log_time) );
		(void) fclose(utmp);
		}

	/* append name to environment */
#if 0
		wrong concept
		(void) sprintf(who_set,"WHO=%s",user->pw_name) ;
		while (*argp++ != (char *)0) ; 
		*argp++ = who_set ; *argp = (char *)0 ;
#endif

	if(*user->pw_dir != '\0') change_dir(user->pw_dir);

	if ((motd = fopen(sMOTD,"r" ) ) != (FILE *)0) 
		{
		while ( ( c = getc(motd) ) != EOF ) putchar( c & 0x7f) ;
		(void) fclose(motd);
		}

	if ((profile = fopen(sPROFILE,"r" )) != (FILE *)0) 
		{
		(void) fclose(profile);
#ifdef MSDOS	/* { */
		(void) sprintf(prof_bat,"command < %s",sPROFILE);
		(void) system(prof_bat);
#else		/* } { */
		printf("JJ .profile not actioned yet\n");
#endif		/* } */
		}

	if(*user->pw_shell == '\0') user->pw_shell = 
#ifdef	MSDOS	/* { */
		"command.com"
#else		/* } { */
		"sh"
#endif		/* } */
			;
#ifdef	MSDOS
#define	SAVE_RAM
	/* were already running command.com, dont waste process space running it again */
#endif
#ifndef SAVE_RAM
	setuid(user->pw_uid);
	setgid(user->pw_gid);
#ifndef unix
	exec_sh(user->pw_shell, user->pw_shargs);
#else
	exec_sh(user->pw_shell);
#endif
	printf("Returned to login\n");
#endif
	/* should play with shells and things here */
	/* now reboot for next user */
#if 0
	user->pw_shell = "command.com"; /* do we need an argument /p */
	user->pw_shargs = "/p";		/* JJ do we want /p */
	exec_sh(user->pw_shell, user->pw_shargs);
#endif	/* } */
	exit(0);
	}

prog_failure()
	{
	fprintf(stderr,"Login program failure.\nReboot machine\n");
	(void) fflush(stderr);
	for(;;) ;
	}

change_dir(str)
	char *str;
	{
	if (chdir(str) != 0)
		{
		fprintf(stderr,"Unknown directory %s\n",str);
		(void) fflush(stderr);
#ifdef	REBOOT	/* { */
		fprintf(stderr,"Reboot machine\n");
		(void) fflush(stderr);
		for(;;);
#else	/* } { */
		fprintf(stderr,"Going to /tmp \n");
		(void) fflush(stderr);
		(void) chdir("/tmp");
#endif		/* } */
		}
	}

#ifdef	MSDOS	/* { */
#define	NULL_DEV	"NUL:"
#define	STDOUT_DEV	"CON:"
#define	STDERR_DEV	"CON:"
#else		/* } { */
#define	NULL_DEV	"/dev/null"
#define	STDOUT_DEV	"/dev/tty"
#define	STDERR_DEV	"/dev/tty"
#endif		/* } */

echo_set(on) 
	int	on ;
	{
#ifdef	unix	/* { */
#include <sys/ioctl.h>
	struct	sgttyb	screen ;
	static short /* JJ really typeof(sg_flags) */ memory ;
	extern int errno ;
	if ( ioctl(fileno(stdin), (int)TIOCGETP, (char *) &screen) == -1 ) 
		{ perror(*ARGV) ; exit(1) ; }
	if (!on)
		{
		memory = screen.sg_flags ;
		screen.sg_flags &= ~ECHO ;
		}
	else 	screen.sg_flags = memory ;
	if ( ioctl(fileno(stdin), (int)TIOCSETP, (char *) &screen) == -1 ) 
		{ perror(*ARGV) ; exit(1) ; }
#else		/* } { */
	/* a different way to do un-echoed chars would be to use
		#include <conio.h> 
		int = getch();
		if (!on) io_raw_mode() ; else io_cooked_mode() ;
	*/
#endif		/* } */
	}

exec_sh(shell, sh_args_ins)
	char	*shell, *sh_args_ins;
	{
	char	*sh_args_local[20] ; /* array of pointers to sh args */
	char	**local_pp;
	char	*in_p;

#ifdef	DEBUG
	printf("exec_sh called with %s and %s\n",shell,sh_args_ins) ;
#endif
	local_pp = sh_args_local;
	*local_pp++ = shell ; /* essential for BSD, msdos survived without */
	for( in_p = sh_args_ins ; *in_p ; )
		{
#ifdef	DEBUG
		printf("now %s\n",in_p) ;
#endif
		*local_pp++ = in_p;
		while (*in_p && *in_p != ' ') in_p++;
		if (*in_p) *in_p++ = '\0';
		}
	*local_pp = (char *)0;
#ifdef	DEBUG
	printf("JJ Shell is %s\n",shell);
	printf("JJ First Arg is %s second is %s\n",
		sh_args_local[0], sh_args_local[1]);
#endif
	execvp(shell, sh_args_local);
	printf("Cannot find shell to execute\n");
#ifdef	REBOOT	/* { */
	printf("Reboot machine\n");
	for(;;);
#else	/* } { */
#endif		/* } */
	}
