1 1.19 perry /* $NetBSD: skeylogin.c,v 1.19 2005/02/04 16:13:14 perry Exp $ */ 2 1.6 thorpej 3 1.1 deraadt /* S/KEY v1.1b (skeylogin.c) 4 1.1 deraadt * 5 1.1 deraadt * Authors: 6 1.1 deraadt * Neil M. Haller <nmh (at) thumper.bellcore.com> 7 1.1 deraadt * Philip R. Karn <karn (at) chicago.qualcomm.com> 8 1.1 deraadt * John S. Walden <jsw (at) thumper.bellcore.com> 9 1.1 deraadt * Scott Chasin <chasin (at) crimelab.com> 10 1.1 deraadt * 11 1.14 mjl * Modifications: 12 1.14 mjl * Todd C. Miller <Todd.Miller (at) courtesan.com> 13 1.14 mjl * Angelos D. Keromytis <adk (at) adk.gr> 14 1.14 mjl * 15 1.1 deraadt * S/KEY verification check, lookups, and authentication. 16 1.1 deraadt */ 17 1.18 lukem 18 1.18 lukem #include <sys/cdefs.h> 19 1.19 perry __RCSID("$NetBSD: skeylogin.c,v 1.19 2005/02/04 16:13:14 perry Exp $"); 20 1.1 deraadt 21 1.1 deraadt #include <sys/param.h> 22 1.1 deraadt #include <sys/stat.h> 23 1.1 deraadt #include <sys/time.h> 24 1.1 deraadt #include <sys/resource.h> 25 1.14 mjl #include <sys/types.h> 26 1.1 deraadt 27 1.14 mjl #include <ctype.h> 28 1.14 mjl #include <err.h> 29 1.14 mjl #include <errno.h> 30 1.14 mjl #include <fcntl.h> 31 1.14 mjl #include <paths.h> 32 1.1 deraadt #include <stdio.h> 33 1.5 pk #include <stdlib.h> 34 1.1 deraadt #include <string.h> 35 1.1 deraadt #include <time.h> 36 1.14 mjl #include <unistd.h> 37 1.5 pk 38 1.1 deraadt #include "skey.h" 39 1.1 deraadt 40 1.14 mjl #define OTP_FMT "otp-%.*s %d %.*s" 41 1.1 deraadt 42 1.1 deraadt /* Issue a skey challenge for user 'name'. If successful, 43 1.1 deraadt * fill in the caller's skey structure and return 0. If unsuccessful 44 1.1 deraadt * (e.g., if name is unknown) return -1. 45 1.1 deraadt * 46 1.1 deraadt * The file read/write pointer is left at the start of the 47 1.1 deraadt * record. 48 1.1 deraadt */ 49 1.14 mjl int getskeyprompt(struct skey *mp, char *name, char *prompt) 50 1.1 deraadt { 51 1.1 deraadt int rval; 52 1.1 deraadt 53 1.1 deraadt sevenbit(name); 54 1.14 mjl rval = skeylookup(mp, name); 55 1.14 mjl 56 1.14 mjl *prompt = '\0'; 57 1.5 pk switch (rval) { 58 1.1 deraadt case -1: /* File error */ 59 1.1 deraadt return -1; 60 1.1 deraadt case 0: /* Lookup succeeded, return challenge */ 61 1.14 mjl sprintf(prompt, OTP_FMT "\n", 62 1.14 mjl SKEY_MAX_HASHNAME_LEN, skey_get_algorithm(), 63 1.14 mjl mp->n - 1, SKEY_MAX_SEED_LEN, mp->seed); 64 1.1 deraadt return 0; 65 1.1 deraadt case 1: /* User not found */ 66 1.1 deraadt fclose(mp->keyfile); 67 1.14 mjl mp->keyfile = NULL; 68 1.1 deraadt return -1; 69 1.1 deraadt } 70 1.1 deraadt return -1; /* Can't happen */ 71 1.5 pk } 72 1.5 pk 73 1.1 deraadt /* Return a skey challenge string for user 'name'. If successful, 74 1.1 deraadt * fill in the caller's skey structure and return 0. If unsuccessful 75 1.1 deraadt * (e.g., if name is unknown) return -1. 76 1.1 deraadt * 77 1.1 deraadt * The file read/write pointer is left at the start of the 78 1.1 deraadt * record. 79 1.1 deraadt */ 80 1.14 mjl int skeychallenge(struct skey *mp, const char *name, char *ss, size_t sslen) 81 1.1 deraadt { 82 1.1 deraadt int rval; 83 1.1 deraadt 84 1.14 mjl rval = skeylookup(mp, name); 85 1.14 mjl 86 1.14 mjl *ss = '\0'; 87 1.1 deraadt switch(rval){ 88 1.1 deraadt case -1: /* File error */ 89 1.1 deraadt return -1; 90 1.1 deraadt case 0: /* Lookup succeeded, issue challenge */ 91 1.14 mjl snprintf(ss, sslen, OTP_FMT, SKEY_MAX_HASHNAME_LEN, 92 1.14 mjl skey_get_algorithm(), mp->n - 1, 93 1.14 mjl SKEY_MAX_SEED_LEN, mp->seed); 94 1.1 deraadt return 0; 95 1.1 deraadt case 1: /* User not found */ 96 1.1 deraadt fclose(mp->keyfile); 97 1.14 mjl mp->keyfile = NULL; 98 1.1 deraadt return -1; 99 1.1 deraadt } 100 1.1 deraadt return -1; /* Can't happen */ 101 1.10 simonb } 102 1.1 deraadt 103 1.14 mjl static FILE *openSkey(void) 104 1.14 mjl { 105 1.14 mjl struct stat statbuf; 106 1.14 mjl FILE *keyfile = NULL; 107 1.14 mjl 108 1.14 mjl /* Open _PATH_SKEYKEYS if it exists, else return an error */ 109 1.14 mjl if (stat(_PATH_SKEYKEYS, &statbuf) == 0 && 110 1.14 mjl (keyfile = fopen(_PATH_SKEYKEYS, "r+"))) { 111 1.14 mjl if ((statbuf.st_mode & 0007777) != 0600) 112 1.14 mjl fchmod(fileno(keyfile), 0600); 113 1.14 mjl } else { 114 1.14 mjl keyfile = NULL; 115 1.14 mjl } 116 1.14 mjl 117 1.14 mjl return keyfile; 118 1.14 mjl } 119 1.14 mjl 120 1.1 deraadt /* Find an entry in the One-time Password database. 121 1.1 deraadt * Return codes: 122 1.1 deraadt * -1: error in opening database 123 1.1 deraadt * 0: entry found, file R/W pointer positioned at beginning of record 124 1.1 deraadt * 1: entry not found, file R/W pointer positioned at EOF 125 1.1 deraadt */ 126 1.14 mjl int skeylookup(struct skey *mp, const char *name) 127 1.1 deraadt { 128 1.14 mjl int found = 0; 129 1.8 christos long recstart = 0; 130 1.14 mjl const char *ht = NULL; 131 1.17 itojun char *last; 132 1.1 deraadt 133 1.14 mjl if(!(mp->keyfile = openSkey())) 134 1.14 mjl return(-1); 135 1.1 deraadt 136 1.1 deraadt /* Look up user name in database */ 137 1.5 pk while (!feof(mp->keyfile)) { 138 1.14 mjl char *cp; 139 1.14 mjl 140 1.1 deraadt recstart = ftell(mp->keyfile); 141 1.1 deraadt mp->recstart = recstart; 142 1.14 mjl if (fgets(mp->buf, sizeof(mp->buf), mp->keyfile) != mp->buf) 143 1.1 deraadt break; 144 1.14 mjl 145 1.1 deraadt rip(mp->buf); 146 1.5 pk if (mp->buf[0] == '#') 147 1.1 deraadt continue; /* Comment */ 148 1.17 itojun if ((mp->logname = strtok_r(mp->buf, " \t", &last)) == NULL) 149 1.1 deraadt continue; 150 1.17 itojun if ((cp = strtok_r(NULL, " \t", &last)) == NULL) 151 1.1 deraadt continue; 152 1.14 mjl /* Save hash type if specified, else use md4 */ 153 1.15 itohy if (isalpha((u_char) *cp)) { 154 1.14 mjl ht = cp; 155 1.17 itojun if ((cp = strtok_r(NULL, " \t", &last)) == NULL) 156 1.14 mjl continue; 157 1.14 mjl } else { 158 1.14 mjl ht = "md4"; 159 1.14 mjl } 160 1.1 deraadt mp->n = atoi(cp); 161 1.17 itojun if ((mp->seed = strtok_r(NULL, " \t", &last)) == NULL) 162 1.1 deraadt continue; 163 1.17 itojun if ((mp->val = strtok_r(NULL, " \t", &last)) == NULL) 164 1.1 deraadt continue; 165 1.14 mjl if (strcmp(mp->logname, name) == 0) { 166 1.1 deraadt found = 1; 167 1.1 deraadt break; 168 1.1 deraadt } 169 1.1 deraadt } 170 1.5 pk if (found) { 171 1.14 mjl fseek(mp->keyfile, recstart, SEEK_SET); 172 1.14 mjl /* Set hash type */ 173 1.14 mjl if (ht && skey_set_algorithm(ht) == NULL) { 174 1.14 mjl warnx("Unknown hash algorithm %s, using %s", ht, 175 1.14 mjl skey_get_algorithm()); 176 1.14 mjl } 177 1.14 mjl return(0); 178 1.14 mjl } else { 179 1.14 mjl return(1); 180 1.14 mjl } 181 1.14 mjl } 182 1.14 mjl 183 1.14 mjl /* Get the next entry in the One-time Password database. 184 1.14 mjl * Return codes: 185 1.14 mjl * -1: error in opening database 186 1.14 mjl * 0: next entry found and stored in mp 187 1.14 mjl * 1: no more entries, file R/W pointer positioned at EOF 188 1.14 mjl */ 189 1.14 mjl int skeygetnext(struct skey *mp) 190 1.14 mjl { 191 1.14 mjl long recstart = 0; 192 1.17 itojun char *last; 193 1.14 mjl 194 1.14 mjl /* Open _PATH_SKEYKEYS if it exists, else return an error */ 195 1.14 mjl if (mp->keyfile == NULL) { 196 1.14 mjl if(!(mp->keyfile = openSkey())) 197 1.14 mjl return(-1); 198 1.14 mjl } 199 1.14 mjl 200 1.14 mjl /* Look up next user in database */ 201 1.14 mjl while (!feof(mp->keyfile)) { 202 1.14 mjl char *cp; 203 1.14 mjl 204 1.14 mjl recstart = ftell(mp->keyfile); 205 1.14 mjl mp->recstart = recstart; 206 1.14 mjl if (fgets(mp->buf, sizeof(mp->buf), mp->keyfile) != mp->buf) 207 1.14 mjl break; 208 1.14 mjl rip(mp->buf); 209 1.14 mjl if (mp->buf[0] == '#') 210 1.14 mjl continue; /* Comment */ 211 1.17 itojun if ((mp->logname = strtok_r(mp->buf, " \t", &last)) == NULL) 212 1.14 mjl continue; 213 1.17 itojun if ((cp = strtok_r(NULL, " \t", &last)) == NULL) 214 1.14 mjl continue; 215 1.14 mjl /* Save hash type if specified, else use md4 */ 216 1.15 itohy if (isalpha((u_char) *cp)) { 217 1.17 itojun if ((cp = strtok_r(NULL, " \t", &last)) == NULL) 218 1.14 mjl continue; 219 1.14 mjl } 220 1.14 mjl mp->n = atoi(cp); 221 1.17 itojun if ((mp->seed = strtok_r(NULL, " \t", &last)) == NULL) 222 1.14 mjl continue; 223 1.17 itojun if ((mp->val = strtok_r(NULL, " \t", &last)) == NULL) 224 1.14 mjl continue; 225 1.14 mjl /* Got a real entry */ 226 1.14 mjl break; 227 1.14 mjl } 228 1.14 mjl return(feof(mp->keyfile)); 229 1.1 deraadt } 230 1.14 mjl 231 1.1 deraadt /* Verify response to a s/key challenge. 232 1.1 deraadt * 233 1.1 deraadt * Return codes: 234 1.1 deraadt * -1: Error of some sort; database unchanged 235 1.1 deraadt * 0: Verify successful, database updated 236 1.1 deraadt * 1: Verify failed, database unchanged 237 1.1 deraadt * 238 1.1 deraadt * The database file is always closed by this call. 239 1.1 deraadt */ 240 1.14 mjl 241 1.14 mjl int skeyverify(struct skey *mp, char *response) 242 1.14 mjl { 243 1.14 mjl char key[SKEY_BINKEY_SIZE]; 244 1.14 mjl char fkey[SKEY_BINKEY_SIZE]; 245 1.14 mjl char filekey[SKEY_BINKEY_SIZE]; 246 1.1 deraadt time_t now; 247 1.1 deraadt struct tm *tm; 248 1.5 pk char tbuf[27]; 249 1.17 itojun char *cp, *last; 250 1.14 mjl int i, rval; 251 1.1 deraadt 252 1.1 deraadt time(&now); 253 1.1 deraadt tm = localtime(&now); 254 1.1 deraadt strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm); 255 1.1 deraadt 256 1.5 pk if (response == NULL) { 257 1.1 deraadt fclose(mp->keyfile); 258 1.14 mjl mp->keyfile = NULL; 259 1.1 deraadt return -1; 260 1.1 deraadt } 261 1.5 pk rip(response); 262 1.1 deraadt 263 1.1 deraadt /* Convert response to binary */ 264 1.5 pk if (etob(key, response) != 1 && atob8(key, response) != 0) { 265 1.1 deraadt /* Neither english words or ascii hex */ 266 1.1 deraadt fclose(mp->keyfile); 267 1.14 mjl mp->keyfile = NULL; 268 1.1 deraadt return -1; 269 1.1 deraadt } 270 1.1 deraadt 271 1.1 deraadt /* Compute fkey = f(key) */ 272 1.14 mjl memcpy(fkey, key, sizeof(key)); 273 1.14 mjl fflush(stdout); 274 1.1 deraadt 275 1.1 deraadt f(fkey); 276 1.14 mjl 277 1.5 pk /* 278 1.14 mjl * Obtain an exclusive lock on the key file so the same password 279 1.14 mjl * cannot be used twice to get in to the system. 280 1.5 pk */ 281 1.14 mjl for (i = 0; i < 300; i++) { 282 1.14 mjl if ((rval = flock(fileno(mp->keyfile), LOCK_EX|LOCK_NB)) == 0 || 283 1.14 mjl errno != EWOULDBLOCK) 284 1.14 mjl break; 285 1.14 mjl usleep(100000); /* Sleep for 0.1 seconds */ 286 1.14 mjl } 287 1.14 mjl if (rval == -1) { /* Can't get exclusive lock */ 288 1.14 mjl errno = EAGAIN; 289 1.14 mjl return(-1); 290 1.13 is } 291 1.1 deraadt 292 1.14 mjl /* Reread the file record NOW */ 293 1.1 deraadt 294 1.14 mjl fseek(mp->keyfile, mp->recstart, SEEK_SET); 295 1.14 mjl if (fgets(mp->buf, sizeof(mp->buf), mp->keyfile) != mp->buf) { 296 1.1 deraadt fclose(mp->keyfile); 297 1.14 mjl mp->keyfile = NULL; 298 1.1 deraadt return -1; 299 1.1 deraadt } 300 1.1 deraadt rip(mp->buf); 301 1.17 itojun if ((mp->logname = strtok_r(mp->buf, " \t", &last)) == NULL) 302 1.17 itojun goto verify_failure; 303 1.17 itojun if ((cp = strtok_r(NULL, " \t", &last)) == NULL) 304 1.17 itojun goto verify_failure; 305 1.15 itohy if (isalpha((u_char) *cp)) 306 1.17 itojun if ((cp = strtok_r(NULL, " \t", &last)) == NULL) 307 1.17 itojun goto verify_failure; 308 1.17 itojun if ((mp->seed = strtok_r(NULL, " \t", &last)) == NULL) 309 1.17 itojun goto verify_failure; 310 1.17 itojun if ((mp->val = strtok_r(NULL, " \t", &last)) == NULL) 311 1.17 itojun goto verify_failure; 312 1.1 deraadt /* And convert file value to hex for comparison */ 313 1.14 mjl atob8(filekey, mp->val); 314 1.1 deraadt 315 1.1 deraadt /* Do actual comparison */ 316 1.14 mjl if (memcmp(filekey, fkey, SKEY_BINKEY_SIZE) != 0) { 317 1.1 deraadt /* Wrong response */ 318 1.1 deraadt fclose(mp->keyfile); 319 1.14 mjl mp->keyfile = NULL; 320 1.1 deraadt return 1; 321 1.1 deraadt } 322 1.1 deraadt 323 1.5 pk /* 324 1.5 pk * Update key in database by overwriting entire record. Note 325 1.1 deraadt * that we must write exactly the same number of bytes as in 326 1.1 deraadt * the original record (note fixed width field for N) 327 1.1 deraadt */ 328 1.14 mjl btoa8(mp->val, key); 329 1.1 deraadt mp->n--; 330 1.14 mjl fseek(mp->keyfile, mp->recstart, SEEK_SET); 331 1.14 mjl /* Don't save algorithm type for md4 (keep record length same) */ 332 1.14 mjl if (strcmp(skey_get_algorithm(), "md4") == 0) 333 1.14 mjl (void)fprintf(mp->keyfile, "%s %04d %-16s %s %-21s\n", 334 1.14 mjl mp->logname, mp->n, mp->seed, mp->val, tbuf); 335 1.14 mjl else 336 1.14 mjl (void)fprintf(mp->keyfile, "%s %s %04d %-16s %s %-21s\n", 337 1.14 mjl mp->logname, skey_get_algorithm(), mp->n, 338 1.14 mjl mp->seed, mp->val, tbuf); 339 1.1 deraadt 340 1.1 deraadt fclose(mp->keyfile); 341 1.14 mjl mp->keyfile = NULL; 342 1.17 itojun return 0; 343 1.10 simonb 344 1.17 itojun verify_failure: 345 1.17 itojun fclose(mp->keyfile); 346 1.17 itojun mp->keyfile = NULL; 347 1.17 itojun return -1; 348 1.1 deraadt } 349 1.1 deraadt 350 1.1 deraadt 351 1.14 mjl /* Returns: 1 user doesnt exist, -1 file error, 0 user exists. */ 352 1.10 simonb 353 1.14 mjl int skey_haskey(const char *username) 354 1.1 deraadt { 355 1.5 pk struct skey skey; 356 1.14 mjl int i; 357 1.14 mjl 358 1.14 mjl i = skeylookup(&skey, username); 359 1.10 simonb 360 1.14 mjl if (skey.keyfile != NULL) { 361 1.14 mjl fclose(skey.keyfile); 362 1.14 mjl skey.keyfile = NULL; 363 1.14 mjl } 364 1.14 mjl return(i); 365 1.1 deraadt } 366 1.10 simonb 367 1.1 deraadt /* 368 1.1 deraadt * Returns the current sequence number and 369 1.1 deraadt * seed for the passed user. 370 1.1 deraadt */ 371 1.16 thorpej const char *skey_keyinfo(const char *username) 372 1.1 deraadt { 373 1.5 pk int i; 374 1.14 mjl static char str[SKEY_MAX_CHALLENGE]; 375 1.5 pk struct skey skey; 376 1.5 pk 377 1.14 mjl i = skeychallenge(&skey, username, str, sizeof str); 378 1.5 pk if (i == -1) 379 1.5 pk return 0; 380 1.1 deraadt 381 1.14 mjl if (skey.keyfile != NULL) { 382 1.14 mjl fclose(skey.keyfile); 383 1.14 mjl skey.keyfile = NULL; 384 1.14 mjl } 385 1.5 pk return str; 386 1.1 deraadt } 387 1.10 simonb 388 1.1 deraadt /* 389 1.1 deraadt * Check to see if answer is the correct one to the current 390 1.1 deraadt * challenge. 391 1.1 deraadt * 392 1.1 deraadt * Returns: 0 success, -1 failure 393 1.1 deraadt */ 394 1.10 simonb 395 1.19 perry int skey_passcheck(const char *username, char *passwd) 396 1.1 deraadt { 397 1.5 pk int i; 398 1.5 pk struct skey skey; 399 1.5 pk 400 1.5 pk i = skeylookup (&skey, username); 401 1.5 pk if (i == -1 || i == 1) 402 1.5 pk return -1; 403 1.5 pk 404 1.5 pk if (skeyverify (&skey, passwd) == 0) 405 1.5 pk return skey.n; 406 1.5 pk 407 1.5 pk return -1; 408 1.1 deraadt } 409 1.1 deraadt 410 1.14 mjl #if DO_FAKE_CHALLENGE 411 1.14 mjl #define ROUND(x) (((x)[0] << 24) + (((x)[1]) << 16) + (((x)[2]) << 8) + \ 412 1.14 mjl ((x)[3])) 413 1.14 mjl 414 1.14 mjl /* 415 1.14 mjl * hash_collapse() 416 1.14 mjl */ 417 1.14 mjl static u_int32_t hash_collapse(u_char *s) 418 1.14 mjl { 419 1.14 mjl int len, target; 420 1.14 mjl u_int32_t i; 421 1.14 mjl int slen; 422 1.14 mjl 423 1.14 mjl slen = strlen((char *)s); 424 1.14 mjl if ((slen % sizeof(u_int32_t)) == 0) 425 1.14 mjl target = slen; /* Multiple of 4 */ 426 1.14 mjl else 427 1.14 mjl target = slen - slen % sizeof(u_int32_t); 428 1.14 mjl 429 1.14 mjl for (i = 0, len = 0; len < target; len += 4) 430 1.14 mjl i ^= ROUND(s + len); 431 1.14 mjl 432 1.14 mjl return i; 433 1.14 mjl } 434 1.14 mjl #endif 435 1.14 mjl 436 1.1 deraadt /* 437 1.1 deraadt * Used when calling program will allow input of the user's 438 1.1 deraadt * response to the challenge. 439 1.1 deraadt * 440 1.1 deraadt * Returns: 0 success, -1 failure 441 1.1 deraadt */ 442 1.10 simonb 443 1.14 mjl int skey_authenticate(const char *username) 444 1.1 deraadt { 445 1.5 pk int i; 446 1.14 mjl char pbuf[SKEY_MAX_PW_LEN+1], skeyprompt[SKEY_MAX_CHALLENGE+1]; 447 1.5 pk struct skey skey; 448 1.14 mjl #if DO_FAKE_CHALLENGE 449 1.14 mjl u_int ptr; 450 1.14 mjl u_char hseed[SKEY_MAX_SEED_LEN], flg = 1, *up; 451 1.14 mjl size_t secretlen; 452 1.14 mjl struct skey skey; 453 1.14 mjl SHA1_CTX ctx; 454 1.14 mjl #endif 455 1.14 mjl /* Attempt a S/Key challenge */ 456 1.14 mjl i = skeychallenge(&skey, username, skeyprompt, sizeof skeyprompt); 457 1.5 pk 458 1.14 mjl #if DO_FAKE_CHALLENGE 459 1.14 mjl /* Cons up a fake prompt if no entry in keys file */ 460 1.14 mjl if (i != 0) { 461 1.14 mjl char *p, *u; 462 1.14 mjl 463 1.14 mjl /* 464 1.14 mjl * Base first 4 chars of seed on hostname. 465 1.14 mjl * Add some filler for short hostnames if necessary. 466 1.14 mjl */ 467 1.14 mjl if (gethostname(pbuf, sizeof(pbuf)) == -1) 468 1.14 mjl *(p = pbuf) = '.'; 469 1.14 mjl else 470 1.15 itohy for (p = pbuf; *p && isalnum((u_char) *p); p++) 471 1.15 itohy if (isalpha((u_char)*p) && isupper((u_char)*p)) 472 1.15 itohy *p = tolower((u_char)*p); 473 1.14 mjl if (*p && pbuf - p < 4) 474 1.14 mjl (void)strncpy(p, "asjd", 4 - (pbuf - p)); 475 1.14 mjl pbuf[4] = '\0'; 476 1.14 mjl 477 1.14 mjl /* Hash the username if possible */ 478 1.14 mjl if ((up = SHA1Data(username, strlen(username), NULL)) != NULL) { 479 1.14 mjl struct stat sb; 480 1.14 mjl time_t t; 481 1.14 mjl int fd; 482 1.14 mjl 483 1.14 mjl /* Collapse the hash */ 484 1.14 mjl ptr = hash_collapse(up); 485 1.14 mjl memset(up, 0, strlen(up)); 486 1.14 mjl 487 1.14 mjl /* See if the random file's there, else use ctime */ 488 1.14 mjl if ((fd = open(_SKEY_RAND_FILE_PATH_, O_RDONLY)) != -1 489 1.14 mjl && fstat(fd, &sb) == 0 && 490 1.14 mjl sb.st_size > (off_t)SKEY_MAX_SEED_LEN && 491 1.14 mjl lseek(fd, ptr % (sb.st_size - SKEY_MAX_SEED_LEN), 492 1.14 mjl SEEK_SET) != -1 && read(fd, hseed, 493 1.14 mjl SKEY_MAX_SEED_LEN) == SKEY_MAX_SEED_LEN) { 494 1.14 mjl close(fd); 495 1.14 mjl fd = -1; 496 1.14 mjl secret = hseed; 497 1.14 mjl secretlen = SKEY_MAX_SEED_LEN; 498 1.14 mjl flg = 0; 499 1.14 mjl } else if (!stat(_PATH_MEM, &sb) || !stat("/", &sb)) { 500 1.14 mjl t = sb.st_ctime; 501 1.14 mjl secret = ctime(&t); 502 1.14 mjl secretlen = strlen(secret); 503 1.14 mjl flg = 0; 504 1.14 mjl } 505 1.14 mjl if (fd != -1) 506 1.14 mjl close(fd); 507 1.14 mjl } 508 1.5 pk 509 1.14 mjl /* Put that in your pipe and smoke it */ 510 1.14 mjl if (flg == 0) { 511 1.14 mjl /* Hash secret value with username */ 512 1.14 mjl SHA1Init(&ctx); 513 1.14 mjl SHA1Update(&ctx, secret, secretlen); 514 1.14 mjl SHA1Update(&ctx, username, strlen(username)); 515 1.14 mjl SHA1End(&ctx, up); 516 1.14 mjl 517 1.14 mjl /* Zero out */ 518 1.14 mjl memset(secret, 0, secretlen); 519 1.14 mjl 520 1.14 mjl /* Now hash the hash */ 521 1.14 mjl SHA1Init(&ctx); 522 1.14 mjl SHA1Update(&ctx, up, strlen(up)); 523 1.14 mjl SHA1End(&ctx, up); 524 1.14 mjl 525 1.14 mjl ptr = hash_collapse(up + 4); 526 1.14 mjl 527 1.14 mjl for (i = 4; i < 9; i++) { 528 1.14 mjl pbuf[i] = (ptr % 10) + '0'; 529 1.14 mjl ptr /= 10; 530 1.14 mjl } 531 1.14 mjl pbuf[i] = '\0'; 532 1.14 mjl 533 1.14 mjl /* Sequence number */ 534 1.14 mjl ptr = ((up[2] + up[3]) % 99) + 1; 535 1.14 mjl 536 1.14 mjl memset(up, 0, 20); /* SHA1 specific */ 537 1.14 mjl free(up); 538 1.14 mjl 539 1.14 mjl (void)sprintf(skeyprompt, 540 1.14 mjl "otp-%.*s %d %.*s", 541 1.14 mjl SKEY_MAX_HASHNAME_LEN, 542 1.14 mjl skey_get_algorithm(), 543 1.14 mjl ptr, SKEY_MAX_SEED_LEN, 544 1.14 mjl pbuf); 545 1.14 mjl } else { 546 1.14 mjl /* Base last 8 chars of seed on username */ 547 1.14 mjl u = username; 548 1.14 mjl i = 8; 549 1.14 mjl p = &pbuf[4]; 550 1.14 mjl do { 551 1.14 mjl if (*u == 0) { 552 1.14 mjl /* Pad remainder with zeros */ 553 1.14 mjl while (--i >= 0) 554 1.14 mjl *p++ = '0'; 555 1.14 mjl break; 556 1.14 mjl } 557 1.14 mjl 558 1.14 mjl *p++ = (*u++ % 10) + '0'; 559 1.14 mjl } while (--i != 0); 560 1.14 mjl pbuf[12] = '\0'; 561 1.14 mjl 562 1.14 mjl (void)sprintf(skeyprompt, "otp-%.*s %d %.*s", 563 1.14 mjl SKEY_MAX_HASHNAME_LEN, 564 1.14 mjl skey_get_algorithm(), 565 1.14 mjl 99, SKEY_MAX_SEED_LEN, pbuf); 566 1.14 mjl } 567 1.14 mjl } 568 1.14 mjl #endif 569 1.5 pk 570 1.14 mjl fprintf(stderr, "[%s]\n", skeyprompt); 571 1.14 mjl fflush(stderr); 572 1.5 pk 573 1.14 mjl fputs("Response: ", stderr); 574 1.14 mjl readskey(pbuf, sizeof(pbuf)); 575 1.5 pk 576 1.5 pk /* Is it a valid response? */ 577 1.14 mjl if (i == 0 && skeyverify(&skey, pbuf) == 0) { 578 1.5 pk if (skey.n < 5) { 579 1.14 mjl fprintf(stderr, 580 1.14 mjl "\nWarning! Key initialization needed soon. (%d logins left)\n", 581 1.14 mjl skey.n); 582 1.5 pk } 583 1.5 pk return 0; 584 1.5 pk } 585 1.5 pk return -1; 586 1.14 mjl } 587 1.14 mjl 588 1.14 mjl /* Comment out user's entry in the s/key database 589 1.14 mjl * 590 1.14 mjl * Return codes: 591 1.14 mjl * -1: Write error; database unchanged 592 1.14 mjl * 0: Database updated 593 1.14 mjl * 594 1.14 mjl * The database file is always closed by this call. 595 1.14 mjl */ 596 1.14 mjl 597 1.14 mjl /* ARGSUSED */ 598 1.14 mjl int skeyzero(struct skey *mp, char *response) 599 1.14 mjl { 600 1.14 mjl /* 601 1.14 mjl * Seek to the right place and write comment character 602 1.14 mjl * which effectively zero's out the entry. 603 1.14 mjl */ 604 1.14 mjl fseek(mp->keyfile, mp->recstart, SEEK_SET); 605 1.14 mjl if (fputc('#', mp->keyfile) == EOF) { 606 1.14 mjl fclose(mp->keyfile); 607 1.14 mjl mp->keyfile = NULL; 608 1.14 mjl return(-1); 609 1.14 mjl } 610 1.14 mjl 611 1.14 mjl fclose(mp->keyfile); 612 1.14 mjl mp->keyfile = NULL; 613 1.14 mjl return(0); 614 1.1 deraadt } 615