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

1.17    ! djm         1: /* $OpenBSD$ */
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)
                     39: #define BEGIN_SIGNATURE                "-----BEGIN SSH SIGNATURE-----\n"
                     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) {
                     55:                error("%s: sshbuf_new failed", __func__);
                     56:                r = SSH_ERR_ALLOC_FAIL;
                     57:                goto out;
                     58:        }
                     59:
                     60:        if ((r = sshbuf_put(buf, BEGIN_SIGNATURE,
                     61:            sizeof(BEGIN_SIGNATURE)-1)) != 0) {
                     62:                error("%s: sshbuf_putf failed: %s", __func__, ssh_err(r));
                     63:                goto out;
                     64:        }
                     65:
                     66:        if ((r = sshbuf_dtob64(blob, buf, 1)) != 0) {
                     67:                error("%s: Couldn't base64 encode signature blob: %s",
                     68:                    __func__, ssh_err(r));
                     69:                goto out;
                     70:        }
                     71:
                     72:        if ((r = sshbuf_put(buf, END_SIGNATURE,
                     73:            sizeof(END_SIGNATURE)-1)) != 0 ||
                     74:            (r = sshbuf_put_u8(buf, '\n')) != 0) {
                     75:                error("%s: sshbuf_put failed: %s", __func__, ssh_err(r));
                     76:                goto out;
                     77:        }
                     78:        /* success */
                     79:        *out = buf;
                     80:        buf = NULL; /* transferred */
                     81:        r = 0;
                     82:  out:
                     83:        sshbuf_free(buf);
                     84:        return r;
                     85: }
                     86:
                     87: int
                     88: sshsig_dearmor(struct sshbuf *sig, struct sshbuf **out)
                     89: {
                     90:        int r;
                     91:        size_t eoffset = 0;
                     92:        struct sshbuf *buf = NULL;
                     93:        struct sshbuf *sbuf = NULL;
                     94:        char *b64 = NULL;
                     95:
                     96:        if ((sbuf = sshbuf_fromb(sig)) == NULL) {
                     97:                error("%s: sshbuf_fromb failed", __func__);
                     98:                return SSH_ERR_ALLOC_FAIL;
                     99:        }
                    100:
                    101:        if ((r = sshbuf_cmp(sbuf, 0,
                    102:            BEGIN_SIGNATURE, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
                    103:                error("Couldn't parse signature: missing header");
                    104:                goto done;
                    105:        }
                    106:
                    107:        if ((r = sshbuf_consume(sbuf, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
                    108:                error("%s: sshbuf_consume failed: %s", __func__, ssh_err(r));
                    109:                goto done;
                    110:        }
                    111:
                    112:        if ((r = sshbuf_find(sbuf, 0, "\n" END_SIGNATURE,
                    113:            sizeof("\n" END_SIGNATURE)-1, &eoffset)) != 0) {
                    114:                error("Couldn't parse signature: missing footer");
                    115:                goto done;
                    116:        }
                    117:
                    118:        if ((r = sshbuf_consume_end(sbuf, sshbuf_len(sbuf)-eoffset)) != 0) {
                    119:                error("%s: sshbuf_consume failed: %s", __func__, ssh_err(r));
                    120:                goto done;
                    121:        }
                    122:
                    123:        if ((b64 = sshbuf_dup_string(sbuf)) == NULL) {
                    124:                error("%s: sshbuf_dup_string failed", __func__);
                    125:                r = SSH_ERR_ALLOC_FAIL;
                    126:                goto done;
                    127:        }
                    128:
                    129:        if ((buf = sshbuf_new()) == NULL) {
                    130:                error("%s: sshbuf_new() failed", __func__);
                    131:                r = SSH_ERR_ALLOC_FAIL;
                    132:                goto done;
                    133:        }
                    134:
                    135:        if ((r = sshbuf_b64tod(buf, b64)) != 0) {
1.3       naddy     136:                error("Couldn't decode signature: %s", ssh_err(r));
1.1       djm       137:                goto done;
                    138:        }
                    139:
                    140:        /* success */
                    141:        *out = buf;
                    142:        r = 0;
                    143:        buf = NULL; /* transferred */
                    144: done:
                    145:        sshbuf_free(buf);
                    146:        sshbuf_free(sbuf);
                    147:        free(b64);
                    148:        return r;
                    149: }
                    150:
                    151: static int
                    152: sshsig_wrap_sign(struct sshkey *key, const char *hashalg,
1.16      djm       153:     const char *sk_provider, const char *sk_pin, const struct sshbuf *h_message,
1.7       djm       154:     const char *sig_namespace, struct sshbuf **out,
                    155:     sshsig_signer *signer, void *signer_ctx)
1.1       djm       156: {
                    157:        int r;
                    158:        size_t slen = 0;
                    159:        u_char *sig = NULL;
                    160:        struct sshbuf *blob = NULL;
                    161:        struct sshbuf *tosign = NULL;
                    162:        const char *sign_alg = NULL;
                    163:
                    164:        if ((tosign = sshbuf_new()) == NULL ||
                    165:            (blob = sshbuf_new()) == NULL) {
                    166:                error("%s: sshbuf_new failed", __func__);
                    167:                r = SSH_ERR_ALLOC_FAIL;
                    168:                goto done;
                    169:        }
                    170:
                    171:        if ((r = sshbuf_put(tosign, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
                    172:            (r = sshbuf_put_cstring(tosign, sig_namespace)) != 0 ||
                    173:            (r = sshbuf_put_string(tosign, NULL, 0)) != 0 || /* reserved */
                    174:            (r = sshbuf_put_cstring(tosign, hashalg)) != 0 ||
1.6       djm       175:            (r = sshbuf_put_stringb(tosign, h_message)) != 0) {
1.1       djm       176:                error("Couldn't construct message to sign: %s", ssh_err(r));
                    177:                goto done;
                    178:        }
                    179:
                    180:        /* If using RSA keys then default to a good signature algorithm */
                    181:        if (sshkey_type_plain(key->type) == KEY_RSA)
                    182:                sign_alg = RSA_SIGN_ALG;
                    183:
                    184:        if (signer != NULL) {
                    185:                if ((r = signer(key, &sig, &slen,
                    186:                    sshbuf_ptr(tosign), sshbuf_len(tosign),
1.16      djm       187:                    sign_alg, sk_provider, sk_pin, 0, signer_ctx)) != 0) {
1.1       djm       188:                        error("Couldn't sign message: %s", ssh_err(r));
                    189:                        goto done;
                    190:                }
                    191:        } else {
                    192:                if ((r = sshkey_sign(key, &sig, &slen,
                    193:                    sshbuf_ptr(tosign), sshbuf_len(tosign),
1.16      djm       194:                    sign_alg, sk_provider, sk_pin, 0)) != 0) {
1.1       djm       195:                        error("Couldn't sign message: %s", ssh_err(r));
                    196:                        goto done;
                    197:                }
                    198:        }
                    199:
                    200:        if ((r = sshbuf_put(blob, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
                    201:            (r = sshbuf_put_u32(blob, SIG_VERSION)) != 0 ||
                    202:            (r = sshkey_puts(key, blob)) != 0 ||
                    203:            (r = sshbuf_put_cstring(blob, sig_namespace)) != 0 ||
                    204:            (r = sshbuf_put_string(blob, NULL, 0)) != 0 || /* reserved */
                    205:            (r = sshbuf_put_cstring(blob, hashalg)) != 0 ||
                    206:            (r = sshbuf_put_string(blob, sig, slen)) != 0) {
                    207:                error("Couldn't populate blob: %s", ssh_err(r));
                    208:                goto done;
                    209:        }
                    210:
1.12      markus    211:        if (out != NULL) {
                    212:                *out = blob;
                    213:                blob = NULL;
                    214:        }
1.1       djm       215:        r = 0;
                    216: done:
                    217:        free(sig);
                    218:        sshbuf_free(blob);
                    219:        sshbuf_free(tosign);
                    220:        return r;
                    221: }
                    222:
                    223: /* Check preamble and version. */
                    224: static int
                    225: sshsig_parse_preamble(struct sshbuf *buf)
                    226: {
                    227:        int r = SSH_ERR_INTERNAL_ERROR;
                    228:        uint32_t sversion;
                    229:
                    230:        if ((r = sshbuf_cmp(buf, 0, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
                    231:            (r = sshbuf_consume(buf, (sizeof(MAGIC_PREAMBLE)-1))) != 0 ||
                    232:            (r = sshbuf_get_u32(buf, &sversion)) != 0) {
                    233:                error("Couldn't verify signature: invalid format");
                    234:                return r;
                    235:        }
                    236:
1.2       djm       237:        if (sversion > SIG_VERSION) {
1.1       djm       238:                error("Signature version %lu is larger than supported "
                    239:                    "version %u", (unsigned long)sversion, SIG_VERSION);
                    240:                return SSH_ERR_INVALID_FORMAT;
                    241:        }
                    242:        return 0;
                    243: }
                    244:
                    245: static int
                    246: sshsig_check_hashalg(const char *hashalg)
                    247: {
1.2       djm       248:        if (hashalg == NULL ||
                    249:            match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1)
1.1       djm       250:                return 0;
                    251:        error("%s: unsupported hash algorithm \"%.100s\"", __func__, hashalg);
                    252:        return SSH_ERR_SIGN_ALG_UNSUPPORTED;
                    253: }
                    254:
                    255: static int
                    256: sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp)
                    257: {
                    258:        struct sshbuf *buf = NULL;
                    259:        char *hashalg = NULL;
                    260:        int r = SSH_ERR_INTERNAL_ERROR;
                    261:
                    262:        if (hashalgp != NULL)
                    263:                *hashalgp = NULL;
                    264:        if ((buf = sshbuf_fromb(signature)) == NULL)
                    265:                return SSH_ERR_ALLOC_FAIL;
                    266:        if ((r = sshsig_parse_preamble(buf)) != 0)
                    267:                goto done;
                    268:        if ((r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
                    269:            (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
                    270:            (r = sshbuf_get_string(buf, NULL, NULL)) != 0 ||
                    271:            (r = sshbuf_get_cstring(buf, &hashalg, NULL)) != 0 ||
                    272:            (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0) {
                    273:                error("Couldn't parse signature blob: %s", ssh_err(r));
                    274:                goto done;
                    275:        }
                    276:
                    277:        /* success */
                    278:        r = 0;
                    279:        *hashalgp = hashalg;
                    280:        hashalg = NULL;
                    281:  done:
                    282:        free(hashalg);
                    283:        sshbuf_free(buf);
                    284:        return r;
                    285: }
                    286:
                    287: static int
                    288: sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
                    289:     const struct sshbuf *h_message, const char *expect_namespace,
1.8       djm       290:     struct sshkey **sign_keyp, struct sshkey_sig_details **sig_details)
1.1       djm       291: {
                    292:        int r = SSH_ERR_INTERNAL_ERROR;
                    293:        struct sshbuf *buf = NULL, *toverify = NULL;
                    294:        struct sshkey *key = NULL;
                    295:        const u_char *sig;
                    296:        char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL;
                    297:        size_t siglen;
                    298:
1.2       djm       299:        debug("%s: verify message length %zu", __func__, sshbuf_len(h_message));
1.8       djm       300:        if (sig_details != NULL)
                    301:                *sig_details = NULL;
1.1       djm       302:        if (sign_keyp != NULL)
                    303:                *sign_keyp = NULL;
                    304:
                    305:        if ((toverify = sshbuf_new()) == NULL) {
                    306:                error("%s: sshbuf_new failed", __func__);
                    307:                r = SSH_ERR_ALLOC_FAIL;
                    308:                goto done;
                    309:        }
                    310:        if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE,
                    311:            MAGIC_PREAMBLE_LEN)) != 0 ||
                    312:            (r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 ||
                    313:            (r = sshbuf_put_string(toverify, NULL, 0)) != 0 || /* reserved */
                    314:            (r = sshbuf_put_cstring(toverify, hashalg)) != 0 ||
1.6       djm       315:            (r = sshbuf_put_stringb(toverify, h_message)) != 0) {
1.1       djm       316:                error("Couldn't construct message to verify: %s", ssh_err(r));
                    317:                goto done;
                    318:        }
                    319:
                    320:        if ((r = sshsig_parse_preamble(signature)) != 0)
                    321:                goto done;
                    322:
                    323:        if ((r = sshkey_froms(signature, &key)) != 0 ||
                    324:            (r = sshbuf_get_cstring(signature, &got_namespace, NULL)) != 0 ||
                    325:            (r = sshbuf_get_string(signature, NULL, NULL)) != 0 ||
                    326:            (r = sshbuf_get_cstring(signature, &sig_hashalg, NULL)) != 0 ||
                    327:            (r = sshbuf_get_string_direct(signature, &sig, &siglen)) != 0) {
                    328:                error("Couldn't parse signature blob: %s", ssh_err(r));
                    329:                goto done;
                    330:        }
                    331:
                    332:        if (sshbuf_len(signature) != 0) {
                    333:                error("Signature contains trailing data");
                    334:                r = SSH_ERR_INVALID_FORMAT;
                    335:                goto done;
                    336:        }
                    337:
                    338:        if (strcmp(expect_namespace, got_namespace) != 0) {
                    339:                error("Couldn't verify signature: namespace does not match");
                    340:                debug("%s: expected namespace \"%s\" received \"%s\"",
                    341:                    __func__, expect_namespace, got_namespace);
                    342:                r = SSH_ERR_SIGNATURE_INVALID;
                    343:                goto done;
                    344:        }
                    345:        if (strcmp(hashalg, sig_hashalg) != 0) {
                    346:                error("Couldn't verify signature: hash algorithm mismatch");
                    347:                debug("%s: expected algorithm \"%s\" received \"%s\"",
                    348:                    __func__, hashalg, sig_hashalg);
                    349:                r = SSH_ERR_SIGNATURE_INVALID;
                    350:                goto done;
                    351:        }
                    352:        /* Ensure that RSA keys use an acceptable signature algorithm */
                    353:        if (sshkey_type_plain(key->type) == KEY_RSA) {
                    354:                if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) {
                    355:                        error("Couldn't verify signature: unable to get "
                    356:                            "signature type: %s", ssh_err(r));
                    357:                        goto done;
                    358:                }
                    359:                if (match_pattern_list(sigtype, RSA_SIGN_ALLOWED, 0) != 1) {
                    360:                        error("Couldn't verify signature: unsupported RSA "
                    361:                            "signature algorithm %s", sigtype);
                    362:                        r = SSH_ERR_SIGN_ALG_UNSUPPORTED;
                    363:                        goto done;
                    364:                }
                    365:        }
                    366:        if ((r = sshkey_verify(key, sig, siglen, sshbuf_ptr(toverify),
1.8       djm       367:            sshbuf_len(toverify), NULL, 0, sig_details)) != 0) {
1.1       djm       368:                error("Signature verification failed: %s", ssh_err(r));
                    369:                goto done;
                    370:        }
                    371:
                    372:        /* success */
                    373:        r = 0;
                    374:        if (sign_keyp != NULL) {
                    375:                *sign_keyp = key;
                    376:                key = NULL; /* transferred */
                    377:        }
                    378: done:
                    379:        free(got_namespace);
                    380:        free(sigtype);
                    381:        free(sig_hashalg);
                    382:        sshbuf_free(buf);
                    383:        sshbuf_free(toverify);
                    384:        sshkey_free(key);
                    385:        return r;
                    386: }
                    387:
1.2       djm       388: static int
                    389: hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp)
1.1       djm       390: {
1.2       djm       391:        char *hex, hash[SSH_DIGEST_MAX_LENGTH];
                    392:        int alg, r = SSH_ERR_INTERNAL_ERROR;
1.1       djm       393:        struct sshbuf *b = NULL;
                    394:
1.2       djm       395:        *bp = NULL;
                    396:        memset(hash, 0, sizeof(hash));
1.1       djm       397:
                    398:        if ((r = sshsig_check_hashalg(hashalg)) != 0)
                    399:                return r;
                    400:        if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
                    401:                error("%s: can't look up hash algorithm %s",
1.2       djm       402:                    __func__, hashalg);
1.1       djm       403:                return SSH_ERR_INTERNAL_ERROR;
                    404:        }
1.2       djm       405:        if ((r = ssh_digest_buffer(alg, m, hash, sizeof(hash))) != 0) {
1.1       djm       406:                error("%s: ssh_digest_buffer failed: %s", __func__, ssh_err(r));
                    407:                return r;
                    408:        }
1.2       djm       409:        if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
                    410:                debug3("%s: final hash: %s", __func__, hex);
                    411:                freezero(hex, strlen(hex));
                    412:        }
                    413:        if ((b = sshbuf_new()) == NULL) {
1.1       djm       414:                r = SSH_ERR_ALLOC_FAIL;
                    415:                goto out;
                    416:        }
1.2       djm       417:        if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
                    418:                error("%s: sshbuf_put: %s", __func__, ssh_err(r));
                    419:                goto out;
                    420:        }
                    421:        *bp = b;
                    422:        b = NULL; /* transferred */
                    423:        /* success */
                    424:        r = 0;
                    425:  out:
                    426:        sshbuf_free(b);
                    427:        explicit_bzero(hash, sizeof(hash));
1.12      markus    428:        return r;
1.2       djm       429: }
                    430:
                    431: int
1.16      djm       432: sshsig_signb(struct sshkey *key, const char *hashalg,
                    433:     const char *sk_provider, const char *sk_pin,
1.2       djm       434:     const struct sshbuf *message, const char *sig_namespace,
                    435:     struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
                    436: {
                    437:        struct sshbuf *b = NULL;
                    438:        int r = SSH_ERR_INTERNAL_ERROR;
                    439:
                    440:        if (hashalg == NULL)
                    441:                hashalg = HASHALG_DEFAULT;
                    442:        if (out != NULL)
                    443:                *out = NULL;
                    444:        if ((r = hash_buffer(message, hashalg, &b)) != 0) {
                    445:                error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
                    446:                goto out;
                    447:        }
1.16      djm       448:        if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
1.7       djm       449:            sig_namespace, out, signer, signer_ctx)) != 0)
1.1       djm       450:                goto out;
                    451:        /* success */
                    452:        r = 0;
                    453:  out:
                    454:        sshbuf_free(b);
                    455:        return r;
                    456: }
                    457:
                    458: int
1.2       djm       459: sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message,
1.8       djm       460:     const char *expect_namespace, struct sshkey **sign_keyp,
                    461:     struct sshkey_sig_details **sig_details)
1.1       djm       462: {
                    463:        struct sshbuf *b = NULL;
1.2       djm       464:        int r = SSH_ERR_INTERNAL_ERROR;
1.1       djm       465:        char *hashalg = NULL;
                    466:
1.8       djm       467:        if (sig_details != NULL)
                    468:                *sig_details = NULL;
1.1       djm       469:        if (sign_keyp != NULL)
                    470:                *sign_keyp = NULL;
                    471:        if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
                    472:                return r;
1.2       djm       473:        debug("%s: signature made with hash \"%s\"", __func__, hashalg);
                    474:        if ((r = hash_buffer(message, hashalg, &b)) != 0) {
                    475:                error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
1.1       djm       476:                goto out;
                    477:        }
                    478:        if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
1.8       djm       479:            sign_keyp, sig_details)) != 0)
1.1       djm       480:                goto out;
                    481:        /* success */
                    482:        r = 0;
                    483:  out:
                    484:        sshbuf_free(b);
                    485:        free(hashalg);
                    486:        return r;
                    487: }
                    488:
                    489: static int
1.2       djm       490: hash_file(int fd, const char *hashalg, struct sshbuf **bp)
1.1       djm       491: {
1.2       djm       492:        char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH];
1.1       djm       493:        ssize_t n, total = 0;
                    494:        struct ssh_digest_ctx *ctx;
1.2       djm       495:        int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR;
                    496:        struct sshbuf *b = NULL;
                    497:
                    498:        *bp = NULL;
                    499:        memset(hash, 0, sizeof(hash));
1.1       djm       500:
1.2       djm       501:        if ((r = sshsig_check_hashalg(hashalg)) != 0)
                    502:                return r;
                    503:        if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
                    504:                error("%s: can't look up hash algorithm %s",
                    505:                    __func__, hashalg);
                    506:                return SSH_ERR_INTERNAL_ERROR;
                    507:        }
                    508:        if ((ctx = ssh_digest_start(alg)) == NULL) {
1.1       djm       509:                error("%s: ssh_digest_start failed", __func__);
                    510:                return SSH_ERR_INTERNAL_ERROR;
                    511:        }
                    512:        for (;;) {
                    513:                if ((n = read(fd, rbuf, sizeof(rbuf))) == -1) {
                    514:                        if (errno == EINTR || errno == EAGAIN)
                    515:                                continue;
                    516:                        oerrno = errno;
                    517:                        error("%s: read: %s", __func__, strerror(errno));
                    518:                        ssh_digest_free(ctx);
                    519:                        errno = oerrno;
1.2       djm       520:                        r = SSH_ERR_SYSTEM_ERROR;
                    521:                        goto out;
1.1       djm       522:                } else if (n == 0) {
                    523:                        debug2("%s: hashed %zu bytes", __func__, total);
                    524:                        break; /* EOF */
                    525:                }
                    526:                total += (size_t)n;
                    527:                if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) {
                    528:                        error("%s: ssh_digest_update: %s",
                    529:                            __func__, ssh_err(r));
1.2       djm       530:                        goto out;
1.1       djm       531:                }
                    532:        }
1.2       djm       533:        if ((r = ssh_digest_final(ctx, hash, sizeof(hash))) != 0) {
1.1       djm       534:                error("%s: ssh_digest_final: %s", __func__, ssh_err(r));
1.2       djm       535:                goto out;
1.1       djm       536:        }
1.2       djm       537:        if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
1.1       djm       538:                debug3("%s: final hash: %s", __func__, hex);
                    539:                freezero(hex, strlen(hex));
                    540:        }
1.2       djm       541:        if ((b = sshbuf_new()) == NULL) {
                    542:                r = SSH_ERR_ALLOC_FAIL;
                    543:                goto out;
                    544:        }
                    545:        if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
                    546:                error("%s: sshbuf_put: %s", __func__, ssh_err(r));
                    547:                goto out;
                    548:        }
                    549:        *bp = b;
                    550:        b = NULL; /* transferred */
1.1       djm       551:        /* success */
1.2       djm       552:        r = 0;
                    553:  out:
                    554:        sshbuf_free(b);
1.1       djm       555:        ssh_digest_free(ctx);
1.2       djm       556:        explicit_bzero(hash, sizeof(hash));
1.12      markus    557:        return r;
1.1       djm       558: }
                    559:
                    560: int
1.16      djm       561: sshsig_sign_fd(struct sshkey *key, const char *hashalg,
                    562:     const char *sk_provider, const char *sk_pin,
1.1       djm       563:     int fd, const char *sig_namespace, struct sshbuf **out,
                    564:     sshsig_signer *signer, void *signer_ctx)
                    565: {
                    566:        struct sshbuf *b = NULL;
1.2       djm       567:        int r = SSH_ERR_INTERNAL_ERROR;
1.1       djm       568:
1.2       djm       569:        if (hashalg == NULL)
                    570:                hashalg = HASHALG_DEFAULT;
1.1       djm       571:        if (out != NULL)
                    572:                *out = NULL;
1.2       djm       573:        if ((r = hash_file(fd, hashalg, &b)) != 0) {
1.1       djm       574:                error("%s: hash_file failed: %s", __func__, ssh_err(r));
                    575:                return r;
                    576:        }
1.16      djm       577:        if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
1.7       djm       578:            sig_namespace, out, signer, signer_ctx)) != 0)
1.1       djm       579:                goto out;
                    580:        /* success */
                    581:        r = 0;
                    582:  out:
                    583:        sshbuf_free(b);
                    584:        return r;
                    585: }
                    586:
                    587: int
                    588: sshsig_verify_fd(struct sshbuf *signature, int fd,
1.8       djm       589:     const char *expect_namespace, struct sshkey **sign_keyp,
                    590:     struct sshkey_sig_details **sig_details)
1.1       djm       591: {
                    592:        struct sshbuf *b = NULL;
1.2       djm       593:        int r = SSH_ERR_INTERNAL_ERROR;
1.1       djm       594:        char *hashalg = NULL;
                    595:
1.8       djm       596:        if (sig_details != NULL)
                    597:                *sig_details = NULL;
1.1       djm       598:        if (sign_keyp != NULL)
                    599:                *sign_keyp = NULL;
                    600:        if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
                    601:                return r;
1.2       djm       602:        debug("%s: signature made with hash \"%s\"", __func__, hashalg);
                    603:        if ((r = hash_file(fd, hashalg, &b)) != 0) {
1.1       djm       604:                error("%s: hash_file failed: %s", __func__, ssh_err(r));
                    605:                goto out;
                    606:        }
                    607:        if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
1.8       djm       608:            sign_keyp, sig_details)) != 0)
1.1       djm       609:                goto out;
                    610:        /* success */
                    611:        r = 0;
                    612:  out:
                    613:        sshbuf_free(b);
                    614:        free(hashalg);
                    615:        return r;
                    616: }
                    617:
1.4       djm       618: struct sshsigopt {
1.1       djm       619:        int ca;
                    620:        char *namespaces;
                    621: };
                    622:
1.4       djm       623: struct sshsigopt *
                    624: sshsigopt_parse(const char *opts, const char *path, u_long linenum,
1.1       djm       625:     const char **errstrp)
                    626: {
1.4       djm       627:        struct sshsigopt *ret;
1.1       djm       628:        int r;
                    629:        const char *errstr = NULL;
                    630:
                    631:        if ((ret = calloc(1, sizeof(*ret))) == NULL)
                    632:                return NULL;
                    633:        if (opts == NULL || *opts == '\0')
                    634:                return ret; /* Empty options yields empty options :) */
                    635:
                    636:        while (*opts && *opts != ' ' && *opts != '\t') {
                    637:                /* flag options */
                    638:                if ((r = opt_flag("cert-authority", 0, &opts)) != -1) {
                    639:                        ret->ca = 1;
                    640:                } else if (opt_match(&opts, "namespaces")) {
                    641:                        if (ret->namespaces != NULL) {
                    642:                                errstr = "multiple \"namespaces\" clauses";
                    643:                                goto fail;
                    644:                        }
                    645:                        ret->namespaces = opt_dequote(&opts, &errstr);
                    646:                        if (ret->namespaces == NULL)
                    647:                                goto fail;
                    648:                }
                    649:                /*
                    650:                 * Skip the comma, and move to the next option
                    651:                 * (or break out if there are no more).
                    652:                 */
                    653:                if (*opts == '\0' || *opts == ' ' || *opts == '\t')
                    654:                        break;          /* End of options. */
                    655:                /* Anything other than a comma is an unknown option */
                    656:                if (*opts != ',') {
                    657:                        errstr = "unknown key option";
                    658:                        goto fail;
                    659:                }
                    660:                opts++;
                    661:                if (*opts == '\0') {
                    662:                        errstr = "unexpected end-of-options";
                    663:                        goto fail;
                    664:                }
                    665:        }
                    666:        /* success */
                    667:        return ret;
                    668:  fail:
                    669:        if (errstrp != NULL)
                    670:                *errstrp = errstr;
1.5       djm       671:        sshsigopt_free(ret);
1.1       djm       672:        return NULL;
                    673: }
                    674:
1.4       djm       675: void
                    676: sshsigopt_free(struct sshsigopt *opts)
1.1       djm       677: {
                    678:        if (opts == NULL)
                    679:                return;
                    680:        free(opts->namespaces);
                    681:        free(opts);
                    682: }
                    683:
                    684: static int
1.9       djm       685: parse_principals_key_and_options(const char *path, u_long linenum, char *line,
                    686:     const char *required_principal, char **principalsp, struct sshkey **keyp,
                    687:     struct sshsigopt **sigoptsp)
1.1       djm       688: {
1.9       djm       689:        char *opts = NULL, *tmp, *cp, *principals = NULL;
1.1       djm       690:        const char *reason = NULL;
1.4       djm       691:        struct sshsigopt *sigopts = NULL;
1.9       djm       692:        struct sshkey *key = NULL;
                    693:        int r = SSH_ERR_INTERNAL_ERROR;
1.1       djm       694:
1.9       djm       695:        if (principalsp != NULL)
                    696:                *principalsp = NULL;
                    697:        if (sigoptsp != NULL)
                    698:                *sigoptsp = NULL;
                    699:        if (keyp != NULL)
                    700:                *keyp = NULL;
1.1       djm       701:
                    702:        cp = line;
                    703:        cp = cp + strspn(cp, " \t"); /* skip leading whitespace */
                    704:        if (*cp == '#' || *cp == '\0')
1.9       djm       705:                return SSH_ERR_KEY_NOT_FOUND; /* blank or all-comment line */
                    706:
                    707:        /* format: identity[,identity...] [option[,option...]] key */
                    708:        if ((tmp = strdelimw(&cp)) == NULL) {
1.1       djm       709:                error("%s:%lu: invalid line", path, linenum);
1.9       djm       710:                r = SSH_ERR_INVALID_FORMAT;
                    711:                goto out;
1.1       djm       712:        }
1.9       djm       713:        if ((principals = strdup(tmp)) == NULL) {
                    714:                error("%s: strdup failed", __func__);
                    715:                r = SSH_ERR_ALLOC_FAIL;
                    716:                goto out;
                    717:        }
                    718:        /*
                    719:         * Bail out early if we're looking for a particular principal and this
                    720:         * line does not list it.
                    721:         */
                    722:        if (required_principal != NULL) {
                    723:                if (match_pattern_list(required_principal,
                    724:                    principals, 0) != 1) {
                    725:                        /* principal didn't match */
                    726:                        r = SSH_ERR_KEY_NOT_FOUND;
                    727:                        goto out;
                    728:                }
                    729:                debug("%s: %s:%lu: matched principal \"%s\"",
                    730:                    __func__, path, linenum, required_principal);
1.1       djm       731:        }
                    732:
1.9       djm       733:        if ((key = sshkey_new(KEY_UNSPEC)) == NULL) {
                    734:                error("%s: sshkey_new failed", __func__);
                    735:                r = SSH_ERR_ALLOC_FAIL;
                    736:                goto out;
                    737:        }
                    738:        if (sshkey_read(key, &cp) != 0) {
1.1       djm       739:                /* no key? Check for options */
                    740:                opts = cp;
                    741:                if (sshkey_advance_past_options(&cp) != 0) {
1.9       djm       742:                        error("%s:%lu: invalid options", path, linenum);
                    743:                        r = SSH_ERR_INVALID_FORMAT;
                    744:                        goto out;
1.1       djm       745:                }
                    746:                *cp++ = '\0';
                    747:                skip_space(&cp);
1.9       djm       748:                if (sshkey_read(key, &cp) != 0) {
                    749:                        error("%s:%lu: invalid key", path, linenum);
                    750:                        r = SSH_ERR_INVALID_FORMAT;
                    751:                        goto out;
1.1       djm       752:                }
                    753:        }
                    754:        debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts);
1.4       djm       755:        if ((sigopts = sshsigopt_parse(opts, path, linenum, &reason)) == NULL) {
1.1       djm       756:                error("%s:%lu: bad options: %s", path, linenum, reason);
1.9       djm       757:                r = SSH_ERR_INVALID_FORMAT;
                    758:                goto out;
                    759:        }
                    760:        /* success */
                    761:        if (principalsp != NULL) {
                    762:                *principalsp = principals;
                    763:                principals = NULL; /* transferred */
                    764:        }
                    765:        if (sigoptsp != NULL) {
                    766:                *sigoptsp = sigopts;
                    767:                sigopts = NULL; /* transferred */
                    768:        }
                    769:        if (keyp != NULL) {
                    770:                *keyp = key;
                    771:                key = NULL; /* transferred */
                    772:        }
                    773:        r = 0;
                    774:  out:
                    775:        free(principals);
                    776:        sshsigopt_free(sigopts);
                    777:        sshkey_free(key);
                    778:        return r;
                    779: }
                    780:
                    781: static int
                    782: check_allowed_keys_line(const char *path, u_long linenum, char *line,
                    783:     const struct sshkey *sign_key, const char *principal,
                    784:     const char *sig_namespace)
                    785: {
                    786:        struct sshkey *found_key = NULL;
                    787:        int r, found = 0;
                    788:        const char *reason = NULL;
                    789:        struct sshsigopt *sigopts = NULL;
                    790:
                    791:        /* Parse the line */
                    792:        if ((r = parse_principals_key_and_options(path, linenum, line,
                    793:            principal, NULL, &found_key, &sigopts)) != 0) {
                    794:                /* error already logged */
1.1       djm       795:                goto done;
                    796:        }
                    797:
                    798:        /* Check whether options preclude the use of this key */
                    799:        if (sigopts->namespaces != NULL &&
                    800:            match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) {
                    801:                error("%s:%lu: key is not permitted for use in signature "
                    802:                    "namespace \"%s\"", path, linenum, sig_namespace);
                    803:                goto done;
                    804:        }
                    805:
                    806:        if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
                    807:                /* Exact match of key */
                    808:                debug("%s:%lu: matched key and principal", path, linenum);
                    809:                /* success */
                    810:                found = 1;
                    811:        } else if (sigopts->ca && sshkey_is_cert(sign_key) &&
                    812:            sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
                    813:                /* Match of certificate's CA key */
                    814:                if ((r = sshkey_cert_check_authority(sign_key, 0, 1,
                    815:                    principal, &reason)) != 0) {
                    816:                        error("%s:%lu: certificate not authorized: %s",
                    817:                            path, linenum, reason);
                    818:                        goto done;
                    819:                }
                    820:                debug("%s:%lu: matched certificate CA key", path, linenum);
                    821:                /* success */
                    822:                found = 1;
                    823:        } else {
                    824:                /* Principal matched but key didn't */
                    825:                goto done;
                    826:        }
                    827:  done:
                    828:        sshkey_free(found_key);
1.4       djm       829:        sshsigopt_free(sigopts);
1.1       djm       830:        return found ? 0 : SSH_ERR_KEY_NOT_FOUND;
                    831: }
                    832:
                    833: int
                    834: sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
                    835:     const char *principal, const char *sig_namespace)
                    836: {
                    837:        FILE *f = NULL;
                    838:        char *line = NULL;
                    839:        size_t linesize = 0;
                    840:        u_long linenum = 0;
1.12      markus    841:        int r = SSH_ERR_INTERNAL_ERROR, oerrno;
1.1       djm       842:
                    843:        /* Check key and principal against file */
                    844:        if ((f = fopen(path, "r")) == NULL) {
                    845:                oerrno = errno;
                    846:                error("Unable to open allowed keys file \"%s\": %s",
                    847:                    path, strerror(errno));
                    848:                errno = oerrno;
                    849:                return SSH_ERR_SYSTEM_ERROR;
                    850:        }
                    851:
                    852:        while (getline(&line, &linesize, f) != -1) {
                    853:                linenum++;
                    854:                r = check_allowed_keys_line(path, linenum, line, sign_key,
                    855:                    principal, sig_namespace);
1.2       djm       856:                free(line);
                    857:                line = NULL;
1.1       djm       858:                if (r == SSH_ERR_KEY_NOT_FOUND)
                    859:                        continue;
                    860:                else if (r == 0) {
                    861:                        /* success */
                    862:                        fclose(f);
                    863:                        return 0;
                    864:                } else
                    865:                        break;
                    866:        }
                    867:        /* Either we hit an error parsing or we simply didn't find the key */
                    868:        fclose(f);
                    869:        free(line);
                    870:        return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
                    871: }
1.10      djm       872:
                    873: static int
1.11      djm       874: cert_filter_principals(const char *path, u_long linenum,
                    875:     char **principalsp, const struct sshkey *cert)
                    876: {
                    877:        char *cp, *oprincipals, *principals;
                    878:        const char *reason;
                    879:        struct sshbuf *nprincipals;
                    880:        int r = SSH_ERR_INTERNAL_ERROR, success = 0;
                    881:
                    882:        oprincipals = principals = *principalsp;
                    883:        *principalsp = NULL;
                    884:
1.15      markus    885:        if ((nprincipals = sshbuf_new()) == NULL) {
                    886:                r = SSH_ERR_ALLOC_FAIL;
                    887:                goto out;
                    888:        }
1.11      djm       889:
                    890:        while ((cp = strsep(&principals, ",")) != NULL && *cp != '\0') {
                    891:                if (strcspn(cp, "!?*") != strlen(cp)) {
                    892:                        debug("%s:%lu: principal \"%s\" not authorized: "
                    893:                            "contains wildcards", path, linenum, cp);
                    894:                        continue;
                    895:                }
                    896:                /* Check against principals list in certificate */
                    897:                if ((r = sshkey_cert_check_authority(cert, 0, 1,
                    898:                    cp, &reason)) != 0) {
                    899:                        debug("%s:%lu: principal \"%s\" not authorized: %s",
                    900:                            path, linenum, cp, reason);
                    901:                        continue;
                    902:                }
                    903:                if ((r = sshbuf_putf(nprincipals, "%s%s",
                    904:                    sshbuf_len(nprincipals) != 0 ? "," : "", cp)) != 0) {
                    905:                        error("%s: buffer error", __func__);
                    906:                        goto out;
                    907:                }
                    908:        }
                    909:        if (sshbuf_len(nprincipals) == 0) {
                    910:                error("%s:%lu: no valid principals found", path, linenum);
                    911:                r = SSH_ERR_KEY_CERT_INVALID;
                    912:                goto out;
                    913:        }
                    914:        if ((principals = sshbuf_dup_string(nprincipals)) == NULL) {
                    915:                error("%s: buffer error", __func__);
                    916:                goto out;
                    917:        }
                    918:        /* success */
                    919:        success = 1;
                    920:        *principalsp = principals;
                    921:  out:
                    922:        sshbuf_free(nprincipals);
                    923:        free(oprincipals);
                    924:        return success ? 0 : r;
                    925: }
                    926:
                    927: static int
                    928: get_matching_principals_from_line(const char *path, u_long linenum, char *line,
1.10      djm       929:     const struct sshkey *sign_key, char **principalsp)
                    930: {
                    931:        struct sshkey *found_key = NULL;
                    932:        char *principals = NULL;
                    933:        int r, found = 0;
                    934:        struct sshsigopt *sigopts = NULL;
                    935:
                    936:        if (principalsp != NULL)
                    937:                *principalsp = NULL;
                    938:
                    939:        /* Parse the line */
                    940:        if ((r = parse_principals_key_and_options(path, linenum, line,
                    941:            NULL, &principals, &found_key, &sigopts)) != 0) {
                    942:                /* error already logged */
                    943:                goto done;
                    944:        }
                    945:
                    946:        if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
                    947:                /* Exact match of key */
                    948:                debug("%s:%lu: matched key", path, linenum);
                    949:                /* success */
                    950:                found = 1;
                    951:        } else if (sigopts->ca && sshkey_is_cert(sign_key) &&
                    952:            sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
1.11      djm       953:                /* Remove principals listed in file but not allowed by cert */
                    954:                if ((r = cert_filter_principals(path, linenum,
                    955:                    &principals, sign_key)) != 0) {
                    956:                        /* error already displayed */
                    957:                        debug("%s:%lu: cert_filter_principals: %s",
                    958:                            path, linenum, ssh_err(r));
1.10      djm       959:                        goto done;
                    960:                }
                    961:                debug("%s:%lu: matched certificate CA key", path, linenum);
                    962:                /* success */
                    963:                found = 1;
                    964:        } else {
                    965:                /* Key didn't match */
                    966:                goto done;
                    967:        }
                    968:  done:
1.13      markus    969:        if (found && principalsp != NULL) {
1.10      djm       970:                *principalsp = principals;
                    971:                principals = NULL; /* transferred */
                    972:        }
                    973:        free(principals);
                    974:        sshkey_free(found_key);
                    975:        sshsigopt_free(sigopts);
                    976:        return found ? 0 : SSH_ERR_KEY_NOT_FOUND;
                    977: }
                    978:
                    979: int
1.11      djm       980: sshsig_find_principals(const char *path, const struct sshkey *sign_key,
                    981:     char **principals)
1.10      djm       982: {
                    983:        FILE *f = NULL;
                    984:        char *line = NULL;
                    985:        size_t linesize = 0;
                    986:        u_long linenum = 0;
1.14      markus    987:        int r = SSH_ERR_INTERNAL_ERROR, oerrno;
1.10      djm       988:
                    989:        if ((f = fopen(path, "r")) == NULL) {
                    990:                oerrno = errno;
                    991:                error("Unable to open allowed keys file \"%s\": %s",
                    992:                    path, strerror(errno));
                    993:                errno = oerrno;
                    994:                return SSH_ERR_SYSTEM_ERROR;
                    995:        }
                    996:
                    997:        while (getline(&line, &linesize, f) != -1) {
                    998:                linenum++;
1.11      djm       999:                r = get_matching_principals_from_line(path, linenum, line,
                   1000:                    sign_key, principals);
1.10      djm      1001:                free(line);
                   1002:                line = NULL;
                   1003:                if (r == SSH_ERR_KEY_NOT_FOUND)
                   1004:                        continue;
                   1005:                else if (r == 0) {
                   1006:                        /* success */
                   1007:                        fclose(f);
                   1008:                        return 0;
                   1009:                } else
                   1010:                        break;
                   1011:        }
                   1012:        free(line);
                   1013:        /* Either we hit an error parsing or we simply didn't find the key */
                   1014:        if (ferror(f) != 0) {
                   1015:                oerrno = errno;
                   1016:                fclose(f);
                   1017:                error("Unable to read allowed keys file \"%s\": %s",
                   1018:                    path, strerror(errno));
                   1019:                errno = oerrno;
                   1020:                return SSH_ERR_SYSTEM_ERROR;
                   1021:        }
                   1022:        fclose(f);
                   1023:        return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
                   1024: }
                   1025:
                   1026: int
                   1027: sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey)
                   1028: {
                   1029:        struct sshkey *pk = NULL;
                   1030:        int r = SSH_ERR_SIGNATURE_INVALID;
                   1031:
1.13      markus   1032:        if (pubkey == NULL)
                   1033:                return SSH_ERR_INTERNAL_ERROR;
1.10      djm      1034:        if ((r = sshsig_parse_preamble(signature)) != 0)
                   1035:                return r;
                   1036:        if ((r = sshkey_froms(signature, &pk)) != 0)
                   1037:                return r;
                   1038:
                   1039:        *pubkey = pk;
                   1040:        pk = NULL;
                   1041:        return 0;
                   1042: }