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