[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.60

1.60    ! tim         1: /*     $OpenBSD: skeyinit.c,v 1.59 2015/10/03 13:10:47 tim 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:
1.20      millert    15: #include <sys/file.h>
1.32      millert    16: #include <sys/resource.h>
                     17: #include <sys/stat.h>
1.1       deraadt    18: #include <sys/time.h>
                     19:
1.32      millert    20: #include <ctype.h>
1.19      millert    21: #include <err.h>
1.20      millert    22: #include <errno.h>
1.19      millert    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.56      deraadt    31: #include <limits.h>
1.6       millert    32: #include <utmp.h>
1.19      millert    33:
1.4       millert    34: #include <skey.h>
1.32      millert    35: #include <bsd_auth.h>
1.1       deraadt    36:
1.4       millert    37: #ifndef SKEY_NAMELEN
1.17      millert    38: #define SKEY_NAMELEN    4
1.4       millert    39: #endif
1.1       deraadt    40:
1.36      millert    41: void   usage(void);
1.44      deraadt    42: void   secure_mode(int *, char *, char *, size_t, char *, size_t);
1.38      millert    43: void   normal_mode(char *, int, char *, char *);
1.32      millert    44: void   enable_db(int);
1.8       millert    45:
1.1       deraadt    46: int
1.36      millert    47: main(int argc, char **argv)
1.1       deraadt    48: {
1.60    ! tim        49:        int     rval, i, l, n, defaultsetup, rmkey, hexmode, enable;
1.56      deraadt    50:        char    hostname[HOST_NAME_MAX+1];
1.38      millert    51:        char    seed[SKEY_MAX_SEED_LEN + 1];
1.32      millert    52:        char    buf[256], key[SKEY_BINKEY_SIZE], filename[PATH_MAX], *ht;
                     53:        char    lastc, me[UT_NAMESIZE + 1], *p, *auth_type;
1.57      deraadt    54:        const char *errstr;
1.1       deraadt    55:        struct skey skey;
                     56:        struct passwd *pp;
                     57:
1.60    ! tim        58:        n = rmkey = hexmode = enable = 0;
1.32      millert    59:        defaultsetup = 1;
                     60:        ht = auth_type = NULL;
1.4       millert    61:
1.59      tim        62:        /* Build up a default seed based on the hostname and some randomness */
1.1       deraadt    63:        if (gethostname(hostname, sizeof(hostname)) < 0)
                     64:                err(1, "gethostname");
1.38      millert    65:        for (i = 0, p = seed; hostname[i] && i < SKEY_NAMELEN; i++) {
1.59      tim        66:                if (isalnum((unsigned char)hostname[i]))
                     67:                        *p++ = tolower((unsigned char)hostname[i]);
1.25      millert    68:        }
1.59      tim        69:        for (i = 0; i < 5; i++)
                     70:                *p++ = arc4random_uniform(10) + '0';
1.25      millert    71:        *p = '\0';
1.1       deraadt    72:
                     73:        if ((pp = getpwuid(getuid())) == NULL)
1.40      deraadt    74:                err(1, "no user with uid %u", getuid());
1.42      deraadt    75:        (void)strlcpy(me, pp->pw_name, sizeof me);
1.1       deraadt    76:
                     77:        if ((pp = getpwnam(me)) == NULL)
                     78:                err(1, "Who are you?");
                     79:
1.8       millert    80:        for (i = 1; i < argc && argv[i][0] == '-' && strcmp(argv[i], "--");) {
                     81:                if (argv[i][2] == '\0') {
                     82:                        /* Single character switch */
                     83:                        switch (argv[i][1]) {
1.32      millert    84:                        case 'a':
                     85:                                if (argv[++i] == NULL || argv[i][0] == '\0')
1.36      millert    86:                                        usage();
1.48      otto       87:                                auth_type = argv[i];
1.32      millert    88:                                break;
1.4       millert    89:                        case 's':
                     90:                                defaultsetup = 0;
1.48      otto       91:                                if (auth_type == NULL)
                     92:                                        auth_type = "skey";
1.4       millert    93:                                break;
                     94:                        case 'x':
                     95:                                hexmode = 1;
                     96:                                break;
1.35      millert    97:                        case 'r':
                     98:                                rmkey = 1;
1.4       millert    99:                                break;
1.17      millert   100:                        case 'n':
1.23      deraadt   101:                                if (argv[++i] == NULL || argv[i][0] == '\0')
1.36      millert   102:                                        usage();
1.57      deraadt   103:                                n = strtonum(argv[i], 1, SKEY_MAX_SEQ - 1, &errstr);
                    104:                                if (errstr)
1.17      millert   105:                                        errx(1, "count must be > 0 and < %d",
                    106:                                             SKEY_MAX_SEQ);
                    107:                                break;
1.32      millert   108:                        case 'D':
                    109:                                enable = -1;
                    110:                                break;
                    111:                        case 'E':
                    112:                                enable = 1;
                    113:                                break;
1.8       millert   114:                        default:
1.36      millert   115:                                usage();
1.8       millert   116:                        }
                    117:                } else {
                    118:                        /* Multi character switches are hash types */
                    119:                        if ((ht = skey_set_algorithm(&argv[i][1])) == NULL) {
                    120:                                warnx("Unknown hash algorithm %s", &argv[i][1]);
1.36      millert   121:                                usage();
1.8       millert   122:                        }
1.7       millert   123:                }
1.8       millert   124:                i++;
1.7       millert   125:        }
1.32      millert   126:        argv += i;
                    127:        argc -= i;
1.7       millert   128:
1.60    ! tim       129:        if (argc > 1 || (enable && argc))
1.36      millert   130:                usage();
1.32      millert   131:
1.60    ! tim       132:        /* Handle -D and -E */
        !           133:        if (enable) {
        !           134:                enable_db(enable);
1.34      millert   135:                exit(0);
                    136:        }
1.32      millert   137:
                    138:        /* Check for optional user string. */
                    139:        if (argc == 1) {
1.36      millert   140:                if ((pp = getpwnam(argv[0])) == NULL) {
1.16      millert   141:                        if (getuid() == 0) {
                    142:                                static struct passwd _pp;
                    143:
1.36      millert   144:                                _pp.pw_name = argv[0];
1.16      millert   145:                                pp = &_pp;
1.36      millert   146:                                warnx("Warning, user unknown: %s", argv[0]);
1.16      millert   147:                        } else {
1.36      millert   148:                                errx(1, "User unknown: %s", argv[0]);
1.16      millert   149:                        }
1.32      millert   150:                } else if (strcmp(pp->pw_name, me) != 0 && getuid() != 0) {
                    151:                        /* Only root can change other's S/Keys. */
                    152:                        errx(1, "Permission denied.");
1.1       deraadt   153:                }
                    154:        }
                    155:
1.41      millert   156:        switch (skey_haskey(pp->pw_name)) {
                    157:        case -1:
                    158:                if (errno == ENOENT || errno == EPERM)
                    159:                        errx(1, "S/Key disabled");
                    160:                else
                    161:                        err(1, "cannot open database");
                    162:                break;
                    163:        case 0:
                    164:                /* existing user */
                    165:                break;
                    166:        case 1:
1.48      otto      167:                if (!defaultsetup && strcmp(auth_type, "skey") == 0) {
1.41      millert   168:                        fprintf(stderr,
1.45      espie     169: "You must authenticate yourself before using S/Key for the first time.  In\n"
                    170: "secure mode this is normally done via an existing S/Key key.  However, since\n"
                    171: "you do not have an entry in the S/Key database you will have to specify an\n"
                    172: "alternate authentication type via the `-a' flag, e.g.\n"
1.54      ajacouto  173: "    \"skeyinit -s -a passwd\"\n\n"
1.45      espie     174: "Note that entering a plaintext password over a non-secure link defeats the\n"
                    175: "purpose of using S/Key in the fist place.\n");
1.41      millert   176:                        exit(1);
                    177:                }
                    178:                break;
                    179:        }
                    180:
1.28      millert   181:        if (defaultsetup)
1.32      millert   182:                fputs("Reminder - Only use this method if you are directly "
                    183:                    "connected\n           or have an encrypted channel.  If "
                    184:                    "you are using telnet,\n           hit return now and use "
                    185:                    "skeyinit -s.\n", stderr);
1.28      millert   186:
1.1       deraadt   187:        if (getuid() != 0) {
1.32      millert   188:                if ((pp = pw_dup(pp)) == NULL)
                    189:                        err(1, NULL);
                    190:                if (!auth_userokay(pp->pw_name, auth_type, NULL, NULL))
                    191:                        errx(1, "Password incorrect");
1.10      millert   192:        }
1.1       deraadt   193:
1.28      millert   194:        /*
                    195:         * Lookup and lock the record we are about to modify.
                    196:         * If this is a new entry this will prevent other users
                    197:         * from appending new entries (and clobbering ours).
                    198:         */
1.1       deraadt   199:        rval = skeylookup(&skey, pp->pw_name);
                    200:        switch (rval) {
1.4       millert   201:                case -1:
1.41      millert   202:                        err(1, "cannot open database");
1.21      millert   203:                        break;
1.4       millert   204:                case 0:
1.35      millert   205:                        /* remove user if asked to do so */
                    206:                        if (rmkey) {
                    207:                                if (snprintf(filename, sizeof(filename),
                    208:                                    "%s/%s", _PATH_SKEYDIR, pp->pw_name)
1.55      guenther  209:                                    >= sizeof(filename))
                    210:                                        errc(1, ENAMETOOLONG,
                    211:                                            "Cannot remove S/Key entry");
1.35      millert   212:                                if (unlink(filename) != 0)
                    213:                                        err(1, "Cannot remove S/Key entry");
                    214:                                printf("S/Key entry for %s removed.\n",
                    215:                                    pp->pw_name);
                    216:                                exit(0);
                    217:                        }
1.4       millert   218:
1.28      millert   219:                        (void)printf("[Updating %s with %s]\n", pp->pw_name,
                    220:                            ht ? ht : skey_get_algorithm());
                    221:                        (void)printf("Old seed: [%s] %s\n",
                    222:                                     skey_get_algorithm(), skey.seed);
1.4       millert   223:
                    224:                        /*
1.25      millert   225:                         * Sanity check old seed.
                    226:                         */
                    227:                        l = strlen(skey.seed);
                    228:                        for (p = skey.seed; *p; p++) {
1.52      deraadt   229:                                if (isalpha((unsigned char)*p)) {
                    230:                                        if (isupper((unsigned char)*p))
                    231:                                                *p = tolower((unsigned char)*p);
                    232:                                } else if (!isdigit((unsigned char)*p)) {
1.25      millert   233:                                        memmove(p, p + 1, l - (p - skey.seed));
                    234:                                        l--;
                    235:                                }
                    236:                        }
                    237:
1.28      millert   238:                        /* If the seed ends in 0-8 just add one.  */
1.4       millert   239:                        if (l > 0) {
                    240:                                lastc = skey.seed[l - 1];
1.52      deraadt   241:                                if (isdigit((unsigned char)lastc) &&
                    242:                                    lastc != '9') {
1.43      deraadt   243:                                        (void)strlcpy(seed, skey.seed,
                    244:                                            sizeof seed);
1.38      millert   245:                                        seed[l - 1] = lastc + 1;
1.4       millert   246:                                }
1.52      deraadt   247:                                if (isdigit((unsigned char)lastc) &&
                    248:                                    lastc == '9' && l < 16) {
1.43      deraadt   249:                                        (void)strlcpy(seed, skey.seed,
                    250:                                            sizeof seed);
1.38      millert   251:                                        seed[l - 1] = '0';
                    252:                                        seed[l] = '0';
                    253:                                        seed[l + 1] = '\0';
1.4       millert   254:                                }
1.1       deraadt   255:                        }
1.4       millert   256:                        break;
                    257:                case 1:
1.35      millert   258:                        if (rmkey)
                    259:                                errx(1, "You have no entry to remove.");
1.28      millert   260:                        (void)printf("[Adding %s with %s]\n", pp->pw_name,
                    261:                            ht ? ht : skey_get_algorithm());
1.32      millert   262:                        if (snprintf(filename, sizeof(filename), "%s/%s",
1.55      guenther  263:                            _PATH_SKEYDIR, pp->pw_name) >= sizeof(filename))
                    264:                                errc(1, ENAMETOOLONG,
                    265:                                    "Cannot create S/Key entry");
1.36      millert   266:                        if ((l = open(filename,
                    267:                            O_RDWR | O_NONBLOCK | O_CREAT | O_TRUNC |O_NOFOLLOW,
1.32      millert   268:                            S_IRUSR | S_IWUSR)) == -1 ||
                    269:                            flock(l, LOCK_EX) != 0 ||
                    270:                            (skey.keyfile = fdopen(l, "r+")) == NULL)
                    271:                                err(1, "Cannot create S/Key entry");
1.4       millert   272:                        break;
1.1       deraadt   273:        }
1.32      millert   274:        if (fchown(fileno(skey.keyfile), pp->pw_uid, -1) != 0 ||
                    275:            fchmod(fileno(skey.keyfile), S_IRUSR | S_IWUSR) != 0)
                    276:                err(1, "can't set owner/mode for %s", pp->pw_name);
1.17      millert   277:        if (n == 0)
1.47      otto      278:                n = 100;
1.1       deraadt   279:
1.8       millert   280:        /* Set hash type if asked to */
1.28      millert   281:        if (ht && strcmp(ht, skey_get_algorithm()) != 0)
                    282:                skey_set_algorithm(ht);
                    283:
                    284:        alarm(180);
                    285:        if (!defaultsetup)
1.44      deraadt   286:                secure_mode(&n, key, seed, sizeof seed, buf, sizeof(buf));
1.28      millert   287:        else
1.38      millert   288:                normal_mode(pp->pw_name, n, key, seed);
1.28      millert   289:        alarm(0);
1.4       millert   290:
1.32      millert   291:        /* XXX - why use malloc here? */
1.58      deraadt   292:        if ((skey.val = malloc(16 + 1)) == NULL)
1.28      millert   293:                err(1, "Can't allocate memory");
                    294:        btoa8(skey.val, key);
1.1       deraadt   295:
1.32      millert   296:        (void)fseek(skey.keyfile, 0L, SEEK_SET);
                    297:        (void)fprintf(skey.keyfile, "%s\n%s\n%04d\n%s\n%s\n",
                    298:            pp->pw_name, skey_get_algorithm(), n, seed, skey.val);
1.28      millert   299:        (void)fclose(skey.keyfile);
1.4       millert   300:
1.28      millert   301:        (void)printf("\nID %s skey is otp-%s %d %s\n", pp->pw_name,
1.38      millert   302:            skey_get_algorithm(), n, seed);
1.28      millert   303:        (void)printf("Next login password: %s\n\n",
                    304:            hexmode ? put8(buf, key) : btoe(buf, key));
                    305:        exit(0);
                    306: }
1.1       deraadt   307:
1.28      millert   308: void
1.44      deraadt   309: secure_mode(int *count, char *key, char *seed, size_t seedlen,
                    310:     char *buf, size_t bufsiz)
1.28      millert   311: {
1.38      millert   312:        char *p, newseed[SKEY_MAX_SEED_LEN + 2];
1.57      deraadt   313:        const char *errstr;
1.28      millert   314:        int i, n;
1.4       millert   315:
1.28      millert   316:        (void)puts("You need the 6 words generated from the \"skey\" command.");
                    317:        for (i = 0; ; i++) {
                    318:                if (i >= 2)
                    319:                        exit(1);
                    320:
                    321:                (void)printf("Enter sequence count from 1 to %d: ",
                    322:                    SKEY_MAX_SEQ);
                    323:                (void)fgets(buf, bufsiz, stdin);
                    324:                clearerr(stdin);
1.57      deraadt   325:                n = strtonum(buf, 1, SKEY_MAX_SEQ-1, &errstr);
                    326:                if (!errstr)
1.28      millert   327:                        break;  /* Valid range */
1.57      deraadt   328:                fprintf(stderr, "ERROR: Count must be between 1 and %d\n",
                    329:                    SKEY_MAX_SEQ - 1);
1.28      millert   330:        }
1.1       deraadt   331:
1.28      millert   332:        for (i = 0; ; i++) {
                    333:                if (i >= 2)
                    334:                        exit(1);
                    335:
1.38      millert   336:                (void)printf("Enter new seed [default %s]: ", seed);
                    337:                (void)fgets(newseed, sizeof(newseed), stdin); /* XXX */
1.28      millert   338:                clearerr(stdin);
1.38      millert   339:                rip(newseed);
                    340:                if (strlen(newseed) > SKEY_MAX_SEED_LEN) {
1.28      millert   341:                        (void)fprintf(stderr, "ERROR: Seed must be between 1 "
                    342:                            "and %d characters in length\n", SKEY_MAX_SEED_LEN);
                    343:                        continue;
                    344:                }
1.38      millert   345:                for (p = newseed; *p; p++) {
1.52      deraadt   346:                        if (isspace((unsigned char)*p)) {
1.28      millert   347:                                (void)fputs("ERROR: Seed must not contain "
                    348:                                    "any spaces\n", stderr);
1.1       deraadt   349:                                break;
1.52      deraadt   350:                        } else if (isalpha((unsigned char)*p)) {
                    351:                                if (isupper((unsigned char)*p))
                    352:                                        *p = tolower((unsigned char)*p);
                    353:                        } else if (!isdigit((unsigned char)*p)) {
1.28      millert   354:                                (void)fputs("ERROR: Seed must be purely "
                    355:                                    "alphanumeric\n", stderr);
                    356:                                break;
                    357:                        }
1.1       deraadt   358:                }
1.28      millert   359:                if (*p == '\0')
                    360:                        break;  /* Valid seed */
                    361:        }
1.38      millert   362:        if (newseed[0] != '\0')
1.44      deraadt   363:                (void)strlcpy(seed, newseed, seedlen);
1.1       deraadt   364:
1.28      millert   365:        for (i = 0; ; i++) {
                    366:                if (i >= 2)
                    367:                        exit(1);
                    368:
                    369:                (void)printf("otp-%s %d %s\nS/Key access password: ",
                    370:                             skey_get_algorithm(), n, seed);
                    371:                (void)fgets(buf, bufsiz, stdin);
                    372:                clearerr(stdin);
                    373:                rip(buf);
                    374:                backspace(buf);
                    375:
                    376:                if (buf[0] == '?') {
                    377:                        (void)puts("Enter 6 words from secure S/Key calculation.");
                    378:                        continue;
                    379:                } else if (buf[0] == '\0')
                    380:                        exit(1);
                    381:
                    382:                if (etob(key, buf) == 1 || atob8(key, buf) == 0)
                    383:                        break;  /* Valid format */
                    384:                (void)fputs("ERROR: Invalid format - try again with the 6 words.\n",
                    385:                    stderr);
1.1       deraadt   386:        }
1.28      millert   387:        *count= n;
                    388: }
1.1       deraadt   389:
1.28      millert   390: void
1.38      millert   391: normal_mode(char *username, int n, char *key, char *seed)
1.28      millert   392: {
                    393:        int i, nn;
1.38      millert   394:        char passwd[SKEY_MAX_PW_LEN+2], key2[SKEY_BINKEY_SIZE];
1.1       deraadt   395:
1.28      millert   396:        /* Get user's secret passphrase */
                    397:        for (i = 0; ; i++) {
                    398:                if (i > 2)
1.38      millert   399:                        errx(1, "S/Key entry not updated");
1.28      millert   400:
1.46      otto      401:                if (readpassphrase("Enter new secret passphrase: ", passwd,
1.28      millert   402:                    sizeof(passwd), 0) == NULL || passwd[0] == '\0')
                    403:                        exit(1);
                    404:
                    405:                if (strlen(passwd) < SKEY_MIN_PW_LEN) {
                    406:                        (void)fprintf(stderr,
                    407:                            "ERROR: Your passphrase must be at least %d "
                    408:                            "characters long.\n", SKEY_MIN_PW_LEN);
                    409:                        continue;
                    410:                } else if (strcmp(passwd, username) == 0) {
                    411:                        (void)fputs("ERROR: Your passphrase may not be the "
                    412:                            "same as your user name.\n", stderr);
                    413:                        continue;
1.50      deraadt   414:                } else if (strspn(passwd, "abcdefghijklmnopqrstuvwxyz") ==
1.28      millert   415:                    strlen(passwd)) {
                    416:                        (void)fputs("ERROR: Your passphrase must contain more "
                    417:                            "than just lower case letters.\nWhitespace, "
1.37      aaron     418:                            "numbers, and punctuation are suggested.\n",
                    419:                            stderr);
1.28      millert   420:                        continue;
                    421:                } else if (strlen(passwd) > 63) {
                    422:                        (void)fprintf(stderr, "WARNING: Your passphrase is "
                    423:                            "longer than the recommended maximum length of 63\n");
                    424:                }
                    425:                /* XXX - should check for passphrase that is really too long */
1.13      millert   426:
1.38      millert   427:                /* Crunch seed and passphrase into starting key */
                    428:                nn = keycrunch(key, seed, passwd);
1.59      tim       429:                explicit_bzero(passwd, sizeof(passwd));
1.38      millert   430:                if (nn != 0)
                    431:                        err(2, "key crunch failed");
                    432:
                    433:                if (readpassphrase("Again secret passphrase: ", passwd,
                    434:                    sizeof(passwd), 0) == NULL || passwd[0] == '\0')
                    435:                        exit(1);
                    436:
                    437:                /* Crunch seed and passphrase into starting key */
                    438:                nn = keycrunch(key2, seed, passwd);
1.59      tim       439:                explicit_bzero(passwd, sizeof(passwd));
1.38      millert   440:                if (nn != 0)
                    441:                        err(2, "key crunch failed");
                    442:
                    443:                if (memcmp(key, key2, sizeof(key2)) == 0)
1.28      millert   444:                        break;
1.1       deraadt   445:
1.28      millert   446:                (void)fputs("Passphrases do not match.\n", stderr);
1.20      millert   447:        }
1.28      millert   448:
                    449:        nn = n;
                    450:        while (nn-- != 0)
                    451:                f(key);
                    452: }
                    453:
1.32      millert   454: void
1.36      millert   455: enable_db(int op)
1.32      millert   456: {
                    457:        if (op == 1) {
                    458:                /* enable */
                    459:                if (mkdir(_PATH_SKEYDIR, 01730) != 0 && errno != EEXIST)
                    460:                        err(1, "can't mkdir %s", _PATH_SKEYDIR);
1.33      millert   461:                if (chown(_PATH_SKEYDIR, geteuid(), getegid()) != 0)
                    462:                        err(1, "can't chown %s", _PATH_SKEYDIR);
1.32      millert   463:                if (chmod(_PATH_SKEYDIR, 01730) != 0)
                    464:                        err(1, "can't chmod %s", _PATH_SKEYDIR);
                    465:        } else {
                    466:                /* disable */
                    467:                if (chmod(_PATH_SKEYDIR, 0) != 0 && errno != ENOENT)
                    468:                        err(1, "can't chmod %s", _PATH_SKEYDIR);
                    469:        }
                    470: }
                    471:
1.8       millert   472: void
1.36      millert   473: usage(void)
1.8       millert   474: {
1.36      millert   475:        extern char *__progname;
                    476:
1.60    ! tim       477:        (void)fprintf(stderr, "usage: %s [-DErsx] [-a auth-type] [-n count]"
1.53      naddy     478:            "\n\t[-md5 | -rmd160 | -sha1] [user]\n", __progname);
1.8       millert   479:        exit(1);
1.1       deraadt   480: }