diff options
-rw-r--r-- | usr.bin/skeyinit/skeyinit.1 | 8 | ||||
-rw-r--r-- | usr.bin/skeyinit/skeyinit.c | 443 |
2 files changed, 288 insertions, 163 deletions
diff --git a/usr.bin/skeyinit/skeyinit.1 b/usr.bin/skeyinit/skeyinit.1 index f7bc468ab89..d11e1825702 100644 --- a/usr.bin/skeyinit/skeyinit.1 +++ b/usr.bin/skeyinit/skeyinit.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: skeyinit.1,v 1.19 2000/11/09 17:52:39 aaron Exp $ +.\" $OpenBSD: skeyinit.1,v 1.20 2001/06/20 22:25:08 millert Exp $ .\" $NetBSD: skeyinit.1,v 1.4 1995/07/07 22:24:09 jtc Exp $ .\" @(#)skeyinit.1 1.1 10/28/93 .\" @@ -97,16 +97,18 @@ The username to be changed/added. By default the current user is operated on. .El .Sh ERRORS -.Bl -tag -width "skey disabled" -.It skey disabled +.Bl -tag -compact -width "skey disabled" +.It "skey disabled" .Pa /etc/skeykeys does not exist. It must be created by the superuser in order to use .Nm skeyinit . +.El .Sh FILES .Bl -tag -width /etc/skeykeys .It Pa /etc/skeykeys database of information for S/Key system +.El .Sh SEE ALSO .Xr skey 1 .Sh AUTHORS diff --git a/usr.bin/skeyinit/skeyinit.c b/usr.bin/skeyinit/skeyinit.c index 7f3407dae25..7ab7378f276 100644 --- a/usr.bin/skeyinit/skeyinit.c +++ b/usr.bin/skeyinit/skeyinit.c @@ -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 <nmh@thumper.bellcore.com> * Philip R. Karn <karn@chicago.qualcomm.com> * John S. Walden <jsw@thumper.bellcore.com> * Scott Chasin <chasin@crimelab.com> - * - * Modifications: * Todd C. Miller <Todd.Miller@courtesan.com> * - * S/KEY initialization and seed update + * S/Key initialization and seed update */ #include <sys/param.h> @@ -23,6 +21,7 @@ #include <errno.h> #include <ctype.h> #include <pwd.h> +#include <readpassphrase.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -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 @@ main(argc, argv) 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 @@ main(argc, argv) } } + 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 @@ main(argc, argv) 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 @@ main(argc, argv) } } - /* - * 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 @@ main(argc, argv) 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; - /* 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; - } + /* 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; - 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); + /* Set hash type if asked to */ + if (ht && strcmp(ht, skey_get_algorithm()) != 0) + skey_set_algorithm(ht); - (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); - } + 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); - for (i = 0;; i++) { - if (i >= 2) - exit(1); + (void)time(&now); + tm = localtime(&now); + (void)strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm); - (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 */ - } - 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 this is an exiting entry, compute the line length and seed pad */ + seedlen = SKEY_MAX_SEED_LEN; + if (rval == 0) { + int nlen; + + 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; } - for (i = 0;; i++) { - if (i >= 2) - exit(1); + /* 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); + } + } - (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); + if ((skey.val = (char *)malloc(16 + 1)) == NULL) + err(1, "Can't allocate memory"); + btoa8(skey.val, key); - 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); + /* 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); - for (i = 0;; i++) { - if (i > 2) - exit(1); + (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); +} - (void)fputs("Enter secret password: ", stderr); - readpass(passwd, sizeof(passwd)); - if (passwd[0] == '\0') - exit(1); +void +lockeof(mp, user) + struct skey *mp; + char *user; +{ + struct flock fl; + + 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 (fcntl(fileno(mp->keyfile), F_SETLKW, &fl) == -1) + err(1, "Can't lock database"); + + /* 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 */ + + fclose(mp->keyfile); + if (skeylookup(mp, user) != 1) + errx(1, "user %s already added", user); + goto dolock; +} - 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; - } +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; + + (void)puts("You need the 6 words generated from the \"skey\" command."); + for (i = 0; ; i++) { + if (i >= 2) + exit(1); + + (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)fputs("Again secret password: ", stderr); - readpass(passwd2, sizeof(passwd)); + for (i = 0; ; i++) { + if (i >= 2) + exit(1); - if (strcmp(passwd, passwd2) == 0) + (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; - - (void)fputs("Passwords do not match.\n", stderr); + } 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; + } } - - /* Crunch seed and password into starting key */ - (void)strcpy(seed, defaultseed); - if (keycrunch(key, seed, passwd) != 0) - err(2, "key crunch failed"); - - nn = n; - while (nn-- != 0) - f(key); + if (*p == '\0') + break; /* Valid seed */ } - (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"); - - /* 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"); + for (i = 0; ; i++) { + if (i >= 2) + exit(1); + + (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; +} - btoa8(skey.val, key); +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 */ - /* - * 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) + if (readpassphrase("Again secret passphrase: ", passwd2, + sizeof(passwd2), 0) && strcmp(passwd, passwd2) == 0) break; - usleep(100000); /* Sleep for 0.1 seconds */ - } - if (rval == -1) { /* Can't get exclusive lock */ - errno = EAGAIN; - err(1, "cannot open database"); + + (void)fputs("Passphrases do not match.\n", stderr); } - /* 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 |