Home | History | Annotate | Line # | Download | only in libskey
skeylogin.c revision 1.1
      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.1 1994/05/21 05:46:16 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 	if(stat(KEYFILE,&statbuf) == -1 && errno == ENOENT){
    116 		mp->keyfile = fopen(KEYFILE,"w+");
    117 	} else {
    118 		/* Otherwise open normally for update */
    119 		mp->keyfile = fopen(KEYFILE,"r+");
    120 	}
    121 	if(mp->keyfile == NULL)
    122 		return -1;
    123 
    124 	/* Look up user name in database */
    125 	len = strlen(name);
    126 	if( len > 8 ) len = 8;		/*  Added 8/2/91  -  nmh */
    127 	found = 0;
    128 	while(!feof(mp->keyfile)){
    129 		recstart = ftell(mp->keyfile);
    130 		mp->recstart = recstart;
    131 		if(fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
    132 			break;
    133 		}
    134 		rip(mp->buf);
    135 		if(mp->buf[0] == '#')
    136 			continue;	/* Comment */
    137 		if((mp->logname = strtok(mp->buf," \t")) == NULL)
    138 			continue;
    139 		if((cp = strtok(NULL," \t")) == NULL)
    140 			continue;
    141 		mp->n = atoi(cp);
    142 		if((mp->seed = strtok(NULL," \t")) == NULL)
    143 			continue;
    144 		if((mp->val = strtok(NULL," \t")) == NULL)
    145 			continue;
    146 		if(strlen(mp->logname) == len
    147 		 && strncmp(mp->logname,name,len) == 0){
    148 			found = 1;
    149 			break;
    150 		}
    151 	}
    152 	if(found){
    153 		fseek(mp->keyfile,recstart,0);
    154 		return 0;
    155 	} else
    156 		return 1;
    157 }
    158 /* Verify response to a s/key challenge.
    159  *
    160  * Return codes:
    161  * -1: Error of some sort; database unchanged
    162  *  0:  Verify successful, database updated
    163  *  1:  Verify failed, database unchanged
    164  *
    165  * The database file is always closed by this call.
    166  */
    167 int
    168 skeyverify(mp,response)
    169 struct skey *mp;
    170 char *response;
    171 {
    172  struct timeval startval;
    173  struct timeval endval;
    174 long microsec;
    175 	char key[8];
    176 	char fkey[8];
    177 	char filekey[8];
    178 	time_t now;
    179 	struct tm *tm;
    180 	char tbuf[27],buf[60];
    181 	char me[80];
    182 	int rval;
    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 	/* in order to make the window of update as short as possible
    208            we must do the comparison here and if OK write it back
    209            other wise the same password can be used twice to get in
    210   	   to the system
    211 	*/
    212 
    213 	setpriority(PRIO_PROCESS, 0, -4);
    214 
    215 	/* reread the file record NOW*/
    216 
    217 	fseek(mp->keyfile,mp->recstart,0);
    218 	if(fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
    219 		setpriority(PRIO_PROCESS, 0, 0);
    220 		fclose(mp->keyfile);
    221 		return -1;
    222 	}
    223 	rip(mp->buf);
    224 	mp->logname = strtok(mp->buf," \t");
    225 	cp = strtok(NULL," \t") ;
    226 	mp->seed = strtok(NULL," \t");
    227 	mp->val = strtok(NULL," \t");
    228 	/* And convert file value to hex for comparison */
    229 	atob8(filekey,mp->val);
    230 
    231 	/* Do actual comparison */
    232         fflush (stdout);
    233 
    234 	if(memcmp(filekey,fkey,8) != 0){
    235 		/* Wrong response */
    236 		setpriority(PRIO_PROCESS, 0, 0);
    237 		fclose(mp->keyfile);
    238 		return 1;
    239 	}
    240 
    241 	/* Update key in database by overwriting entire record. Note
    242 	 * that we must write exactly the same number of bytes as in
    243 	 * the original record (note fixed width field for N)
    244 	 */
    245 	btoa8(mp->val,key);
    246 	mp->n--;
    247 	fseek(mp->keyfile,mp->recstart,0);
    248 	fprintf(mp->keyfile,"%s %04d %-16s %s %-21s\n",mp->logname,mp->n,mp->seed,
    249 	 mp->val, tbuf);
    250 
    251 	fclose(mp->keyfile);
    252 
    253 	setpriority(PRIO_PROCESS, 0, 0);
    254 	return 0;
    255 }
    256 
    257 
    258 /* Convert 8-byte hex-ascii string to binary array
    259  * Returns 0 on success, -1 on error
    260  */
    261 atob8(out,in)
    262 register char *out,*in;
    263 {
    264 	register int i;
    265 	register int val;
    266 
    267 	if (in == NULL || out == NULL)
    268 		return -1;
    269 
    270 	for(i=0;i<8;i++){
    271 		if((in = skipspace(in)) == NULL)
    272 			return -1;
    273 		if((val = htoi(*in++)) == -1)
    274 			return -1;
    275 		*out = val << 4;
    276 
    277 		if((in = skipspace(in)) == NULL)
    278 			return -1;
    279 		if((val = htoi(*in++)) == -1)
    280 			return -1;
    281 		*out++ |= val;
    282 	}
    283 	return 0;
    284 }
    285 
    286 char *
    287 skipspace(cp)
    288 register char *cp;
    289 {
    290 	while(*cp == ' ' || *cp == '\t')
    291 		cp++;
    292 
    293 	if(*cp == '\0')
    294 		return NULL;
    295 	else
    296 		return cp;
    297 }
    298 
    299 /* Convert 8-byte binary array to hex-ascii string */
    300 int
    301 btoa8(out,in)
    302 register char *out,*in;
    303 {
    304 	register int i;
    305 
    306 	if(in == NULL || out == NULL)
    307 		return -1;
    308 
    309 	for(i=0;i<8;i++){
    310 		sprintf(out,"%02x",*in++ & 0xff);
    311 		out += 2;
    312 	}
    313 	return 0;
    314 }
    315 
    316 
    317 /* Convert hex digit to binary integer */
    318 int
    319 htoi(c)
    320 register char c;
    321 {
    322 	if('0' <= c && c <= '9')
    323 		return c - '0';
    324 	if('a' <= c && c <= 'f')
    325 		return 10 + c - 'a';
    326 	if('A' <= c && c <= 'F')
    327 		return 10 + c - 'A';
    328 	return -1;
    329 }
    330 
    331 /*
    332  * skey_haskey ()
    333  *
    334  * Returns: 1 user doesnt exist, -1 fle error, 0 user exists.
    335  *
    336  */
    337 
    338 skey_haskey (username)
    339   char *username;
    340 {
    341   int i;
    342   struct skey skey;
    343 
    344   return (skeylookup (&skey, username));
    345 }
    346 
    347 /*
    348  * skey_keyinfo ()
    349  *
    350  * Returns the current sequence number and
    351  * seed for the passed user.
    352  *
    353  */
    354 char *skey_keyinfo (username)
    355   char *username;
    356 {
    357   int i;
    358   static char str [50];
    359 
    360   struct skey skey;
    361 
    362   i = skeychallenge (&skey, username, str);
    363   if (i == -1)
    364      return 0;
    365 
    366   return str;
    367 }
    368 
    369 /*
    370  * skey_passcheck ()
    371  *
    372  * Check to see if answer is the correct one to the current
    373  * challenge.
    374  *
    375  * Returns: 0 success, -1 failure
    376  *
    377  */
    378 
    379 skey_passcheck (username, passwd)
    380   char *username, *passwd;
    381 {
    382   int i;
    383   struct skey skey;
    384 
    385   i = skeylookup (&skey, username);
    386 
    387   if (i == -1 || i == 1)
    388       return -1;
    389 
    390   if (skeyverify (&skey, passwd) == 0)
    391       return skey.n;
    392 
    393   return -1;
    394 }
    395 
    396 /*
    397  * skey_authenticate ()
    398  *
    399  * Used when calling program will allow input of the user's
    400  * response to the challenge.
    401  *
    402  * Returns: 0 success, -1 failure
    403  *
    404  */
    405 
    406 skey_authenticate (username)
    407   char *username;
    408 {
    409   int i;
    410   char pbuf [256], skeyprompt [50];
    411   struct skey skey;
    412 
    413   /* Attempt a S/Key challenge */
    414   i = skeychallenge (&skey, username, skeyprompt);
    415 
    416   if (i == -2)
    417     return 0;
    418 
    419   printf ("[%s]\n", skeyprompt);
    420   fflush (stdout);
    421 
    422   printf ("Response: ");
    423   readpass (pbuf, sizeof (pbuf));
    424   rip (pbuf);
    425 
    426   /* Is it a valid response? */
    427   if (i == 0 && skeyverify (&skey, pbuf) == 0)
    428   {
    429     if (skey.n < 5)
    430     {
    431       printf ("\nWarning! Key initialization needed soon.  ");
    432       printf ("(%d logins left)\n", skey.n);
    433     }
    434     return 0;
    435   }
    436   return -1;
    437 }
    438 
    439