[BACK]Return to skeyinit.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / skeyinit

Annotation of src/usr.bin/skeyinit/skeyinit.c, Revision 1.31

1.31    ! millert     1: /*     $OpenBSD: skeyinit.c,v 1.30 2001/11/01 18:26:58 miod 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.31    ! millert    39: void   lockeof(struct skey *, char *);
        !            40: void   usage(char *);
        !            41: void   secure_mode(int *, char *, char *, char *, char *, size_t);
        !            42: void   normal_mode(char *, int, char *, char *, char *);
        !            43: void   timedout(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)
1.29      millert   191:                                exit(skeyzero(&skey));
1.4       millert   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) {
1.29      millert   279:                        (void)skeyzero(&skey);
1.28      millert   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)
1.30      miod      297:                (void)fprintf(skey.keyfile, "%s %04d %-*s %s %-21s\n",
1.28      millert   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: }