Annotation of src/usr.bin/skeyinit/skeyinit.c, Revision 1.39
1.39 ! millert 1: /* $OpenBSD: skeyinit.c,v 1.38 2002/06/07 21:35:26 millert 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.32 millert 17: #include <sys/resource.h>
18: #include <sys/stat.h>
1.1 deraadt 19: #include <sys/time.h>
20:
1.32 millert 21: #include <ctype.h>
1.19 millert 22: #include <err.h>
1.20 millert 23: #include <errno.h>
1.19 millert 24: #include <pwd.h>
1.28 millert 25: #include <readpassphrase.h>
1.1 deraadt 26: #include <stdio.h>
27: #include <stdlib.h>
28: #include <string.h>
1.19 millert 29: #include <syslog.h>
30: #include <time.h>
1.1 deraadt 31: #include <unistd.h>
1.6 millert 32: #include <utmp.h>
1.19 millert 33:
1.4 millert 34: #include <skey.h>
1.32 millert 35: #include <bsd_auth.h>
1.1 deraadt 36:
1.4 millert 37: #ifndef SKEY_NAMELEN
1.17 millert 38: #define SKEY_NAMELEN 4
1.4 millert 39: #endif
1.1 deraadt 40:
1.36 millert 41: void usage(void);
1.38 millert 42: void secure_mode(int *, char *, char *, char *, size_t);
43: void normal_mode(char *, int, char *, char *);
1.31 millert 44: void timedout(int);
1.32 millert 45: void convert_db(void);
46: void enable_db(int);
1.8 millert 47:
1.1 deraadt 48: int
1.36 millert 49: main(int argc, char **argv)
1.1 deraadt 50: {
1.35 millert 51: int rval, i, l, n, defaultsetup, rmkey, hexmode, enable, convert;
1.1 deraadt 52: char hostname[MAXHOSTNAMELEN];
1.38 millert 53: char seed[SKEY_MAX_SEED_LEN + 1];
1.32 millert 54: char buf[256], key[SKEY_BINKEY_SIZE], filename[PATH_MAX], *ht;
55: char lastc, me[UT_NAMESIZE + 1], *p, *auth_type;
1.39 ! millert 56: u_int32_t noise;
1.1 deraadt 57: struct skey skey;
58: struct passwd *pp;
59:
1.35 millert 60: n = rmkey = hexmode = enable = convert = 0;
1.32 millert 61: defaultsetup = 1;
62: ht = auth_type = NULL;
1.4 millert 63:
1.39 ! millert 64: /* Build up a default seed based on the hostname and some noise */
1.1 deraadt 65: if (gethostname(hostname, sizeof(hostname)) < 0)
66: err(1, "gethostname");
1.38 millert 67: for (i = 0, p = seed; hostname[i] && i < SKEY_NAMELEN; i++) {
1.25 millert 68: if (isalpha(hostname[i])) {
1.26 millert 69: if (isupper(hostname[i]))
70: hostname[i] = tolower(hostname[i]);
71: *p++ = hostname[i];
1.25 millert 72: } else if (isdigit(hostname[i]))
73: *p++ = hostname[i];
1.39 ! millert 74: }
! 75: noise = arc4random();
! 76: for (i = 0; i < 5; i++) {
! 77: *p++ = (noise % 10) + '0';
! 78: noise /= 10;
1.25 millert 79: }
80: *p = '\0';
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?");
88:
1.8 millert 89: for (i = 1; i < argc && argv[i][0] == '-' && strcmp(argv[i], "--");) {
90: if (argv[i][2] == '\0') {
91: /* Single character switch */
92: switch (argv[i][1]) {
1.32 millert 93: case 'a':
94: if (argv[++i] == NULL || argv[i][0] == '\0')
1.36 millert 95: usage();
1.32 millert 96: auth_type = argv[i];
97: break;
1.4 millert 98: case 's':
99: defaultsetup = 0;
1.32 millert 100: auth_type = "skey";
1.4 millert 101: break;
102: case 'x':
103: hexmode = 1;
104: break;
1.35 millert 105: case 'r':
106: rmkey = 1;
1.4 millert 107: break;
1.17 millert 108: case 'n':
1.23 deraadt 109: if (argv[++i] == NULL || argv[i][0] == '\0')
1.36 millert 110: usage();
1.17 millert 111: if ((n = atoi(argv[i])) < 1 || n >= SKEY_MAX_SEQ)
112: errx(1, "count must be > 0 and < %d",
113: SKEY_MAX_SEQ);
114: break;
1.32 millert 115: case 'C':
116: convert = 1;
117: break;
118: case 'D':
119: enable = -1;
120: break;
121: case 'E':
122: enable = 1;
123: break;
1.8 millert 124: default:
1.36 millert 125: usage();
1.8 millert 126: }
127: } else {
128: /* Multi character switches are hash types */
129: if ((ht = skey_set_algorithm(&argv[i][1])) == NULL) {
130: warnx("Unknown hash algorithm %s", &argv[i][1]);
1.36 millert 131: usage();
1.8 millert 132: }
1.7 millert 133: }
1.8 millert 134: i++;
1.7 millert 135: }
1.32 millert 136: argv += i;
137: argc -= i;
1.7 millert 138:
1.32 millert 139: if (argc > 1 || (enable && convert) || (enable && argc) ||
140: (convert && argc))
1.36 millert 141: usage();
1.32 millert 142:
143: /* Handle -C, -D, and -E */
1.34 millert 144: if (convert || enable) {
145: if (convert)
146: convert_db();
147: else
148: enable_db(enable);
149: exit(0);
150: }
1.32 millert 151:
152: /* Check for optional user string. */
153: if (argc == 1) {
1.36 millert 154: if ((pp = getpwnam(argv[0])) == NULL) {
1.16 millert 155: if (getuid() == 0) {
156: static struct passwd _pp;
157:
1.36 millert 158: _pp.pw_name = argv[0];
1.16 millert 159: pp = &_pp;
1.36 millert 160: warnx("Warning, user unknown: %s", argv[0]);
1.16 millert 161: } else {
1.36 millert 162: errx(1, "User unknown: %s", argv[0]);
1.16 millert 163: }
1.32 millert 164: } else if (strcmp(pp->pw_name, me) != 0 && getuid() != 0) {
165: /* Only root can change other's S/Keys. */
166: errx(1, "Permission denied.");
1.1 deraadt 167: }
168: }
169:
1.28 millert 170: if (defaultsetup)
1.32 millert 171: fputs("Reminder - Only use this method if you are directly "
172: "connected\n or have an encrypted channel. If "
173: "you are using telnet,\n hit return now and use "
174: "skeyinit -s.\n", stderr);
1.28 millert 175:
1.1 deraadt 176: if (getuid() != 0) {
1.32 millert 177: if ((pp = pw_dup(pp)) == NULL)
178: err(1, NULL);
179: if (!auth_userokay(pp->pw_name, auth_type, NULL, NULL))
180: errx(1, "Password incorrect");
1.10 millert 181: }
1.1 deraadt 182:
1.28 millert 183: /*
184: * Lookup and lock the record we are about to modify.
185: * If this is a new entry this will prevent other users
186: * from appending new entries (and clobbering ours).
187: */
1.1 deraadt 188: rval = skeylookup(&skey, pp->pw_name);
189: switch (rval) {
1.4 millert 190: case -1:
1.21 millert 191: if (errno == ENOENT)
1.27 millert 192: errx(1, "S/Key disabled");
1.21 millert 193: else
194: err(1, "cannot open database");
195: break;
1.4 millert 196: case 0:
1.35 millert 197: /* remove user if asked to do so */
198: if (rmkey) {
199: if (snprintf(filename, sizeof(filename),
200: "%s/%s", _PATH_SKEYDIR, pp->pw_name)
201: >= sizeof(filename)) {
202: errno = ENAMETOOLONG;
203: err(1, "Cannot remove S/Key entry");
204: }
205: if (unlink(filename) != 0)
206: err(1, "Cannot remove S/Key entry");
207: printf("S/Key entry for %s removed.\n",
208: pp->pw_name);
209: exit(0);
210: }
1.4 millert 211:
1.28 millert 212: (void)printf("[Updating %s with %s]\n", pp->pw_name,
213: ht ? ht : skey_get_algorithm());
214: (void)printf("Old seed: [%s] %s\n",
215: skey_get_algorithm(), skey.seed);
1.4 millert 216:
217: /*
1.25 millert 218: * Sanity check old seed.
219: */
220: l = strlen(skey.seed);
221: for (p = skey.seed; *p; p++) {
222: if (isalpha(*p)) {
223: if (isupper(*p))
224: *p = tolower(*p);
225: } else if (!isdigit(*p)) {
226: memmove(p, p + 1, l - (p - skey.seed));
227: l--;
228: }
229: }
230:
1.28 millert 231: /* If the seed ends in 0-8 just add one. */
1.4 millert 232: if (l > 0) {
233: lastc = skey.seed[l - 1];
234: if (isdigit(lastc) && lastc != '9') {
1.38 millert 235: (void)strcpy(seed, skey.seed);
236: seed[l - 1] = lastc + 1;
1.4 millert 237: }
238: if (isdigit(lastc) && lastc == '9' && l < 16) {
1.38 millert 239: (void)strcpy(seed, skey.seed);
240: seed[l - 1] = '0';
241: seed[l] = '0';
242: seed[l + 1] = '\0';
1.4 millert 243: }
1.1 deraadt 244: }
1.4 millert 245: break;
246: case 1:
1.35 millert 247: if (rmkey)
248: errx(1, "You have no entry to remove.");
1.28 millert 249: (void)printf("[Adding %s with %s]\n", pp->pw_name,
250: ht ? ht : skey_get_algorithm());
1.32 millert 251: if (snprintf(filename, sizeof(filename), "%s/%s",
252: _PATH_SKEYDIR, pp->pw_name) >= sizeof(filename)) {
253: errno = ENAMETOOLONG;
254: err(1, "Cannot create S/Key entry");
255: }
1.36 millert 256: if ((l = open(filename,
257: O_RDWR | O_NONBLOCK | O_CREAT | O_TRUNC |O_NOFOLLOW,
1.32 millert 258: S_IRUSR | S_IWUSR)) == -1 ||
259: flock(l, LOCK_EX) != 0 ||
260: (skey.keyfile = fdopen(l, "r+")) == NULL)
261: err(1, "Cannot create S/Key entry");
1.4 millert 262: break;
1.1 deraadt 263: }
1.32 millert 264: if (fchown(fileno(skey.keyfile), pp->pw_uid, -1) != 0 ||
265: fchmod(fileno(skey.keyfile), S_IRUSR | S_IWUSR) != 0)
266: err(1, "can't set owner/mode for %s", pp->pw_name);
1.17 millert 267: if (n == 0)
268: n = 99;
1.1 deraadt 269:
1.8 millert 270: /* Set hash type if asked to */
1.28 millert 271: if (ht && strcmp(ht, skey_get_algorithm()) != 0)
272: skey_set_algorithm(ht);
273:
274: alarm(180);
275: if (!defaultsetup)
1.38 millert 276: secure_mode(&n, key, seed, buf, sizeof(buf));
1.28 millert 277: else
1.38 millert 278: normal_mode(pp->pw_name, n, key, seed);
1.28 millert 279: alarm(0);
1.4 millert 280:
1.32 millert 281: /* XXX - why use malloc here? */
1.28 millert 282: if ((skey.val = (char *)malloc(16 + 1)) == NULL)
283: err(1, "Can't allocate memory");
284: btoa8(skey.val, key);
1.1 deraadt 285:
1.32 millert 286: (void)fseek(skey.keyfile, 0L, SEEK_SET);
287: (void)fprintf(skey.keyfile, "%s\n%s\n%04d\n%s\n%s\n",
288: pp->pw_name, skey_get_algorithm(), n, seed, skey.val);
1.28 millert 289: (void)fclose(skey.keyfile);
1.4 millert 290:
1.28 millert 291: (void)printf("\nID %s skey is otp-%s %d %s\n", pp->pw_name,
1.38 millert 292: skey_get_algorithm(), n, seed);
1.28 millert 293: (void)printf("Next login password: %s\n\n",
294: hexmode ? put8(buf, key) : btoe(buf, key));
295: exit(0);
296: }
1.1 deraadt 297:
1.28 millert 298: void
1.38 millert 299: secure_mode(int *count, char *key, char *seed, char *buf, size_t bufsiz)
1.28 millert 300: {
1.38 millert 301: char *p, newseed[SKEY_MAX_SEED_LEN + 2];
1.28 millert 302: int i, n;
1.4 millert 303:
1.28 millert 304: (void)puts("You need the 6 words generated from the \"skey\" command.");
305: for (i = 0; ; i++) {
306: if (i >= 2)
307: exit(1);
308:
309: (void)printf("Enter sequence count from 1 to %d: ",
310: SKEY_MAX_SEQ);
311: (void)fgets(buf, bufsiz, stdin);
312: clearerr(stdin);
313: n = atoi(buf);
314: if (n > 0 && n < SKEY_MAX_SEQ)
315: break; /* Valid range */
316: (void)fprintf(stderr, "ERROR: Count must be between 1 and %d\n",
317: SKEY_MAX_SEQ);
318: }
1.1 deraadt 319:
1.28 millert 320: for (i = 0; ; i++) {
321: if (i >= 2)
322: exit(1);
323:
1.38 millert 324: (void)printf("Enter new seed [default %s]: ", seed);
325: (void)fgets(newseed, sizeof(newseed), stdin); /* XXX */
1.28 millert 326: clearerr(stdin);
1.38 millert 327: rip(newseed);
328: if (strlen(newseed) > SKEY_MAX_SEED_LEN) {
1.28 millert 329: (void)fprintf(stderr, "ERROR: Seed must be between 1 "
330: "and %d characters in length\n", SKEY_MAX_SEED_LEN);
331: continue;
332: }
1.38 millert 333: for (p = newseed; *p; p++) {
1.28 millert 334: if (isspace(*p)) {
335: (void)fputs("ERROR: Seed must not contain "
336: "any spaces\n", stderr);
1.1 deraadt 337: break;
1.28 millert 338: } else if (isalpha(*p)) {
339: if (isupper(*p))
340: *p = tolower(*p);
341: } else if (!isdigit(*p)) {
342: (void)fputs("ERROR: Seed must be purely "
343: "alphanumeric\n", stderr);
344: break;
345: }
1.1 deraadt 346: }
1.28 millert 347: if (*p == '\0')
348: break; /* Valid seed */
349: }
1.38 millert 350: if (newseed[0] != '\0')
351: (void)strcpy(seed, newseed);
1.1 deraadt 352:
1.28 millert 353: for (i = 0; ; i++) {
354: if (i >= 2)
355: exit(1);
356:
357: (void)printf("otp-%s %d %s\nS/Key access password: ",
358: skey_get_algorithm(), n, seed);
359: (void)fgets(buf, bufsiz, stdin);
360: clearerr(stdin);
361: rip(buf);
362: backspace(buf);
363:
364: if (buf[0] == '?') {
365: (void)puts("Enter 6 words from secure S/Key calculation.");
366: continue;
367: } else if (buf[0] == '\0')
368: exit(1);
369:
370: if (etob(key, buf) == 1 || atob8(key, buf) == 0)
371: break; /* Valid format */
372: (void)fputs("ERROR: Invalid format - try again with the 6 words.\n",
373: stderr);
1.1 deraadt 374: }
1.28 millert 375: *count= n;
376: }
1.1 deraadt 377:
1.28 millert 378: void
1.38 millert 379: normal_mode(char *username, int n, char *key, char *seed)
1.28 millert 380: {
381: int i, nn;
1.38 millert 382: char passwd[SKEY_MAX_PW_LEN+2], key2[SKEY_BINKEY_SIZE];
1.1 deraadt 383:
1.28 millert 384: /* Get user's secret passphrase */
385: for (i = 0; ; i++) {
386: if (i > 2)
1.38 millert 387: errx(1, "S/Key entry not updated");
1.28 millert 388:
389: if (readpassphrase("Enter secret passphrase: ", passwd,
390: sizeof(passwd), 0) == NULL || passwd[0] == '\0')
391: exit(1);
392:
393: if (strlen(passwd) < SKEY_MIN_PW_LEN) {
394: (void)fprintf(stderr,
395: "ERROR: Your passphrase must be at least %d "
396: "characters long.\n", SKEY_MIN_PW_LEN);
397: continue;
398: } else if (strcmp(passwd, username) == 0) {
399: (void)fputs("ERROR: Your passphrase may not be the "
400: "same as your user name.\n", stderr);
401: continue;
402: } else if (strspn(passwd, "abcdefghijklmnopqrstuvwxyz") ==
403: strlen(passwd)) {
404: (void)fputs("ERROR: Your passphrase must contain more "
405: "than just lower case letters.\nWhitespace, "
1.37 aaron 406: "numbers, and punctuation are suggested.\n",
407: stderr);
1.28 millert 408: continue;
409: } else if (strlen(passwd) > 63) {
410: (void)fprintf(stderr, "WARNING: Your passphrase is "
411: "longer than the recommended maximum length of 63\n");
412: }
413: /* XXX - should check for passphrase that is really too long */
1.13 millert 414:
1.38 millert 415: /* Crunch seed and passphrase into starting key */
416: nn = keycrunch(key, seed, passwd);
417: memset(passwd, 0, sizeof(passwd));
418: if (nn != 0)
419: err(2, "key crunch failed");
420:
421: if (readpassphrase("Again secret passphrase: ", passwd,
422: sizeof(passwd), 0) == NULL || passwd[0] == '\0')
423: exit(1);
424:
425: /* Crunch seed and passphrase into starting key */
426: nn = keycrunch(key2, seed, passwd);
427: memset(passwd, 0, sizeof(passwd));
428: if (nn != 0)
429: err(2, "key crunch failed");
430:
431: if (memcmp(key, key2, sizeof(key2)) == 0)
1.28 millert 432: break;
1.1 deraadt 433:
1.28 millert 434: (void)fputs("Passphrases do not match.\n", stderr);
1.20 millert 435: }
1.28 millert 436:
437: nn = n;
438: while (nn-- != 0)
439: f(key);
440: }
441:
1.32 millert 442: void
1.36 millert 443: enable_db(int op)
1.32 millert 444: {
445: if (op == 1) {
446: /* enable */
447: if (mkdir(_PATH_SKEYDIR, 01730) != 0 && errno != EEXIST)
448: err(1, "can't mkdir %s", _PATH_SKEYDIR);
1.33 millert 449: if (chown(_PATH_SKEYDIR, geteuid(), getegid()) != 0)
450: err(1, "can't chown %s", _PATH_SKEYDIR);
1.32 millert 451: if (chmod(_PATH_SKEYDIR, 01730) != 0)
452: err(1, "can't chmod %s", _PATH_SKEYDIR);
453: } else {
454: /* disable */
455: if (chmod(_PATH_SKEYDIR, 0) != 0 && errno != ENOENT)
456: err(1, "can't chmod %s", _PATH_SKEYDIR);
457: }
458: }
459:
460: #define _PATH_SKEYKEYS "/etc/skeykeys"
461: void
462: convert_db(void)
463: {
464: struct passwd *pp;
465: uid_t uid;
466: FILE *keyfile;
467: FILE *newfile;
468: char buf[256], *logname, *hashtype, *seed, *val, *cp;
469: char filename[PATH_MAX];
470: int fd, n;
471:
472: if ((keyfile = fopen(_PATH_SKEYKEYS, "r")) == NULL)
473: err(1, "can't open %s", _PATH_SKEYKEYS);
474: if (flock(fileno(keyfile), LOCK_EX) != 0)
475: err(1, "can't lock %s", _PATH_SKEYKEYS);
1.34 millert 476: enable_db(1);
1.32 millert 477:
478: /*
479: * Loop over each entry in _PATH_SKEYKEYS, creating a file
480: * in _PATH_SKEYDIR for each one.
481: */
482: while (fgets(buf, sizeof(buf), keyfile) != NULL) {
483: if (buf[0] == '#')
484: continue;
485: if ((logname = strtok(buf, " \t")) == NULL)
486: continue;
487: if ((cp = strtok(NULL, " \t")) == NULL)
488: continue;
489: if (isalpha(*cp)) {
490: hashtype = cp;
491: if ((cp = strtok(NULL, " \t")) == NULL)
492: continue;
493: } else
494: hashtype = "md4";
495: n = atoi(cp);
496: if ((seed = strtok(NULL, " \t")) == NULL)
497: continue;
498: if ((val = strtok(NULL, " \t")) == NULL)
499: continue;
500:
501: if ((pp = getpwnam(logname)) != NULL)
502: uid = pp->pw_uid;
503: else
504: uid = 0;
505:
506: /* Now write the new-style record. */
507: if (snprintf(filename, sizeof(filename), "%s/%s", _PATH_SKEYDIR,
508: logname) >= sizeof(filename)) {
509: errno = ENAMETOOLONG;
510: warn("%s", logname);
511: continue;
512: }
513: fd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
514: if (fd == -1 || flock(fd, LOCK_EX) != 0 ||
515: (newfile = fdopen(fd, "r+")) == NULL) {
516: warn("%s", logname);
517: continue;
518: }
519: (void)fprintf(newfile, "%s\n%s\n%04d\n%s\n%s\n", logname,
520: hashtype, n, seed, val);
521: (void)fchown(fileno(newfile), uid, -1);
522: (void)fclose(newfile);
523: }
524: printf("%s has been populated. NOTE: %s has *not* been removed.\n"
525: "It should be removed once you have verified that the new keys "
526: "work.\n", _PATH_SKEYDIR, _PATH_SKEYKEYS);
527: }
528:
1.28 millert 529: #define TIMEOUT_MSG "Timed out waiting for input.\n"
530: void
1.36 millert 531: timedout(int signo)
1.28 millert 532: {
1.10 millert 533:
1.28 millert 534: write(STDERR_FILENO, TIMEOUT_MSG, sizeof(TIMEOUT_MSG) - 1);
535: _exit(1);
1.8 millert 536: }
537:
538: void
1.36 millert 539: usage(void)
1.8 millert 540: {
1.36 millert 541: extern char *__progname;
542:
543: (void)fprintf(stderr, "usage: %s [-r] [-s] [-x] [-C] [-D] [-E] "
1.32 millert 544: "[-a auth_type] [-n count]\n "
1.36 millert 545: "[-md4|-md5|-sha1|-rmd160] [user]\n", __progname);
1.8 millert 546: exit(1);
1.1 deraadt 547: }