16888a9beSDag-Erling Smørgrav /* 26888a9beSDag-Erling Smørgrav * Copyright (c) 2012 Damien Miller <djm@mindrot.org> 36888a9beSDag-Erling Smørgrav * 46888a9beSDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 56888a9beSDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 66888a9beSDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 76888a9beSDag-Erling Smørgrav * 86888a9beSDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 96888a9beSDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 106888a9beSDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 116888a9beSDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 126888a9beSDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 136888a9beSDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 146888a9beSDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 156888a9beSDag-Erling Smørgrav */ 166888a9beSDag-Erling Smørgrav 17*19261079SEd Maste /* $OpenBSD: krl.c,v 1.53 2021/06/04 06:19:07 djm Exp $ */ 186888a9beSDag-Erling Smørgrav 196888a9beSDag-Erling Smørgrav #include "includes.h" 206888a9beSDag-Erling Smørgrav 216888a9beSDag-Erling Smørgrav #include <sys/types.h> 226888a9beSDag-Erling Smørgrav #include <openbsd-compat/sys-tree.h> 236888a9beSDag-Erling Smørgrav #include <openbsd-compat/sys-queue.h> 246888a9beSDag-Erling Smørgrav 256888a9beSDag-Erling Smørgrav #include <errno.h> 266888a9beSDag-Erling Smørgrav #include <fcntl.h> 276888a9beSDag-Erling Smørgrav #include <limits.h> 28*19261079SEd Maste #include <stdlib.h> 296888a9beSDag-Erling Smørgrav #include <string.h> 306888a9beSDag-Erling Smørgrav #include <time.h> 316888a9beSDag-Erling Smørgrav #include <unistd.h> 326888a9beSDag-Erling Smørgrav 33bc5531deSDag-Erling Smørgrav #include "sshbuf.h" 34bc5531deSDag-Erling Smørgrav #include "ssherr.h" 35bc5531deSDag-Erling Smørgrav #include "sshkey.h" 366888a9beSDag-Erling Smørgrav #include "authfile.h" 376888a9beSDag-Erling Smørgrav #include "misc.h" 386888a9beSDag-Erling Smørgrav #include "log.h" 39bc5531deSDag-Erling Smørgrav #include "digest.h" 40bc5531deSDag-Erling Smørgrav #include "bitmap.h" 41*19261079SEd Maste #include "utf8.h" 426888a9beSDag-Erling Smørgrav 436888a9beSDag-Erling Smørgrav #include "krl.h" 446888a9beSDag-Erling Smørgrav 456888a9beSDag-Erling Smørgrav /* #define DEBUG_KRL */ 466888a9beSDag-Erling Smørgrav #ifdef DEBUG_KRL 47*19261079SEd Maste # define KRL_DBG(x) debug3_f x 486888a9beSDag-Erling Smørgrav #else 496888a9beSDag-Erling Smørgrav # define KRL_DBG(x) 506888a9beSDag-Erling Smørgrav #endif 516888a9beSDag-Erling Smørgrav 526888a9beSDag-Erling Smørgrav /* 536888a9beSDag-Erling Smørgrav * Trees of revoked serial numbers, key IDs and keys. This allows 546888a9beSDag-Erling Smørgrav * quick searching, querying and producing lists in canonical order. 556888a9beSDag-Erling Smørgrav */ 566888a9beSDag-Erling Smørgrav 576888a9beSDag-Erling Smørgrav /* Tree of serial numbers. XXX make smarter: really need a real sparse bitmap */ 586888a9beSDag-Erling Smørgrav struct revoked_serial { 596888a9beSDag-Erling Smørgrav u_int64_t lo, hi; 606888a9beSDag-Erling Smørgrav RB_ENTRY(revoked_serial) tree_entry; 616888a9beSDag-Erling Smørgrav }; 626888a9beSDag-Erling Smørgrav static int serial_cmp(struct revoked_serial *a, struct revoked_serial *b); 636888a9beSDag-Erling Smørgrav RB_HEAD(revoked_serial_tree, revoked_serial); 64*19261079SEd Maste RB_GENERATE_STATIC(revoked_serial_tree, revoked_serial, tree_entry, serial_cmp) 656888a9beSDag-Erling Smørgrav 666888a9beSDag-Erling Smørgrav /* Tree of key IDs */ 676888a9beSDag-Erling Smørgrav struct revoked_key_id { 686888a9beSDag-Erling Smørgrav char *key_id; 696888a9beSDag-Erling Smørgrav RB_ENTRY(revoked_key_id) tree_entry; 706888a9beSDag-Erling Smørgrav }; 716888a9beSDag-Erling Smørgrav static int key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b); 726888a9beSDag-Erling Smørgrav RB_HEAD(revoked_key_id_tree, revoked_key_id); 73*19261079SEd Maste RB_GENERATE_STATIC(revoked_key_id_tree, revoked_key_id, tree_entry, key_id_cmp) 746888a9beSDag-Erling Smørgrav 756888a9beSDag-Erling Smørgrav /* Tree of blobs (used for keys and fingerprints) */ 766888a9beSDag-Erling Smørgrav struct revoked_blob { 776888a9beSDag-Erling Smørgrav u_char *blob; 78bc5531deSDag-Erling Smørgrav size_t len; 796888a9beSDag-Erling Smørgrav RB_ENTRY(revoked_blob) tree_entry; 806888a9beSDag-Erling Smørgrav }; 816888a9beSDag-Erling Smørgrav static int blob_cmp(struct revoked_blob *a, struct revoked_blob *b); 826888a9beSDag-Erling Smørgrav RB_HEAD(revoked_blob_tree, revoked_blob); 83*19261079SEd Maste RB_GENERATE_STATIC(revoked_blob_tree, revoked_blob, tree_entry, blob_cmp) 846888a9beSDag-Erling Smørgrav 856888a9beSDag-Erling Smørgrav /* Tracks revoked certs for a single CA */ 866888a9beSDag-Erling Smørgrav struct revoked_certs { 87bc5531deSDag-Erling Smørgrav struct sshkey *ca_key; 886888a9beSDag-Erling Smørgrav struct revoked_serial_tree revoked_serials; 896888a9beSDag-Erling Smørgrav struct revoked_key_id_tree revoked_key_ids; 906888a9beSDag-Erling Smørgrav TAILQ_ENTRY(revoked_certs) entry; 916888a9beSDag-Erling Smørgrav }; 926888a9beSDag-Erling Smørgrav TAILQ_HEAD(revoked_certs_list, revoked_certs); 936888a9beSDag-Erling Smørgrav 946888a9beSDag-Erling Smørgrav struct ssh_krl { 956888a9beSDag-Erling Smørgrav u_int64_t krl_version; 966888a9beSDag-Erling Smørgrav u_int64_t generated_date; 976888a9beSDag-Erling Smørgrav u_int64_t flags; 986888a9beSDag-Erling Smørgrav char *comment; 996888a9beSDag-Erling Smørgrav struct revoked_blob_tree revoked_keys; 1006888a9beSDag-Erling Smørgrav struct revoked_blob_tree revoked_sha1s; 1012f513db7SEd Maste struct revoked_blob_tree revoked_sha256s; 1026888a9beSDag-Erling Smørgrav struct revoked_certs_list revoked_certs; 1036888a9beSDag-Erling Smørgrav }; 1046888a9beSDag-Erling Smørgrav 1056888a9beSDag-Erling Smørgrav /* Return equal if a and b overlap */ 1066888a9beSDag-Erling Smørgrav static int 1076888a9beSDag-Erling Smørgrav serial_cmp(struct revoked_serial *a, struct revoked_serial *b) 1086888a9beSDag-Erling Smørgrav { 1096888a9beSDag-Erling Smørgrav if (a->hi >= b->lo && a->lo <= b->hi) 1106888a9beSDag-Erling Smørgrav return 0; 1116888a9beSDag-Erling Smørgrav return a->lo < b->lo ? -1 : 1; 1126888a9beSDag-Erling Smørgrav } 1136888a9beSDag-Erling Smørgrav 1146888a9beSDag-Erling Smørgrav static int 1156888a9beSDag-Erling Smørgrav key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b) 1166888a9beSDag-Erling Smørgrav { 1176888a9beSDag-Erling Smørgrav return strcmp(a->key_id, b->key_id); 1186888a9beSDag-Erling Smørgrav } 1196888a9beSDag-Erling Smørgrav 1206888a9beSDag-Erling Smørgrav static int 1216888a9beSDag-Erling Smørgrav blob_cmp(struct revoked_blob *a, struct revoked_blob *b) 1226888a9beSDag-Erling Smørgrav { 1236888a9beSDag-Erling Smørgrav int r; 1246888a9beSDag-Erling Smørgrav 1256888a9beSDag-Erling Smørgrav if (a->len != b->len) { 126ca86bcf2SDag-Erling Smørgrav if ((r = memcmp(a->blob, b->blob, MINIMUM(a->len, b->len))) != 0) 1276888a9beSDag-Erling Smørgrav return r; 1286888a9beSDag-Erling Smørgrav return a->len > b->len ? 1 : -1; 1296888a9beSDag-Erling Smørgrav } else 1306888a9beSDag-Erling Smørgrav return memcmp(a->blob, b->blob, a->len); 1316888a9beSDag-Erling Smørgrav } 1326888a9beSDag-Erling Smørgrav 1336888a9beSDag-Erling Smørgrav struct ssh_krl * 1346888a9beSDag-Erling Smørgrav ssh_krl_init(void) 1356888a9beSDag-Erling Smørgrav { 1366888a9beSDag-Erling Smørgrav struct ssh_krl *krl; 1376888a9beSDag-Erling Smørgrav 1386888a9beSDag-Erling Smørgrav if ((krl = calloc(1, sizeof(*krl))) == NULL) 1396888a9beSDag-Erling Smørgrav return NULL; 1406888a9beSDag-Erling Smørgrav RB_INIT(&krl->revoked_keys); 1416888a9beSDag-Erling Smørgrav RB_INIT(&krl->revoked_sha1s); 1422f513db7SEd Maste RB_INIT(&krl->revoked_sha256s); 1436888a9beSDag-Erling Smørgrav TAILQ_INIT(&krl->revoked_certs); 1446888a9beSDag-Erling Smørgrav return krl; 1456888a9beSDag-Erling Smørgrav } 1466888a9beSDag-Erling Smørgrav 1476888a9beSDag-Erling Smørgrav static void 1486888a9beSDag-Erling Smørgrav revoked_certs_free(struct revoked_certs *rc) 1496888a9beSDag-Erling Smørgrav { 1506888a9beSDag-Erling Smørgrav struct revoked_serial *rs, *trs; 1516888a9beSDag-Erling Smørgrav struct revoked_key_id *rki, *trki; 1526888a9beSDag-Erling Smørgrav 1536888a9beSDag-Erling Smørgrav RB_FOREACH_SAFE(rs, revoked_serial_tree, &rc->revoked_serials, trs) { 1546888a9beSDag-Erling Smørgrav RB_REMOVE(revoked_serial_tree, &rc->revoked_serials, rs); 1556888a9beSDag-Erling Smørgrav free(rs); 1566888a9beSDag-Erling Smørgrav } 1576888a9beSDag-Erling Smørgrav RB_FOREACH_SAFE(rki, revoked_key_id_tree, &rc->revoked_key_ids, trki) { 1586888a9beSDag-Erling Smørgrav RB_REMOVE(revoked_key_id_tree, &rc->revoked_key_ids, rki); 1596888a9beSDag-Erling Smørgrav free(rki->key_id); 1606888a9beSDag-Erling Smørgrav free(rki); 1616888a9beSDag-Erling Smørgrav } 162bc5531deSDag-Erling Smørgrav sshkey_free(rc->ca_key); 1636888a9beSDag-Erling Smørgrav } 1646888a9beSDag-Erling Smørgrav 1656888a9beSDag-Erling Smørgrav void 1666888a9beSDag-Erling Smørgrav ssh_krl_free(struct ssh_krl *krl) 1676888a9beSDag-Erling Smørgrav { 1686888a9beSDag-Erling Smørgrav struct revoked_blob *rb, *trb; 1696888a9beSDag-Erling Smørgrav struct revoked_certs *rc, *trc; 1706888a9beSDag-Erling Smørgrav 1716888a9beSDag-Erling Smørgrav if (krl == NULL) 1726888a9beSDag-Erling Smørgrav return; 1736888a9beSDag-Erling Smørgrav 1746888a9beSDag-Erling Smørgrav free(krl->comment); 1756888a9beSDag-Erling Smørgrav RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_keys, trb) { 1766888a9beSDag-Erling Smørgrav RB_REMOVE(revoked_blob_tree, &krl->revoked_keys, rb); 1776888a9beSDag-Erling Smørgrav free(rb->blob); 1786888a9beSDag-Erling Smørgrav free(rb); 1796888a9beSDag-Erling Smørgrav } 1806888a9beSDag-Erling Smørgrav RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_sha1s, trb) { 1816888a9beSDag-Erling Smørgrav RB_REMOVE(revoked_blob_tree, &krl->revoked_sha1s, rb); 1826888a9beSDag-Erling Smørgrav free(rb->blob); 1836888a9beSDag-Erling Smørgrav free(rb); 1846888a9beSDag-Erling Smørgrav } 1852f513db7SEd Maste RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_sha256s, trb) { 1862f513db7SEd Maste RB_REMOVE(revoked_blob_tree, &krl->revoked_sha256s, rb); 1872f513db7SEd Maste free(rb->blob); 1882f513db7SEd Maste free(rb); 1892f513db7SEd Maste } 1906888a9beSDag-Erling Smørgrav TAILQ_FOREACH_SAFE(rc, &krl->revoked_certs, entry, trc) { 1916888a9beSDag-Erling Smørgrav TAILQ_REMOVE(&krl->revoked_certs, rc, entry); 1926888a9beSDag-Erling Smørgrav revoked_certs_free(rc); 1936888a9beSDag-Erling Smørgrav } 1946888a9beSDag-Erling Smørgrav } 1956888a9beSDag-Erling Smørgrav 1966888a9beSDag-Erling Smørgrav void 1976888a9beSDag-Erling Smørgrav ssh_krl_set_version(struct ssh_krl *krl, u_int64_t version) 1986888a9beSDag-Erling Smørgrav { 1996888a9beSDag-Erling Smørgrav krl->krl_version = version; 2006888a9beSDag-Erling Smørgrav } 2016888a9beSDag-Erling Smørgrav 202bc5531deSDag-Erling Smørgrav int 2036888a9beSDag-Erling Smørgrav ssh_krl_set_comment(struct ssh_krl *krl, const char *comment) 2046888a9beSDag-Erling Smørgrav { 2056888a9beSDag-Erling Smørgrav free(krl->comment); 2066888a9beSDag-Erling Smørgrav if ((krl->comment = strdup(comment)) == NULL) 207bc5531deSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 208bc5531deSDag-Erling Smørgrav return 0; 2096888a9beSDag-Erling Smørgrav } 2106888a9beSDag-Erling Smørgrav 2116888a9beSDag-Erling Smørgrav /* 2126888a9beSDag-Erling Smørgrav * Find the revoked_certs struct for a CA key. If allow_create is set then 2136888a9beSDag-Erling Smørgrav * create a new one in the tree if one did not exist already. 2146888a9beSDag-Erling Smørgrav */ 2156888a9beSDag-Erling Smørgrav static int 216bc5531deSDag-Erling Smørgrav revoked_certs_for_ca_key(struct ssh_krl *krl, const struct sshkey *ca_key, 2176888a9beSDag-Erling Smørgrav struct revoked_certs **rcp, int allow_create) 2186888a9beSDag-Erling Smørgrav { 2196888a9beSDag-Erling Smørgrav struct revoked_certs *rc; 220bc5531deSDag-Erling Smørgrav int r; 2216888a9beSDag-Erling Smørgrav 2226888a9beSDag-Erling Smørgrav *rcp = NULL; 2236888a9beSDag-Erling Smørgrav TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { 224bc5531deSDag-Erling Smørgrav if ((ca_key == NULL && rc->ca_key == NULL) || 225bc5531deSDag-Erling Smørgrav sshkey_equal(rc->ca_key, ca_key)) { 2266888a9beSDag-Erling Smørgrav *rcp = rc; 2276888a9beSDag-Erling Smørgrav return 0; 2286888a9beSDag-Erling Smørgrav } 2296888a9beSDag-Erling Smørgrav } 2306888a9beSDag-Erling Smørgrav if (!allow_create) 2316888a9beSDag-Erling Smørgrav return 0; 2326888a9beSDag-Erling Smørgrav /* If this CA doesn't exist in the list then add it now */ 2336888a9beSDag-Erling Smørgrav if ((rc = calloc(1, sizeof(*rc))) == NULL) 234bc5531deSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 235bc5531deSDag-Erling Smørgrav if (ca_key == NULL) 236bc5531deSDag-Erling Smørgrav rc->ca_key = NULL; 237bc5531deSDag-Erling Smørgrav else if ((r = sshkey_from_private(ca_key, &rc->ca_key)) != 0) { 2386888a9beSDag-Erling Smørgrav free(rc); 239bc5531deSDag-Erling Smørgrav return r; 2406888a9beSDag-Erling Smørgrav } 2416888a9beSDag-Erling Smørgrav RB_INIT(&rc->revoked_serials); 2426888a9beSDag-Erling Smørgrav RB_INIT(&rc->revoked_key_ids); 2436888a9beSDag-Erling Smørgrav TAILQ_INSERT_TAIL(&krl->revoked_certs, rc, entry); 244*19261079SEd Maste KRL_DBG(("new CA %s", ca_key == NULL ? "*" : sshkey_type(ca_key))); 2456888a9beSDag-Erling Smørgrav *rcp = rc; 2466888a9beSDag-Erling Smørgrav return 0; 2476888a9beSDag-Erling Smørgrav } 2486888a9beSDag-Erling Smørgrav 2496888a9beSDag-Erling Smørgrav static int 2506888a9beSDag-Erling Smørgrav insert_serial_range(struct revoked_serial_tree *rt, u_int64_t lo, u_int64_t hi) 2516888a9beSDag-Erling Smørgrav { 2526888a9beSDag-Erling Smørgrav struct revoked_serial rs, *ers, *crs, *irs; 2536888a9beSDag-Erling Smørgrav 254*19261079SEd Maste KRL_DBG(("insert %llu:%llu", lo, hi)); 255b83788ffSDag-Erling Smørgrav memset(&rs, 0, sizeof(rs)); 2566888a9beSDag-Erling Smørgrav rs.lo = lo; 2576888a9beSDag-Erling Smørgrav rs.hi = hi; 2586888a9beSDag-Erling Smørgrav ers = RB_NFIND(revoked_serial_tree, rt, &rs); 2596888a9beSDag-Erling Smørgrav if (ers == NULL || serial_cmp(ers, &rs) != 0) { 2606888a9beSDag-Erling Smørgrav /* No entry matches. Just insert */ 2616888a9beSDag-Erling Smørgrav if ((irs = malloc(sizeof(rs))) == NULL) 262bc5531deSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 2636888a9beSDag-Erling Smørgrav memcpy(irs, &rs, sizeof(*irs)); 2646888a9beSDag-Erling Smørgrav ers = RB_INSERT(revoked_serial_tree, rt, irs); 2656888a9beSDag-Erling Smørgrav if (ers != NULL) { 266*19261079SEd Maste KRL_DBG(("bad: ers != NULL")); 2676888a9beSDag-Erling Smørgrav /* Shouldn't happen */ 2686888a9beSDag-Erling Smørgrav free(irs); 269bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 2706888a9beSDag-Erling Smørgrav } 2716888a9beSDag-Erling Smørgrav ers = irs; 2726888a9beSDag-Erling Smørgrav } else { 273*19261079SEd Maste KRL_DBG(("overlap found %llu:%llu", ers->lo, ers->hi)); 2746888a9beSDag-Erling Smørgrav /* 2756888a9beSDag-Erling Smørgrav * The inserted entry overlaps an existing one. Grow the 2766888a9beSDag-Erling Smørgrav * existing entry. 2776888a9beSDag-Erling Smørgrav */ 2786888a9beSDag-Erling Smørgrav if (ers->lo > lo) 2796888a9beSDag-Erling Smørgrav ers->lo = lo; 2806888a9beSDag-Erling Smørgrav if (ers->hi < hi) 2816888a9beSDag-Erling Smørgrav ers->hi = hi; 2826888a9beSDag-Erling Smørgrav } 283bc5531deSDag-Erling Smørgrav 2846888a9beSDag-Erling Smørgrav /* 2856888a9beSDag-Erling Smørgrav * The inserted or revised range might overlap or abut adjacent ones; 2866888a9beSDag-Erling Smørgrav * coalesce as necessary. 2876888a9beSDag-Erling Smørgrav */ 2886888a9beSDag-Erling Smørgrav 2896888a9beSDag-Erling Smørgrav /* Check predecessors */ 2906888a9beSDag-Erling Smørgrav while ((crs = RB_PREV(revoked_serial_tree, rt, ers)) != NULL) { 291*19261079SEd Maste KRL_DBG(("pred %llu:%llu", crs->lo, crs->hi)); 2926888a9beSDag-Erling Smørgrav if (ers->lo != 0 && crs->hi < ers->lo - 1) 2936888a9beSDag-Erling Smørgrav break; 2946888a9beSDag-Erling Smørgrav /* This entry overlaps. */ 2956888a9beSDag-Erling Smørgrav if (crs->lo < ers->lo) { 2966888a9beSDag-Erling Smørgrav ers->lo = crs->lo; 297*19261079SEd Maste KRL_DBG(("pred extend %llu:%llu", ers->lo, ers->hi)); 2986888a9beSDag-Erling Smørgrav } 2996888a9beSDag-Erling Smørgrav RB_REMOVE(revoked_serial_tree, rt, crs); 3006888a9beSDag-Erling Smørgrav free(crs); 3016888a9beSDag-Erling Smørgrav } 3026888a9beSDag-Erling Smørgrav /* Check successors */ 3036888a9beSDag-Erling Smørgrav while ((crs = RB_NEXT(revoked_serial_tree, rt, ers)) != NULL) { 304*19261079SEd Maste KRL_DBG(("succ %llu:%llu", crs->lo, crs->hi)); 3056888a9beSDag-Erling Smørgrav if (ers->hi != (u_int64_t)-1 && crs->lo > ers->hi + 1) 3066888a9beSDag-Erling Smørgrav break; 3076888a9beSDag-Erling Smørgrav /* This entry overlaps. */ 3086888a9beSDag-Erling Smørgrav if (crs->hi > ers->hi) { 3096888a9beSDag-Erling Smørgrav ers->hi = crs->hi; 310*19261079SEd Maste KRL_DBG(("succ extend %llu:%llu", ers->lo, ers->hi)); 3116888a9beSDag-Erling Smørgrav } 3126888a9beSDag-Erling Smørgrav RB_REMOVE(revoked_serial_tree, rt, crs); 3136888a9beSDag-Erling Smørgrav free(crs); 3146888a9beSDag-Erling Smørgrav } 315*19261079SEd Maste KRL_DBG(("done, final %llu:%llu", ers->lo, ers->hi)); 3166888a9beSDag-Erling Smørgrav return 0; 3176888a9beSDag-Erling Smørgrav } 3186888a9beSDag-Erling Smørgrav 3196888a9beSDag-Erling Smørgrav int 320bc5531deSDag-Erling Smørgrav ssh_krl_revoke_cert_by_serial(struct ssh_krl *krl, const struct sshkey *ca_key, 3216888a9beSDag-Erling Smørgrav u_int64_t serial) 3226888a9beSDag-Erling Smørgrav { 3236888a9beSDag-Erling Smørgrav return ssh_krl_revoke_cert_by_serial_range(krl, ca_key, serial, serial); 3246888a9beSDag-Erling Smørgrav } 3256888a9beSDag-Erling Smørgrav 3266888a9beSDag-Erling Smørgrav int 327bc5531deSDag-Erling Smørgrav ssh_krl_revoke_cert_by_serial_range(struct ssh_krl *krl, 328bc5531deSDag-Erling Smørgrav const struct sshkey *ca_key, u_int64_t lo, u_int64_t hi) 3296888a9beSDag-Erling Smørgrav { 3306888a9beSDag-Erling Smørgrav struct revoked_certs *rc; 331bc5531deSDag-Erling Smørgrav int r; 3326888a9beSDag-Erling Smørgrav 3336888a9beSDag-Erling Smørgrav if (lo > hi || lo == 0) 334bc5531deSDag-Erling Smørgrav return SSH_ERR_INVALID_ARGUMENT; 335bc5531deSDag-Erling Smørgrav if ((r = revoked_certs_for_ca_key(krl, ca_key, &rc, 1)) != 0) 336bc5531deSDag-Erling Smørgrav return r; 3376888a9beSDag-Erling Smørgrav return insert_serial_range(&rc->revoked_serials, lo, hi); 3386888a9beSDag-Erling Smørgrav } 3396888a9beSDag-Erling Smørgrav 3406888a9beSDag-Erling Smørgrav int 341bc5531deSDag-Erling Smørgrav ssh_krl_revoke_cert_by_key_id(struct ssh_krl *krl, const struct sshkey *ca_key, 3426888a9beSDag-Erling Smørgrav const char *key_id) 3436888a9beSDag-Erling Smørgrav { 3446888a9beSDag-Erling Smørgrav struct revoked_key_id *rki, *erki; 3456888a9beSDag-Erling Smørgrav struct revoked_certs *rc; 346bc5531deSDag-Erling Smørgrav int r; 3476888a9beSDag-Erling Smørgrav 348bc5531deSDag-Erling Smørgrav if ((r = revoked_certs_for_ca_key(krl, ca_key, &rc, 1)) != 0) 349bc5531deSDag-Erling Smørgrav return r; 3506888a9beSDag-Erling Smørgrav 351*19261079SEd Maste KRL_DBG(("revoke %s", key_id)); 3526888a9beSDag-Erling Smørgrav if ((rki = calloc(1, sizeof(*rki))) == NULL || 3536888a9beSDag-Erling Smørgrav (rki->key_id = strdup(key_id)) == NULL) { 3546888a9beSDag-Erling Smørgrav free(rki); 355bc5531deSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 3566888a9beSDag-Erling Smørgrav } 3576888a9beSDag-Erling Smørgrav erki = RB_INSERT(revoked_key_id_tree, &rc->revoked_key_ids, rki); 3586888a9beSDag-Erling Smørgrav if (erki != NULL) { 3596888a9beSDag-Erling Smørgrav free(rki->key_id); 3606888a9beSDag-Erling Smørgrav free(rki); 3616888a9beSDag-Erling Smørgrav } 3626888a9beSDag-Erling Smørgrav return 0; 3636888a9beSDag-Erling Smørgrav } 3646888a9beSDag-Erling Smørgrav 3656888a9beSDag-Erling Smørgrav /* Convert "key" to a public key blob without any certificate information */ 3666888a9beSDag-Erling Smørgrav static int 367bc5531deSDag-Erling Smørgrav plain_key_blob(const struct sshkey *key, u_char **blob, size_t *blen) 3686888a9beSDag-Erling Smørgrav { 369bc5531deSDag-Erling Smørgrav struct sshkey *kcopy; 3706888a9beSDag-Erling Smørgrav int r; 3716888a9beSDag-Erling Smørgrav 372bc5531deSDag-Erling Smørgrav if ((r = sshkey_from_private(key, &kcopy)) != 0) 373bc5531deSDag-Erling Smørgrav return r; 374bc5531deSDag-Erling Smørgrav if (sshkey_is_cert(kcopy)) { 375bc5531deSDag-Erling Smørgrav if ((r = sshkey_drop_cert(kcopy)) != 0) { 376bc5531deSDag-Erling Smørgrav sshkey_free(kcopy); 377bc5531deSDag-Erling Smørgrav return r; 3786888a9beSDag-Erling Smørgrav } 3796888a9beSDag-Erling Smørgrav } 380bc5531deSDag-Erling Smørgrav r = sshkey_to_blob(kcopy, blob, blen); 381bc5531deSDag-Erling Smørgrav sshkey_free(kcopy); 382a0ee8cc6SDag-Erling Smørgrav return r; 3836888a9beSDag-Erling Smørgrav } 3846888a9beSDag-Erling Smørgrav 3856888a9beSDag-Erling Smørgrav /* Revoke a key blob. Ownership of blob is transferred to the tree */ 3866888a9beSDag-Erling Smørgrav static int 387bc5531deSDag-Erling Smørgrav revoke_blob(struct revoked_blob_tree *rbt, u_char *blob, size_t len) 3886888a9beSDag-Erling Smørgrav { 3896888a9beSDag-Erling Smørgrav struct revoked_blob *rb, *erb; 3906888a9beSDag-Erling Smørgrav 3916888a9beSDag-Erling Smørgrav if ((rb = calloc(1, sizeof(*rb))) == NULL) 392bc5531deSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 3936888a9beSDag-Erling Smørgrav rb->blob = blob; 3946888a9beSDag-Erling Smørgrav rb->len = len; 3956888a9beSDag-Erling Smørgrav erb = RB_INSERT(revoked_blob_tree, rbt, rb); 3966888a9beSDag-Erling Smørgrav if (erb != NULL) { 3976888a9beSDag-Erling Smørgrav free(rb->blob); 3986888a9beSDag-Erling Smørgrav free(rb); 3996888a9beSDag-Erling Smørgrav } 4006888a9beSDag-Erling Smørgrav return 0; 4016888a9beSDag-Erling Smørgrav } 4026888a9beSDag-Erling Smørgrav 4036888a9beSDag-Erling Smørgrav int 404bc5531deSDag-Erling Smørgrav ssh_krl_revoke_key_explicit(struct ssh_krl *krl, const struct sshkey *key) 4056888a9beSDag-Erling Smørgrav { 4066888a9beSDag-Erling Smørgrav u_char *blob; 407bc5531deSDag-Erling Smørgrav size_t len; 408bc5531deSDag-Erling Smørgrav int r; 4096888a9beSDag-Erling Smørgrav 410*19261079SEd Maste debug3_f("revoke type %s", sshkey_type(key)); 411bc5531deSDag-Erling Smørgrav if ((r = plain_key_blob(key, &blob, &len)) != 0) 412bc5531deSDag-Erling Smørgrav return r; 4136888a9beSDag-Erling Smørgrav return revoke_blob(&krl->revoked_keys, blob, len); 4146888a9beSDag-Erling Smørgrav } 4156888a9beSDag-Erling Smørgrav 4162f513db7SEd Maste static int 4172f513db7SEd Maste revoke_by_hash(struct revoked_blob_tree *target, const u_char *p, size_t len) 4186888a9beSDag-Erling Smørgrav { 4196888a9beSDag-Erling Smørgrav u_char *blob; 420bc5531deSDag-Erling Smørgrav int r; 4216888a9beSDag-Erling Smørgrav 4222f513db7SEd Maste /* need to copy hash, as revoke_blob steals ownership */ 4232f513db7SEd Maste if ((blob = malloc(len)) == NULL) 4242f513db7SEd Maste return SSH_ERR_SYSTEM_ERROR; 4252f513db7SEd Maste memcpy(blob, p, len); 4262f513db7SEd Maste if ((r = revoke_blob(target, blob, len)) != 0) { 4272f513db7SEd Maste free(blob); 428bc5531deSDag-Erling Smørgrav return r; 4292f513db7SEd Maste } 4302f513db7SEd Maste return 0; 4312f513db7SEd Maste } 4322f513db7SEd Maste 4332f513db7SEd Maste int 4342f513db7SEd Maste ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const u_char *p, size_t len) 4352f513db7SEd Maste { 436*19261079SEd Maste debug3_f("revoke by sha1"); 4372f513db7SEd Maste if (len != 20) 4382f513db7SEd Maste return SSH_ERR_INVALID_FORMAT; 4392f513db7SEd Maste return revoke_by_hash(&krl->revoked_sha1s, p, len); 4402f513db7SEd Maste } 4412f513db7SEd Maste 4422f513db7SEd Maste int 4432f513db7SEd Maste ssh_krl_revoke_key_sha256(struct ssh_krl *krl, const u_char *p, size_t len) 4442f513db7SEd Maste { 445*19261079SEd Maste debug3_f("revoke by sha256"); 4462f513db7SEd Maste if (len != 32) 4472f513db7SEd Maste return SSH_ERR_INVALID_FORMAT; 4482f513db7SEd Maste return revoke_by_hash(&krl->revoked_sha256s, p, len); 4496888a9beSDag-Erling Smørgrav } 4506888a9beSDag-Erling Smørgrav 4516888a9beSDag-Erling Smørgrav int 452bc5531deSDag-Erling Smørgrav ssh_krl_revoke_key(struct ssh_krl *krl, const struct sshkey *key) 4536888a9beSDag-Erling Smørgrav { 4542f513db7SEd Maste /* XXX replace with SHA256? */ 455bc5531deSDag-Erling Smørgrav if (!sshkey_is_cert(key)) 4562f513db7SEd Maste return ssh_krl_revoke_key_explicit(krl, key); 4576888a9beSDag-Erling Smørgrav 458eccfee6eSDag-Erling Smørgrav if (key->cert->serial == 0) { 4596888a9beSDag-Erling Smørgrav return ssh_krl_revoke_cert_by_key_id(krl, 4606888a9beSDag-Erling Smørgrav key->cert->signature_key, 4616888a9beSDag-Erling Smørgrav key->cert->key_id); 4626888a9beSDag-Erling Smørgrav } else { 4636888a9beSDag-Erling Smørgrav return ssh_krl_revoke_cert_by_serial(krl, 4646888a9beSDag-Erling Smørgrav key->cert->signature_key, 4656888a9beSDag-Erling Smørgrav key->cert->serial); 4666888a9beSDag-Erling Smørgrav } 4676888a9beSDag-Erling Smørgrav } 4686888a9beSDag-Erling Smørgrav 4696888a9beSDag-Erling Smørgrav /* 470bc5531deSDag-Erling Smørgrav * Select the most compact section type to emit next in a KRL based on 471bc5531deSDag-Erling Smørgrav * the current section type, the run length of contiguous revoked serial 4726888a9beSDag-Erling Smørgrav * numbers and the gaps from the last and to the next revoked serial. 4736888a9beSDag-Erling Smørgrav * Applies a mostly-accurate bit cost model to select the section type 4746888a9beSDag-Erling Smørgrav * that will minimise the size of the resultant KRL. 4756888a9beSDag-Erling Smørgrav */ 4766888a9beSDag-Erling Smørgrav static int 4776888a9beSDag-Erling Smørgrav choose_next_state(int current_state, u_int64_t contig, int final, 4786888a9beSDag-Erling Smørgrav u_int64_t last_gap, u_int64_t next_gap, int *force_new_section) 4796888a9beSDag-Erling Smørgrav { 4806888a9beSDag-Erling Smørgrav int new_state; 4816888a9beSDag-Erling Smørgrav u_int64_t cost, cost_list, cost_range, cost_bitmap, cost_bitmap_restart; 4826888a9beSDag-Erling Smørgrav 4836888a9beSDag-Erling Smørgrav /* 4846888a9beSDag-Erling Smørgrav * Avoid unsigned overflows. 4856888a9beSDag-Erling Smørgrav * The limits are high enough to avoid confusing the calculations. 4866888a9beSDag-Erling Smørgrav */ 487ca86bcf2SDag-Erling Smørgrav contig = MINIMUM(contig, 1ULL<<31); 488ca86bcf2SDag-Erling Smørgrav last_gap = MINIMUM(last_gap, 1ULL<<31); 489ca86bcf2SDag-Erling Smørgrav next_gap = MINIMUM(next_gap, 1ULL<<31); 4906888a9beSDag-Erling Smørgrav 4916888a9beSDag-Erling Smørgrav /* 4926888a9beSDag-Erling Smørgrav * Calculate the cost to switch from the current state to candidates. 4936888a9beSDag-Erling Smørgrav * NB. range sections only ever contain a single range, so their 4946888a9beSDag-Erling Smørgrav * switching cost is independent of the current_state. 4956888a9beSDag-Erling Smørgrav */ 4966888a9beSDag-Erling Smørgrav cost_list = cost_bitmap = cost_bitmap_restart = 0; 4976888a9beSDag-Erling Smørgrav cost_range = 8; 4986888a9beSDag-Erling Smørgrav switch (current_state) { 4996888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_LIST: 5006888a9beSDag-Erling Smørgrav cost_bitmap_restart = cost_bitmap = 8 + 64; 5016888a9beSDag-Erling Smørgrav break; 5026888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_BITMAP: 5036888a9beSDag-Erling Smørgrav cost_list = 8; 5046888a9beSDag-Erling Smørgrav cost_bitmap_restart = 8 + 64; 5056888a9beSDag-Erling Smørgrav break; 5066888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_RANGE: 5076888a9beSDag-Erling Smørgrav case 0: 5086888a9beSDag-Erling Smørgrav cost_bitmap_restart = cost_bitmap = 8 + 64; 5096888a9beSDag-Erling Smørgrav cost_list = 8; 5106888a9beSDag-Erling Smørgrav } 5116888a9beSDag-Erling Smørgrav 5126888a9beSDag-Erling Smørgrav /* Estimate base cost in bits of each section type */ 5136888a9beSDag-Erling Smørgrav cost_list += 64 * contig + (final ? 0 : 8+64); 5146888a9beSDag-Erling Smørgrav cost_range += (2 * 64) + (final ? 0 : 8+64); 515ca86bcf2SDag-Erling Smørgrav cost_bitmap += last_gap + contig + (final ? 0 : MINIMUM(next_gap, 8+64)); 516ca86bcf2SDag-Erling Smørgrav cost_bitmap_restart += contig + (final ? 0 : MINIMUM(next_gap, 8+64)); 5176888a9beSDag-Erling Smørgrav 5186888a9beSDag-Erling Smørgrav /* Convert to byte costs for actual comparison */ 5196888a9beSDag-Erling Smørgrav cost_list = (cost_list + 7) / 8; 5206888a9beSDag-Erling Smørgrav cost_bitmap = (cost_bitmap + 7) / 8; 5216888a9beSDag-Erling Smørgrav cost_bitmap_restart = (cost_bitmap_restart + 7) / 8; 5226888a9beSDag-Erling Smørgrav cost_range = (cost_range + 7) / 8; 5236888a9beSDag-Erling Smørgrav 5246888a9beSDag-Erling Smørgrav /* Now pick the best choice */ 5256888a9beSDag-Erling Smørgrav *force_new_section = 0; 5266888a9beSDag-Erling Smørgrav new_state = KRL_SECTION_CERT_SERIAL_BITMAP; 5276888a9beSDag-Erling Smørgrav cost = cost_bitmap; 5286888a9beSDag-Erling Smørgrav if (cost_range < cost) { 5296888a9beSDag-Erling Smørgrav new_state = KRL_SECTION_CERT_SERIAL_RANGE; 5306888a9beSDag-Erling Smørgrav cost = cost_range; 5316888a9beSDag-Erling Smørgrav } 5326888a9beSDag-Erling Smørgrav if (cost_list < cost) { 5336888a9beSDag-Erling Smørgrav new_state = KRL_SECTION_CERT_SERIAL_LIST; 5346888a9beSDag-Erling Smørgrav cost = cost_list; 5356888a9beSDag-Erling Smørgrav } 5366888a9beSDag-Erling Smørgrav if (cost_bitmap_restart < cost) { 5376888a9beSDag-Erling Smørgrav new_state = KRL_SECTION_CERT_SERIAL_BITMAP; 5386888a9beSDag-Erling Smørgrav *force_new_section = 1; 5396888a9beSDag-Erling Smørgrav cost = cost_bitmap_restart; 5406888a9beSDag-Erling Smørgrav } 541*19261079SEd Maste KRL_DBG(("contig %llu last_gap %llu next_gap %llu final %d, costs:" 5426888a9beSDag-Erling Smørgrav "list %llu range %llu bitmap %llu new bitmap %llu, " 543*19261079SEd Maste "selected 0x%02x%s", (long long unsigned)contig, 544e4a9863fSDag-Erling Smørgrav (long long unsigned)last_gap, (long long unsigned)next_gap, final, 545e4a9863fSDag-Erling Smørgrav (long long unsigned)cost_list, (long long unsigned)cost_range, 546e4a9863fSDag-Erling Smørgrav (long long unsigned)cost_bitmap, 547e4a9863fSDag-Erling Smørgrav (long long unsigned)cost_bitmap_restart, new_state, 548bc5531deSDag-Erling Smørgrav *force_new_section ? " restart" : "")); 5496888a9beSDag-Erling Smørgrav return new_state; 5506888a9beSDag-Erling Smørgrav } 5516888a9beSDag-Erling Smørgrav 552bc5531deSDag-Erling Smørgrav static int 553bc5531deSDag-Erling Smørgrav put_bitmap(struct sshbuf *buf, struct bitmap *bitmap) 554bc5531deSDag-Erling Smørgrav { 555bc5531deSDag-Erling Smørgrav size_t len; 556bc5531deSDag-Erling Smørgrav u_char *blob; 557bc5531deSDag-Erling Smørgrav int r; 558bc5531deSDag-Erling Smørgrav 559bc5531deSDag-Erling Smørgrav len = bitmap_nbytes(bitmap); 560bc5531deSDag-Erling Smørgrav if ((blob = malloc(len)) == NULL) 561bc5531deSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 562bc5531deSDag-Erling Smørgrav if (bitmap_to_string(bitmap, blob, len) != 0) { 563bc5531deSDag-Erling Smørgrav free(blob); 564bc5531deSDag-Erling Smørgrav return SSH_ERR_INTERNAL_ERROR; 565bc5531deSDag-Erling Smørgrav } 566bc5531deSDag-Erling Smørgrav r = sshbuf_put_bignum2_bytes(buf, blob, len); 567bc5531deSDag-Erling Smørgrav free(blob); 568bc5531deSDag-Erling Smørgrav return r; 569bc5531deSDag-Erling Smørgrav } 570bc5531deSDag-Erling Smørgrav 5716888a9beSDag-Erling Smørgrav /* Generate a KRL_SECTION_CERTIFICATES KRL section */ 5726888a9beSDag-Erling Smørgrav static int 573bc5531deSDag-Erling Smørgrav revoked_certs_generate(struct revoked_certs *rc, struct sshbuf *buf) 5746888a9beSDag-Erling Smørgrav { 575bc5531deSDag-Erling Smørgrav int final, force_new_sect, r = SSH_ERR_INTERNAL_ERROR; 5766888a9beSDag-Erling Smørgrav u_int64_t i, contig, gap, last = 0, bitmap_start = 0; 5776888a9beSDag-Erling Smørgrav struct revoked_serial *rs, *nrs; 5786888a9beSDag-Erling Smørgrav struct revoked_key_id *rki; 5796888a9beSDag-Erling Smørgrav int next_state, state = 0; 580bc5531deSDag-Erling Smørgrav struct sshbuf *sect; 581bc5531deSDag-Erling Smørgrav struct bitmap *bitmap = NULL; 5826888a9beSDag-Erling Smørgrav 583bc5531deSDag-Erling Smørgrav if ((sect = sshbuf_new()) == NULL) 584bc5531deSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 5856888a9beSDag-Erling Smørgrav 586bc5531deSDag-Erling Smørgrav /* Store the header: optional CA scope key, reserved */ 587bc5531deSDag-Erling Smørgrav if (rc->ca_key == NULL) { 588bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_string(buf, NULL, 0)) != 0) 589bc5531deSDag-Erling Smørgrav goto out; 590bc5531deSDag-Erling Smørgrav } else { 591bc5531deSDag-Erling Smørgrav if ((r = sshkey_puts(rc->ca_key, buf)) != 0) 592bc5531deSDag-Erling Smørgrav goto out; 593bc5531deSDag-Erling Smørgrav } 594bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_string(buf, NULL, 0)) != 0) 595bc5531deSDag-Erling Smørgrav goto out; 5966888a9beSDag-Erling Smørgrav 5976888a9beSDag-Erling Smørgrav /* Store the revoked serials. */ 5986888a9beSDag-Erling Smørgrav for (rs = RB_MIN(revoked_serial_tree, &rc->revoked_serials); 5996888a9beSDag-Erling Smørgrav rs != NULL; 6006888a9beSDag-Erling Smørgrav rs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs)) { 601*19261079SEd Maste KRL_DBG(("serial %llu:%llu state 0x%02x", 602e4a9863fSDag-Erling Smørgrav (long long unsigned)rs->lo, (long long unsigned)rs->hi, 603bc5531deSDag-Erling Smørgrav state)); 6046888a9beSDag-Erling Smørgrav 6056888a9beSDag-Erling Smørgrav /* Check contiguous length and gap to next section (if any) */ 6066888a9beSDag-Erling Smørgrav nrs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs); 6076888a9beSDag-Erling Smørgrav final = nrs == NULL; 6086888a9beSDag-Erling Smørgrav gap = nrs == NULL ? 0 : nrs->lo - rs->hi; 6096888a9beSDag-Erling Smørgrav contig = 1 + (rs->hi - rs->lo); 6106888a9beSDag-Erling Smørgrav 6116888a9beSDag-Erling Smørgrav /* Choose next state based on these */ 6126888a9beSDag-Erling Smørgrav next_state = choose_next_state(state, contig, final, 6136888a9beSDag-Erling Smørgrav state == 0 ? 0 : rs->lo - last, gap, &force_new_sect); 6146888a9beSDag-Erling Smørgrav 6156888a9beSDag-Erling Smørgrav /* 6166888a9beSDag-Erling Smørgrav * If the current section is a range section or has a different 6176888a9beSDag-Erling Smørgrav * type to the next section, then finish it off now. 6186888a9beSDag-Erling Smørgrav */ 6196888a9beSDag-Erling Smørgrav if (state != 0 && (force_new_sect || next_state != state || 6206888a9beSDag-Erling Smørgrav state == KRL_SECTION_CERT_SERIAL_RANGE)) { 621*19261079SEd Maste KRL_DBG(("finish state 0x%02x", state)); 6226888a9beSDag-Erling Smørgrav switch (state) { 6236888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_LIST: 6246888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_RANGE: 6256888a9beSDag-Erling Smørgrav break; 6266888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_BITMAP: 627bc5531deSDag-Erling Smørgrav if ((r = put_bitmap(sect, bitmap)) != 0) 628bc5531deSDag-Erling Smørgrav goto out; 629bc5531deSDag-Erling Smørgrav bitmap_free(bitmap); 6306888a9beSDag-Erling Smørgrav bitmap = NULL; 6316888a9beSDag-Erling Smørgrav break; 6326888a9beSDag-Erling Smørgrav } 633bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(buf, state)) != 0 || 634bc5531deSDag-Erling Smørgrav (r = sshbuf_put_stringb(buf, sect)) != 0) 635bc5531deSDag-Erling Smørgrav goto out; 636bc5531deSDag-Erling Smørgrav sshbuf_reset(sect); 6376888a9beSDag-Erling Smørgrav } 6386888a9beSDag-Erling Smørgrav 6396888a9beSDag-Erling Smørgrav /* If we are starting a new section then prepare it now */ 6406888a9beSDag-Erling Smørgrav if (next_state != state || force_new_sect) { 641*19261079SEd Maste KRL_DBG(("start state 0x%02x", 642bc5531deSDag-Erling Smørgrav next_state)); 6436888a9beSDag-Erling Smørgrav state = next_state; 644bc5531deSDag-Erling Smørgrav sshbuf_reset(sect); 6456888a9beSDag-Erling Smørgrav switch (state) { 6466888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_LIST: 6476888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_RANGE: 6486888a9beSDag-Erling Smørgrav break; 6496888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_BITMAP: 650bc5531deSDag-Erling Smørgrav if ((bitmap = bitmap_new()) == NULL) { 651bc5531deSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 6526888a9beSDag-Erling Smørgrav goto out; 653bc5531deSDag-Erling Smørgrav } 6546888a9beSDag-Erling Smørgrav bitmap_start = rs->lo; 655bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u64(sect, 656bc5531deSDag-Erling Smørgrav bitmap_start)) != 0) 657bc5531deSDag-Erling Smørgrav goto out; 6586888a9beSDag-Erling Smørgrav break; 6596888a9beSDag-Erling Smørgrav } 6606888a9beSDag-Erling Smørgrav } 6616888a9beSDag-Erling Smørgrav 6626888a9beSDag-Erling Smørgrav /* Perform section-specific processing */ 6636888a9beSDag-Erling Smørgrav switch (state) { 6646888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_LIST: 665bc5531deSDag-Erling Smørgrav for (i = 0; i < contig; i++) { 666bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u64(sect, rs->lo + i)) != 0) 667bc5531deSDag-Erling Smørgrav goto out; 668bc5531deSDag-Erling Smørgrav } 6696888a9beSDag-Erling Smørgrav break; 6706888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_RANGE: 671bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u64(sect, rs->lo)) != 0 || 672bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(sect, rs->hi)) != 0) 673bc5531deSDag-Erling Smørgrav goto out; 6746888a9beSDag-Erling Smørgrav break; 6756888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_BITMAP: 6766888a9beSDag-Erling Smørgrav if (rs->lo - bitmap_start > INT_MAX) { 677*19261079SEd Maste error_f("insane bitmap gap"); 6786888a9beSDag-Erling Smørgrav goto out; 6796888a9beSDag-Erling Smørgrav } 6806888a9beSDag-Erling Smørgrav for (i = 0; i < contig; i++) { 681bc5531deSDag-Erling Smørgrav if (bitmap_set_bit(bitmap, 682bc5531deSDag-Erling Smørgrav rs->lo + i - bitmap_start) != 0) { 683bc5531deSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 6846888a9beSDag-Erling Smørgrav goto out; 6856888a9beSDag-Erling Smørgrav } 686bc5531deSDag-Erling Smørgrav } 6876888a9beSDag-Erling Smørgrav break; 6886888a9beSDag-Erling Smørgrav } 6896888a9beSDag-Erling Smørgrav last = rs->hi; 6906888a9beSDag-Erling Smørgrav } 6916888a9beSDag-Erling Smørgrav /* Flush the remaining section, if any */ 6926888a9beSDag-Erling Smørgrav if (state != 0) { 693*19261079SEd Maste KRL_DBG(("serial final flush for state 0x%02x", state)); 6946888a9beSDag-Erling Smørgrav switch (state) { 6956888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_LIST: 6966888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_RANGE: 6976888a9beSDag-Erling Smørgrav break; 6986888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_BITMAP: 699bc5531deSDag-Erling Smørgrav if ((r = put_bitmap(sect, bitmap)) != 0) 700bc5531deSDag-Erling Smørgrav goto out; 701bc5531deSDag-Erling Smørgrav bitmap_free(bitmap); 7026888a9beSDag-Erling Smørgrav bitmap = NULL; 7036888a9beSDag-Erling Smørgrav break; 7046888a9beSDag-Erling Smørgrav } 705bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(buf, state)) != 0 || 706bc5531deSDag-Erling Smørgrav (r = sshbuf_put_stringb(buf, sect)) != 0) 707bc5531deSDag-Erling Smørgrav goto out; 7086888a9beSDag-Erling Smørgrav } 709*19261079SEd Maste KRL_DBG(("serial done ")); 7106888a9beSDag-Erling Smørgrav 7116888a9beSDag-Erling Smørgrav /* Now output a section for any revocations by key ID */ 712bc5531deSDag-Erling Smørgrav sshbuf_reset(sect); 7136888a9beSDag-Erling Smørgrav RB_FOREACH(rki, revoked_key_id_tree, &rc->revoked_key_ids) { 714*19261079SEd Maste KRL_DBG(("key ID %s", rki->key_id)); 715bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(sect, rki->key_id)) != 0) 716bc5531deSDag-Erling Smørgrav goto out; 7176888a9beSDag-Erling Smørgrav } 718bc5531deSDag-Erling Smørgrav if (sshbuf_len(sect) != 0) { 719bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(buf, KRL_SECTION_CERT_KEY_ID)) != 0 || 720bc5531deSDag-Erling Smørgrav (r = sshbuf_put_stringb(buf, sect)) != 0) 721bc5531deSDag-Erling Smørgrav goto out; 7226888a9beSDag-Erling Smørgrav } 7236888a9beSDag-Erling Smørgrav r = 0; 7246888a9beSDag-Erling Smørgrav out: 725bc5531deSDag-Erling Smørgrav bitmap_free(bitmap); 726bc5531deSDag-Erling Smørgrav sshbuf_free(sect); 7276888a9beSDag-Erling Smørgrav return r; 7286888a9beSDag-Erling Smørgrav } 7296888a9beSDag-Erling Smørgrav 7306888a9beSDag-Erling Smørgrav int 731bc5531deSDag-Erling Smørgrav ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf, 732*19261079SEd Maste struct sshkey **sign_keys, u_int nsign_keys) 7336888a9beSDag-Erling Smørgrav { 734bc5531deSDag-Erling Smørgrav int r = SSH_ERR_INTERNAL_ERROR; 7356888a9beSDag-Erling Smørgrav struct revoked_certs *rc; 7366888a9beSDag-Erling Smørgrav struct revoked_blob *rb; 737bc5531deSDag-Erling Smørgrav struct sshbuf *sect; 738bc5531deSDag-Erling Smørgrav u_char *sblob = NULL; 739bc5531deSDag-Erling Smørgrav size_t slen, i; 7406888a9beSDag-Erling Smørgrav 7416888a9beSDag-Erling Smørgrav if (krl->generated_date == 0) 7426888a9beSDag-Erling Smørgrav krl->generated_date = time(NULL); 7436888a9beSDag-Erling Smørgrav 744bc5531deSDag-Erling Smørgrav if ((sect = sshbuf_new()) == NULL) 745bc5531deSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 7466888a9beSDag-Erling Smørgrav 7476888a9beSDag-Erling Smørgrav /* Store the header */ 748bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put(buf, KRL_MAGIC, sizeof(KRL_MAGIC) - 1)) != 0 || 749bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(buf, KRL_FORMAT_VERSION)) != 0 || 750bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(buf, krl->krl_version)) != 0 || 751acc1a9efSDag-Erling Smørgrav (r = sshbuf_put_u64(buf, krl->generated_date)) != 0 || 752bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(buf, krl->flags)) != 0 || 753bc5531deSDag-Erling Smørgrav (r = sshbuf_put_string(buf, NULL, 0)) != 0 || 754bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(buf, krl->comment)) != 0) 755bc5531deSDag-Erling Smørgrav goto out; 7566888a9beSDag-Erling Smørgrav 7576888a9beSDag-Erling Smørgrav /* Store sections for revoked certificates */ 7586888a9beSDag-Erling Smørgrav TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { 759bc5531deSDag-Erling Smørgrav sshbuf_reset(sect); 760bc5531deSDag-Erling Smørgrav if ((r = revoked_certs_generate(rc, sect)) != 0) 7616888a9beSDag-Erling Smørgrav goto out; 762bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(buf, KRL_SECTION_CERTIFICATES)) != 0 || 763bc5531deSDag-Erling Smørgrav (r = sshbuf_put_stringb(buf, sect)) != 0) 764bc5531deSDag-Erling Smørgrav goto out; 7656888a9beSDag-Erling Smørgrav } 7666888a9beSDag-Erling Smørgrav 7676888a9beSDag-Erling Smørgrav /* Finally, output sections for revocations by public key/hash */ 768bc5531deSDag-Erling Smørgrav sshbuf_reset(sect); 7696888a9beSDag-Erling Smørgrav RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) { 770*19261079SEd Maste KRL_DBG(("key len %zu ", rb->len)); 771bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0) 772bc5531deSDag-Erling Smørgrav goto out; 7736888a9beSDag-Erling Smørgrav } 774bc5531deSDag-Erling Smørgrav if (sshbuf_len(sect) != 0) { 775bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(buf, KRL_SECTION_EXPLICIT_KEY)) != 0 || 776bc5531deSDag-Erling Smørgrav (r = sshbuf_put_stringb(buf, sect)) != 0) 777bc5531deSDag-Erling Smørgrav goto out; 7786888a9beSDag-Erling Smørgrav } 779bc5531deSDag-Erling Smørgrav sshbuf_reset(sect); 7806888a9beSDag-Erling Smørgrav RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) { 781*19261079SEd Maste KRL_DBG(("hash len %zu ", rb->len)); 782bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0) 783bc5531deSDag-Erling Smørgrav goto out; 7846888a9beSDag-Erling Smørgrav } 785bc5531deSDag-Erling Smørgrav if (sshbuf_len(sect) != 0) { 786bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(buf, 787bc5531deSDag-Erling Smørgrav KRL_SECTION_FINGERPRINT_SHA1)) != 0 || 788bc5531deSDag-Erling Smørgrav (r = sshbuf_put_stringb(buf, sect)) != 0) 789bc5531deSDag-Erling Smørgrav goto out; 7906888a9beSDag-Erling Smørgrav } 7912f513db7SEd Maste sshbuf_reset(sect); 7922f513db7SEd Maste RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha256s) { 793*19261079SEd Maste KRL_DBG(("hash len %zu ", rb->len)); 7942f513db7SEd Maste if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0) 7952f513db7SEd Maste goto out; 7962f513db7SEd Maste } 7972f513db7SEd Maste if (sshbuf_len(sect) != 0) { 7982f513db7SEd Maste if ((r = sshbuf_put_u8(buf, 7992f513db7SEd Maste KRL_SECTION_FINGERPRINT_SHA256)) != 0 || 8002f513db7SEd Maste (r = sshbuf_put_stringb(buf, sect)) != 0) 8012f513db7SEd Maste goto out; 8022f513db7SEd Maste } 8036888a9beSDag-Erling Smørgrav 8046888a9beSDag-Erling Smørgrav for (i = 0; i < nsign_keys; i++) { 805*19261079SEd Maste KRL_DBG(("sig key %s", sshkey_ssh_name(sign_keys[i]))); 806bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(buf, KRL_SECTION_SIGNATURE)) != 0 || 807bc5531deSDag-Erling Smørgrav (r = sshkey_puts(sign_keys[i], buf)) != 0) 8086888a9beSDag-Erling Smørgrav goto out; 809*19261079SEd Maste /* XXX support sk-* keys */ 810bc5531deSDag-Erling Smørgrav if ((r = sshkey_sign(sign_keys[i], &sblob, &slen, 811*19261079SEd Maste sshbuf_ptr(buf), sshbuf_len(buf), NULL, NULL, 812*19261079SEd Maste NULL, 0)) != 0) 8136888a9beSDag-Erling Smørgrav goto out; 814*19261079SEd Maste KRL_DBG(("signature sig len %zu", slen)); 815bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_string(buf, sblob, slen)) != 0) 816bc5531deSDag-Erling Smørgrav goto out; 8176888a9beSDag-Erling Smørgrav } 8186888a9beSDag-Erling Smørgrav 8196888a9beSDag-Erling Smørgrav r = 0; 8206888a9beSDag-Erling Smørgrav out: 8216888a9beSDag-Erling Smørgrav free(sblob); 822bc5531deSDag-Erling Smørgrav sshbuf_free(sect); 8236888a9beSDag-Erling Smørgrav return r; 8246888a9beSDag-Erling Smørgrav } 8256888a9beSDag-Erling Smørgrav 8266888a9beSDag-Erling Smørgrav static void 8276888a9beSDag-Erling Smørgrav format_timestamp(u_int64_t timestamp, char *ts, size_t nts) 8286888a9beSDag-Erling Smørgrav { 8296888a9beSDag-Erling Smørgrav time_t t; 8306888a9beSDag-Erling Smørgrav struct tm *tm; 8316888a9beSDag-Erling Smørgrav 8326888a9beSDag-Erling Smørgrav t = timestamp; 8336888a9beSDag-Erling Smørgrav tm = localtime(&t); 834bc5531deSDag-Erling Smørgrav if (tm == NULL) 835bc5531deSDag-Erling Smørgrav strlcpy(ts, "<INVALID>", nts); 836bc5531deSDag-Erling Smørgrav else { 8376888a9beSDag-Erling Smørgrav *ts = '\0'; 8386888a9beSDag-Erling Smørgrav strftime(ts, nts, "%Y%m%dT%H%M%S", tm); 8396888a9beSDag-Erling Smørgrav } 840bc5531deSDag-Erling Smørgrav } 8416888a9beSDag-Erling Smørgrav 8426888a9beSDag-Erling Smørgrav static int 843bc5531deSDag-Erling Smørgrav parse_revoked_certs(struct sshbuf *buf, struct ssh_krl *krl) 8446888a9beSDag-Erling Smørgrav { 845bc5531deSDag-Erling Smørgrav int r = SSH_ERR_INTERNAL_ERROR; 846a0ee8cc6SDag-Erling Smørgrav u_char type; 847a0ee8cc6SDag-Erling Smørgrav const u_char *blob; 848bc5531deSDag-Erling Smørgrav size_t blen, nbits; 849bc5531deSDag-Erling Smørgrav struct sshbuf *subsect = NULL; 8506888a9beSDag-Erling Smørgrav u_int64_t serial, serial_lo, serial_hi; 851bc5531deSDag-Erling Smørgrav struct bitmap *bitmap = NULL; 8526888a9beSDag-Erling Smørgrav char *key_id = NULL; 853bc5531deSDag-Erling Smørgrav struct sshkey *ca_key = NULL; 8546888a9beSDag-Erling Smørgrav 855bc5531deSDag-Erling Smørgrav if ((subsect = sshbuf_new()) == NULL) 856bc5531deSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 8576888a9beSDag-Erling Smørgrav 858bc5531deSDag-Erling Smørgrav /* Header: key, reserved */ 859bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_string_direct(buf, &blob, &blen)) != 0 || 860bc5531deSDag-Erling Smørgrav (r = sshbuf_skip_string(buf)) != 0) 8616888a9beSDag-Erling Smørgrav goto out; 862bc5531deSDag-Erling Smørgrav if (blen != 0 && (r = sshkey_from_blob(blob, blen, &ca_key)) != 0) 863bc5531deSDag-Erling Smørgrav goto out; 864bc5531deSDag-Erling Smørgrav 865bc5531deSDag-Erling Smørgrav while (sshbuf_len(buf) > 0) { 866bc5531deSDag-Erling Smørgrav sshbuf_free(subsect); 867bc5531deSDag-Erling Smørgrav subsect = NULL; 868bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u8(buf, &type)) != 0 || 869bc5531deSDag-Erling Smørgrav (r = sshbuf_froms(buf, &subsect)) != 0) 8706888a9beSDag-Erling Smørgrav goto out; 871*19261079SEd Maste KRL_DBG(("subsection type 0x%02x", type)); 872bc5531deSDag-Erling Smørgrav /* sshbuf_dump(subsect, stderr); */ 8736888a9beSDag-Erling Smørgrav 8746888a9beSDag-Erling Smørgrav switch (type) { 8756888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_LIST: 876bc5531deSDag-Erling Smørgrav while (sshbuf_len(subsect) > 0) { 877bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u64(subsect, &serial)) != 0) 8786888a9beSDag-Erling Smørgrav goto out; 879bc5531deSDag-Erling Smørgrav if ((r = ssh_krl_revoke_cert_by_serial(krl, 880bc5531deSDag-Erling Smørgrav ca_key, serial)) != 0) 8816888a9beSDag-Erling Smørgrav goto out; 8826888a9beSDag-Erling Smørgrav } 8836888a9beSDag-Erling Smørgrav break; 8846888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_RANGE: 885bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 || 886bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(subsect, &serial_hi)) != 0) 8876888a9beSDag-Erling Smørgrav goto out; 888bc5531deSDag-Erling Smørgrav if ((r = ssh_krl_revoke_cert_by_serial_range(krl, 889bc5531deSDag-Erling Smørgrav ca_key, serial_lo, serial_hi)) != 0) 8906888a9beSDag-Erling Smørgrav goto out; 8916888a9beSDag-Erling Smørgrav break; 8926888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_BITMAP: 893bc5531deSDag-Erling Smørgrav if ((bitmap = bitmap_new()) == NULL) { 894bc5531deSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 8956888a9beSDag-Erling Smørgrav goto out; 8966888a9beSDag-Erling Smørgrav } 897bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 || 898bc5531deSDag-Erling Smørgrav (r = sshbuf_get_bignum2_bytes_direct(subsect, 899bc5531deSDag-Erling Smørgrav &blob, &blen)) != 0) 900bc5531deSDag-Erling Smørgrav goto out; 901bc5531deSDag-Erling Smørgrav if (bitmap_from_string(bitmap, blob, blen) != 0) { 902bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 9036888a9beSDag-Erling Smørgrav goto out; 9046888a9beSDag-Erling Smørgrav } 905bc5531deSDag-Erling Smørgrav nbits = bitmap_nbits(bitmap); 906bc5531deSDag-Erling Smørgrav for (serial = 0; serial < (u_int64_t)nbits; serial++) { 9076888a9beSDag-Erling Smørgrav if (serial > 0 && serial_lo + serial == 0) { 908*19261079SEd Maste error_f("bitmap wraps u64"); 909bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 9106888a9beSDag-Erling Smørgrav goto out; 9116888a9beSDag-Erling Smørgrav } 912bc5531deSDag-Erling Smørgrav if (!bitmap_test_bit(bitmap, serial)) 9136888a9beSDag-Erling Smørgrav continue; 914bc5531deSDag-Erling Smørgrav if ((r = ssh_krl_revoke_cert_by_serial(krl, 915bc5531deSDag-Erling Smørgrav ca_key, serial_lo + serial)) != 0) 9166888a9beSDag-Erling Smørgrav goto out; 9176888a9beSDag-Erling Smørgrav } 918bc5531deSDag-Erling Smørgrav bitmap_free(bitmap); 9196888a9beSDag-Erling Smørgrav bitmap = NULL; 9206888a9beSDag-Erling Smørgrav break; 9216888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_KEY_ID: 922bc5531deSDag-Erling Smørgrav while (sshbuf_len(subsect) > 0) { 923bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(subsect, 924bc5531deSDag-Erling Smørgrav &key_id, NULL)) != 0) 9256888a9beSDag-Erling Smørgrav goto out; 926bc5531deSDag-Erling Smørgrav if ((r = ssh_krl_revoke_cert_by_key_id(krl, 927bc5531deSDag-Erling Smørgrav ca_key, key_id)) != 0) 9286888a9beSDag-Erling Smørgrav goto out; 9296888a9beSDag-Erling Smørgrav free(key_id); 9306888a9beSDag-Erling Smørgrav key_id = NULL; 9316888a9beSDag-Erling Smørgrav } 9326888a9beSDag-Erling Smørgrav break; 9336888a9beSDag-Erling Smørgrav default: 9346888a9beSDag-Erling Smørgrav error("Unsupported KRL certificate section %u", type); 935bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 9366888a9beSDag-Erling Smørgrav goto out; 9376888a9beSDag-Erling Smørgrav } 938bc5531deSDag-Erling Smørgrav if (sshbuf_len(subsect) > 0) { 9396888a9beSDag-Erling Smørgrav error("KRL certificate section contains unparsed data"); 940bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 9416888a9beSDag-Erling Smørgrav goto out; 9426888a9beSDag-Erling Smørgrav } 9436888a9beSDag-Erling Smørgrav } 9446888a9beSDag-Erling Smørgrav 945bc5531deSDag-Erling Smørgrav r = 0; 9466888a9beSDag-Erling Smørgrav out: 9476888a9beSDag-Erling Smørgrav if (bitmap != NULL) 948bc5531deSDag-Erling Smørgrav bitmap_free(bitmap); 9496888a9beSDag-Erling Smørgrav free(key_id); 950bc5531deSDag-Erling Smørgrav sshkey_free(ca_key); 951bc5531deSDag-Erling Smørgrav sshbuf_free(subsect); 952bc5531deSDag-Erling Smørgrav return r; 9536888a9beSDag-Erling Smørgrav } 9546888a9beSDag-Erling Smørgrav 9552f513db7SEd Maste static int 9562f513db7SEd Maste blob_section(struct sshbuf *sect, struct revoked_blob_tree *target_tree, 9572f513db7SEd Maste size_t expected_len) 9582f513db7SEd Maste { 9592f513db7SEd Maste u_char *rdata = NULL; 9602f513db7SEd Maste size_t rlen = 0; 9612f513db7SEd Maste int r; 9622f513db7SEd Maste 9632f513db7SEd Maste while (sshbuf_len(sect) > 0) { 9642f513db7SEd Maste if ((r = sshbuf_get_string(sect, &rdata, &rlen)) != 0) 9652f513db7SEd Maste return r; 9662f513db7SEd Maste if (expected_len != 0 && rlen != expected_len) { 967*19261079SEd Maste error_f("bad length"); 9682f513db7SEd Maste free(rdata); 9692f513db7SEd Maste return SSH_ERR_INVALID_FORMAT; 9702f513db7SEd Maste } 9712f513db7SEd Maste if ((r = revoke_blob(target_tree, rdata, rlen)) != 0) { 9722f513db7SEd Maste free(rdata); 9732f513db7SEd Maste return r; 9742f513db7SEd Maste } 9752f513db7SEd Maste } 9762f513db7SEd Maste return 0; 9772f513db7SEd Maste } 9786888a9beSDag-Erling Smørgrav 9796888a9beSDag-Erling Smørgrav /* Attempt to parse a KRL, checking its signature (if any) with sign_ca_keys. */ 9806888a9beSDag-Erling Smørgrav int 981bc5531deSDag-Erling Smørgrav ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp, 982bc5531deSDag-Erling Smørgrav const struct sshkey **sign_ca_keys, size_t nsign_ca_keys) 9836888a9beSDag-Erling Smørgrav { 984bc5531deSDag-Erling Smørgrav struct sshbuf *copy = NULL, *sect = NULL; 985bc5531deSDag-Erling Smørgrav struct ssh_krl *krl = NULL; 9866888a9beSDag-Erling Smørgrav char timestamp[64]; 987bc5531deSDag-Erling Smørgrav int r = SSH_ERR_INTERNAL_ERROR, sig_seen; 988bc5531deSDag-Erling Smørgrav struct sshkey *key = NULL, **ca_used = NULL, **tmp_ca_used; 9892f513db7SEd Maste u_char type; 990a0ee8cc6SDag-Erling Smørgrav const u_char *blob; 9912f513db7SEd Maste size_t i, j, sig_off, sects_off, blen, nca_used; 992bc5531deSDag-Erling Smørgrav u_int format_version; 9936888a9beSDag-Erling Smørgrav 994e4a9863fSDag-Erling Smørgrav nca_used = 0; 9956888a9beSDag-Erling Smørgrav *krlp = NULL; 996bc5531deSDag-Erling Smørgrav if (sshbuf_len(buf) < sizeof(KRL_MAGIC) - 1 || 997bc5531deSDag-Erling Smørgrav memcmp(sshbuf_ptr(buf), KRL_MAGIC, sizeof(KRL_MAGIC) - 1) != 0) { 998*19261079SEd Maste debug3_f("not a KRL"); 999bc5531deSDag-Erling Smørgrav return SSH_ERR_KRL_BAD_MAGIC; 10006888a9beSDag-Erling Smørgrav } 10016888a9beSDag-Erling Smørgrav 10026888a9beSDag-Erling Smørgrav /* Take a copy of the KRL buffer so we can verify its signature later */ 1003bc5531deSDag-Erling Smørgrav if ((copy = sshbuf_fromb(buf)) == NULL) { 1004bc5531deSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 1005bc5531deSDag-Erling Smørgrav goto out; 1006bc5531deSDag-Erling Smørgrav } 1007bc5531deSDag-Erling Smørgrav if ((r = sshbuf_consume(copy, sizeof(KRL_MAGIC) - 1)) != 0) 1008bc5531deSDag-Erling Smørgrav goto out; 10096888a9beSDag-Erling Smørgrav 10106888a9beSDag-Erling Smørgrav if ((krl = ssh_krl_init()) == NULL) { 1011*19261079SEd Maste error_f("alloc failed"); 10126888a9beSDag-Erling Smørgrav goto out; 10136888a9beSDag-Erling Smørgrav } 10146888a9beSDag-Erling Smørgrav 1015bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(copy, &format_version)) != 0) 10166888a9beSDag-Erling Smørgrav goto out; 10176888a9beSDag-Erling Smørgrav if (format_version != KRL_FORMAT_VERSION) { 1018bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 10196888a9beSDag-Erling Smørgrav goto out; 10206888a9beSDag-Erling Smørgrav } 1021bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u64(copy, &krl->krl_version)) != 0 || 1022bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(copy, &krl->generated_date)) != 0 || 1023bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(copy, &krl->flags)) != 0 || 1024bc5531deSDag-Erling Smørgrav (r = sshbuf_skip_string(copy)) != 0 || 1025bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(copy, &krl->comment, NULL)) != 0) 10266888a9beSDag-Erling Smørgrav goto out; 10276888a9beSDag-Erling Smørgrav 10286888a9beSDag-Erling Smørgrav format_timestamp(krl->generated_date, timestamp, sizeof(timestamp)); 102959928918SDag-Erling Smørgrav debug("KRL version %llu generated at %s%s%s", 1030e4a9863fSDag-Erling Smørgrav (long long unsigned)krl->krl_version, timestamp, 103159928918SDag-Erling Smørgrav *krl->comment ? ": " : "", krl->comment); 10326888a9beSDag-Erling Smørgrav 10336888a9beSDag-Erling Smørgrav /* 10346888a9beSDag-Erling Smørgrav * 1st pass: verify signatures, if any. This is done to avoid 10356888a9beSDag-Erling Smørgrav * detailed parsing of data whose provenance is unverified. 10366888a9beSDag-Erling Smørgrav */ 10376888a9beSDag-Erling Smørgrav sig_seen = 0; 1038bc5531deSDag-Erling Smørgrav if (sshbuf_len(buf) < sshbuf_len(copy)) { 1039bc5531deSDag-Erling Smørgrav /* Shouldn't happen */ 1040bc5531deSDag-Erling Smørgrav r = SSH_ERR_INTERNAL_ERROR; 10416888a9beSDag-Erling Smørgrav goto out; 10426888a9beSDag-Erling Smørgrav } 1043bc5531deSDag-Erling Smørgrav sects_off = sshbuf_len(buf) - sshbuf_len(copy); 1044bc5531deSDag-Erling Smørgrav while (sshbuf_len(copy) > 0) { 1045bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u8(copy, &type)) != 0 || 1046bc5531deSDag-Erling Smørgrav (r = sshbuf_get_string_direct(copy, &blob, &blen)) != 0) 1047bc5531deSDag-Erling Smørgrav goto out; 1048*19261079SEd Maste KRL_DBG(("first pass, section 0x%02x", type)); 10496888a9beSDag-Erling Smørgrav if (type != KRL_SECTION_SIGNATURE) { 10506888a9beSDag-Erling Smørgrav if (sig_seen) { 10516888a9beSDag-Erling Smørgrav error("KRL contains non-signature section " 10526888a9beSDag-Erling Smørgrav "after signature"); 1053bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 10546888a9beSDag-Erling Smørgrav goto out; 10556888a9beSDag-Erling Smørgrav } 10566888a9beSDag-Erling Smørgrav /* Not interested for now. */ 10576888a9beSDag-Erling Smørgrav continue; 10586888a9beSDag-Erling Smørgrav } 10596888a9beSDag-Erling Smørgrav sig_seen = 1; 10606888a9beSDag-Erling Smørgrav /* First string component is the signing key */ 1061bc5531deSDag-Erling Smørgrav if ((r = sshkey_from_blob(blob, blen, &key)) != 0) { 1062bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 10636888a9beSDag-Erling Smørgrav goto out; 10646888a9beSDag-Erling Smørgrav } 1065bc5531deSDag-Erling Smørgrav if (sshbuf_len(buf) < sshbuf_len(copy)) { 1066bc5531deSDag-Erling Smørgrav /* Shouldn't happen */ 1067bc5531deSDag-Erling Smørgrav r = SSH_ERR_INTERNAL_ERROR; 1068bc5531deSDag-Erling Smørgrav goto out; 1069bc5531deSDag-Erling Smørgrav } 1070bc5531deSDag-Erling Smørgrav sig_off = sshbuf_len(buf) - sshbuf_len(copy); 10716888a9beSDag-Erling Smørgrav /* Second string component is the signature itself */ 1072bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_string_direct(copy, &blob, &blen)) != 0) { 1073bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 10746888a9beSDag-Erling Smørgrav goto out; 10756888a9beSDag-Erling Smørgrav } 10766888a9beSDag-Erling Smørgrav /* Check signature over entire KRL up to this point */ 1077bc5531deSDag-Erling Smørgrav if ((r = sshkey_verify(key, blob, blen, 1078*19261079SEd Maste sshbuf_ptr(buf), sig_off, NULL, 0, NULL)) != 0) 10796888a9beSDag-Erling Smørgrav goto out; 10806888a9beSDag-Erling Smørgrav /* Check if this key has already signed this KRL */ 10816888a9beSDag-Erling Smørgrav for (i = 0; i < nca_used; i++) { 1082bc5531deSDag-Erling Smørgrav if (sshkey_equal(ca_used[i], key)) { 10836888a9beSDag-Erling Smørgrav error("KRL signed more than once with " 10846888a9beSDag-Erling Smørgrav "the same key"); 1085bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 10866888a9beSDag-Erling Smørgrav goto out; 10876888a9beSDag-Erling Smørgrav } 10886888a9beSDag-Erling Smørgrav } 10896888a9beSDag-Erling Smørgrav /* Record keys used to sign the KRL */ 10904f52dfbbSDag-Erling Smørgrav tmp_ca_used = recallocarray(ca_used, nca_used, nca_used + 1, 1091bc5531deSDag-Erling Smørgrav sizeof(*ca_used)); 1092bc5531deSDag-Erling Smørgrav if (tmp_ca_used == NULL) { 1093bc5531deSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 1094bc5531deSDag-Erling Smørgrav goto out; 1095bc5531deSDag-Erling Smørgrav } 1096bc5531deSDag-Erling Smørgrav ca_used = tmp_ca_used; 10976888a9beSDag-Erling Smørgrav ca_used[nca_used++] = key; 10986888a9beSDag-Erling Smørgrav key = NULL; 10996888a9beSDag-Erling Smørgrav } 11006888a9beSDag-Erling Smørgrav 1101bc5531deSDag-Erling Smørgrav if (sshbuf_len(copy) != 0) { 1102bc5531deSDag-Erling Smørgrav /* Shouldn't happen */ 1103bc5531deSDag-Erling Smørgrav r = SSH_ERR_INTERNAL_ERROR; 1104bc5531deSDag-Erling Smørgrav goto out; 1105bc5531deSDag-Erling Smørgrav } 1106bc5531deSDag-Erling Smørgrav 11076888a9beSDag-Erling Smørgrav /* 11086888a9beSDag-Erling Smørgrav * 2nd pass: parse and load the KRL, skipping the header to the point 11096888a9beSDag-Erling Smørgrav * where the section start. 11106888a9beSDag-Erling Smørgrav */ 1111bc5531deSDag-Erling Smørgrav sshbuf_free(copy); 1112bc5531deSDag-Erling Smørgrav if ((copy = sshbuf_fromb(buf)) == NULL) { 1113bc5531deSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 11146888a9beSDag-Erling Smørgrav goto out; 11156888a9beSDag-Erling Smørgrav } 1116bc5531deSDag-Erling Smørgrav if ((r = sshbuf_consume(copy, sects_off)) != 0) 1117bc5531deSDag-Erling Smørgrav goto out; 1118bc5531deSDag-Erling Smørgrav while (sshbuf_len(copy) > 0) { 1119bc5531deSDag-Erling Smørgrav sshbuf_free(sect); 1120bc5531deSDag-Erling Smørgrav sect = NULL; 1121bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u8(copy, &type)) != 0 || 1122bc5531deSDag-Erling Smørgrav (r = sshbuf_froms(copy, §)) != 0) 1123bc5531deSDag-Erling Smørgrav goto out; 1124*19261079SEd Maste KRL_DBG(("second pass, section 0x%02x", type)); 11256888a9beSDag-Erling Smørgrav 11266888a9beSDag-Erling Smørgrav switch (type) { 11276888a9beSDag-Erling Smørgrav case KRL_SECTION_CERTIFICATES: 1128bc5531deSDag-Erling Smørgrav if ((r = parse_revoked_certs(sect, krl)) != 0) 11296888a9beSDag-Erling Smørgrav goto out; 11306888a9beSDag-Erling Smørgrav break; 11316888a9beSDag-Erling Smørgrav case KRL_SECTION_EXPLICIT_KEY: 11322f513db7SEd Maste if ((r = blob_section(sect, 11332f513db7SEd Maste &krl->revoked_keys, 0)) != 0) 11342f513db7SEd Maste goto out; 11352f513db7SEd Maste break; 11366888a9beSDag-Erling Smørgrav case KRL_SECTION_FINGERPRINT_SHA1: 11372f513db7SEd Maste if ((r = blob_section(sect, 11382f513db7SEd Maste &krl->revoked_sha1s, 20)) != 0) 11396888a9beSDag-Erling Smørgrav goto out; 11402f513db7SEd Maste break; 11412f513db7SEd Maste case KRL_SECTION_FINGERPRINT_SHA256: 11422f513db7SEd Maste if ((r = blob_section(sect, 11432f513db7SEd Maste &krl->revoked_sha256s, 32)) != 0) 11446888a9beSDag-Erling Smørgrav goto out; 11456888a9beSDag-Erling Smørgrav break; 11466888a9beSDag-Erling Smørgrav case KRL_SECTION_SIGNATURE: 11476888a9beSDag-Erling Smørgrav /* Handled above, but still need to stay in synch */ 1148d93a896eSDag-Erling Smørgrav sshbuf_free(sect); 1149bc5531deSDag-Erling Smørgrav sect = NULL; 1150bc5531deSDag-Erling Smørgrav if ((r = sshbuf_skip_string(copy)) != 0) 11516888a9beSDag-Erling Smørgrav goto out; 11526888a9beSDag-Erling Smørgrav break; 11536888a9beSDag-Erling Smørgrav default: 11546888a9beSDag-Erling Smørgrav error("Unsupported KRL section %u", type); 1155bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 11566888a9beSDag-Erling Smørgrav goto out; 11576888a9beSDag-Erling Smørgrav } 1158acc1a9efSDag-Erling Smørgrav if (sect != NULL && sshbuf_len(sect) > 0) { 11596888a9beSDag-Erling Smørgrav error("KRL section contains unparsed data"); 1160bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 11616888a9beSDag-Erling Smørgrav goto out; 11626888a9beSDag-Erling Smørgrav } 11636888a9beSDag-Erling Smørgrav } 11646888a9beSDag-Erling Smørgrav 11656888a9beSDag-Erling Smørgrav /* Check that the key(s) used to sign the KRL weren't revoked */ 11666888a9beSDag-Erling Smørgrav sig_seen = 0; 11676888a9beSDag-Erling Smørgrav for (i = 0; i < nca_used; i++) { 11686888a9beSDag-Erling Smørgrav if (ssh_krl_check_key(krl, ca_used[i]) == 0) 11696888a9beSDag-Erling Smørgrav sig_seen = 1; 11706888a9beSDag-Erling Smørgrav else { 1171bc5531deSDag-Erling Smørgrav sshkey_free(ca_used[i]); 11726888a9beSDag-Erling Smørgrav ca_used[i] = NULL; 11736888a9beSDag-Erling Smørgrav } 11746888a9beSDag-Erling Smørgrav } 11756888a9beSDag-Erling Smørgrav if (nca_used && !sig_seen) { 11766888a9beSDag-Erling Smørgrav error("All keys used to sign KRL were revoked"); 1177bc5531deSDag-Erling Smørgrav r = SSH_ERR_KEY_REVOKED; 11786888a9beSDag-Erling Smørgrav goto out; 11796888a9beSDag-Erling Smørgrav } 11806888a9beSDag-Erling Smørgrav 11816888a9beSDag-Erling Smørgrav /* If we have CA keys, then verify that one was used to sign the KRL */ 11826888a9beSDag-Erling Smørgrav if (sig_seen && nsign_ca_keys != 0) { 11836888a9beSDag-Erling Smørgrav sig_seen = 0; 11846888a9beSDag-Erling Smørgrav for (i = 0; !sig_seen && i < nsign_ca_keys; i++) { 11856888a9beSDag-Erling Smørgrav for (j = 0; j < nca_used; j++) { 11866888a9beSDag-Erling Smørgrav if (ca_used[j] == NULL) 11876888a9beSDag-Erling Smørgrav continue; 1188bc5531deSDag-Erling Smørgrav if (sshkey_equal(ca_used[j], sign_ca_keys[i])) { 11896888a9beSDag-Erling Smørgrav sig_seen = 1; 11906888a9beSDag-Erling Smørgrav break; 11916888a9beSDag-Erling Smørgrav } 11926888a9beSDag-Erling Smørgrav } 11936888a9beSDag-Erling Smørgrav } 11946888a9beSDag-Erling Smørgrav if (!sig_seen) { 1195bc5531deSDag-Erling Smørgrav r = SSH_ERR_SIGNATURE_INVALID; 11966888a9beSDag-Erling Smørgrav error("KRL not signed with any trusted key"); 11976888a9beSDag-Erling Smørgrav goto out; 11986888a9beSDag-Erling Smørgrav } 11996888a9beSDag-Erling Smørgrav } 12006888a9beSDag-Erling Smørgrav 12016888a9beSDag-Erling Smørgrav *krlp = krl; 1202bc5531deSDag-Erling Smørgrav r = 0; 12036888a9beSDag-Erling Smørgrav out: 1204bc5531deSDag-Erling Smørgrav if (r != 0) 12056888a9beSDag-Erling Smørgrav ssh_krl_free(krl); 1206bc5531deSDag-Erling Smørgrav for (i = 0; i < nca_used; i++) 1207bc5531deSDag-Erling Smørgrav sshkey_free(ca_used[i]); 12086888a9beSDag-Erling Smørgrav free(ca_used); 1209bc5531deSDag-Erling Smørgrav sshkey_free(key); 1210bc5531deSDag-Erling Smørgrav sshbuf_free(copy); 1211bc5531deSDag-Erling Smørgrav sshbuf_free(sect); 1212bc5531deSDag-Erling Smørgrav return r; 12136888a9beSDag-Erling Smørgrav } 12146888a9beSDag-Erling Smørgrav 1215bc5531deSDag-Erling Smørgrav /* Checks certificate serial number and key ID revocation */ 12166888a9beSDag-Erling Smørgrav static int 1217bc5531deSDag-Erling Smørgrav is_cert_revoked(const struct sshkey *key, struct revoked_certs *rc) 12186888a9beSDag-Erling Smørgrav { 12196888a9beSDag-Erling Smørgrav struct revoked_serial rs, *ers; 12206888a9beSDag-Erling Smørgrav struct revoked_key_id rki, *erki; 12216888a9beSDag-Erling Smørgrav 12226888a9beSDag-Erling Smørgrav /* Check revocation by cert key ID */ 1223b83788ffSDag-Erling Smørgrav memset(&rki, 0, sizeof(rki)); 12246888a9beSDag-Erling Smørgrav rki.key_id = key->cert->key_id; 12256888a9beSDag-Erling Smørgrav erki = RB_FIND(revoked_key_id_tree, &rc->revoked_key_ids, &rki); 12266888a9beSDag-Erling Smørgrav if (erki != NULL) { 1227*19261079SEd Maste KRL_DBG(("revoked by key ID")); 1228bc5531deSDag-Erling Smørgrav return SSH_ERR_KEY_REVOKED; 12296888a9beSDag-Erling Smørgrav } 12306888a9beSDag-Erling Smørgrav 12316888a9beSDag-Erling Smørgrav /* 1232eccfee6eSDag-Erling Smørgrav * Zero serials numbers are ignored (it's the default when the 1233eccfee6eSDag-Erling Smørgrav * CA doesn't specify one). 12346888a9beSDag-Erling Smørgrav */ 1235eccfee6eSDag-Erling Smørgrav if (key->cert->serial == 0) 12366888a9beSDag-Erling Smørgrav return 0; 12376888a9beSDag-Erling Smørgrav 1238b83788ffSDag-Erling Smørgrav memset(&rs, 0, sizeof(rs)); 12396888a9beSDag-Erling Smørgrav rs.lo = rs.hi = key->cert->serial; 12406888a9beSDag-Erling Smørgrav ers = RB_FIND(revoked_serial_tree, &rc->revoked_serials, &rs); 12416888a9beSDag-Erling Smørgrav if (ers != NULL) { 1242*19261079SEd Maste KRL_DBG(("revoked serial %llu matched %llu:%llu", 12436888a9beSDag-Erling Smørgrav key->cert->serial, ers->lo, ers->hi)); 1244bc5531deSDag-Erling Smørgrav return SSH_ERR_KEY_REVOKED; 12456888a9beSDag-Erling Smørgrav } 1246bc5531deSDag-Erling Smørgrav return 0; 1247bc5531deSDag-Erling Smørgrav } 12486888a9beSDag-Erling Smørgrav 1249bc5531deSDag-Erling Smørgrav /* Checks whether a given key/cert is revoked. Does not check its CA */ 1250bc5531deSDag-Erling Smørgrav static int 1251bc5531deSDag-Erling Smørgrav is_key_revoked(struct ssh_krl *krl, const struct sshkey *key) 1252bc5531deSDag-Erling Smørgrav { 1253bc5531deSDag-Erling Smørgrav struct revoked_blob rb, *erb; 1254bc5531deSDag-Erling Smørgrav struct revoked_certs *rc; 1255bc5531deSDag-Erling Smørgrav int r; 1256bc5531deSDag-Erling Smørgrav 1257bc5531deSDag-Erling Smørgrav /* Check explicitly revoked hashes first */ 1258bc5531deSDag-Erling Smørgrav memset(&rb, 0, sizeof(rb)); 1259bc5531deSDag-Erling Smørgrav if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA1, 1260bc5531deSDag-Erling Smørgrav &rb.blob, &rb.len)) != 0) 1261bc5531deSDag-Erling Smørgrav return r; 1262bc5531deSDag-Erling Smørgrav erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha1s, &rb); 1263bc5531deSDag-Erling Smørgrav free(rb.blob); 1264bc5531deSDag-Erling Smørgrav if (erb != NULL) { 1265*19261079SEd Maste KRL_DBG(("revoked by key SHA1")); 1266bc5531deSDag-Erling Smørgrav return SSH_ERR_KEY_REVOKED; 1267bc5531deSDag-Erling Smørgrav } 12682f513db7SEd Maste memset(&rb, 0, sizeof(rb)); 12692f513db7SEd Maste if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA256, 12702f513db7SEd Maste &rb.blob, &rb.len)) != 0) 12712f513db7SEd Maste return r; 12722f513db7SEd Maste erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha256s, &rb); 12732f513db7SEd Maste free(rb.blob); 12742f513db7SEd Maste if (erb != NULL) { 1275*19261079SEd Maste KRL_DBG(("revoked by key SHA256")); 12762f513db7SEd Maste return SSH_ERR_KEY_REVOKED; 12772f513db7SEd Maste } 1278bc5531deSDag-Erling Smørgrav 1279bc5531deSDag-Erling Smørgrav /* Next, explicit keys */ 1280bc5531deSDag-Erling Smørgrav memset(&rb, 0, sizeof(rb)); 1281bc5531deSDag-Erling Smørgrav if ((r = plain_key_blob(key, &rb.blob, &rb.len)) != 0) 1282bc5531deSDag-Erling Smørgrav return r; 1283bc5531deSDag-Erling Smørgrav erb = RB_FIND(revoked_blob_tree, &krl->revoked_keys, &rb); 1284bc5531deSDag-Erling Smørgrav free(rb.blob); 1285bc5531deSDag-Erling Smørgrav if (erb != NULL) { 1286*19261079SEd Maste KRL_DBG(("revoked by explicit key")); 1287bc5531deSDag-Erling Smørgrav return SSH_ERR_KEY_REVOKED; 1288bc5531deSDag-Erling Smørgrav } 1289bc5531deSDag-Erling Smørgrav 1290bc5531deSDag-Erling Smørgrav if (!sshkey_is_cert(key)) 1291bc5531deSDag-Erling Smørgrav return 0; 1292bc5531deSDag-Erling Smørgrav 1293bc5531deSDag-Erling Smørgrav /* Check cert revocation for the specified CA */ 1294bc5531deSDag-Erling Smørgrav if ((r = revoked_certs_for_ca_key(krl, key->cert->signature_key, 1295bc5531deSDag-Erling Smørgrav &rc, 0)) != 0) 1296bc5531deSDag-Erling Smørgrav return r; 1297bc5531deSDag-Erling Smørgrav if (rc != NULL) { 1298bc5531deSDag-Erling Smørgrav if ((r = is_cert_revoked(key, rc)) != 0) 1299bc5531deSDag-Erling Smørgrav return r; 1300bc5531deSDag-Erling Smørgrav } 1301bc5531deSDag-Erling Smørgrav /* Check cert revocation for the wildcard CA */ 1302bc5531deSDag-Erling Smørgrav if ((r = revoked_certs_for_ca_key(krl, NULL, &rc, 0)) != 0) 1303bc5531deSDag-Erling Smørgrav return r; 1304bc5531deSDag-Erling Smørgrav if (rc != NULL) { 1305bc5531deSDag-Erling Smørgrav if ((r = is_cert_revoked(key, rc)) != 0) 1306bc5531deSDag-Erling Smørgrav return r; 1307bc5531deSDag-Erling Smørgrav } 1308bc5531deSDag-Erling Smørgrav 1309*19261079SEd Maste KRL_DBG(("%llu no match", key->cert->serial)); 13106888a9beSDag-Erling Smørgrav return 0; 13116888a9beSDag-Erling Smørgrav } 13126888a9beSDag-Erling Smørgrav 13136888a9beSDag-Erling Smørgrav int 1314bc5531deSDag-Erling Smørgrav ssh_krl_check_key(struct ssh_krl *krl, const struct sshkey *key) 13156888a9beSDag-Erling Smørgrav { 13166888a9beSDag-Erling Smørgrav int r; 13176888a9beSDag-Erling Smørgrav 1318*19261079SEd Maste KRL_DBG(("checking key")); 13196888a9beSDag-Erling Smørgrav if ((r = is_key_revoked(krl, key)) != 0) 13206888a9beSDag-Erling Smørgrav return r; 1321bc5531deSDag-Erling Smørgrav if (sshkey_is_cert(key)) { 1322*19261079SEd Maste debug2_f("checking CA key"); 13236888a9beSDag-Erling Smørgrav if ((r = is_key_revoked(krl, key->cert->signature_key)) != 0) 13246888a9beSDag-Erling Smørgrav return r; 13256888a9beSDag-Erling Smørgrav } 1326*19261079SEd Maste KRL_DBG(("key okay")); 13276888a9beSDag-Erling Smørgrav return 0; 13286888a9beSDag-Erling Smørgrav } 13296888a9beSDag-Erling Smørgrav 13306888a9beSDag-Erling Smørgrav int 1331bc5531deSDag-Erling Smørgrav ssh_krl_file_contains_key(const char *path, const struct sshkey *key) 13326888a9beSDag-Erling Smørgrav { 1333bc5531deSDag-Erling Smørgrav struct sshbuf *krlbuf = NULL; 1334bc5531deSDag-Erling Smørgrav struct ssh_krl *krl = NULL; 1335*19261079SEd Maste int oerrno = 0, r; 13366888a9beSDag-Erling Smørgrav 13376888a9beSDag-Erling Smørgrav if (path == NULL) 13386888a9beSDag-Erling Smørgrav return 0; 1339*19261079SEd Maste if ((r = sshbuf_load_file(path, &krlbuf)) != 0) { 1340bc5531deSDag-Erling Smørgrav oerrno = errno; 1341bc5531deSDag-Erling Smørgrav goto out; 13426888a9beSDag-Erling Smørgrav } 1343bc5531deSDag-Erling Smørgrav if ((r = ssh_krl_from_blob(krlbuf, &krl, NULL, 0)) != 0) 1344bc5531deSDag-Erling Smørgrav goto out; 1345*19261079SEd Maste debug2_f("checking KRL %s", path); 1346bc5531deSDag-Erling Smørgrav r = ssh_krl_check_key(krl, key); 1347bc5531deSDag-Erling Smørgrav out: 1348bc5531deSDag-Erling Smørgrav sshbuf_free(krlbuf); 13496888a9beSDag-Erling Smørgrav ssh_krl_free(krl); 1350bc5531deSDag-Erling Smørgrav if (r != 0) 1351bc5531deSDag-Erling Smørgrav errno = oerrno; 1352bc5531deSDag-Erling Smørgrav return r; 13536888a9beSDag-Erling Smørgrav } 1354*19261079SEd Maste 1355*19261079SEd Maste int 1356*19261079SEd Maste krl_dump(struct ssh_krl *krl, FILE *f) 1357*19261079SEd Maste { 1358*19261079SEd Maste struct sshkey *key = NULL; 1359*19261079SEd Maste struct revoked_blob *rb; 1360*19261079SEd Maste struct revoked_certs *rc; 1361*19261079SEd Maste struct revoked_serial *rs; 1362*19261079SEd Maste struct revoked_key_id *rki; 1363*19261079SEd Maste int r, ret = 0; 1364*19261079SEd Maste char *fp, timestamp[64]; 1365*19261079SEd Maste 1366*19261079SEd Maste /* Try to print in a KRL spec-compatible format */ 1367*19261079SEd Maste format_timestamp(krl->generated_date, timestamp, sizeof(timestamp)); 1368*19261079SEd Maste fprintf(f, "# KRL version %llu\n", 1369*19261079SEd Maste (unsigned long long)krl->krl_version); 1370*19261079SEd Maste fprintf(f, "# Generated at %s\n", timestamp); 1371*19261079SEd Maste if (krl->comment != NULL && *krl->comment != '\0') { 1372*19261079SEd Maste r = INT_MAX; 1373*19261079SEd Maste asmprintf(&fp, INT_MAX, &r, "%s", krl->comment); 1374*19261079SEd Maste fprintf(f, "# Comment: %s\n", fp); 1375*19261079SEd Maste free(fp); 1376*19261079SEd Maste } 1377*19261079SEd Maste fputc('\n', f); 1378*19261079SEd Maste 1379*19261079SEd Maste RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) { 1380*19261079SEd Maste if ((r = sshkey_from_blob(rb->blob, rb->len, &key)) != 0) { 1381*19261079SEd Maste ret = SSH_ERR_INVALID_FORMAT; 1382*19261079SEd Maste error_r(r, "parse KRL key"); 1383*19261079SEd Maste continue; 1384*19261079SEd Maste } 1385*19261079SEd Maste if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, 1386*19261079SEd Maste SSH_FP_DEFAULT)) == NULL) { 1387*19261079SEd Maste ret = SSH_ERR_INVALID_FORMAT; 1388*19261079SEd Maste error("sshkey_fingerprint failed"); 1389*19261079SEd Maste continue; 1390*19261079SEd Maste } 1391*19261079SEd Maste fprintf(f, "hash: SHA256:%s # %s\n", fp, sshkey_ssh_name(key)); 1392*19261079SEd Maste free(fp); 1393*19261079SEd Maste free(key); 1394*19261079SEd Maste } 1395*19261079SEd Maste RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha256s) { 1396*19261079SEd Maste fp = tohex(rb->blob, rb->len); 1397*19261079SEd Maste fprintf(f, "hash: SHA256:%s\n", fp); 1398*19261079SEd Maste free(fp); 1399*19261079SEd Maste } 1400*19261079SEd Maste RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) { 1401*19261079SEd Maste /* 1402*19261079SEd Maste * There is not KRL spec keyword for raw SHA1 hashes, so 1403*19261079SEd Maste * print them as comments. 1404*19261079SEd Maste */ 1405*19261079SEd Maste fp = tohex(rb->blob, rb->len); 1406*19261079SEd Maste fprintf(f, "# hash SHA1:%s\n", fp); 1407*19261079SEd Maste free(fp); 1408*19261079SEd Maste } 1409*19261079SEd Maste 1410*19261079SEd Maste TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { 1411*19261079SEd Maste fputc('\n', f); 1412*19261079SEd Maste if (rc->ca_key == NULL) 1413*19261079SEd Maste fprintf(f, "# Wildcard CA\n"); 1414*19261079SEd Maste else { 1415*19261079SEd Maste if ((fp = sshkey_fingerprint(rc->ca_key, 1416*19261079SEd Maste SSH_FP_HASH_DEFAULT, SSH_FP_DEFAULT)) == NULL) { 1417*19261079SEd Maste ret = SSH_ERR_INVALID_FORMAT; 1418*19261079SEd Maste error("sshkey_fingerprint failed"); 1419*19261079SEd Maste continue; 1420*19261079SEd Maste } 1421*19261079SEd Maste fprintf(f, "# CA key %s %s\n", 1422*19261079SEd Maste sshkey_ssh_name(rc->ca_key), fp); 1423*19261079SEd Maste free(fp); 1424*19261079SEd Maste } 1425*19261079SEd Maste RB_FOREACH(rs, revoked_serial_tree, &rc->revoked_serials) { 1426*19261079SEd Maste if (rs->lo == rs->hi) { 1427*19261079SEd Maste fprintf(f, "serial: %llu\n", 1428*19261079SEd Maste (unsigned long long)rs->lo); 1429*19261079SEd Maste } else { 1430*19261079SEd Maste fprintf(f, "serial: %llu-%llu\n", 1431*19261079SEd Maste (unsigned long long)rs->lo, 1432*19261079SEd Maste (unsigned long long)rs->hi); 1433*19261079SEd Maste } 1434*19261079SEd Maste } 1435*19261079SEd Maste RB_FOREACH(rki, revoked_key_id_tree, &rc->revoked_key_ids) { 1436*19261079SEd Maste /* 1437*19261079SEd Maste * We don't want key IDs with embedded newlines to 1438*19261079SEd Maste * mess up the display. 1439*19261079SEd Maste */ 1440*19261079SEd Maste r = INT_MAX; 1441*19261079SEd Maste asmprintf(&fp, INT_MAX, &r, "%s", rki->key_id); 1442*19261079SEd Maste fprintf(f, "id: %s\n", fp); 1443*19261079SEd Maste free(fp); 1444*19261079SEd Maste } 1445*19261079SEd Maste } 1446*19261079SEd Maste return ret; 1447*19261079SEd Maste } 1448