[BACK]Return to auth2-pubkeyfile.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Annotation of src/usr.bin/ssh/auth2-pubkeyfile.c, Revision 1.3

1.3     ! djm         1: /* $OpenBSD: auth2-pubkeyfile.c,v 1.2 2022/06/03 04:47:21 djm Exp $ */
1.1       djm         2: /*
                      3:  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
                      4:  * Copyright (c) 2010 Damien Miller.  All rights reserved.
                      5:  *
                      6:  * Redistribution and use in source and binary forms, with or without
                      7:  * modification, are permitted provided that the following conditions
                      8:  * are met:
                      9:  * 1. Redistributions of source code must retain the above copyright
                     10:  *    notice, this list of conditions and the following disclaimer.
                     11:  * 2. Redistributions in binary form must reproduce the above copyright
                     12:  *    notice, this list of conditions and the following disclaimer in the
                     13:  *    documentation and/or other materials provided with the distribution.
                     14:  *
                     15:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
                     16:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
                     17:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
                     18:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
                     19:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
                     20:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
                     21:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
                     22:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     23:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
                     24:  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     25:  */
                     26:
                     27:
                     28: #include <sys/types.h>
                     29: #include <sys/stat.h>
                     30:
                     31: #include <stdlib.h>
                     32: #include <errno.h>
                     33: #include <fcntl.h>
                     34: #include <pwd.h>
                     35: #include <stdio.h>
                     36: #include <stdarg.h>
                     37: #include <string.h>
                     38: #include <time.h>
                     39: #include <unistd.h>
                     40:
                     41: #include "ssh.h"
                     42: #include "log.h"
                     43: #include "misc.h"
                     44: #include "compat.h"
                     45: #include "sshkey.h"
                     46: #include "digest.h"
                     47: #include "hostfile.h"
                     48: #include "auth.h"
                     49: #include "auth-options.h"
                     50: #include "authfile.h"
                     51: #include "match.h"
                     52: #include "ssherr.h"
                     53:
                     54: int
                     55: auth_authorise_keyopts(struct passwd *pw, struct sshauthopt *opts,
                     56:     int allow_cert_authority, const char *remote_ip, const char *remote_host,
                     57:     const char *loc)
                     58: {
                     59:        time_t now = time(NULL);
                     60:        char buf[64];
                     61:
                     62:        /*
                     63:         * Check keys/principals file expiry time.
                     64:         * NB. validity interval in certificate is handled elsewhere.
                     65:         */
                     66:        if (opts->valid_before && now > 0 &&
                     67:            opts->valid_before < (uint64_t)now) {
                     68:                format_absolute_time(opts->valid_before, buf, sizeof(buf));
                     69:                debug("%s: entry expired at %s", loc, buf);
                     70:                auth_debug_add("%s: entry expired at %s", loc, buf);
                     71:                return -1;
                     72:        }
                     73:        /* Consistency checks */
                     74:        if (opts->cert_principals != NULL && !opts->cert_authority) {
                     75:                debug("%s: principals on non-CA key", loc);
                     76:                auth_debug_add("%s: principals on non-CA key", loc);
                     77:                /* deny access */
                     78:                return -1;
                     79:        }
                     80:        /* cert-authority flag isn't valid in authorized_principals files */
                     81:        if (!allow_cert_authority && opts->cert_authority) {
                     82:                debug("%s: cert-authority flag invalid here", loc);
                     83:                auth_debug_add("%s: cert-authority flag invalid here", loc);
                     84:                /* deny access */
                     85:                return -1;
                     86:        }
                     87:
                     88:        /* Perform from= checks */
                     89:        if (opts->required_from_host_keys != NULL) {
                     90:                switch (match_host_and_ip(remote_host, remote_ip,
                     91:                    opts->required_from_host_keys )) {
                     92:                case 1:
                     93:                        /* Host name matches. */
                     94:                        break;
                     95:                case -1:
                     96:                default:
                     97:                        debug("%s: invalid from criteria", loc);
                     98:                        auth_debug_add("%s: invalid from criteria", loc);
                     99:                        /* FALLTHROUGH */
                    100:                case 0:
                    101:                        logit("%s: Authentication tried for %.100s with "
                    102:                            "correct key but not from a permitted "
                    103:                            "host (host=%.200s, ip=%.200s, required=%.200s).",
                    104:                            loc, pw->pw_name, remote_host, remote_ip,
                    105:                            opts->required_from_host_keys);
                    106:                        auth_debug_add("%s: Your host '%.200s' is not "
                    107:                            "permitted to use this key for login.",
                    108:                            loc, remote_host);
                    109:                        /* deny access */
                    110:                        return -1;
                    111:                }
                    112:        }
                    113:        /* Check source-address restriction from certificate */
                    114:        if (opts->required_from_host_cert != NULL) {
                    115:                switch (addr_match_cidr_list(remote_ip,
                    116:                    opts->required_from_host_cert)) {
                    117:                case 1:
                    118:                        /* accepted */
                    119:                        break;
                    120:                case -1:
                    121:                default:
                    122:                        /* invalid */
                    123:                        error("%s: Certificate source-address invalid", loc);
                    124:                        /* FALLTHROUGH */
                    125:                case 0:
                    126:                        logit("%s: Authentication tried for %.100s with valid "
                    127:                            "certificate but not from a permitted source "
                    128:                            "address (%.200s).", loc, pw->pw_name, remote_ip);
                    129:                        auth_debug_add("%s: Your address '%.200s' is not "
                    130:                            "permitted to use this certificate for login.",
                    131:                            loc, remote_ip);
                    132:                        return -1;
                    133:                }
                    134:        }
                    135:        /*
                    136:         *
                    137:         * XXX this is spammy. We should report remotely only for keys
                    138:         *     that are successful in actual auth attempts, and not PK_OK
                    139:         *     tests.
                    140:         */
                    141:        auth_log_authopts(loc, opts, 1);
                    142:
                    143:        return 0;
                    144: }
                    145:
                    146: static int
                    147: match_principals_option(const char *principal_list, struct sshkey_cert *cert)
                    148: {
                    149:        char *result;
                    150:        u_int i;
                    151:
                    152:        /* XXX percent_expand() sequences for authorized_principals? */
                    153:
                    154:        for (i = 0; i < cert->nprincipals; i++) {
                    155:                if ((result = match_list(cert->principals[i],
                    156:                    principal_list, NULL)) != NULL) {
                    157:                        debug3("matched principal from key options \"%.100s\"",
                    158:                            result);
                    159:                        free(result);
                    160:                        return 1;
                    161:                }
                    162:        }
                    163:        return 0;
                    164: }
                    165:
                    166: /*
                    167:  * Process a single authorized_principals format line. Returns 0 and sets
                    168:  * authoptsp is principal is authorised, -1 otherwise. "loc" is used as a
                    169:  * log preamble for file/line information.
                    170:  */
                    171: int
                    172: auth_check_principals_line(char *cp, const struct sshkey_cert *cert,
                    173:     const char *loc, struct sshauthopt **authoptsp)
                    174: {
                    175:        u_int i, found = 0;
                    176:        char *ep, *line_opts;
                    177:        const char *reason = NULL;
                    178:        struct sshauthopt *opts = NULL;
                    179:
                    180:        if (authoptsp != NULL)
                    181:                *authoptsp = NULL;
                    182:
                    183:        /* Trim trailing whitespace. */
                    184:        ep = cp + strlen(cp) - 1;
                    185:        while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
                    186:                *ep-- = '\0';
                    187:
                    188:        /*
                    189:         * If the line has internal whitespace then assume it has
                    190:         * key options.
                    191:         */
                    192:        line_opts = NULL;
                    193:        if ((ep = strrchr(cp, ' ')) != NULL ||
                    194:            (ep = strrchr(cp, '\t')) != NULL) {
                    195:                for (; *ep == ' ' || *ep == '\t'; ep++)
                    196:                        ;
                    197:                line_opts = cp;
                    198:                cp = ep;
                    199:        }
                    200:        if ((opts = sshauthopt_parse(line_opts, &reason)) == NULL) {
                    201:                debug("%s: bad principals options: %s", loc, reason);
                    202:                auth_debug_add("%s: bad principals options: %s", loc, reason);
                    203:                return -1;
                    204:        }
                    205:        /* Check principals in cert against those on line */
                    206:        for (i = 0; i < cert->nprincipals; i++) {
                    207:                if (strcmp(cp, cert->principals[i]) != 0)
                    208:                        continue;
                    209:                debug3("%s: matched principal \"%.100s\"",
                    210:                    loc, cert->principals[i]);
                    211:                found = 1;
                    212:        }
                    213:        if (found && authoptsp != NULL) {
                    214:                *authoptsp = opts;
                    215:                opts = NULL;
                    216:        }
                    217:        sshauthopt_free(opts);
                    218:        return found ? 0 : -1;
                    219: }
                    220:
                    221: int
                    222: auth_process_principals(FILE *f, const char *file,
                    223:     const struct sshkey_cert *cert, struct sshauthopt **authoptsp)
                    224: {
                    225:        char loc[256], *line = NULL, *cp, *ep;
                    226:        size_t linesize = 0;
                    227:        u_long linenum = 0, nonblank = 0;
                    228:        u_int found_principal = 0;
                    229:
                    230:        if (authoptsp != NULL)
                    231:                *authoptsp = NULL;
                    232:
                    233:        while (getline(&line, &linesize, f) != -1) {
                    234:                linenum++;
                    235:                /* Always consume entire input */
                    236:                if (found_principal)
                    237:                        continue;
                    238:
                    239:                /* Skip leading whitespace. */
                    240:                for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
                    241:                        ;
                    242:                /* Skip blank and comment lines. */
                    243:                if ((ep = strchr(cp, '#')) != NULL)
                    244:                        *ep = '\0';
                    245:                if (!*cp || *cp == '\n')
                    246:                        continue;
                    247:
                    248:                nonblank++;
                    249:                snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
                    250:                if (auth_check_principals_line(cp, cert, loc, authoptsp) == 0)
                    251:                        found_principal = 1;
                    252:        }
                    253:        debug2_f("%s: processed %lu/%lu lines", file, nonblank, linenum);
                    254:        free(line);
                    255:        return found_principal;
                    256: }
                    257:
                    258: /*
                    259:  * Check a single line of an authorized_keys-format file. Returns 0 if key
                    260:  * matches, -1 otherwise. Will return key/cert options via *authoptsp
                    261:  * on success. "loc" is used as file/line location in log messages.
                    262:  */
                    263: int
                    264: auth_check_authkey_line(struct passwd *pw, struct sshkey *key,
                    265:     char *cp, const char *remote_ip, const char *remote_host, const char *loc,
                    266:     struct sshauthopt **authoptsp)
                    267: {
                    268:        int want_keytype = sshkey_is_cert(key) ? KEY_UNSPEC : key->type;
                    269:        struct sshkey *found = NULL;
                    270:        struct sshauthopt *keyopts = NULL, *certopts = NULL, *finalopts = NULL;
                    271:        char *key_options = NULL, *fp = NULL;
                    272:        const char *reason = NULL;
                    273:        int ret = -1;
                    274:
                    275:        if (authoptsp != NULL)
                    276:                *authoptsp = NULL;
                    277:
                    278:        if ((found = sshkey_new(want_keytype)) == NULL) {
                    279:                debug3_f("keytype %d failed", want_keytype);
                    280:                goto out;
                    281:        }
                    282:
                    283:        /* XXX djm: peek at key type in line and skip if unwanted */
                    284:
                    285:        if (sshkey_read(found, &cp) != 0) {
                    286:                /* no key?  check for options */
                    287:                debug2("%s: check options: '%s'", loc, cp);
                    288:                key_options = cp;
                    289:                if (sshkey_advance_past_options(&cp) != 0) {
                    290:                        reason = "invalid key option string";
                    291:                        goto fail_reason;
                    292:                }
                    293:                skip_space(&cp);
                    294:                if (sshkey_read(found, &cp) != 0) {
                    295:                        /* still no key?  advance to next line*/
                    296:                        debug2("%s: advance: '%s'", loc, cp);
                    297:                        goto out;
                    298:                }
                    299:        }
                    300:        /* Parse key options now; we need to know if this is a CA key */
                    301:        if ((keyopts = sshauthopt_parse(key_options, &reason)) == NULL) {
                    302:                debug("%s: bad key options: %s", loc, reason);
                    303:                auth_debug_add("%s: bad key options: %s", loc, reason);
                    304:                goto out;
                    305:        }
                    306:        /* Ignore keys that don't match or incorrectly marked as CAs */
                    307:        if (sshkey_is_cert(key)) {
                    308:                /* Certificate; check signature key against CA */
                    309:                if (!sshkey_equal(found, key->cert->signature_key) ||
                    310:                    !keyopts->cert_authority)
                    311:                        goto out;
                    312:        } else {
                    313:                /* Plain key: check it against key found in file */
                    314:                if (!sshkey_equal(found, key) || keyopts->cert_authority)
                    315:                        goto out;
                    316:        }
                    317:
                    318:        /* We have a candidate key, perform authorisation checks */
                    319:        if ((fp = sshkey_fingerprint(found,
                    320:            SSH_FP_HASH_DEFAULT, SSH_FP_DEFAULT)) == NULL)
                    321:                fatal_f("fingerprint failed");
                    322:
                    323:        debug("%s: matching %s found: %s %s", loc,
                    324:            sshkey_is_cert(key) ? "CA" : "key", sshkey_type(found), fp);
                    325:
                    326:        if (auth_authorise_keyopts(pw, keyopts,
                    327:            sshkey_is_cert(key), remote_ip, remote_host, loc) != 0) {
                    328:                reason = "Refused by key options";
                    329:                goto fail_reason;
                    330:        }
                    331:        /* That's all we need for plain keys. */
                    332:        if (!sshkey_is_cert(key)) {
                    333:                verbose("Accepted key %s %s found at %s",
                    334:                    sshkey_type(found), fp, loc);
                    335:                finalopts = keyopts;
                    336:                keyopts = NULL;
                    337:                goto success;
                    338:        }
                    339:
                    340:        /*
                    341:         * Additional authorisation for certificates.
                    342:         */
                    343:
                    344:        /* Parse and check options present in certificate */
                    345:        if ((certopts = sshauthopt_from_cert(key)) == NULL) {
                    346:                reason = "Invalid certificate options";
                    347:                goto fail_reason;
                    348:        }
                    349:        if (auth_authorise_keyopts(pw, certopts, 0,
                    350:            remote_ip, remote_host, loc) != 0) {
                    351:                reason = "Refused by certificate options";
                    352:                goto fail_reason;
                    353:        }
                    354:        if ((finalopts = sshauthopt_merge(keyopts, certopts, &reason)) == NULL)
                    355:                goto fail_reason;
                    356:
                    357:        /*
                    358:         * If the user has specified a list of principals as
                    359:         * a key option, then prefer that list to matching
                    360:         * their username in the certificate principals list.
                    361:         */
                    362:        if (keyopts->cert_principals != NULL &&
                    363:            !match_principals_option(keyopts->cert_principals, key->cert)) {
                    364:                reason = "Certificate does not contain an authorized principal";
                    365:                goto fail_reason;
                    366:        }
                    367:        if (sshkey_cert_check_authority_now(key, 0, 0, 0,
                    368:            keyopts->cert_principals == NULL ? pw->pw_name : NULL,
                    369:            &reason) != 0)
                    370:                goto fail_reason;
                    371:
                    372:        verbose("Accepted certificate ID \"%s\" (serial %llu) "
                    373:            "signed by CA %s %s found at %s",
                    374:            key->cert->key_id,
                    375:            (unsigned long long)key->cert->serial,
                    376:            sshkey_type(found), fp, loc);
                    377:
                    378:  success:
                    379:        if (finalopts == NULL)
                    380:                fatal_f("internal error: missing options");
                    381:        if (authoptsp != NULL) {
                    382:                *authoptsp = finalopts;
                    383:                finalopts = NULL;
                    384:        }
                    385:        /* success */
                    386:        ret = 0;
                    387:        goto out;
                    388:
                    389:  fail_reason:
                    390:        error("%s", reason);
                    391:        auth_debug_add("%s", reason);
                    392:  out:
                    393:        free(fp);
                    394:        sshauthopt_free(keyopts);
                    395:        sshauthopt_free(certopts);
                    396:        sshauthopt_free(finalopts);
                    397:        sshkey_free(found);
                    398:        return ret;
                    399: }
                    400:
                    401: /*
                    402:  * Checks whether key is allowed in authorized_keys-format file,
                    403:  * returns 1 if the key is allowed or 0 otherwise.
                    404:  */
                    405: int
                    406: auth_check_authkeys_file(struct passwd *pw, FILE *f, char *file,
                    407:     struct sshkey *key, const char *remote_ip,
                    408:     const char *remote_host, struct sshauthopt **authoptsp)
                    409: {
                    410:        char *cp, *line = NULL, loc[256];
                    411:        size_t linesize = 0;
                    412:        int found_key = 0;
                    413:        u_long linenum = 0, nonblank = 0;
                    414:
                    415:        if (authoptsp != NULL)
                    416:                *authoptsp = NULL;
                    417:
                    418:        while (getline(&line, &linesize, f) != -1) {
                    419:                linenum++;
                    420:                /* Always consume entire file */
                    421:                if (found_key)
                    422:                        continue;
                    423:
                    424:                /* Skip leading whitespace, empty and comment lines. */
                    425:                cp = line;
                    426:                skip_space(&cp);
                    427:                if (!*cp || *cp == '\n' || *cp == '#')
                    428:                        continue;
                    429:
                    430:                nonblank++;
                    431:                snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
                    432:                if (auth_check_authkey_line(pw, key, cp,
                    433:                    remote_ip, remote_host, loc, authoptsp) == 0)
                    434:                        found_key = 1;
                    435:        }
                    436:        free(line);
                    437:        debug2_f("%s: processed %lu/%lu lines", file, nonblank, linenum);
                    438:        return found_key;
                    439: }
                    440:
1.2       djm       441: static FILE *
                    442: auth_openfile(const char *file, struct passwd *pw, int strict_modes,
                    443:     int log_missing, char *file_type)
                    444: {
                    445:        char line[1024];
                    446:        struct stat st;
                    447:        int fd;
                    448:        FILE *f;
                    449:
                    450:        if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) {
1.3     ! djm       451:                if (errno != ENOENT) {
        !           452:                        logit("Could not open user '%s' %s '%s': %s",
        !           453:                            pw->pw_name, file_type, file, strerror(errno));
        !           454:                } else if (log_missing) {
        !           455:                        debug("Could not open user '%s' %s '%s': %s",
        !           456:                            pw->pw_name, file_type, file, strerror(errno));
        !           457:                }
1.2       djm       458:                return NULL;
                    459:        }
                    460:
                    461:        if (fstat(fd, &st) == -1) {
                    462:                close(fd);
                    463:                return NULL;
                    464:        }
                    465:        if (!S_ISREG(st.st_mode)) {
1.3     ! djm       466:                logit("User '%s' %s '%s' is not a regular file",
1.2       djm       467:                    pw->pw_name, file_type, file);
                    468:                close(fd);
                    469:                return NULL;
                    470:        }
                    471:        unset_nonblock(fd);
                    472:        if ((f = fdopen(fd, "r")) == NULL) {
                    473:                close(fd);
                    474:                return NULL;
                    475:        }
                    476:        if (strict_modes &&
                    477:            safe_path_fd(fileno(f), file, pw, line, sizeof(line)) != 0) {
                    478:                fclose(f);
                    479:                logit("Authentication refused: %s", line);
                    480:                auth_debug_add("Ignored %s: %s", file_type, line);
                    481:                return NULL;
                    482:        }
                    483:
                    484:        return f;
                    485: }
                    486:
                    487:
                    488: FILE *
                    489: auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes)
                    490: {
                    491:        return auth_openfile(file, pw, strict_modes, 1, "authorized keys");
                    492: }
                    493:
                    494: FILE *
                    495: auth_openprincipals(const char *file, struct passwd *pw, int strict_modes)
                    496: {
                    497:        return auth_openfile(file, pw, strict_modes, 0,
                    498:            "authorized principals");
                    499: }
1.1       djm       500: