skeylogin.c revision 1.3
1/* S/KEY v1.1b (skeylogin.c) 2 * 3 * Authors: 4 * Neil M. Haller <nmh@thumper.bellcore.com> 5 * Philip R. Karn <karn@chicago.qualcomm.com> 6 * John S. Walden <jsw@thumper.bellcore.com> 7 * Scott Chasin <chasin@crimelab.com> 8 * 9 * S/KEY verification check, lookups, and authentication. 10 * 11 * $Id: skeylogin.c,v 1.3 1996/09/27 15:38:59 millert 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#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26#include <sys/types.h> 27#include <sys/stat.h> 28#include <time.h> 29#include <errno.h> 30 31#include "skey.h" 32 33#define _PATH_KEYFILE "/etc/skeykeys" 34 35char *skipspace __ARGS((char *)); 36int skeylookup __ARGS((struct skey *, char *)); 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 */ 45int 46getskeyprompt(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 (void)strcpy(prompt, "s/key MD0 55 latour1\n"); 56 switch (rval) { 57 case -1: /* File error */ 58 return -1; 59 case 0: /* Lookup succeeded, return challenge */ 60 (void)sprintf(prompt, "s/key MD%d %d %s\n", skey_get_MDX(), 61 mp->n - 1, mp->seed); 62 return 0; 63 case 1: /* User not found */ 64 (void)fclose(mp->keyfile); 65 return -1; 66 } 67 return -1; /* Can't happen */ 68} 69 70/* Return a skey challenge string for user 'name'. If successful, 71 * fill in the caller's skey structure and return 0. If unsuccessful 72 * (e.g., if name is unknown) return -1. 73 * 74 * The file read/write pointer is left at the start of the 75 * record. 76 */ 77int 78skeychallenge(mp,name, ss) 79 struct skey *mp; 80 char *name; 81 char *ss; 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)sprintf(ss, "s/key MD%d %d %s", skey_get_MDX(), 91 mp->n - 1, mp->seed); 92 return 0; 93 case 1: /* User not found */ 94 (void)fclose(mp->keyfile); 95 return -1; 96 } 97 return -1; /* Can't happen */ 98} 99 100/* Find an entry in the One-time Password database. 101 * Return codes: 102 * -1: error in opening database 103 * 0: entry found, file R/W pointer positioned at beginning of record 104 * 1: entry not found, file R/W pointer positioned at EOF 105 */ 106int 107skeylookup(mp,name) 108 struct skey *mp; 109 char *name; 110{ 111 int found, len; 112 long recstart = 0; 113 char *cp; 114 struct stat statbuf; 115 116 /* See if _PATH_KEYFILE exists, and create it if not */ 117 if (stat(_PATH_KEYFILE, &statbuf) == -1 && errno == ENOENT) { 118 mp->keyfile = fopen(_PATH_KEYFILE, "w+"); 119 if (mp->keyfile) 120 chmod(_PATH_KEYFILE, 0644); 121 } else { 122 /* Otherwise open normally for update */ 123 mp->keyfile = fopen(_PATH_KEYFILE, "r+"); 124 } 125 if (mp->keyfile == NULL) 126 return -1; 127 128 /* Look up user name in database */ 129 len = strlen(name); 130 /* XXX - do we really want to limit it to 8 char usernames? */ 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 /* Set MDX if specified, else use MD4 */ 148 if (cp[0] == 'M' && cp[1] == 'D') { 149 skey_set_MDX(atoi(&cp[2])); 150 if ((cp = strtok(NULL, " \t")) == NULL) 151 continue; 152 } else { 153 skey_set_MDX(4); 154 } 155 mp->n = atoi(cp); 156 if ((mp->seed = strtok(NULL, " \t")) == NULL) 157 continue; 158 if ((mp->val = strtok(NULL, " \t")) == NULL) 159 continue; 160 if (strlen(mp->logname) == len && 161 strncmp(mp->logname, name, len) == 0) { 162 found = 1; 163 break; 164 } 165 } 166 if (found) { 167 (void)fseek(mp->keyfile, recstart, SEEK_SET); 168 return 0; 169 } else 170 return 1; 171} 172 173/* Verify response to a s/key challenge. 174 * 175 * Return codes: 176 * -1: Error of some sort; database unchanged 177 * 0: Verify successful, database updated 178 * 1: Verify failed, database unchanged 179 * 180 * The database file is always closed by this call. 181 */ 182int 183skeyverify(mp,response) 184 struct skey *mp; 185 char *response; 186{ 187 char key[8]; 188 char fkey[8]; 189 char filekey[8]; 190 time_t now; 191 struct tm *tm; 192 char tbuf[27]; 193 char *cp; 194 195 time(&now); 196 tm = localtime(&now); 197 (void)strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm); 198 199 if (response == NULL) { 200 (void)fclose(mp->keyfile); 201 return -1; 202 } 203 rip(response); 204 205 /* Convert response to binary */ 206 if (etob(key, response) != 1 && atob8(key, response) != 0) { 207 /* Neither english words or ascii hex */ 208 (void)fclose(mp->keyfile); 209 return -1; 210 } 211 212 /* Compute fkey = f(key) */ 213 (void)memcpy(fkey,key,sizeof(key)); 214 (void)fflush(stdout); 215 f(fkey); 216 217 /* 218 * in order to make the window of update as short as possible 219 * we must do the comparison here and if OK write it back 220 * other wise the same password can be used twice to get in 221 * to the system 222 */ 223 (void)setpriority(PRIO_PROCESS, 0, -4); 224 225 /* reread the file record NOW */ 226 (void)fseek(mp->keyfile, mp->recstart, SEEK_SET); 227 if (fgets(mp->buf, sizeof(mp->buf), mp->keyfile) != mp->buf) { 228 (void)setpriority(PRIO_PROCESS, 0, 0); 229 (void)fclose(mp->keyfile); 230 return -1; 231 } 232 rip(mp->buf); 233 mp->logname = strtok(mp->buf," \t"); 234 cp = strtok(NULL," \t") ; 235 cp = strtok(NULL," \t") ; 236 mp->seed = strtok(NULL," \t"); 237 mp->val = strtok(NULL," \t"); 238 /* And convert file value to hex for comparison */ 239 atob8(filekey,mp->val); 240 241 /* Do actual comparison */ 242 if (memcmp(filekey,fkey,8) != 0){ 243 /* Wrong response */ 244 (void)setpriority(PRIO_PROCESS, 0, 0); 245 (void)fclose(mp->keyfile); 246 return 1; 247 } 248 249 /* 250 * Update key in database by overwriting entire record. Note 251 * that we must write exactly the same number of bytes as in 252 * the original record (note fixed width field for N) 253 */ 254 btoa8(mp->val,key); 255 mp->n--; 256 (void)fseek(mp->keyfile, mp->recstart, SEEK_SET); 257 (void)fprintf(mp->keyfile, "%s MD%d %04d %-16s %s %-21s\n", 258 mp->logname, skey_get_MDX(), mp->n, mp->seed, mp->val, tbuf); 259 260 (void)fclose(mp->keyfile); 261 262 (void)setpriority(PRIO_PROCESS, 0, 0); 263 return 0; 264} 265 266/* 267 * skey_haskey() 268 * 269 * Returns: 1 user doesnt exist, -1 fle error, 0 user exists. 270 * 271 */ 272int 273skey_haskey(username) 274 char *username; 275{ 276 struct skey skey; 277 278 return(skeylookup(&skey, username)); 279} 280 281/* 282 * skey_keyinfo() 283 * 284 * Returns the current sequence number and 285 * seed for the passed user. 286 * 287 */ 288char * 289skey_keyinfo(username) 290 char *username; 291{ 292 int i; 293 static char str[50]; 294 struct skey skey; 295 296 i = skeychallenge(&skey, username, str); 297 if (i == -1) 298 return 0; 299 300 return str; 301} 302 303/* 304 * skey_passcheck() 305 * 306 * Check to see if answer is the correct one to the current 307 * challenge. 308 * 309 * Returns: 0 success, -1 failure 310 * 311 */ 312int 313skey_passcheck(username, passwd) 314 char *username, *passwd; 315{ 316 int i; 317 struct skey skey; 318 319 i = skeylookup(&skey, username); 320 if (i == -1 || i == 1) 321 return -1; 322 323 if (skeyverify(&skey, passwd) == 0) 324 return skey.n; 325 326 return -1; 327} 328 329/* 330 * skey_authenticate() 331 * 332 * Used when calling program will allow input of the user's 333 * response to the challenge. 334 * 335 * Returns: 0 success, -1 failure 336 * 337 */ 338int 339skey_authenticate(username) 340 char *username; 341{ 342 int i; 343 char pbuf[256], skeyprompt[50]; 344 struct skey skey; 345 346 /* Attempt an S/Key challenge */ 347 i = skeychallenge(&skey, username, skeyprompt); 348 349 if (i == -2) 350 return 0; 351 352 (void)fprintf(stderr, "[%s]\n", skeyprompt); 353 (void)fflush(stderr); 354 355 (void)fputs("Response: ", stderr); 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 (void)fprintf(stderr, 363 "\nWarning! Key initialization needed soon. (%d logins left)\n", 364 skey.n); 365 } 366 return 0; 367 } 368 return -1; 369} 370 371/* Comment out user's entry in the s/key database 372 * 373 * Return codes: 374 * -1: Write error; database unchanged 375 * 0: Database updated 376 * 377 * The database file is always closed by this call. 378 */ 379int 380skeyzero(mp, response) 381 struct skey *mp; 382 char *response; 383{ 384 /* 385 * Seek to the right place and write comment character 386 * which effectively zero's out the entry. 387 */ 388 (void)fseek(mp->keyfile, mp->recstart, SEEK_SET); 389 if (fputc('#', mp->keyfile) == EOF) { 390 fclose(mp->keyfile); 391 return -1; 392 } 393 394 (void)fclose(mp->keyfile); 395 396 return 0; 397} 398