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