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

1.1       djm         1: /*
                      2:  * Copyright (c) 2019 Google LLC
                      3:  *
                      4:  * Permission to use, copy, modify, and distribute this software for any
                      5:  * purpose with or without fee is hereby granted, provided that the above
                      6:  * copyright notice and this permission notice appear in all copies.
                      7:  *
                      8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                      9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     15:  */
                     16:
                     17: #include <stdio.h>
                     18: #include <stdlib.h>
                     19: #include <stdarg.h>
                     20: #include <errno.h>
                     21: #include <string.h>
                     22: #include <unistd.h>
                     23:
                     24: #include "authfd.h"
                     25: #include "authfile.h"
                     26: #include "log.h"
                     27: #include "misc.h"
                     28: #include "sshbuf.h"
                     29: #include "sshsig.h"
                     30: #include "ssherr.h"
                     31: #include "sshkey.h"
                     32: #include "match.h"
                     33: #include "digest.h"
                     34:
                     35: #define SIG_VERSION            0x01
                     36: #define MAGIC_PREAMBLE         "SSHSIG"
                     37: #define MAGIC_PREAMBLE_LEN     (sizeof(MAGIC_PREAMBLE) - 1)
                     38: #define BEGIN_SIGNATURE                "-----BEGIN SSH SIGNATURE-----\n"
                     39: #define END_SIGNATURE          "-----END SSH SIGNATURE-----"
                     40: #define RSA_SIGN_ALG           "rsa-sha2-512" /* XXX maybe make configurable */
                     41: #define RSA_SIGN_ALLOWED       "rsa-sha2-512,rsa-sha2-256"
                     42: #define HASHALG_DEFAULT                "sha512" /* XXX maybe make configurable */
                     43: #define HASHALG_ALLOWED                "sha256,sha512"
                     44:
                     45: int
                     46: sshsig_armor(const struct sshbuf *blob, struct sshbuf **out)
                     47: {
                     48:        struct sshbuf *buf = NULL;
                     49:        int r = SSH_ERR_INTERNAL_ERROR;
                     50:
                     51:        *out = NULL;
                     52:
                     53:        if ((buf = sshbuf_new()) == NULL) {
                     54:                error("%s: sshbuf_new failed", __func__);
                     55:                r = SSH_ERR_ALLOC_FAIL;
                     56:                goto out;
                     57:        }
                     58:
                     59:        if ((r = sshbuf_put(buf, BEGIN_SIGNATURE,
                     60:            sizeof(BEGIN_SIGNATURE)-1)) != 0) {
                     61:                error("%s: sshbuf_putf failed: %s", __func__, ssh_err(r));
                     62:                goto out;
                     63:        }
                     64:
                     65:        if ((r = sshbuf_dtob64(blob, buf, 1)) != 0) {
                     66:                error("%s: Couldn't base64 encode signature blob: %s",
                     67:                    __func__, ssh_err(r));
                     68:                goto out;
                     69:        }
                     70:
                     71:        if ((r = sshbuf_put(buf, END_SIGNATURE,
                     72:            sizeof(END_SIGNATURE)-1)) != 0 ||
                     73:            (r = sshbuf_put_u8(buf, '\n')) != 0) {
                     74:                error("%s: sshbuf_put failed: %s", __func__, ssh_err(r));
                     75:                goto out;
                     76:        }
                     77:        /* success */
                     78:        *out = buf;
                     79:        buf = NULL; /* transferred */
                     80:        r = 0;
                     81:  out:
                     82:        sshbuf_free(buf);
                     83:        return r;
                     84: }
                     85:
                     86: int
                     87: sshsig_dearmor(struct sshbuf *sig, struct sshbuf **out)
                     88: {
                     89:        int r;
                     90:        size_t eoffset = 0;
                     91:        struct sshbuf *buf = NULL;
                     92:        struct sshbuf *sbuf = NULL;
                     93:        char *b64 = NULL;
                     94:
                     95:        if ((sbuf = sshbuf_fromb(sig)) == NULL) {
                     96:                error("%s: sshbuf_fromb failed", __func__);
                     97:                return SSH_ERR_ALLOC_FAIL;
                     98:        }
                     99:
                    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:
                    106:        if ((r = sshbuf_consume(sbuf, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
                    107:                error("%s: sshbuf_consume failed: %s", __func__, ssh_err(r));
                    108:                goto done;
                    109:        }
                    110:
                    111:        if ((r = sshbuf_find(sbuf, 0, "\n" END_SIGNATURE,
                    112:            sizeof("\n" END_SIGNATURE)-1, &eoffset)) != 0) {
                    113:                error("Couldn't parse signature: missing footer");
                    114:                goto done;
                    115:        }
                    116:
                    117:        if ((r = sshbuf_consume_end(sbuf, sshbuf_len(sbuf)-eoffset)) != 0) {
                    118:                error("%s: sshbuf_consume failed: %s", __func__, ssh_err(r));
                    119:                goto done;
                    120:        }
                    121:
                    122:        if ((b64 = sshbuf_dup_string(sbuf)) == NULL) {
                    123:                error("%s: sshbuf_dup_string failed", __func__);
                    124:                r = SSH_ERR_ALLOC_FAIL;
                    125:                goto done;
                    126:        }
                    127:
                    128:        if ((buf = sshbuf_new()) == NULL) {
                    129:                error("%s: sshbuf_new() failed", __func__);
                    130:                r = SSH_ERR_ALLOC_FAIL;
                    131:                goto done;
                    132:        }
                    133:
                    134:        if ((r = sshbuf_b64tod(buf, b64)) != 0) {
1.3       naddy     135:                error("Couldn't decode signature: %s", ssh_err(r));
1.1       djm       136:                goto done;
                    137:        }
                    138:
                    139:        /* success */
                    140:        *out = buf;
                    141:        r = 0;
                    142:        buf = NULL; /* transferred */
                    143: done:
                    144:        sshbuf_free(buf);
                    145:        sshbuf_free(sbuf);
                    146:        free(b64);
                    147:        return r;
                    148: }
                    149:
                    150: static int
                    151: sshsig_wrap_sign(struct sshkey *key, const char *hashalg,
1.7       djm       152:     const char *sk_provider, const struct sshbuf *h_message,
                    153:     const char *sig_namespace, struct sshbuf **out,
                    154:     sshsig_signer *signer, void *signer_ctx)
1.1       djm       155: {
                    156:        int r;
                    157:        size_t slen = 0;
                    158:        u_char *sig = NULL;
                    159:        struct sshbuf *blob = NULL;
                    160:        struct sshbuf *tosign = NULL;
                    161:        const char *sign_alg = NULL;
                    162:
                    163:        if ((tosign = sshbuf_new()) == NULL ||
                    164:            (blob = sshbuf_new()) == NULL) {
                    165:                error("%s: sshbuf_new failed", __func__);
                    166:                r = SSH_ERR_ALLOC_FAIL;
                    167:                goto done;
                    168:        }
                    169:
                    170:        if ((r = sshbuf_put(tosign, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
                    171:            (r = sshbuf_put_cstring(tosign, sig_namespace)) != 0 ||
                    172:            (r = sshbuf_put_string(tosign, NULL, 0)) != 0 || /* reserved */
                    173:            (r = sshbuf_put_cstring(tosign, hashalg)) != 0 ||
1.6       djm       174:            (r = sshbuf_put_stringb(tosign, h_message)) != 0) {
1.1       djm       175:                error("Couldn't construct message to sign: %s", ssh_err(r));
                    176:                goto done;
                    177:        }
                    178:
                    179:        /* If using RSA keys then default to a good signature algorithm */
                    180:        if (sshkey_type_plain(key->type) == KEY_RSA)
                    181:                sign_alg = RSA_SIGN_ALG;
                    182:
                    183:        if (signer != NULL) {
                    184:                if ((r = signer(key, &sig, &slen,
                    185:                    sshbuf_ptr(tosign), sshbuf_len(tosign),
1.7       djm       186:                    sign_alg, sk_provider, 0, signer_ctx)) != 0) {
1.1       djm       187:                        error("Couldn't sign message: %s", ssh_err(r));
                    188:                        goto done;
                    189:                }
                    190:        } else {
                    191:                if ((r = sshkey_sign(key, &sig, &slen,
                    192:                    sshbuf_ptr(tosign), sshbuf_len(tosign),
1.7       djm       193:                    sign_alg, sk_provider, 0)) != 0) {
1.1       djm       194:                        error("Couldn't sign message: %s", ssh_err(r));
                    195:                        goto done;
                    196:                }
                    197:        }
                    198:
                    199:        if ((r = sshbuf_put(blob, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
                    200:            (r = sshbuf_put_u32(blob, SIG_VERSION)) != 0 ||
                    201:            (r = sshkey_puts(key, blob)) != 0 ||
                    202:            (r = sshbuf_put_cstring(blob, sig_namespace)) != 0 ||
                    203:            (r = sshbuf_put_string(blob, NULL, 0)) != 0 || /* reserved */
                    204:            (r = sshbuf_put_cstring(blob, hashalg)) != 0 ||
                    205:            (r = sshbuf_put_string(blob, sig, slen)) != 0) {
                    206:                error("Couldn't populate blob: %s", ssh_err(r));
                    207:                goto done;
                    208:        }
                    209:
                    210:        *out = blob;
                    211:        blob = NULL;
                    212:        r = 0;
                    213: done:
                    214:        free(sig);
                    215:        sshbuf_free(blob);
                    216:        sshbuf_free(tosign);
                    217:        return r;
                    218: }
                    219:
                    220: /* Check preamble and version. */
                    221: static int
                    222: sshsig_parse_preamble(struct sshbuf *buf)
                    223: {
                    224:        int r = SSH_ERR_INTERNAL_ERROR;
                    225:        uint32_t sversion;
                    226:
                    227:        if ((r = sshbuf_cmp(buf, 0, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
                    228:            (r = sshbuf_consume(buf, (sizeof(MAGIC_PREAMBLE)-1))) != 0 ||
                    229:            (r = sshbuf_get_u32(buf, &sversion)) != 0) {
                    230:                error("Couldn't verify signature: invalid format");
                    231:                return r;
                    232:        }
                    233:
1.2       djm       234:        if (sversion > SIG_VERSION) {
1.1       djm       235:                error("Signature version %lu is larger than supported "
                    236:                    "version %u", (unsigned long)sversion, SIG_VERSION);
                    237:                return SSH_ERR_INVALID_FORMAT;
                    238:        }
                    239:        return 0;
                    240: }
                    241:
                    242: static int
                    243: sshsig_check_hashalg(const char *hashalg)
                    244: {
1.2       djm       245:        if (hashalg == NULL ||
                    246:            match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1)
1.1       djm       247:                return 0;
                    248:        error("%s: unsupported hash algorithm \"%.100s\"", __func__, hashalg);
                    249:        return SSH_ERR_SIGN_ALG_UNSUPPORTED;
                    250: }
                    251:
                    252: static int
                    253: sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp)
                    254: {
                    255:        struct sshbuf *buf = NULL;
                    256:        char *hashalg = NULL;
                    257:        int r = SSH_ERR_INTERNAL_ERROR;
                    258:
                    259:        if (hashalgp != NULL)
                    260:                *hashalgp = NULL;
                    261:        if ((buf = sshbuf_fromb(signature)) == NULL)
                    262:                return SSH_ERR_ALLOC_FAIL;
                    263:        if ((r = sshsig_parse_preamble(buf)) != 0)
                    264:                goto done;
                    265:        if ((r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
                    266:            (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
                    267:            (r = sshbuf_get_string(buf, NULL, NULL)) != 0 ||
                    268:            (r = sshbuf_get_cstring(buf, &hashalg, NULL)) != 0 ||
                    269:            (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0) {
                    270:                error("Couldn't parse signature blob: %s", ssh_err(r));
                    271:                goto done;
                    272:        }
                    273:
                    274:        /* success */
                    275:        r = 0;
                    276:        *hashalgp = hashalg;
                    277:        hashalg = NULL;
                    278:  done:
                    279:        free(hashalg);
                    280:        sshbuf_free(buf);
                    281:        return r;
                    282: }
                    283:
                    284: static int
                    285: sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
                    286:     const struct sshbuf *h_message, const char *expect_namespace,
1.8     ! djm       287:     struct sshkey **sign_keyp, struct sshkey_sig_details **sig_details)
1.1       djm       288: {
                    289:        int r = SSH_ERR_INTERNAL_ERROR;
                    290:        struct sshbuf *buf = NULL, *toverify = NULL;
                    291:        struct sshkey *key = NULL;
                    292:        const u_char *sig;
                    293:        char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL;
                    294:        size_t siglen;
                    295:
1.2       djm       296:        debug("%s: verify message length %zu", __func__, sshbuf_len(h_message));
1.8     ! djm       297:        if (sig_details != NULL)
        !           298:                *sig_details = NULL;
1.1       djm       299:        if (sign_keyp != NULL)
                    300:                *sign_keyp = NULL;
                    301:
                    302:        if ((toverify = sshbuf_new()) == NULL) {
                    303:                error("%s: sshbuf_new failed", __func__);
                    304:                r = SSH_ERR_ALLOC_FAIL;
                    305:                goto done;
                    306:        }
                    307:        if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE,
                    308:            MAGIC_PREAMBLE_LEN)) != 0 ||
                    309:            (r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 ||
                    310:            (r = sshbuf_put_string(toverify, NULL, 0)) != 0 || /* reserved */
                    311:            (r = sshbuf_put_cstring(toverify, hashalg)) != 0 ||
1.6       djm       312:            (r = sshbuf_put_stringb(toverify, h_message)) != 0) {
1.1       djm       313:                error("Couldn't construct message to verify: %s", ssh_err(r));
                    314:                goto done;
                    315:        }
                    316:
                    317:        if ((r = sshsig_parse_preamble(signature)) != 0)
                    318:                goto done;
                    319:
                    320:        if ((r = sshkey_froms(signature, &key)) != 0 ||
                    321:            (r = sshbuf_get_cstring(signature, &got_namespace, NULL)) != 0 ||
                    322:            (r = sshbuf_get_string(signature, NULL, NULL)) != 0 ||
                    323:            (r = sshbuf_get_cstring(signature, &sig_hashalg, NULL)) != 0 ||
                    324:            (r = sshbuf_get_string_direct(signature, &sig, &siglen)) != 0) {
                    325:                error("Couldn't parse signature blob: %s", ssh_err(r));
                    326:                goto done;
                    327:        }
                    328:
                    329:        if (sshbuf_len(signature) != 0) {
                    330:                error("Signature contains trailing data");
                    331:                r = SSH_ERR_INVALID_FORMAT;
                    332:                goto done;
                    333:        }
                    334:
                    335:        if (strcmp(expect_namespace, got_namespace) != 0) {
                    336:                error("Couldn't verify signature: namespace does not match");
                    337:                debug("%s: expected namespace \"%s\" received \"%s\"",
                    338:                    __func__, expect_namespace, got_namespace);
                    339:                r = SSH_ERR_SIGNATURE_INVALID;
                    340:                goto done;
                    341:        }
                    342:        if (strcmp(hashalg, sig_hashalg) != 0) {
                    343:                error("Couldn't verify signature: hash algorithm mismatch");
                    344:                debug("%s: expected algorithm \"%s\" received \"%s\"",
                    345:                    __func__, hashalg, sig_hashalg);
                    346:                r = SSH_ERR_SIGNATURE_INVALID;
                    347:                goto done;
                    348:        }
                    349:        /* Ensure that RSA keys use an acceptable signature algorithm */
                    350:        if (sshkey_type_plain(key->type) == KEY_RSA) {
                    351:                if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) {
                    352:                        error("Couldn't verify signature: unable to get "
                    353:                            "signature type: %s", ssh_err(r));
                    354:                        goto done;
                    355:                }
                    356:                if (match_pattern_list(sigtype, RSA_SIGN_ALLOWED, 0) != 1) {
                    357:                        error("Couldn't verify signature: unsupported RSA "
                    358:                            "signature algorithm %s", sigtype);
                    359:                        r = SSH_ERR_SIGN_ALG_UNSUPPORTED;
                    360:                        goto done;
                    361:                }
                    362:        }
                    363:        if ((r = sshkey_verify(key, sig, siglen, sshbuf_ptr(toverify),
1.8     ! djm       364:            sshbuf_len(toverify), NULL, 0, sig_details)) != 0) {
1.1       djm       365:                error("Signature verification failed: %s", ssh_err(r));
                    366:                goto done;
                    367:        }
                    368:
                    369:        /* success */
                    370:        r = 0;
                    371:        if (sign_keyp != NULL) {
                    372:                *sign_keyp = key;
                    373:                key = NULL; /* transferred */
                    374:        }
                    375: done:
                    376:        free(got_namespace);
                    377:        free(sigtype);
                    378:        free(sig_hashalg);
                    379:        sshbuf_free(buf);
                    380:        sshbuf_free(toverify);
                    381:        sshkey_free(key);
                    382:        return r;
                    383: }
                    384:
1.2       djm       385: static int
                    386: hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp)
1.1       djm       387: {
1.2       djm       388:        char *hex, hash[SSH_DIGEST_MAX_LENGTH];
                    389:        int alg, r = SSH_ERR_INTERNAL_ERROR;
1.1       djm       390:        struct sshbuf *b = NULL;
                    391:
1.2       djm       392:        *bp = NULL;
                    393:        memset(hash, 0, sizeof(hash));
1.1       djm       394:
                    395:        if ((r = sshsig_check_hashalg(hashalg)) != 0)
                    396:                return r;
                    397:        if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
                    398:                error("%s: can't look up hash algorithm %s",
1.2       djm       399:                    __func__, hashalg);
1.1       djm       400:                return SSH_ERR_INTERNAL_ERROR;
                    401:        }
1.2       djm       402:        if ((r = ssh_digest_buffer(alg, m, hash, sizeof(hash))) != 0) {
1.1       djm       403:                error("%s: ssh_digest_buffer failed: %s", __func__, ssh_err(r));
                    404:                return r;
                    405:        }
1.2       djm       406:        if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
                    407:                debug3("%s: final hash: %s", __func__, hex);
                    408:                freezero(hex, strlen(hex));
                    409:        }
                    410:        if ((b = sshbuf_new()) == NULL) {
1.1       djm       411:                r = SSH_ERR_ALLOC_FAIL;
                    412:                goto out;
                    413:        }
1.2       djm       414:        if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
                    415:                error("%s: sshbuf_put: %s", __func__, ssh_err(r));
                    416:                goto out;
                    417:        }
                    418:        *bp = b;
                    419:        b = NULL; /* transferred */
                    420:        /* success */
                    421:        r = 0;
                    422:  out:
                    423:        sshbuf_free(b);
                    424:        explicit_bzero(hash, sizeof(hash));
                    425:        return 0;
                    426: }
                    427:
                    428: int
1.7       djm       429: sshsig_signb(struct sshkey *key, const char *hashalg, const char *sk_provider,
1.2       djm       430:     const struct sshbuf *message, const char *sig_namespace,
                    431:     struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
                    432: {
                    433:        struct sshbuf *b = NULL;
                    434:        int r = SSH_ERR_INTERNAL_ERROR;
                    435:
                    436:        if (hashalg == NULL)
                    437:                hashalg = HASHALG_DEFAULT;
                    438:        if (out != NULL)
                    439:                *out = NULL;
                    440:        if ((r = hash_buffer(message, hashalg, &b)) != 0) {
                    441:                error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
                    442:                goto out;
                    443:        }
1.7       djm       444:        if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, b,
                    445:            sig_namespace, out, signer, signer_ctx)) != 0)
1.1       djm       446:                goto out;
                    447:        /* success */
                    448:        r = 0;
                    449:  out:
                    450:        sshbuf_free(b);
                    451:        return r;
                    452: }
                    453:
                    454: int
1.2       djm       455: sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message,
1.8     ! djm       456:     const char *expect_namespace, struct sshkey **sign_keyp,
        !           457:     struct sshkey_sig_details **sig_details)
1.1       djm       458: {
                    459:        struct sshbuf *b = NULL;
1.2       djm       460:        int r = SSH_ERR_INTERNAL_ERROR;
1.1       djm       461:        char *hashalg = NULL;
                    462:
1.8     ! djm       463:        if (sig_details != NULL)
        !           464:                *sig_details = NULL;
1.1       djm       465:        if (sign_keyp != NULL)
                    466:                *sign_keyp = NULL;
                    467:        if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
                    468:                return r;
1.2       djm       469:        debug("%s: signature made with hash \"%s\"", __func__, hashalg);
                    470:        if ((r = hash_buffer(message, hashalg, &b)) != 0) {
                    471:                error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
1.1       djm       472:                goto out;
                    473:        }
                    474:        if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
1.8     ! djm       475:            sign_keyp, sig_details)) != 0)
1.1       djm       476:                goto out;
                    477:        /* success */
                    478:        r = 0;
                    479:  out:
                    480:        sshbuf_free(b);
                    481:        free(hashalg);
                    482:        return r;
                    483: }
                    484:
                    485: static int
1.2       djm       486: hash_file(int fd, const char *hashalg, struct sshbuf **bp)
1.1       djm       487: {
1.2       djm       488:        char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH];
1.1       djm       489:        ssize_t n, total = 0;
                    490:        struct ssh_digest_ctx *ctx;
1.2       djm       491:        int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR;
                    492:        struct sshbuf *b = NULL;
                    493:
                    494:        *bp = NULL;
                    495:        memset(hash, 0, sizeof(hash));
1.1       djm       496:
1.2       djm       497:        if ((r = sshsig_check_hashalg(hashalg)) != 0)
                    498:                return r;
                    499:        if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
                    500:                error("%s: can't look up hash algorithm %s",
                    501:                    __func__, hashalg);
                    502:                return SSH_ERR_INTERNAL_ERROR;
                    503:        }
                    504:        if ((ctx = ssh_digest_start(alg)) == NULL) {
1.1       djm       505:                error("%s: ssh_digest_start failed", __func__);
                    506:                return SSH_ERR_INTERNAL_ERROR;
                    507:        }
                    508:        for (;;) {
                    509:                if ((n = read(fd, rbuf, sizeof(rbuf))) == -1) {
                    510:                        if (errno == EINTR || errno == EAGAIN)
                    511:                                continue;
                    512:                        oerrno = errno;
                    513:                        error("%s: read: %s", __func__, strerror(errno));
                    514:                        ssh_digest_free(ctx);
                    515:                        errno = oerrno;
1.2       djm       516:                        r = SSH_ERR_SYSTEM_ERROR;
                    517:                        goto out;
1.1       djm       518:                } else if (n == 0) {
                    519:                        debug2("%s: hashed %zu bytes", __func__, total);
                    520:                        break; /* EOF */
                    521:                }
                    522:                total += (size_t)n;
                    523:                if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) {
                    524:                        error("%s: ssh_digest_update: %s",
                    525:                            __func__, ssh_err(r));
1.2       djm       526:                        goto out;
1.1       djm       527:                }
                    528:        }
1.2       djm       529:        if ((r = ssh_digest_final(ctx, hash, sizeof(hash))) != 0) {
1.1       djm       530:                error("%s: ssh_digest_final: %s", __func__, ssh_err(r));
1.2       djm       531:                goto out;
1.1       djm       532:        }
1.2       djm       533:        if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
1.1       djm       534:                debug3("%s: final hash: %s", __func__, hex);
                    535:                freezero(hex, strlen(hex));
                    536:        }
1.2       djm       537:        if ((b = sshbuf_new()) == NULL) {
                    538:                r = SSH_ERR_ALLOC_FAIL;
                    539:                goto out;
                    540:        }
                    541:        if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
                    542:                error("%s: sshbuf_put: %s", __func__, ssh_err(r));
                    543:                goto out;
                    544:        }
                    545:        *bp = b;
                    546:        b = NULL; /* transferred */
1.1       djm       547:        /* success */
1.2       djm       548:        r = 0;
                    549:  out:
                    550:        sshbuf_free(b);
1.1       djm       551:        ssh_digest_free(ctx);
1.2       djm       552:        explicit_bzero(hash, sizeof(hash));
1.1       djm       553:        return 0;
                    554: }
                    555:
                    556: int
1.7       djm       557: sshsig_sign_fd(struct sshkey *key, const char *hashalg, const char *sk_provider,
1.1       djm       558:     int fd, const char *sig_namespace, struct sshbuf **out,
                    559:     sshsig_signer *signer, void *signer_ctx)
                    560: {
                    561:        struct sshbuf *b = NULL;
1.2       djm       562:        int r = SSH_ERR_INTERNAL_ERROR;
1.1       djm       563:
1.2       djm       564:        if (hashalg == NULL)
                    565:                hashalg = HASHALG_DEFAULT;
1.1       djm       566:        if (out != NULL)
                    567:                *out = NULL;
1.2       djm       568:        if ((r = hash_file(fd, hashalg, &b)) != 0) {
1.1       djm       569:                error("%s: hash_file failed: %s", __func__, ssh_err(r));
                    570:                return r;
                    571:        }
1.7       djm       572:        if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, b,
                    573:            sig_namespace, out, signer, signer_ctx)) != 0)
1.1       djm       574:                goto out;
                    575:        /* success */
                    576:        r = 0;
                    577:  out:
                    578:        sshbuf_free(b);
                    579:        return r;
                    580: }
                    581:
                    582: int
                    583: sshsig_verify_fd(struct sshbuf *signature, int fd,
1.8     ! djm       584:     const char *expect_namespace, struct sshkey **sign_keyp,
        !           585:     struct sshkey_sig_details **sig_details)
1.1       djm       586: {
                    587:        struct sshbuf *b = NULL;
1.2       djm       588:        int r = SSH_ERR_INTERNAL_ERROR;
1.1       djm       589:        char *hashalg = NULL;
                    590:
1.8     ! djm       591:        if (sig_details != NULL)
        !           592:                *sig_details = NULL;
1.1       djm       593:        if (sign_keyp != NULL)
                    594:                *sign_keyp = NULL;
                    595:        if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
                    596:                return r;
1.2       djm       597:        debug("%s: signature made with hash \"%s\"", __func__, hashalg);
                    598:        if ((r = hash_file(fd, hashalg, &b)) != 0) {
1.1       djm       599:                error("%s: hash_file failed: %s", __func__, ssh_err(r));
                    600:                goto out;
                    601:        }
                    602:        if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
1.8     ! djm       603:            sign_keyp, sig_details)) != 0)
1.1       djm       604:                goto out;
                    605:        /* success */
                    606:        r = 0;
                    607:  out:
                    608:        sshbuf_free(b);
                    609:        free(hashalg);
                    610:        return r;
                    611: }
                    612:
1.4       djm       613: struct sshsigopt {
1.1       djm       614:        int ca;
                    615:        char *namespaces;
                    616: };
                    617:
1.4       djm       618: struct sshsigopt *
                    619: sshsigopt_parse(const char *opts, const char *path, u_long linenum,
1.1       djm       620:     const char **errstrp)
                    621: {
1.4       djm       622:        struct sshsigopt *ret;
1.1       djm       623:        int r;
                    624:        const char *errstr = NULL;
                    625:
                    626:        if ((ret = calloc(1, sizeof(*ret))) == NULL)
                    627:                return NULL;
                    628:        if (opts == NULL || *opts == '\0')
                    629:                return ret; /* Empty options yields empty options :) */
                    630:
                    631:        while (*opts && *opts != ' ' && *opts != '\t') {
                    632:                /* flag options */
                    633:                if ((r = opt_flag("cert-authority", 0, &opts)) != -1) {
                    634:                        ret->ca = 1;
                    635:                } else if (opt_match(&opts, "namespaces")) {
                    636:                        if (ret->namespaces != NULL) {
                    637:                                errstr = "multiple \"namespaces\" clauses";
                    638:                                goto fail;
                    639:                        }
                    640:                        ret->namespaces = opt_dequote(&opts, &errstr);
                    641:                        if (ret->namespaces == NULL)
                    642:                                goto fail;
                    643:                }
                    644:                /*
                    645:                 * Skip the comma, and move to the next option
                    646:                 * (or break out if there are no more).
                    647:                 */
                    648:                if (*opts == '\0' || *opts == ' ' || *opts == '\t')
                    649:                        break;          /* End of options. */
                    650:                /* Anything other than a comma is an unknown option */
                    651:                if (*opts != ',') {
                    652:                        errstr = "unknown key option";
                    653:                        goto fail;
                    654:                }
                    655:                opts++;
                    656:                if (*opts == '\0') {
                    657:                        errstr = "unexpected end-of-options";
                    658:                        goto fail;
                    659:                }
                    660:        }
                    661:        /* success */
                    662:        return ret;
                    663:  fail:
                    664:        if (errstrp != NULL)
                    665:                *errstrp = errstr;
1.5       djm       666:        sshsigopt_free(ret);
1.1       djm       667:        return NULL;
                    668: }
                    669:
1.4       djm       670: void
                    671: sshsigopt_free(struct sshsigopt *opts)
1.1       djm       672: {
                    673:        if (opts == NULL)
                    674:                return;
                    675:        free(opts->namespaces);
                    676:        free(opts);
                    677: }
                    678:
                    679: static int
                    680: check_allowed_keys_line(const char *path, u_long linenum, char *line,
                    681:     const struct sshkey *sign_key, const char *principal,
                    682:     const char *sig_namespace)
                    683: {
                    684:        struct sshkey *found_key = NULL;
                    685:        char *cp, *opts = NULL, *identities = NULL;
                    686:        int r, found = 0;
                    687:        const char *reason = NULL;
1.4       djm       688:        struct sshsigopt *sigopts = NULL;
1.1       djm       689:
                    690:        if ((found_key = sshkey_new(KEY_UNSPEC)) == NULL) {
                    691:                error("%s: sshkey_new failed", __func__);
                    692:                return SSH_ERR_ALLOC_FAIL;
                    693:        }
                    694:
                    695:        /* format: identity[,identity...] [option[,option...]] key */
                    696:        cp = line;
                    697:        cp = cp + strspn(cp, " \t"); /* skip leading whitespace */
                    698:        if (*cp == '#' || *cp == '\0')
                    699:                goto done;
                    700:        if ((identities = strdelimw(&cp)) == NULL) {
                    701:                error("%s:%lu: invalid line", path, linenum);
                    702:                goto done;
                    703:        }
                    704:        if (match_pattern_list(principal, identities, 0) != 1) {
                    705:                /* principal didn't match */
                    706:                goto done;
                    707:        }
                    708:        debug("%s: %s:%lu: matched principal \"%s\"",
                    709:            __func__, path, linenum, principal);
                    710:
                    711:        if (sshkey_read(found_key, &cp) != 0) {
                    712:                /* no key? Check for options */
                    713:                opts = cp;
                    714:                if (sshkey_advance_past_options(&cp) != 0) {
                    715:                        error("%s:%lu: invalid options",
                    716:                            path, linenum);
                    717:                        goto done;
                    718:                }
                    719:                *cp++ = '\0';
                    720:                skip_space(&cp);
                    721:                if (sshkey_read(found_key, &cp) != 0) {
                    722:                        error("%s:%lu: invalid key", path,
                    723:                            linenum);
                    724:                        goto done;
                    725:                }
                    726:        }
                    727:        debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts);
1.4       djm       728:        if ((sigopts = sshsigopt_parse(opts, path, linenum, &reason)) == NULL) {
1.1       djm       729:                error("%s:%lu: bad options: %s", path, linenum, reason);
                    730:                goto done;
                    731:        }
                    732:
                    733:        /* Check whether options preclude the use of this key */
                    734:        if (sigopts->namespaces != NULL &&
                    735:            match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) {
                    736:                error("%s:%lu: key is not permitted for use in signature "
                    737:                    "namespace \"%s\"", path, linenum, sig_namespace);
                    738:                goto done;
                    739:        }
                    740:
                    741:        if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
                    742:                /* Exact match of key */
                    743:                debug("%s:%lu: matched key and principal", path, linenum);
                    744:                /* success */
                    745:                found = 1;
                    746:        } else if (sigopts->ca && sshkey_is_cert(sign_key) &&
                    747:            sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
                    748:                /* Match of certificate's CA key */
                    749:                if ((r = sshkey_cert_check_authority(sign_key, 0, 1,
                    750:                    principal, &reason)) != 0) {
                    751:                        error("%s:%lu: certificate not authorized: %s",
                    752:                            path, linenum, reason);
                    753:                        goto done;
                    754:                }
                    755:                debug("%s:%lu: matched certificate CA key", path, linenum);
                    756:                /* success */
                    757:                found = 1;
                    758:        } else {
                    759:                /* Principal matched but key didn't */
                    760:                goto done;
                    761:        }
                    762:  done:
                    763:        sshkey_free(found_key);
1.4       djm       764:        sshsigopt_free(sigopts);
1.1       djm       765:        return found ? 0 : SSH_ERR_KEY_NOT_FOUND;
                    766: }
                    767:
                    768: int
                    769: sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
                    770:     const char *principal, const char *sig_namespace)
                    771: {
                    772:        FILE *f = NULL;
                    773:        char *line = NULL;
                    774:        size_t linesize = 0;
                    775:        u_long linenum = 0;
                    776:        int r, oerrno;
                    777:
                    778:        /* Check key and principal against file */
                    779:        if ((f = fopen(path, "r")) == NULL) {
                    780:                oerrno = errno;
                    781:                error("Unable to open allowed keys file \"%s\": %s",
                    782:                    path, strerror(errno));
                    783:                errno = oerrno;
                    784:                return SSH_ERR_SYSTEM_ERROR;
                    785:        }
                    786:
                    787:        while (getline(&line, &linesize, f) != -1) {
                    788:                linenum++;
                    789:                r = check_allowed_keys_line(path, linenum, line, sign_key,
                    790:                    principal, sig_namespace);
1.2       djm       791:                free(line);
                    792:                line = NULL;
1.1       djm       793:                if (r == SSH_ERR_KEY_NOT_FOUND)
                    794:                        continue;
                    795:                else if (r == 0) {
                    796:                        /* success */
                    797:                        fclose(f);
                    798:                        return 0;
                    799:                } else
                    800:                        break;
                    801:        }
                    802:        /* Either we hit an error parsing or we simply didn't find the key */
                    803:        fclose(f);
                    804:        free(line);
                    805:        return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
                    806: }