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