Home | History | Annotate | Line # | Download | only in libskey
skeylogin.c revision 1.9
      1 /*	$NetBSD: skeylogin.c,v 1.9 1998/07/26 21:58:46 mycroft 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 ) len = 8;		/*  Added 8/2/91  -  nmh */
    132 	found = 0;
    133 	while (!feof(mp->keyfile)) {
    134 		recstart = ftell(mp->keyfile);
    135 		mp->recstart = recstart;
    136 		if (fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf) {
    137 			break;
    138 		}
    139 		rip(mp->buf);
    140 		if (mp->buf[0] == '#')
    141 			continue;	/* Comment */
    142 		if ((mp->logname = strtok(mp->buf," \t")) == NULL)
    143 			continue;
    144 		if ((cp = strtok(NULL," \t")) == NULL)
    145 			continue;
    146 		mp->n = atoi(cp);
    147 		if ((mp->seed = strtok(NULL," \t")) == NULL)
    148 			continue;
    149 		if ((mp->val = strtok(NULL," \t")) == NULL)
    150 			continue;
    151 		if (strlen(mp->logname) == len
    152 		 && strncmp(mp->logname,name,len) == 0){
    153 			found = 1;
    154 			break;
    155 		}
    156 	}
    157 	if (found) {
    158 		fseek(mp->keyfile,recstart,0);
    159 		return 0;
    160 	} else
    161 		return 1;
    162 }
    163 /* Verify response to a s/key challenge.
    164  *
    165  * Return codes:
    166  * -1: Error of some sort; database unchanged
    167  *  0:  Verify successful, database updated
    168  *  1:  Verify failed, database unchanged
    169  *
    170  * The database file is always closed by this call.
    171  */
    172 int
    173 skeyverify(mp,response)
    174 	struct skey *mp;
    175 	char *response;
    176 {
    177 	char key[8];
    178 	char fkey[8];
    179 	char filekey[8];
    180 	time_t now;
    181 	struct tm *tm;
    182 	char tbuf[27];
    183 	char *cp;
    184 
    185 	time(&now);
    186 	tm = localtime(&now);
    187 	strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm);
    188 
    189 	if (response == NULL) {
    190 		fclose(mp->keyfile);
    191 		return -1;
    192 	}
    193 	rip(response);
    194 
    195 	/* Convert response to binary */
    196 	if (etob(key, response) != 1 && atob8(key, response) != 0) {
    197 		/* Neither english words or ascii hex */
    198 		fclose(mp->keyfile);
    199 		return -1;
    200 	}
    201 
    202 	/* Compute fkey = f(key) */
    203 	memcpy(fkey,key,sizeof(key));
    204         fflush (stdout);
    205 
    206 	f(fkey);
    207 	/*
    208 	 * in order to make the window of update as short as possible
    209 	 * we must do the comparison here and if OK write it back
    210 	 * other wise the same password can be used twice to get in
    211 	 * to the system
    212 	 */
    213 
    214 	setpriority(PRIO_PROCESS, 0, -4);
    215 
    216 	/* reread the file record NOW*/
    217 
    218 	fseek(mp->keyfile,mp->recstart,0);
    219 	if (fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
    220 		setpriority(PRIO_PROCESS, 0, 0);
    221 		fclose(mp->keyfile);
    222 		return -1;
    223 	}
    224 	rip(mp->buf);
    225 	mp->logname = strtok(mp->buf," \t");
    226 	cp = strtok(NULL," \t") ;
    227 	mp->seed = strtok(NULL," \t");
    228 	mp->val = strtok(NULL," \t");
    229 	/* And convert file value to hex for comparison */
    230 	atob8(filekey,mp->val);
    231 
    232 	/* Do actual comparison */
    233         fflush (stdout);
    234 
    235 	if (memcmp(filekey,fkey,8) != 0){
    236 		/* Wrong response */
    237 		setpriority(PRIO_PROCESS, 0, 0);
    238 		fclose(mp->keyfile);
    239 		return 1;
    240 	}
    241 
    242 	/*
    243 	 * Update key in database by overwriting entire record. Note
    244 	 * that we must write exactly the same number of bytes as in
    245 	 * the original record (note fixed width field for N)
    246 	 */
    247 	btoa8(mp->val,key);
    248 	mp->n--;
    249 	fseek(mp->keyfile,mp->recstart,0);
    250 	fprintf(mp->keyfile, "%s %04d %-16s %s %-21s\n",
    251 		mp->logname,mp->n,mp->seed, mp->val, tbuf);
    252 
    253 	fclose(mp->keyfile);
    254 
    255 	setpriority(PRIO_PROCESS, 0, 0);
    256 	return 0;
    257 }
    258 
    259 
    260 /*
    261  * skey_haskey ()
    262  *
    263  * Returns: 1 user doesnt exist, -1 fle error, 0 user exists.
    264  *
    265  */
    266 
    267 int
    268 skey_haskey (username)
    269 	const char *username;
    270 {
    271 	struct skey skey;
    272 
    273 	return (skeylookup (&skey, username));
    274 }
    275 
    276 /*
    277  * skey_keyinfo ()
    278  *
    279  * Returns the current sequence number and
    280  * seed for the passed user.
    281  *
    282  */
    283 char *
    284 skey_keyinfo (username)
    285 	const char *username;
    286 {
    287 	int i;
    288 	static char str [50];
    289 	struct skey skey;
    290 
    291 	i = skeychallenge (&skey, username, str, sizeof str);
    292 	if (i == -1)
    293 		return 0;
    294 
    295 	return str;
    296 }
    297 
    298 /*
    299  * skey_passcheck ()
    300  *
    301  * Check to see if answer is the correct one to the current
    302  * challenge.
    303  *
    304  * Returns: 0 success, -1 failure
    305  *
    306  */
    307 
    308 int
    309 skey_passcheck (username, passwd)
    310 	const char *username;
    311 	char *passwd;
    312 {
    313 	int i;
    314 	struct skey skey;
    315 
    316 	i = skeylookup (&skey, username);
    317 	if (i == -1 || i == 1)
    318 		return -1;
    319 
    320 	if (skeyverify (&skey, passwd) == 0)
    321 		return skey.n;
    322 
    323 	return -1;
    324 }
    325 
    326 /*
    327  * skey_authenticate ()
    328  *
    329  * Used when calling program will allow input of the user's
    330  * response to the challenge.
    331  *
    332  * Returns: 0 success, -1 failure
    333  *
    334  */
    335 
    336 int
    337 skey_authenticate (username)
    338 	const char *username;
    339 {
    340 	int i;
    341 	char pbuf[256], skeyprompt[50];
    342 	struct skey skey;
    343 
    344 	/* Attempt a S/Key challenge */
    345 	i = skeychallenge (&skey, username, skeyprompt, sizeof skeyprompt);
    346 
    347 	if (i == -2)
    348 		return 0;
    349 
    350 	printf("[%s]\n", skeyprompt);
    351 	fflush(stdout);
    352 
    353 	printf("Response: ");
    354 	readskey(pbuf, sizeof (pbuf));
    355 	rip(pbuf);
    356 
    357 	/* Is it a valid response? */
    358 	if (i == 0 && skeyverify (&skey, pbuf) == 0) {
    359 		if (skey.n < 5) {
    360 			printf ("\nWarning! Key initialization needed soon.  ");
    361 			printf ("(%d logins left)\n", skey.n);
    362 		}
    363 		return 0;
    364 	}
    365 	return -1;
    366 }
    367