=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/skeyinit/skeyinit.c,v retrieving revision 1.27 retrieving revision 1.28 diff -u -r1.27 -r1.28 --- src/usr.bin/skeyinit/skeyinit.c 2001/01/26 16:27:04 1.27 +++ src/usr.bin/skeyinit/skeyinit.c 2001/06/20 22:25:08 1.28 @@ -1,17 +1,15 @@ -/* $OpenBSD: skeyinit.c,v 1.27 2001/01/26 16:27:04 millert Exp $ */ +/* $OpenBSD: skeyinit.c,v 1.28 2001/06/20 22:25:08 millert Exp $ */ -/* S/KEY v1.1b (skeyinit.c) +/* OpenBSD S/Key (skeyinit.c) * * Authors: * Neil M. Haller * Philip R. Karn * John S. Walden * Scott Chasin - * - * Modifications: * Todd C. Miller * - * S/KEY initialization and seed update + * S/Key initialization and seed update */ #include @@ -23,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -37,20 +36,26 @@ #define SKEY_NAMELEN 4 #endif +void lockeof __P((struct skey *, char *)); void usage __P((char *)); +void secure_mode __P((int *, char *, char *, char *, char *, size_t)); +void normal_mode __P((char *, int, char *, char *, char *)); +void timedout __P((int)); int main(argc, argv) int argc; char *argv[]; { - int rval, nn, i, l, n=0, defaultsetup=1, zerokey=0, hexmode=0; + int rval, i, l, n=0, defaultsetup=1, zerokey=0, hexmode=0; + int oldmd4=0; time_t now; + size_t seedlen; char hostname[MAXHOSTNAMELEN]; - char passwd[SKEY_MAX_PW_LEN+2], passwd2[SKEY_MAX_PW_LEN+2]; + char passwd[SKEY_MAX_PW_LEN+2]; char seed[SKEY_MAX_SEED_LEN+2], defaultseed[SKEY_MAX_SEED_LEN+1]; - char tbuf[27], buf[80], key[SKEY_BINKEY_SIZE]; - char lastc, me[UT_NAMESIZE+1], *salt, *p, *pw, *ht=NULL; + char tbuf[27], buf[256], key[SKEY_BINKEY_SIZE]; + char lastc, me[UT_NAMESIZE+1], *salt, *p, *ht=NULL; struct skey skey; struct passwd *pp; struct tm *tm; @@ -58,6 +63,7 @@ if (geteuid() != 0) errx(1, "must be setuid root."); + /* Build up a default seed based on the hostname and time */ if (gethostname(hostname, sizeof(hostname)) < 0) err(1, "gethostname"); for (i = 0, p = defaultseed; hostname[i] && i < SKEY_NAMELEN; i++) { @@ -136,20 +142,41 @@ } } + if (defaultsetup) + 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); + if (getuid() != 0) { - pw = getpass("Password (or `s/key'):"); - if (strcasecmp(pw, "s/key") == 0) { - if (skey_haskey(me)) - exit(1); - if (skey_authenticate(me)) + /* XXX - use BSD auth */ + passwd[0] = '\0'; + if (!defaultsetup && skeychallenge(&skey, me, buf) == 0) { + printf("Enter S/Key password below or hit return twice " + "to enter standard password.\n%s\n", buf); + fflush(stdout); + if (!readpassphrase("S/Key Password: ", passwd, + sizeof(passwd), 0) || passwd[0] == '\0') { + readpassphrase("S/Key Password: [echo on] ", + passwd, sizeof(passwd), RPP_ECHO_ON); + } + } + if (passwd[0]) { + if (skeyverify(&skey, passwd) != 0) errx(1, "Password incorrect."); } else { - p = crypt(pw, salt); - if (strcmp(p, pp->pw_passwd)) - errx(1, "Password incorrect."); + fflush(stdout); + readpassphrase("Password: ", passwd, sizeof(passwd), 0); + if (strcmp(crypt(passwd, salt), pp->pw_passwd)) { + if (passwd[0]) + warnx("Password incorrect."); + exit(1); + } } } + /* + * Lookup and lock the record we are about to modify. + * If this is a new entry this will prevent other users + * from appending new entries (and clobbering ours). + */ rval = skeylookup(&skey, pp->pw_name); switch (rval) { case -1: @@ -163,9 +190,10 @@ if (zerokey) exit(skeyzero(&skey, pp->pw_name)); - (void)printf("[Updating %s]\n", pp->pw_name); - (void)printf("Old key: [%s] %s\n", skey_get_algorithm(), - skey.seed); + (void)printf("[Updating %s with %s]\n", pp->pw_name, + ht ? ht : skey_get_algorithm()); + (void)printf("Old seed: [%s] %s\n", + skey_get_algorithm(), skey.seed); /* * Sanity check old seed. @@ -181,10 +209,7 @@ } } - /* - * Let's be nice if they have an skey.seed that - * ends in 0-8 just add one - */ + /* If the seed ends in 0-8 just add one. */ if (l > 0) { lastc = skey.seed[l - 1]; if (isdigit(lastc) && lastc != '9') { @@ -202,172 +227,270 @@ case 1: if (zerokey) errx(1, "You have no entry to zero."); - (void)printf("[Adding %s]\n", pp->pw_name); + (void)printf("[Adding %s with %s]\n", pp->pw_name, + ht ? ht : skey_get_algorithm()); + lockeof(&skey, pp->pw_name); break; } if (n == 0) n = 99; + /* Do we have an old-style md4 entry? */ + if (rval == 0 && strcmp("md4", skey_get_algorithm()) == 0 && + strcmp("md4", skey.logname + strlen(skey.logname) + 1) != 0) + oldmd4 = 1; + /* Set hash type if asked to */ - if (ht) { - /* Need to zero out old key when changing algorithm */ - if (strcmp(ht, skey_get_algorithm()) && skey_set_algorithm(ht)) - zerokey = 1; - } + if (ht && strcmp(ht, skey_get_algorithm()) != 0) + skey_set_algorithm(ht); - if (!defaultsetup) { - (void)printf("You need the 6 english words generated from the \"skey\" command.\n"); - for (i = 0; ; i++) { - if (i >= 2) - exit(1); + alarm(180); + if (!defaultsetup) + secure_mode(&n, key, seed, defaultseed, buf, sizeof(buf)); + else + normal_mode(pp->pw_name, n, key, seed, defaultseed); + alarm(0); - (void)printf("Enter sequence count from 1 to %d: ", - SKEY_MAX_SEQ); - (void)fgets(buf, sizeof(buf), stdin); - n = atoi(buf); - if (n > 0 && n < SKEY_MAX_SEQ) - break; /* Valid range */ - (void)printf("Error: Count must be > 0 and < %d\n", - SKEY_MAX_SEQ); - } + (void)time(&now); + tm = localtime(&now); + (void)strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm); - for (i = 0;; i++) { - if (i >= 2) - exit(1); + /* If this is an exiting entry, compute the line length and seed pad */ + seedlen = SKEY_MAX_SEED_LEN; + if (rval == 0) { + int nlen; - (void)printf("Enter new key [default %s]: ", - defaultseed); - (void)fgets(seed, sizeof(seed), stdin); - rip(seed); - if (seed[0] == '\0') - (void)strcpy(seed, defaultseed); - for (p = seed; *p; p++) { - if (isalpha(*p)) { - if (isupper(*p)) - *p = tolower(*p); - } else if (!isdigit(*p)) { - (void)puts("Error: seed may only contain alpha numeric characters"); - break; - } - } - if (*p == '\0') - break; /* Valid seed */ + nlen = strlen(pp->pw_name) + 1 + strlen(skey_get_algorithm()) + + 1 + 4 + 1 + strlen(seed) + 1 + 16 + 1 + strlen(tbuf) + 1; + + /* + * If there was no hash type (md4) add one unless we + * are short on space. + */ + if (oldmd4) { + if (nlen > skey.len) + nlen -= 4; + else + oldmd4 = 0; } - if (strlen(seed) > SKEY_MAX_SEED_LEN) { - (void)printf("Notice: Seed truncated to %d characters.\n", - SKEY_MAX_SEED_LEN); - seed[SKEY_MAX_SEED_LEN] = '\0'; + + /* If new entry is longer than the old, comment out the old. */ + if (nlen > skey.len) { + (void)skeyzero(&skey, pp->pw_name); + /* Re-open keys file and seek to the end */ + if (skeylookup(&skey, pp->pw_name) == -1) + err(1, "cannot reopen database"); + lockeof(&skey, pp->pw_name); + } else { + /* Compute how much to space-pad the seed */ + seedlen = strlen(seed) + (skey.len - nlen); } + } - for (i = 0;; i++) { - if (i >= 2) - exit(1); + if ((skey.val = (char *)malloc(16 + 1)) == NULL) + err(1, "Can't allocate memory"); + btoa8(skey.val, key); - (void)printf("otp-%s %d %s\nS/Key access password: ", - skey_get_algorithm(), n, seed); - (void)fgets(buf, sizeof(buf), stdin); - rip(buf); - backspace(buf); + /* Don't save algorithm type for md4 (maintain record length) */ + /* XXX - should check return values of fprintf + fclose */ + if (oldmd4) + (void)fprintf(skey.keyfile, "%s %04d %-* %s %-21s\n", + pp->pw_name, n, seedlen, seed, skey.val, tbuf); + else + (void)fprintf(skey.keyfile, "%s %s %04d %-*s %s %-21s\n", + pp->pw_name, skey_get_algorithm(), n, seedlen, seed, + skey.val, tbuf); + (void)fclose(skey.keyfile); - if (buf[0] == '?') { - (void)puts("Enter 6 English words from secure S/Key calculation."); - continue; - } else if (buf[0] == '\0') - exit(1); - if (etob(key, buf) == 1 || atob8(key, buf) == 0) - break; /* Valid format */ - (void)puts("Invalid format - try again with 6 English words."); - } - } else { - /* Get user's secret password */ - 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, exit with no password and use skeyinit -s.\n", stderr); + (void)printf("\nID %s skey is otp-%s %d %s\n", pp->pw_name, + skey_get_algorithm(), n, seed); + (void)printf("Next login password: %s\n\n", + hexmode ? put8(buf, key) : btoe(buf, key)); + exit(0); +} - for (i = 0;; i++) { - if (i > 2) - exit(1); +void +lockeof(mp, user) + struct skey *mp; + char *user; +{ + struct flock fl; - (void)fputs("Enter secret password: ", stderr); - readpass(passwd, sizeof(passwd)); - if (passwd[0] == '\0') - exit(1); + fseek(mp->keyfile, 0, SEEK_END); +dolock: + fl.l_start = ftell(mp->keyfile); + fl.l_len = mp->len; + fl.l_pid = getpid(); + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; - if (strlen(passwd) < SKEY_MIN_PW_LEN) { - (void)fprintf(stderr, - "Your password must be at least %d characters long.\n", SKEY_MIN_PW_LEN); - continue; - } else if (strcmp(passwd, pp->pw_name) == 0) { - (void)fputs("Your password may not be the same as your user name.\n", stderr); - continue; - } else if (strspn(passwd, "abcdefghijklmnopqrstuvwxyz") == strlen(passwd)) { - (void)fputs("Your password must contain more than just lower case letters.\nWhitespace, numbers, and puctuation are suggested.\n", stderr); - continue; - } + if (fcntl(fileno(mp->keyfile), F_SETLKW, &fl) == -1) + err(1, "Can't lock database"); - (void)fputs("Again secret password: ", stderr); - readpass(passwd2, sizeof(passwd)); + /* Make sure we are still at the end. */ + fseek(mp->keyfile, 0, SEEK_END); + if (fl.l_start == ftell(mp->keyfile)) + return; /* still at EOF */ - if (strcmp(passwd, passwd2) == 0) - break; + fclose(mp->keyfile); + if (skeylookup(mp, user) != 1) + errx(1, "user %s already added", user); + goto dolock; +} - (void)fputs("Passwords do not match.\n", stderr); - } +void +secure_mode(count, key, seed, defaultseed, buf, bufsiz) + int *count; + char *key; + char *seed; + char *defaultseed; + char *buf; + size_t bufsiz; +{ + int i, n; + char *p; - /* Crunch seed and password into starting key */ - (void)strcpy(seed, defaultseed); - if (keycrunch(key, seed, passwd) != 0) - err(2, "key crunch failed"); + (void)puts("You need the 6 words generated from the \"skey\" command."); + for (i = 0; ; i++) { + if (i >= 2) + exit(1); - nn = n; - while (nn-- != 0) - f(key); + (void)printf("Enter sequence count from 1 to %d: ", + SKEY_MAX_SEQ); + (void)fgets(buf, bufsiz, stdin); + clearerr(stdin); + n = atoi(buf); + if (n > 0 && n < SKEY_MAX_SEQ) + break; /* Valid range */ + (void)fprintf(stderr, "ERROR: Count must be between 1 and %d\n", + SKEY_MAX_SEQ); } - (void)time(&now); - tm = localtime(&now); - (void)strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm); - if ((skey.val = (char *)malloc(16 + 1)) == NULL) - err(1, "Can't allocate memory"); + for (i = 0; ; i++) { + if (i >= 2) + exit(1); - /* Zero out old key if necesary (entry would change size) */ - if (zerokey) { - (void)skeyzero(&skey, pp->pw_name); - /* Re-open keys file and seek to the end */ - if (skeylookup(&skey, pp->pw_name) == -1) - err(1, "cannot open database"); + (void)printf("Enter new seed [default %s]: ", + defaultseed); + (void)fgets(seed, SKEY_MAX_SEED_LEN+2, stdin); /* XXX */ + clearerr(stdin); + rip(seed); + if (strlen(seed) > SKEY_MAX_SEED_LEN) { + (void)fprintf(stderr, "ERROR: Seed must be between 1 " + "and %d characters in length\n", SKEY_MAX_SEED_LEN); + continue; + } + if (seed[0] == '\0') + (void)strcpy(seed, defaultseed); + for (p = seed; *p; p++) { + if (isspace(*p)) { + (void)fputs("ERROR: Seed must not contain " + "any spaces\n", stderr); + break; + } else if (isalpha(*p)) { + if (isupper(*p)) + *p = tolower(*p); + } else if (!isdigit(*p)) { + (void)fputs("ERROR: Seed must be purely " + "alphanumeric\n", stderr); + break; + } + } + if (*p == '\0') + break; /* Valid seed */ } - btoa8(skey.val, key); + for (i = 0; ; i++) { + if (i >= 2) + exit(1); - /* - * Obtain an exclusive lock on the key file so we don't - * clobber someone authenticating themselves at the same time. - */ - for (i = 0; i < 300; i++) { - if ((rval = flock(fileno(skey.keyfile), LOCK_EX|LOCK_NB)) == 0 - || errno != EWOULDBLOCK) + (void)printf("otp-%s %d %s\nS/Key access password: ", + skey_get_algorithm(), n, seed); + (void)fgets(buf, bufsiz, stdin); + clearerr(stdin); + rip(buf); + backspace(buf); + + if (buf[0] == '?') { + (void)puts("Enter 6 words from secure S/Key calculation."); + continue; + } else if (buf[0] == '\0') + exit(1); + + if (etob(key, buf) == 1 || atob8(key, buf) == 0) + break; /* Valid format */ + (void)fputs("ERROR: Invalid format - try again with the 6 words.\n", + stderr); + } + *count= n; +} + +void +normal_mode(username, n, key, seed, defaultseed) + char *username; + int n; + char *key; + char *seed; + char *defaultseed; +{ + int i, nn; + char passwd[SKEY_MAX_PW_LEN+2], passwd2[SKEY_MAX_PW_LEN+2]; + + /* Get user's secret passphrase */ + for (i = 0; ; i++) { + if (i > 2) + exit(1); + + if (readpassphrase("Enter secret passphrase: ", passwd, + sizeof(passwd), 0) == NULL || passwd[0] == '\0') + exit(1); + + if (strlen(passwd) < SKEY_MIN_PW_LEN) { + (void)fprintf(stderr, + "ERROR: Your passphrase must be at least %d " + "characters long.\n", SKEY_MIN_PW_LEN); + continue; + } else if (strcmp(passwd, username) == 0) { + (void)fputs("ERROR: Your passphrase may not be the " + "same as your user name.\n", stderr); + continue; + } else if (strspn(passwd, "abcdefghijklmnopqrstuvwxyz") == + strlen(passwd)) { + (void)fputs("ERROR: Your passphrase must contain more " + "than just lower case letters.\nWhitespace, " + "numbers, and puctuation are suggested.\n", stderr); + continue; + } else if (strlen(passwd) > 63) { + (void)fprintf(stderr, "WARNING: Your passphrase is " + "longer than the recommended maximum length of 63\n"); + } + /* XXX - should check for passphrase that is really too long */ + + if (readpassphrase("Again secret passphrase: ", passwd2, + sizeof(passwd2), 0) && strcmp(passwd, passwd2) == 0) break; - usleep(100000); /* Sleep for 0.1 seconds */ + + (void)fputs("Passphrases do not match.\n", stderr); } - if (rval == -1) { /* Can't get exclusive lock */ - errno = EAGAIN; - err(1, "cannot open database"); - } - /* Don't save algorithm type for md4 (keep record length same) */ - if (strcmp(skey_get_algorithm(), "md4") == 0) - (void)fprintf(skey.keyfile, "%s %04d %-16s %s %-21s\n", - pp->pw_name, n, seed, skey.val, tbuf); - else - (void)fprintf(skey.keyfile, "%s %s %04d %-16s %s %-21s\n", - pp->pw_name, skey_get_algorithm(), n, seed, skey.val, tbuf); + /* Crunch seed and passphrase into starting key */ + (void)strcpy(seed, defaultseed); + if (keycrunch(key, seed, passwd) != 0) + err(2, "key crunch failed"); - (void)fclose(skey.keyfile); + nn = n; + while (nn-- != 0) + f(key); +} - (void)printf("\nID %s skey is otp-%s %d %s\n", pp->pw_name, - skey_get_algorithm(), n, seed); - (void)printf("Next login password: %s\n\n", - hexmode ? put8(buf, key) : btoe(buf, key)); - exit(0); +#define TIMEOUT_MSG "Timed out waiting for input.\n" +void +timedout(signo) + int signo; +{ + + write(STDERR_FILENO, TIMEOUT_MSG, sizeof(TIMEOUT_MSG) - 1); + _exit(1); } void