aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuido van Rooij <guido@FreeBSD.org>1994-05-19 18:13:11 +0000
committerGuido van Rooij <guido@FreeBSD.org>1994-05-19 18:13:11 +0000
commit110af3d672597c7d44e65f81538e132126eaf6cd (patch)
treebea6080606e2fc5fcfcff2cb5a12f3acd66bde2e
parent9eb28b5fdec65b0956d71b947179aa8f03b93c1b (diff)
downloadsrc-110af3d672597c7d44e65f81538e132126eaf6cd.tar.gz
src-110af3d672597c7d44e65f81538e132126eaf6cd.zip
1) Added s/key support .
2 Added optional excessive login logging. 3) Added login acces control on a per host/tty base. 4) See skey(1) for skey descriptions and src/usr.bin/login/README for the logging and access control features. -Guido
Notes
Notes: svn path=/head/; revision=1519
-rw-r--r--etc/login.access44
-rw-r--r--etc/skey.access8
-rw-r--r--lib/libskey/Makefile7
-rw-r--r--lib/libskey/authfile.c170
-rw-r--r--lib/libskey/md4.c316
-rw-r--r--lib/libskey/md4.h50
-rw-r--r--lib/libskey/put.c2289
-rw-r--r--lib/libskey/skey_crypt.c38
-rw-r--r--lib/libskey/skeylogin.c328
-rw-r--r--lib/libskey/skeysubr.c225
-rw-r--r--usr.bin/key/Makefile21
-rw-r--r--usr.bin/key/README.WZV100
-rw-r--r--usr.bin/key/key.149
-rw-r--r--usr.bin/key/skey.159
-rw-r--r--usr.bin/key/skey.c128
-rw-r--r--usr.bin/keyinfo/Makefile9
-rw-r--r--usr.bin/keyinfo/keyinfo.140
-rw-r--r--usr.bin/keyinfo/keyinfo.sh10
-rw-r--r--usr.bin/keyinit/Makefile21
-rw-r--r--usr.bin/keyinit/keyinit.164
-rw-r--r--usr.bin/keyinit/skeyinit.c195
21 files changed, 4171 insertions, 0 deletions
diff --git a/etc/login.access b/etc/login.access
new file mode 100644
index 000000000000..5cf54541463d
--- /dev/null
+++ b/etc/login.access
@@ -0,0 +1,44 @@
+# Login access control table.
+#
+# When someone logs in, the table is scanned for the first entry that
+# matches the (user, host) combination, or, in case of non-networked
+# logins, the first entry that matches the (user, tty) combination. The
+# permissions field of that table entry determines whether the login will
+# be accepted or refused.
+#
+# Format of the login access control table is three fields separated by a
+# ":" character:
+#
+# permission : users : origins
+#
+# The first field should be a "+" (access granted) or "-" (access denied)
+# character. The second field should be a list of one or more login names,
+# group names, or ALL (always matches). The third field should be a list
+# of one or more tty names (for non-networked logins), host names, domain
+# names (begin with "."), host addresses, internet network numbers (end
+# with "."), ALL (always matches) or LOCAL (matches any string that does
+# not contain a "." character). If you run NIS you can use @netgroupname
+# in host or user patterns.
+#
+# The EXCEPT operator makes it possible to write very compact rules.
+#
+# The group file is searched only when a name does not match that of the
+# logged-in user. Only groups are matched in which users are explicitly
+# listed: the program does not look at a user's primary group id value.
+#
+##############################################################################
+#
+# Disallow console logins to all but a few accounts.
+#
+#-:ALL EXCEPT wheel shutdown sync:console
+#
+# Disallow non-local logins to privileged accounts (group wheel).
+#
+#-:wheel:ALL EXCEPT LOCAL .win.tue.nl
+#
+# Some accounts are not allowed to login from anywhere:
+#
+#-:wsbscaro wsbsecr wsbspac wsbsym wscosor wstaiwde:ALL
+#
+# All other accounts are allowed to login from anywhere.
+#
diff --git a/etc/skey.access b/etc/skey.access
new file mode 100644
index 000000000000..22cdb69fe107
--- /dev/null
+++ b/etc/skey.access
@@ -0,0 +1,8 @@
+# First word says if UNIX passwords are to be permitted or denied.
+# remainder of the rule is a networknumber and mask. A rule matches a
+# host if any of its addresses satisfies:
+#
+# network = (address & mask)
+#
+#what network mask
+permit 0.0.0.0 0.0.0.0
diff --git a/lib/libskey/Makefile b/lib/libskey/Makefile
new file mode 100644
index 000000000000..ecf483333ce8
--- /dev/null
+++ b/lib/libskey/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 5.4 (Berkeley) 5/7/91
+
+LIB= skey
+SRCS= authfile.c md4.c put.c skey_crypt.c skeylogin.c skeysubr.c
+CFLAGS+=-DMPU8086
+.include <bsd.lib.mk>
+
diff --git a/lib/libskey/authfile.c b/lib/libskey/authfile.c
new file mode 100644
index 000000000000..32b920a727db
--- /dev/null
+++ b/lib/libskey/authfile.c
@@ -0,0 +1,170 @@
+ /* Portions taken from the skey distribution on Oct 21 1993 */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#if (MAXHOSTNAMELEN < 64) /* AIX weirdness */
+#undef MAXHOSTNAMELEN
+#endif
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 255
+#endif
+
+#include "skey.h"
+
+static int isaddr();
+static int rdnets();
+
+#define MAXADDR 16 /* how many addresses can a machine
+ * have? */
+
+ /*
+ * Turn host into an IP address and then look it up in the authorization
+ * database to determine if ordinary password logins are OK
+ */
+int authfile(host)
+char *host;
+{
+ char *addr[MAXADDR];
+ char **ap;
+ long n;
+ struct hostent *hp;
+ char **lp;
+ struct hostent *xp;
+ int addr_length;
+
+ if (strlen(host) == 0) {
+ /* Local login, okay */
+ return 1;
+ }
+ if (isaddr(host)) {
+ return rdnets(inet_addr(host));
+ } else {
+
+ /*
+ * Stash away a copy of the host address list because it will be
+ * clobbered by other gethostbyXXX() calls.
+ */
+
+ hp = gethostbyname(host);
+ if (hp == NULL) {
+ syslog(LOG_ERR, "unknown host: %s", host);
+ return 0;
+ }
+ if (hp->h_addrtype != AF_INET) {
+ syslog(LOG_ERR, "unknown network family: %d", hp->h_addrtype);
+ return 0;
+ }
+ for (lp = hp->h_addr_list, ap = addr; ap < addr + MAXADDR; lp++, ap++) {
+ if (*lp == NULL) {
+ *ap = 0;
+ break;
+ } else {
+ if ((*ap = malloc(hp->h_length)) == 0) {
+ syslog(LOG_ERR, "out of memory");
+ return 0;
+ }
+ memcpy(*ap, *lp, hp->h_length);
+ }
+ }
+ addr_length = hp->h_length;
+
+ /*
+ * See if any of the addresses matches a pattern in the control file.
+ * Report and skip the address if it does not belong to the remote
+ * host. Assume localhost == localhost.domain.
+ */
+
+#define NEQ(x,y) (strcasecmp((x),(y)) != 0)
+
+ while (ap-- > addr) {
+ memcpy((char *) &n, *ap, addr_length);
+ if (rdnets(n)) {
+ if ((hp = gethostbyaddr(*ap, addr_length, AF_INET)) == 0
+ || (NEQ(host, hp->h_name) && NEQ(host, "localhost"))) {
+ syslog(LOG_ERR, "IP address %s not registered for host %s",
+ inet_ntoa(*(struct in_addr *) * ap), host);
+ continue;
+ }
+ return 1;
+ }
+ }
+ return 0;
+ }
+}
+static int rdnets(host)
+unsigned long host;
+{
+ FILE *fp;
+ char buf[128],
+ *cp;
+ long pattern,
+ mask;
+ char *strtok();
+ int permit_it = 0;
+
+ fp = fopen("/etc/skey.access", "r");
+ if (fp == NULL)
+ return 1; /* XXX */
+ while (fgets(buf, sizeof(buf), fp), !feof(fp)) {
+ if (buf[0] == '#')
+ continue; /* Comment */
+ cp = strtok(buf, " \t");
+ if (cp == NULL)
+ continue;
+ /* two choices permit or deny */
+ if (strncasecmp(cp, "permit", 4) == 0) {
+ permit_it = 1;
+ } else {
+ if (strncasecmp(cp, "deny", 4) == 0) {
+ permit_it = 0;
+ } else {
+ continue; /* ignore this it is not
+ * permit/deny */
+ }
+ }
+ cp = strtok(NULL, " \t");
+ if (cp == NULL)
+ continue; /* Invalid line */
+ pattern = inet_addr(cp);
+ cp = strtok(NULL, " \t");
+ if (cp == NULL)
+ continue; /* Invalid line */
+ mask = inet_addr(cp);
+ if ((host & mask) == pattern) {
+ fclose(fp);
+ return permit_it;
+ }
+ }
+ fclose(fp);
+ return 0;
+}
+
+ /*
+ * Return TRUE if string appears to be an IP address in dotted decimal;
+ * return FALSE otherwise (i.e., if string is a domain name)
+ */
+static int isaddr(s)
+register char *s;
+{
+ char c;
+
+ if (s == NULL)
+ return 1; /* Can't happen */
+
+ while ((c = *s++) != '\0') {
+ if (c != '[' && c != ']' && !isdigit(c) && c != '.')
+ return 0;
+ }
+ return 1;
+}
diff --git a/lib/libskey/md4.c b/lib/libskey/md4.c
new file mode 100644
index 000000000000..96bc7d0bf9e0
--- /dev/null
+++ b/lib/libskey/md4.c
@@ -0,0 +1,316 @@
+/*
+ * md4.c -- Implementation of MD4 Message Digest Algorithm
+ * Updated: 2/16/90 by Ronald L. Rivest
+ * (C) 1990 RSA Data Security, Inc.
+ *
+ * Portability nits fixed and reformatted - 2/12/91 Phil Karn
+ */
+
+/*
+ * To use MD4:
+ * -- Include md4.h in your program
+ * -- Declare an MDstruct MD to hold the state of the digest computation.
+ * -- Initialize MD using MDbegin(&MD)
+ * -- For each full block (64 bytes) X you wish to process, call
+ * MDupdate(&MD,X,512)
+ * (512 is the number of bits in a full block.)
+ * -- For the last block (less than 64 bytes) you wish to process,
+ * MDupdate(&MD,X,n)
+ * where n is the number of bits in the partial block. A partial
+ * block terminates the computation, so every MD computation should
+ * terminate by processing a partial block, even if it has n = 0.
+ * -- The message digest is available in MD.buffer[0] ... MD.buffer[3].
+ * (Least-significant byte of each word should be output first.)
+ * -- You can print out the digest using MDprint(&MD)
+ */
+
+/* Implementation notes:
+ * This implementation assumes that longs are 32-bit quantities.
+ * If the machine stores the least-significant byte of an long in the
+ * least-addressed byte (eg., VAX and 8086), then LOWBYTEFIRST should be
+ * set to TRUE. Otherwise (eg., SUNS), LOWBYTEFIRST should be set to
+ * FALSE. Note that on machines with LOWBYTEFIRST FALSE the routine
+ * MDupdate modifies has a side-effect on its input array (the order of bytes
+ * in each word are reversed). If this is undesired a call to MDreverse(X) can
+ * reverse the bytes of X back into order after each call to MDupdate.
+ */
+#define TRUE 1
+#define FALSE 0
+
+#if (defined(__MSDOS__) || defined(MPU8086) || defined(MPU8080) \
+ || defined(vax) || defined (MIPSEL))
+#define LOWBYTEFIRST TRUE /* Low order bytes are first in memory */
+#else /* Almost all other machines are big-endian */
+#define LOWBYTEFIRST FALSE
+#endif
+
+
+/* Compile-time includes */
+#include <stdio.h>
+#include "md4.h"
+
+/* Compile-time declarations of MD4 ``magic constants'' */
+#define I0 0x67452301 /* Initial values for MD buffer */
+#define I1 0xefcdab89
+#define I2 0x98badcfe
+#define I3 0x10325476
+#define C2 013240474631 /* round 2 constant = sqrt(2) in octal */
+#define C3 015666365641 /* round 3 constant = sqrt(3) in octal */
+/* C2 and C3 are from Knuth, The Art of Programming, Volume 2
+ * (Seminumerical Algorithms), Second Edition (1981), Addison-Wesley.
+ * Table 2, page 660.
+ */
+#define fs1 3 /* round 1 shift amounts */
+#define fs2 7
+#define fs3 11
+#define fs4 19
+#define gs1 3 /* round 2 shift amounts */
+#define gs2 5
+#define gs3 9
+#define gs4 13
+#define hs1 3 /* round 3 shift amounts */
+#define hs2 9
+#define hs3 11
+#define hs4 15
+
+
+/* Compile-time macro declarations for MD4.
+ * Note: The ``rot'' operator uses the variable ``tmp''.
+ * It assumes tmp is declared as unsigned long, so that the >>
+ * operator will shift in zeros rather than extending the sign bit.
+ */
+#define f(X,Y,Z) ((X&Y) | ((~X)&Z))
+#define g(X,Y,Z) ((X&Y) | (X&Z) | (Y&Z))
+#define h(X,Y,Z) (X^Y^Z)
+#define rot(X,S) (tmp=X,(tmp<<S) | (tmp>>(32-S)))
+#define ff(A,B,C,D,i,s) A = rot((A + f(B,C,D) + X[i]),s)
+#define gg(A,B,C,D,i,s) A = rot((A + g(B,C,D) + X[i] + C2),s)
+#define hh(A,B,C,D,i,s) A = rot((A + h(B,C,D) + X[i] + C3),s)
+
+void MDreverse __ARGS((unsigned long *X));
+
+/* MDprint(MDp)
+ * Print message digest buffer MDp as 32 hexadecimal digits.
+ * Order is from low-order byte of buffer[0] to high-order byte of buffer[3].
+ * Each byte is printed with high-order hexadecimal digit first.
+ * This is a user-callable routine.
+ */
+void
+MDprint(MDp)
+MDptr MDp;
+{
+ int i,j;
+
+ for(i=0;i<4;i++)
+ for(j=0;j<32;j=j+8)
+ printf("%02lx",(MDp->buffer[i]>>j) & 0xFF);
+}
+
+/* MDbegin(MDp)
+ * Initialize message digest buffer MDp.
+ * This is a user-callable routine.
+ */
+void
+MDbegin(MDp)
+MDptr MDp;
+{
+ int i;
+
+ MDp->buffer[0] = I0;
+ MDp->buffer[1] = I1;
+ MDp->buffer[2] = I2;
+ MDp->buffer[3] = I3;
+ for(i=0;i<8;i++)
+ MDp->count[i] = 0;
+ MDp->done = 0;
+}
+
+/* MDreverse(X)
+ * Reverse the byte-ordering of every long in X.
+ * Assumes X is an array of 16 longs.
+ * The macro revx reverses the byte-ordering of the next word of X.
+ */
+#define revx { t = (*X << 16) | (*X >> 16); \
+ *X++ = ((t & 0xFF00FF00) >> 8) | ((t & 0x00FF00FF) << 8); }
+void
+MDreverse(X)
+unsigned long *X;
+{
+ register unsigned long t;
+
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+}
+
+/* MDblock(MDp,X)
+ * Update message digest buffer MDp->buffer using 16-word data block X.
+ * Assumes all 16 words of X are full of data.
+ * Does not update MDp->count.
+ * This routine is not user-callable.
+ */
+static void
+MDblock(MDp,X)
+MDptr MDp;
+unsigned long *X;
+{
+ register unsigned long tmp, A, B, C, D;
+
+#if LOWBYTEFIRST == FALSE
+ MDreverse(X);
+#endif
+ A = MDp->buffer[0];
+ B = MDp->buffer[1];
+ C = MDp->buffer[2];
+ D = MDp->buffer[3];
+ /* Update the message digest buffer */
+ ff(A,B,C,D,0,fs1); /* Round 1 */
+ ff(D,A,B,C,1,fs2);
+ ff(C,D,A,B,2,fs3);
+ ff(B,C,D,A,3,fs4);
+ ff(A,B,C,D,4,fs1);
+ ff(D,A,B,C,5,fs2);
+ ff(C,D,A,B,6,fs3);
+ ff(B,C,D,A,7,fs4);
+ ff(A,B,C,D,8,fs1);
+ ff(D,A,B,C,9,fs2);
+ ff(C,D,A,B,10,fs3);
+ ff(B,C,D,A,11,fs4);
+ ff(A,B,C,D,12,fs1);
+ ff(D,A,B,C,13,fs2);
+ ff(C,D,A,B,14,fs3);
+ ff(B,C,D,A,15,fs4);
+ gg(A,B,C,D,0,gs1); /* Round 2 */
+ gg(D,A,B,C,4,gs2);
+ gg(C,D,A,B,8,gs3);
+ gg(B,C,D,A,12,gs4);
+ gg(A,B,C,D,1,gs1);
+ gg(D,A,B,C,5,gs2);
+ gg(C,D,A,B,9,gs3);
+ gg(B,C,D,A,13,gs4);
+ gg(A,B,C,D,2,gs1);
+ gg(D,A,B,C,6,gs2);
+ gg(C,D,A,B,10,gs3);
+ gg(B,C,D,A,14,gs4);
+ gg(A,B,C,D,3,gs1);
+ gg(D,A,B,C,7,gs2);
+ gg(C,D,A,B,11,gs3);
+ gg(B,C,D,A,15,gs4);
+ hh(A,B,C,D,0,hs1); /* Round 3 */
+ hh(D,A,B,C,8,hs2);
+ hh(C,D,A,B,4,hs3);
+ hh(B,C,D,A,12,hs4);
+ hh(A,B,C,D,2,hs1);
+ hh(D,A,B,C,10,hs2);
+ hh(C,D,A,B,6,hs3);
+ hh(B,C,D,A,14,hs4);
+ hh(A,B,C,D,1,hs1);
+ hh(D,A,B,C,9,hs2);
+ hh(C,D,A,B,5,hs3);
+ hh(B,C,D,A,13,hs4);
+ hh(A,B,C,D,3,hs1);
+ hh(D,A,B,C,11,hs2);
+ hh(C,D,A,B,7,hs3);
+ hh(B,C,D,A,15,hs4);
+ MDp->buffer[0] += A;
+ MDp->buffer[1] += B;
+ MDp->buffer[2] += C;
+ MDp->buffer[3] += D;
+}
+
+/* MDupdate(MDp,X,count)
+ * Input: MDp -- an MDptr
+ * X -- a pointer to an array of unsigned characters.
+ * count -- the number of bits of X to use.
+ * (if not a multiple of 8, uses high bits of last byte.)
+ * Update MDp using the number of bits of X given by count.
+ * This is the basic input routine for an MD4 user.
+ * The routine completes the MD computation when count < 512, so
+ * every MD computation should end with one call to MDupdate with a
+ * count less than 512. A call with count 0 will be ignored if the
+ * MD has already been terminated (done != 0), so an extra call with count
+ * 0 can be given as a ``courtesy close'' to force termination if desired.
+ */
+void
+MDupdate(MDp,X,count)
+MDptr MDp;
+unsigned char *X;
+unsigned int count;
+{
+ int i,bit,byte,mask;
+ unsigned long tmp;
+ unsigned char XX[64];
+ unsigned char *p;
+
+ /* return with no error if this is a courtesy close with count
+ * zero and MDp->done is true.
+ */
+ if(count == 0 && MDp->done)
+ return;
+ /* check to see if MD is already done and report error */
+ if(MDp->done){
+ printf("\nError: MDupdate MD already done.");
+ return;
+ }
+ /* Add count to MDp->count */
+ tmp = count;
+ p = MDp->count;
+ while(tmp){
+ tmp += *p;
+ *p++ = tmp;
+ tmp = tmp >> 8;
+ }
+ /* Process data */
+ if(count == 512){
+ /* Full block of data to handle */
+ MDblock(MDp,(unsigned long *)X);
+ } else if(count > 512){
+ /* Check for count too large */
+ printf("\nError: MDupdate called with illegal count value %ld.",count);
+ return;
+ } else {
+ /* partial block -- must be last block so finish up
+ * Find out how many bytes and residual bits there are
+ */
+ byte = count >> 3;
+ bit = count & 7;
+ /* Copy X into XX since we need to modify it */
+ for(i=0;i<=byte;i++)
+ XX[i] = X[i];
+ for(i=byte+1;i<64;i++)
+ XX[i] = 0;
+ /* Add padding '1' bit and low-order zeros in last byte */
+ mask = 1 << (7 - bit);
+ XX[byte] = (XX[byte] | mask) & ~( mask - 1);
+ /* If room for bit count, finish up with this block */
+ if(byte <= 55){
+ for(i=0;i<8;i++)
+ XX[56+i] = MDp->count[i];
+ MDblock(MDp,(unsigned long *)XX);
+ } else {
+ /* need to do two blocks to finish up */
+ MDblock(MDp,(unsigned long *)XX);
+ for(i=0;i<56;i++)
+ XX[i] = 0;
+ for(i=0;i<8;i++)
+ XX[56+i] = MDp->count[i];
+ MDblock(MDp,(unsigned long *)XX);
+ }
+ /* Set flag saying we're done with MD computation */
+ MDp->done = 1;
+ }
+}
+/* End of md4.c */
diff --git a/lib/libskey/md4.h b/lib/libskey/md4.h
new file mode 100644
index 000000000000..5f2e031eb551
--- /dev/null
+++ b/lib/libskey/md4.h
@@ -0,0 +1,50 @@
+#ifdef __STDC__
+#define __ARGS(X) X /* For ANSI C */
+#else
+#define __ARGS(X) ()
+#endif
+
+/*
+ *
+ * md4.h -- Header file for implementation of MD4 Message Digest Algorithm
+ * Updated: 2/13/90 by Ronald L. Rivest
+ * (C) 1990 RSA Data Security, Inc.
+ * Reformatted and de-linted - 2/12/91 Phil Karn
+ */
+
+/* MDstruct is the data structure for a message digest computation. */
+typedef struct {
+ unsigned long buffer[4];/* Holds 4-word result of MD computation */
+ unsigned char count[8]; /* Number of bits processed so far */
+ unsigned int done; /* Nonzero means MD computation finished */
+} MDstruct, *MDptr;
+
+/* MDbegin(MD)
+ * Input: MD -- an MDptr
+ * Initialize the MDstruct prepatory to doing a message digest computation.
+ */
+extern void MDbegin __ARGS((MDptr MDp));
+
+/* MDupdate(MD,X,count)
+ * Input: MD -- an MDptr
+ * X -- a pointer to an array of unsigned characters.
+ * count -- the number of bits of X to use (an unsigned int).
+ * Updates MD using the first ``count'' bits of X.
+ * The array pointed to by X is not modified.
+ * If count is not a multiple of 8, MDupdate uses high bits of last byte.
+ * This is the basic input routine for a user.
+ * The routine terminates the MD computation when count < 512, so
+ * every MD computation should end with one call to MDupdate with a
+ * count less than 512. Zero is OK for a count.
+ */
+extern void MDupdate __ARGS((MDptr MDp,unsigned char *X,unsigned int count));
+
+/* MDprint(MD)
+ * Input: MD -- an MDptr
+ * Prints message digest buffer MD as 32 hexadecimal digits.
+ * Order is from low-order byte of buffer[0] to high-order byte of buffer[3].
+ * Each byte is printed with high-order hexadecimal digit first.
+ */
+extern void MDprint __ARGS((MDptr MDp));
+
+/* End of md4.h */
diff --git a/lib/libskey/put.c b/lib/libskey/put.c
new file mode 100644
index 000000000000..1ef1ec0a8656
--- /dev/null
+++ b/lib/libskey/put.c
@@ -0,0 +1,2289 @@
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include "skey.h"
+
+static unsigned long extract __ARGS((char *s,int start,int length));
+static void standard __ARGS((char *word));
+static void insert __ARGS((char *s, int x, int start, int length));
+static int wsrch __ARGS((char *w,int low,int high));
+
+/* Dictionary for integer-word translations */
+char Wp[2048][4] = {
+"A",
+"ABE",
+"ACE",
+"ACT",
+"AD",
+"ADA",
+"ADD",
+"AGO",
+"AID",
+"AIM",
+"AIR",
+"ALL",
+"ALP",
+"AM",
+"AMY",
+"AN",
+"ANA",
+"AND",
+"ANN",
+"ANT",
+"ANY",
+"APE",
+"APS",
+"APT",
+"ARC",
+"ARE",
+"ARK",
+"ARM",
+"ART",
+"AS",
+"ASH",
+"ASK",
+"AT",
+"ATE",
+"AUG",
+"AUK",
+"AVE",
+"AWE",
+"AWK",
+"AWL",
+"AWN",
+"AX",
+"AYE",
+"BAD",
+"BAG",
+"BAH",
+"BAM",
+"BAN",
+"BAR",
+"BAT",
+"BAY",
+"BE",
+"BED",
+"BEE",
+"BEG",
+"BEN",
+"BET",
+"BEY",
+"BIB",
+"BID",
+"BIG",
+"BIN",
+"BIT",
+"BOB",
+"BOG",
+"BON",
+"BOO",
+"BOP",
+"BOW",
+"BOY",
+"BUB",
+"BUD",
+"BUG",
+"BUM",
+"BUN",
+"BUS",
+"BUT",
+"BUY",
+"BY",
+"BYE",
+"CAB",
+"CAL",
+"CAM",
+"CAN",
+"CAP",
+"CAR",
+"CAT",
+"CAW",
+"COD",
+"COG",
+"COL",
+"CON",
+"COO",
+"COP",
+"COT",
+"COW",
+"COY",
+"CRY",
+"CUB",
+"CUE",
+"CUP",
+"CUR",
+"CUT",
+"DAB",
+"DAD",
+"DAM",
+"DAN",
+"DAR",
+"DAY",
+"DEE",
+"DEL",
+"DEN",
+"DES",
+"DEW",
+"DID",
+"DIE",
+"DIG",
+"DIN",
+"DIP",
+"DO",
+"DOE",
+"DOG",
+"DON",
+"DOT",
+"DOW",
+"DRY",
+"DUB",
+"DUD",
+"DUE",
+"DUG",
+"DUN",
+"EAR",
+"EAT",
+"ED",
+"EEL",
+"EGG",
+"EGO",
+"ELI",
+"ELK",
+"ELM",
+"ELY",
+"EM",
+"END",
+"EST",
+"ETC",
+"EVA",
+"EVE",
+"EWE",
+"EYE",
+"FAD",
+"FAN",
+"FAR",
+"FAT",
+"FAY",
+"FED",
+"FEE",
+"FEW",
+"FIB",
+"FIG",
+"FIN",
+"FIR",
+"FIT",
+"FLO",
+"FLY",
+"FOE",
+"FOG",
+"FOR",
+"FRY",
+"FUM",
+"FUN",
+"FUR",
+"GAB",
+"GAD",
+"GAG",
+"GAL",
+"GAM",
+"GAP",
+"GAS",
+"GAY",
+"GEE",
+"GEL",
+"GEM",
+"GET",
+"GIG",
+"GIL",
+"GIN",
+"GO",
+"GOT",
+"GUM",
+"GUN",
+"GUS",
+"GUT",
+"GUY",
+"GYM",
+"GYP",
+"HA",
+"HAD",
+"HAL",
+"HAM",
+"HAN",
+"HAP",
+"HAS",
+"HAT",
+"HAW",
+"HAY",
+"HE",
+"HEM",
+"HEN",
+"HER",
+"HEW",
+"HEY",
+"HI",
+"HID",
+"HIM",
+"HIP",
+"HIS",
+"HIT",
+"HO",
+"HOB",
+"HOC",
+"HOE",
+"HOG",
+"HOP",
+"HOT",
+"HOW",
+"HUB",
+"HUE",
+"HUG",
+"HUH",
+"HUM",
+"HUT",
+"I",
+"ICY",
+"IDA",
+"IF",
+"IKE",
+"ILL",
+"INK",
+"INN",
+"IO",
+"ION",
+"IQ",
+"IRA",
+"IRE",
+"IRK",
+"IS",
+"IT",
+"ITS",
+"IVY",
+"JAB",
+"JAG",
+"JAM",
+"JAN",
+"JAR",
+"JAW",
+"JAY",
+"JET",
+"JIG",
+"JIM",
+"JO",
+"JOB",
+"JOE",
+"JOG",
+"JOT",
+"JOY",
+"JUG",
+"JUT",
+"KAY",
+"KEG",
+"KEN",
+"KEY",
+"KID",
+"KIM",
+"KIN",
+"KIT",
+"LA",
+"LAB",
+"LAC",
+"LAD",
+"LAG",
+"LAM",
+"LAP",
+"LAW",
+"LAY",
+"LEA",
+"LED",
+"LEE",
+"LEG",
+"LEN",
+"LEO",
+"LET",
+"LEW",
+"LID",
+"LIE",
+"LIN",
+"LIP",
+"LIT",
+"LO",
+"LOB",
+"LOG",
+"LOP",
+"LOS",
+"LOT",
+"LOU",
+"LOW",
+"LOY",
+"LUG",
+"LYE",
+"MA",
+"MAC",
+"MAD",
+"MAE",
+"MAN",
+"MAO",
+"MAP",
+"MAT",
+"MAW",
+"MAY",
+"ME",
+"MEG",
+"MEL",
+"MEN",
+"MET",
+"MEW",
+"MID",
+"MIN",
+"MIT",
+"MOB",
+"MOD",
+"MOE",
+"MOO",
+"MOP",
+"MOS",
+"MOT",
+"MOW",
+"MUD",
+"MUG",
+"MUM",
+"MY",
+"NAB",
+"NAG",
+"NAN",
+"NAP",
+"NAT",
+"NAY",
+"NE",
+"NED",
+"NEE",
+"NET",
+"NEW",
+"NIB",
+"NIL",
+"NIP",
+"NIT",
+"NO",
+"NOB",
+"NOD",
+"NON",
+"NOR",
+"NOT",
+"NOV",
+"NOW",
+"NU",
+"NUN",
+"NUT",
+"O",
+"OAF",
+"OAK",
+"OAR",
+"OAT",
+"ODD",
+"ODE",
+"OF",
+"OFF",
+"OFT",
+"OH",
+"OIL",
+"OK",
+"OLD",
+"ON",
+"ONE",
+"OR",
+"ORB",
+"ORE",
+"ORR",
+"OS",
+"OTT",
+"OUR",
+"OUT",
+"OVA",
+"OW",
+"OWE",
+"OWL",
+"OWN",
+"OX",
+"PA",
+"PAD",
+"PAL",
+"PAM",
+"PAN",
+"PAP",
+"PAR",
+"PAT",
+"PAW",
+"PAY",
+"PEA",
+"PEG",
+"PEN",
+"PEP",
+"PER",
+"PET",
+"PEW",
+"PHI",
+"PI",
+"PIE",
+"PIN",
+"PIT",
+"PLY",
+"PO",
+"POD",
+"POE",
+"POP",
+"POT",
+"POW",
+"PRO",
+"PRY",
+"PUB",
+"PUG",
+"PUN",
+"PUP",
+"PUT",
+"QUO",
+"RAG",
+"RAM",
+"RAN",
+"RAP",
+"RAT",
+"RAW",
+"RAY",
+"REB",
+"RED",
+"REP",
+"RET",
+"RIB",
+"RID",
+"RIG",
+"RIM",
+"RIO",
+"RIP",
+"ROB",
+"ROD",
+"ROE",
+"RON",
+"ROT",
+"ROW",
+"ROY",
+"RUB",
+"RUE",
+"RUG",
+"RUM",
+"RUN",
+"RYE",
+"SAC",
+"SAD",
+"SAG",
+"SAL",
+"SAM",
+"SAN",
+"SAP",
+"SAT",
+"SAW",
+"SAY",
+"SEA",
+"SEC",
+"SEE",
+"SEN",
+"SET",
+"SEW",
+"SHE",
+"SHY",
+"SIN",
+"SIP",
+"SIR",
+"SIS",
+"SIT",
+"SKI",
+"SKY",
+"SLY",
+"SO",
+"SOB",
+"SOD",
+"SON",
+"SOP",
+"SOW",
+"SOY",
+"SPA",
+"SPY",
+"SUB",
+"SUD",
+"SUE",
+"SUM",
+"SUN",
+"SUP",
+"TAB",
+"TAD",
+"TAG",
+"TAN",
+"TAP",
+"TAR",
+"TEA",
+"TED",
+"TEE",
+"TEN",
+"THE",
+"THY",
+"TIC",
+"TIE",
+"TIM",
+"TIN",
+"TIP",
+"TO",
+"TOE",
+"TOG",
+"TOM",
+"TON",
+"TOO",
+"TOP",
+"TOW",
+"TOY",
+"TRY",
+"TUB",
+"TUG",
+"TUM",
+"TUN",
+"TWO",
+"UN",
+"UP",
+"US",
+"USE",
+"VAN",
+"VAT",
+"VET",
+"VIE",
+"WAD",
+"WAG",
+"WAR",
+"WAS",
+"WAY",
+"WE",
+"WEB",
+"WED",
+"WEE",
+"WET",
+"WHO",
+"WHY",
+"WIN",
+"WIT",
+"WOK",
+"WON",
+"WOO",
+"WOW",
+"WRY",
+"WU",
+"YAM",
+"YAP",
+"YAW",
+"YE",
+"YEA",
+"YES",
+"YET",
+"YOU",
+"ABED",
+"ABEL",
+"ABET",
+"ABLE",
+"ABUT",
+"ACHE",
+"ACID",
+"ACME",
+"ACRE",
+"ACTA",
+"ACTS",
+"ADAM",
+"ADDS",
+"ADEN",
+"AFAR",
+"AFRO",
+"AGEE",
+"AHEM",
+"AHOY",
+"AIDA",
+"AIDE",
+"AIDS",
+"AIRY",
+"AJAR",
+"AKIN",
+"ALAN",
+"ALEC",
+"ALGA",
+"ALIA",
+"ALLY",
+"ALMA",
+"ALOE",
+"ALSO",
+"ALTO",
+"ALUM",
+"ALVA",
+"AMEN",
+"AMES",
+"AMID",
+"AMMO",
+"AMOK",
+"AMOS",
+"AMRA",
+"ANDY",
+"ANEW",
+"ANNA",
+"ANNE",
+"ANTE",
+"ANTI",
+"AQUA",
+"ARAB",
+"ARCH",
+"AREA",
+"ARGO",
+"ARID",
+"ARMY",
+"ARTS",
+"ARTY",
+"ASIA",
+"ASKS",
+"ATOM",
+"AUNT",
+"AURA",
+"AUTO",
+"AVER",
+"AVID",
+"AVIS",
+"AVON",
+"AVOW",
+"AWAY",
+"AWRY",
+"BABE",
+"BABY",
+"BACH",
+"BACK",
+"BADE",
+"BAIL",
+"BAIT",
+"BAKE",
+"BALD",
+"BALE",
+"BALI",
+"BALK",
+"BALL",
+"BALM",
+"BAND",
+"BANE",
+"BANG",
+"BANK",
+"BARB",
+"BARD",
+"BARE",
+"BARK",
+"BARN",
+"BARR",
+"BASE",
+"BASH",
+"BASK",
+"BASS",
+"BATE",
+"BATH",
+"BAWD",
+"BAWL",
+"BEAD",
+"BEAK",
+"BEAM",
+"BEAN",
+"BEAR",
+"BEAT",
+"BEAU",
+"BECK",
+"BEEF",
+"BEEN",
+"BEER",
+"BEET",
+"BELA",
+"BELL",
+"BELT",
+"BEND",
+"BENT",
+"BERG",
+"BERN",
+"BERT",
+"BESS",
+"BEST",
+"BETA",
+"BETH",
+"BHOY",
+"BIAS",
+"BIDE",
+"BIEN",
+"BILE",
+"BILK",
+"BILL",
+"BIND",
+"BING",
+"BIRD",
+"BITE",
+"BITS",
+"BLAB",
+"BLAT",
+"BLED",
+"BLEW",
+"BLOB",
+"BLOC",
+"BLOT",
+"BLOW",
+"BLUE",
+"BLUM",
+"BLUR",
+"BOAR",
+"BOAT",
+"BOCA",
+"BOCK",
+"BODE",
+"BODY",
+"BOGY",
+"BOHR",
+"BOIL",
+"BOLD",
+"BOLO",
+"BOLT",
+"BOMB",
+"BONA",
+"BOND",
+"BONE",
+"BONG",
+"BONN",
+"BONY",
+"BOOK",
+"BOOM",
+"BOON",
+"BOOT",
+"BORE",
+"BORG",
+"BORN",
+"BOSE",
+"BOSS",
+"BOTH",
+"BOUT",
+"BOWL",
+"BOYD",
+"BRAD",
+"BRAE",
+"BRAG",
+"BRAN",
+"BRAY",
+"BRED",
+"BREW",
+"BRIG",
+"BRIM",
+"BROW",
+"BUCK",
+"BUDD",
+"BUFF",
+"BULB",
+"BULK",
+"BULL",
+"BUNK",
+"BUNT",
+"BUOY",
+"BURG",
+"BURL",
+"BURN",
+"BURR",
+"BURT",
+"BURY",
+"BUSH",
+"BUSS",
+"BUST",
+"BUSY",
+"BYTE",
+"CADY",
+"CAFE",
+"CAGE",
+"CAIN",
+"CAKE",
+"CALF",
+"CALL",
+"CALM",
+"CAME",
+"CANE",
+"CANT",
+"CARD",
+"CARE",
+"CARL",
+"CARR",
+"CART",
+"CASE",
+"CASH",
+"CASK",
+"CAST",
+"CAVE",
+"CEIL",
+"CELL",
+"CENT",
+"CERN",
+"CHAD",
+"CHAR",
+"CHAT",
+"CHAW",
+"CHEF",
+"CHEN",
+"CHEW",
+"CHIC",
+"CHIN",
+"CHOU",
+"CHOW",
+"CHUB",
+"CHUG",
+"CHUM",
+"CITE",
+"CITY",
+"CLAD",
+"CLAM",
+"CLAN",
+"CLAW",
+"CLAY",
+"CLOD",
+"CLOG",
+"CLOT",
+"CLUB",
+"CLUE",
+"COAL",
+"COAT",
+"COCA",
+"COCK",
+"COCO",
+"CODA",
+"CODE",
+"CODY",
+"COED",
+"COIL",
+"COIN",
+"COKE",
+"COLA",
+"COLD",
+"COLT",
+"COMA",
+"COMB",
+"COME",
+"COOK",
+"COOL",
+"COON",
+"COOT",
+"CORD",
+"CORE",
+"CORK",
+"CORN",
+"COST",
+"COVE",
+"COWL",
+"CRAB",
+"CRAG",
+"CRAM",
+"CRAY",
+"CREW",
+"CRIB",
+"CROW",
+"CRUD",
+"CUBA",
+"CUBE",
+"CUFF",
+"CULL",
+"CULT",
+"CUNY",
+"CURB",
+"CURD",
+"CURE",
+"CURL",
+"CURT",
+"CUTS",
+"DADE",
+"DALE",
+"DAME",
+"DANA",
+"DANE",
+"DANG",
+"DANK",
+"DARE",
+"DARK",
+"DARN",
+"DART",
+"DASH",
+"DATA",
+"DATE",
+"DAVE",
+"DAVY",
+"DAWN",
+"DAYS",
+"DEAD",
+"DEAF",
+"DEAL",
+"DEAN",
+"DEAR",
+"DEBT",
+"DECK",
+"DEED",
+"DEEM",
+"DEER",
+"DEFT",
+"DEFY",
+"DELL",
+"DENT",
+"DENY",
+"DESK",
+"DIAL",
+"DICE",
+"DIED",
+"DIET",
+"DIME",
+"DINE",
+"DING",
+"DINT",
+"DIRE",
+"DIRT",
+"DISC",
+"DISH",
+"DISK",
+"DIVE",
+"DOCK",
+"DOES",
+"DOLE",
+"DOLL",
+"DOLT",
+"DOME",
+"DONE",
+"DOOM",
+"DOOR",
+"DORA",
+"DOSE",
+"DOTE",
+"DOUG",
+"DOUR",
+"DOVE",
+"DOWN",
+"DRAB",
+"DRAG",
+"DRAM",
+"DRAW",
+"DREW",
+"DRUB",
+"DRUG",
+"DRUM",
+"DUAL",
+"DUCK",
+"DUCT",
+"DUEL",
+"DUET",
+"DUKE",
+"DULL",
+"DUMB",
+"DUNE",
+"DUNK",
+"DUSK",
+"DUST",
+"DUTY",
+"EACH",
+"EARL",
+"EARN",
+"EASE",
+"EAST",
+"EASY",
+"EBEN",
+"ECHO",
+"EDDY",
+"EDEN",
+"EDGE",
+"EDGY",
+"EDIT",
+"EDNA",
+"EGAN",
+"ELAN",
+"ELBA",
+"ELLA",
+"ELSE",
+"EMIL",
+"EMIT",
+"EMMA",
+"ENDS",
+"ERIC",
+"EROS",
+"EVEN",
+"EVER",
+"EVIL",
+"EYED",
+"FACE",
+"FACT",
+"FADE",
+"FAIL",
+"FAIN",
+"FAIR",
+"FAKE",
+"FALL",
+"FAME",
+"FANG",
+"FARM",
+"FAST",
+"FATE",
+"FAWN",
+"FEAR",
+"FEAT",
+"FEED",
+"FEEL",
+"FEET",
+"FELL",
+"FELT",
+"FEND",
+"FERN",
+"FEST",
+"FEUD",
+"FIEF",
+"FIGS",
+"FILE",
+"FILL",
+"FILM",
+"FIND",
+"FINE",
+"FINK",
+"FIRE",
+"FIRM",
+"FISH",
+"FISK",
+"FIST",
+"FITS",
+"FIVE",
+"FLAG",
+"FLAK",
+"FLAM",
+"FLAT",
+"FLAW",
+"FLEA",
+"FLED",
+"FLEW",
+"FLIT",
+"FLOC",
+"FLOG",
+"FLOW",
+"FLUB",
+"FLUE",
+"FOAL",
+"FOAM",
+"FOGY",
+"FOIL",
+"FOLD",
+"FOLK",
+"FOND",
+"FONT",
+"FOOD",
+"FOOL",
+"FOOT",
+"FORD",
+"FORE",
+"FORK",
+"FORM",
+"FORT",
+"FOSS",
+"FOUL",
+"FOUR",
+"FOWL",
+"FRAU",
+"FRAY",
+"FRED",
+"FREE",
+"FRET",
+"FREY",
+"FROG",
+"FROM",
+"FUEL",
+"FULL",
+"FUME",
+"FUND",
+"FUNK",
+"FURY",
+"FUSE",
+"FUSS",
+"GAFF",
+"GAGE",
+"GAIL",
+"GAIN",
+"GAIT",
+"GALA",
+"GALE",
+"GALL",
+"GALT",
+"GAME",
+"GANG",
+"GARB",
+"GARY",
+"GASH",
+"GATE",
+"GAUL",
+"GAUR",
+"GAVE",
+"GAWK",
+"GEAR",
+"GELD",
+"GENE",
+"GENT",
+"GERM",
+"GETS",
+"GIBE",
+"GIFT",
+"GILD",
+"GILL",
+"GILT",
+"GINA",
+"GIRD",
+"GIRL",
+"GIST",
+"GIVE",
+"GLAD",
+"GLEE",
+"GLEN",
+"GLIB",
+"GLOB",
+"GLOM",
+"GLOW",
+"GLUE",
+"GLUM",
+"GLUT",
+"GOAD",
+"GOAL",
+"GOAT",
+"GOER",
+"GOES",
+"GOLD",
+"GOLF",
+"GONE",
+"GONG",
+"GOOD",
+"GOOF",
+"GORE",
+"GORY",
+"GOSH",
+"GOUT",
+"GOWN",
+"GRAB",
+"GRAD",
+"GRAY",
+"GREG",
+"GREW",
+"GREY",
+"GRID",
+"GRIM",
+"GRIN",
+"GRIT",
+"GROW",
+"GRUB",
+"GULF",
+"GULL",
+"GUNK",
+"GURU",
+"GUSH",
+"GUST",
+"GWEN",
+"GWYN",
+"HAAG",
+"HAAS",
+"HACK",
+"HAIL",
+"HAIR",
+"HALE",
+"HALF",
+"HALL",
+"HALO",
+"HALT",
+"HAND",
+"HANG",
+"HANK",
+"HANS",
+"HARD",
+"HARK",
+"HARM",
+"HART",
+"HASH",
+"HAST",
+"HATE",
+"HATH",
+"HAUL",
+"HAVE",
+"HAWK",
+"HAYS",
+"HEAD",
+"HEAL",
+"HEAR",
+"HEAT",
+"HEBE",
+"HECK",
+"HEED",
+"HEEL",
+"HEFT",
+"HELD",
+"HELL",
+"HELM",
+"HERB",
+"HERD",
+"HERE",
+"HERO",
+"HERS",
+"HESS",
+"HEWN",
+"HICK",
+"HIDE",
+"HIGH",
+"HIKE",
+"HILL",
+"HILT",
+"HIND",
+"HINT",
+"HIRE",
+"HISS",
+"HIVE",
+"HOBO",
+"HOCK",
+"HOFF",
+"HOLD",
+"HOLE",
+"HOLM",
+"HOLT",
+"HOME",
+"HONE",
+"HONK",
+"HOOD",
+"HOOF",
+"HOOK",
+"HOOT",
+"HORN",
+"HOSE",
+"HOST",
+"HOUR",
+"HOVE",
+"HOWE",
+"HOWL",
+"HOYT",
+"HUCK",
+"HUED",
+"HUFF",
+"HUGE",
+"HUGH",
+"HUGO",
+"HULK",
+"HULL",
+"HUNK",
+"HUNT",
+"HURD",
+"HURL",
+"HURT",
+"HUSH",
+"HYDE",
+"HYMN",
+"IBIS",
+"ICON",
+"IDEA",
+"IDLE",
+"IFFY",
+"INCA",
+"INCH",
+"INTO",
+"IONS",
+"IOTA",
+"IOWA",
+"IRIS",
+"IRMA",
+"IRON",
+"ISLE",
+"ITCH",
+"ITEM",
+"IVAN",
+"JACK",
+"JADE",
+"JAIL",
+"JAKE",
+"JANE",
+"JAVA",
+"JEAN",
+"JEFF",
+"JERK",
+"JESS",
+"JEST",
+"JIBE",
+"JILL",
+"JILT",
+"JIVE",
+"JOAN",
+"JOBS",
+"JOCK",
+"JOEL",
+"JOEY",
+"JOHN",
+"JOIN",
+"JOKE",
+"JOLT",
+"JOVE",
+"JUDD",
+"JUDE",
+"JUDO",
+"JUDY",
+"JUJU",
+"JUKE",
+"JULY",
+"JUNE",
+"JUNK",
+"JUNO",
+"JURY",
+"JUST",
+"JUTE",
+"KAHN",
+"KALE",
+"KANE",
+"KANT",
+"KARL",
+"KATE",
+"KEEL",
+"KEEN",
+"KENO",
+"KENT",
+"KERN",
+"KERR",
+"KEYS",
+"KICK",
+"KILL",
+"KIND",
+"KING",
+"KIRK",
+"KISS",
+"KITE",
+"KLAN",
+"KNEE",
+"KNEW",
+"KNIT",
+"KNOB",
+"KNOT",
+"KNOW",
+"KOCH",
+"KONG",
+"KUDO",
+"KURD",
+"KURT",
+"KYLE",
+"LACE",
+"LACK",
+"LACY",
+"LADY",
+"LAID",
+"LAIN",
+"LAIR",
+"LAKE",
+"LAMB",
+"LAME",
+"LAND",
+"LANE",
+"LANG",
+"LARD",
+"LARK",
+"LASS",
+"LAST",
+"LATE",
+"LAUD",
+"LAVA",
+"LAWN",
+"LAWS",
+"LAYS",
+"LEAD",
+"LEAF",
+"LEAK",
+"LEAN",
+"LEAR",
+"LEEK",
+"LEER",
+"LEFT",
+"LEND",
+"LENS",
+"LENT",
+"LEON",
+"LESK",
+"LESS",
+"LEST",
+"LETS",
+"LIAR",
+"LICE",
+"LICK",
+"LIED",
+"LIEN",
+"LIES",
+"LIEU",
+"LIFE",
+"LIFT",
+"LIKE",
+"LILA",
+"LILT",
+"LILY",
+"LIMA",
+"LIMB",
+"LIME",
+"LIND",
+"LINE",
+"LINK",
+"LINT",
+"LION",
+"LISA",
+"LIST",
+"LIVE",
+"LOAD",
+"LOAF",
+"LOAM",
+"LOAN",
+"LOCK",
+"LOFT",
+"LOGE",
+"LOIS",
+"LOLA",
+"LONE",
+"LONG",
+"LOOK",
+"LOON",
+"LOOT",
+"LORD",
+"LORE",
+"LOSE",
+"LOSS",
+"LOST",
+"LOUD",
+"LOVE",
+"LOWE",
+"LUCK",
+"LUCY",
+"LUGE",
+"LUKE",
+"LULU",
+"LUND",
+"LUNG",
+"LURA",
+"LURE",
+"LURK",
+"LUSH",
+"LUST",
+"LYLE",
+"LYNN",
+"LYON",
+"LYRA",
+"MACE",
+"MADE",
+"MAGI",
+"MAID",
+"MAIL",
+"MAIN",
+"MAKE",
+"MALE",
+"MALI",
+"MALL",
+"MALT",
+"MANA",
+"MANN",
+"MANY",
+"MARC",
+"MARE",
+"MARK",
+"MARS",
+"MART",
+"MARY",
+"MASH",
+"MASK",
+"MASS",
+"MAST",
+"MATE",
+"MATH",
+"MAUL",
+"MAYO",
+"MEAD",
+"MEAL",
+"MEAN",
+"MEAT",
+"MEEK",
+"MEET",
+"MELD",
+"MELT",
+"MEMO",
+"MEND",
+"MENU",
+"MERT",
+"MESH",
+"MESS",
+"MICE",
+"MIKE",
+"MILD",
+"MILE",
+"MILK",
+"MILL",
+"MILT",
+"MIMI",
+"MIND",
+"MINE",
+"MINI",
+"MINK",
+"MINT",
+"MIRE",
+"MISS",
+"MIST",
+"MITE",
+"MITT",
+"MOAN",
+"MOAT",
+"MOCK",
+"MODE",
+"MOLD",
+"MOLE",
+"MOLL",
+"MOLT",
+"MONA",
+"MONK",
+"MONT",
+"MOOD",
+"MOON",
+"MOOR",
+"MOOT",
+"MORE",
+"MORN",
+"MORT",
+"MOSS",
+"MOST",
+"MOTH",
+"MOVE",
+"MUCH",
+"MUCK",
+"MUDD",
+"MUFF",
+"MULE",
+"MULL",
+"MURK",
+"MUSH",
+"MUST",
+"MUTE",
+"MUTT",
+"MYRA",
+"MYTH",
+"NAGY",
+"NAIL",
+"NAIR",
+"NAME",
+"NARY",
+"NASH",
+"NAVE",
+"NAVY",
+"NEAL",
+"NEAR",
+"NEAT",
+"NECK",
+"NEED",
+"NEIL",
+"NELL",
+"NEON",
+"NERO",
+"NESS",
+"NEST",
+"NEWS",
+"NEWT",
+"NIBS",
+"NICE",
+"NICK",
+"NILE",
+"NINA",
+"NINE",
+"NOAH",
+"NODE",
+"NOEL",
+"NOLL",
+"NONE",
+"NOOK",
+"NOON",
+"NORM",
+"NOSE",
+"NOTE",
+"NOUN",
+"NOVA",
+"NUDE",
+"NULL",
+"NUMB",
+"OATH",
+"OBEY",
+"OBOE",
+"ODIN",
+"OHIO",
+"OILY",
+"OINT",
+"OKAY",
+"OLAF",
+"OLDY",
+"OLGA",
+"OLIN",
+"OMAN",
+"OMEN",
+"OMIT",
+"ONCE",
+"ONES",
+"ONLY",
+"ONTO",
+"ONUS",
+"ORAL",
+"ORGY",
+"OSLO",
+"OTIS",
+"OTTO",
+"OUCH",
+"OUST",
+"OUTS",
+"OVAL",
+"OVEN",
+"OVER",
+"OWLY",
+"OWNS",
+"QUAD",
+"QUIT",
+"QUOD",
+"RACE",
+"RACK",
+"RACY",
+"RAFT",
+"RAGE",
+"RAID",
+"RAIL",
+"RAIN",
+"RAKE",
+"RANK",
+"RANT",
+"RARE",
+"RASH",
+"RATE",
+"RAVE",
+"RAYS",
+"READ",
+"REAL",
+"REAM",
+"REAR",
+"RECK",
+"REED",
+"REEF",
+"REEK",
+"REEL",
+"REID",
+"REIN",
+"RENA",
+"REND",
+"RENT",
+"REST",
+"RICE",
+"RICH",
+"RICK",
+"RIDE",
+"RIFT",
+"RILL",
+"RIME",
+"RING",
+"RINK",
+"RISE",
+"RISK",
+"RITE",
+"ROAD",
+"ROAM",
+"ROAR",
+"ROBE",
+"ROCK",
+"RODE",
+"ROIL",
+"ROLL",
+"ROME",
+"ROOD",
+"ROOF",
+"ROOK",
+"ROOM",
+"ROOT",
+"ROSA",
+"ROSE",
+"ROSS",
+"ROSY",
+"ROTH",
+"ROUT",
+"ROVE",
+"ROWE",
+"ROWS",
+"RUBE",
+"RUBY",
+"RUDE",
+"RUDY",
+"RUIN",
+"RULE",
+"RUNG",
+"RUNS",
+"RUNT",
+"RUSE",
+"RUSH",
+"RUSK",
+"RUSS",
+"RUST",
+"RUTH",
+"SACK",
+"SAFE",
+"SAGE",
+"SAID",
+"SAIL",
+"SALE",
+"SALK",
+"SALT",
+"SAME",
+"SAND",
+"SANE",
+"SANG",
+"SANK",
+"SARA",
+"SAUL",
+"SAVE",
+"SAYS",
+"SCAN",
+"SCAR",
+"SCAT",
+"SCOT",
+"SEAL",
+"SEAM",
+"SEAR",
+"SEAT",
+"SEED",
+"SEEK",
+"SEEM",
+"SEEN",
+"SEES",
+"SELF",
+"SELL",
+"SEND",
+"SENT",
+"SETS",
+"SEWN",
+"SHAG",
+"SHAM",
+"SHAW",
+"SHAY",
+"SHED",
+"SHIM",
+"SHIN",
+"SHOD",
+"SHOE",
+"SHOT",
+"SHOW",
+"SHUN",
+"SHUT",
+"SICK",
+"SIDE",
+"SIFT",
+"SIGH",
+"SIGN",
+"SILK",
+"SILL",
+"SILO",
+"SILT",
+"SINE",
+"SING",
+"SINK",
+"SIRE",
+"SITE",
+"SITS",
+"SITU",
+"SKAT",
+"SKEW",
+"SKID",
+"SKIM",
+"SKIN",
+"SKIT",
+"SLAB",
+"SLAM",
+"SLAT",
+"SLAY",
+"SLED",
+"SLEW",
+"SLID",
+"SLIM",
+"SLIT",
+"SLOB",
+"SLOG",
+"SLOT",
+"SLOW",
+"SLUG",
+"SLUM",
+"SLUR",
+"SMOG",
+"SMUG",
+"SNAG",
+"SNOB",
+"SNOW",
+"SNUB",
+"SNUG",
+"SOAK",
+"SOAR",
+"SOCK",
+"SODA",
+"SOFA",
+"SOFT",
+"SOIL",
+"SOLD",
+"SOME",
+"SONG",
+"SOON",
+"SOOT",
+"SORE",
+"SORT",
+"SOUL",
+"SOUR",
+"SOWN",
+"STAB",
+"STAG",
+"STAN",
+"STAR",
+"STAY",
+"STEM",
+"STEW",
+"STIR",
+"STOW",
+"STUB",
+"STUN",
+"SUCH",
+"SUDS",
+"SUIT",
+"SULK",
+"SUMS",
+"SUNG",
+"SUNK",
+"SURE",
+"SURF",
+"SWAB",
+"SWAG",
+"SWAM",
+"SWAN",
+"SWAT",
+"SWAY",
+"SWIM",
+"SWUM",
+"TACK",
+"TACT",
+"TAIL",
+"TAKE",
+"TALE",
+"TALK",
+"TALL",
+"TANK",
+"TASK",
+"TATE",
+"TAUT",
+"TEAL",
+"TEAM",
+"TEAR",
+"TECH",
+"TEEM",
+"TEEN",
+"TEET",
+"TELL",
+"TEND",
+"TENT",
+"TERM",
+"TERN",
+"TESS",
+"TEST",
+"THAN",
+"THAT",
+"THEE",
+"THEM",
+"THEN",
+"THEY",
+"THIN",
+"THIS",
+"THUD",
+"THUG",
+"TICK",
+"TIDE",
+"TIDY",
+"TIED",
+"TIER",
+"TILE",
+"TILL",
+"TILT",
+"TIME",
+"TINA",
+"TINE",
+"TINT",
+"TINY",
+"TIRE",
+"TOAD",
+"TOGO",
+"TOIL",
+"TOLD",
+"TOLL",
+"TONE",
+"TONG",
+"TONY",
+"TOOK",
+"TOOL",
+"TOOT",
+"TORE",
+"TORN",
+"TOTE",
+"TOUR",
+"TOUT",
+"TOWN",
+"TRAG",
+"TRAM",
+"TRAY",
+"TREE",
+"TREK",
+"TRIG",
+"TRIM",
+"TRIO",
+"TROD",
+"TROT",
+"TROY",
+"TRUE",
+"TUBA",
+"TUBE",
+"TUCK",
+"TUFT",
+"TUNA",
+"TUNE",
+"TUNG",
+"TURF",
+"TURN",
+"TUSK",
+"TWIG",
+"TWIN",
+"TWIT",
+"ULAN",
+"UNIT",
+"URGE",
+"USED",
+"USER",
+"USES",
+"UTAH",
+"VAIL",
+"VAIN",
+"VALE",
+"VARY",
+"VASE",
+"VAST",
+"VEAL",
+"VEDA",
+"VEIL",
+"VEIN",
+"VEND",
+"VENT",
+"VERB",
+"VERY",
+"VETO",
+"VICE",
+"VIEW",
+"VINE",
+"VISE",
+"VOID",
+"VOLT",
+"VOTE",
+"WACK",
+"WADE",
+"WAGE",
+"WAIL",
+"WAIT",
+"WAKE",
+"WALE",
+"WALK",
+"WALL",
+"WALT",
+"WAND",
+"WANE",
+"WANG",
+"WANT",
+"WARD",
+"WARM",
+"WARN",
+"WART",
+"WASH",
+"WAST",
+"WATS",
+"WATT",
+"WAVE",
+"WAVY",
+"WAYS",
+"WEAK",
+"WEAL",
+"WEAN",
+"WEAR",
+"WEED",
+"WEEK",
+"WEIR",
+"WELD",
+"WELL",
+"WELT",
+"WENT",
+"WERE",
+"WERT",
+"WEST",
+"WHAM",
+"WHAT",
+"WHEE",
+"WHEN",
+"WHET",
+"WHOA",
+"WHOM",
+"WICK",
+"WIFE",
+"WILD",
+"WILL",
+"WIND",
+"WINE",
+"WING",
+"WINK",
+"WINO",
+"WIRE",
+"WISE",
+"WISH",
+"WITH",
+"WOLF",
+"WONT",
+"WOOD",
+"WOOL",
+"WORD",
+"WORE",
+"WORK",
+"WORM",
+"WORN",
+"WOVE",
+"WRIT",
+"WYNN",
+"YALE",
+"YANG",
+"YANK",
+"YARD",
+"YARN",
+"YAWL",
+"YAWN",
+"YEAH",
+"YEAR",
+"YELL",
+"YOGA",
+"YOKE"
+};
+
+/* Encode 8 bytes in 'c' as a string of English words.
+ * Returns a pointer to a static buffer
+ */
+char *
+btoe(engout,c)
+char *c, *engout;
+{
+ char cp[9]; /* add in room for the parity 2 bits*/
+ int p,i ;
+
+ engout[0] = '\0';
+ memcpy(cp, c,8);
+ /* compute parity */
+ for(p = 0,i = 0; i < 64;i += 2)
+ p += extract(cp,i,2);
+
+ cp[8] = (char)p << 6;
+ strncat(engout,&Wp[extract(cp, 0,11)][0],4);
+ strcat(engout," ");
+ strncat(engout,&Wp[extract(cp,11,11)][0],4);
+ strcat(engout," ");
+ strncat(engout,&Wp[extract(cp,22,11)][0],4);
+ strcat(engout," ");
+ strncat(engout,&Wp[extract(cp,33,11)][0],4);
+ strcat(engout," ");
+ strncat(engout,&Wp[extract(cp,44,11)][0],4);
+ strcat(engout," ");
+ strncat(engout,&Wp[extract(cp,55,11)][0],4);
+#ifdef notdef
+ printf("engout is %s\n\r",engout);
+#endif
+ return(engout);
+}
+
+/* convert English to binary
+ * returns 1 OK - all good words and parity is OK
+ * 0 word not in data base
+ * -1 badly formed in put ie > 4 char word
+ * -2 words OK but parity is wrong
+ */
+int
+etob(out, e)
+char *out;
+char *e;
+{
+ char *word;
+ int i, p, v,l, low,high;
+ char b[9];
+ char input[36];
+
+ if(e == NULL)
+ return -1;
+
+ strncpy(input,e,sizeof(input));
+ memset(b, 0, sizeof(b));
+ memset(out, 0, 8);
+ for(i=0,p=0;i<6;i++,p+=11){
+ if((word = strtok(i == 0 ? input : NULL," ")) == NULL)
+ return -1;
+ l = strlen(word);
+ if(l > 4 || l < 1){
+ return -1;
+ } else if(l < 4){
+ low = 0;
+ high = 570;
+ } else {
+ low = 571;
+ high = 2047;
+ }
+ standard(word);
+ if( (v = wsrch(word,low,high)) < 0 )
+ return 0;
+ insert(b,v,p,11);
+ }
+
+ /* now check the parity of what we got */
+ for(p = 0, i = 0; i < 64; i +=2)
+ p += extract(b, i, 2);
+
+ if( (p & 3) != extract(b, 64,2) )
+ return -2;
+
+ memcpy(out,b,8);
+
+ return 1;
+}
+/* Display 8 bytes as a series of 16-bit hex digits */
+char *
+put8(out,s)
+char *out;
+char *s;
+{
+ sprintf(out,"%02X%02X %02X%02X %02X%02X %02X%02X",
+ s[0] & 0xff,s[1] & 0xff,s[2] & 0xff,
+ s[3] & 0xff,s[4] & 0xff,s[5] & 0xff,
+ s[6] & 0xff,s[7] & 0xff);
+ return out;
+}
+#ifdef notdef
+/* Encode 8 bytes in 'cp' as stream of ascii letters.
+ * Provided as a possible alternative to btoe()
+ */
+char *
+btoc(cp)
+char *cp;
+{
+ int i;
+ static char out[31];
+
+ /* code out put by characters 6 bits each added to 0x21 (!)*/
+ for(i=0;i <= 10;i++){
+ /* last one is only 4 bits not 6*/
+ out[i] = '!'+ extract(cp,6*i,i >= 10 ? 4:6);
+ }
+ out[i] = '\0';
+ return(out);
+}
+#endif
+
+/* Internal subroutines for word encoding/decoding */
+
+/* Dictionary binary search */
+static int
+wsrch(w,low,high)
+char *w;
+int low, high;
+{
+ int i,j;
+
+ for(;;){
+ i = (low + high)/2;
+ if((j = strncmp(w,Wp[i],4)) == 0)
+ return i; /* Found it */
+ if(high == low+1){
+ /* Avoid effects of integer truncation in /2 */
+ if(strncmp(w,Wp[high],4) == 0)
+ return high;
+ else
+ return -1;
+ }
+ if(low >= high)
+ return -1; /* I don't *think* this can happen...*/
+ if(j < 0)
+ high = i; /* Search lower half */
+ else
+ low = i; /* Search upper half */
+ }
+}
+static void
+insert(s, x, start, length)
+char *s;
+int x;
+int start, length;
+{
+ unsigned char cl;
+ unsigned char cc;
+ unsigned char cr;
+ unsigned long y;
+ int shift;
+
+ assert(length <= 11);
+ assert(start >= 0);
+ assert(length >= 0);
+ assert(start +length <= 66);
+
+ shift = ((8 -(( start + length) % 8))%8);
+ y = (long) x << shift;
+ cl = (y >> 16) & 0xff;
+ cc = (y >> 8) & 0xff;
+ cr = y & 0xff;
+ if(shift + length > 16){
+ s[start /8] |= cl;
+ s[start/8 +1] |= cc;
+ s[start/8 +2] |= cr;
+ } else if(shift +length > 8){
+ s[start/8] |= cc;
+ s[start/8 + 1] |= cr;
+ } else {
+ s[start/8] |= cr;
+ }
+}
+
+static void
+standard(word)
+register char *word;
+{
+ while(*word){
+ if(!isascii(*word))
+ break;
+ if(islower(*word))
+ *word = toupper(*word);
+ if(*word == '1')
+ *word = 'L';
+ if(*word == '0')
+ *word = 'O';
+ if(*word == '5')
+ *word = 'S';
+ word++;
+ }
+}
+
+/* Extract 'length' bits from the char array 's' starting with bit 'start' */
+static unsigned long
+extract(s, start, length)
+char *s;
+int start, length;
+{
+ unsigned char cl;
+ unsigned char cc;
+ unsigned char cr;
+ unsigned long x;
+
+ assert(length <= 11);
+ assert(start >= 0);
+ assert(length >= 0);
+ assert(start +length <= 66);
+
+ cl = s[start/8];
+ cc = s[start/8 +1];
+ cr = s[start/8 +2];
+ x = ((long)(cl<<8 | cc) <<8 | cr) ;
+ x = x >> (24 - (length + (start %8)));
+ x =( x & (0xffff >> (16-length) ) );
+ return(x);
+}
+
diff --git a/lib/libskey/skey_crypt.c b/lib/libskey/skey_crypt.c
new file mode 100644
index 000000000000..ca1024f77418
--- /dev/null
+++ b/lib/libskey/skey_crypt.c
@@ -0,0 +1,38 @@
+/* Author: Wietse Venema, Eindhoven University of Technology. */
+
+#include <string.h>
+#include <stdio.h>
+#include <pwd.h>
+
+#include "skey.h"
+
+/* skey_crypt - return encrypted UNIX passwd if s/key or regular password ok */
+
+char *skey_crypt(pp, salt, pwd, pwok)
+char *pp;
+char *salt;
+struct passwd *pwd;
+int pwok;
+{
+ struct skey skey;
+ char *p;
+ char *crypt();
+
+ /* Try s/key authentication even when the UNIX password is permitted. */
+
+ if (pwd != 0 && skeylookup(&skey, pwd->pw_name) == 0
+ && skeyverify(&skey, pp) == 0) {
+ /* s/key authentication succeeded */
+ return (pwd->pw_passwd);
+ }
+
+ /* When s/key authentication does not work, always invoke crypt(). */
+
+ p = crypt(pp, salt);
+ if (pwok && pwd != 0 && strcmp(p, pwd->pw_passwd) == 0)
+ return (pwd->pw_passwd);
+
+ /* The user does not exist or entered bad input. */
+
+ return (":");
+}
diff --git a/lib/libskey/skeylogin.c b/lib/libskey/skeylogin.c
new file mode 100644
index 000000000000..bc49cb715231
--- /dev/null
+++ b/lib/libskey/skeylogin.c
@@ -0,0 +1,328 @@
+/* Login code for S/KEY Authentication. S/KEY is a trademark
+ * of Bellcore.
+ *
+ * Mink is the former name of the S/KEY authentication system.
+ * Many references for mink may still be found in this program. */
+
+#include <sys/param.h>
+#ifdef QUOTA
+#include <sys/quota.h>
+#endif
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <errno.h>
+#include "skey.h"
+
+#define KEYFILE "/etc/skeykeys"
+
+char *skipspace();
+int skeylookup __ARGS((struct skey *mp,char *name));
+
+#define setpriority(x,y,z) /* nothing */
+
+/* Issue a skey challenge for user 'name'. If successful,
+ * fill in the caller's skey structure and return 0. If unsuccessful
+ * (e.g., if name is unknown) return -1.
+ *
+ * The file read/write pointer is left at the start of the
+ * record.
+ */
+int
+getskeyprompt(mp,name,prompt)
+struct skey *mp;
+char *name;
+char *prompt;
+{
+ int rval;
+
+ sevenbit(name);
+ rval = skeylookup(mp,name);
+ strcpy(prompt,"s/key 55 latour1\n");
+ switch(rval){
+ case -1: /* File error */
+ return -1;
+ case 0: /* Lookup succeeded, return challenge */
+ sprintf(prompt,"s/key %d %s\n",mp->n - 1,mp->seed);
+ return 0;
+ case 1: /* User not found */
+ fclose(mp->keyfile);
+ return -1;
+ }
+ return -1; /* Can't happen */
+}
+/* Return a skey challenge string for user 'name'. If successful,
+ * fill in the caller's skey structure and return 0. If unsuccessful
+ * (e.g., if name is unknown) return -1.
+ *
+ * The file read/write pointer is left at the start of the
+ * record.
+ */
+int
+skeychallenge(mp,name, ss)
+struct skey *mp;
+char *name;
+char *ss;
+{
+ int rval;
+
+ rval = skeylookup(mp,name);
+ switch(rval){
+ case -1: /* File error */
+ return -1;
+ case 0: /* Lookup succeeded, issue challenge */
+ sprintf(ss, "s/key %d %s",mp->n - 1,mp->seed);
+ return 0;
+ case 1: /* User not found */
+ fclose(mp->keyfile);
+ return -1;
+ }
+ return -1; /* Can't happen */
+}
+
+/* Find an entry in the One-time Password database.
+ * Return codes:
+ * -1: error in opening database
+ * 0: entry found, file R/W pointer positioned at beginning of record
+ * 1: entry not found, file R/W pointer positioned at EOF
+ */
+int
+skeylookup(mp,name)
+struct skey *mp;
+char *name;
+{
+ int found;
+ int len;
+ long recstart;
+ char *cp;
+ struct stat statbuf;
+
+ /* See if the KEYFILE exists, and create it if not */
+ if(stat(KEYFILE,&statbuf) == -1 && errno == ENOENT){
+ mp->keyfile = fopen(KEYFILE,"w+");
+ (void) chmod(KEYFILE, 0644);
+ } else {
+ /* Otherwise open normally for update */
+ mp->keyfile = fopen(KEYFILE,"r+");
+ }
+ if(mp->keyfile == NULL)
+ return -1;
+
+ /* Look up user name in database */
+ len = strlen(name);
+ if( len > 8 ) len = 8; /* Added 8/2/91 - nmh */
+ found = 0;
+ while(!feof(mp->keyfile)){
+ recstart = ftell(mp->keyfile);
+ mp->recstart = recstart;
+ if(fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
+ break;
+ }
+ rip(mp->buf);
+ if(mp->buf[0] == '#')
+ continue; /* Comment */
+ if((mp->logname = strtok(mp->buf," \t")) == NULL)
+ continue;
+ if((cp = strtok(NULL," \t")) == NULL)
+ continue;
+ mp->n = atoi(cp);
+ if((mp->seed = strtok(NULL," \t")) == NULL)
+ continue;
+ if((mp->val = strtok(NULL," \t")) == NULL)
+ continue;
+ if(strlen(mp->logname) == len
+ && strncmp(mp->logname,name,len) == 0){
+ found = 1;
+ break;
+ }
+ }
+ if(found){
+ fseek(mp->keyfile,recstart,0);
+ return 0;
+ } else
+ return 1;
+}
+/* Verify response to a s/key challenge.
+ *
+ * Return codes:
+ * -1: Error of some sort; database unchanged
+ * 0: Verify successful, database updated
+ * 1: Verify failed, database unchanged
+ *
+ * The database file is always closed by this call.
+ */
+int
+skeyverify(mp,response)
+struct skey *mp;
+char *response;
+{
+ struct timeval startval;
+ struct timeval endval;
+long microsec;
+ char key[8];
+ char fkey[8];
+ char filekey[8];
+ time_t now;
+ struct tm *tm;
+ char tbuf[27],buf[60];
+ char me[80];
+ int rval;
+ char *cp;
+
+ time(&now);
+ tm = localtime(&now);
+ strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm);
+
+ if(response == NULL){
+ fclose(mp->keyfile);
+ return -1;
+ }
+ rip(response);
+
+ /* Convert response to binary */
+ if(etob(key,response) != 1 && atob8(key,response) != 0){
+ /* Neither english words or ascii hex */
+ fclose(mp->keyfile);
+ return -1;
+ }
+
+ /* Compute fkey = f(key) */
+ memcpy(fkey,key,sizeof(key));
+ f(fkey);
+ /* in order to make the window of update as short as possible
+ we must do the comparison here and if OK write it back
+ other wise the same password can be used twice to get in
+ to the system
+ */
+
+ setpriority(PRIO_PROCESS, 0, -4);
+/*
+ gettimeofday(&startval, (char *)0 );
+*/
+
+ /* reread the file record NOW*/
+
+ fseek(mp->keyfile,mp->recstart,0);
+ if(fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
+ setpriority(PRIO_PROCESS, 0, 0);
+ fclose(mp->keyfile);
+ return -1;
+ }
+ rip(mp->buf);
+ mp->logname = strtok(mp->buf," \t");
+ cp = strtok(NULL," \t") ;
+ mp->seed = strtok(NULL," \t");
+ mp->val = strtok(NULL," \t");
+ /* And convert file value to hex for comparison */
+ atob8(filekey,mp->val);
+
+ /* Do actual comparison */
+ if(memcmp(filekey,fkey,8) != 0){
+ /* Wrong response */
+ setpriority(PRIO_PROCESS, 0, 0);
+ fclose(mp->keyfile);
+ return 1;
+ }
+
+ /* Update key in database by overwriting entire record. Note
+ * that we must write exactly the same number of bytes as in
+ * the original record (note fixed width field for N)
+ */
+ btoa8(mp->val,key);
+ mp->n--;
+ fseek(mp->keyfile,mp->recstart,0);
+ fprintf(mp->keyfile,"%s %04d %-16s %s %-21s\n",mp->logname,mp->n,mp->seed,
+ mp->val, tbuf);
+/*
+gettimeofday(&endval, (char *)0 );
+ microsec = (endval.tv_sec - startval.tv_sec) * 1000000 + (endval.tv_usec - startval.tv_usec);
+fprintf(stderr, "window= %d micro seconds \n" , microsec);
+*/
+
+
+ fclose(mp->keyfile);
+
+ setpriority(PRIO_PROCESS, 0, 0);
+ return 0;
+}
+
+
+/* Convert 8-byte hex-ascii string to binary array
+ * Returns 0 on success, -1 on error
+ */
+atob8(out,in)
+register char *out,*in;
+{
+ register int i;
+ register int val;
+
+ if(in == NULL || out == NULL)
+ return -1;
+
+ for(i=0;i<8;i++){
+ if((in = skipspace(in)) == NULL)
+ return -1;
+ if((val = htoi(*in++)) == -1)
+ return -1;
+ *out = val << 4;
+
+ if((in = skipspace(in)) == NULL)
+ return -1;
+ if((val = htoi(*in++)) == -1)
+ return -1;
+ *out++ |= val;
+ }
+ return 0;
+}
+
+char *
+skipspace(cp)
+register char *cp;
+{
+ while(*cp == ' ' || *cp == '\t')
+ cp++;
+
+ if(*cp == '\0')
+ return NULL;
+ else
+ return cp;
+}
+
+/* Convert 8-byte binary array to hex-ascii string */
+int
+btoa8(out,in)
+register char *out,*in;
+{
+ register int i;
+
+ if(in == NULL || out == NULL)
+ return -1;
+
+ for(i=0;i<8;i++){
+ sprintf(out,"%02x",*in++ & 0xff);
+ out += 2;
+ }
+ return 0;
+}
+
+
+/* Convert hex digit to binary integer */
+int
+htoi(c)
+register char c;
+{
+ if('0' <= c && c <= '9')
+ return c - '0';
+ if('a' <= c && c <= 'f')
+ return 10 + c - 'a';
+ if('A' <= c && c <= 'F')
+ return 10 + c - 'A';
+ return -1;
+}
diff --git a/lib/libskey/skeysubr.c b/lib/libskey/skeysubr.c
new file mode 100644
index 000000000000..9762f1a6c95a
--- /dev/null
+++ b/lib/libskey/skeysubr.c
@@ -0,0 +1,225 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef __MSDOS__
+#include <dos.h>
+#endif
+#ifdef unix /* Assume POSIX */
+#include <fcntl.h>
+#include <termios.h>
+#endif
+#include "md4.h"
+#include "skey.h"
+
+#if (defined(__MSDOS__) || defined(MPU8086) || defined(MPU8080) \
+ || defined(vax) || defined (MIPSEL))
+#define LITTLE_ENDIAN /* Low order bytes are first in memory */
+#endif /* Almost all other machines are big-endian */
+
+/* Crunch a key:
+ * concatenate the seed and the password, run through MD4 and
+ * collapse to 64 bits. This is defined as the user's starting key.
+ */
+int
+keycrunch(result,seed,passwd)
+char *result; /* 8-byte result */
+char *seed; /* Seed, any length */
+char *passwd; /* Password, any length */
+{
+ char *buf;
+ MDstruct md;
+ unsigned int buflen;
+#ifndef LITTLE_ENDIAN
+ int i;
+ register long tmp;
+#endif
+
+ buflen = strlen(seed) + strlen(passwd);
+ if((buf = malloc(buflen+1)) == NULL)
+ return -1;
+ strcpy(buf,seed);
+ strcat(buf,passwd);
+
+ /* Crunch the key through MD4 */
+ sevenbit(buf);
+ MDbegin(&md);
+ MDupdate(&md,(unsigned char *)buf,8*buflen);
+
+ free(buf);
+
+ /* Fold result from 128 to 64 bits */
+ md.buffer[0] ^= md.buffer[2];
+ md.buffer[1] ^= md.buffer[3];
+
+#ifdef LITTLE_ENDIAN
+ /* Only works on byte-addressed little-endian machines!! */
+ memcpy(result,(char *)md.buffer,8);
+#else
+ /* Default (but slow) code that will convert to
+ * little-endian byte ordering on any machine
+ */
+ for(i=0;i<2;i++){
+ tmp = md.buffer[i];
+ *result++ = tmp;
+ tmp >>= 8;
+ *result++ = tmp;
+ tmp >>= 8;
+ *result++ = tmp;
+ tmp >>= 8;
+ *result++ = tmp;
+ }
+#endif
+
+ return 0;
+}
+
+/* The one-way function f(). Takes 8 bytes and returns 8 bytes in place */
+void
+f(x)
+char *x;
+{
+ MDstruct md;
+#ifndef LITTLE_ENDIAN
+ register long tmp;
+#endif
+
+ MDbegin(&md);
+ MDupdate(&md,(unsigned char *)x,64);
+
+ /* Fold 128 to 64 bits */
+ md.buffer[0] ^= md.buffer[2];
+ md.buffer[1] ^= md.buffer[3];
+
+#ifdef LITTLE_ENDIAN
+ /* Only works on byte-addressed little-endian machines!! */
+ memcpy(x,(char *)md.buffer,8);
+
+#else
+ /* Default (but slow) code that will convert to
+ * little-endian byte ordering on any machine
+ */
+ tmp = md.buffer[0];
+ *x++ = tmp;
+ tmp >>= 8;
+ *x++ = tmp;
+ tmp >>= 8;
+ *x++ = tmp;
+ tmp >>= 8;
+ *x++ = tmp;
+
+ tmp = md.buffer[1];
+ *x++ = tmp;
+ tmp >>= 8;
+ *x++ = tmp;
+ tmp >>= 8;
+ *x++ = tmp;
+ tmp >>= 8;
+ *x = tmp;
+#endif
+}
+
+/* Strip trailing cr/lf from a line of text */
+void
+rip(buf)
+char *buf;
+{
+ char *cp;
+
+ if((cp = strchr(buf,'\r')) != NULL)
+ *cp = '\0';
+
+ if((cp = strchr(buf,'\n')) != NULL)
+ *cp = '\0';
+}
+/************************/
+#ifdef __MSDOS__
+char *
+readpass(buf,n)
+char *buf;
+int n;
+{
+ int i;
+ char *cp;
+
+ for(cp=buf,i = 0; i < n ; i++)
+ if ((*cp++ = bdos(7,0,0)) == '\r')
+ break;
+ *cp = '\0';
+ printf("\n");
+ rip(buf);
+ return buf;
+}
+#else
+char *
+readpass(buf,n)
+char *buf;
+int n;
+{
+ struct termios saved_ttymode;
+ struct termios noecho_ttymode;
+
+ /* Save normal line editing modes */
+ tcgetattr(0, &saved_ttymode);
+
+ /* Turn off echoing */
+ tcgetattr(0, &noecho_ttymode);
+ noecho_ttymode.c_lflag &= ~ECHO;
+ tcsetattr(0, TCSANOW, &noecho_ttymode);
+ fgets(buf,n,stdin);
+ rip(buf);
+
+ /* Restore previous tty modes */
+ tcsetattr(0, TCSANOW, &saved_ttymode);
+
+ /*
+ after the secret key is taken from the keyboard, the line feed is
+ written to standard error instead of standard output. That means that
+ anyone using the program from a terminal won't notice, but capturing
+ standard output will get the key words without a newline in front of
+ them.
+ */
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ sevenbit(buf);
+
+ return buf;
+}
+
+#endif
+
+/* removebackspaced over charaters from the string*/
+backspace(buf)
+char *buf;
+{
+ char bs = 0x8;
+ char *cp = buf;
+ char *out = buf;
+
+ while(*cp){
+ if( *cp == bs ) {
+ if(out == buf){
+ cp++;
+ continue;
+ }
+ else {
+ cp++;
+ out--;
+ }
+ }
+ else {
+ *out++ = *cp++;
+ }
+
+ }
+ *out = '\0';
+
+}
+sevenbit(s)
+char *s;
+{
+ /* make sure there are only 7 bit code in the line*/
+ while(*s){
+ *s = 0x7f & ( *s);
+ s++;
+ }
+}
diff --git a/usr.bin/key/Makefile b/usr.bin/key/Makefile
new file mode 100644
index 000000000000..b8553abe3879
--- /dev/null
+++ b/usr.bin/key/Makefile
@@ -0,0 +1,21 @@
+
+# @(#)Makefile 5.6 (Berkeley) 3/5/91
+#
+
+PROG= key
+MAN1= key.1 skey.1
+CFLAGS+=-I${.CURDIR}/../../lib
+
+
+DPADD= /usr/bin/libskey.a
+LDADD= -lskey
+
+.if exists(/usr/lib/libcrypt.a)
+DPADD+= ${LIBCRYPT}
+LDADD+= -lcrypt
+.endif
+
+SRCS= skey.c
+
+.include <bsd.prog.mk>
+
diff --git a/usr.bin/key/README.WZV b/usr.bin/key/README.WZV
new file mode 100644
index 000000000000..a13f3b5824c6
--- /dev/null
+++ b/usr.bin/key/README.WZV
@@ -0,0 +1,100 @@
+One of the nice things of S/Key is that it still leaves you the option
+to use regular UNIX passwords. In fact, the presence of S/Key support
+is completely invisible for a user until she has set up a password with
+the keyinit command. You can permit regular UNIX passwords for local
+logins, while at the same time insisting on S/Key passwords for logins
+from outside.
+
+ORIGIN
+
+These files are modified versions of the s/key files found on
+thumper.bellcore.com at 21 oct 1993. They have been fixed to
+run on top of SunOS 4.1.3 and Solaris 2.3.
+
+Installation is described at the end of this file.
+
+USAGE
+
+Use the keyinit command to set up a new series of s/key passwords.
+
+ wzv_6% keyinit
+ Updating wietse:
+ Old key: wz173500
+ Reminder - Only use this method if you are direct connected.
+ If you are using telnet or dial-in exit with no password and use keyinit -s.
+ Enter secret password:
+ Again secret password:
+
+ ID wietse s/key is 99 wz173501
+ BLAH BLA BLAH BLAH BLAH BLA
+
+Be sure to make your secret password sufficiently long. Try using a
+full sentence instead of just one single word.
+
+You will have to do a "keyinit" on every system that you want to login
+on using one-time passwords.
+
+Whenever you log into an s/key protected system you will see
+something like:
+
+ login: wietse
+ s/key 98 wz173501
+ Password:
+
+In this case you can either enter your regular UNIX password or
+your one-time s/key password. For example, I open a local window
+to compute the password:
+
+ local% key 98 wz173501
+ Reminder - Do not use key while logged in via telnet or rlogin.
+ Enter secret password:
+ BLAH BLA BLAH BLAH BLAH BLA
+
+The "BLAH BLA BLAH BLAH BLAH BLA" is the one-time s/key password.
+
+If you have to type the one-time password in by hand, it is convenient
+to have echo turned on so that you can correct typing errors. Just type
+a newline at the "Password:" prompt:
+
+ login: wietse
+ s/key 98 wz173501
+ Password: (turning echo on)
+ Password:BLAH BLA BLAH BLAH BLAH BLA
+
+The 98 in the challenge will be 97 the next time, and so on. You'll get
+a warning when you are about to run out of s/key passwords, so that you
+will have to run the keyinit command again.
+
+Sometimes it is more practical to carry a piece of paper with a small
+series of one-time passwords. You can generate the list with:
+
+ % key -n 10 98 wz173501
+ 98: BLAH BLA BLAH BLAH BLAH BLA
+ 97: ...
+ 96: ...
+
+Be careful when printing material like this!
+
+INSTALLATION
+
+To install, do: make sunos4 (or whatever), then: make install.
+
+The UNIX password is always permitted with non-network logins. By
+default, UNIX passwords are always permitted (the Bellcore code by
+default disallows UNIX passwords but I think that is too painful). In
+order to permit UNIX passwords only with logins from specific networks,
+create a file /etc/skey.access. For example,
+
+ # First word says if UNIX passwords are to be permitted or denied.
+ # remainder of the rule is a networknumber and mask. A rule matches a
+ # host if any of its addresses satisfies:
+ #
+ # network = (address & mask)
+ #
+ #what network mask
+ permit 131.155.210.0 255.255.255.0
+ deny 0.0.0.0 0.0.0.0
+
+This particular example will permit UNIX passwords with logins from any
+host on network 131.155.210, but will insist on one-time passwords in
+all other cases.
diff --git a/usr.bin/key/key.1 b/usr.bin/key/key.1
new file mode 100644
index 000000000000..d9da463712e5
--- /dev/null
+++ b/usr.bin/key/key.1
@@ -0,0 +1,49 @@
+.ll 6i
+.pl 10.5i
+.\" @(#)key.1 1.0 (Bellcore) 12/2/91
+.\"
+.lt 6.0i
+.TH KEY 1 "2 December 1991"
+.AT 3
+.SH NAME
+key \- Stand\-alone program for computing responses to S/Key challenges.
+.SH SYNOPSIS
+.B key [\-n <count>] <Sequence> <key>
+.SH DESCRIPTION
+.I key
+Takes the optional count of the number of one time access
+passwords to print
+along with a (maximum) sequence number and key as command line args,
+it prompts for the user's secret password, and produces both word
+and hex format responses.
+.SH EXAMPLE
+.sh
+ Usage example:
+.sp 0
+ >key \-n 5 99 th91334
+.sp 0
+ Enter password: <your secret password is entered here>
+.sp 0
+ OMEN US HORN OMIT BACK AHOY
+.sp 0
+ .... 4 more passwords.
+.sp 0
+ >
+.LP
+.SH OPTIONS
+.LP
+.B \-n <count>
+the number of one time access passwords to print.
+The default is one.
+.SH DIAGNOSTICS
+.SH BUGS
+.LP
+.SH SEE ALSO
+.BR skey(1),
+.BR keyinit(1),
+.BR keysu(1),
+.BR keyinfo(1)
+.SH AUTHOR
+Command by Phil Karn, Neil M. Haller, John S. Walden
+.SH CONTACT
+staff@thumper.bellcore.com
diff --git a/usr.bin/key/skey.1 b/usr.bin/key/skey.1
new file mode 100644
index 000000000000..0a8b1b668a9e
--- /dev/null
+++ b/usr.bin/key/skey.1
@@ -0,0 +1,59 @@
+.ll 6i
+.pl 10.5i
+.\" @(#)skey.1 1.1 10/28/93
+.\"
+.lt 6.0i
+.TH KEY 1 "28 October 1993"
+.AT 3
+.SH NAME
+S/key \- A proceedure to use one time passwords for accessing computer systems.
+.SH DESCRIPTION
+.I S/key
+is a proceedure for using one time password to authenticate access to
+compter systems. It uses 64 bits of information transformed by the
+MD4 algorithm. The user supplies the 64 bits in the form of 6 English
+words that are generated by a secure computer.
+Example use of the S/key program
+.I key
+.sp
+ Usage example:
+.sp 0
+ >key 99 th91334
+.sp 0
+ Enter password: <your secret password is intered here>
+.sp 0
+ OMEN US HORN OMIT BACK AHOY
+.sp 0
+ >
+.sp
+The programs that are part of the S/Key system are keyinit, key, and
+keyinfo. Keyinit is used to get your ID set up, key is
+used to get the one time password each time,
+keyinfo is used to extract information from the S/Key database.
+.sp
+When you run "keyinit" you inform the system of your
+secret password. Running "key" then generates the
+one-time passwords, and also requires your secret
+password. If however, you misspell your password
+while running "key", you will get a list of passwords
+that will not work, and no indication about the problem.
+.sp
+Password sequence numbers count backward from 99. If you
+don't know this, the syntax for "key" will be confusing.
+.sp
+You can enter the passwords using small letters, even
+though the "key" program gives them in caps.
+.sp
+Macintosh and a general purpose PC use
+are available.
+.sp
+Under FreeBSD, you can control, with /etc/skey.access, from which
+hosts and/or networks the use of S/Key passwords is obligated.
+.LP
+.SH SEE ALSO
+.BR keyinit(1),
+.BR key(1),
+.BR keyinfo(1)
+.BR skey.access(5)
+.SH AUTHOR
+Phil Karn, Neil M. Haller, John S. Walden, Scott Chasin
diff --git a/usr.bin/key/skey.c b/usr.bin/key/skey.c
new file mode 100644
index 000000000000..e0253120252a
--- /dev/null
+++ b/usr.bin/key/skey.c
@@ -0,0 +1,128 @@
+/* Stand-alone program for computing responses to S/Key challenges.
+ * Takes the iteration count and seed as command line args, prompts
+ * for the user's key, and produces both word and hex format responses.
+ *
+ * Usage example:
+ * >skey 88 ka9q2
+ * Enter password:
+ * OMEN US HORN OMIT BACK AHOY
+ * C848 666B 6435 0A93
+ * >
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef __MSDOS__
+#include <dos.h>
+#else /* Assume BSD unix */
+#include <fcntl.h>
+#endif
+#include "libskey/md4.h"
+#include "libskey/skey.h"
+
+char *readpass();
+void usage();
+int getopt();
+extern int optind;
+extern char *optarg;
+
+int
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ int n,cnt,i;
+ char passwd[256],passwd2[256];
+ char key[8];
+ char *seed;
+ char buf[33];
+ char *slash;
+
+ cnt = 1;
+ while((i = getopt(argc,argv,"n:")) != EOF){
+ switch(i){
+ case 'n':
+ cnt = atoi(optarg);
+ break;
+ }
+ }
+ /* could be in the form <number>/<seed> */
+ if(argc <= optind + 1){
+ /*look for / in it */
+ if(argc <= optind){
+ usage(argv[0]);
+ return 1;
+ }
+
+ slash = strchr(argv[optind], '/');
+ if(slash == NULL){
+ usage(argv[0]);
+ return 1;
+ }
+ *slash++ = '\0';
+ seed = slash;
+
+ if((n = atoi(argv[optind])) < 0){
+ fprintf(stderr,"%s not positive\n",argv[optind]);
+ usage(argv[0]);
+ return 1;
+ }
+ }
+ else {
+
+ if((n = atoi(argv[optind])) < 0){
+ fprintf(stderr,"%s not positive\n",argv[optind]);
+ usage(argv[0]);
+ return 1;
+ }
+ seed = argv[++optind];
+ }
+ fprintf(stderr,"Reminder - Do not use this program while logged in via telnet or rlogin.\n");
+
+ /* Get user's secret password */
+ for(;;){
+ fprintf(stderr,"Enter secret password: ");
+ readpass(passwd,sizeof(passwd));
+ break;
+ /************
+ fprintf(stderr,"Again secret password: ");
+ readpass(passwd2,sizeof(passwd));
+ if(strcmp(passwd,passwd2) == 0) break;
+ fprintf(stderr, "Sorry no match\n");
+ **************/
+
+ }
+
+ /* Crunch seed and password into starting key */
+ if(keycrunch(key,seed,passwd) != 0){
+ fprintf(stderr,"%s: key crunch failed\n",argv[0]);
+ return 1;
+ }
+ if(cnt == 1){
+ while(n-- != 0)
+ f(key);
+ printf("%s\n",btoe(buf,key));
+#ifdef HEXIN
+ printf("%s\n",put8(buf,key));
+#endif
+ } else {
+ for(i=0;i<=n-cnt;i++)
+ f(key);
+ for(;i<=n;i++){
+#ifdef HEXIN
+ printf("%d: %-29s %s\n",i,btoe(buf,key),put8(buf,key));
+#else
+ printf("%d: %-29s\n",i,btoe(buf,key));
+#endif
+ f(key);
+ }
+ }
+ return 0;
+}
+void
+usage(s)
+char *s;
+{
+ fprintf(stderr,"Usage: %s [-n count] <sequence #>[/] <key> \n",s);
+}
+
diff --git a/usr.bin/keyinfo/Makefile b/usr.bin/keyinfo/Makefile
new file mode 100644
index 000000000000..41baee6e1d5f
--- /dev/null
+++ b/usr.bin/keyinfo/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 5.5 (Berkeley) 7/1/90
+
+MAN1= keyinfo.1
+
+beforeinstall:
+ install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/keyinfo.sh ${DESTDIR}${BINDIR}/keyinfo
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/keyinfo/keyinfo.1 b/usr.bin/keyinfo/keyinfo.1
new file mode 100644
index 000000000000..b12aa962b990
--- /dev/null
+++ b/usr.bin/keyinfo/keyinfo.1
@@ -0,0 +1,40 @@
+.ll 6i
+.pl 10.5i
+.\" @(#)keyinfo.1 1.1 (Bellcore) 7/20/93
+.\"
+.lt 6.0i
+.TH KEYINFO 1 "20 July 1993"
+.AT 3
+.SH NAME
+keyinfo \- display current S/Key sequence number and seed
+.SH SYNOPSIS
+.B keyinfo [username]
+.SH DESCRIPTION
+.I keyinfo
+takes an optional user name and displays the user\'s current sequence
+number and seed found in the S/Key database /etc/skeykeys.
+.sp 1
+The command can be useful when generating a list of passwords for use
+on a field trip, by combining with the command
+.I key
+in the form:
+.sp
+ >key \-n <number of passwords to print> `keyinfo`|lpr
+.SH EXAMPLE
+.sh
+Usage example:
+.sp 0
+ >keyinfo
+.sp 0
+ 0098 ws91340
+.LP
+.SH ARGUMENTS
+.TP
+.B username
+The S/key user to display the information for. The default is
+to display S/Key information on the user who invokes the command.
+.SH SEE ALSO
+.BR keyinit(1),
+.BR key(1)
+.SH AUTHOR
+Command by Phil Karn, Neil M. Haller, John S. Walden
diff --git a/usr.bin/keyinfo/keyinfo.sh b/usr.bin/keyinfo/keyinfo.sh
new file mode 100644
index 000000000000..5879442db188
--- /dev/null
+++ b/usr.bin/keyinfo/keyinfo.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+# search /etc/skeykeys for the skey string for this user OR user specified
+# in 1st parameter
+
+PATH=/bin:/usr/bin
+
+test -f /etc/skeykeys && {
+ WHO=${1-`id | sed 's/^[^(]*(\([^)]*\).*/\1/'`}
+ awk '/^'${WHO}'[ ]/ { print $2-1, $3 }' /etc/skeykeys
+}
diff --git a/usr.bin/keyinit/Makefile b/usr.bin/keyinit/Makefile
new file mode 100644
index 000000000000..4c44d30f2173
--- /dev/null
+++ b/usr.bin/keyinit/Makefile
@@ -0,0 +1,21 @@
+
+# @(#)Makefile 5.6 (Berkeley) 3/5/91
+#
+
+PROG= keyinit
+MAN1= keyinit.1
+CFLAGS+=-I${.CURDIR}/../../lib
+DPADD= /usr/bin/libskey.a
+LDADD= -lskey
+
+.if exists(/usr/lib/libcrypt.a)
+DPADD+= ${LIBCRYPT}
+LDADD+= -lcrypt
+.endif
+
+SRCS= skeyinit.c
+
+BINOWN= root
+BINMODE=4555
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/keyinit/keyinit.1 b/usr.bin/keyinit/keyinit.1
new file mode 100644
index 000000000000..2fe2d03944db
--- /dev/null
+++ b/usr.bin/keyinit/keyinit.1
@@ -0,0 +1,64 @@
+.ll 6i
+.pl 10.5i
+.\" @(#)keyinit.1 1.0 (Bellcore) 7/20/93
+.\"
+.lt 6.0i
+.TH KEYINIT 1 "20 July 1993"
+.AT 3
+.SH NAME
+keyinit \- Change password or add user to S/Key authentication system.
+.SH SYNOPSIS
+.B keyinit [\-s] [<user ID >]
+.SH DESCRIPTION
+.I keyinit
+initializes the system so you can use S/Key one-time passwords to
+login. The program will ask you to enter a secret pass phrase; enter a
+phrase of several words in response. After the S/Key database has been
+updated you can login using either your regular UNIX password or using
+S/Key one-time passwords.
+.PP
+When logging in from another machine you can avoid typing a real
+password over the network, by typing your S/Key pass phrase to the
+\fIkey\fR command on the local machine: the program will respond with
+the one-time password that you should use to log into the remote
+machine. This is most conveniently done with cut-and-paste operations
+using a mouse. Alternatively, you can pre-compute one-time passwords
+using the \fIkey\fR command and carry them with you on a piece of paper.
+.PP
+\fIkeyinit\fR requires you to type your secret password, so it should
+be used only on a secure terminal. For example, on the console of a
+workstation. If you are using \fIkeyinit\fR while logged in over an
+untrusted network, follow the instructions given below with the \-s
+option.
+.SH OPTIONS
+.IP \-s
+Set secure mode where the user is expected to have used a secure
+machine to generate the first one time password. Without the \-s the
+system will assume you are direct connected over secure communications
+and prompt you for your secret password.
+The \-s option also allows one to set the seed and count for complete
+control of the parameters. You can use keyinit -s in compination with
+the
+.I key
+command to set the seed and count if you do not like the defaults.
+To do this run keyinit in one window and put in your count and seed
+then run key in another window to generate the correct 6 english words
+for that count and seed. You can then
+"cut" and "paste" them or copy them into the keyinit window.
+.sp
+.LP
+.B <user ID>
+the ID for the user to be changed/added
+.SH DIAGNOSTICS
+.SH FILES
+.TP
+/etc/skeykeys data base of information for S/Key system.
+.SH BUGS
+.LP
+.SH SEE ALSO
+.BR skey(1),
+.BR key(1),
+.BR keysu(1),
+.BR keyinfo(1)
+.SH AUTHOR
+Command by Phil Karn, Neil M. Haller, John S. Walden
diff --git a/usr.bin/keyinit/skeyinit.c b/usr.bin/keyinit/skeyinit.c
new file mode 100644
index 000000000000..d13bd6b0ba93
--- /dev/null
+++ b/usr.bin/keyinit/skeyinit.c
@@ -0,0 +1,195 @@
+/* change password or add user to S/KEY authentication system.
+ * S/KEY is a tradmark of Bellcore */
+
+#include <stdio.h>
+#include <string.h>
+#include <pwd.h>
+#include "libskey/skey.h"
+#include <stdio.h>
+#include <time.h>
+
+extern int optind;
+extern char *optarg;
+
+char * readpass();
+
+int skeylookup __ARGS((struct skey *mp,char *name));
+
+#define NAMELEN 2
+int
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ struct skey skey;
+ int rval,n,nn,i,defaultsetup;
+ char seed[18],tmp[80],key[8];
+ struct passwd *ppuser,*pp;
+ char defaultseed[17], passwd[256],passwd2[256] ;
+
+
+ time_t now;
+ struct tm *tm;
+ char tbuf[27],buf[60];
+ char lastc, me[80];
+ int l;
+
+ time(&now);
+#if 0 /* Choose a more random seed */
+ tm = localtime(&now);
+ strftime(tbuf, sizeof(tbuf), "%M%j", tm);
+#else
+ sprintf(tbuf, "%05ld", (long) (now % 100000));
+#endif
+ gethostname(defaultseed,NAMELEN);
+ strcpy(&defaultseed[NAMELEN],tbuf);
+
+ pp = ppuser = getpwuid(getuid());
+ strcpy(me,pp->pw_name);
+ defaultsetup = 1;
+ if( argc > 1){
+ if(strcmp("-s", argv[1]) == 0)
+ defaultsetup = 0;
+ else
+ pp = getpwnam(argv[1]);
+ if(argc > 2)
+ pp = getpwnam(argv[2]);
+
+ }
+ if(pp == NULL){
+ printf("User unknown\n");
+ return 1;
+ }
+ if(strcmp( pp->pw_name,me) != 0){
+ if(getuid() != 0){
+ /* Only root can change other's passwds */
+ printf("Permission denied.\n");
+ return(1);
+ }
+ }
+
+
+
+ rval = skeylookup(&skey,pp->pw_name);
+ switch(rval){
+ case -1:
+ perror("error in opening database");
+ return 1;
+ case 0:
+ printf("Updating %s:\n",pp->pw_name);
+ printf("Old key: %s\n",skey.seed);
+ /* lets be nice if they have a skey.seed that ends in 0-8 just add one*/
+ l = strlen(skey.seed);
+ if( l > 0){
+ lastc = skey.seed[l-1];
+ if( isdigit(lastc) && lastc != '9' ){
+ strcpy(defaultseed, skey.seed);
+ defaultseed[l-1] = lastc + 1;
+ }
+ if( isdigit(lastc) && lastc == '9' && l < 16){
+ strcpy(defaultseed, skey.seed);
+ defaultseed[l-1] = '0';
+ defaultseed[l] = '0';
+ defaultseed[l+1] = '\0';
+ }
+ }
+ break;
+ case 1:
+ skey.val = 0; /* XXX */
+ printf("Adding %s:\n",pp->pw_name);
+ break;
+ }
+ n = 99;
+ if( ! defaultsetup){
+ printf("Reminder you need the 6 english words from the skey command.\n");
+ for(i=0;;i++){
+ if(i >= 2) exit(1);
+ printf("Enter sequence count from 1 to 10000: ");
+ fgets(tmp,sizeof(tmp),stdin);
+ n = atoi(tmp);
+ if(n > 0 && n < 10000)
+ break; /* Valid range */
+ printf("Count must be > 0 and < 10000\n");
+ }
+ }
+ if( !defaultsetup){
+ printf("Enter new key [default %s]: ", defaultseed);
+ fflush(stdout);
+ fgets(seed,sizeof(seed),stdin);
+ rip(seed);
+ if(strlen(seed) > 16){
+ printf("Seed truncated to 16 chars\n");
+ seed[16] = '\0';
+ }
+ if( seed[0] == '\0') strcpy(seed,defaultseed);
+ for(i=0;;i++){
+ if(i >= 2) exit(1);
+ printf("s/key %d %s\ns/key access password: ",n,seed);
+ fgets(tmp,sizeof(tmp),stdin);
+ rip(tmp);
+ backspace(tmp);
+ if(tmp[0] == '?'){
+ printf("Enter 6 English words from secure S/Key calculation.\n");
+ continue;
+ }
+ if(tmp[0] == '\0'){
+ exit(1);
+ }
+ if(etob(key,tmp) == 1 || atob8(key,tmp) == 0)
+ break; /* Valid format */
+ printf("Invalid format, try again with 6 English words.\n");
+ }
+ } else {
+ /* Get user's secret password */
+ fprintf(stderr,"Reminder - Only use this method if you are directly connected.\n");
+ fprintf(stderr,"If you are using telnet or rlogin exit with no password and use keyinit -s.\n");
+ for(i=0;;i++){
+ if(i >= 2) exit(1);
+ fprintf(stderr,"Enter secret password: ");
+ readpass(passwd,sizeof(passwd));
+ if(passwd[0] == '\0'){
+ exit(1);
+ }
+ fprintf(stderr,"Again secret password: ");
+ readpass(passwd2,sizeof(passwd));
+ if(passwd2[0] == '\0'){
+ exit(1);
+ }
+ if(strlen(passwd) < 4 && strlen(passwd2) < 4) {
+ fprintf(stderr, "Sorry your password must be longer\n\r");
+ exit(1);
+ }
+ if(strcmp(passwd,passwd2) == 0) break;
+ fprintf(stderr, "Sorry no match\n");
+
+
+ }
+ strcpy(seed,defaultseed);
+
+ /* Crunch seed and password into starting key */
+ if(keycrunch(key,seed,passwd) != 0){
+ fprintf(stderr,"%s: key crunch failed\n",argv[0]);
+ return 1;
+ }
+ nn = n;
+ while(nn-- != 0)
+ f(key);
+ }
+ time(&now);
+ tm = localtime(&now);
+ strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm);
+ if (skey.val == NULL)
+ skey.val = (char *) malloc(16+1);
+
+
+ btoa8(skey.val,key);
+ fprintf(skey.keyfile,"%s %04d %-16s %s %-21s\n",pp->pw_name,n,
+ seed,skey.val, tbuf);
+ fclose(skey.keyfile);
+ printf("\nID %s s/key is %d %s\n",pp->pw_name,n,seed);
+ printf("%s\n",btoe(buf,key));
+#ifdef HEXIN
+ printf("%s\n",put8(buf,key));
+#endif
+ return 0;
+}