Annotation of src/usr.bin/skeyinit/skeyinit.c, Revision 1.28
1.28 ! millert 1: /* $OpenBSD: skeyinit.c,v 1.27 2001/01/26 16:27:04 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.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.28 ! millert 39: void lockeof __P((struct skey *, char *));
1.8 millert 40: void usage __P((char *));
1.28 ! millert 41: void secure_mode __P((int *, char *, char *, char *, char *, size_t));
! 42: void normal_mode __P((char *, int, char *, char *, char *));
! 43: void timedout __P((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)
191: exit(skeyzero(&skey, pp->pw_name));
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) {
! 279: (void)skeyzero(&skey, pp->pw_name);
! 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)
! 297: (void)fprintf(skey.keyfile, "%s %04d %-* %s %-21s\n",
! 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: }