Home | History | Annotate | Line # | Download | only in libskey
      1  1.19     perry /*	$NetBSD: skeylogin.c,v 1.19 2005/02/04 16:13:14 perry Exp $	*/
      2   1.6   thorpej 
      3   1.1   deraadt /* S/KEY v1.1b (skeylogin.c)
      4   1.1   deraadt  *
      5   1.1   deraadt  * Authors:
      6   1.1   deraadt  *          Neil M. Haller <nmh (at) thumper.bellcore.com>
      7   1.1   deraadt  *          Philip R. Karn <karn (at) chicago.qualcomm.com>
      8   1.1   deraadt  *          John S. Walden <jsw (at) thumper.bellcore.com>
      9   1.1   deraadt  *          Scott Chasin <chasin (at) crimelab.com>
     10   1.1   deraadt  *
     11  1.14       mjl  * Modifications:
     12  1.14       mjl  *          Todd C. Miller <Todd.Miller (at) courtesan.com>
     13  1.14       mjl  *          Angelos D. Keromytis <adk (at) adk.gr>
     14  1.14       mjl  *
     15   1.1   deraadt  * S/KEY verification check, lookups, and authentication.
     16   1.1   deraadt  */
     17  1.18     lukem 
     18  1.18     lukem #include <sys/cdefs.h>
     19  1.19     perry __RCSID("$NetBSD: skeylogin.c,v 1.19 2005/02/04 16:13:14 perry Exp $");
     20   1.1   deraadt 
     21   1.1   deraadt #include <sys/param.h>
     22   1.1   deraadt #include <sys/stat.h>
     23   1.1   deraadt #include <sys/time.h>
     24   1.1   deraadt #include <sys/resource.h>
     25  1.14       mjl #include <sys/types.h>
     26   1.1   deraadt 
     27  1.14       mjl #include <ctype.h>
     28  1.14       mjl #include <err.h>
     29  1.14       mjl #include <errno.h>
     30  1.14       mjl #include <fcntl.h>
     31  1.14       mjl #include <paths.h>
     32   1.1   deraadt #include <stdio.h>
     33   1.5        pk #include <stdlib.h>
     34   1.1   deraadt #include <string.h>
     35   1.1   deraadt #include <time.h>
     36  1.14       mjl #include <unistd.h>
     37   1.5        pk 
     38   1.1   deraadt #include "skey.h"
     39   1.1   deraadt 
     40  1.14       mjl #define OTP_FMT "otp-%.*s %d %.*s"
     41   1.1   deraadt 
     42   1.1   deraadt /* Issue a skey challenge for user 'name'. If successful,
     43   1.1   deraadt  * fill in the caller's skey structure and return 0. If unsuccessful
     44   1.1   deraadt  * (e.g., if name is unknown) return -1.
     45   1.1   deraadt  *
     46   1.1   deraadt  * The file read/write pointer is left at the start of the
     47   1.1   deraadt  * record.
     48   1.1   deraadt  */
     49  1.14       mjl int getskeyprompt(struct skey *mp, char *name, char *prompt)
     50   1.1   deraadt {
     51   1.1   deraadt 	int rval;
     52   1.1   deraadt 
     53   1.1   deraadt 	sevenbit(name);
     54  1.14       mjl 	rval = skeylookup(mp, name);
     55  1.14       mjl 
     56  1.14       mjl 	*prompt = '\0';
     57   1.5        pk 	switch (rval) {
     58   1.1   deraadt 	case -1:	/* File error */
     59   1.1   deraadt 		return -1;
     60   1.1   deraadt 	case 0:		/* Lookup succeeded, return challenge */
     61  1.14       mjl 		sprintf(prompt, OTP_FMT "\n",
     62  1.14       mjl 				SKEY_MAX_HASHNAME_LEN, skey_get_algorithm(),
     63  1.14       mjl 				mp->n - 1, SKEY_MAX_SEED_LEN, mp->seed);
     64   1.1   deraadt 		return 0;
     65   1.1   deraadt 	case 1:		/* User not found */
     66   1.1   deraadt 		fclose(mp->keyfile);
     67  1.14       mjl 		mp->keyfile = NULL;
     68   1.1   deraadt 		return -1;
     69   1.1   deraadt 	}
     70   1.1   deraadt 	return -1;	/* Can't happen */
     71   1.5        pk }
     72   1.5        pk 
     73   1.1   deraadt /* Return  a skey challenge string for user 'name'. If successful,
     74   1.1   deraadt  * fill in the caller's skey structure and return 0. If unsuccessful
     75   1.1   deraadt  * (e.g., if name is unknown) return -1.
     76   1.1   deraadt  *
     77   1.1   deraadt  * The file read/write pointer is left at the start of the
     78   1.1   deraadt  * record.
     79   1.1   deraadt  */
     80  1.14       mjl int skeychallenge(struct skey *mp, const char *name, char *ss, size_t sslen)
     81   1.1   deraadt {
     82   1.1   deraadt 	int rval;
     83   1.1   deraadt 
     84  1.14       mjl 	rval = skeylookup(mp, name);
     85  1.14       mjl 
     86  1.14       mjl 	*ss = '\0';
     87   1.1   deraadt 	switch(rval){
     88   1.1   deraadt 	case -1:	/* File error */
     89   1.1   deraadt 		return -1;
     90   1.1   deraadt 	case 0:		/* Lookup succeeded, issue challenge */
     91  1.14       mjl 		snprintf(ss, sslen, OTP_FMT, SKEY_MAX_HASHNAME_LEN,
     92  1.14       mjl 				skey_get_algorithm(), mp->n - 1,
     93  1.14       mjl 				SKEY_MAX_SEED_LEN, mp->seed);
     94   1.1   deraadt 		return 0;
     95   1.1   deraadt 	case 1:		/* User not found */
     96   1.1   deraadt 		fclose(mp->keyfile);
     97  1.14       mjl 		mp->keyfile = NULL;
     98   1.1   deraadt 		return -1;
     99   1.1   deraadt 	}
    100   1.1   deraadt 	return -1;	/* Can't happen */
    101  1.10    simonb }
    102   1.1   deraadt 
    103  1.14       mjl static FILE *openSkey(void)
    104  1.14       mjl {
    105  1.14       mjl 	struct stat statbuf;
    106  1.14       mjl 	FILE *keyfile = NULL;
    107  1.14       mjl 
    108  1.14       mjl 	/* Open _PATH_SKEYKEYS if it exists, else return an error */
    109  1.14       mjl 	if (stat(_PATH_SKEYKEYS, &statbuf) == 0 &&
    110  1.14       mjl 	    (keyfile = fopen(_PATH_SKEYKEYS, "r+"))) {
    111  1.14       mjl 		if ((statbuf.st_mode & 0007777) != 0600)
    112  1.14       mjl 		fchmod(fileno(keyfile), 0600);
    113  1.14       mjl         } else {
    114  1.14       mjl 		keyfile = NULL;
    115  1.14       mjl 	}
    116  1.14       mjl 
    117  1.14       mjl 	return keyfile;
    118  1.14       mjl }
    119  1.14       mjl 
    120   1.1   deraadt /* Find an entry in the One-time Password database.
    121   1.1   deraadt  * Return codes:
    122   1.1   deraadt  * -1: error in opening database
    123   1.1   deraadt  *  0: entry found, file R/W pointer positioned at beginning of record
    124   1.1   deraadt  *  1: entry not found, file R/W pointer positioned at EOF
    125   1.1   deraadt  */
    126  1.14       mjl int skeylookup(struct skey *mp, const char *name)
    127   1.1   deraadt {
    128  1.14       mjl 	int found = 0;
    129   1.8  christos 	long recstart = 0;
    130  1.14       mjl 	const char *ht = NULL;
    131  1.17    itojun 	char *last;
    132   1.1   deraadt 
    133  1.14       mjl 	if(!(mp->keyfile = openSkey()))
    134  1.14       mjl 		return(-1);
    135   1.1   deraadt 
    136   1.1   deraadt 	/* Look up user name in database */
    137   1.5        pk 	while (!feof(mp->keyfile)) {
    138  1.14       mjl 		char *cp;
    139  1.14       mjl 
    140   1.1   deraadt 		recstart = ftell(mp->keyfile);
    141   1.1   deraadt 		mp->recstart = recstart;
    142  1.14       mjl 		if (fgets(mp->buf, sizeof(mp->buf), mp->keyfile) != mp->buf)
    143   1.1   deraadt 			break;
    144  1.14       mjl 
    145   1.1   deraadt 		rip(mp->buf);
    146   1.5        pk 		if (mp->buf[0] == '#')
    147   1.1   deraadt 			continue;	/* Comment */
    148  1.17    itojun 		if ((mp->logname = strtok_r(mp->buf, " \t", &last)) == NULL)
    149   1.1   deraadt 			continue;
    150  1.17    itojun 		if ((cp = strtok_r(NULL, " \t", &last)) == NULL)
    151   1.1   deraadt 			continue;
    152  1.14       mjl 		/* Save hash type if specified, else use md4 */
    153  1.15     itohy 		if (isalpha((u_char) *cp)) {
    154  1.14       mjl 			ht = cp;
    155  1.17    itojun 			if ((cp = strtok_r(NULL, " \t", &last)) == NULL)
    156  1.14       mjl 				continue;
    157  1.14       mjl 		} else {
    158  1.14       mjl 			ht = "md4";
    159  1.14       mjl 		}
    160   1.1   deraadt 		mp->n = atoi(cp);
    161  1.17    itojun 		if ((mp->seed = strtok_r(NULL, " \t", &last)) == NULL)
    162   1.1   deraadt 			continue;
    163  1.17    itojun 		if ((mp->val = strtok_r(NULL, " \t", &last)) == NULL)
    164   1.1   deraadt 			continue;
    165  1.14       mjl 		if (strcmp(mp->logname, name) == 0) {
    166   1.1   deraadt 			found = 1;
    167   1.1   deraadt 			break;
    168   1.1   deraadt 		}
    169   1.1   deraadt 	}
    170   1.5        pk 	if (found) {
    171  1.14       mjl 		fseek(mp->keyfile, recstart, SEEK_SET);
    172  1.14       mjl 		/* Set hash type */
    173  1.14       mjl 		if (ht && skey_set_algorithm(ht) == NULL) {
    174  1.14       mjl 			warnx("Unknown hash algorithm %s, using %s", ht,
    175  1.14       mjl 				skey_get_algorithm());
    176  1.14       mjl 		}
    177  1.14       mjl 		return(0);
    178  1.14       mjl 	} else {
    179  1.14       mjl         	return(1);
    180  1.14       mjl 	}
    181  1.14       mjl }
    182  1.14       mjl 
    183  1.14       mjl /* Get the next entry in the One-time Password database.
    184  1.14       mjl  * Return codes:
    185  1.14       mjl  * -1: error in opening database
    186  1.14       mjl  *  0: next entry found and stored in mp
    187  1.14       mjl  *  1: no more entries, file R/W pointer positioned at EOF
    188  1.14       mjl  */
    189  1.14       mjl int skeygetnext(struct skey *mp)
    190  1.14       mjl {
    191  1.14       mjl 	long recstart = 0;
    192  1.17    itojun 	char *last;
    193  1.14       mjl 
    194  1.14       mjl 	/* Open _PATH_SKEYKEYS if it exists, else return an error */
    195  1.14       mjl 	if (mp->keyfile == NULL) {
    196  1.14       mjl 		if(!(mp->keyfile = openSkey()))
    197  1.14       mjl 			return(-1);
    198  1.14       mjl 	}
    199  1.14       mjl 
    200  1.14       mjl 	/* Look up next user in database */
    201  1.14       mjl 	while (!feof(mp->keyfile)) {
    202  1.14       mjl 		char *cp;
    203  1.14       mjl 
    204  1.14       mjl 		recstart = ftell(mp->keyfile);
    205  1.14       mjl 		mp->recstart = recstart;
    206  1.14       mjl 		if (fgets(mp->buf, sizeof(mp->buf), mp->keyfile) != mp->buf)
    207  1.14       mjl 			break;
    208  1.14       mjl 		rip(mp->buf);
    209  1.14       mjl 		if (mp->buf[0] == '#')
    210  1.14       mjl 			continue;	/* Comment */
    211  1.17    itojun 		if ((mp->logname = strtok_r(mp->buf, " \t", &last)) == NULL)
    212  1.14       mjl 			continue;
    213  1.17    itojun 		if ((cp = strtok_r(NULL, " \t", &last)) == NULL)
    214  1.14       mjl 			continue;
    215  1.14       mjl 		/* Save hash type if specified, else use md4 */
    216  1.15     itohy 		if (isalpha((u_char) *cp)) {
    217  1.17    itojun 			if ((cp = strtok_r(NULL, " \t", &last)) == NULL)
    218  1.14       mjl 				continue;
    219  1.14       mjl 		}
    220  1.14       mjl 		mp->n = atoi(cp);
    221  1.17    itojun 		if ((mp->seed = strtok_r(NULL, " \t", &last)) == NULL)
    222  1.14       mjl 			continue;
    223  1.17    itojun 		if ((mp->val = strtok_r(NULL, " \t", &last)) == NULL)
    224  1.14       mjl 			continue;
    225  1.14       mjl 		/* Got a real entry */
    226  1.14       mjl 		break;
    227  1.14       mjl 	}
    228  1.14       mjl 	return(feof(mp->keyfile));
    229   1.1   deraadt }
    230  1.14       mjl 
    231   1.1   deraadt /* Verify response to a s/key challenge.
    232   1.1   deraadt  *
    233   1.1   deraadt  * Return codes:
    234   1.1   deraadt  * -1: Error of some sort; database unchanged
    235   1.1   deraadt  *  0:  Verify successful, database updated
    236   1.1   deraadt  *  1:  Verify failed, database unchanged
    237   1.1   deraadt  *
    238   1.1   deraadt  * The database file is always closed by this call.
    239   1.1   deraadt  */
    240  1.14       mjl 
    241  1.14       mjl int skeyverify(struct skey *mp, char *response)
    242  1.14       mjl {
    243  1.14       mjl 	char key[SKEY_BINKEY_SIZE];
    244  1.14       mjl 	char fkey[SKEY_BINKEY_SIZE];
    245  1.14       mjl 	char filekey[SKEY_BINKEY_SIZE];
    246   1.1   deraadt 	time_t now;
    247   1.1   deraadt 	struct tm *tm;
    248   1.5        pk 	char tbuf[27];
    249  1.17    itojun 	char *cp, *last;
    250  1.14       mjl 	int i, rval;
    251   1.1   deraadt 
    252   1.1   deraadt 	time(&now);
    253   1.1   deraadt 	tm = localtime(&now);
    254   1.1   deraadt 	strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm);
    255   1.1   deraadt 
    256   1.5        pk 	if (response == NULL) {
    257   1.1   deraadt 		fclose(mp->keyfile);
    258  1.14       mjl 		mp->keyfile = NULL;
    259   1.1   deraadt 		return -1;
    260   1.1   deraadt 	}
    261   1.5        pk 	rip(response);
    262   1.1   deraadt 
    263   1.1   deraadt 	/* Convert response to binary */
    264   1.5        pk 	if (etob(key, response) != 1 && atob8(key, response) != 0) {
    265   1.1   deraadt 		/* Neither english words or ascii hex */
    266   1.1   deraadt 		fclose(mp->keyfile);
    267  1.14       mjl 		mp->keyfile = NULL;
    268   1.1   deraadt 		return -1;
    269   1.1   deraadt 	}
    270   1.1   deraadt 
    271   1.1   deraadt 	/* Compute fkey = f(key) */
    272  1.14       mjl 	memcpy(fkey, key, sizeof(key));
    273  1.14       mjl         fflush(stdout);
    274   1.1   deraadt 
    275   1.1   deraadt 	f(fkey);
    276  1.14       mjl 
    277   1.5        pk 	/*
    278  1.14       mjl 	 * Obtain an exclusive lock on the key file so the same password
    279  1.14       mjl 	 * cannot be used twice to get in to the system.
    280   1.5        pk 	 */
    281  1.14       mjl 	for (i = 0; i < 300; i++) {
    282  1.14       mjl 		if ((rval = flock(fileno(mp->keyfile), LOCK_EX|LOCK_NB)) == 0 ||
    283  1.14       mjl 		    errno != EWOULDBLOCK)
    284  1.14       mjl 			break;
    285  1.14       mjl 		usleep(100000);			/* Sleep for 0.1 seconds */
    286  1.14       mjl 	}
    287  1.14       mjl 	if (rval == -1) {			/* Can't get exclusive lock */
    288  1.14       mjl 		errno = EAGAIN;
    289  1.14       mjl 		return(-1);
    290  1.13        is 	}
    291   1.1   deraadt 
    292  1.14       mjl 	/* Reread the file record NOW */
    293   1.1   deraadt 
    294  1.14       mjl 	fseek(mp->keyfile, mp->recstart, SEEK_SET);
    295  1.14       mjl 	if (fgets(mp->buf, sizeof(mp->buf), mp->keyfile) != mp->buf) {
    296   1.1   deraadt 		fclose(mp->keyfile);
    297  1.14       mjl 		mp->keyfile = NULL;
    298   1.1   deraadt 		return -1;
    299   1.1   deraadt 	}
    300   1.1   deraadt 	rip(mp->buf);
    301  1.17    itojun 	if ((mp->logname = strtok_r(mp->buf, " \t", &last)) == NULL)
    302  1.17    itojun 		goto verify_failure;
    303  1.17    itojun 	if ((cp = strtok_r(NULL, " \t", &last)) == NULL)
    304  1.17    itojun 		goto verify_failure;
    305  1.15     itohy 	if (isalpha((u_char) *cp))
    306  1.17    itojun 		if ((cp = strtok_r(NULL, " \t", &last)) == NULL)
    307  1.17    itojun 			goto verify_failure;
    308  1.17    itojun 	if ((mp->seed = strtok_r(NULL, " \t", &last)) == NULL)
    309  1.17    itojun 		goto verify_failure;
    310  1.17    itojun 	if ((mp->val = strtok_r(NULL, " \t", &last)) == NULL)
    311  1.17    itojun 		goto verify_failure;
    312   1.1   deraadt 	/* And convert file value to hex for comparison */
    313  1.14       mjl 	atob8(filekey, mp->val);
    314   1.1   deraadt 
    315   1.1   deraadt 	/* Do actual comparison */
    316  1.14       mjl 	if (memcmp(filekey, fkey, SKEY_BINKEY_SIZE) != 0) {
    317   1.1   deraadt 		/* Wrong response */
    318   1.1   deraadt 		fclose(mp->keyfile);
    319  1.14       mjl 		mp->keyfile = NULL;
    320   1.1   deraadt 		return 1;
    321   1.1   deraadt 	}
    322   1.1   deraadt 
    323   1.5        pk 	/*
    324   1.5        pk 	 * Update key in database by overwriting entire record. Note
    325   1.1   deraadt 	 * that we must write exactly the same number of bytes as in
    326   1.1   deraadt 	 * the original record (note fixed width field for N)
    327   1.1   deraadt 	 */
    328  1.14       mjl 	btoa8(mp->val, key);
    329   1.1   deraadt 	mp->n--;
    330  1.14       mjl 	fseek(mp->keyfile, mp->recstart, SEEK_SET);
    331  1.14       mjl 	/* Don't save algorithm type for md4 (keep record length same) */
    332  1.14       mjl 	if (strcmp(skey_get_algorithm(), "md4") == 0)
    333  1.14       mjl 		(void)fprintf(mp->keyfile, "%s %04d %-16s %s %-21s\n",
    334  1.14       mjl 			      mp->logname, mp->n, mp->seed, mp->val, tbuf);
    335  1.14       mjl 	else
    336  1.14       mjl 		(void)fprintf(mp->keyfile, "%s %s %04d %-16s %s %-21s\n",
    337  1.14       mjl 			      mp->logname, skey_get_algorithm(), mp->n,
    338  1.14       mjl 			      mp->seed, mp->val, tbuf);
    339   1.1   deraadt 
    340   1.1   deraadt 	fclose(mp->keyfile);
    341  1.14       mjl 	mp->keyfile = NULL;
    342  1.17    itojun 	return 0;
    343  1.10    simonb 
    344  1.17    itojun   verify_failure:
    345  1.17    itojun 	fclose(mp->keyfile);
    346  1.17    itojun 	mp->keyfile = NULL;
    347  1.17    itojun 	return -1;
    348   1.1   deraadt }
    349   1.1   deraadt 
    350   1.1   deraadt 
    351  1.14       mjl /* Returns: 1 user doesnt exist, -1 file error, 0 user exists. */
    352  1.10    simonb 
    353  1.14       mjl int skey_haskey(const char *username)
    354   1.1   deraadt {
    355   1.5        pk 	struct skey skey;
    356  1.14       mjl 	int i;
    357  1.14       mjl 
    358  1.14       mjl 	i = skeylookup(&skey, username);
    359  1.10    simonb 
    360  1.14       mjl 	if (skey.keyfile != NULL) {
    361  1.14       mjl 		fclose(skey.keyfile);
    362  1.14       mjl 		skey.keyfile = NULL;
    363  1.14       mjl 	}
    364  1.14       mjl 	return(i);
    365   1.1   deraadt }
    366  1.10    simonb 
    367   1.1   deraadt /*
    368   1.1   deraadt  * Returns the current sequence number and
    369   1.1   deraadt  * seed for the passed user.
    370   1.1   deraadt  */
    371  1.16   thorpej const char *skey_keyinfo(const char *username)
    372   1.1   deraadt {
    373   1.5        pk 	int i;
    374  1.14       mjl 	static char str[SKEY_MAX_CHALLENGE];
    375   1.5        pk 	struct skey skey;
    376   1.5        pk 
    377  1.14       mjl 	i = skeychallenge(&skey, username, str, sizeof str);
    378   1.5        pk 	if (i == -1)
    379   1.5        pk 		return 0;
    380   1.1   deraadt 
    381  1.14       mjl 	if (skey.keyfile != NULL) {
    382  1.14       mjl 		fclose(skey.keyfile);
    383  1.14       mjl 		skey.keyfile = NULL;
    384  1.14       mjl 	}
    385   1.5        pk 	return str;
    386   1.1   deraadt }
    387  1.10    simonb 
    388   1.1   deraadt /*
    389   1.1   deraadt  * Check to see if answer is the correct one to the current
    390   1.1   deraadt  * challenge.
    391   1.1   deraadt  *
    392   1.1   deraadt  * Returns: 0 success, -1 failure
    393   1.1   deraadt  */
    394  1.10    simonb 
    395  1.19     perry int skey_passcheck(const char *username, char *passwd)
    396   1.1   deraadt {
    397   1.5        pk 	int i;
    398   1.5        pk 	struct skey skey;
    399   1.5        pk 
    400   1.5        pk 	i = skeylookup (&skey, username);
    401   1.5        pk 	if (i == -1 || i == 1)
    402   1.5        pk 		return -1;
    403   1.5        pk 
    404   1.5        pk 	if (skeyverify (&skey, passwd) == 0)
    405   1.5        pk 		return skey.n;
    406   1.5        pk 
    407   1.5        pk 	return -1;
    408   1.1   deraadt }
    409   1.1   deraadt 
    410  1.14       mjl #if DO_FAKE_CHALLENGE
    411  1.14       mjl #define ROUND(x)   (((x)[0] << 24) + (((x)[1]) << 16) + (((x)[2]) << 8) + \
    412  1.14       mjl 		    ((x)[3]))
    413  1.14       mjl 
    414  1.14       mjl /*
    415  1.14       mjl  * hash_collapse()
    416  1.14       mjl  */
    417  1.14       mjl static u_int32_t hash_collapse(u_char *s)
    418  1.14       mjl {
    419  1.14       mjl         int len, target;
    420  1.14       mjl 	u_int32_t i;
    421  1.14       mjl 	int slen;
    422  1.14       mjl 
    423  1.14       mjl 	slen = strlen((char *)s);
    424  1.14       mjl 	if ((slen % sizeof(u_int32_t)) == 0)
    425  1.14       mjl   		target = slen;    /* Multiple of 4 */
    426  1.14       mjl 	else
    427  1.14       mjl 		target = slen - slen % sizeof(u_int32_t);
    428  1.14       mjl 
    429  1.14       mjl 	for (i = 0, len = 0; len < target; len += 4)
    430  1.14       mjl         	i ^= ROUND(s + len);
    431  1.14       mjl 
    432  1.14       mjl 	return i;
    433  1.14       mjl }
    434  1.14       mjl #endif
    435  1.14       mjl 
    436   1.1   deraadt /*
    437   1.1   deraadt  * Used when calling program will allow input of the user's
    438   1.1   deraadt  * response to the challenge.
    439   1.1   deraadt  *
    440   1.1   deraadt  * Returns: 0 success, -1 failure
    441   1.1   deraadt  */
    442  1.10    simonb 
    443  1.14       mjl int skey_authenticate(const char *username)
    444   1.1   deraadt {
    445   1.5        pk 	int i;
    446  1.14       mjl 	char pbuf[SKEY_MAX_PW_LEN+1], skeyprompt[SKEY_MAX_CHALLENGE+1];
    447   1.5        pk 	struct skey skey;
    448  1.14       mjl #if DO_FAKE_CHALLENGE
    449  1.14       mjl 	u_int ptr;
    450  1.14       mjl 	u_char hseed[SKEY_MAX_SEED_LEN], flg = 1, *up;
    451  1.14       mjl 	size_t secretlen;
    452  1.14       mjl 	struct skey skey;
    453  1.14       mjl 	SHA1_CTX ctx;
    454  1.14       mjl #endif
    455  1.14       mjl 	/* Attempt a S/Key challenge */
    456  1.14       mjl 	i = skeychallenge(&skey, username, skeyprompt, sizeof skeyprompt);
    457   1.5        pk 
    458  1.14       mjl #if DO_FAKE_CHALLENGE
    459  1.14       mjl 	/* Cons up a fake prompt if no entry in keys file */
    460  1.14       mjl 	if (i != 0) {
    461  1.14       mjl 		char *p, *u;
    462  1.14       mjl 
    463  1.14       mjl 		/*
    464  1.14       mjl 		 * Base first 4 chars of seed on hostname.
    465  1.14       mjl 		 * Add some filler for short hostnames if necessary.
    466  1.14       mjl 		 */
    467  1.14       mjl 		if (gethostname(pbuf, sizeof(pbuf)) == -1)
    468  1.14       mjl 			*(p = pbuf) = '.';
    469  1.14       mjl 		else
    470  1.15     itohy 			for (p = pbuf; *p && isalnum((u_char) *p); p++)
    471  1.15     itohy 				if (isalpha((u_char)*p) && isupper((u_char)*p))
    472  1.15     itohy 					*p = tolower((u_char)*p);
    473  1.14       mjl 		if (*p && pbuf - p < 4)
    474  1.14       mjl 			(void)strncpy(p, "asjd", 4 - (pbuf - p));
    475  1.14       mjl 		pbuf[4] = '\0';
    476  1.14       mjl 
    477  1.14       mjl 		/* Hash the username if possible */
    478  1.14       mjl 		if ((up = SHA1Data(username, strlen(username), NULL)) != NULL) {
    479  1.14       mjl 			struct stat sb;
    480  1.14       mjl 			time_t t;
    481  1.14       mjl 			int fd;
    482  1.14       mjl 
    483  1.14       mjl 			/* Collapse the hash */
    484  1.14       mjl 			ptr = hash_collapse(up);
    485  1.14       mjl 			memset(up, 0, strlen(up));
    486  1.14       mjl 
    487  1.14       mjl 			/* See if the random file's there, else use ctime */
    488  1.14       mjl 			if ((fd = open(_SKEY_RAND_FILE_PATH_, O_RDONLY)) != -1
    489  1.14       mjl 			    && fstat(fd, &sb) == 0 &&
    490  1.14       mjl 			    sb.st_size > (off_t)SKEY_MAX_SEED_LEN &&
    491  1.14       mjl 			    lseek(fd, ptr % (sb.st_size - SKEY_MAX_SEED_LEN),
    492  1.14       mjl 			    SEEK_SET) != -1 && read(fd, hseed,
    493  1.14       mjl 			    SKEY_MAX_SEED_LEN) == SKEY_MAX_SEED_LEN) {
    494  1.14       mjl 				close(fd);
    495  1.14       mjl 				fd = -1;
    496  1.14       mjl 				secret = hseed;
    497  1.14       mjl 				secretlen = SKEY_MAX_SEED_LEN;
    498  1.14       mjl 				flg = 0;
    499  1.14       mjl 			} else if (!stat(_PATH_MEM, &sb) || !stat("/", &sb)) {
    500  1.14       mjl 				t = sb.st_ctime;
    501  1.14       mjl 				secret = ctime(&t);
    502  1.14       mjl 				secretlen = strlen(secret);
    503  1.14       mjl 				flg = 0;
    504  1.14       mjl 			}
    505  1.14       mjl 			if (fd != -1)
    506  1.14       mjl 				close(fd);
    507  1.14       mjl 		}
    508   1.5        pk 
    509  1.14       mjl 		/* Put that in your pipe and smoke it */
    510  1.14       mjl 		if (flg == 0) {
    511  1.14       mjl 			/* Hash secret value with username */
    512  1.14       mjl 			SHA1Init(&ctx);
    513  1.14       mjl 			SHA1Update(&ctx, secret, secretlen);
    514  1.14       mjl 			SHA1Update(&ctx, username, strlen(username));
    515  1.14       mjl 			SHA1End(&ctx, up);
    516  1.14       mjl 
    517  1.14       mjl 			/* Zero out */
    518  1.14       mjl 			memset(secret, 0, secretlen);
    519  1.14       mjl 
    520  1.14       mjl 			/* Now hash the hash */
    521  1.14       mjl 			SHA1Init(&ctx);
    522  1.14       mjl 			SHA1Update(&ctx, up, strlen(up));
    523  1.14       mjl 			SHA1End(&ctx, up);
    524  1.14       mjl 
    525  1.14       mjl 			ptr = hash_collapse(up + 4);
    526  1.14       mjl 
    527  1.14       mjl 			for (i = 4; i < 9; i++) {
    528  1.14       mjl 				pbuf[i] = (ptr % 10) + '0';
    529  1.14       mjl 				ptr /= 10;
    530  1.14       mjl 			}
    531  1.14       mjl 			pbuf[i] = '\0';
    532  1.14       mjl 
    533  1.14       mjl 			/* Sequence number */
    534  1.14       mjl 			ptr = ((up[2] + up[3]) % 99) + 1;
    535  1.14       mjl 
    536  1.14       mjl 			memset(up, 0, 20); /* SHA1 specific */
    537  1.14       mjl 			free(up);
    538  1.14       mjl 
    539  1.14       mjl 			(void)sprintf(skeyprompt,
    540  1.14       mjl 				      "otp-%.*s %d %.*s",
    541  1.14       mjl 				      SKEY_MAX_HASHNAME_LEN,
    542  1.14       mjl 				      skey_get_algorithm(),
    543  1.14       mjl 				      ptr, SKEY_MAX_SEED_LEN,
    544  1.14       mjl 				      pbuf);
    545  1.14       mjl 		} else {
    546  1.14       mjl 			/* Base last 8 chars of seed on username */
    547  1.14       mjl 			u = username;
    548  1.14       mjl 			i = 8;
    549  1.14       mjl 			p = &pbuf[4];
    550  1.14       mjl 			do {
    551  1.14       mjl 				if (*u == 0) {
    552  1.14       mjl 					/* Pad remainder with zeros */
    553  1.14       mjl 					while (--i >= 0)
    554  1.14       mjl 						*p++ = '0';
    555  1.14       mjl 					break;
    556  1.14       mjl 				}
    557  1.14       mjl 
    558  1.14       mjl 				*p++ = (*u++ % 10) + '0';
    559  1.14       mjl 			} while (--i != 0);
    560  1.14       mjl 			pbuf[12] = '\0';
    561  1.14       mjl 
    562  1.14       mjl 			(void)sprintf(skeyprompt, "otp-%.*s %d %.*s",
    563  1.14       mjl 				      SKEY_MAX_HASHNAME_LEN,
    564  1.14       mjl 				      skey_get_algorithm(),
    565  1.14       mjl 				      99, SKEY_MAX_SEED_LEN, pbuf);
    566  1.14       mjl 		}
    567  1.14       mjl 	}
    568  1.14       mjl #endif
    569   1.5        pk 
    570  1.14       mjl 	fprintf(stderr, "[%s]\n", skeyprompt);
    571  1.14       mjl 	fflush(stderr);
    572   1.5        pk 
    573  1.14       mjl 	fputs("Response: ", stderr);
    574  1.14       mjl 	readskey(pbuf, sizeof(pbuf));
    575   1.5        pk 
    576   1.5        pk 	/* Is it a valid response? */
    577  1.14       mjl 	if (i == 0 && skeyverify(&skey, pbuf) == 0) {
    578   1.5        pk 		if (skey.n < 5) {
    579  1.14       mjl 			fprintf(stderr,
    580  1.14       mjl 			"\nWarning! Key initialization needed soon.  (%d logins left)\n",
    581  1.14       mjl 			skey.n);
    582   1.5        pk 		}
    583   1.5        pk 		return 0;
    584   1.5        pk 	}
    585   1.5        pk 	return -1;
    586  1.14       mjl }
    587  1.14       mjl 
    588  1.14       mjl /* Comment out user's entry in the s/key database
    589  1.14       mjl  *
    590  1.14       mjl  * Return codes:
    591  1.14       mjl  * -1: Write error; database unchanged
    592  1.14       mjl  *  0:  Database updated
    593  1.14       mjl  *
    594  1.14       mjl  * The database file is always closed by this call.
    595  1.14       mjl  */
    596  1.14       mjl 
    597  1.14       mjl /* ARGSUSED */
    598  1.14       mjl int skeyzero(struct skey *mp, char *response)
    599  1.14       mjl {
    600  1.14       mjl 	/*
    601  1.14       mjl 	 * Seek to the right place and write comment character
    602  1.14       mjl 	 * which effectively zero's out the entry.
    603  1.14       mjl 	 */
    604  1.14       mjl 	fseek(mp->keyfile, mp->recstart, SEEK_SET);
    605  1.14       mjl 	if (fputc('#', mp->keyfile) == EOF) {
    606  1.14       mjl 		fclose(mp->keyfile);
    607  1.14       mjl 		mp->keyfile = NULL;
    608  1.14       mjl 		return(-1);
    609  1.14       mjl 	}
    610  1.14       mjl 
    611  1.14       mjl 	fclose(mp->keyfile);
    612  1.14       mjl 	mp->keyfile = NULL;
    613  1.14       mjl 	return(0);
    614   1.1   deraadt }
    615