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

Annotation of src/usr.bin/ssh/sshsig.c, Revision 1.35

1.35    ! djm         1: /* $OpenBSD: sshsig.c,v 1.34 2023/12/08 09:18:39 markus Exp $ */
1.1       djm         2: /*
                      3:  * Copyright (c) 2019 Google LLC
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
                      8:  *
                      9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     16:  */
                     17:
                     18: #include <stdio.h>
                     19: #include <stdlib.h>
                     20: #include <stdarg.h>
                     21: #include <errno.h>
                     22: #include <string.h>
                     23: #include <unistd.h>
                     24:
                     25: #include "authfd.h"
                     26: #include "authfile.h"
                     27: #include "log.h"
                     28: #include "misc.h"
                     29: #include "sshbuf.h"
                     30: #include "sshsig.h"
                     31: #include "ssherr.h"
                     32: #include "sshkey.h"
                     33: #include "match.h"
                     34: #include "digest.h"
                     35:
                     36: #define SIG_VERSION            0x01
                     37: #define MAGIC_PREAMBLE         "SSHSIG"
                     38: #define MAGIC_PREAMBLE_LEN     (sizeof(MAGIC_PREAMBLE) - 1)
1.33      djm        39: #define BEGIN_SIGNATURE                "-----BEGIN SSH SIGNATURE-----"
1.1       djm        40: #define END_SIGNATURE          "-----END SSH SIGNATURE-----"
                     41: #define RSA_SIGN_ALG           "rsa-sha2-512" /* XXX maybe make configurable */
                     42: #define RSA_SIGN_ALLOWED       "rsa-sha2-512,rsa-sha2-256"
                     43: #define HASHALG_DEFAULT                "sha512" /* XXX maybe make configurable */
                     44: #define HASHALG_ALLOWED                "sha256,sha512"
                     45:
                     46: int
                     47: sshsig_armor(const struct sshbuf *blob, struct sshbuf **out)
                     48: {
                     49:        struct sshbuf *buf = NULL;
                     50:        int r = SSH_ERR_INTERNAL_ERROR;
                     51:
                     52:        *out = NULL;
                     53:
                     54:        if ((buf = sshbuf_new()) == NULL) {
1.18      djm        55:                error_f("sshbuf_new failed");
1.1       djm        56:                r = SSH_ERR_ALLOC_FAIL;
                     57:                goto out;
                     58:        }
                     59:
1.33      djm        60:        if ((r = sshbuf_putf(buf, "%s\n", BEGIN_SIGNATURE)) != 0) {
1.18      djm        61:                error_fr(r, "sshbuf_putf");
1.1       djm        62:                goto out;
                     63:        }
                     64:
                     65:        if ((r = sshbuf_dtob64(blob, buf, 1)) != 0) {
1.18      djm        66:                error_fr(r, "base64 encode signature");
1.1       djm        67:                goto out;
                     68:        }
                     69:
                     70:        if ((r = sshbuf_put(buf, END_SIGNATURE,
                     71:            sizeof(END_SIGNATURE)-1)) != 0 ||
                     72:            (r = sshbuf_put_u8(buf, '\n')) != 0) {
1.18      djm        73:                error_fr(r, "sshbuf_put");
1.1       djm        74:                goto out;
                     75:        }
                     76:        /* success */
                     77:        *out = buf;
                     78:        buf = NULL; /* transferred */
                     79:        r = 0;
                     80:  out:
                     81:        sshbuf_free(buf);
                     82:        return r;
                     83: }
                     84:
                     85: int
                     86: sshsig_dearmor(struct sshbuf *sig, struct sshbuf **out)
                     87: {
                     88:        int r;
                     89:        size_t eoffset = 0;
                     90:        struct sshbuf *buf = NULL;
                     91:        struct sshbuf *sbuf = NULL;
                     92:        char *b64 = NULL;
                     93:
                     94:        if ((sbuf = sshbuf_fromb(sig)) == NULL) {
1.18      djm        95:                error_f("sshbuf_fromb failed");
1.1       djm        96:                return SSH_ERR_ALLOC_FAIL;
                     97:        }
                     98:
1.33      djm        99:        /* Expect and consume preamble + lf/crlf */
1.1       djm       100:        if ((r = sshbuf_cmp(sbuf, 0,
                    101:            BEGIN_SIGNATURE, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
                    102:                error("Couldn't parse signature: missing header");
                    103:                goto done;
                    104:        }
                    105:        if ((r = sshbuf_consume(sbuf, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
1.18      djm       106:                error_fr(r, "consume");
1.1       djm       107:                goto done;
                    108:        }
1.33      djm       109:        if ((r = sshbuf_cmp(sbuf, 0, "\r\n", 2)) == 0)
                    110:                eoffset = 2;
                    111:        else if ((r = sshbuf_cmp(sbuf, 0, "\n", 1)) == 0)
                    112:                eoffset = 1;
                    113:        else {
                    114:                r = SSH_ERR_INVALID_FORMAT;
                    115:                error_f("no header eol");
                    116:                goto done;
                    117:        }
                    118:        if ((r = sshbuf_consume(sbuf, eoffset)) != 0) {
                    119:                error_fr(r, "consume eol");
                    120:                goto done;
                    121:        }
                    122:        /* Find and consume lf + suffix (any prior cr would be ignored) */
1.1       djm       123:        if ((r = sshbuf_find(sbuf, 0, "\n" END_SIGNATURE,
1.33      djm       124:            sizeof(END_SIGNATURE), &eoffset)) != 0) {
1.1       djm       125:                error("Couldn't parse signature: missing footer");
                    126:                goto done;
                    127:        }
                    128:        if ((r = sshbuf_consume_end(sbuf, sshbuf_len(sbuf)-eoffset)) != 0) {
1.18      djm       129:                error_fr(r, "consume");
1.1       djm       130:                goto done;
                    131:        }
                    132:
                    133:        if ((b64 = sshbuf_dup_string(sbuf)) == NULL) {
1.18      djm       134:                error_f("sshbuf_dup_string failed");
1.1       djm       135:                r = SSH_ERR_ALLOC_FAIL;
                    136:                goto done;
                    137:        }
                    138:
                    139:        if ((buf = sshbuf_new()) == NULL) {
1.18      djm       140:                error_f("sshbuf_new() failed");
1.1       djm       141:                r = SSH_ERR_ALLOC_FAIL;
                    142:                goto done;
                    143:        }
                    144:
                    145:        if ((r = sshbuf_b64tod(buf, b64)) != 0) {
1.18      djm       146:                error_fr(r, "decode base64");
1.1       djm       147:                goto done;
                    148:        }
                    149:
                    150:        /* success */
                    151:        *out = buf;
                    152:        r = 0;
                    153:        buf = NULL; /* transferred */
                    154: done:
                    155:        sshbuf_free(buf);
                    156:        sshbuf_free(sbuf);
                    157:        free(b64);
                    158:        return r;
                    159: }
                    160:
                    161: static int
                    162: sshsig_wrap_sign(struct sshkey *key, const char *hashalg,
1.16      djm       163:     const char *sk_provider, const char *sk_pin, const struct sshbuf *h_message,
1.7       djm       164:     const char *sig_namespace, struct sshbuf **out,
                    165:     sshsig_signer *signer, void *signer_ctx)
1.1       djm       166: {
                    167:        int r;
                    168:        size_t slen = 0;
                    169:        u_char *sig = NULL;
                    170:        struct sshbuf *blob = NULL;
                    171:        struct sshbuf *tosign = NULL;
                    172:        const char *sign_alg = NULL;
                    173:
                    174:        if ((tosign = sshbuf_new()) == NULL ||
                    175:            (blob = sshbuf_new()) == NULL) {
1.18      djm       176:                error_f("sshbuf_new failed");
1.1       djm       177:                r = SSH_ERR_ALLOC_FAIL;
                    178:                goto done;
                    179:        }
                    180:
                    181:        if ((r = sshbuf_put(tosign, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
                    182:            (r = sshbuf_put_cstring(tosign, sig_namespace)) != 0 ||
                    183:            (r = sshbuf_put_string(tosign, NULL, 0)) != 0 || /* reserved */
                    184:            (r = sshbuf_put_cstring(tosign, hashalg)) != 0 ||
1.6       djm       185:            (r = sshbuf_put_stringb(tosign, h_message)) != 0) {
1.18      djm       186:                error_fr(r, "assemble message to sign");
1.1       djm       187:                goto done;
                    188:        }
                    189:
                    190:        /* If using RSA keys then default to a good signature algorithm */
                    191:        if (sshkey_type_plain(key->type) == KEY_RSA)
                    192:                sign_alg = RSA_SIGN_ALG;
                    193:
                    194:        if (signer != NULL) {
                    195:                if ((r = signer(key, &sig, &slen,
                    196:                    sshbuf_ptr(tosign), sshbuf_len(tosign),
1.16      djm       197:                    sign_alg, sk_provider, sk_pin, 0, signer_ctx)) != 0) {
1.18      djm       198:                        error_r(r, "Couldn't sign message (signer)");
1.1       djm       199:                        goto done;
                    200:                }
                    201:        } else {
                    202:                if ((r = sshkey_sign(key, &sig, &slen,
                    203:                    sshbuf_ptr(tosign), sshbuf_len(tosign),
1.16      djm       204:                    sign_alg, sk_provider, sk_pin, 0)) != 0) {
1.18      djm       205:                        error_r(r, "Couldn't sign message");
1.1       djm       206:                        goto done;
                    207:                }
                    208:        }
                    209:
                    210:        if ((r = sshbuf_put(blob, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
                    211:            (r = sshbuf_put_u32(blob, SIG_VERSION)) != 0 ||
                    212:            (r = sshkey_puts(key, blob)) != 0 ||
                    213:            (r = sshbuf_put_cstring(blob, sig_namespace)) != 0 ||
                    214:            (r = sshbuf_put_string(blob, NULL, 0)) != 0 || /* reserved */
                    215:            (r = sshbuf_put_cstring(blob, hashalg)) != 0 ||
                    216:            (r = sshbuf_put_string(blob, sig, slen)) != 0) {
1.18      djm       217:                error_fr(r, "assemble signature object");
1.1       djm       218:                goto done;
                    219:        }
                    220:
1.12      markus    221:        if (out != NULL) {
                    222:                *out = blob;
                    223:                blob = NULL;
                    224:        }
1.1       djm       225:        r = 0;
                    226: done:
                    227:        free(sig);
                    228:        sshbuf_free(blob);
                    229:        sshbuf_free(tosign);
                    230:        return r;
                    231: }
                    232:
                    233: /* Check preamble and version. */
                    234: static int
                    235: sshsig_parse_preamble(struct sshbuf *buf)
                    236: {
                    237:        int r = SSH_ERR_INTERNAL_ERROR;
                    238:        uint32_t sversion;
                    239:
                    240:        if ((r = sshbuf_cmp(buf, 0, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
                    241:            (r = sshbuf_consume(buf, (sizeof(MAGIC_PREAMBLE)-1))) != 0 ||
                    242:            (r = sshbuf_get_u32(buf, &sversion)) != 0) {
                    243:                error("Couldn't verify signature: invalid format");
                    244:                return r;
                    245:        }
                    246:
1.2       djm       247:        if (sversion > SIG_VERSION) {
1.1       djm       248:                error("Signature version %lu is larger than supported "
                    249:                    "version %u", (unsigned long)sversion, SIG_VERSION);
                    250:                return SSH_ERR_INVALID_FORMAT;
                    251:        }
                    252:        return 0;
                    253: }
                    254:
                    255: static int
                    256: sshsig_check_hashalg(const char *hashalg)
                    257: {
1.2       djm       258:        if (hashalg == NULL ||
                    259:            match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1)
1.1       djm       260:                return 0;
1.18      djm       261:        error_f("unsupported hash algorithm \"%.100s\"", hashalg);
1.1       djm       262:        return SSH_ERR_SIGN_ALG_UNSUPPORTED;
                    263: }
                    264:
                    265: static int
                    266: sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp)
                    267: {
                    268:        struct sshbuf *buf = NULL;
                    269:        char *hashalg = NULL;
                    270:        int r = SSH_ERR_INTERNAL_ERROR;
                    271:
                    272:        if (hashalgp != NULL)
                    273:                *hashalgp = NULL;
                    274:        if ((buf = sshbuf_fromb(signature)) == NULL)
                    275:                return SSH_ERR_ALLOC_FAIL;
                    276:        if ((r = sshsig_parse_preamble(buf)) != 0)
                    277:                goto done;
                    278:        if ((r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
                    279:            (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
                    280:            (r = sshbuf_get_string(buf, NULL, NULL)) != 0 ||
                    281:            (r = sshbuf_get_cstring(buf, &hashalg, NULL)) != 0 ||
                    282:            (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0) {
1.18      djm       283:                error_fr(r, "parse signature object");
1.1       djm       284:                goto done;
                    285:        }
                    286:
                    287:        /* success */
                    288:        r = 0;
                    289:        *hashalgp = hashalg;
                    290:        hashalg = NULL;
                    291:  done:
                    292:        free(hashalg);
                    293:        sshbuf_free(buf);
                    294:        return r;
                    295: }
                    296:
                    297: static int
                    298: sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
                    299:     const struct sshbuf *h_message, const char *expect_namespace,
1.8       djm       300:     struct sshkey **sign_keyp, struct sshkey_sig_details **sig_details)
1.1       djm       301: {
                    302:        int r = SSH_ERR_INTERNAL_ERROR;
                    303:        struct sshbuf *buf = NULL, *toverify = NULL;
                    304:        struct sshkey *key = NULL;
                    305:        const u_char *sig;
                    306:        char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL;
                    307:        size_t siglen;
                    308:
1.18      djm       309:        debug_f("verify message length %zu", sshbuf_len(h_message));
1.8       djm       310:        if (sig_details != NULL)
                    311:                *sig_details = NULL;
1.1       djm       312:        if (sign_keyp != NULL)
                    313:                *sign_keyp = NULL;
                    314:
                    315:        if ((toverify = sshbuf_new()) == NULL) {
1.18      djm       316:                error_f("sshbuf_new failed");
1.1       djm       317:                r = SSH_ERR_ALLOC_FAIL;
                    318:                goto done;
                    319:        }
                    320:        if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE,
                    321:            MAGIC_PREAMBLE_LEN)) != 0 ||
                    322:            (r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 ||
                    323:            (r = sshbuf_put_string(toverify, NULL, 0)) != 0 || /* reserved */
                    324:            (r = sshbuf_put_cstring(toverify, hashalg)) != 0 ||
1.6       djm       325:            (r = sshbuf_put_stringb(toverify, h_message)) != 0) {
1.18      djm       326:                error_fr(r, "assemble message to verify");
1.1       djm       327:                goto done;
                    328:        }
                    329:
                    330:        if ((r = sshsig_parse_preamble(signature)) != 0)
                    331:                goto done;
                    332:
                    333:        if ((r = sshkey_froms(signature, &key)) != 0 ||
                    334:            (r = sshbuf_get_cstring(signature, &got_namespace, NULL)) != 0 ||
                    335:            (r = sshbuf_get_string(signature, NULL, NULL)) != 0 ||
                    336:            (r = sshbuf_get_cstring(signature, &sig_hashalg, NULL)) != 0 ||
                    337:            (r = sshbuf_get_string_direct(signature, &sig, &siglen)) != 0) {
1.18      djm       338:                error_fr(r, "parse signature object");
1.1       djm       339:                goto done;
                    340:        }
                    341:
                    342:        if (sshbuf_len(signature) != 0) {
                    343:                error("Signature contains trailing data");
                    344:                r = SSH_ERR_INVALID_FORMAT;
                    345:                goto done;
                    346:        }
                    347:
                    348:        if (strcmp(expect_namespace, got_namespace) != 0) {
                    349:                error("Couldn't verify signature: namespace does not match");
1.18      djm       350:                debug_f("expected namespace \"%s\" received \"%s\"",
                    351:                    expect_namespace, got_namespace);
1.1       djm       352:                r = SSH_ERR_SIGNATURE_INVALID;
                    353:                goto done;
                    354:        }
                    355:        if (strcmp(hashalg, sig_hashalg) != 0) {
                    356:                error("Couldn't verify signature: hash algorithm mismatch");
1.18      djm       357:                debug_f("expected algorithm \"%s\" received \"%s\"",
                    358:                    hashalg, sig_hashalg);
1.1       djm       359:                r = SSH_ERR_SIGNATURE_INVALID;
                    360:                goto done;
                    361:        }
                    362:        /* Ensure that RSA keys use an acceptable signature algorithm */
                    363:        if (sshkey_type_plain(key->type) == KEY_RSA) {
                    364:                if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) {
1.18      djm       365:                        error_r(r, "Couldn't verify signature: unable to get "
                    366:                            "signature type");
1.1       djm       367:                        goto done;
                    368:                }
                    369:                if (match_pattern_list(sigtype, RSA_SIGN_ALLOWED, 0) != 1) {
                    370:                        error("Couldn't verify signature: unsupported RSA "
                    371:                            "signature algorithm %s", sigtype);
                    372:                        r = SSH_ERR_SIGN_ALG_UNSUPPORTED;
                    373:                        goto done;
                    374:                }
                    375:        }
                    376:        if ((r = sshkey_verify(key, sig, siglen, sshbuf_ptr(toverify),
1.8       djm       377:            sshbuf_len(toverify), NULL, 0, sig_details)) != 0) {
1.18      djm       378:                error_r(r, "Signature verification failed");
1.1       djm       379:                goto done;
                    380:        }
                    381:
                    382:        /* success */
                    383:        r = 0;
                    384:        if (sign_keyp != NULL) {
                    385:                *sign_keyp = key;
                    386:                key = NULL; /* transferred */
                    387:        }
                    388: done:
                    389:        free(got_namespace);
                    390:        free(sigtype);
                    391:        free(sig_hashalg);
                    392:        sshbuf_free(buf);
                    393:        sshbuf_free(toverify);
                    394:        sshkey_free(key);
                    395:        return r;
                    396: }
                    397:
1.2       djm       398: static int
                    399: hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp)
1.1       djm       400: {
1.2       djm       401:        char *hex, hash[SSH_DIGEST_MAX_LENGTH];
                    402:        int alg, r = SSH_ERR_INTERNAL_ERROR;
1.1       djm       403:        struct sshbuf *b = NULL;
                    404:
1.2       djm       405:        *bp = NULL;
                    406:        memset(hash, 0, sizeof(hash));
1.1       djm       407:
                    408:        if ((r = sshsig_check_hashalg(hashalg)) != 0)
                    409:                return r;
                    410:        if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
1.18      djm       411:                error_f("can't look up hash algorithm %s", hashalg);
1.1       djm       412:                return SSH_ERR_INTERNAL_ERROR;
                    413:        }
1.2       djm       414:        if ((r = ssh_digest_buffer(alg, m, hash, sizeof(hash))) != 0) {
1.18      djm       415:                error_fr(r, "ssh_digest_buffer");
1.1       djm       416:                return r;
                    417:        }
1.2       djm       418:        if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
1.18      djm       419:                debug3_f("final hash: %s", hex);
1.2       djm       420:                freezero(hex, strlen(hex));
                    421:        }
                    422:        if ((b = sshbuf_new()) == NULL) {
1.1       djm       423:                r = SSH_ERR_ALLOC_FAIL;
                    424:                goto out;
                    425:        }
1.2       djm       426:        if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
1.18      djm       427:                error_fr(r, "sshbuf_put");
1.2       djm       428:                goto out;
                    429:        }
                    430:        *bp = b;
                    431:        b = NULL; /* transferred */
                    432:        /* success */
                    433:        r = 0;
                    434:  out:
                    435:        sshbuf_free(b);
                    436:        explicit_bzero(hash, sizeof(hash));
1.12      markus    437:        return r;
1.2       djm       438: }
                    439:
                    440: int
1.16      djm       441: sshsig_signb(struct sshkey *key, const char *hashalg,
                    442:     const char *sk_provider, const char *sk_pin,
1.2       djm       443:     const struct sshbuf *message, const char *sig_namespace,
                    444:     struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
                    445: {
                    446:        struct sshbuf *b = NULL;
                    447:        int r = SSH_ERR_INTERNAL_ERROR;
                    448:
                    449:        if (hashalg == NULL)
                    450:                hashalg = HASHALG_DEFAULT;
                    451:        if (out != NULL)
                    452:                *out = NULL;
                    453:        if ((r = hash_buffer(message, hashalg, &b)) != 0) {
1.18      djm       454:                error_fr(r, "hash buffer");
1.2       djm       455:                goto out;
                    456:        }
1.16      djm       457:        if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
1.7       djm       458:            sig_namespace, out, signer, signer_ctx)) != 0)
1.1       djm       459:                goto out;
                    460:        /* success */
                    461:        r = 0;
                    462:  out:
                    463:        sshbuf_free(b);
                    464:        return r;
                    465: }
                    466:
                    467: int
1.2       djm       468: sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message,
1.8       djm       469:     const char *expect_namespace, struct sshkey **sign_keyp,
                    470:     struct sshkey_sig_details **sig_details)
1.1       djm       471: {
                    472:        struct sshbuf *b = NULL;
1.2       djm       473:        int r = SSH_ERR_INTERNAL_ERROR;
1.1       djm       474:        char *hashalg = NULL;
                    475:
1.8       djm       476:        if (sig_details != NULL)
                    477:                *sig_details = NULL;
1.1       djm       478:        if (sign_keyp != NULL)
                    479:                *sign_keyp = NULL;
                    480:        if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
                    481:                return r;
1.18      djm       482:        debug_f("signature made with hash \"%s\"", hashalg);
1.2       djm       483:        if ((r = hash_buffer(message, hashalg, &b)) != 0) {
1.18      djm       484:                error_fr(r, "hash buffer");
1.1       djm       485:                goto out;
                    486:        }
                    487:        if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
1.8       djm       488:            sign_keyp, sig_details)) != 0)
1.1       djm       489:                goto out;
                    490:        /* success */
                    491:        r = 0;
                    492:  out:
                    493:        sshbuf_free(b);
                    494:        free(hashalg);
                    495:        return r;
                    496: }
                    497:
                    498: static int
1.2       djm       499: hash_file(int fd, const char *hashalg, struct sshbuf **bp)
1.1       djm       500: {
1.2       djm       501:        char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH];
1.1       djm       502:        ssize_t n, total = 0;
1.30      djm       503:        struct ssh_digest_ctx *ctx = NULL;
1.2       djm       504:        int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR;
                    505:        struct sshbuf *b = NULL;
                    506:
                    507:        *bp = NULL;
                    508:        memset(hash, 0, sizeof(hash));
1.1       djm       509:
1.2       djm       510:        if ((r = sshsig_check_hashalg(hashalg)) != 0)
                    511:                return r;
                    512:        if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
1.18      djm       513:                error_f("can't look up hash algorithm %s", hashalg);
1.2       djm       514:                return SSH_ERR_INTERNAL_ERROR;
                    515:        }
                    516:        if ((ctx = ssh_digest_start(alg)) == NULL) {
1.18      djm       517:                error_f("ssh_digest_start failed");
1.1       djm       518:                return SSH_ERR_INTERNAL_ERROR;
                    519:        }
                    520:        for (;;) {
                    521:                if ((n = read(fd, rbuf, sizeof(rbuf))) == -1) {
                    522:                        if (errno == EINTR || errno == EAGAIN)
                    523:                                continue;
                    524:                        oerrno = errno;
1.18      djm       525:                        error_f("read: %s", strerror(errno));
1.1       djm       526:                        errno = oerrno;
1.2       djm       527:                        r = SSH_ERR_SYSTEM_ERROR;
                    528:                        goto out;
1.1       djm       529:                } else if (n == 0) {
1.18      djm       530:                        debug2_f("hashed %zu bytes", total);
1.1       djm       531:                        break; /* EOF */
                    532:                }
                    533:                total += (size_t)n;
                    534:                if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) {
1.18      djm       535:                        error_fr(r, "ssh_digest_update");
1.2       djm       536:                        goto out;
1.1       djm       537:                }
                    538:        }
1.2       djm       539:        if ((r = ssh_digest_final(ctx, hash, sizeof(hash))) != 0) {
1.18      djm       540:                error_fr(r, "ssh_digest_final");
1.2       djm       541:                goto out;
1.1       djm       542:        }
1.2       djm       543:        if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
1.18      djm       544:                debug3_f("final hash: %s", hex);
1.1       djm       545:                freezero(hex, strlen(hex));
                    546:        }
1.2       djm       547:        if ((b = sshbuf_new()) == NULL) {
                    548:                r = SSH_ERR_ALLOC_FAIL;
                    549:                goto out;
                    550:        }
                    551:        if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
1.18      djm       552:                error_fr(r, "sshbuf_put");
1.2       djm       553:                goto out;
                    554:        }
                    555:        *bp = b;
                    556:        b = NULL; /* transferred */
1.1       djm       557:        /* success */
1.2       djm       558:        r = 0;
                    559:  out:
1.30      djm       560:        oerrno = errno;
1.2       djm       561:        sshbuf_free(b);
1.1       djm       562:        ssh_digest_free(ctx);
1.2       djm       563:        explicit_bzero(hash, sizeof(hash));
1.30      djm       564:        errno = oerrno;
1.12      markus    565:        return r;
1.1       djm       566: }
                    567:
                    568: int
1.16      djm       569: sshsig_sign_fd(struct sshkey *key, const char *hashalg,
                    570:     const char *sk_provider, const char *sk_pin,
1.1       djm       571:     int fd, const char *sig_namespace, struct sshbuf **out,
                    572:     sshsig_signer *signer, void *signer_ctx)
                    573: {
                    574:        struct sshbuf *b = NULL;
1.2       djm       575:        int r = SSH_ERR_INTERNAL_ERROR;
1.1       djm       576:
1.2       djm       577:        if (hashalg == NULL)
                    578:                hashalg = HASHALG_DEFAULT;
1.1       djm       579:        if (out != NULL)
                    580:                *out = NULL;
1.2       djm       581:        if ((r = hash_file(fd, hashalg, &b)) != 0) {
1.18      djm       582:                error_fr(r, "hash_file");
1.1       djm       583:                return r;
                    584:        }
1.16      djm       585:        if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
1.7       djm       586:            sig_namespace, out, signer, signer_ctx)) != 0)
1.1       djm       587:                goto out;
                    588:        /* success */
                    589:        r = 0;
                    590:  out:
                    591:        sshbuf_free(b);
                    592:        return r;
                    593: }
                    594:
                    595: int
                    596: sshsig_verify_fd(struct sshbuf *signature, int fd,
1.8       djm       597:     const char *expect_namespace, struct sshkey **sign_keyp,
                    598:     struct sshkey_sig_details **sig_details)
1.1       djm       599: {
                    600:        struct sshbuf *b = NULL;
1.2       djm       601:        int r = SSH_ERR_INTERNAL_ERROR;
1.1       djm       602:        char *hashalg = NULL;
                    603:
1.8       djm       604:        if (sig_details != NULL)
                    605:                *sig_details = NULL;
1.1       djm       606:        if (sign_keyp != NULL)
                    607:                *sign_keyp = NULL;
                    608:        if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
                    609:                return r;
1.18      djm       610:        debug_f("signature made with hash \"%s\"", hashalg);
1.2       djm       611:        if ((r = hash_file(fd, hashalg, &b)) != 0) {
1.18      djm       612:                error_fr(r, "hash_file");
1.1       djm       613:                goto out;
                    614:        }
                    615:        if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
1.8       djm       616:            sign_keyp, sig_details)) != 0)
1.1       djm       617:                goto out;
                    618:        /* success */
                    619:        r = 0;
                    620:  out:
                    621:        sshbuf_free(b);
                    622:        free(hashalg);
                    623:        return r;
                    624: }
                    625:
1.4       djm       626: struct sshsigopt {
1.1       djm       627:        int ca;
                    628:        char *namespaces;
1.21      djm       629:        uint64_t valid_after, valid_before;
1.1       djm       630: };
                    631:
1.4       djm       632: struct sshsigopt *
                    633: sshsigopt_parse(const char *opts, const char *path, u_long linenum,
1.1       djm       634:     const char **errstrp)
                    635: {
1.4       djm       636:        struct sshsigopt *ret;
1.1       djm       637:        int r;
1.21      djm       638:        char *opt;
1.1       djm       639:        const char *errstr = NULL;
                    640:
                    641:        if ((ret = calloc(1, sizeof(*ret))) == NULL)
                    642:                return NULL;
                    643:        if (opts == NULL || *opts == '\0')
                    644:                return ret; /* Empty options yields empty options :) */
                    645:
                    646:        while (*opts && *opts != ' ' && *opts != '\t') {
                    647:                /* flag options */
                    648:                if ((r = opt_flag("cert-authority", 0, &opts)) != -1) {
                    649:                        ret->ca = 1;
                    650:                } else if (opt_match(&opts, "namespaces")) {
                    651:                        if (ret->namespaces != NULL) {
                    652:                                errstr = "multiple \"namespaces\" clauses";
                    653:                                goto fail;
                    654:                        }
                    655:                        ret->namespaces = opt_dequote(&opts, &errstr);
                    656:                        if (ret->namespaces == NULL)
                    657:                                goto fail;
1.21      djm       658:                } else if (opt_match(&opts, "valid-after")) {
                    659:                        if (ret->valid_after != 0) {
                    660:                                errstr = "multiple \"valid-after\" clauses";
                    661:                                goto fail;
                    662:                        }
                    663:                        if ((opt = opt_dequote(&opts, &errstr)) == NULL)
                    664:                                goto fail;
                    665:                        if (parse_absolute_time(opt, &ret->valid_after) != 0 ||
                    666:                            ret->valid_after == 0) {
                    667:                                free(opt);
                    668:                                errstr = "invalid \"valid-after\" time";
                    669:                                goto fail;
                    670:                        }
                    671:                        free(opt);
                    672:                } else if (opt_match(&opts, "valid-before")) {
                    673:                        if (ret->valid_before != 0) {
                    674:                                errstr = "multiple \"valid-before\" clauses";
                    675:                                goto fail;
                    676:                        }
                    677:                        if ((opt = opt_dequote(&opts, &errstr)) == NULL)
                    678:                                goto fail;
                    679:                        if (parse_absolute_time(opt, &ret->valid_before) != 0 ||
                    680:                            ret->valid_before == 0) {
                    681:                                free(opt);
                    682:                                errstr = "invalid \"valid-before\" time";
                    683:                                goto fail;
                    684:                        }
                    685:                        free(opt);
1.1       djm       686:                }
                    687:                /*
                    688:                 * Skip the comma, and move to the next option
                    689:                 * (or break out if there are no more).
                    690:                 */
                    691:                if (*opts == '\0' || *opts == ' ' || *opts == '\t')
                    692:                        break;          /* End of options. */
                    693:                /* Anything other than a comma is an unknown option */
                    694:                if (*opts != ',') {
                    695:                        errstr = "unknown key option";
                    696:                        goto fail;
                    697:                }
                    698:                opts++;
                    699:                if (*opts == '\0') {
                    700:                        errstr = "unexpected end-of-options";
                    701:                        goto fail;
                    702:                }
                    703:        }
1.21      djm       704:        /* final consistency check */
                    705:        if (ret->valid_after != 0 && ret->valid_before != 0 &&
                    706:            ret->valid_before <= ret->valid_after) {
                    707:                errstr = "\"valid-before\" time is before \"valid-after\"";
                    708:                goto fail;
                    709:        }
1.1       djm       710:        /* success */
                    711:        return ret;
                    712:  fail:
                    713:        if (errstrp != NULL)
                    714:                *errstrp = errstr;
1.5       djm       715:        sshsigopt_free(ret);
1.1       djm       716:        return NULL;
                    717: }
                    718:
1.4       djm       719: void
                    720: sshsigopt_free(struct sshsigopt *opts)
1.1       djm       721: {
                    722:        if (opts == NULL)
                    723:                return;
                    724:        free(opts->namespaces);
                    725:        free(opts);
                    726: }
                    727:
                    728: static int
1.9       djm       729: parse_principals_key_and_options(const char *path, u_long linenum, char *line,
                    730:     const char *required_principal, char **principalsp, struct sshkey **keyp,
                    731:     struct sshsigopt **sigoptsp)
1.1       djm       732: {
1.9       djm       733:        char *opts = NULL, *tmp, *cp, *principals = NULL;
1.1       djm       734:        const char *reason = NULL;
1.4       djm       735:        struct sshsigopt *sigopts = NULL;
1.9       djm       736:        struct sshkey *key = NULL;
                    737:        int r = SSH_ERR_INTERNAL_ERROR;
1.1       djm       738:
1.9       djm       739:        if (principalsp != NULL)
                    740:                *principalsp = NULL;
                    741:        if (sigoptsp != NULL)
                    742:                *sigoptsp = NULL;
                    743:        if (keyp != NULL)
                    744:                *keyp = NULL;
1.1       djm       745:
                    746:        cp = line;
1.35    ! djm       747:        cp = cp + strspn(cp, " \t\n\r"); /* skip leading whitespace */
1.1       djm       748:        if (*cp == '#' || *cp == '\0')
1.9       djm       749:                return SSH_ERR_KEY_NOT_FOUND; /* blank or all-comment line */
                    750:
                    751:        /* format: identity[,identity...] [option[,option...]] key */
1.29      djm       752:        if ((tmp = strdelimw(&cp)) == NULL || cp == NULL) {
1.1       djm       753:                error("%s:%lu: invalid line", path, linenum);
1.9       djm       754:                r = SSH_ERR_INVALID_FORMAT;
                    755:                goto out;
1.1       djm       756:        }
1.9       djm       757:        if ((principals = strdup(tmp)) == NULL) {
1.18      djm       758:                error_f("strdup failed");
1.9       djm       759:                r = SSH_ERR_ALLOC_FAIL;
                    760:                goto out;
                    761:        }
                    762:        /*
                    763:         * Bail out early if we're looking for a particular principal and this
                    764:         * line does not list it.
                    765:         */
                    766:        if (required_principal != NULL) {
                    767:                if (match_pattern_list(required_principal,
                    768:                    principals, 0) != 1) {
                    769:                        /* principal didn't match */
                    770:                        r = SSH_ERR_KEY_NOT_FOUND;
                    771:                        goto out;
                    772:                }
1.18      djm       773:                debug_f("%s:%lu: matched principal \"%s\"",
                    774:                    path, linenum, required_principal);
1.1       djm       775:        }
                    776:
1.9       djm       777:        if ((key = sshkey_new(KEY_UNSPEC)) == NULL) {
1.18      djm       778:                error_f("sshkey_new failed");
1.9       djm       779:                r = SSH_ERR_ALLOC_FAIL;
                    780:                goto out;
                    781:        }
                    782:        if (sshkey_read(key, &cp) != 0) {
1.1       djm       783:                /* no key? Check for options */
                    784:                opts = cp;
                    785:                if (sshkey_advance_past_options(&cp) != 0) {
1.9       djm       786:                        error("%s:%lu: invalid options", path, linenum);
1.29      djm       787:                        r = SSH_ERR_INVALID_FORMAT;
                    788:                        goto out;
                    789:                }
                    790:                if (cp == NULL || *cp == '\0') {
                    791:                        error("%s:%lu: missing key", path, linenum);
1.9       djm       792:                        r = SSH_ERR_INVALID_FORMAT;
                    793:                        goto out;
1.1       djm       794:                }
                    795:                *cp++ = '\0';
                    796:                skip_space(&cp);
1.9       djm       797:                if (sshkey_read(key, &cp) != 0) {
                    798:                        error("%s:%lu: invalid key", path, linenum);
                    799:                        r = SSH_ERR_INVALID_FORMAT;
                    800:                        goto out;
1.1       djm       801:                }
                    802:        }
                    803:        debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts);
1.4       djm       804:        if ((sigopts = sshsigopt_parse(opts, path, linenum, &reason)) == NULL) {
1.1       djm       805:                error("%s:%lu: bad options: %s", path, linenum, reason);
1.9       djm       806:                r = SSH_ERR_INVALID_FORMAT;
                    807:                goto out;
                    808:        }
                    809:        /* success */
                    810:        if (principalsp != NULL) {
                    811:                *principalsp = principals;
                    812:                principals = NULL; /* transferred */
                    813:        }
                    814:        if (sigoptsp != NULL) {
                    815:                *sigoptsp = sigopts;
                    816:                sigopts = NULL; /* transferred */
                    817:        }
                    818:        if (keyp != NULL) {
                    819:                *keyp = key;
                    820:                key = NULL; /* transferred */
                    821:        }
                    822:        r = 0;
                    823:  out:
                    824:        free(principals);
                    825:        sshsigopt_free(sigopts);
                    826:        sshkey_free(key);
                    827:        return r;
                    828: }
                    829:
                    830: static int
1.22      djm       831: cert_filter_principals(const char *path, u_long linenum,
                    832:     char **principalsp, const struct sshkey *cert, uint64_t verify_time)
                    833: {
                    834:        char *cp, *oprincipals, *principals;
                    835:        const char *reason;
                    836:        struct sshbuf *nprincipals;
                    837:        int r = SSH_ERR_INTERNAL_ERROR, success = 0;
1.28      djm       838:        u_int i;
1.22      djm       839:
                    840:        oprincipals = principals = *principalsp;
                    841:        *principalsp = NULL;
                    842:
                    843:        if ((nprincipals = sshbuf_new()) == NULL) {
                    844:                r = SSH_ERR_ALLOC_FAIL;
                    845:                goto out;
                    846:        }
                    847:
                    848:        while ((cp = strsep(&principals, ",")) != NULL && *cp != '\0') {
1.28      djm       849:                /* Check certificate validity */
1.22      djm       850:                if ((r = sshkey_cert_check_authority(cert, 0, 1, 0,
1.28      djm       851:                    verify_time, NULL, &reason)) != 0) {
1.22      djm       852:                        debug("%s:%lu: principal \"%s\" not authorized: %s",
                    853:                            path, linenum, cp, reason);
                    854:                        continue;
                    855:                }
1.28      djm       856:                /* Return all matching principal names from the cert */
                    857:                for (i = 0; i < cert->cert->nprincipals; i++) {
                    858:                        if (match_pattern(cert->cert->principals[i], cp)) {
                    859:                                if ((r = sshbuf_putf(nprincipals, "%s%s",
                    860:                                        sshbuf_len(nprincipals) != 0 ? "," : "",
                    861:                                                cert->cert->principals[i])) != 0) {
                    862:                                        error_f("buffer error");
                    863:                                        goto out;
                    864:                                }
                    865:                        }
1.22      djm       866:                }
                    867:        }
                    868:        if (sshbuf_len(nprincipals) == 0) {
                    869:                error("%s:%lu: no valid principals found", path, linenum);
                    870:                r = SSH_ERR_KEY_CERT_INVALID;
                    871:                goto out;
                    872:        }
                    873:        if ((principals = sshbuf_dup_string(nprincipals)) == NULL) {
                    874:                error_f("buffer error");
                    875:                goto out;
                    876:        }
                    877:        /* success */
                    878:        success = 1;
                    879:        *principalsp = principals;
                    880:  out:
                    881:        sshbuf_free(nprincipals);
                    882:        free(oprincipals);
                    883:        return success ? 0 : r;
                    884: }
                    885:
                    886: static int
1.9       djm       887: check_allowed_keys_line(const char *path, u_long linenum, char *line,
                    888:     const struct sshkey *sign_key, const char *principal,
1.23      djm       889:     const char *sig_namespace, uint64_t verify_time, char **principalsp)
1.9       djm       890: {
                    891:        struct sshkey *found_key = NULL;
1.23      djm       892:        char *principals = NULL;
1.21      djm       893:        int r, success = 0;
1.9       djm       894:        const char *reason = NULL;
                    895:        struct sshsigopt *sigopts = NULL;
1.21      djm       896:        char tvalid[64], tverify[64];
1.9       djm       897:
1.23      djm       898:        if (principalsp != NULL)
                    899:                *principalsp = NULL;
                    900:
1.9       djm       901:        /* Parse the line */
                    902:        if ((r = parse_principals_key_and_options(path, linenum, line,
1.23      djm       903:            principal, &principals, &found_key, &sigopts)) != 0) {
1.9       djm       904:                /* error already logged */
1.1       djm       905:                goto done;
                    906:        }
                    907:
                    908:        if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
                    909:                /* Exact match of key */
1.21      djm       910:                debug("%s:%lu: matched key", path, linenum);
1.1       djm       911:        } else if (sigopts->ca && sshkey_is_cert(sign_key) &&
                    912:            sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
1.23      djm       913:                if (principal) {
                    914:                        /* Match certificate CA key with specified principal */
                    915:                        if ((r = sshkey_cert_check_authority(sign_key, 0, 1, 0,
                    916:                            verify_time, principal, &reason)) != 0) {
                    917:                                error("%s:%lu: certificate not authorized: %s",
                    918:                                    path, linenum, reason);
                    919:                                goto done;
                    920:                        }
                    921:                        debug("%s:%lu: matched certificate CA key",
                    922:                            path, linenum);
                    923:                } else {
                    924:                        /* No principal specified - find all matching ones */
                    925:                        if ((r = cert_filter_principals(path, linenum,
                    926:                            &principals, sign_key, verify_time)) != 0) {
                    927:                                /* error already displayed */
                    928:                                debug_r(r, "%s:%lu: cert_filter_principals",
                    929:                                    path, linenum);
                    930:                                goto done;
                    931:                        }
                    932:                        debug("%s:%lu: matched certificate CA key",
                    933:                            path, linenum);
1.1       djm       934:                }
                    935:        } else {
1.21      djm       936:                /* Didn't match key */
1.1       djm       937:                goto done;
                    938:        }
1.21      djm       939:
                    940:        /* Check whether options preclude the use of this key */
1.27      djm       941:        if (sigopts->namespaces != NULL && sig_namespace != NULL &&
1.21      djm       942:            match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) {
                    943:                error("%s:%lu: key is not permitted for use in signature "
                    944:                    "namespace \"%s\"", path, linenum, sig_namespace);
                    945:                goto done;
                    946:        }
                    947:
                    948:        /* check key time validity */
                    949:        format_absolute_time((uint64_t)verify_time, tverify, sizeof(tverify));
                    950:        if (sigopts->valid_after != 0 &&
                    951:            (uint64_t)verify_time < sigopts->valid_after) {
                    952:                format_absolute_time(sigopts->valid_after,
                    953:                    tvalid, sizeof(tvalid));
                    954:                error("%s:%lu: key is not yet valid: "
                    955:                    "verify time %s < valid-after %s", path, linenum,
                    956:                    tverify, tvalid);
                    957:                goto done;
                    958:        }
                    959:        if (sigopts->valid_before != 0 &&
                    960:            (uint64_t)verify_time > sigopts->valid_before) {
                    961:                format_absolute_time(sigopts->valid_before,
                    962:                    tvalid, sizeof(tvalid));
                    963:                error("%s:%lu: key has expired: "
                    964:                    "verify time %s > valid-before %s", path, linenum,
                    965:                    tverify, tvalid);
                    966:                goto done;
                    967:        }
                    968:        success = 1;
                    969:
1.1       djm       970:  done:
1.23      djm       971:        if (success && principalsp != NULL) {
                    972:                *principalsp = principals;
                    973:                principals = NULL; /* transferred */
                    974:        }
                    975:        free(principals);
1.1       djm       976:        sshkey_free(found_key);
1.4       djm       977:        sshsigopt_free(sigopts);
1.21      djm       978:        return success ? 0 : SSH_ERR_KEY_NOT_FOUND;
1.1       djm       979: }
                    980:
                    981: int
                    982: sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
1.21      djm       983:     const char *principal, const char *sig_namespace, uint64_t verify_time)
1.1       djm       984: {
                    985:        FILE *f = NULL;
                    986:        char *line = NULL;
                    987:        size_t linesize = 0;
                    988:        u_long linenum = 0;
1.31      djm       989:        int r = SSH_ERR_KEY_NOT_FOUND, oerrno;
1.1       djm       990:
                    991:        /* Check key and principal against file */
                    992:        if ((f = fopen(path, "r")) == NULL) {
                    993:                oerrno = errno;
                    994:                error("Unable to open allowed keys file \"%s\": %s",
                    995:                    path, strerror(errno));
                    996:                errno = oerrno;
                    997:                return SSH_ERR_SYSTEM_ERROR;
                    998:        }
                    999:
                   1000:        while (getline(&line, &linesize, f) != -1) {
                   1001:                linenum++;
                   1002:                r = check_allowed_keys_line(path, linenum, line, sign_key,
1.23      djm      1003:                    principal, sig_namespace, verify_time, NULL);
1.2       djm      1004:                free(line);
                   1005:                line = NULL;
1.20      dtucker  1006:                linesize = 0;
1.1       djm      1007:                if (r == SSH_ERR_KEY_NOT_FOUND)
                   1008:                        continue;
                   1009:                else if (r == 0) {
                   1010:                        /* success */
                   1011:                        fclose(f);
                   1012:                        return 0;
                   1013:                } else
                   1014:                        break;
                   1015:        }
                   1016:        /* Either we hit an error parsing or we simply didn't find the key */
                   1017:        fclose(f);
                   1018:        free(line);
1.31      djm      1019:        return r;
1.11      djm      1020: }
                   1021:
1.10      djm      1022: int
1.11      djm      1023: sshsig_find_principals(const char *path, const struct sshkey *sign_key,
1.21      djm      1024:     uint64_t verify_time, char **principals)
1.10      djm      1025: {
                   1026:        FILE *f = NULL;
                   1027:        char *line = NULL;
                   1028:        size_t linesize = 0;
                   1029:        u_long linenum = 0;
1.32      djm      1030:        int r = SSH_ERR_KEY_NOT_FOUND, oerrno;
1.10      djm      1031:
                   1032:        if ((f = fopen(path, "r")) == NULL) {
                   1033:                oerrno = errno;
                   1034:                error("Unable to open allowed keys file \"%s\": %s",
                   1035:                    path, strerror(errno));
                   1036:                errno = oerrno;
                   1037:                return SSH_ERR_SYSTEM_ERROR;
                   1038:        }
                   1039:
                   1040:        while (getline(&line, &linesize, f) != -1) {
                   1041:                linenum++;
1.23      djm      1042:                r = check_allowed_keys_line(path, linenum, line,
                   1043:                    sign_key, NULL, NULL, verify_time, principals);
1.10      djm      1044:                free(line);
                   1045:                line = NULL;
1.20      dtucker  1046:                linesize = 0;
1.10      djm      1047:                if (r == SSH_ERR_KEY_NOT_FOUND)
                   1048:                        continue;
                   1049:                else if (r == 0) {
                   1050:                        /* success */
                   1051:                        fclose(f);
                   1052:                        return 0;
                   1053:                } else
                   1054:                        break;
                   1055:        }
                   1056:        free(line);
                   1057:        /* Either we hit an error parsing or we simply didn't find the key */
                   1058:        if (ferror(f) != 0) {
                   1059:                oerrno = errno;
                   1060:                fclose(f);
                   1061:                error("Unable to read allowed keys file \"%s\": %s",
                   1062:                    path, strerror(errno));
                   1063:                errno = oerrno;
                   1064:                return SSH_ERR_SYSTEM_ERROR;
                   1065:        }
                   1066:        fclose(f);
1.32      djm      1067:        return r;
1.24      djm      1068: }
                   1069:
                   1070: int
                   1071: sshsig_match_principals(const char *path, const char *principal,
                   1072:     char ***principalsp, size_t *nprincipalsp)
                   1073: {
                   1074:        FILE *f = NULL;
                   1075:        char *found, *line = NULL, **principals = NULL, **tmp;
                   1076:        size_t i, nprincipals = 0, linesize = 0;
                   1077:        u_long linenum = 0;
1.25      djm      1078:        int oerrno = 0, r, ret = 0;
1.24      djm      1079:
                   1080:        if (principalsp != NULL)
                   1081:                *principalsp = NULL;
                   1082:        if (nprincipalsp != NULL)
                   1083:                *nprincipalsp = 0;
                   1084:
                   1085:        /* Check key and principal against file */
                   1086:        if ((f = fopen(path, "r")) == NULL) {
                   1087:                oerrno = errno;
                   1088:                error("Unable to open allowed keys file \"%s\": %s",
                   1089:                    path, strerror(errno));
                   1090:                errno = oerrno;
                   1091:                return SSH_ERR_SYSTEM_ERROR;
                   1092:        }
                   1093:
                   1094:        while (getline(&line, &linesize, f) != -1) {
                   1095:                linenum++;
                   1096:                /* Parse the line */
                   1097:                if ((r = parse_principals_key_and_options(path, linenum, line,
                   1098:                    principal, &found, NULL, NULL)) != 0) {
                   1099:                        if (r == SSH_ERR_KEY_NOT_FOUND)
                   1100:                                continue;
                   1101:                        ret = r;
                   1102:                        oerrno = errno;
                   1103:                        break; /* unexpected error */
                   1104:                }
                   1105:                if ((tmp = recallocarray(principals, nprincipals,
                   1106:                    nprincipals + 1, sizeof(*principals))) == NULL) {
                   1107:                        ret = SSH_ERR_ALLOC_FAIL;
                   1108:                        free(found);
                   1109:                        break;
                   1110:                }
                   1111:                principals = tmp;
                   1112:                principals[nprincipals++] = found; /* transferred */
                   1113:                free(line);
                   1114:                line = NULL;
                   1115:                linesize = 0;
                   1116:        }
                   1117:        fclose(f);
                   1118:
                   1119:        if (ret == 0) {
                   1120:                if (nprincipals == 0)
                   1121:                        ret = SSH_ERR_KEY_NOT_FOUND;
1.34      markus   1122:                if (nprincipalsp != 0)
                   1123:                        *nprincipalsp = nprincipals;
1.24      djm      1124:                if (principalsp != NULL) {
                   1125:                        *principalsp = principals;
                   1126:                        principals = NULL; /* transferred */
                   1127:                        nprincipals = 0;
                   1128:                }
                   1129:        }
                   1130:
                   1131:        for (i = 0; i < nprincipals; i++)
                   1132:                free(principals[i]);
                   1133:        free(principals);
                   1134:
                   1135:        errno = oerrno;
                   1136:        return ret;
1.10      djm      1137: }
                   1138:
                   1139: int
                   1140: sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey)
                   1141: {
                   1142:        struct sshkey *pk = NULL;
                   1143:        int r = SSH_ERR_SIGNATURE_INVALID;
                   1144:
1.13      markus   1145:        if (pubkey == NULL)
                   1146:                return SSH_ERR_INTERNAL_ERROR;
1.10      djm      1147:        if ((r = sshsig_parse_preamble(signature)) != 0)
                   1148:                return r;
                   1149:        if ((r = sshkey_froms(signature, &pk)) != 0)
                   1150:                return r;
                   1151:
                   1152:        *pubkey = pk;
                   1153:        pk = NULL;
                   1154:        return 0;
                   1155: }