Home | History | Annotate | Line # | Download | only in libskey
skeylogin.c revision 1.13
      1 /*	$NetBSD: skeylogin.c,v 1.13 2000/04/13 13:25:56 is Exp $	*/
      2 
      3 /* S/KEY v1.1b (skeylogin.c)
      4  *
      5  * Authors:
      6  *          Neil M. Haller <nmh (at) thumper.bellcore.com>
      7  *          Philip R. Karn <karn (at) chicago.qualcomm.com>
      8  *          John S. Walden <jsw (at) thumper.bellcore.com>
      9  *          Scott Chasin <chasin (at) crimelab.com>
     10  *
     11  * S/KEY verification check, lookups, and authentication.
     12  */
     13 
     14 #include <sys/param.h>
     15 #ifdef	QUOTA
     16 #include <sys/quota.h>
     17 #endif
     18 #include <sys/stat.h>
     19 #include <sys/time.h>
     20 #include <sys/timeb.h>
     21 #include <sys/resource.h>
     22 
     23 
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 #include <sys/types.h>
     28 #include <sys/stat.h>
     29 #include <time.h>
     30 #include <errno.h>
     31 
     32 #include "skey.h"
     33 
     34 #define	_PATH_KEYFILE	"/etc/skeykeys"
     35 
     36 /* Issue a skey challenge for user 'name'. If successful,
     37  * fill in the caller's skey structure and return 0. If unsuccessful
     38  * (e.g., if name is unknown) return -1.
     39  *
     40  * The file read/write pointer is left at the start of the
     41  * record.
     42  *
     43  * XXX this function should be changed to have a length param for the
     44  * prompt!
     45  */
     46 int
     47 getskeyprompt(mp,name,prompt)
     48 	struct skey *mp;
     49 	char *name;
     50 	char *prompt;
     51 {
     52 	int rval;
     53 
     54 	sevenbit(name);
     55 	rval = skeylookup(mp,name);
     56 #if 0
     57 	strcpy(prompt, "s/key 55 latour1\n");
     58 #endif
     59 	switch (rval) {
     60 	case -1:	/* File error */
     61 		return -1;
     62 	case 0:		/* Lookup succeeded, return challenge */
     63 		sprintf(prompt,"s/key %d %s\n",mp->n - 1,mp->seed);
     64 		return 0;
     65 	case 1:		/* User not found */
     66 		fclose(mp->keyfile);
     67 		return -1;
     68 	}
     69 	return -1;	/* Can't happen */
     70 }
     71 
     72 /* Return  a skey challenge string for user 'name'. If successful,
     73  * fill in the caller's skey structure and return 0. If unsuccessful
     74  * (e.g., if name is unknown) return -1.
     75  *
     76  * The file read/write pointer is left at the start of the
     77  * record.
     78  */
     79 int
     80 skeychallenge(mp,name, ss, sslen)
     81 	struct skey *mp;
     82 	const char *name;
     83 	char *ss;
     84 	int sslen;
     85 {
     86 	int rval;
     87 
     88 	rval = skeylookup(mp,name);
     89 	switch(rval){
     90 	case -1:	/* File error */
     91 		return -1;
     92 	case 0:		/* Lookup succeeded, issue challenge */
     93                 (void)snprintf(ss, (size_t)sslen, "s/key %d %s", mp->n - 1,
     94 		    mp->seed);
     95 		return 0;
     96 	case 1:		/* User not found */
     97 		fclose(mp->keyfile);
     98 		return -1;
     99 	}
    100 	return -1;	/* Can't happen */
    101 }
    102 
    103 /* Find an entry in the One-time Password database.
    104  * Return codes:
    105  * -1: error in opening database
    106  *  0: entry found, file R/W pointer positioned at beginning of record
    107  *  1: entry not found, file R/W pointer positioned at EOF
    108  */
    109 int
    110 skeylookup(mp,name)
    111 	struct skey *mp;
    112 	const char *name;
    113 {
    114 	int found;
    115 	size_t len;
    116 	long recstart = 0;
    117 	char *cp;
    118 	struct stat statbuf;
    119 
    120 	/* See if the _PATH_KEYFILE exists, and create it if not */
    121 
    122 	if (stat(_PATH_KEYFILE,&statbuf) == -1 && errno == ENOENT) {
    123 		mp->keyfile = fopen(_PATH_KEYFILE,"w+");
    124 		if (mp->keyfile)
    125 			chmod(_PATH_KEYFILE, 0644);
    126 	} else {
    127 		/* Otherwise open normally for update */
    128 		mp->keyfile = fopen(_PATH_KEYFILE,"r+");
    129 	}
    130 	if (mp->keyfile == NULL)
    131 		return -1;
    132 
    133 	/* Look up user name in database */
    134 	len = strlen(name);
    135 	if (len > 8)
    136 		len = 8;		/*  Added 8/2/91  -  nmh */
    137 	found = 0;
    138 	while (!feof(mp->keyfile)) {
    139 		recstart = ftell(mp->keyfile);
    140 		mp->recstart = recstart;
    141 		if (fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf) {
    142 			break;
    143 		}
    144 		rip(mp->buf);
    145 		if (mp->buf[0] == '#')
    146 			continue;	/* Comment */
    147 		if ((mp->logname = strtok(mp->buf," \t")) == NULL)
    148 			continue;
    149 		if ((cp = strtok(NULL," \t")) == NULL)
    150 			continue;
    151 		mp->n = atoi(cp);
    152 		if ((mp->seed = strtok(NULL," \t")) == NULL)
    153 			continue;
    154 		if ((mp->val = strtok(NULL," \t")) == NULL)
    155 			continue;
    156 		if (strlen(mp->logname) == len
    157 		 && strncmp(mp->logname,name,len) == 0){
    158 			found = 1;
    159 			break;
    160 		}
    161 	}
    162 	if (found) {
    163 		fseek(mp->keyfile,recstart,0);
    164 		return 0;
    165 	} else
    166 		return 1;
    167 }
    168 /* Verify response to a s/key challenge.
    169  *
    170  * Return codes:
    171  * -1: Error of some sort; database unchanged
    172  *  0:  Verify successful, database updated
    173  *  1:  Verify failed, database unchanged
    174  *
    175  * The database file is always closed by this call.
    176  */
    177 int
    178 skeyverify(mp,response)
    179 	struct skey *mp;
    180 	char *response;
    181 {
    182 	char key[8];
    183 	char fkey[8];
    184 	char filekey[8];
    185 	time_t now;
    186 	int  prevprio;
    187 	struct tm *tm;
    188 	char tbuf[27];
    189 
    190 	time(&now);
    191 	tm = localtime(&now);
    192 	strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm);
    193 
    194 	if (response == NULL) {
    195 		fclose(mp->keyfile);
    196 		return -1;
    197 	}
    198 	rip(response);
    199 
    200 	/* Convert response to binary */
    201 	if (etob(key, response) != 1 && atob8(key, response) != 0) {
    202 		/* Neither english words or ascii hex */
    203 		fclose(mp->keyfile);
    204 		return -1;
    205 	}
    206 
    207 	/* Compute fkey = f(key) */
    208 	memcpy(fkey,key,sizeof(key));
    209         fflush (stdout);
    210 
    211 	f(fkey);
    212 	/*
    213 	 * in order to make the window of update as short as possible
    214 	 * we must do the comparison here and if OK write it back
    215 	 * other wise the same password can be used twice to get in
    216 	 * to the system
    217 	 */
    218 
    219 	/* Save the priority for later use. */
    220 	errno = 0;
    221 	prevprio = getpriority(PRIO_PROCESS, 0);
    222 	if (errno) {
    223 		/* Don't report the error; just don't use it later. */
    224 		prevprio = PRIO_MAX + 1;
    225 	} else {
    226 		setpriority(PRIO_PROCESS, 0, -4);
    227 	}
    228 
    229 	/* reread the file record NOW*/
    230 
    231 	fseek(mp->keyfile,mp->recstart,0);
    232 	if (fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
    233 		if (prevprio != PRIO_MAX + 1) {
    234 			setpriority(PRIO_PROCESS, 0, prevprio);
    235 		}
    236 		fclose(mp->keyfile);
    237 		return -1;
    238 	}
    239 	rip(mp->buf);
    240 	mp->logname = strtok(mp->buf," \t");
    241 	(void) strtok(NULL," \t");	/* ignore counter */
    242 	mp->seed = strtok(NULL," \t");
    243 	mp->val = strtok(NULL," \t");
    244 	/* And convert file value to hex for comparison */
    245 	atob8(filekey,mp->val);
    246 
    247 	/* Do actual comparison */
    248         fflush (stdout);
    249 
    250 	if (memcmp(filekey,fkey,8) != 0){
    251 		/* Wrong response */
    252 		if (prevprio != PRIO_MAX + 1) {
    253 			setpriority(PRIO_PROCESS, 0, prevprio);
    254 		}
    255 		fclose(mp->keyfile);
    256 		return 1;
    257 	}
    258 
    259 	/*
    260 	 * Update key in database by overwriting entire record. Note
    261 	 * that we must write exactly the same number of bytes as in
    262 	 * the original record (note fixed width field for N)
    263 	 */
    264 	btoa8(mp->val,key);
    265 	mp->n--;
    266 	fseek(mp->keyfile,mp->recstart,0);
    267 	fprintf(mp->keyfile, "%s %04d %-16s %s %-21s\n",
    268 		mp->logname,mp->n,mp->seed, mp->val, tbuf);
    269 
    270 	fclose(mp->keyfile);
    271 
    272 	if (prevprio != PRIO_MAX + 1) {
    273 		setpriority(PRIO_PROCESS, 0, prevprio);
    274 	}
    275 	return 0;
    276 }
    277 
    278 
    279 /*
    280  * skey_haskey ()
    281  *
    282  * Returns: 1 user doesnt exist, -1 fle error, 0 user exists.
    283  *
    284  */
    285 
    286 int
    287 skey_haskey (username)
    288 	const char *username;
    289 {
    290 	struct skey skey;
    291 
    292 	return (skeylookup (&skey, username));
    293 }
    294 
    295 /*
    296  * skey_keyinfo ()
    297  *
    298  * Returns the current sequence number and
    299  * seed for the passed user.
    300  *
    301  */
    302 char *
    303 skey_keyinfo (username)
    304 	const char *username;
    305 {
    306 	int i;
    307 	static char str [50];
    308 	struct skey skey;
    309 
    310 	i = skeychallenge (&skey, username, str, sizeof str);
    311 	if (i == -1)
    312 		return 0;
    313 
    314 	return str;
    315 }
    316 
    317 /*
    318  * skey_passcheck ()
    319  *
    320  * Check to see if answer is the correct one to the current
    321  * challenge.
    322  *
    323  * Returns: 0 success, -1 failure
    324  *
    325  */
    326 
    327 int
    328 skey_passcheck (username, passwd)
    329 	const char *username;
    330 	char *passwd;
    331 {
    332 	int i;
    333 	struct skey skey;
    334 
    335 	i = skeylookup (&skey, username);
    336 	if (i == -1 || i == 1)
    337 		return -1;
    338 
    339 	if (skeyverify (&skey, passwd) == 0)
    340 		return skey.n;
    341 
    342 	return -1;
    343 }
    344 
    345 /*
    346  * skey_authenticate ()
    347  *
    348  * Used when calling program will allow input of the user's
    349  * response to the challenge.
    350  *
    351  * Returns: 0 success, -1 failure
    352  *
    353  */
    354 
    355 int
    356 skey_authenticate (username)
    357 	const char *username;
    358 {
    359 	int i;
    360 	char pbuf[256], skeyprompt[50];
    361 	struct skey skey;
    362 
    363 	/* Attempt a S/Key challenge */
    364 	i = skeychallenge (&skey, username, skeyprompt, sizeof skeyprompt);
    365 
    366 	if (i == -2)
    367 		return 0;
    368 
    369 	printf("[%s]\n", skeyprompt);
    370 	fflush(stdout);
    371 
    372 	printf("Response: ");
    373 	readskey(pbuf, sizeof (pbuf));
    374 	rip(pbuf);
    375 
    376 	/* Is it a valid response? */
    377 	if (i == 0 && skeyverify (&skey, pbuf) == 0) {
    378 		if (skey.n < 5) {
    379 			printf ("\nWarning! Key initialization needed soon.  ");
    380 			printf ("(%d logins left)\n", skey.n);
    381 		}
    382 		return 0;
    383 	}
    384 	return -1;
    385 }
    386