[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.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: }