[alt.sources] LockTTY hack for SysV w/o networking

dburr@ocf.berkeley.edu (Donald Burr) (05/12/91)

Recently, a program came over the net called LockTTY that will lock your
terminal, so nobody could use it while you're away from it.  This program
worked great, except for one thing.  I run a Callan Unistar 300 (real old
SysV box without networking, save UUCP), and it doesn't have gethostname()
function.  So, I hacked a gethostname.c that uses uname() and returns the
system's nodename as the result of gethostname().  I have integrated this
call into LockTTY, and here is my modified version.

Questions, bug reports/fixes, suggestions for improvement, code hacks,
etc. about LockTTY in general should be distributed to its author,
nickel@cs.tu-berlin.de (Juergen Nickelsen).

Questions, bug reports/fixes, suggestions for improvement, code hacks,
etc. about my gethostname.c or my hacks to LocktTY should be directed
to me, Donald Burr, at either dburr@ocf.berkeley.edu (likely to go away
after a few months or so), or 72540.3071@compuserve.COM (likely to stay
longer).

Enjoy!!

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	Makefile
#	README
#	README.sysv
#	gethostname.c
#	locktty.1
#	locktty.c
# Wrapped by root@deutsch  on Sat May 11 10:37:59 1991
export PATH; PATH=/bin:$PATH
echo shar: extracting "'Makefile'" '(290 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
CC = cc
LIBS = -lcurses -ltermcap
SHELL = /bin/sh
CFLAGS = -O
SOURCE = locktty.c
TARGET = locktty

$(TARGET) : $(SOURCE)
	$(CC) $(CFLAGS) -o $(TARGET) $(SOURCE) $(LIBS)

isc :
	make LIBS="$(LIBS) -linet"

sysv :
	make SOURCE="$(SOURCE) gethostname.c"

clean : 
	rm -f *.o core *~ $(TARGET)
SHAR_EOF
if test 290 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 290 characters)'
fi
chmod +x 'Makefile'
fi # end of overwriting check
echo shar: extracting "'README'" '(1495 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
locktty 1.1

A long time I was looking for a utility to lock the screen of an ASCII
terminal, something like lockscreen or xlock on Sunview resp. X Windows,
but didn't find anything.

OK, here is "locktty". To keep it portable even for systems with a
shadow password file, I keep the crypted password in a file in the
user's home directory. If the file does not exist, the user is
prompted for a password.

The password file can be changed by using the -p option.

Changes to 1.0:
  - crypt() and getenv() predeclared (for ANSI compilers)
  - errno declared (isn't declared in every errno.h)
  - inserted fflush() after every (f)puts()
  - unlock prompt is really erased now
  - the number of failed unlock attempts is displayed after unlock
  - username and hostname are displayed in the unlock prompt

These changes were inspired or suggested by the following people:
David A. Berson, Tim Dawson, Greg Miller, Chris Phillips, Randy Hall.

The program is without any changes successfully tested on
  - SunOS 4.1 on sun3, sun4, with cc, gcc, but on a sun3 with cc and -O it
    throws core sometimes,
  - Interactive ix/386 2.0.2, 
  - Ultrix Worksystem V2.1 (Rev. 14), 
  - HP-UX 7.00,
  - IRIX System V Release 3.2.1,
  - Nixdorf TOS 4.0.12,
  - IBM AIX 1.2 (PS/2),
  - ConvexOS Release V9.0.

To compile it under Interactive UNIX, type "make isc", otherwise just
type "make" to build locktty.

Please send corrections, improvements, and flames to

	nickel@cs.tu-berlin.de (Juergen Nickelsen)
SHAR_EOF
if test 1495 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 1495 characters)'
fi
chmod +x 'README'
fi # end of overwriting check
echo shar: extracting "'README.sysv'" '(735 characters)'
if test -f 'README.sysv'
then
	echo shar: will not over-write existing file "'README.sysv'"
else
cat << \SHAR_EOF > 'README.sysv'
LOCKTTY HACK FOR SYSV
by Donald Burr

Current email addresses: dburr@ocf.berkeley.edu -- likely to change soon
                         72540.3071@compuserve.com -- probably permanent

LockTTY can really be ported to SysV easily. The only BSDism in there is
'gethostname'.  I have hacked up a fake 'gethostname.c' that simply
calls uuname and passes its result back to the calling program.  It
compiled fine on my Callan 300, so it should work on yours as well.
Mail me with any bugs, comments, suggestions, flames, whatever.

This version of locktty can make itself for both BSD and SysV.

To make for BSD, simply type "make".
To make for Interactive UNIX, simply type "make isc".
To make the SysV version, type "make sysv".

Enjoy!!
SHAR_EOF
if test 735 -ne "`wc -c < 'README.sysv'`"
then
	echo shar: error transmitting "'README.sysv'" '(should have been 735 characters)'
fi
chmod +x 'README.sysv'
fi # end of overwriting check
echo shar: extracting "'gethostname.c'" '(769 characters)'
if test -f 'gethostname.c'
then
	echo shar: will not over-write existing file "'gethostname.c'"
else
cat << \SHAR_EOF > 'gethostname.c'
/* gethostname() for SysV -- by Donald Burr
   Uses uname() and returns the system's UUCP nodename for a hostname
   A quick hack I did for locktty */

#include <sys/utsname.h>	/* #include for uname() call */
#include <string.h>		/* String routines strcpy() */

int gethostname(hostname, somenumber)

char	*hostname[];		/* Where the hostname will be returned */
int	somenumber;		/* Haven't figured out what this is */

{
	struct utsname name;	/* uname returns the sysname here */
/* had an asterisk */
	if ((uname(&name)) == -1)	/* did it fail? */
		return -1;		/* failed, so return a failure */
	else {
		strcpy(hostname, name.nodename);
					/* return the nodename */
		return 1;  }		/* successfull completion */

	return -1;		/* something must be wrong... exit */
}
SHAR_EOF
if test 769 -ne "`wc -c < 'gethostname.c'`"
then
	echo shar: error transmitting "'gethostname.c'" '(should have been 769 characters)'
fi
chmod +x 'gethostname.c'
fi # end of overwriting check
echo shar: extracting "'locktty.1'" '(1439 characters)'
if test -f 'locktty.1'
then
	echo shar: will not over-write existing file "'locktty.1'"
else
cat << \SHAR_EOF > 'locktty.1'
.TH LOCKTTY 1L "LOCKTTY 1.1" ""
.SH NAME
locktty - lock terminal
.SH SYNOPSIS
.B locktty
[
.B -p
]
.PP
.SH DESCRIPTION
.I Locktty\^
puts the terminal into raw mode, clears the screen, and
prompts the user for a password. Entered passwords are crypted and
checked against a crypted password in a file in the users' home
directory (~/.lockpasswd). If the correct password is given, the
terminal is set into normal state again. If not, a message is given,
and after some time a new password is read.
.PP
If the file ~/.lockpasswd does not exist, the user is prompted for
a password twice before the screen is locked. This password is
crypted and stored in ~/.lockpasswd.
.PP
The encrypted password in ~/.lockpasswd can be changed by invoking
.I locktty 
with the 
.B -p 
option.
.SH OPTIONS
.TP
.B -p
ask for new password in ~/.lockpasswd
.SH DIAGNOSTICS
.TP
~/.lockpasswd in wrong format
~/.lockpasswd does not appear to be a valid password crypt.
.SH FILES
$HOME/.lockpasswd             encrypted password
.SH BUGS
I haven't found a portable way to flush input before reading a new
password.
.br
The executable is huge compared to the source.
.br
.I Locktty
pretends to check the validity of ~/.lockpasswd, but checks only the
length and the character set.
.br
Only the first 8 characters of the password are significant.
.SH AUTHOR
Juergen Nickelsen, Technische Universitaet Berlin, Germany
.br
nickel@cs.tu-berlin.de
.SH COPYRIGHTS
None.
SHAR_EOF
if test 1439 -ne "`wc -c < 'locktty.1'`"
then
	echo shar: error transmitting "'locktty.1'" '(should have been 1439 characters)'
fi
chmod +x 'locktty.1'
fi # end of overwriting check
echo shar: extracting "'locktty.c'" '(7027 characters)'
if test -f 'locktty.c'
then
	echo shar: will not over-write existing file "'locktty.c'"
else
cat << \SHAR_EOF > 'locktty.c'
/* locktty - lock terminal 
 * version 1.1 patchlevel 1
 *
 * compile with cc -o locktty locktty.c -lcurses -ltermcap
 * Usage: locktty [ -p ]
 *
 * locktty puts the terminal into raw mode, clears the screen, and
 * prompts the user for a password. Entered passwords are crypted and
 * checked against a crypted password in a file in the users' home
 * directory (~/.lockpasswd). If the correct password is given, the
 * terminal is set into normal state again. If not, a messgae is
 * given, and after some time a new password is read.
 * If the file ~/.lockpasswd does not exist, the user is prompted for
 * a password twice before the screen is locked. This password is
 * crypted and stored in ~/.lockpasswd.
 * The encrypted password in ~/.lockpasswd can be changed by invoking
 * locktty with the -p option.
 *
 * Please send corrections, improvements, and flames to
 *	nickel@cs.tu-berlin.de (Juergen Nickelsen)
 */

#include <curses.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <pwd.h>

#define true	1
#define false	0

#define SIZE	1024		/* maximum size for password */
#define PFILE	".lockpasswd"	/* name of password file */
#define MAXPATHLEN	4096	/* this should exceed the real value
				 * on nearly all machines */
#define CLEN	14		/* enough for the crypted pwd's */
#define SALTC	\
	"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
				/* characters that may appear in the salt */

int 
    uid,			/* userid */
    fail_count = -1;		/* number of failed unlock attempts */

struct passwd 
    *pw_entry;			/* passwd file entry */

char
    *progname,			/* the name of the game */
    newc[CLEN + 1],		/* entered pwd (crypted) */
    oldc[CLEN + 1],		/* old pwd (crypted) */
    pfilnam[MAXPATHLEN],	/* name of password file */
    hostname[20],		/* hostname */
    prompt[40] ;		/* unlock prompt */

extern int errno ;
extern char *getenv(), *crypt() ;


main(argc, argv)
int argc ;
char **argv ;
{
    char 
	tmpl[SIZE+1],		/* space for reading from terminal */
	*slp ;			/* pointer to slash in progname */
    int pwdfile ;		/* read handle for password file */

    /* determine name of program */
    progname = *argv ;
    if (slp = strrchr(progname, '/')) {
	progname = slp + 1 ;
    }

    /* get host and user names */

    if (gethostname(hostname, 19) == -1) {
	hostname[0] = '\0' ;
    }
    uid = getuid() ;
    pw_entry = getpwuid(uid) ;

    /* note that pw_entry->pw_dir is the user's home dir */
    if (pw_entry != NULL) {
	if (hostname[0] != '\0') {
	    sprintf(prompt, "Unlock %s@%s: ", pw_entry->pw_name, hostname) ;
	} else {
	    sprintf(prompt, "Unlock user %s: ", pw_entry->pw_name) ;
	}
    } else if (hostname[0] != '\0') {
	sprintf(prompt, "Unlock host %s ", hostname) ;
    } else {
	sprintf(prompt, "Unlock: " ) ;
    }

    /* build name of password file */
    
    strcpy(pfilnam, getenv("HOME")) ;
    strcat(pfilnam, "/") ;
    strcat(pfilnam, PFILE) ;

    prepterm() ;		/* prepare terminal */

    if (argc > 1) {		/* check arguments */
	if (strcmp(argv[1], "-p") || argc != 2) {
	    reset() ;
	    usage() ;
	    exit(1) ;
	} else {
	    newpwdfile() ;	/* -p: new password */
	}
    }

    /* Open password file. If it does not exist, create it first. */
    do {
	if ((pwdfile = open(pfilnam, O_RDONLY)) == -1) {
	    if (errno == ENOENT) {
		puts("No password file.\r") ;
		fflush(stdout) ;
		newpwdfile() ;
	    } else {
		perror(pfilnam) ;
		reset() ;
		exit(errno) ;
	    }
	}
    } while (pwdfile == -1) ;
    
    /* read and check old crypt */
    if (read(pwdfile, oldc, CLEN) != CLEN || checkoldc()) {
	fputs("~/", stdout) ;
	fputs(PFILE, stdout) ;
	puts(" in wrong format\r") ;
	fflush(stdout) ;
	reset() ;
	exit(3) ;
    }

    /* clear screen and read password */
    do {
        fail_count++;
	clear() ;
	refresh() ;
	fputs(prompt, stdout) ;
	fflush(stdout) ;

	readpass(tmpl, false) ;
	strcpy(newc, crypt(tmpl, oldc)) ;
    } while (strcmp(newc, oldc) && 
	     (puts("\r\nNo way.\r"), fflush(stdout), sleep(3), 1)) ;
				/* this is not nice, I know */

    /* ready */
    reset() ;
    if (fail_count) {  
	printf("Failed attemps: %d\nPress return to exit ", fail_count) ;
	fflush(stdout) ;
	gets(oldc);
    }
    exit(0) ;
}


/* read a line in raw mode, terminated by newline or carriage return
 * or exceeding SIZE. Can get interrupted by Ctrl-C if intr != 0. */
readpass(p, intr)
char *p ;
int intr ;
{
    int n ;
    char c = ' ' ;		/* to have a value which is neither */
				/* '\n' nor '\r' */

    for (n = 0; n < SIZE && c != '\r' && c != '\n'; n++) {
	c = p[n] = getchar() ;
	if (intr && c == '\003') {
	    reset() ;
	    exit(1) ;
	}
    }
    p[n] = '\0' ;
}


/* prepare terminal: open stdin and stdout to /dev/tty, don't echo */
/* characters and make sure we get no signal (raw mode + some help). */
prepterm()
{
    int in ;

    /* we want to read the password ONLY from a terminal */
    if ((in = open("/dev/tty", O_RDWR)) == -1) {
	perror("/dev/tty") ;
	exit(1) ;
    }
    close(0) ;
    dup(in) ;
    close(1) ;
    dup(in) ;
    close(2) ;
    dup(in) ;

    /* make sure we won't get interrupted */
    initscr() ;
    raw() ;
    /* the break key on ISC's at386 console generates a SIGINT even in
     * raw mode */
    signal(SIGINT, SIG_IGN) ;

    /* don't echo keystrokes */
    noecho() ;
}


/* put terminal into a state the user is supposed to want after the */
/* termination of the program. */
reset()
{
    int i ;

    /* reset modes */
    clear() ;
    noraw() ;
    echo() ;
    /* end curses */
    endwin() ;

    /* delete prompt */
    for (i = 0; i < strlen(prompt); i++) {
	putchar('\b');
	putchar(' ');
	putchar('\b');
    }
}


/* create a new password file */
newpwdfile()
{
    char tmpl[SIZE+1], salt[3] ;
    int out ;

    /* make salt for crypt */
    srand(time(NULL)) ;
    salt[0] = SALTC[rand() % strlen(SALTC)] ;
    salt[1] = SALTC[rand() % strlen(SALTC)] ;
    salt[2] = '\0' ;

    /* read and verify password */
    fputs("Enter password:  ", stdout) ;
    fflush(stdout) ;
    readpass(tmpl, true) ;
    strcpy(oldc, crypt(tmpl, salt)) ;
    fputs("\r\nRetype password: ", stdout) ;
    fflush(stdout) ;
    readpass(tmpl, true) ;
    strcpy(newc, crypt(tmpl, salt)) ;
    newc[CLEN] = '\0' ;

    if (strcmp(oldc, newc)) {
	puts("\r\nNo match.\r") ;
	fflush(stdout) ;
	reset() ;
	exit(2) ;
    }

    /* create password file */
    if ((out = open(pfilnam, O_WRONLY | O_CREAT, 0600)) == -1) {
	reset() ;
	perror(pfilnam) ;
	exit(errno) ;
    }

    /* write crypt */
    write(out, newc, CLEN) ;
    close(out) ;
}


usage()
{
    fputs("Usage: ", stdout) ;
    fputs(progname, stdout) ;
    puts(" [ -p ]\r") ;
}


/* returns true if oldc does not look like a valid crypt */
checkoldc()
{
    int i ;

    /* check for illegal characters */
    for (i = 0; i < CLEN - 1; i++) {
	if (!strchr(SALTC, oldc[i])) {
	    return true ;
	}
    }

    /* check for terminating null character */
    return oldc[i] ;
}
SHAR_EOF
if test 7027 -ne "`wc -c < 'locktty.c'`"
then
	echo shar: error transmitting "'locktty.c'" '(should have been 7027 characters)'
fi
chmod +x 'locktty.c'
fi # end of overwriting check
#	End of shell archive
echo shar: end of shell archive.
exit 0
===============================================================================
Donald Burr, Univ. of California,Berkeley | America Online: DonaldBurr
INTERNET: dburr@ocf.Berkeley.EDU          |_Compu$erve:_72540,3071_____________
      or: 72540.3071@compuserve.COM       |    "Send flames to /dev/null."