skeylogin.c revision 1.18 1 1.18 lukem /* $NetBSD: skeylogin.c,v 1.18 2003/03/09 00:46:07 lukem 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.18 lukem __RCSID("$NetBSD: skeylogin.c,v 1.18 2003/03/09 00:46:07 lukem 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.14 mjl 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