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