Annotation of src/usr.bin/skeyinit/skeyinit.c, Revision 1.31
1.31 ! millert 1: /* $OpenBSD: skeyinit.c,v 1.30 2001/11/01 18:26:58 miod Exp $ */
1.1 deraadt 2:
1.28 millert 3: /* OpenBSD S/Key (skeyinit.c)
1.1 deraadt 4: *
5: * Authors:
6: * Neil M. Haller <nmh@thumper.bellcore.com>
7: * Philip R. Karn <karn@chicago.qualcomm.com>
8: * John S. Walden <jsw@thumper.bellcore.com>
9: * Scott Chasin <chasin@crimelab.com>
1.20 millert 10: * Todd C. Miller <Todd.Miller@courtesan.com>
11: *
1.28 millert 12: * S/Key initialization and seed update
1.1 deraadt 13: */
14:
15: #include <sys/param.h>
1.20 millert 16: #include <sys/file.h>
1.1 deraadt 17: #include <sys/time.h>
18: #include <sys/resource.h>
19:
1.19 millert 20: #include <err.h>
1.20 millert 21: #include <errno.h>
1.19 millert 22: #include <ctype.h>
23: #include <pwd.h>
1.28 millert 24: #include <readpassphrase.h>
1.1 deraadt 25: #include <stdio.h>
26: #include <stdlib.h>
27: #include <string.h>
1.19 millert 28: #include <syslog.h>
29: #include <time.h>
1.1 deraadt 30: #include <unistd.h>
1.6 millert 31: #include <utmp.h>
1.19 millert 32:
1.4 millert 33: #include <skey.h>
1.1 deraadt 34:
1.4 millert 35: #ifndef SKEY_NAMELEN
1.17 millert 36: #define SKEY_NAMELEN 4
1.4 millert 37: #endif
1.1 deraadt 38:
1.31 ! millert 39: void lockeof(struct skey *, char *);
! 40: void usage(char *);
! 41: void secure_mode(int *, char *, char *, char *, char *, size_t);
! 42: void normal_mode(char *, int, char *, char *, char *);
! 43: void timedout(int);
1.8 millert 44:
1.1 deraadt 45: int
46: main(argc, argv)
47: int argc;
48: char *argv[];
49: {
1.28 millert 50: int rval, i, l, n=0, defaultsetup=1, zerokey=0, hexmode=0;
51: int oldmd4=0;
1.1 deraadt 52: time_t now;
1.28 millert 53: size_t seedlen;
1.1 deraadt 54: char hostname[MAXHOSTNAMELEN];
1.28 millert 55: char passwd[SKEY_MAX_PW_LEN+2];
1.17 millert 56: char seed[SKEY_MAX_SEED_LEN+2], defaultseed[SKEY_MAX_SEED_LEN+1];
1.28 millert 57: char tbuf[27], buf[256], key[SKEY_BINKEY_SIZE];
58: char lastc, me[UT_NAMESIZE+1], *salt, *p, *ht=NULL;
1.1 deraadt 59: struct skey skey;
60: struct passwd *pp;
61: struct tm *tm;
62:
1.4 millert 63: if (geteuid() != 0)
64: errx(1, "must be setuid root.");
65:
1.28 millert 66: /* Build up a default seed based on the hostname and time */
1.1 deraadt 67: if (gethostname(hostname, sizeof(hostname)) < 0)
68: err(1, "gethostname");
1.25 millert 69: for (i = 0, p = defaultseed; hostname[i] && i < SKEY_NAMELEN; i++) {
70: if (isalpha(hostname[i])) {
1.26 millert 71: if (isupper(hostname[i]))
72: hostname[i] = tolower(hostname[i]);
73: *p++ = hostname[i];
1.25 millert 74: } else if (isdigit(hostname[i]))
75: *p++ = hostname[i];
76: }
77: *p = '\0';
1.17 millert 78: (void)time(&now);
79: (void)sprintf(tbuf, "%05ld", (long) (now % 100000));
1.4 millert 80: (void)strncat(defaultseed, tbuf, sizeof(defaultseed) - 5);
1.1 deraadt 81:
82: if ((pp = getpwuid(getuid())) == NULL)
83: err(1, "no user with uid %d", getuid());
1.4 millert 84: (void)strcpy(me, pp->pw_name);
1.1 deraadt 85:
86: if ((pp = getpwnam(me)) == NULL)
87: err(1, "Who are you?");
1.16 millert 88: salt = pp->pw_passwd;
1.1 deraadt 89:
1.8 millert 90: for (i = 1; i < argc && argv[i][0] == '-' && strcmp(argv[i], "--");) {
91: if (argv[i][2] == '\0') {
92: /* Single character switch */
93: switch (argv[i][1]) {
1.4 millert 94: case 's':
95: defaultsetup = 0;
96: break;
97: case 'x':
98: hexmode = 1;
99: break;
100: case 'z':
101: zerokey = 1;
102: break;
1.17 millert 103: case 'n':
1.23 deraadt 104: if (argv[++i] == NULL || argv[i][0] == '\0')
1.17 millert 105: usage(argv[0]);
106: if ((n = atoi(argv[i])) < 1 || n >= SKEY_MAX_SEQ)
107: errx(1, "count must be > 0 and < %d",
108: SKEY_MAX_SEQ);
109: break;
1.8 millert 110: default:
111: usage(argv[0]);
112: }
113: } else {
114: /* Multi character switches are hash types */
115: if ((ht = skey_set_algorithm(&argv[i][1])) == NULL) {
116: warnx("Unknown hash algorithm %s", &argv[i][1]);
117: usage(argv[0]);
118: }
1.7 millert 119: }
1.8 millert 120: i++;
1.7 millert 121: }
122:
123: /* check for optional user string */
1.8 millert 124: if (argc - i > 1) {
125: usage(argv[0]);
126: } else if (argv[i]) {
1.16 millert 127: if ((pp = getpwnam(argv[i])) == NULL) {
128: if (getuid() == 0) {
129: static struct passwd _pp;
130:
131: _pp.pw_name = argv[i];
132: pp = &_pp;
133: warnx("Warning, user unknown: %s", argv[i]);
134: } else {
135: errx(1, "User unknown: %s", argv[i]);
136: }
137: } else if (strcmp(pp->pw_name, me) != 0) {
1.4 millert 138: if (getuid() != 0) {
139: /* Only root can change other's passwds */
140: errx(1, "Permission denied.");
141: }
1.1 deraadt 142: }
143: }
144:
1.28 millert 145: if (defaultsetup)
146: fputs("Reminder - Only use this method if you are directly connected\n or have an encrypted channel. If you are using telnet\n or rlogin, hit return now and use skeyinit -s.\n", stderr);
147:
1.1 deraadt 148: if (getuid() != 0) {
1.28 millert 149: /* XXX - use BSD auth */
150: passwd[0] = '\0';
151: if (!defaultsetup && skeychallenge(&skey, me, buf) == 0) {
152: printf("Enter S/Key password below or hit return twice "
153: "to enter standard password.\n%s\n", buf);
154: fflush(stdout);
155: if (!readpassphrase("S/Key Password: ", passwd,
156: sizeof(passwd), 0) || passwd[0] == '\0') {
157: readpassphrase("S/Key Password: [echo on] ",
158: passwd, sizeof(passwd), RPP_ECHO_ON);
159: }
160: }
161: if (passwd[0]) {
162: if (skeyverify(&skey, passwd) != 0)
1.10 millert 163: errx(1, "Password incorrect.");
164: } else {
1.28 millert 165: fflush(stdout);
166: readpassphrase("Password: ", passwd, sizeof(passwd), 0);
167: if (strcmp(crypt(passwd, salt), pp->pw_passwd)) {
168: if (passwd[0])
169: warnx("Password incorrect.");
170: exit(1);
171: }
1.10 millert 172: }
173: }
1.1 deraadt 174:
1.28 millert 175: /*
176: * Lookup and lock the record we are about to modify.
177: * If this is a new entry this will prevent other users
178: * from appending new entries (and clobbering ours).
179: */
1.1 deraadt 180: rval = skeylookup(&skey, pp->pw_name);
181: switch (rval) {
1.4 millert 182: case -1:
1.21 millert 183: if (errno == ENOENT)
1.27 millert 184: errx(1, "S/Key disabled");
1.21 millert 185: else
186: err(1, "cannot open database");
187: break;
1.4 millert 188: case 0:
189: /* comment out user if asked to */
190: if (zerokey)
1.29 millert 191: exit(skeyzero(&skey));
1.4 millert 192:
1.28 millert 193: (void)printf("[Updating %s with %s]\n", pp->pw_name,
194: ht ? ht : skey_get_algorithm());
195: (void)printf("Old seed: [%s] %s\n",
196: skey_get_algorithm(), skey.seed);
1.4 millert 197:
198: /*
1.25 millert 199: * Sanity check old seed.
200: */
201: l = strlen(skey.seed);
202: for (p = skey.seed; *p; p++) {
203: if (isalpha(*p)) {
204: if (isupper(*p))
205: *p = tolower(*p);
206: } else if (!isdigit(*p)) {
207: memmove(p, p + 1, l - (p - skey.seed));
208: l--;
209: }
210: }
211:
1.28 millert 212: /* If the seed ends in 0-8 just add one. */
1.4 millert 213: if (l > 0) {
214: lastc = skey.seed[l - 1];
215: if (isdigit(lastc) && lastc != '9') {
216: (void)strcpy(defaultseed, skey.seed);
217: defaultseed[l - 1] = lastc + 1;
218: }
219: if (isdigit(lastc) && lastc == '9' && l < 16) {
220: (void)strcpy(defaultseed, skey.seed);
221: defaultseed[l - 1] = '0';
222: defaultseed[l] = '0';
223: defaultseed[l + 1] = '\0';
224: }
1.1 deraadt 225: }
1.4 millert 226: break;
227: case 1:
228: if (zerokey)
229: errx(1, "You have no entry to zero.");
1.28 millert 230: (void)printf("[Adding %s with %s]\n", pp->pw_name,
231: ht ? ht : skey_get_algorithm());
232: lockeof(&skey, pp->pw_name);
1.4 millert 233: break;
1.1 deraadt 234: }
1.17 millert 235: if (n == 0)
236: n = 99;
1.1 deraadt 237:
1.28 millert 238: /* Do we have an old-style md4 entry? */
239: if (rval == 0 && strcmp("md4", skey_get_algorithm()) == 0 &&
240: strcmp("md4", skey.logname + strlen(skey.logname) + 1) != 0)
241: oldmd4 = 1;
242:
1.8 millert 243: /* Set hash type if asked to */
1.28 millert 244: if (ht && strcmp(ht, skey_get_algorithm()) != 0)
245: skey_set_algorithm(ht);
246:
247: alarm(180);
248: if (!defaultsetup)
249: secure_mode(&n, key, seed, defaultseed, buf, sizeof(buf));
250: else
251: normal_mode(pp->pw_name, n, key, seed, defaultseed);
252: alarm(0);
1.4 millert 253:
1.28 millert 254: (void)time(&now);
255: tm = localtime(&now);
256: (void)strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm);
1.10 millert 257:
1.28 millert 258: /* If this is an exiting entry, compute the line length and seed pad */
259: seedlen = SKEY_MAX_SEED_LEN;
260: if (rval == 0) {
261: int nlen;
262:
263: nlen = strlen(pp->pw_name) + 1 + strlen(skey_get_algorithm()) +
264: 1 + 4 + 1 + strlen(seed) + 1 + 16 + 1 + strlen(tbuf) + 1;
265:
266: /*
267: * If there was no hash type (md4) add one unless we
268: * are short on space.
269: */
270: if (oldmd4) {
271: if (nlen > skey.len)
272: nlen -= 4;
273: else
274: oldmd4 = 0;
1.1 deraadt 275: }
1.4 millert 276:
1.28 millert 277: /* If new entry is longer than the old, comment out the old. */
278: if (nlen > skey.len) {
1.29 millert 279: (void)skeyzero(&skey);
1.28 millert 280: /* Re-open keys file and seek to the end */
281: if (skeylookup(&skey, pp->pw_name) == -1)
282: err(1, "cannot reopen database");
283: lockeof(&skey, pp->pw_name);
284: } else {
285: /* Compute how much to space-pad the seed */
286: seedlen = strlen(seed) + (skey.len - nlen);
1.25 millert 287: }
1.28 millert 288: }
1.1 deraadt 289:
1.28 millert 290: if ((skey.val = (char *)malloc(16 + 1)) == NULL)
291: err(1, "Can't allocate memory");
292: btoa8(skey.val, key);
1.1 deraadt 293:
1.28 millert 294: /* Don't save algorithm type for md4 (maintain record length) */
295: /* XXX - should check return values of fprintf + fclose */
296: if (oldmd4)
1.30 miod 297: (void)fprintf(skey.keyfile, "%s %04d %-*s %s %-21s\n",
1.28 millert 298: pp->pw_name, n, seedlen, seed, skey.val, tbuf);
299: else
300: (void)fprintf(skey.keyfile, "%s %s %04d %-*s %s %-21s\n",
301: pp->pw_name, skey_get_algorithm(), n, seedlen, seed,
302: skey.val, tbuf);
303: (void)fclose(skey.keyfile);
1.4 millert 304:
1.28 millert 305: (void)printf("\nID %s skey is otp-%s %d %s\n", pp->pw_name,
306: skey_get_algorithm(), n, seed);
307: (void)printf("Next login password: %s\n\n",
308: hexmode ? put8(buf, key) : btoe(buf, key));
309: exit(0);
310: }
1.1 deraadt 311:
1.28 millert 312: void
313: lockeof(mp, user)
314: struct skey *mp;
315: char *user;
316: {
317: struct flock fl;
1.1 deraadt 318:
1.28 millert 319: fseek(mp->keyfile, 0, SEEK_END);
320: dolock:
321: fl.l_start = ftell(mp->keyfile);
322: fl.l_len = mp->len;
323: fl.l_pid = getpid();
324: fl.l_type = F_WRLCK;
325: fl.l_whence = SEEK_SET;
326:
327: if (fcntl(fileno(mp->keyfile), F_SETLKW, &fl) == -1)
328: err(1, "Can't lock database");
329:
330: /* Make sure we are still at the end. */
331: fseek(mp->keyfile, 0, SEEK_END);
332: if (fl.l_start == ftell(mp->keyfile))
333: return; /* still at EOF */
334:
335: fclose(mp->keyfile);
336: if (skeylookup(mp, user) != 1)
337: errx(1, "user %s already added", user);
338: goto dolock;
339: }
340:
341: void
342: secure_mode(count, key, seed, defaultseed, buf, bufsiz)
343: int *count;
344: char *key;
345: char *seed;
346: char *defaultseed;
347: char *buf;
348: size_t bufsiz;
349: {
350: int i, n;
351: char *p;
1.4 millert 352:
1.28 millert 353: (void)puts("You need the 6 words generated from the \"skey\" command.");
354: for (i = 0; ; i++) {
355: if (i >= 2)
356: exit(1);
357:
358: (void)printf("Enter sequence count from 1 to %d: ",
359: SKEY_MAX_SEQ);
360: (void)fgets(buf, bufsiz, stdin);
361: clearerr(stdin);
362: n = atoi(buf);
363: if (n > 0 && n < SKEY_MAX_SEQ)
364: break; /* Valid range */
365: (void)fprintf(stderr, "ERROR: Count must be between 1 and %d\n",
366: SKEY_MAX_SEQ);
367: }
1.1 deraadt 368:
1.28 millert 369: for (i = 0; ; i++) {
370: if (i >= 2)
371: exit(1);
372:
373: (void)printf("Enter new seed [default %s]: ",
374: defaultseed);
375: (void)fgets(seed, SKEY_MAX_SEED_LEN+2, stdin); /* XXX */
376: clearerr(stdin);
377: rip(seed);
378: if (strlen(seed) > SKEY_MAX_SEED_LEN) {
379: (void)fprintf(stderr, "ERROR: Seed must be between 1 "
380: "and %d characters in length\n", SKEY_MAX_SEED_LEN);
381: continue;
382: }
383: if (seed[0] == '\0')
384: (void)strcpy(seed, defaultseed);
385: for (p = seed; *p; p++) {
386: if (isspace(*p)) {
387: (void)fputs("ERROR: Seed must not contain "
388: "any spaces\n", stderr);
1.1 deraadt 389: break;
1.28 millert 390: } else if (isalpha(*p)) {
391: if (isupper(*p))
392: *p = tolower(*p);
393: } else if (!isdigit(*p)) {
394: (void)fputs("ERROR: Seed must be purely "
395: "alphanumeric\n", stderr);
396: break;
397: }
1.1 deraadt 398: }
1.28 millert 399: if (*p == '\0')
400: break; /* Valid seed */
401: }
1.1 deraadt 402:
1.28 millert 403: for (i = 0; ; i++) {
404: if (i >= 2)
405: exit(1);
406:
407: (void)printf("otp-%s %d %s\nS/Key access password: ",
408: skey_get_algorithm(), n, seed);
409: (void)fgets(buf, bufsiz, stdin);
410: clearerr(stdin);
411: rip(buf);
412: backspace(buf);
413:
414: if (buf[0] == '?') {
415: (void)puts("Enter 6 words from secure S/Key calculation.");
416: continue;
417: } else if (buf[0] == '\0')
418: exit(1);
419:
420: if (etob(key, buf) == 1 || atob8(key, buf) == 0)
421: break; /* Valid format */
422: (void)fputs("ERROR: Invalid format - try again with the 6 words.\n",
423: stderr);
1.1 deraadt 424: }
1.28 millert 425: *count= n;
426: }
1.1 deraadt 427:
1.28 millert 428: void
429: normal_mode(username, n, key, seed, defaultseed)
430: char *username;
431: int n;
432: char *key;
433: char *seed;
434: char *defaultseed;
435: {
436: int i, nn;
437: char passwd[SKEY_MAX_PW_LEN+2], passwd2[SKEY_MAX_PW_LEN+2];
1.1 deraadt 438:
1.28 millert 439: /* Get user's secret passphrase */
440: for (i = 0; ; i++) {
441: if (i > 2)
442: exit(1);
443:
444: if (readpassphrase("Enter secret passphrase: ", passwd,
445: sizeof(passwd), 0) == NULL || passwd[0] == '\0')
446: exit(1);
447:
448: if (strlen(passwd) < SKEY_MIN_PW_LEN) {
449: (void)fprintf(stderr,
450: "ERROR: Your passphrase must be at least %d "
451: "characters long.\n", SKEY_MIN_PW_LEN);
452: continue;
453: } else if (strcmp(passwd, username) == 0) {
454: (void)fputs("ERROR: Your passphrase may not be the "
455: "same as your user name.\n", stderr);
456: continue;
457: } else if (strspn(passwd, "abcdefghijklmnopqrstuvwxyz") ==
458: strlen(passwd)) {
459: (void)fputs("ERROR: Your passphrase must contain more "
460: "than just lower case letters.\nWhitespace, "
461: "numbers, and puctuation are suggested.\n", stderr);
462: continue;
463: } else if (strlen(passwd) > 63) {
464: (void)fprintf(stderr, "WARNING: Your passphrase is "
465: "longer than the recommended maximum length of 63\n");
466: }
467: /* XXX - should check for passphrase that is really too long */
1.13 millert 468:
1.28 millert 469: if (readpassphrase("Again secret passphrase: ", passwd2,
470: sizeof(passwd2), 0) && strcmp(passwd, passwd2) == 0)
471: break;
1.1 deraadt 472:
1.28 millert 473: (void)fputs("Passphrases do not match.\n", stderr);
1.20 millert 474: }
475:
1.28 millert 476: /* Crunch seed and passphrase into starting key */
477: (void)strcpy(seed, defaultseed);
478: if (keycrunch(key, seed, passwd) != 0)
479: err(2, "key crunch failed");
480:
481: nn = n;
482: while (nn-- != 0)
483: f(key);
484: }
485:
486: #define TIMEOUT_MSG "Timed out waiting for input.\n"
487: void
488: timedout(signo)
489: int signo;
490: {
1.10 millert 491:
1.28 millert 492: write(STDERR_FILENO, TIMEOUT_MSG, sizeof(TIMEOUT_MSG) - 1);
493: _exit(1);
1.8 millert 494: }
495:
496: void
497: usage(s)
498: char *s;
499: {
500: (void)fprintf(stderr,
1.18 millert 501: "Usage: %s [-s] [-x] [-z] [-n count] [-md4|-md5|-sha1|-rmd160] [user]\n", s);
1.8 millert 502: exit(1);
1.1 deraadt 503: }