1*535af610SEd Maste /* $OpenBSD: krl.c,v 1.59 2023/07/17 05:22:30 djm Exp $ */ 26888a9beSDag-Erling Smørgrav /* 36888a9beSDag-Erling Smørgrav * Copyright (c) 2012 Damien Miller <djm@mindrot.org> 46888a9beSDag-Erling Smørgrav * 56888a9beSDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 66888a9beSDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 76888a9beSDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 86888a9beSDag-Erling Smørgrav * 96888a9beSDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 106888a9beSDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 116888a9beSDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 126888a9beSDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 136888a9beSDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 146888a9beSDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 156888a9beSDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 166888a9beSDag-Erling Smørgrav */ 176888a9beSDag-Erling Smørgrav 186888a9beSDag-Erling Smørgrav #include "includes.h" 196888a9beSDag-Erling Smørgrav 206888a9beSDag-Erling Smørgrav #include <sys/types.h> 216888a9beSDag-Erling Smørgrav #include <openbsd-compat/sys-tree.h> 226888a9beSDag-Erling Smørgrav #include <openbsd-compat/sys-queue.h> 236888a9beSDag-Erling Smørgrav 246888a9beSDag-Erling Smørgrav #include <errno.h> 256888a9beSDag-Erling Smørgrav #include <fcntl.h> 266888a9beSDag-Erling Smørgrav #include <limits.h> 2719261079SEd Maste #include <stdlib.h> 286888a9beSDag-Erling Smørgrav #include <string.h> 296888a9beSDag-Erling Smørgrav #include <time.h> 306888a9beSDag-Erling Smørgrav #include <unistd.h> 316888a9beSDag-Erling Smørgrav 32bc5531deSDag-Erling Smørgrav #include "sshbuf.h" 33bc5531deSDag-Erling Smørgrav #include "ssherr.h" 34bc5531deSDag-Erling Smørgrav #include "sshkey.h" 356888a9beSDag-Erling Smørgrav #include "authfile.h" 366888a9beSDag-Erling Smørgrav #include "misc.h" 376888a9beSDag-Erling Smørgrav #include "log.h" 38bc5531deSDag-Erling Smørgrav #include "digest.h" 39bc5531deSDag-Erling Smørgrav #include "bitmap.h" 4019261079SEd Maste #include "utf8.h" 416888a9beSDag-Erling Smørgrav 426888a9beSDag-Erling Smørgrav #include "krl.h" 436888a9beSDag-Erling Smørgrav 446888a9beSDag-Erling Smørgrav /* #define DEBUG_KRL */ 456888a9beSDag-Erling Smørgrav #ifdef DEBUG_KRL 4619261079SEd Maste # define KRL_DBG(x) debug3_f x 476888a9beSDag-Erling Smørgrav #else 486888a9beSDag-Erling Smørgrav # define KRL_DBG(x) 496888a9beSDag-Erling Smørgrav #endif 506888a9beSDag-Erling Smørgrav 516888a9beSDag-Erling Smørgrav /* 526888a9beSDag-Erling Smørgrav * Trees of revoked serial numbers, key IDs and keys. This allows 536888a9beSDag-Erling Smørgrav * quick searching, querying and producing lists in canonical order. 546888a9beSDag-Erling Smørgrav */ 556888a9beSDag-Erling Smørgrav 566888a9beSDag-Erling Smørgrav /* Tree of serial numbers. XXX make smarter: really need a real sparse bitmap */ 576888a9beSDag-Erling Smørgrav struct revoked_serial { 586888a9beSDag-Erling Smørgrav u_int64_t lo, hi; 596888a9beSDag-Erling Smørgrav RB_ENTRY(revoked_serial) tree_entry; 606888a9beSDag-Erling Smørgrav }; 616888a9beSDag-Erling Smørgrav static int serial_cmp(struct revoked_serial *a, struct revoked_serial *b); 626888a9beSDag-Erling Smørgrav RB_HEAD(revoked_serial_tree, revoked_serial); 6319261079SEd Maste RB_GENERATE_STATIC(revoked_serial_tree, revoked_serial, tree_entry, serial_cmp) 646888a9beSDag-Erling Smørgrav 656888a9beSDag-Erling Smørgrav /* Tree of key IDs */ 666888a9beSDag-Erling Smørgrav struct revoked_key_id { 676888a9beSDag-Erling Smørgrav char *key_id; 686888a9beSDag-Erling Smørgrav RB_ENTRY(revoked_key_id) tree_entry; 696888a9beSDag-Erling Smørgrav }; 706888a9beSDag-Erling Smørgrav static int key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b); 716888a9beSDag-Erling Smørgrav RB_HEAD(revoked_key_id_tree, revoked_key_id); 7219261079SEd Maste RB_GENERATE_STATIC(revoked_key_id_tree, revoked_key_id, tree_entry, key_id_cmp) 736888a9beSDag-Erling Smørgrav 746888a9beSDag-Erling Smørgrav /* Tree of blobs (used for keys and fingerprints) */ 756888a9beSDag-Erling Smørgrav struct revoked_blob { 766888a9beSDag-Erling Smørgrav u_char *blob; 77bc5531deSDag-Erling Smørgrav size_t len; 786888a9beSDag-Erling Smørgrav RB_ENTRY(revoked_blob) tree_entry; 796888a9beSDag-Erling Smørgrav }; 806888a9beSDag-Erling Smørgrav static int blob_cmp(struct revoked_blob *a, struct revoked_blob *b); 816888a9beSDag-Erling Smørgrav RB_HEAD(revoked_blob_tree, revoked_blob); 8219261079SEd Maste RB_GENERATE_STATIC(revoked_blob_tree, revoked_blob, tree_entry, blob_cmp) 836888a9beSDag-Erling Smørgrav 846888a9beSDag-Erling Smørgrav /* Tracks revoked certs for a single CA */ 856888a9beSDag-Erling Smørgrav struct revoked_certs { 86bc5531deSDag-Erling Smørgrav struct sshkey *ca_key; 876888a9beSDag-Erling Smørgrav struct revoked_serial_tree revoked_serials; 886888a9beSDag-Erling Smørgrav struct revoked_key_id_tree revoked_key_ids; 896888a9beSDag-Erling Smørgrav TAILQ_ENTRY(revoked_certs) entry; 906888a9beSDag-Erling Smørgrav }; 916888a9beSDag-Erling Smørgrav TAILQ_HEAD(revoked_certs_list, revoked_certs); 926888a9beSDag-Erling Smørgrav 936888a9beSDag-Erling Smørgrav struct ssh_krl { 946888a9beSDag-Erling Smørgrav u_int64_t krl_version; 956888a9beSDag-Erling Smørgrav u_int64_t generated_date; 966888a9beSDag-Erling Smørgrav u_int64_t flags; 976888a9beSDag-Erling Smørgrav char *comment; 986888a9beSDag-Erling Smørgrav struct revoked_blob_tree revoked_keys; 996888a9beSDag-Erling Smørgrav struct revoked_blob_tree revoked_sha1s; 1002f513db7SEd Maste struct revoked_blob_tree revoked_sha256s; 1016888a9beSDag-Erling Smørgrav struct revoked_certs_list revoked_certs; 1026888a9beSDag-Erling Smørgrav }; 1036888a9beSDag-Erling Smørgrav 1046888a9beSDag-Erling Smørgrav /* Return equal if a and b overlap */ 1056888a9beSDag-Erling Smørgrav static int 1066888a9beSDag-Erling Smørgrav serial_cmp(struct revoked_serial *a, struct revoked_serial *b) 1076888a9beSDag-Erling Smørgrav { 1086888a9beSDag-Erling Smørgrav if (a->hi >= b->lo && a->lo <= b->hi) 1096888a9beSDag-Erling Smørgrav return 0; 1106888a9beSDag-Erling Smørgrav return a->lo < b->lo ? -1 : 1; 1116888a9beSDag-Erling Smørgrav } 1126888a9beSDag-Erling Smørgrav 1136888a9beSDag-Erling Smørgrav static int 1146888a9beSDag-Erling Smørgrav key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b) 1156888a9beSDag-Erling Smørgrav { 1166888a9beSDag-Erling Smørgrav return strcmp(a->key_id, b->key_id); 1176888a9beSDag-Erling Smørgrav } 1186888a9beSDag-Erling Smørgrav 1196888a9beSDag-Erling Smørgrav static int 1206888a9beSDag-Erling Smørgrav blob_cmp(struct revoked_blob *a, struct revoked_blob *b) 1216888a9beSDag-Erling Smørgrav { 1226888a9beSDag-Erling Smørgrav int r; 1236888a9beSDag-Erling Smørgrav 1246888a9beSDag-Erling Smørgrav if (a->len != b->len) { 125ca86bcf2SDag-Erling Smørgrav if ((r = memcmp(a->blob, b->blob, MINIMUM(a->len, b->len))) != 0) 1266888a9beSDag-Erling Smørgrav return r; 1276888a9beSDag-Erling Smørgrav return a->len > b->len ? 1 : -1; 1286888a9beSDag-Erling Smørgrav } else 1296888a9beSDag-Erling Smørgrav return memcmp(a->blob, b->blob, a->len); 1306888a9beSDag-Erling Smørgrav } 1316888a9beSDag-Erling Smørgrav 1326888a9beSDag-Erling Smørgrav struct ssh_krl * 1336888a9beSDag-Erling Smørgrav ssh_krl_init(void) 1346888a9beSDag-Erling Smørgrav { 1356888a9beSDag-Erling Smørgrav struct ssh_krl *krl; 1366888a9beSDag-Erling Smørgrav 1376888a9beSDag-Erling Smørgrav if ((krl = calloc(1, sizeof(*krl))) == NULL) 1386888a9beSDag-Erling Smørgrav return NULL; 1396888a9beSDag-Erling Smørgrav RB_INIT(&krl->revoked_keys); 1406888a9beSDag-Erling Smørgrav RB_INIT(&krl->revoked_sha1s); 1412f513db7SEd Maste RB_INIT(&krl->revoked_sha256s); 1426888a9beSDag-Erling Smørgrav TAILQ_INIT(&krl->revoked_certs); 1436888a9beSDag-Erling Smørgrav return krl; 1446888a9beSDag-Erling Smørgrav } 1456888a9beSDag-Erling Smørgrav 1466888a9beSDag-Erling Smørgrav static void 1476888a9beSDag-Erling Smørgrav revoked_certs_free(struct revoked_certs *rc) 1486888a9beSDag-Erling Smørgrav { 1496888a9beSDag-Erling Smørgrav struct revoked_serial *rs, *trs; 1506888a9beSDag-Erling Smørgrav struct revoked_key_id *rki, *trki; 1516888a9beSDag-Erling Smørgrav 1526888a9beSDag-Erling Smørgrav RB_FOREACH_SAFE(rs, revoked_serial_tree, &rc->revoked_serials, trs) { 1536888a9beSDag-Erling Smørgrav RB_REMOVE(revoked_serial_tree, &rc->revoked_serials, rs); 1546888a9beSDag-Erling Smørgrav free(rs); 1556888a9beSDag-Erling Smørgrav } 1566888a9beSDag-Erling Smørgrav RB_FOREACH_SAFE(rki, revoked_key_id_tree, &rc->revoked_key_ids, trki) { 1576888a9beSDag-Erling Smørgrav RB_REMOVE(revoked_key_id_tree, &rc->revoked_key_ids, rki); 1586888a9beSDag-Erling Smørgrav free(rki->key_id); 1596888a9beSDag-Erling Smørgrav free(rki); 1606888a9beSDag-Erling Smørgrav } 161bc5531deSDag-Erling Smørgrav sshkey_free(rc->ca_key); 1626888a9beSDag-Erling Smørgrav } 1636888a9beSDag-Erling Smørgrav 1646888a9beSDag-Erling Smørgrav void 1656888a9beSDag-Erling Smørgrav ssh_krl_free(struct ssh_krl *krl) 1666888a9beSDag-Erling Smørgrav { 1676888a9beSDag-Erling Smørgrav struct revoked_blob *rb, *trb; 1686888a9beSDag-Erling Smørgrav struct revoked_certs *rc, *trc; 1696888a9beSDag-Erling Smørgrav 1706888a9beSDag-Erling Smørgrav if (krl == NULL) 1716888a9beSDag-Erling Smørgrav return; 1726888a9beSDag-Erling Smørgrav 1736888a9beSDag-Erling Smørgrav free(krl->comment); 1746888a9beSDag-Erling Smørgrav RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_keys, trb) { 1756888a9beSDag-Erling Smørgrav RB_REMOVE(revoked_blob_tree, &krl->revoked_keys, rb); 1766888a9beSDag-Erling Smørgrav free(rb->blob); 1776888a9beSDag-Erling Smørgrav free(rb); 1786888a9beSDag-Erling Smørgrav } 1796888a9beSDag-Erling Smørgrav RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_sha1s, trb) { 1806888a9beSDag-Erling Smørgrav RB_REMOVE(revoked_blob_tree, &krl->revoked_sha1s, rb); 1816888a9beSDag-Erling Smørgrav free(rb->blob); 1826888a9beSDag-Erling Smørgrav free(rb); 1836888a9beSDag-Erling Smørgrav } 1842f513db7SEd Maste RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_sha256s, trb) { 1852f513db7SEd Maste RB_REMOVE(revoked_blob_tree, &krl->revoked_sha256s, rb); 1862f513db7SEd Maste free(rb->blob); 1872f513db7SEd Maste free(rb); 1882f513db7SEd Maste } 1896888a9beSDag-Erling Smørgrav TAILQ_FOREACH_SAFE(rc, &krl->revoked_certs, entry, trc) { 1906888a9beSDag-Erling Smørgrav TAILQ_REMOVE(&krl->revoked_certs, rc, entry); 1916888a9beSDag-Erling Smørgrav revoked_certs_free(rc); 1926888a9beSDag-Erling Smørgrav } 1934d3fc8b0SEd Maste free(krl); 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); 24419261079SEd 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 25419261079SEd 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) { 26619261079SEd 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 { 27319261079SEd 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) { 29119261079SEd 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; 29719261079SEd 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) { 30419261079SEd 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; 31019261079SEd 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 } 31519261079SEd 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 35119261079SEd 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 41019261079SEd 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 { 43619261079SEd 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 { 44519261079SEd 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 } 54119261079SEd 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, " 54319261079SEd 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)) { 60119261079SEd 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)) { 62119261079SEd 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) { 64119261079SEd 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) { 67719261079SEd 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) { 69319261079SEd 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 } 70919261079SEd 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) { 71419261079SEd 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 731*535af610SEd Maste ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf) 7326888a9beSDag-Erling Smørgrav { 733bc5531deSDag-Erling Smørgrav int r = SSH_ERR_INTERNAL_ERROR; 7346888a9beSDag-Erling Smørgrav struct revoked_certs *rc; 7356888a9beSDag-Erling Smørgrav struct revoked_blob *rb; 736bc5531deSDag-Erling Smørgrav struct sshbuf *sect; 737bc5531deSDag-Erling Smørgrav u_char *sblob = NULL; 7386888a9beSDag-Erling Smørgrav 7396888a9beSDag-Erling Smørgrav if (krl->generated_date == 0) 7406888a9beSDag-Erling Smørgrav krl->generated_date = time(NULL); 7416888a9beSDag-Erling Smørgrav 742bc5531deSDag-Erling Smørgrav if ((sect = sshbuf_new()) == NULL) 743bc5531deSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 7446888a9beSDag-Erling Smørgrav 7456888a9beSDag-Erling Smørgrav /* Store the header */ 746bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put(buf, KRL_MAGIC, sizeof(KRL_MAGIC) - 1)) != 0 || 747bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(buf, KRL_FORMAT_VERSION)) != 0 || 748bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(buf, krl->krl_version)) != 0 || 749acc1a9efSDag-Erling Smørgrav (r = sshbuf_put_u64(buf, krl->generated_date)) != 0 || 750bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(buf, krl->flags)) != 0 || 751bc5531deSDag-Erling Smørgrav (r = sshbuf_put_string(buf, NULL, 0)) != 0 || 752bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(buf, krl->comment)) != 0) 753bc5531deSDag-Erling Smørgrav goto out; 7546888a9beSDag-Erling Smørgrav 7556888a9beSDag-Erling Smørgrav /* Store sections for revoked certificates */ 7566888a9beSDag-Erling Smørgrav TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { 757bc5531deSDag-Erling Smørgrav sshbuf_reset(sect); 758bc5531deSDag-Erling Smørgrav if ((r = revoked_certs_generate(rc, sect)) != 0) 7596888a9beSDag-Erling Smørgrav goto out; 760bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(buf, KRL_SECTION_CERTIFICATES)) != 0 || 761bc5531deSDag-Erling Smørgrav (r = sshbuf_put_stringb(buf, sect)) != 0) 762bc5531deSDag-Erling Smørgrav goto out; 7636888a9beSDag-Erling Smørgrav } 7646888a9beSDag-Erling Smørgrav 7656888a9beSDag-Erling Smørgrav /* Finally, output sections for revocations by public key/hash */ 766bc5531deSDag-Erling Smørgrav sshbuf_reset(sect); 7676888a9beSDag-Erling Smørgrav RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) { 76819261079SEd Maste KRL_DBG(("key len %zu ", rb->len)); 769bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0) 770bc5531deSDag-Erling Smørgrav goto out; 7716888a9beSDag-Erling Smørgrav } 772bc5531deSDag-Erling Smørgrav if (sshbuf_len(sect) != 0) { 773bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(buf, KRL_SECTION_EXPLICIT_KEY)) != 0 || 774bc5531deSDag-Erling Smørgrav (r = sshbuf_put_stringb(buf, sect)) != 0) 775bc5531deSDag-Erling Smørgrav goto out; 7766888a9beSDag-Erling Smørgrav } 777bc5531deSDag-Erling Smørgrav sshbuf_reset(sect); 7786888a9beSDag-Erling Smørgrav RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) { 77919261079SEd Maste KRL_DBG(("hash len %zu ", rb->len)); 780bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0) 781bc5531deSDag-Erling Smørgrav goto out; 7826888a9beSDag-Erling Smørgrav } 783bc5531deSDag-Erling Smørgrav if (sshbuf_len(sect) != 0) { 784bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(buf, 785bc5531deSDag-Erling Smørgrav KRL_SECTION_FINGERPRINT_SHA1)) != 0 || 786bc5531deSDag-Erling Smørgrav (r = sshbuf_put_stringb(buf, sect)) != 0) 787bc5531deSDag-Erling Smørgrav goto out; 7886888a9beSDag-Erling Smørgrav } 7892f513db7SEd Maste sshbuf_reset(sect); 7902f513db7SEd Maste RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha256s) { 79119261079SEd Maste KRL_DBG(("hash len %zu ", rb->len)); 7922f513db7SEd Maste if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0) 7932f513db7SEd Maste goto out; 7942f513db7SEd Maste } 7952f513db7SEd Maste if (sshbuf_len(sect) != 0) { 7962f513db7SEd Maste if ((r = sshbuf_put_u8(buf, 7972f513db7SEd Maste KRL_SECTION_FINGERPRINT_SHA256)) != 0 || 7982f513db7SEd Maste (r = sshbuf_put_stringb(buf, sect)) != 0) 7992f513db7SEd Maste goto out; 8002f513db7SEd Maste } 801*535af610SEd Maste /* success */ 8026888a9beSDag-Erling Smørgrav r = 0; 8036888a9beSDag-Erling Smørgrav out: 8046888a9beSDag-Erling Smørgrav free(sblob); 805bc5531deSDag-Erling Smørgrav sshbuf_free(sect); 8066888a9beSDag-Erling Smørgrav return r; 8076888a9beSDag-Erling Smørgrav } 8086888a9beSDag-Erling Smørgrav 8096888a9beSDag-Erling Smørgrav static void 8106888a9beSDag-Erling Smørgrav format_timestamp(u_int64_t timestamp, char *ts, size_t nts) 8116888a9beSDag-Erling Smørgrav { 8126888a9beSDag-Erling Smørgrav time_t t; 8136888a9beSDag-Erling Smørgrav struct tm *tm; 8146888a9beSDag-Erling Smørgrav 8156888a9beSDag-Erling Smørgrav t = timestamp; 8166888a9beSDag-Erling Smørgrav tm = localtime(&t); 817bc5531deSDag-Erling Smørgrav if (tm == NULL) 818bc5531deSDag-Erling Smørgrav strlcpy(ts, "<INVALID>", nts); 819bc5531deSDag-Erling Smørgrav else { 8206888a9beSDag-Erling Smørgrav *ts = '\0'; 8216888a9beSDag-Erling Smørgrav strftime(ts, nts, "%Y%m%dT%H%M%S", tm); 8226888a9beSDag-Erling Smørgrav } 823bc5531deSDag-Erling Smørgrav } 8246888a9beSDag-Erling Smørgrav 8256888a9beSDag-Erling Smørgrav static int 826*535af610SEd Maste cert_extension_subsection(struct sshbuf *subsect, struct ssh_krl *krl) 827*535af610SEd Maste { 828*535af610SEd Maste int r = SSH_ERR_INTERNAL_ERROR; 829*535af610SEd Maste u_char critical = 1; 830*535af610SEd Maste struct sshbuf *value = NULL; 831*535af610SEd Maste char *name = NULL; 832*535af610SEd Maste 833*535af610SEd Maste if ((r = sshbuf_get_cstring(subsect, &name, NULL)) != 0 || 834*535af610SEd Maste (r = sshbuf_get_u8(subsect, &critical)) != 0 || 835*535af610SEd Maste (r = sshbuf_froms(subsect, &value)) != 0) { 836*535af610SEd Maste debug_fr(r, "parse"); 837*535af610SEd Maste error("KRL has invalid certificate extension subsection"); 838*535af610SEd Maste r = SSH_ERR_INVALID_FORMAT; 839*535af610SEd Maste goto out; 840*535af610SEd Maste } 841*535af610SEd Maste if (sshbuf_len(subsect) != 0) { 842*535af610SEd Maste error("KRL has invalid certificate extension subsection: " 843*535af610SEd Maste "trailing data"); 844*535af610SEd Maste r = SSH_ERR_INVALID_FORMAT; 845*535af610SEd Maste goto out; 846*535af610SEd Maste } 847*535af610SEd Maste debug_f("cert extension %s critical %u len %zu", 848*535af610SEd Maste name, critical, sshbuf_len(value)); 849*535af610SEd Maste /* no extensions are currently supported */ 850*535af610SEd Maste if (critical) { 851*535af610SEd Maste error("KRL contains unsupported critical certificate " 852*535af610SEd Maste "subsection \"%s\"", name); 853*535af610SEd Maste r = SSH_ERR_FEATURE_UNSUPPORTED; 854*535af610SEd Maste goto out; 855*535af610SEd Maste } 856*535af610SEd Maste /* success */ 857*535af610SEd Maste r = 0; 858*535af610SEd Maste out: 859*535af610SEd Maste free(name); 860*535af610SEd Maste sshbuf_free(value); 861*535af610SEd Maste return r; 862*535af610SEd Maste } 863*535af610SEd Maste 864*535af610SEd Maste static int 865bc5531deSDag-Erling Smørgrav parse_revoked_certs(struct sshbuf *buf, struct ssh_krl *krl) 8666888a9beSDag-Erling Smørgrav { 867bc5531deSDag-Erling Smørgrav int r = SSH_ERR_INTERNAL_ERROR; 868a0ee8cc6SDag-Erling Smørgrav u_char type; 869a0ee8cc6SDag-Erling Smørgrav const u_char *blob; 870bc5531deSDag-Erling Smørgrav size_t blen, nbits; 871bc5531deSDag-Erling Smørgrav struct sshbuf *subsect = NULL; 8726888a9beSDag-Erling Smørgrav u_int64_t serial, serial_lo, serial_hi; 873bc5531deSDag-Erling Smørgrav struct bitmap *bitmap = NULL; 8746888a9beSDag-Erling Smørgrav char *key_id = NULL; 875bc5531deSDag-Erling Smørgrav struct sshkey *ca_key = NULL; 8766888a9beSDag-Erling Smørgrav 877bc5531deSDag-Erling Smørgrav if ((subsect = sshbuf_new()) == NULL) 878bc5531deSDag-Erling Smørgrav return SSH_ERR_ALLOC_FAIL; 8796888a9beSDag-Erling Smørgrav 880bc5531deSDag-Erling Smørgrav /* Header: key, reserved */ 881bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_string_direct(buf, &blob, &blen)) != 0 || 882bc5531deSDag-Erling Smørgrav (r = sshbuf_skip_string(buf)) != 0) 8836888a9beSDag-Erling Smørgrav goto out; 884bc5531deSDag-Erling Smørgrav if (blen != 0 && (r = sshkey_from_blob(blob, blen, &ca_key)) != 0) 885bc5531deSDag-Erling Smørgrav goto out; 886bc5531deSDag-Erling Smørgrav 887bc5531deSDag-Erling Smørgrav while (sshbuf_len(buf) > 0) { 888bc5531deSDag-Erling Smørgrav sshbuf_free(subsect); 889bc5531deSDag-Erling Smørgrav subsect = NULL; 890bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u8(buf, &type)) != 0 || 891bc5531deSDag-Erling Smørgrav (r = sshbuf_froms(buf, &subsect)) != 0) 8926888a9beSDag-Erling Smørgrav goto out; 89319261079SEd Maste KRL_DBG(("subsection type 0x%02x", type)); 894bc5531deSDag-Erling Smørgrav /* sshbuf_dump(subsect, stderr); */ 8956888a9beSDag-Erling Smørgrav 8966888a9beSDag-Erling Smørgrav switch (type) { 8976888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_LIST: 898bc5531deSDag-Erling Smørgrav while (sshbuf_len(subsect) > 0) { 899bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u64(subsect, &serial)) != 0) 9006888a9beSDag-Erling Smørgrav goto out; 901bc5531deSDag-Erling Smørgrav if ((r = ssh_krl_revoke_cert_by_serial(krl, 902bc5531deSDag-Erling Smørgrav ca_key, serial)) != 0) 9036888a9beSDag-Erling Smørgrav goto out; 9046888a9beSDag-Erling Smørgrav } 9056888a9beSDag-Erling Smørgrav break; 9066888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_RANGE: 907bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 || 908bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(subsect, &serial_hi)) != 0) 9096888a9beSDag-Erling Smørgrav goto out; 910bc5531deSDag-Erling Smørgrav if ((r = ssh_krl_revoke_cert_by_serial_range(krl, 911bc5531deSDag-Erling Smørgrav ca_key, serial_lo, serial_hi)) != 0) 9126888a9beSDag-Erling Smørgrav goto out; 9136888a9beSDag-Erling Smørgrav break; 9146888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_BITMAP: 915bc5531deSDag-Erling Smørgrav if ((bitmap = bitmap_new()) == NULL) { 916bc5531deSDag-Erling Smørgrav r = SSH_ERR_ALLOC_FAIL; 9176888a9beSDag-Erling Smørgrav goto out; 9186888a9beSDag-Erling Smørgrav } 919bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 || 920bc5531deSDag-Erling Smørgrav (r = sshbuf_get_bignum2_bytes_direct(subsect, 921bc5531deSDag-Erling Smørgrav &blob, &blen)) != 0) 922bc5531deSDag-Erling Smørgrav goto out; 923bc5531deSDag-Erling Smørgrav if (bitmap_from_string(bitmap, blob, blen) != 0) { 924bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 9256888a9beSDag-Erling Smørgrav goto out; 9266888a9beSDag-Erling Smørgrav } 927bc5531deSDag-Erling Smørgrav nbits = bitmap_nbits(bitmap); 928bc5531deSDag-Erling Smørgrav for (serial = 0; serial < (u_int64_t)nbits; serial++) { 9296888a9beSDag-Erling Smørgrav if (serial > 0 && serial_lo + serial == 0) { 93019261079SEd Maste error_f("bitmap wraps u64"); 931bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 9326888a9beSDag-Erling Smørgrav goto out; 9336888a9beSDag-Erling Smørgrav } 934bc5531deSDag-Erling Smørgrav if (!bitmap_test_bit(bitmap, serial)) 9356888a9beSDag-Erling Smørgrav continue; 936bc5531deSDag-Erling Smørgrav if ((r = ssh_krl_revoke_cert_by_serial(krl, 937bc5531deSDag-Erling Smørgrav ca_key, serial_lo + serial)) != 0) 9386888a9beSDag-Erling Smørgrav goto out; 9396888a9beSDag-Erling Smørgrav } 940bc5531deSDag-Erling Smørgrav bitmap_free(bitmap); 9416888a9beSDag-Erling Smørgrav bitmap = NULL; 9426888a9beSDag-Erling Smørgrav break; 9436888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_KEY_ID: 944bc5531deSDag-Erling Smørgrav while (sshbuf_len(subsect) > 0) { 945bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(subsect, 946bc5531deSDag-Erling Smørgrav &key_id, NULL)) != 0) 9476888a9beSDag-Erling Smørgrav goto out; 948bc5531deSDag-Erling Smørgrav if ((r = ssh_krl_revoke_cert_by_key_id(krl, 949bc5531deSDag-Erling Smørgrav ca_key, key_id)) != 0) 9506888a9beSDag-Erling Smørgrav goto out; 9516888a9beSDag-Erling Smørgrav free(key_id); 9526888a9beSDag-Erling Smørgrav key_id = NULL; 9536888a9beSDag-Erling Smørgrav } 9546888a9beSDag-Erling Smørgrav break; 955*535af610SEd Maste case KRL_SECTION_CERT_EXTENSION: 956*535af610SEd Maste if ((r = cert_extension_subsection(subsect, krl)) != 0) 957*535af610SEd Maste goto out; 958*535af610SEd Maste break; 9596888a9beSDag-Erling Smørgrav default: 9606888a9beSDag-Erling Smørgrav error("Unsupported KRL certificate section %u", type); 961bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 9626888a9beSDag-Erling Smørgrav goto out; 9636888a9beSDag-Erling Smørgrav } 964bc5531deSDag-Erling Smørgrav if (sshbuf_len(subsect) > 0) { 9656888a9beSDag-Erling Smørgrav error("KRL certificate section contains unparsed data"); 966bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 9676888a9beSDag-Erling Smørgrav goto out; 9686888a9beSDag-Erling Smørgrav } 9696888a9beSDag-Erling Smørgrav } 9706888a9beSDag-Erling Smørgrav 971bc5531deSDag-Erling Smørgrav r = 0; 9726888a9beSDag-Erling Smørgrav out: 9736888a9beSDag-Erling Smørgrav if (bitmap != NULL) 974bc5531deSDag-Erling Smørgrav bitmap_free(bitmap); 9756888a9beSDag-Erling Smørgrav free(key_id); 976bc5531deSDag-Erling Smørgrav sshkey_free(ca_key); 977bc5531deSDag-Erling Smørgrav sshbuf_free(subsect); 978bc5531deSDag-Erling Smørgrav return r; 9796888a9beSDag-Erling Smørgrav } 9806888a9beSDag-Erling Smørgrav 9812f513db7SEd Maste static int 9822f513db7SEd Maste blob_section(struct sshbuf *sect, struct revoked_blob_tree *target_tree, 9832f513db7SEd Maste size_t expected_len) 9842f513db7SEd Maste { 9852f513db7SEd Maste u_char *rdata = NULL; 9862f513db7SEd Maste size_t rlen = 0; 9872f513db7SEd Maste int r; 9882f513db7SEd Maste 9892f513db7SEd Maste while (sshbuf_len(sect) > 0) { 9902f513db7SEd Maste if ((r = sshbuf_get_string(sect, &rdata, &rlen)) != 0) 9912f513db7SEd Maste return r; 9922f513db7SEd Maste if (expected_len != 0 && rlen != expected_len) { 99319261079SEd Maste error_f("bad length"); 9942f513db7SEd Maste free(rdata); 9952f513db7SEd Maste return SSH_ERR_INVALID_FORMAT; 9962f513db7SEd Maste } 9972f513db7SEd Maste if ((r = revoke_blob(target_tree, rdata, rlen)) != 0) { 9982f513db7SEd Maste free(rdata); 9992f513db7SEd Maste return r; 10002f513db7SEd Maste } 10012f513db7SEd Maste } 10022f513db7SEd Maste return 0; 10032f513db7SEd Maste } 10046888a9beSDag-Erling Smørgrav 1005*535af610SEd Maste static int 1006*535af610SEd Maste extension_section(struct sshbuf *sect, struct ssh_krl *krl) 1007*535af610SEd Maste { 1008*535af610SEd Maste int r = SSH_ERR_INTERNAL_ERROR; 1009*535af610SEd Maste u_char critical = 1; 1010*535af610SEd Maste struct sshbuf *value = NULL; 1011*535af610SEd Maste char *name = NULL; 1012*535af610SEd Maste 1013*535af610SEd Maste if ((r = sshbuf_get_cstring(sect, &name, NULL)) != 0 || 1014*535af610SEd Maste (r = sshbuf_get_u8(sect, &critical)) != 0 || 1015*535af610SEd Maste (r = sshbuf_froms(sect, &value)) != 0) { 1016*535af610SEd Maste debug_fr(r, "parse"); 1017*535af610SEd Maste error("KRL has invalid extension section"); 1018*535af610SEd Maste r = SSH_ERR_INVALID_FORMAT; 1019*535af610SEd Maste goto out; 1020*535af610SEd Maste } 1021*535af610SEd Maste if (sshbuf_len(sect) != 0) { 1022*535af610SEd Maste error("KRL has invalid extension section: trailing data"); 1023*535af610SEd Maste r = SSH_ERR_INVALID_FORMAT; 1024*535af610SEd Maste goto out; 1025*535af610SEd Maste } 1026*535af610SEd Maste debug_f("extension %s critical %u len %zu", 1027*535af610SEd Maste name, critical, sshbuf_len(value)); 1028*535af610SEd Maste /* no extensions are currently supported */ 1029*535af610SEd Maste if (critical) { 1030*535af610SEd Maste error("KRL contains unsupported critical section \"%s\"", name); 1031*535af610SEd Maste r = SSH_ERR_FEATURE_UNSUPPORTED; 1032*535af610SEd Maste goto out; 1033*535af610SEd Maste } 1034*535af610SEd Maste /* success */ 1035*535af610SEd Maste r = 0; 1036*535af610SEd Maste out: 1037*535af610SEd Maste free(name); 1038*535af610SEd Maste sshbuf_free(value); 1039*535af610SEd Maste return r; 1040*535af610SEd Maste } 1041*535af610SEd Maste 1042*535af610SEd Maste /* Attempt to parse a KRL */ 10436888a9beSDag-Erling Smørgrav int 1044*535af610SEd Maste ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp) 10456888a9beSDag-Erling Smørgrav { 1046bc5531deSDag-Erling Smørgrav struct sshbuf *copy = NULL, *sect = NULL; 1047bc5531deSDag-Erling Smørgrav struct ssh_krl *krl = NULL; 10486888a9beSDag-Erling Smørgrav char timestamp[64]; 1049*535af610SEd Maste int r = SSH_ERR_INTERNAL_ERROR; 10502f513db7SEd Maste u_char type; 1051bc5531deSDag-Erling Smørgrav u_int format_version; 10526888a9beSDag-Erling Smørgrav 10536888a9beSDag-Erling Smørgrav *krlp = NULL; 1054*535af610SEd Maste 1055*535af610SEd Maste /* KRL must begin with magic string */ 1056*535af610SEd Maste if ((r = sshbuf_cmp(buf, 0, KRL_MAGIC, sizeof(KRL_MAGIC) - 1)) != 0) { 1057*535af610SEd Maste debug2_f("bad KRL magic header"); 1058bc5531deSDag-Erling Smørgrav return SSH_ERR_KRL_BAD_MAGIC; 10596888a9beSDag-Erling Smørgrav } 10606888a9beSDag-Erling Smørgrav 10616888a9beSDag-Erling Smørgrav if ((krl = ssh_krl_init()) == NULL) { 106219261079SEd Maste error_f("alloc failed"); 10636888a9beSDag-Erling Smørgrav goto out; 10646888a9beSDag-Erling Smørgrav } 1065*535af610SEd Maste /* Don't modify buffer */ 1066*535af610SEd Maste if ((copy = sshbuf_fromb(buf)) == NULL) { 1067*535af610SEd Maste r = SSH_ERR_ALLOC_FAIL; 1068*535af610SEd Maste goto out; 1069*535af610SEd Maste } 1070*535af610SEd Maste if ((r = sshbuf_consume(copy, sizeof(KRL_MAGIC) - 1)) != 0 || 1071*535af610SEd Maste (r = sshbuf_get_u32(copy, &format_version)) != 0) 10726888a9beSDag-Erling Smørgrav goto out; 10736888a9beSDag-Erling Smørgrav if (format_version != KRL_FORMAT_VERSION) { 1074*535af610SEd Maste error_f("unsupported KRL format version %u", format_version); 1075bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 10766888a9beSDag-Erling Smørgrav goto out; 10776888a9beSDag-Erling Smørgrav } 1078bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u64(copy, &krl->krl_version)) != 0 || 1079bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(copy, &krl->generated_date)) != 0 || 1080bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(copy, &krl->flags)) != 0 || 1081bc5531deSDag-Erling Smørgrav (r = sshbuf_skip_string(copy)) != 0 || 1082*535af610SEd Maste (r = sshbuf_get_cstring(copy, &krl->comment, NULL)) != 0) { 1083*535af610SEd Maste error_fr(r, "parse KRL header"); 10846888a9beSDag-Erling Smørgrav goto out; 1085*535af610SEd Maste } 10866888a9beSDag-Erling Smørgrav format_timestamp(krl->generated_date, timestamp, sizeof(timestamp)); 108759928918SDag-Erling Smørgrav debug("KRL version %llu generated at %s%s%s", 1088e4a9863fSDag-Erling Smørgrav (long long unsigned)krl->krl_version, timestamp, 108959928918SDag-Erling Smørgrav *krl->comment ? ": " : "", krl->comment); 10906888a9beSDag-Erling Smørgrav 1091*535af610SEd Maste /* Parse and load the KRL sections. */ 1092bc5531deSDag-Erling Smørgrav while (sshbuf_len(copy) > 0) { 1093bc5531deSDag-Erling Smørgrav sshbuf_free(sect); 1094bc5531deSDag-Erling Smørgrav sect = NULL; 1095bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u8(copy, &type)) != 0 || 1096bc5531deSDag-Erling Smørgrav (r = sshbuf_froms(copy, §)) != 0) 1097bc5531deSDag-Erling Smørgrav goto out; 1098*535af610SEd Maste KRL_DBG(("section 0x%02x", type)); 10996888a9beSDag-Erling Smørgrav 11006888a9beSDag-Erling Smørgrav switch (type) { 11016888a9beSDag-Erling Smørgrav case KRL_SECTION_CERTIFICATES: 1102bc5531deSDag-Erling Smørgrav if ((r = parse_revoked_certs(sect, krl)) != 0) 11036888a9beSDag-Erling Smørgrav goto out; 11046888a9beSDag-Erling Smørgrav break; 11056888a9beSDag-Erling Smørgrav case KRL_SECTION_EXPLICIT_KEY: 11062f513db7SEd Maste if ((r = blob_section(sect, 11072f513db7SEd Maste &krl->revoked_keys, 0)) != 0) 11082f513db7SEd Maste goto out; 11092f513db7SEd Maste break; 11106888a9beSDag-Erling Smørgrav case KRL_SECTION_FINGERPRINT_SHA1: 11112f513db7SEd Maste if ((r = blob_section(sect, 11122f513db7SEd Maste &krl->revoked_sha1s, 20)) != 0) 11136888a9beSDag-Erling Smørgrav goto out; 11142f513db7SEd Maste break; 11152f513db7SEd Maste case KRL_SECTION_FINGERPRINT_SHA256: 11162f513db7SEd Maste if ((r = blob_section(sect, 11172f513db7SEd Maste &krl->revoked_sha256s, 32)) != 0) 11186888a9beSDag-Erling Smørgrav goto out; 11196888a9beSDag-Erling Smørgrav break; 1120*535af610SEd Maste case KRL_SECTION_EXTENSION: 1121*535af610SEd Maste if ((r = extension_section(sect, krl)) != 0) 1122*535af610SEd Maste goto out; 1123*535af610SEd Maste break; 11246888a9beSDag-Erling Smørgrav case KRL_SECTION_SIGNATURE: 11256888a9beSDag-Erling Smørgrav /* Handled above, but still need to stay in synch */ 1126d93a896eSDag-Erling Smørgrav sshbuf_free(sect); 1127bc5531deSDag-Erling Smørgrav sect = NULL; 1128bc5531deSDag-Erling Smørgrav if ((r = sshbuf_skip_string(copy)) != 0) 11296888a9beSDag-Erling Smørgrav goto out; 11306888a9beSDag-Erling Smørgrav break; 11316888a9beSDag-Erling Smørgrav default: 11326888a9beSDag-Erling Smørgrav error("Unsupported KRL section %u", type); 1133bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 11346888a9beSDag-Erling Smørgrav goto out; 11356888a9beSDag-Erling Smørgrav } 1136acc1a9efSDag-Erling Smørgrav if (sect != NULL && sshbuf_len(sect) > 0) { 11376888a9beSDag-Erling Smørgrav error("KRL section contains unparsed data"); 1138bc5531deSDag-Erling Smørgrav r = SSH_ERR_INVALID_FORMAT; 11396888a9beSDag-Erling Smørgrav goto out; 11406888a9beSDag-Erling Smørgrav } 11416888a9beSDag-Erling Smørgrav } 11426888a9beSDag-Erling Smørgrav 1143*535af610SEd Maste /* Success */ 11446888a9beSDag-Erling Smørgrav *krlp = krl; 1145bc5531deSDag-Erling Smørgrav r = 0; 11466888a9beSDag-Erling Smørgrav out: 1147bc5531deSDag-Erling Smørgrav if (r != 0) 11486888a9beSDag-Erling Smørgrav ssh_krl_free(krl); 1149bc5531deSDag-Erling Smørgrav sshbuf_free(copy); 1150bc5531deSDag-Erling Smørgrav sshbuf_free(sect); 1151bc5531deSDag-Erling Smørgrav return r; 11526888a9beSDag-Erling Smørgrav } 11536888a9beSDag-Erling Smørgrav 1154bc5531deSDag-Erling Smørgrav /* Checks certificate serial number and key ID revocation */ 11556888a9beSDag-Erling Smørgrav static int 1156bc5531deSDag-Erling Smørgrav is_cert_revoked(const struct sshkey *key, struct revoked_certs *rc) 11576888a9beSDag-Erling Smørgrav { 11586888a9beSDag-Erling Smørgrav struct revoked_serial rs, *ers; 11596888a9beSDag-Erling Smørgrav struct revoked_key_id rki, *erki; 11606888a9beSDag-Erling Smørgrav 11616888a9beSDag-Erling Smørgrav /* Check revocation by cert key ID */ 1162b83788ffSDag-Erling Smørgrav memset(&rki, 0, sizeof(rki)); 11636888a9beSDag-Erling Smørgrav rki.key_id = key->cert->key_id; 11646888a9beSDag-Erling Smørgrav erki = RB_FIND(revoked_key_id_tree, &rc->revoked_key_ids, &rki); 11656888a9beSDag-Erling Smørgrav if (erki != NULL) { 116619261079SEd Maste KRL_DBG(("revoked by key ID")); 1167bc5531deSDag-Erling Smørgrav return SSH_ERR_KEY_REVOKED; 11686888a9beSDag-Erling Smørgrav } 11696888a9beSDag-Erling Smørgrav 11706888a9beSDag-Erling Smørgrav /* 1171eccfee6eSDag-Erling Smørgrav * Zero serials numbers are ignored (it's the default when the 1172eccfee6eSDag-Erling Smørgrav * CA doesn't specify one). 11736888a9beSDag-Erling Smørgrav */ 1174eccfee6eSDag-Erling Smørgrav if (key->cert->serial == 0) 11756888a9beSDag-Erling Smørgrav return 0; 11766888a9beSDag-Erling Smørgrav 1177b83788ffSDag-Erling Smørgrav memset(&rs, 0, sizeof(rs)); 11786888a9beSDag-Erling Smørgrav rs.lo = rs.hi = key->cert->serial; 11796888a9beSDag-Erling Smørgrav ers = RB_FIND(revoked_serial_tree, &rc->revoked_serials, &rs); 11806888a9beSDag-Erling Smørgrav if (ers != NULL) { 118119261079SEd Maste KRL_DBG(("revoked serial %llu matched %llu:%llu", 11826888a9beSDag-Erling Smørgrav key->cert->serial, ers->lo, ers->hi)); 1183bc5531deSDag-Erling Smørgrav return SSH_ERR_KEY_REVOKED; 11846888a9beSDag-Erling Smørgrav } 1185bc5531deSDag-Erling Smørgrav return 0; 1186bc5531deSDag-Erling Smørgrav } 11876888a9beSDag-Erling Smørgrav 1188bc5531deSDag-Erling Smørgrav /* Checks whether a given key/cert is revoked. Does not check its CA */ 1189bc5531deSDag-Erling Smørgrav static int 1190bc5531deSDag-Erling Smørgrav is_key_revoked(struct ssh_krl *krl, const struct sshkey *key) 1191bc5531deSDag-Erling Smørgrav { 1192bc5531deSDag-Erling Smørgrav struct revoked_blob rb, *erb; 1193bc5531deSDag-Erling Smørgrav struct revoked_certs *rc; 1194bc5531deSDag-Erling Smørgrav int r; 1195bc5531deSDag-Erling Smørgrav 1196bc5531deSDag-Erling Smørgrav /* Check explicitly revoked hashes first */ 1197bc5531deSDag-Erling Smørgrav memset(&rb, 0, sizeof(rb)); 1198bc5531deSDag-Erling Smørgrav if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA1, 1199bc5531deSDag-Erling Smørgrav &rb.blob, &rb.len)) != 0) 1200bc5531deSDag-Erling Smørgrav return r; 1201bc5531deSDag-Erling Smørgrav erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha1s, &rb); 1202bc5531deSDag-Erling Smørgrav free(rb.blob); 1203bc5531deSDag-Erling Smørgrav if (erb != NULL) { 120419261079SEd Maste KRL_DBG(("revoked by key SHA1")); 1205bc5531deSDag-Erling Smørgrav return SSH_ERR_KEY_REVOKED; 1206bc5531deSDag-Erling Smørgrav } 12072f513db7SEd Maste memset(&rb, 0, sizeof(rb)); 12082f513db7SEd Maste if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA256, 12092f513db7SEd Maste &rb.blob, &rb.len)) != 0) 12102f513db7SEd Maste return r; 12112f513db7SEd Maste erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha256s, &rb); 12122f513db7SEd Maste free(rb.blob); 12132f513db7SEd Maste if (erb != NULL) { 121419261079SEd Maste KRL_DBG(("revoked by key SHA256")); 12152f513db7SEd Maste return SSH_ERR_KEY_REVOKED; 12162f513db7SEd Maste } 1217bc5531deSDag-Erling Smørgrav 1218bc5531deSDag-Erling Smørgrav /* Next, explicit keys */ 1219bc5531deSDag-Erling Smørgrav memset(&rb, 0, sizeof(rb)); 1220bc5531deSDag-Erling Smørgrav if ((r = plain_key_blob(key, &rb.blob, &rb.len)) != 0) 1221bc5531deSDag-Erling Smørgrav return r; 1222bc5531deSDag-Erling Smørgrav erb = RB_FIND(revoked_blob_tree, &krl->revoked_keys, &rb); 1223bc5531deSDag-Erling Smørgrav free(rb.blob); 1224bc5531deSDag-Erling Smørgrav if (erb != NULL) { 122519261079SEd Maste KRL_DBG(("revoked by explicit key")); 1226bc5531deSDag-Erling Smørgrav return SSH_ERR_KEY_REVOKED; 1227bc5531deSDag-Erling Smørgrav } 1228bc5531deSDag-Erling Smørgrav 1229bc5531deSDag-Erling Smørgrav if (!sshkey_is_cert(key)) 1230bc5531deSDag-Erling Smørgrav return 0; 1231bc5531deSDag-Erling Smørgrav 1232bc5531deSDag-Erling Smørgrav /* Check cert revocation for the specified CA */ 1233bc5531deSDag-Erling Smørgrav if ((r = revoked_certs_for_ca_key(krl, key->cert->signature_key, 1234bc5531deSDag-Erling Smørgrav &rc, 0)) != 0) 1235bc5531deSDag-Erling Smørgrav return r; 1236bc5531deSDag-Erling Smørgrav if (rc != NULL) { 1237bc5531deSDag-Erling Smørgrav if ((r = is_cert_revoked(key, rc)) != 0) 1238bc5531deSDag-Erling Smørgrav return r; 1239bc5531deSDag-Erling Smørgrav } 1240bc5531deSDag-Erling Smørgrav /* Check cert revocation for the wildcard CA */ 1241bc5531deSDag-Erling Smørgrav if ((r = revoked_certs_for_ca_key(krl, NULL, &rc, 0)) != 0) 1242bc5531deSDag-Erling Smørgrav return r; 1243bc5531deSDag-Erling Smørgrav if (rc != NULL) { 1244bc5531deSDag-Erling Smørgrav if ((r = is_cert_revoked(key, rc)) != 0) 1245bc5531deSDag-Erling Smørgrav return r; 1246bc5531deSDag-Erling Smørgrav } 1247bc5531deSDag-Erling Smørgrav 124819261079SEd Maste KRL_DBG(("%llu no match", key->cert->serial)); 12496888a9beSDag-Erling Smørgrav return 0; 12506888a9beSDag-Erling Smørgrav } 12516888a9beSDag-Erling Smørgrav 12526888a9beSDag-Erling Smørgrav int 1253bc5531deSDag-Erling Smørgrav ssh_krl_check_key(struct ssh_krl *krl, const struct sshkey *key) 12546888a9beSDag-Erling Smørgrav { 12556888a9beSDag-Erling Smørgrav int r; 12566888a9beSDag-Erling Smørgrav 125719261079SEd Maste KRL_DBG(("checking key")); 12586888a9beSDag-Erling Smørgrav if ((r = is_key_revoked(krl, key)) != 0) 12596888a9beSDag-Erling Smørgrav return r; 1260bc5531deSDag-Erling Smørgrav if (sshkey_is_cert(key)) { 126119261079SEd Maste debug2_f("checking CA key"); 12626888a9beSDag-Erling Smørgrav if ((r = is_key_revoked(krl, key->cert->signature_key)) != 0) 12636888a9beSDag-Erling Smørgrav return r; 12646888a9beSDag-Erling Smørgrav } 126519261079SEd Maste KRL_DBG(("key okay")); 12666888a9beSDag-Erling Smørgrav return 0; 12676888a9beSDag-Erling Smørgrav } 12686888a9beSDag-Erling Smørgrav 12696888a9beSDag-Erling Smørgrav int 1270bc5531deSDag-Erling Smørgrav ssh_krl_file_contains_key(const char *path, const struct sshkey *key) 12716888a9beSDag-Erling Smørgrav { 1272bc5531deSDag-Erling Smørgrav struct sshbuf *krlbuf = NULL; 1273bc5531deSDag-Erling Smørgrav struct ssh_krl *krl = NULL; 127419261079SEd Maste int oerrno = 0, r; 12756888a9beSDag-Erling Smørgrav 12766888a9beSDag-Erling Smørgrav if (path == NULL) 12776888a9beSDag-Erling Smørgrav return 0; 127819261079SEd Maste if ((r = sshbuf_load_file(path, &krlbuf)) != 0) { 1279bc5531deSDag-Erling Smørgrav oerrno = errno; 1280bc5531deSDag-Erling Smørgrav goto out; 12816888a9beSDag-Erling Smørgrav } 1282*535af610SEd Maste if ((r = ssh_krl_from_blob(krlbuf, &krl)) != 0) 1283bc5531deSDag-Erling Smørgrav goto out; 128419261079SEd Maste debug2_f("checking KRL %s", path); 1285bc5531deSDag-Erling Smørgrav r = ssh_krl_check_key(krl, key); 1286bc5531deSDag-Erling Smørgrav out: 1287bc5531deSDag-Erling Smørgrav sshbuf_free(krlbuf); 12886888a9beSDag-Erling Smørgrav ssh_krl_free(krl); 1289bc5531deSDag-Erling Smørgrav if (r != 0) 1290bc5531deSDag-Erling Smørgrav errno = oerrno; 1291bc5531deSDag-Erling Smørgrav return r; 12926888a9beSDag-Erling Smørgrav } 129319261079SEd Maste 129419261079SEd Maste int 129519261079SEd Maste krl_dump(struct ssh_krl *krl, FILE *f) 129619261079SEd Maste { 129719261079SEd Maste struct sshkey *key = NULL; 129819261079SEd Maste struct revoked_blob *rb; 129919261079SEd Maste struct revoked_certs *rc; 130019261079SEd Maste struct revoked_serial *rs; 130119261079SEd Maste struct revoked_key_id *rki; 130219261079SEd Maste int r, ret = 0; 130319261079SEd Maste char *fp, timestamp[64]; 130419261079SEd Maste 130519261079SEd Maste /* Try to print in a KRL spec-compatible format */ 130619261079SEd Maste format_timestamp(krl->generated_date, timestamp, sizeof(timestamp)); 130719261079SEd Maste fprintf(f, "# KRL version %llu\n", 130819261079SEd Maste (unsigned long long)krl->krl_version); 130919261079SEd Maste fprintf(f, "# Generated at %s\n", timestamp); 131019261079SEd Maste if (krl->comment != NULL && *krl->comment != '\0') { 131119261079SEd Maste r = INT_MAX; 131219261079SEd Maste asmprintf(&fp, INT_MAX, &r, "%s", krl->comment); 131319261079SEd Maste fprintf(f, "# Comment: %s\n", fp); 131419261079SEd Maste free(fp); 131519261079SEd Maste } 131619261079SEd Maste fputc('\n', f); 131719261079SEd Maste 131819261079SEd Maste RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) { 131919261079SEd Maste if ((r = sshkey_from_blob(rb->blob, rb->len, &key)) != 0) { 132019261079SEd Maste ret = SSH_ERR_INVALID_FORMAT; 132119261079SEd Maste error_r(r, "parse KRL key"); 132219261079SEd Maste continue; 132319261079SEd Maste } 132419261079SEd Maste if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, 132519261079SEd Maste SSH_FP_DEFAULT)) == NULL) { 132619261079SEd Maste ret = SSH_ERR_INVALID_FORMAT; 132719261079SEd Maste error("sshkey_fingerprint failed"); 132819261079SEd Maste continue; 132919261079SEd Maste } 133038a52bd3SEd Maste fprintf(f, "hash: %s # %s\n", fp, sshkey_ssh_name(key)); 133119261079SEd Maste free(fp); 133219261079SEd Maste free(key); 133319261079SEd Maste } 133419261079SEd Maste RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha256s) { 133519261079SEd Maste fp = tohex(rb->blob, rb->len); 133619261079SEd Maste fprintf(f, "hash: SHA256:%s\n", fp); 133719261079SEd Maste free(fp); 133819261079SEd Maste } 133919261079SEd Maste RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) { 134019261079SEd Maste /* 134119261079SEd Maste * There is not KRL spec keyword for raw SHA1 hashes, so 134219261079SEd Maste * print them as comments. 134319261079SEd Maste */ 134419261079SEd Maste fp = tohex(rb->blob, rb->len); 134519261079SEd Maste fprintf(f, "# hash SHA1:%s\n", fp); 134619261079SEd Maste free(fp); 134719261079SEd Maste } 134819261079SEd Maste 134919261079SEd Maste TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { 135019261079SEd Maste fputc('\n', f); 135119261079SEd Maste if (rc->ca_key == NULL) 135219261079SEd Maste fprintf(f, "# Wildcard CA\n"); 135319261079SEd Maste else { 135419261079SEd Maste if ((fp = sshkey_fingerprint(rc->ca_key, 135519261079SEd Maste SSH_FP_HASH_DEFAULT, SSH_FP_DEFAULT)) == NULL) { 135619261079SEd Maste ret = SSH_ERR_INVALID_FORMAT; 135719261079SEd Maste error("sshkey_fingerprint failed"); 135819261079SEd Maste continue; 135919261079SEd Maste } 136019261079SEd Maste fprintf(f, "# CA key %s %s\n", 136119261079SEd Maste sshkey_ssh_name(rc->ca_key), fp); 136219261079SEd Maste free(fp); 136319261079SEd Maste } 136419261079SEd Maste RB_FOREACH(rs, revoked_serial_tree, &rc->revoked_serials) { 136519261079SEd Maste if (rs->lo == rs->hi) { 136619261079SEd Maste fprintf(f, "serial: %llu\n", 136719261079SEd Maste (unsigned long long)rs->lo); 136819261079SEd Maste } else { 136919261079SEd Maste fprintf(f, "serial: %llu-%llu\n", 137019261079SEd Maste (unsigned long long)rs->lo, 137119261079SEd Maste (unsigned long long)rs->hi); 137219261079SEd Maste } 137319261079SEd Maste } 137419261079SEd Maste RB_FOREACH(rki, revoked_key_id_tree, &rc->revoked_key_ids) { 137519261079SEd Maste /* 137619261079SEd Maste * We don't want key IDs with embedded newlines to 137719261079SEd Maste * mess up the display. 137819261079SEd Maste */ 137919261079SEd Maste r = INT_MAX; 138019261079SEd Maste asmprintf(&fp, INT_MAX, &r, "%s", rki->key_id); 138119261079SEd Maste fprintf(f, "id: %s\n", fp); 138219261079SEd Maste free(fp); 138319261079SEd Maste } 138419261079SEd Maste } 138519261079SEd Maste return ret; 138619261079SEd Maste } 1387