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