[alt.sources] locktty 1.1 -- lock terminal

nickel@kokos.cs.tu-berlin.de (Juergen Nickelsen) (05/06/91)

From 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)


#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 05/06/1991 12:23 UTC by a0528@gray3
# Source directory /usr/a0528/src/locktty
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#    243 -rw-r--r-- Makefile
#   1495 -rw-r--r-- README
#   1439 -rw-r--r-- locktty.1
#   7014 -rw-r--r-- locktty.c
#
# ============= Makefile ==============
if test -f 'Makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping Makefile (File already exists)'
else
echo 'x - extracting Makefile (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
CC = cc
LIBS = -lcurses -ltermcap
SHELL = /bin/sh
CFLAGS = -O
SOURCE = locktty.c
TARGET = locktty
X
$(TARGET) : $(SOURCE)
X	$(CC) $(CFLAGS) -o $(TARGET) $(SOURCE) $(LIBS)
X
isc :
X	make LIBS="$(LIBS) -linet"
X
clean : 
X	rm -f *.o core *~ $(TARGET)
SHAR_EOF
chmod 0644 Makefile ||
echo 'restore of Makefile failed'
Wc_c="`wc -c < 'Makefile'`"
test 243 -eq "$Wc_c" ||
	echo 'Makefile: original size 243, current size' "$Wc_c"
fi
# ============= README ==============
if test -f 'README' -a X"$1" != X"-c"; then
	echo 'x - skipping README (File already exists)'
else
echo 'x - extracting README (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'README' &&
locktty 1.1
X
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.
X
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.
X
The password file can be changed by using the -p option.
X
Changes to 1.0:
X  - crypt() and getenv() predeclared (for ANSI compilers)
X  - errno declared (isn't declared in every errno.h)
X  - inserted fflush() after every (f)puts()
X  - unlock prompt is really erased now
X  - the number of failed unlock attempts is displayed after unlock
X  - username and hostname are displayed in the unlock prompt
X
These changes were inspired or suggested by the following people:
David A. Berson, Tim Dawson, Greg Miller, Chris Phillips, Randy Hall.
X
The program is without any changes successfully tested on
X  - SunOS 4.1 on sun3, sun4, with cc, gcc, but on a sun3 with cc and -O it
X    throws core sometimes,
X  - Interactive ix/386 2.0.2, 
X  - Ultrix Worksystem V2.1 (Rev. 14), 
X  - HP-UX 7.00,
X  - IRIX System V Release 3.2.1,
X  - Nixdorf TOS 4.0.12,
X  - IBM AIX 1.2 (PS/2),
X  - ConvexOS Release V9.0.
X
To compile it under Interactive UNIX, type "make isc", otherwise just
type "make" to build locktty.
X
Please send corrections, improvements, and flames to
X
X	nickel@cs.tu-berlin.de (Juergen Nickelsen)
SHAR_EOF
chmod 0644 README ||
echo 'restore of README failed'
Wc_c="`wc -c < 'README'`"
test 1495 -eq "$Wc_c" ||
	echo 'README: original size 1495, current size' "$Wc_c"
fi
# ============= locktty.1 ==============
if test -f 'locktty.1' -a X"$1" != X"-c"; then
	echo 'x - skipping locktty.1 (File already exists)'
else
echo 'x - extracting locktty.1 (Text)'
sed 's/^X//' << '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
chmod 0644 locktty.1 ||
echo 'restore of locktty.1 failed'
Wc_c="`wc -c < 'locktty.1'`"
test 1439 -eq "$Wc_c" ||
	echo 'locktty.1: original size 1439, current size' "$Wc_c"
fi
# ============= locktty.c ==============
if test -f 'locktty.c' -a X"$1" != X"-c"; then
	echo 'x - skipping locktty.c (File already exists)'
else
echo 'x - extracting locktty.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'locktty.c' &&
/* locktty - lock terminal 
X * version 1.1
X *
X * compile with cc -o locktty locktty.c -lcurses -ltermcap
X * Usage: locktty [ -p ]
X *
X * locktty puts the terminal into raw mode, clears the screen, and
X * prompts the user for a password. Entered passwords are crypted and
X * checked against a crypted password in a file in the users' home
X * directory (~/.lockpasswd). If the correct password is given, the
X * terminal is set into normal state again. If not, a messgae is
X * given, and after some time a new password is read.
X * If the file ~/.lockpasswd does not exist, the user is prompted for
X * a password twice before the screen is locked. This password is
X * crypted and stored in ~/.lockpasswd.
X * The encrypted password in ~/.lockpasswd can be changed by invoking
X * locktty with the -p option.
X *
X * Please send corrections, improvements, and flames to
X *	nickel@cs.tu-berlin.de (Juergen Nickelsen)
X */
X
#include <curses.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <pwd.h>
X
#define true	1
#define false	0
X
#define SIZE	1024		/* maximum size for password */
#define PFILE	".lockpasswd"	/* name of password file */
#define MAXPATHLEN	4096	/* this should exceed the real value
X				 * on nearly all machines */
#define CLEN	14		/* enough for the crypted pwd's */
#define SALTC	\
X	"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
X				/* characters that may appear in the salt */
X
int 
X    uid,			/* userid */
X    fail_count = -1;		/* number of failed unlock attempts */
X
struct passwd 
X    *pw_entry;			/* passwd file entry */
X
char
X    *progname,			/* the name of the game */
X    newc[CLEN + 1],		/* entered pwd (crypted) */
X    oldc[CLEN + 1],		/* old pwd (crypted) */
X    pfilnam[MAXPATHLEN],	/* name of password file */
X    hostname[20],		/* hostname */
X    prompt[40] ;		/* unlock prompt */
X
extern int errno ;
extern char *getenv(), *crypt() ;
X
X
main(argc, argv)
int argc ;
char **argv ;
{
X    char 
X	tmpl[SIZE+1],		/* space for reading from terminal */
X	*slp ;			/* pointer to slash in progname */
X    int pwdfile ;		/* read handle for password file */
X
X    /* determine name of program */
X    progname = *argv ;
X    if (slp = strrchr(progname, '/')) {
X	progname = slp + 1 ;
X    }
X
X    /* get host and user names */
X
X    if (gethostname(hostname, 19) == -1) {
X	hostname[0] = '\0' ;
X    }
X    uid = getuid() ;
X    pw_entry = getpwuid(uid) ;
X
X    /* note that pw_entry->pw_dir is the user's home dir */
X    if (pw_entry != NULL) {
X	if (hostname[0] != '\0') {
X	    sprintf(prompt, "Unlock %s@%s: ", pw_entry->pw_name, hostname) ;
X	} else {
X	    sprintf(prompt, "Unlock user %s: ", pw_entry->pw_name) ;
X	}
X    } else if (hostname[0] != '\0') {
X	sprintf(prompt, "Unlock host %s ", hostname) ;
X    } else {
X	sprintf(prompt, "Unlock: " ) ;
X    }
X
X    /* build name of password file */
X    
X    strcpy(pfilnam, getenv("HOME")) ;
X    strcat(pfilnam, "/") ;
X    strcat(pfilnam, PFILE) ;
X
X    prepterm() ;		/* prepare terminal */
X
X    if (argc > 1) {		/* check arguments */
X	if (strcmp(argv[1], "-p") || argc != 2) {
X	    reset() ;
X	    usage() ;
X	    exit(1) ;
X	} else {
X	    newpwdfile() ;	/* -p: new password */
X	}
X    }
X
X    /* Open password file. If it does not exist, create it first. */
X    do {
X	if ((pwdfile = open(pfilnam, O_RDONLY)) == -1) {
X	    if (errno == ENOENT) {
X		puts("No password file.\r") ;
X		fflush(stdout) ;
X		newpwdfile() ;
X	    } else {
X		perror(pfilnam) ;
X		reset() ;
X		exit(errno) ;
X	    }
X	}
X    } while (pwdfile == -1) ;
X    
X    /* read and check old crypt */
X    if (read(pwdfile, oldc, CLEN) != CLEN || checkoldc()) {
X	fputs("~/", stdout) ;
X	fputs(PFILE, stdout) ;
X	puts(" in wrong format\r") ;
X	fflush(stdout) ;
X	reset() ;
X	exit(3) ;
X    }
X
X    /* clear screen and read password */
X    do {
X        fail_count++;
X	clear() ;
X	refresh() ;
X	fputs(prompt, stdout) ;
X	fflush(stdout) ;
X
X	readpass(tmpl, false) ;
X	strcpy(newc, crypt(tmpl, oldc)) ;
X    } while (strcmp(newc, oldc) && 
X	     (puts("\r\nNo way.\r"), fflush(stdout), sleep(3), 1)) ;
X				/* this is not nice, I know */
X
X    /* ready */
X    reset() ;
X    if (fail_count) {  
X	printf("Failed attemps: %d\nPress return to exit ", fail_count) ;
X	fflush(stdout) ;
X	gets(oldc);
X    }
X    exit(0) ;
}
X
X
/* read a line in raw mode, terminated by newline or carriage return
X * or exceeding SIZE. Can get interrupted by Ctrl-C if intr != 0. */
readpass(p, intr)
char *p ;
int intr ;
{
X    int n ;
X    char c = ' ' ;		/* to have a value which is neither */
X				/* '\n' nor '\r' */
X
X    for (n = 0; n < SIZE && c != '\r' && c != '\n'; n++) {
X	c = p[n] = getchar() ;
X	if (intr && c == '\003') {
X	    reset() ;
X	    exit(1) ;
X	}
X    }
X    p[n] = '\0' ;
}
X
X
/* 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()
{
X    int in ;
X
X    /* we want to read the password ONLY from a terminal */
X    if ((in = open("/dev/tty", O_RDWR)) == -1) {
X	perror("/dev/tty") ;
X	exit(1) ;
X    }
X    close(0) ;
X    dup(in) ;
X    close(1) ;
X    dup(in) ;
X    close(2) ;
X    dup(in) ;
X
X    /* make sure we won't get interrupted */
X    initscr() ;
X    raw() ;
X    /* the break key on ISC's at386 console generates a SIGINT even in
X     * raw mode */
X    signal(SIGINT, SIG_IGN) ;
X
X    /* don't echo keystrokes */
X    noecho() ;
}
X
X
/* put terminal into a state the user is supposed to want after the */
/* termination of the program. */
reset()
{
X    int i ;
X
X    /* reset modes */
X    clear() ;
X    noraw() ;
X    echo() ;
X    /* end curses */
X    endwin() ;
X
X    /* delete prompt */
X    for (i = 0; i < strlen(prompt); i++) {
X	putchar('\b');
X	putchar(' ');
X	putchar('\b');
X    }
}
X
X
/* create a new password file */
newpwdfile()
{
X    char tmpl[SIZE+1], salt[3] ;
X    int out ;
X
X    /* make salt for crypt */
X    srand(time(NULL)) ;
X    salt[0] = SALTC[rand() % strlen(SALTC)] ;
X    salt[1] = SALTC[rand() % strlen(SALTC)] ;
X    salt[3] = '\0' ;
X
X    /* read and verify password */
X    fputs("Enter password:  ", stdout) ;
X    fflush(stdout) ;
X    readpass(tmpl, true) ;
X    strcpy(oldc, crypt(tmpl, salt)) ;
X    fputs("\r\nRetype password: ", stdout) ;
X    fflush(stdout) ;
X    readpass(tmpl, true) ;
X    strcpy(newc, crypt(tmpl, salt)) ;
X    newc[CLEN] = '\0' ;
X
X    if (strcmp(oldc, newc)) {
X	puts("\r\nNo match.\r") ;
X	fflush(stdout) ;
X	reset() ;
X	exit(2) ;
X    }
X
X    /* create password file */
X    if ((out = open(pfilnam, O_WRONLY | O_CREAT, 0600)) == -1) {
X	reset() ;
X	perror(pfilnam) ;
X	exit(errno) ;
X    }
X
X    /* write crypt */
X    write(out, newc, CLEN) ;
X    close(out) ;
}
X
X
usage()
{
X    fputs("Usage: ", stdout) ;
X    fputs(progname, stdout) ;
X    puts(" [ -p ]\r") ;
}
X
X
/* returns true if oldc does not look like a valid crypt */
checkoldc()
{
X    int i ;
X
X    /* check for illegal characters */
X    for (i = 0; i < CLEN - 1; i++) {
X	if (!strchr(SALTC, oldc[i])) {
X	    return true ;
X	}
X    }
X
X    /* check for terminating null character */
X    return oldc[i] ;
}
SHAR_EOF
chmod 0644 locktty.c ||
echo 'restore of locktty.c failed'
Wc_c="`wc -c < 'locktty.c'`"
test 7014 -eq "$Wc_c" ||
	echo 'locktty.c: original size 7014, current size' "$Wc_c"
fi
exit 0
--
Juergen Nickelsen                               nickel@cs.tu-berlin.de