diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2001-06-20 22:25:09 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2001-06-20 22:25:09 +0000 |
commit | 7fb52fb4d5d2c6136128b1358b4549d08f3dd3d8 (patch) | |
tree | 1fd445334c2391045c42c77d02ae7e34a4309547 | |
parent | b2ed4201f44c6105e504ec1feaf012eb4d2d8540 (diff) |
o When converting from a different hash type, if there is room on the line
we don't need to comment out the existing entry since the key almost
never takes up as much room as is allocated for it.
o Do per-record locking (and timeout) now that libskey does.
o Rearrange the code into functions.
o Use readpassphrase()
-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 |