Home | History | Annotate | Line # | Download | only in libskey
skeylogin.c revision 1.2
      1 /* S/KEY v1.1b (skeylogin.c)
      2  *
      3  * Authors:
      4  *          Neil M. Haller <nmh (at) thumper.bellcore.com>
      5  *          Philip R. Karn <karn (at) chicago.qualcomm.com>
      6  *          John S. Walden <jsw (at) thumper.bellcore.com>
      7  *          Scott Chasin <chasin (at) crimelab.com>
      8  *
      9  * S/KEY verification check, lookups, and authentication.
     10  *
     11  * $Id: skeylogin.c,v 1.2 1994/05/31 08:50:31 deraadt Exp $
     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 <string.h>
     26 #include <sys/types.h>
     27 #include <sys/stat.h>
     28 #include <time.h>
     29 #include <errno.h>
     30 #include "skey.h"
     31 
     32 #define	KEYFILE	"/etc/skeykeys"
     33 
     34 char *skipspace();
     35 int skeylookup __ARGS((struct skey *mp,char *name));
     36 
     37 
     38 /* Issue a skey challenge for user 'name'. If successful,
     39  * fill in the caller's skey structure and return 0. If unsuccessful
     40  * (e.g., if name is unknown) return -1.
     41  *
     42  * The file read/write pointer is left at the start of the
     43  * record.
     44  */
     45 int
     46 getskeyprompt(mp,name,prompt)
     47 struct skey *mp;
     48 char *name;
     49 char *prompt;
     50 {
     51 	int rval;
     52 
     53 	sevenbit(name);
     54 	rval = skeylookup(mp,name);
     55 	strcpy(prompt,"s/key 55 latour1\n");
     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);
     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 /* Return  a skey challenge string for user 'name'. If successful,
     69  * fill in the caller's skey structure and return 0. If unsuccessful
     70  * (e.g., if name is unknown) return -1.
     71  *
     72  * The file read/write pointer is left at the start of the
     73  * record.
     74  */
     75 int
     76 skeychallenge(mp,name, ss)
     77 struct skey *mp;
     78 char *name;
     79 char *ss;
     80 {
     81 	int rval;
     82 
     83 	rval = skeylookup(mp,name);
     84 	switch(rval){
     85 	case -1:	/* File error */
     86 		return -1;
     87 	case 0:		/* Lookup succeeded, issue challenge */
     88                 sprintf(ss, "s/key %d %s",mp->n - 1,mp->seed);
     89 		return 0;
     90 	case 1:		/* User not found */
     91 		fclose(mp->keyfile);
     92 		return -1;
     93 	}
     94 	return -1;	/* Can't happen */
     95 }
     96 
     97 /* Find an entry in the One-time Password database.
     98  * Return codes:
     99  * -1: error in opening database
    100  *  0: entry found, file R/W pointer positioned at beginning of record
    101  *  1: entry not found, file R/W pointer positioned at EOF
    102  */
    103 int
    104 skeylookup(mp,name)
    105 struct skey *mp;
    106 char *name;
    107 {
    108 	int found;
    109 	int len;
    110 	long recstart;
    111 	char *cp;
    112 	struct stat statbuf;
    113 
    114 	/* See if the KEYFILE exists, and create it if not */
    115 
    116 	if(stat(KEYFILE,&statbuf) == -1 && errno == ENOENT) {
    117 		mp->keyfile = fopen(KEYFILE,"w+");
    118 		if(mp->keyfile)
    119 			chmod(KEYFILE, 644);
    120 	} else {
    121 		/* Otherwise open normally for update */
    122 		mp->keyfile = fopen(KEYFILE,"r+");
    123 	}
    124 	if(mp->keyfile == NULL)
    125 		return -1;
    126 
    127 	/* Look up user name in database */
    128 	len = strlen(name);
    129 	if( len > 8 ) len = 8;		/*  Added 8/2/91  -  nmh */
    130 	found = 0;
    131 	while(!feof(mp->keyfile)){
    132 		recstart = ftell(mp->keyfile);
    133 		mp->recstart = recstart;
    134 		if(fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
    135 			break;
    136 		}
    137 		rip(mp->buf);
    138 		if(mp->buf[0] == '#')
    139 			continue;	/* Comment */
    140 		if((mp->logname = strtok(mp->buf," \t")) == NULL)
    141 			continue;
    142 		if((cp = strtok(NULL," \t")) == NULL)
    143 			continue;
    144 		mp->n = atoi(cp);
    145 		if((mp->seed = strtok(NULL," \t")) == NULL)
    146 			continue;
    147 		if((mp->val = strtok(NULL," \t")) == NULL)
    148 			continue;
    149 		if(strlen(mp->logname) == len
    150 		 && strncmp(mp->logname,name,len) == 0){
    151 			found = 1;
    152 			break;
    153 		}
    154 	}
    155 	if(found){
    156 		fseek(mp->keyfile,recstart,0);
    157 		return 0;
    158 	} else
    159 		return 1;
    160 }
    161 /* Verify response to a s/key challenge.
    162  *
    163  * Return codes:
    164  * -1: Error of some sort; database unchanged
    165  *  0:  Verify successful, database updated
    166  *  1:  Verify failed, database unchanged
    167  *
    168  * The database file is always closed by this call.
    169  */
    170 int
    171 skeyverify(mp,response)
    172 struct skey *mp;
    173 char *response;
    174 {
    175  struct timeval startval;
    176  struct timeval endval;
    177 long microsec;
    178 	char key[8];
    179 	char fkey[8];
    180 	char filekey[8];
    181 	time_t now;
    182 	struct tm *tm;
    183 	char tbuf[27],buf[60];
    184 	char me[80];
    185 	int rval;
    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 	/* 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 	setpriority(PRIO_PROCESS, 0, -4);
    217 
    218 	/* reread the file record NOW*/
    219 
    220 	fseek(mp->keyfile,mp->recstart,0);
    221 	if(fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
    222 		setpriority(PRIO_PROCESS, 0, 0);
    223 		fclose(mp->keyfile);
    224 		return -1;
    225 	}
    226 	rip(mp->buf);
    227 	mp->logname = strtok(mp->buf," \t");
    228 	cp = strtok(NULL," \t") ;
    229 	mp->seed = strtok(NULL," \t");
    230 	mp->val = strtok(NULL," \t");
    231 	/* And convert file value to hex for comparison */
    232 	atob8(filekey,mp->val);
    233 
    234 	/* Do actual comparison */
    235         fflush (stdout);
    236 
    237 	if(memcmp(filekey,fkey,8) != 0){
    238 		/* Wrong response */
    239 		setpriority(PRIO_PROCESS, 0, 0);
    240 		fclose(mp->keyfile);
    241 		return 1;
    242 	}
    243 
    244 	/* Update key in database by overwriting entire record. Note
    245 	 * that we must write exactly the same number of bytes as in
    246 	 * the original record (note fixed width field for N)
    247 	 */
    248 	btoa8(mp->val,key);
    249 	mp->n--;
    250 	fseek(mp->keyfile,mp->recstart,0);
    251 	fprintf(mp->keyfile,"%s %04d %-16s %s %-21s\n",mp->logname,mp->n,mp->seed,
    252 	 mp->val, tbuf);
    253 
    254 	fclose(mp->keyfile);
    255 
    256 	setpriority(PRIO_PROCESS, 0, 0);
    257 	return 0;
    258 }
    259 
    260 
    261 /* Convert 8-byte hex-ascii string to binary array
    262  * Returns 0 on success, -1 on error
    263  */
    264 atob8(out,in)
    265 register char *out,*in;
    266 {
    267 	register int i;
    268 	register int val;
    269 
    270 	if (in == NULL || out == NULL)
    271 		return -1;
    272 
    273 	for(i=0;i<8;i++){
    274 		if((in = skipspace(in)) == NULL)
    275 			return -1;
    276 		if((val = htoi(*in++)) == -1)
    277 			return -1;
    278 		*out = val << 4;
    279 
    280 		if((in = skipspace(in)) == NULL)
    281 			return -1;
    282 		if((val = htoi(*in++)) == -1)
    283 			return -1;
    284 		*out++ |= val;
    285 	}
    286 	return 0;
    287 }
    288 
    289 char *
    290 skipspace(cp)
    291 register char *cp;
    292 {
    293 	while(*cp == ' ' || *cp == '\t')
    294 		cp++;
    295 
    296 	if(*cp == '\0')
    297 		return NULL;
    298 	else
    299 		return cp;
    300 }
    301 
    302 /* Convert 8-byte binary array to hex-ascii string */
    303 int
    304 btoa8(out,in)
    305 register char *out,*in;
    306 {
    307 	register int i;
    308 
    309 	if(in == NULL || out == NULL)
    310 		return -1;
    311 
    312 	for(i=0;i<8;i++){
    313 		sprintf(out,"%02x",*in++ & 0xff);
    314 		out += 2;
    315 	}
    316 	return 0;
    317 }
    318 
    319 
    320 /* Convert hex digit to binary integer */
    321 int
    322 htoi(c)
    323 register char c;
    324 {
    325 	if('0' <= c && c <= '9')
    326 		return c - '0';
    327 	if('a' <= c && c <= 'f')
    328 		return 10 + c - 'a';
    329 	if('A' <= c && c <= 'F')
    330 		return 10 + c - 'A';
    331 	return -1;
    332 }
    333 
    334 /*
    335  * skey_haskey ()
    336  *
    337  * Returns: 1 user doesnt exist, -1 fle error, 0 user exists.
    338  *
    339  */
    340 
    341 skey_haskey (username)
    342   char *username;
    343 {
    344   int i;
    345   struct skey skey;
    346 
    347   return (skeylookup (&skey, username));
    348 }
    349 
    350 /*
    351  * skey_keyinfo ()
    352  *
    353  * Returns the current sequence number and
    354  * seed for the passed user.
    355  *
    356  */
    357 char *skey_keyinfo (username)
    358   char *username;
    359 {
    360   int i;
    361   static char str [50];
    362 
    363   struct skey skey;
    364 
    365   i = skeychallenge (&skey, username, str);
    366   if (i == -1)
    367      return 0;
    368 
    369   return str;
    370 }
    371 
    372 /*
    373  * skey_passcheck ()
    374  *
    375  * Check to see if answer is the correct one to the current
    376  * challenge.
    377  *
    378  * Returns: 0 success, -1 failure
    379  *
    380  */
    381 
    382 skey_passcheck (username, passwd)
    383   char *username, *passwd;
    384 {
    385   int i;
    386   struct skey skey;
    387 
    388   i = skeylookup (&skey, username);
    389 
    390   if (i == -1 || i == 1)
    391       return -1;
    392 
    393   if (skeyverify (&skey, passwd) == 0)
    394       return skey.n;
    395 
    396   return -1;
    397 }
    398 
    399 /*
    400  * skey_authenticate ()
    401  *
    402  * Used when calling program will allow input of the user's
    403  * response to the challenge.
    404  *
    405  * Returns: 0 success, -1 failure
    406  *
    407  */
    408 
    409 skey_authenticate (username)
    410   char *username;
    411 {
    412   int i;
    413   char pbuf [256], skeyprompt [50];
    414   struct skey skey;
    415 
    416   /* Attempt a S/Key challenge */
    417   i = skeychallenge (&skey, username, skeyprompt);
    418 
    419   if (i == -2)
    420     return 0;
    421 
    422   printf ("[%s]\n", skeyprompt);
    423   fflush (stdout);
    424 
    425   printf ("Response: ");
    426   readpass (pbuf, sizeof (pbuf));
    427   rip (pbuf);
    428 
    429   /* Is it a valid response? */
    430   if (i == 0 && skeyverify (&skey, pbuf) == 0)
    431   {
    432     if (skey.n < 5)
    433     {
    434       printf ("\nWarning! Key initialization needed soon.  ");
    435       printf ("(%d logins left)\n", skey.n);
    436     }
    437     return 0;
    438   }
    439   return -1;
    440 }
    441 
    442