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*e4a9863fSDag-Erling Smørgrav /* $OpenBSD: krl.c,v 1.13 2013/07/20 22:20:42 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 <sys/param.h> 236888a9beSDag-Erling Smørgrav #include <openbsd-compat/sys-tree.h> 246888a9beSDag-Erling Smørgrav #include <openbsd-compat/sys-queue.h> 256888a9beSDag-Erling Smørgrav 266888a9beSDag-Erling Smørgrav #include <errno.h> 276888a9beSDag-Erling Smørgrav #include <fcntl.h> 286888a9beSDag-Erling Smørgrav #include <limits.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 336888a9beSDag-Erling Smørgrav #include "buffer.h" 346888a9beSDag-Erling Smørgrav #include "key.h" 356888a9beSDag-Erling Smørgrav #include "authfile.h" 366888a9beSDag-Erling Smørgrav #include "misc.h" 376888a9beSDag-Erling Smørgrav #include "log.h" 386888a9beSDag-Erling Smørgrav #include "xmalloc.h" 396888a9beSDag-Erling Smørgrav 406888a9beSDag-Erling Smørgrav #include "krl.h" 416888a9beSDag-Erling Smørgrav 426888a9beSDag-Erling Smørgrav /* #define DEBUG_KRL */ 436888a9beSDag-Erling Smørgrav #ifdef DEBUG_KRL 446888a9beSDag-Erling Smørgrav # define KRL_DBG(x) debug3 x 456888a9beSDag-Erling Smørgrav #else 466888a9beSDag-Erling Smørgrav # define KRL_DBG(x) 476888a9beSDag-Erling Smørgrav #endif 486888a9beSDag-Erling Smørgrav 496888a9beSDag-Erling Smørgrav /* 506888a9beSDag-Erling Smørgrav * Trees of revoked serial numbers, key IDs and keys. This allows 516888a9beSDag-Erling Smørgrav * quick searching, querying and producing lists in canonical order. 526888a9beSDag-Erling Smørgrav */ 536888a9beSDag-Erling Smørgrav 546888a9beSDag-Erling Smørgrav /* Tree of serial numbers. XXX make smarter: really need a real sparse bitmap */ 556888a9beSDag-Erling Smørgrav struct revoked_serial { 566888a9beSDag-Erling Smørgrav u_int64_t lo, hi; 576888a9beSDag-Erling Smørgrav RB_ENTRY(revoked_serial) tree_entry; 586888a9beSDag-Erling Smørgrav }; 596888a9beSDag-Erling Smørgrav static int serial_cmp(struct revoked_serial *a, struct revoked_serial *b); 606888a9beSDag-Erling Smørgrav RB_HEAD(revoked_serial_tree, revoked_serial); 616888a9beSDag-Erling Smørgrav RB_GENERATE_STATIC(revoked_serial_tree, revoked_serial, tree_entry, serial_cmp); 626888a9beSDag-Erling Smørgrav 636888a9beSDag-Erling Smørgrav /* Tree of key IDs */ 646888a9beSDag-Erling Smørgrav struct revoked_key_id { 656888a9beSDag-Erling Smørgrav char *key_id; 666888a9beSDag-Erling Smørgrav RB_ENTRY(revoked_key_id) tree_entry; 676888a9beSDag-Erling Smørgrav }; 686888a9beSDag-Erling Smørgrav static int key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b); 696888a9beSDag-Erling Smørgrav RB_HEAD(revoked_key_id_tree, revoked_key_id); 706888a9beSDag-Erling Smørgrav RB_GENERATE_STATIC(revoked_key_id_tree, revoked_key_id, tree_entry, key_id_cmp); 716888a9beSDag-Erling Smørgrav 726888a9beSDag-Erling Smørgrav /* Tree of blobs (used for keys and fingerprints) */ 736888a9beSDag-Erling Smørgrav struct revoked_blob { 746888a9beSDag-Erling Smørgrav u_char *blob; 756888a9beSDag-Erling Smørgrav u_int len; 766888a9beSDag-Erling Smørgrav RB_ENTRY(revoked_blob) tree_entry; 776888a9beSDag-Erling Smørgrav }; 786888a9beSDag-Erling Smørgrav static int blob_cmp(struct revoked_blob *a, struct revoked_blob *b); 796888a9beSDag-Erling Smørgrav RB_HEAD(revoked_blob_tree, revoked_blob); 806888a9beSDag-Erling Smørgrav RB_GENERATE_STATIC(revoked_blob_tree, revoked_blob, tree_entry, blob_cmp); 816888a9beSDag-Erling Smørgrav 826888a9beSDag-Erling Smørgrav /* Tracks revoked certs for a single CA */ 836888a9beSDag-Erling Smørgrav struct revoked_certs { 846888a9beSDag-Erling Smørgrav Key *ca_key; 856888a9beSDag-Erling Smørgrav struct revoked_serial_tree revoked_serials; 866888a9beSDag-Erling Smørgrav struct revoked_key_id_tree revoked_key_ids; 876888a9beSDag-Erling Smørgrav TAILQ_ENTRY(revoked_certs) entry; 886888a9beSDag-Erling Smørgrav }; 896888a9beSDag-Erling Smørgrav TAILQ_HEAD(revoked_certs_list, revoked_certs); 906888a9beSDag-Erling Smørgrav 916888a9beSDag-Erling Smørgrav struct ssh_krl { 926888a9beSDag-Erling Smørgrav u_int64_t krl_version; 936888a9beSDag-Erling Smørgrav u_int64_t generated_date; 946888a9beSDag-Erling Smørgrav u_int64_t flags; 956888a9beSDag-Erling Smørgrav char *comment; 966888a9beSDag-Erling Smørgrav struct revoked_blob_tree revoked_keys; 976888a9beSDag-Erling Smørgrav struct revoked_blob_tree revoked_sha1s; 986888a9beSDag-Erling Smørgrav struct revoked_certs_list revoked_certs; 996888a9beSDag-Erling Smørgrav }; 1006888a9beSDag-Erling Smørgrav 1016888a9beSDag-Erling Smørgrav /* Return equal if a and b overlap */ 1026888a9beSDag-Erling Smørgrav static int 1036888a9beSDag-Erling Smørgrav serial_cmp(struct revoked_serial *a, struct revoked_serial *b) 1046888a9beSDag-Erling Smørgrav { 1056888a9beSDag-Erling Smørgrav if (a->hi >= b->lo && a->lo <= b->hi) 1066888a9beSDag-Erling Smørgrav return 0; 1076888a9beSDag-Erling Smørgrav return a->lo < b->lo ? -1 : 1; 1086888a9beSDag-Erling Smørgrav } 1096888a9beSDag-Erling Smørgrav 1106888a9beSDag-Erling Smørgrav static int 1116888a9beSDag-Erling Smørgrav key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b) 1126888a9beSDag-Erling Smørgrav { 1136888a9beSDag-Erling Smørgrav return strcmp(a->key_id, b->key_id); 1146888a9beSDag-Erling Smørgrav } 1156888a9beSDag-Erling Smørgrav 1166888a9beSDag-Erling Smørgrav static int 1176888a9beSDag-Erling Smørgrav blob_cmp(struct revoked_blob *a, struct revoked_blob *b) 1186888a9beSDag-Erling Smørgrav { 1196888a9beSDag-Erling Smørgrav int r; 1206888a9beSDag-Erling Smørgrav 1216888a9beSDag-Erling Smørgrav if (a->len != b->len) { 1226888a9beSDag-Erling Smørgrav if ((r = memcmp(a->blob, b->blob, MIN(a->len, b->len))) != 0) 1236888a9beSDag-Erling Smørgrav return r; 1246888a9beSDag-Erling Smørgrav return a->len > b->len ? 1 : -1; 1256888a9beSDag-Erling Smørgrav } else 1266888a9beSDag-Erling Smørgrav return memcmp(a->blob, b->blob, a->len); 1276888a9beSDag-Erling Smørgrav } 1286888a9beSDag-Erling Smørgrav 1296888a9beSDag-Erling Smørgrav struct ssh_krl * 1306888a9beSDag-Erling Smørgrav ssh_krl_init(void) 1316888a9beSDag-Erling Smørgrav { 1326888a9beSDag-Erling Smørgrav struct ssh_krl *krl; 1336888a9beSDag-Erling Smørgrav 1346888a9beSDag-Erling Smørgrav if ((krl = calloc(1, sizeof(*krl))) == NULL) 1356888a9beSDag-Erling Smørgrav return NULL; 1366888a9beSDag-Erling Smørgrav RB_INIT(&krl->revoked_keys); 1376888a9beSDag-Erling Smørgrav RB_INIT(&krl->revoked_sha1s); 1386888a9beSDag-Erling Smørgrav TAILQ_INIT(&krl->revoked_certs); 1396888a9beSDag-Erling Smørgrav return krl; 1406888a9beSDag-Erling Smørgrav } 1416888a9beSDag-Erling Smørgrav 1426888a9beSDag-Erling Smørgrav static void 1436888a9beSDag-Erling Smørgrav revoked_certs_free(struct revoked_certs *rc) 1446888a9beSDag-Erling Smørgrav { 1456888a9beSDag-Erling Smørgrav struct revoked_serial *rs, *trs; 1466888a9beSDag-Erling Smørgrav struct revoked_key_id *rki, *trki; 1476888a9beSDag-Erling Smørgrav 1486888a9beSDag-Erling Smørgrav RB_FOREACH_SAFE(rs, revoked_serial_tree, &rc->revoked_serials, trs) { 1496888a9beSDag-Erling Smørgrav RB_REMOVE(revoked_serial_tree, &rc->revoked_serials, rs); 1506888a9beSDag-Erling Smørgrav free(rs); 1516888a9beSDag-Erling Smørgrav } 1526888a9beSDag-Erling Smørgrav RB_FOREACH_SAFE(rki, revoked_key_id_tree, &rc->revoked_key_ids, trki) { 1536888a9beSDag-Erling Smørgrav RB_REMOVE(revoked_key_id_tree, &rc->revoked_key_ids, rki); 1546888a9beSDag-Erling Smørgrav free(rki->key_id); 1556888a9beSDag-Erling Smørgrav free(rki); 1566888a9beSDag-Erling Smørgrav } 1576888a9beSDag-Erling Smørgrav if (rc->ca_key != NULL) 1586888a9beSDag-Erling Smørgrav key_free(rc->ca_key); 1596888a9beSDag-Erling Smørgrav } 1606888a9beSDag-Erling Smørgrav 1616888a9beSDag-Erling Smørgrav void 1626888a9beSDag-Erling Smørgrav ssh_krl_free(struct ssh_krl *krl) 1636888a9beSDag-Erling Smørgrav { 1646888a9beSDag-Erling Smørgrav struct revoked_blob *rb, *trb; 1656888a9beSDag-Erling Smørgrav struct revoked_certs *rc, *trc; 1666888a9beSDag-Erling Smørgrav 1676888a9beSDag-Erling Smørgrav if (krl == NULL) 1686888a9beSDag-Erling Smørgrav return; 1696888a9beSDag-Erling Smørgrav 1706888a9beSDag-Erling Smørgrav free(krl->comment); 1716888a9beSDag-Erling Smørgrav RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_keys, trb) { 1726888a9beSDag-Erling Smørgrav RB_REMOVE(revoked_blob_tree, &krl->revoked_keys, rb); 1736888a9beSDag-Erling Smørgrav free(rb->blob); 1746888a9beSDag-Erling Smørgrav free(rb); 1756888a9beSDag-Erling Smørgrav } 1766888a9beSDag-Erling Smørgrav RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_sha1s, trb) { 1776888a9beSDag-Erling Smørgrav RB_REMOVE(revoked_blob_tree, &krl->revoked_sha1s, rb); 1786888a9beSDag-Erling Smørgrav free(rb->blob); 1796888a9beSDag-Erling Smørgrav free(rb); 1806888a9beSDag-Erling Smørgrav } 1816888a9beSDag-Erling Smørgrav TAILQ_FOREACH_SAFE(rc, &krl->revoked_certs, entry, trc) { 1826888a9beSDag-Erling Smørgrav TAILQ_REMOVE(&krl->revoked_certs, rc, entry); 1836888a9beSDag-Erling Smørgrav revoked_certs_free(rc); 1846888a9beSDag-Erling Smørgrav } 1856888a9beSDag-Erling Smørgrav } 1866888a9beSDag-Erling Smørgrav 1876888a9beSDag-Erling Smørgrav void 1886888a9beSDag-Erling Smørgrav ssh_krl_set_version(struct ssh_krl *krl, u_int64_t version) 1896888a9beSDag-Erling Smørgrav { 1906888a9beSDag-Erling Smørgrav krl->krl_version = version; 1916888a9beSDag-Erling Smørgrav } 1926888a9beSDag-Erling Smørgrav 1936888a9beSDag-Erling Smørgrav void 1946888a9beSDag-Erling Smørgrav ssh_krl_set_comment(struct ssh_krl *krl, const char *comment) 1956888a9beSDag-Erling Smørgrav { 1966888a9beSDag-Erling Smørgrav free(krl->comment); 1976888a9beSDag-Erling Smørgrav if ((krl->comment = strdup(comment)) == NULL) 1986888a9beSDag-Erling Smørgrav fatal("%s: strdup", __func__); 1996888a9beSDag-Erling Smørgrav } 2006888a9beSDag-Erling Smørgrav 2016888a9beSDag-Erling Smørgrav /* 2026888a9beSDag-Erling Smørgrav * Find the revoked_certs struct for a CA key. If allow_create is set then 2036888a9beSDag-Erling Smørgrav * create a new one in the tree if one did not exist already. 2046888a9beSDag-Erling Smørgrav */ 2056888a9beSDag-Erling Smørgrav static int 2066888a9beSDag-Erling Smørgrav revoked_certs_for_ca_key(struct ssh_krl *krl, const Key *ca_key, 2076888a9beSDag-Erling Smørgrav struct revoked_certs **rcp, int allow_create) 2086888a9beSDag-Erling Smørgrav { 2096888a9beSDag-Erling Smørgrav struct revoked_certs *rc; 2106888a9beSDag-Erling Smørgrav 2116888a9beSDag-Erling Smørgrav *rcp = NULL; 2126888a9beSDag-Erling Smørgrav TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { 2136888a9beSDag-Erling Smørgrav if (key_equal(rc->ca_key, ca_key)) { 2146888a9beSDag-Erling Smørgrav *rcp = rc; 2156888a9beSDag-Erling Smørgrav return 0; 2166888a9beSDag-Erling Smørgrav } 2176888a9beSDag-Erling Smørgrav } 2186888a9beSDag-Erling Smørgrav if (!allow_create) 2196888a9beSDag-Erling Smørgrav return 0; 2206888a9beSDag-Erling Smørgrav /* If this CA doesn't exist in the list then add it now */ 2216888a9beSDag-Erling Smørgrav if ((rc = calloc(1, sizeof(*rc))) == NULL) 2226888a9beSDag-Erling Smørgrav return -1; 2236888a9beSDag-Erling Smørgrav if ((rc->ca_key = key_from_private(ca_key)) == NULL) { 2246888a9beSDag-Erling Smørgrav free(rc); 2256888a9beSDag-Erling Smørgrav return -1; 2266888a9beSDag-Erling Smørgrav } 2276888a9beSDag-Erling Smørgrav RB_INIT(&rc->revoked_serials); 2286888a9beSDag-Erling Smørgrav RB_INIT(&rc->revoked_key_ids); 2296888a9beSDag-Erling Smørgrav TAILQ_INSERT_TAIL(&krl->revoked_certs, rc, entry); 2306888a9beSDag-Erling Smørgrav debug3("%s: new CA %s", __func__, key_type(ca_key)); 2316888a9beSDag-Erling Smørgrav *rcp = rc; 2326888a9beSDag-Erling Smørgrav return 0; 2336888a9beSDag-Erling Smørgrav } 2346888a9beSDag-Erling Smørgrav 2356888a9beSDag-Erling Smørgrav static int 2366888a9beSDag-Erling Smørgrav insert_serial_range(struct revoked_serial_tree *rt, u_int64_t lo, u_int64_t hi) 2376888a9beSDag-Erling Smørgrav { 2386888a9beSDag-Erling Smørgrav struct revoked_serial rs, *ers, *crs, *irs; 2396888a9beSDag-Erling Smørgrav 2406888a9beSDag-Erling Smørgrav KRL_DBG(("%s: insert %llu:%llu", __func__, lo, hi)); 2416888a9beSDag-Erling Smørgrav bzero(&rs, sizeof(rs)); 2426888a9beSDag-Erling Smørgrav rs.lo = lo; 2436888a9beSDag-Erling Smørgrav rs.hi = hi; 2446888a9beSDag-Erling Smørgrav ers = RB_NFIND(revoked_serial_tree, rt, &rs); 2456888a9beSDag-Erling Smørgrav if (ers == NULL || serial_cmp(ers, &rs) != 0) { 2466888a9beSDag-Erling Smørgrav /* No entry matches. Just insert */ 2476888a9beSDag-Erling Smørgrav if ((irs = malloc(sizeof(rs))) == NULL) 2486888a9beSDag-Erling Smørgrav return -1; 2496888a9beSDag-Erling Smørgrav memcpy(irs, &rs, sizeof(*irs)); 2506888a9beSDag-Erling Smørgrav ers = RB_INSERT(revoked_serial_tree, rt, irs); 2516888a9beSDag-Erling Smørgrav if (ers != NULL) { 2526888a9beSDag-Erling Smørgrav KRL_DBG(("%s: bad: ers != NULL", __func__)); 2536888a9beSDag-Erling Smørgrav /* Shouldn't happen */ 2546888a9beSDag-Erling Smørgrav free(irs); 2556888a9beSDag-Erling Smørgrav return -1; 2566888a9beSDag-Erling Smørgrav } 2576888a9beSDag-Erling Smørgrav ers = irs; 2586888a9beSDag-Erling Smørgrav } else { 2596888a9beSDag-Erling Smørgrav KRL_DBG(("%s: overlap found %llu:%llu", __func__, 2606888a9beSDag-Erling Smørgrav ers->lo, ers->hi)); 2616888a9beSDag-Erling Smørgrav /* 2626888a9beSDag-Erling Smørgrav * The inserted entry overlaps an existing one. Grow the 2636888a9beSDag-Erling Smørgrav * existing entry. 2646888a9beSDag-Erling Smørgrav */ 2656888a9beSDag-Erling Smørgrav if (ers->lo > lo) 2666888a9beSDag-Erling Smørgrav ers->lo = lo; 2676888a9beSDag-Erling Smørgrav if (ers->hi < hi) 2686888a9beSDag-Erling Smørgrav ers->hi = hi; 2696888a9beSDag-Erling Smørgrav } 2706888a9beSDag-Erling Smørgrav /* 2716888a9beSDag-Erling Smørgrav * The inserted or revised range might overlap or abut adjacent ones; 2726888a9beSDag-Erling Smørgrav * coalesce as necessary. 2736888a9beSDag-Erling Smørgrav */ 2746888a9beSDag-Erling Smørgrav 2756888a9beSDag-Erling Smørgrav /* Check predecessors */ 2766888a9beSDag-Erling Smørgrav while ((crs = RB_PREV(revoked_serial_tree, rt, ers)) != NULL) { 2776888a9beSDag-Erling Smørgrav KRL_DBG(("%s: pred %llu:%llu", __func__, crs->lo, crs->hi)); 2786888a9beSDag-Erling Smørgrav if (ers->lo != 0 && crs->hi < ers->lo - 1) 2796888a9beSDag-Erling Smørgrav break; 2806888a9beSDag-Erling Smørgrav /* This entry overlaps. */ 2816888a9beSDag-Erling Smørgrav if (crs->lo < ers->lo) { 2826888a9beSDag-Erling Smørgrav ers->lo = crs->lo; 2836888a9beSDag-Erling Smørgrav KRL_DBG(("%s: pred extend %llu:%llu", __func__, 2846888a9beSDag-Erling Smørgrav ers->lo, ers->hi)); 2856888a9beSDag-Erling Smørgrav } 2866888a9beSDag-Erling Smørgrav RB_REMOVE(revoked_serial_tree, rt, crs); 2876888a9beSDag-Erling Smørgrav free(crs); 2886888a9beSDag-Erling Smørgrav } 2896888a9beSDag-Erling Smørgrav /* Check successors */ 2906888a9beSDag-Erling Smørgrav while ((crs = RB_NEXT(revoked_serial_tree, rt, ers)) != NULL) { 2916888a9beSDag-Erling Smørgrav KRL_DBG(("%s: succ %llu:%llu", __func__, crs->lo, crs->hi)); 2926888a9beSDag-Erling Smørgrav if (ers->hi != (u_int64_t)-1 && crs->lo > ers->hi + 1) 2936888a9beSDag-Erling Smørgrav break; 2946888a9beSDag-Erling Smørgrav /* This entry overlaps. */ 2956888a9beSDag-Erling Smørgrav if (crs->hi > ers->hi) { 2966888a9beSDag-Erling Smørgrav ers->hi = crs->hi; 2976888a9beSDag-Erling Smørgrav KRL_DBG(("%s: succ extend %llu:%llu", __func__, 2986888a9beSDag-Erling Smørgrav ers->lo, ers->hi)); 2996888a9beSDag-Erling Smørgrav } 3006888a9beSDag-Erling Smørgrav RB_REMOVE(revoked_serial_tree, rt, crs); 3016888a9beSDag-Erling Smørgrav free(crs); 3026888a9beSDag-Erling Smørgrav } 3036888a9beSDag-Erling Smørgrav KRL_DBG(("%s: done, final %llu:%llu", __func__, ers->lo, ers->hi)); 3046888a9beSDag-Erling Smørgrav return 0; 3056888a9beSDag-Erling Smørgrav } 3066888a9beSDag-Erling Smørgrav 3076888a9beSDag-Erling Smørgrav int 3086888a9beSDag-Erling Smørgrav ssh_krl_revoke_cert_by_serial(struct ssh_krl *krl, const Key *ca_key, 3096888a9beSDag-Erling Smørgrav u_int64_t serial) 3106888a9beSDag-Erling Smørgrav { 3116888a9beSDag-Erling Smørgrav return ssh_krl_revoke_cert_by_serial_range(krl, ca_key, serial, serial); 3126888a9beSDag-Erling Smørgrav } 3136888a9beSDag-Erling Smørgrav 3146888a9beSDag-Erling Smørgrav int 3156888a9beSDag-Erling Smørgrav ssh_krl_revoke_cert_by_serial_range(struct ssh_krl *krl, const Key *ca_key, 3166888a9beSDag-Erling Smørgrav u_int64_t lo, u_int64_t hi) 3176888a9beSDag-Erling Smørgrav { 3186888a9beSDag-Erling Smørgrav struct revoked_certs *rc; 3196888a9beSDag-Erling Smørgrav 3206888a9beSDag-Erling Smørgrav if (lo > hi || lo == 0) 3216888a9beSDag-Erling Smørgrav return -1; 3226888a9beSDag-Erling Smørgrav if (revoked_certs_for_ca_key(krl, ca_key, &rc, 1) != 0) 3236888a9beSDag-Erling Smørgrav return -1; 3246888a9beSDag-Erling Smørgrav return insert_serial_range(&rc->revoked_serials, lo, hi); 3256888a9beSDag-Erling Smørgrav } 3266888a9beSDag-Erling Smørgrav 3276888a9beSDag-Erling Smørgrav int 3286888a9beSDag-Erling Smørgrav ssh_krl_revoke_cert_by_key_id(struct ssh_krl *krl, const Key *ca_key, 3296888a9beSDag-Erling Smørgrav const char *key_id) 3306888a9beSDag-Erling Smørgrav { 3316888a9beSDag-Erling Smørgrav struct revoked_key_id *rki, *erki; 3326888a9beSDag-Erling Smørgrav struct revoked_certs *rc; 3336888a9beSDag-Erling Smørgrav 3346888a9beSDag-Erling Smørgrav if (revoked_certs_for_ca_key(krl, ca_key, &rc, 1) != 0) 3356888a9beSDag-Erling Smørgrav return -1; 3366888a9beSDag-Erling Smørgrav 3376888a9beSDag-Erling Smørgrav debug3("%s: revoke %s", __func__, key_id); 3386888a9beSDag-Erling Smørgrav if ((rki = calloc(1, sizeof(*rki))) == NULL || 3396888a9beSDag-Erling Smørgrav (rki->key_id = strdup(key_id)) == NULL) { 3406888a9beSDag-Erling Smørgrav free(rki); 3416888a9beSDag-Erling Smørgrav fatal("%s: strdup", __func__); 3426888a9beSDag-Erling Smørgrav } 3436888a9beSDag-Erling Smørgrav erki = RB_INSERT(revoked_key_id_tree, &rc->revoked_key_ids, rki); 3446888a9beSDag-Erling Smørgrav if (erki != NULL) { 3456888a9beSDag-Erling Smørgrav free(rki->key_id); 3466888a9beSDag-Erling Smørgrav free(rki); 3476888a9beSDag-Erling Smørgrav } 3486888a9beSDag-Erling Smørgrav return 0; 3496888a9beSDag-Erling Smørgrav } 3506888a9beSDag-Erling Smørgrav 3516888a9beSDag-Erling Smørgrav /* Convert "key" to a public key blob without any certificate information */ 3526888a9beSDag-Erling Smørgrav static int 3536888a9beSDag-Erling Smørgrav plain_key_blob(const Key *key, u_char **blob, u_int *blen) 3546888a9beSDag-Erling Smørgrav { 3556888a9beSDag-Erling Smørgrav Key *kcopy; 3566888a9beSDag-Erling Smørgrav int r; 3576888a9beSDag-Erling Smørgrav 3586888a9beSDag-Erling Smørgrav if ((kcopy = key_from_private(key)) == NULL) 3596888a9beSDag-Erling Smørgrav return -1; 3606888a9beSDag-Erling Smørgrav if (key_is_cert(kcopy)) { 3616888a9beSDag-Erling Smørgrav if (key_drop_cert(kcopy) != 0) { 3626888a9beSDag-Erling Smørgrav error("%s: key_drop_cert", __func__); 3636888a9beSDag-Erling Smørgrav key_free(kcopy); 3646888a9beSDag-Erling Smørgrav return -1; 3656888a9beSDag-Erling Smørgrav } 3666888a9beSDag-Erling Smørgrav } 3676888a9beSDag-Erling Smørgrav r = key_to_blob(kcopy, blob, blen); 3686888a9beSDag-Erling Smørgrav free(kcopy); 3696888a9beSDag-Erling Smørgrav return r == 0 ? -1 : 0; 3706888a9beSDag-Erling Smørgrav } 3716888a9beSDag-Erling Smørgrav 3726888a9beSDag-Erling Smørgrav /* Revoke a key blob. Ownership of blob is transferred to the tree */ 3736888a9beSDag-Erling Smørgrav static int 3746888a9beSDag-Erling Smørgrav revoke_blob(struct revoked_blob_tree *rbt, u_char *blob, u_int len) 3756888a9beSDag-Erling Smørgrav { 3766888a9beSDag-Erling Smørgrav struct revoked_blob *rb, *erb; 3776888a9beSDag-Erling Smørgrav 3786888a9beSDag-Erling Smørgrav if ((rb = calloc(1, sizeof(*rb))) == NULL) 3796888a9beSDag-Erling Smørgrav return -1; 3806888a9beSDag-Erling Smørgrav rb->blob = blob; 3816888a9beSDag-Erling Smørgrav rb->len = len; 3826888a9beSDag-Erling Smørgrav erb = RB_INSERT(revoked_blob_tree, rbt, rb); 3836888a9beSDag-Erling Smørgrav if (erb != NULL) { 3846888a9beSDag-Erling Smørgrav free(rb->blob); 3856888a9beSDag-Erling Smørgrav free(rb); 3866888a9beSDag-Erling Smørgrav } 3876888a9beSDag-Erling Smørgrav return 0; 3886888a9beSDag-Erling Smørgrav } 3896888a9beSDag-Erling Smørgrav 3906888a9beSDag-Erling Smørgrav int 3916888a9beSDag-Erling Smørgrav ssh_krl_revoke_key_explicit(struct ssh_krl *krl, const Key *key) 3926888a9beSDag-Erling Smørgrav { 3936888a9beSDag-Erling Smørgrav u_char *blob; 3946888a9beSDag-Erling Smørgrav u_int len; 3956888a9beSDag-Erling Smørgrav 3966888a9beSDag-Erling Smørgrav debug3("%s: revoke type %s", __func__, key_type(key)); 3976888a9beSDag-Erling Smørgrav if (plain_key_blob(key, &blob, &len) != 0) 3986888a9beSDag-Erling Smørgrav return -1; 3996888a9beSDag-Erling Smørgrav return revoke_blob(&krl->revoked_keys, blob, len); 4006888a9beSDag-Erling Smørgrav } 4016888a9beSDag-Erling Smørgrav 4026888a9beSDag-Erling Smørgrav int 4036888a9beSDag-Erling Smørgrav ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const Key *key) 4046888a9beSDag-Erling Smørgrav { 4056888a9beSDag-Erling Smørgrav u_char *blob; 4066888a9beSDag-Erling Smørgrav u_int len; 4076888a9beSDag-Erling Smørgrav 4086888a9beSDag-Erling Smørgrav debug3("%s: revoke type %s by sha1", __func__, key_type(key)); 4096888a9beSDag-Erling Smørgrav if ((blob = key_fingerprint_raw(key, SSH_FP_SHA1, &len)) == NULL) 4106888a9beSDag-Erling Smørgrav return -1; 4116888a9beSDag-Erling Smørgrav return revoke_blob(&krl->revoked_sha1s, blob, len); 4126888a9beSDag-Erling Smørgrav } 4136888a9beSDag-Erling Smørgrav 4146888a9beSDag-Erling Smørgrav int 4156888a9beSDag-Erling Smørgrav ssh_krl_revoke_key(struct ssh_krl *krl, const Key *key) 4166888a9beSDag-Erling Smørgrav { 4176888a9beSDag-Erling Smørgrav if (!key_is_cert(key)) 4186888a9beSDag-Erling Smørgrav return ssh_krl_revoke_key_sha1(krl, key); 4196888a9beSDag-Erling Smørgrav 4206888a9beSDag-Erling Smørgrav if (key_cert_is_legacy(key) || key->cert->serial == 0) { 4216888a9beSDag-Erling Smørgrav return ssh_krl_revoke_cert_by_key_id(krl, 4226888a9beSDag-Erling Smørgrav key->cert->signature_key, 4236888a9beSDag-Erling Smørgrav key->cert->key_id); 4246888a9beSDag-Erling Smørgrav } else { 4256888a9beSDag-Erling Smørgrav return ssh_krl_revoke_cert_by_serial(krl, 4266888a9beSDag-Erling Smørgrav key->cert->signature_key, 4276888a9beSDag-Erling Smørgrav key->cert->serial); 4286888a9beSDag-Erling Smørgrav } 4296888a9beSDag-Erling Smørgrav } 4306888a9beSDag-Erling Smørgrav 4316888a9beSDag-Erling Smørgrav /* 4326888a9beSDag-Erling Smørgrav * Select a copact next section type to emit in a KRL based on the 4336888a9beSDag-Erling Smørgrav * current section type, the run length of contiguous revoked serial 4346888a9beSDag-Erling Smørgrav * numbers and the gaps from the last and to the next revoked serial. 4356888a9beSDag-Erling Smørgrav * Applies a mostly-accurate bit cost model to select the section type 4366888a9beSDag-Erling Smørgrav * that will minimise the size of the resultant KRL. 4376888a9beSDag-Erling Smørgrav */ 4386888a9beSDag-Erling Smørgrav static int 4396888a9beSDag-Erling Smørgrav choose_next_state(int current_state, u_int64_t contig, int final, 4406888a9beSDag-Erling Smørgrav u_int64_t last_gap, u_int64_t next_gap, int *force_new_section) 4416888a9beSDag-Erling Smørgrav { 4426888a9beSDag-Erling Smørgrav int new_state; 4436888a9beSDag-Erling Smørgrav u_int64_t cost, cost_list, cost_range, cost_bitmap, cost_bitmap_restart; 4446888a9beSDag-Erling Smørgrav 4456888a9beSDag-Erling Smørgrav /* 4466888a9beSDag-Erling Smørgrav * Avoid unsigned overflows. 4476888a9beSDag-Erling Smørgrav * The limits are high enough to avoid confusing the calculations. 4486888a9beSDag-Erling Smørgrav */ 4496888a9beSDag-Erling Smørgrav contig = MIN(contig, 1ULL<<31); 4506888a9beSDag-Erling Smørgrav last_gap = MIN(last_gap, 1ULL<<31); 4516888a9beSDag-Erling Smørgrav next_gap = MIN(next_gap, 1ULL<<31); 4526888a9beSDag-Erling Smørgrav 4536888a9beSDag-Erling Smørgrav /* 4546888a9beSDag-Erling Smørgrav * Calculate the cost to switch from the current state to candidates. 4556888a9beSDag-Erling Smørgrav * NB. range sections only ever contain a single range, so their 4566888a9beSDag-Erling Smørgrav * switching cost is independent of the current_state. 4576888a9beSDag-Erling Smørgrav */ 4586888a9beSDag-Erling Smørgrav cost_list = cost_bitmap = cost_bitmap_restart = 0; 4596888a9beSDag-Erling Smørgrav cost_range = 8; 4606888a9beSDag-Erling Smørgrav switch (current_state) { 4616888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_LIST: 4626888a9beSDag-Erling Smørgrav cost_bitmap_restart = cost_bitmap = 8 + 64; 4636888a9beSDag-Erling Smørgrav break; 4646888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_BITMAP: 4656888a9beSDag-Erling Smørgrav cost_list = 8; 4666888a9beSDag-Erling Smørgrav cost_bitmap_restart = 8 + 64; 4676888a9beSDag-Erling Smørgrav break; 4686888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_RANGE: 4696888a9beSDag-Erling Smørgrav case 0: 4706888a9beSDag-Erling Smørgrav cost_bitmap_restart = cost_bitmap = 8 + 64; 4716888a9beSDag-Erling Smørgrav cost_list = 8; 4726888a9beSDag-Erling Smørgrav } 4736888a9beSDag-Erling Smørgrav 4746888a9beSDag-Erling Smørgrav /* Estimate base cost in bits of each section type */ 4756888a9beSDag-Erling Smørgrav cost_list += 64 * contig + (final ? 0 : 8+64); 4766888a9beSDag-Erling Smørgrav cost_range += (2 * 64) + (final ? 0 : 8+64); 4776888a9beSDag-Erling Smørgrav cost_bitmap += last_gap + contig + (final ? 0 : MIN(next_gap, 8+64)); 4786888a9beSDag-Erling Smørgrav cost_bitmap_restart += contig + (final ? 0 : MIN(next_gap, 8+64)); 4796888a9beSDag-Erling Smørgrav 4806888a9beSDag-Erling Smørgrav /* Convert to byte costs for actual comparison */ 4816888a9beSDag-Erling Smørgrav cost_list = (cost_list + 7) / 8; 4826888a9beSDag-Erling Smørgrav cost_bitmap = (cost_bitmap + 7) / 8; 4836888a9beSDag-Erling Smørgrav cost_bitmap_restart = (cost_bitmap_restart + 7) / 8; 4846888a9beSDag-Erling Smørgrav cost_range = (cost_range + 7) / 8; 4856888a9beSDag-Erling Smørgrav 4866888a9beSDag-Erling Smørgrav /* Now pick the best choice */ 4876888a9beSDag-Erling Smørgrav *force_new_section = 0; 4886888a9beSDag-Erling Smørgrav new_state = KRL_SECTION_CERT_SERIAL_BITMAP; 4896888a9beSDag-Erling Smørgrav cost = cost_bitmap; 4906888a9beSDag-Erling Smørgrav if (cost_range < cost) { 4916888a9beSDag-Erling Smørgrav new_state = KRL_SECTION_CERT_SERIAL_RANGE; 4926888a9beSDag-Erling Smørgrav cost = cost_range; 4936888a9beSDag-Erling Smørgrav } 4946888a9beSDag-Erling Smørgrav if (cost_list < cost) { 4956888a9beSDag-Erling Smørgrav new_state = KRL_SECTION_CERT_SERIAL_LIST; 4966888a9beSDag-Erling Smørgrav cost = cost_list; 4976888a9beSDag-Erling Smørgrav } 4986888a9beSDag-Erling Smørgrav if (cost_bitmap_restart < cost) { 4996888a9beSDag-Erling Smørgrav new_state = KRL_SECTION_CERT_SERIAL_BITMAP; 5006888a9beSDag-Erling Smørgrav *force_new_section = 1; 5016888a9beSDag-Erling Smørgrav cost = cost_bitmap_restart; 5026888a9beSDag-Erling Smørgrav } 5036888a9beSDag-Erling Smørgrav debug3("%s: contig %llu last_gap %llu next_gap %llu final %d, costs:" 5046888a9beSDag-Erling Smørgrav "list %llu range %llu bitmap %llu new bitmap %llu, " 505*e4a9863fSDag-Erling Smørgrav "selected 0x%02x%s", __func__, (long long unsigned)contig, 506*e4a9863fSDag-Erling Smørgrav (long long unsigned)last_gap, (long long unsigned)next_gap, final, 507*e4a9863fSDag-Erling Smørgrav (long long unsigned)cost_list, (long long unsigned)cost_range, 508*e4a9863fSDag-Erling Smørgrav (long long unsigned)cost_bitmap, 509*e4a9863fSDag-Erling Smørgrav (long long unsigned)cost_bitmap_restart, new_state, 5106888a9beSDag-Erling Smørgrav *force_new_section ? " restart" : ""); 5116888a9beSDag-Erling Smørgrav return new_state; 5126888a9beSDag-Erling Smørgrav } 5136888a9beSDag-Erling Smørgrav 5146888a9beSDag-Erling Smørgrav /* Generate a KRL_SECTION_CERTIFICATES KRL section */ 5156888a9beSDag-Erling Smørgrav static int 5166888a9beSDag-Erling Smørgrav revoked_certs_generate(struct revoked_certs *rc, Buffer *buf) 5176888a9beSDag-Erling Smørgrav { 5186888a9beSDag-Erling Smørgrav int final, force_new_sect, r = -1; 5196888a9beSDag-Erling Smørgrav u_int64_t i, contig, gap, last = 0, bitmap_start = 0; 5206888a9beSDag-Erling Smørgrav struct revoked_serial *rs, *nrs; 5216888a9beSDag-Erling Smørgrav struct revoked_key_id *rki; 5226888a9beSDag-Erling Smørgrav int next_state, state = 0; 5236888a9beSDag-Erling Smørgrav Buffer sect; 5246888a9beSDag-Erling Smørgrav u_char *kblob = NULL; 5256888a9beSDag-Erling Smørgrav u_int klen; 5266888a9beSDag-Erling Smørgrav BIGNUM *bitmap = NULL; 5276888a9beSDag-Erling Smørgrav 5286888a9beSDag-Erling Smørgrav /* Prepare CA scope key blob if we have one supplied */ 5296888a9beSDag-Erling Smørgrav if (key_to_blob(rc->ca_key, &kblob, &klen) == 0) 5306888a9beSDag-Erling Smørgrav return -1; 5316888a9beSDag-Erling Smørgrav 5326888a9beSDag-Erling Smørgrav buffer_init(§); 5336888a9beSDag-Erling Smørgrav 5346888a9beSDag-Erling Smørgrav /* Store the header */ 5356888a9beSDag-Erling Smørgrav buffer_put_string(buf, kblob, klen); 5366888a9beSDag-Erling Smørgrav buffer_put_string(buf, NULL, 0); /* Reserved */ 5376888a9beSDag-Erling Smørgrav 5386888a9beSDag-Erling Smørgrav free(kblob); 5396888a9beSDag-Erling Smørgrav 5406888a9beSDag-Erling Smørgrav /* Store the revoked serials. */ 5416888a9beSDag-Erling Smørgrav for (rs = RB_MIN(revoked_serial_tree, &rc->revoked_serials); 5426888a9beSDag-Erling Smørgrav rs != NULL; 5436888a9beSDag-Erling Smørgrav rs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs)) { 5446888a9beSDag-Erling Smørgrav debug3("%s: serial %llu:%llu state 0x%02x", __func__, 545*e4a9863fSDag-Erling Smørgrav (long long unsigned)rs->lo, (long long unsigned)rs->hi, 54659928918SDag-Erling Smørgrav state); 5476888a9beSDag-Erling Smørgrav 5486888a9beSDag-Erling Smørgrav /* Check contiguous length and gap to next section (if any) */ 5496888a9beSDag-Erling Smørgrav nrs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs); 5506888a9beSDag-Erling Smørgrav final = nrs == NULL; 5516888a9beSDag-Erling Smørgrav gap = nrs == NULL ? 0 : nrs->lo - rs->hi; 5526888a9beSDag-Erling Smørgrav contig = 1 + (rs->hi - rs->lo); 5536888a9beSDag-Erling Smørgrav 5546888a9beSDag-Erling Smørgrav /* Choose next state based on these */ 5556888a9beSDag-Erling Smørgrav next_state = choose_next_state(state, contig, final, 5566888a9beSDag-Erling Smørgrav state == 0 ? 0 : rs->lo - last, gap, &force_new_sect); 5576888a9beSDag-Erling Smørgrav 5586888a9beSDag-Erling Smørgrav /* 5596888a9beSDag-Erling Smørgrav * If the current section is a range section or has a different 5606888a9beSDag-Erling Smørgrav * type to the next section, then finish it off now. 5616888a9beSDag-Erling Smørgrav */ 5626888a9beSDag-Erling Smørgrav if (state != 0 && (force_new_sect || next_state != state || 5636888a9beSDag-Erling Smørgrav state == KRL_SECTION_CERT_SERIAL_RANGE)) { 5646888a9beSDag-Erling Smørgrav debug3("%s: finish state 0x%02x", __func__, state); 5656888a9beSDag-Erling Smørgrav switch (state) { 5666888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_LIST: 5676888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_RANGE: 5686888a9beSDag-Erling Smørgrav break; 5696888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_BITMAP: 5706888a9beSDag-Erling Smørgrav buffer_put_bignum2(§, bitmap); 5716888a9beSDag-Erling Smørgrav BN_free(bitmap); 5726888a9beSDag-Erling Smørgrav bitmap = NULL; 5736888a9beSDag-Erling Smørgrav break; 5746888a9beSDag-Erling Smørgrav } 5756888a9beSDag-Erling Smørgrav buffer_put_char(buf, state); 5766888a9beSDag-Erling Smørgrav buffer_put_string(buf, 5776888a9beSDag-Erling Smørgrav buffer_ptr(§), buffer_len(§)); 5786888a9beSDag-Erling Smørgrav } 5796888a9beSDag-Erling Smørgrav 5806888a9beSDag-Erling Smørgrav /* If we are starting a new section then prepare it now */ 5816888a9beSDag-Erling Smørgrav if (next_state != state || force_new_sect) { 5826888a9beSDag-Erling Smørgrav debug3("%s: start state 0x%02x", __func__, next_state); 5836888a9beSDag-Erling Smørgrav state = next_state; 5846888a9beSDag-Erling Smørgrav buffer_clear(§); 5856888a9beSDag-Erling Smørgrav switch (state) { 5866888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_LIST: 5876888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_RANGE: 5886888a9beSDag-Erling Smørgrav break; 5896888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_BITMAP: 5906888a9beSDag-Erling Smørgrav if ((bitmap = BN_new()) == NULL) 5916888a9beSDag-Erling Smørgrav goto out; 5926888a9beSDag-Erling Smørgrav bitmap_start = rs->lo; 5936888a9beSDag-Erling Smørgrav buffer_put_int64(§, bitmap_start); 5946888a9beSDag-Erling Smørgrav break; 5956888a9beSDag-Erling Smørgrav } 5966888a9beSDag-Erling Smørgrav } 5976888a9beSDag-Erling Smørgrav 5986888a9beSDag-Erling Smørgrav /* Perform section-specific processing */ 5996888a9beSDag-Erling Smørgrav switch (state) { 6006888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_LIST: 6016888a9beSDag-Erling Smørgrav for (i = 0; i < contig; i++) 6026888a9beSDag-Erling Smørgrav buffer_put_int64(§, rs->lo + i); 6036888a9beSDag-Erling Smørgrav break; 6046888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_RANGE: 6056888a9beSDag-Erling Smørgrav buffer_put_int64(§, rs->lo); 6066888a9beSDag-Erling Smørgrav buffer_put_int64(§, rs->hi); 6076888a9beSDag-Erling Smørgrav break; 6086888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_BITMAP: 6096888a9beSDag-Erling Smørgrav if (rs->lo - bitmap_start > INT_MAX) { 6106888a9beSDag-Erling Smørgrav error("%s: insane bitmap gap", __func__); 6116888a9beSDag-Erling Smørgrav goto out; 6126888a9beSDag-Erling Smørgrav } 6136888a9beSDag-Erling Smørgrav for (i = 0; i < contig; i++) { 6146888a9beSDag-Erling Smørgrav if (BN_set_bit(bitmap, 6156888a9beSDag-Erling Smørgrav rs->lo + i - bitmap_start) != 1) 6166888a9beSDag-Erling Smørgrav goto out; 6176888a9beSDag-Erling Smørgrav } 6186888a9beSDag-Erling Smørgrav break; 6196888a9beSDag-Erling Smørgrav } 6206888a9beSDag-Erling Smørgrav last = rs->hi; 6216888a9beSDag-Erling Smørgrav } 6226888a9beSDag-Erling Smørgrav /* Flush the remaining section, if any */ 6236888a9beSDag-Erling Smørgrav if (state != 0) { 6246888a9beSDag-Erling Smørgrav debug3("%s: serial final flush for state 0x%02x", 6256888a9beSDag-Erling Smørgrav __func__, state); 6266888a9beSDag-Erling Smørgrav switch (state) { 6276888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_LIST: 6286888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_RANGE: 6296888a9beSDag-Erling Smørgrav break; 6306888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_BITMAP: 6316888a9beSDag-Erling Smørgrav buffer_put_bignum2(§, bitmap); 6326888a9beSDag-Erling Smørgrav BN_free(bitmap); 6336888a9beSDag-Erling Smørgrav bitmap = NULL; 6346888a9beSDag-Erling Smørgrav break; 6356888a9beSDag-Erling Smørgrav } 6366888a9beSDag-Erling Smørgrav buffer_put_char(buf, state); 6376888a9beSDag-Erling Smørgrav buffer_put_string(buf, 6386888a9beSDag-Erling Smørgrav buffer_ptr(§), buffer_len(§)); 6396888a9beSDag-Erling Smørgrav } 6406888a9beSDag-Erling Smørgrav debug3("%s: serial done ", __func__); 6416888a9beSDag-Erling Smørgrav 6426888a9beSDag-Erling Smørgrav /* Now output a section for any revocations by key ID */ 6436888a9beSDag-Erling Smørgrav buffer_clear(§); 6446888a9beSDag-Erling Smørgrav RB_FOREACH(rki, revoked_key_id_tree, &rc->revoked_key_ids) { 6456888a9beSDag-Erling Smørgrav debug3("%s: key ID %s", __func__, rki->key_id); 6466888a9beSDag-Erling Smørgrav buffer_put_cstring(§, rki->key_id); 6476888a9beSDag-Erling Smørgrav } 6486888a9beSDag-Erling Smørgrav if (buffer_len(§) != 0) { 6496888a9beSDag-Erling Smørgrav buffer_put_char(buf, KRL_SECTION_CERT_KEY_ID); 6506888a9beSDag-Erling Smørgrav buffer_put_string(buf, buffer_ptr(§), 6516888a9beSDag-Erling Smørgrav buffer_len(§)); 6526888a9beSDag-Erling Smørgrav } 6536888a9beSDag-Erling Smørgrav r = 0; 6546888a9beSDag-Erling Smørgrav out: 6556888a9beSDag-Erling Smørgrav if (bitmap != NULL) 6566888a9beSDag-Erling Smørgrav BN_free(bitmap); 6576888a9beSDag-Erling Smørgrav buffer_free(§); 6586888a9beSDag-Erling Smørgrav return r; 6596888a9beSDag-Erling Smørgrav } 6606888a9beSDag-Erling Smørgrav 6616888a9beSDag-Erling Smørgrav int 6626888a9beSDag-Erling Smørgrav ssh_krl_to_blob(struct ssh_krl *krl, Buffer *buf, const Key **sign_keys, 6636888a9beSDag-Erling Smørgrav u_int nsign_keys) 6646888a9beSDag-Erling Smørgrav { 6656888a9beSDag-Erling Smørgrav int r = -1; 6666888a9beSDag-Erling Smørgrav struct revoked_certs *rc; 6676888a9beSDag-Erling Smørgrav struct revoked_blob *rb; 6686888a9beSDag-Erling Smørgrav Buffer sect; 6696888a9beSDag-Erling Smørgrav u_char *kblob = NULL, *sblob = NULL; 6706888a9beSDag-Erling Smørgrav u_int klen, slen, i; 6716888a9beSDag-Erling Smørgrav 6726888a9beSDag-Erling Smørgrav if (krl->generated_date == 0) 6736888a9beSDag-Erling Smørgrav krl->generated_date = time(NULL); 6746888a9beSDag-Erling Smørgrav 6756888a9beSDag-Erling Smørgrav buffer_init(§); 6766888a9beSDag-Erling Smørgrav 6776888a9beSDag-Erling Smørgrav /* Store the header */ 6786888a9beSDag-Erling Smørgrav buffer_append(buf, KRL_MAGIC, sizeof(KRL_MAGIC) - 1); 6796888a9beSDag-Erling Smørgrav buffer_put_int(buf, KRL_FORMAT_VERSION); 6806888a9beSDag-Erling Smørgrav buffer_put_int64(buf, krl->krl_version); 6816888a9beSDag-Erling Smørgrav buffer_put_int64(buf, krl->generated_date); 6826888a9beSDag-Erling Smørgrav buffer_put_int64(buf, krl->flags); 6836888a9beSDag-Erling Smørgrav buffer_put_string(buf, NULL, 0); 6846888a9beSDag-Erling Smørgrav buffer_put_cstring(buf, krl->comment ? krl->comment : ""); 6856888a9beSDag-Erling Smørgrav 6866888a9beSDag-Erling Smørgrav /* Store sections for revoked certificates */ 6876888a9beSDag-Erling Smørgrav TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { 6886888a9beSDag-Erling Smørgrav if (revoked_certs_generate(rc, §) != 0) 6896888a9beSDag-Erling Smørgrav goto out; 6906888a9beSDag-Erling Smørgrav buffer_put_char(buf, KRL_SECTION_CERTIFICATES); 6916888a9beSDag-Erling Smørgrav buffer_put_string(buf, buffer_ptr(§), 6926888a9beSDag-Erling Smørgrav buffer_len(§)); 6936888a9beSDag-Erling Smørgrav } 6946888a9beSDag-Erling Smørgrav 6956888a9beSDag-Erling Smørgrav /* Finally, output sections for revocations by public key/hash */ 6966888a9beSDag-Erling Smørgrav buffer_clear(§); 6976888a9beSDag-Erling Smørgrav RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) { 6986888a9beSDag-Erling Smørgrav debug3("%s: key len %u ", __func__, rb->len); 6996888a9beSDag-Erling Smørgrav buffer_put_string(§, rb->blob, rb->len); 7006888a9beSDag-Erling Smørgrav } 7016888a9beSDag-Erling Smørgrav if (buffer_len(§) != 0) { 7026888a9beSDag-Erling Smørgrav buffer_put_char(buf, KRL_SECTION_EXPLICIT_KEY); 7036888a9beSDag-Erling Smørgrav buffer_put_string(buf, buffer_ptr(§), 7046888a9beSDag-Erling Smørgrav buffer_len(§)); 7056888a9beSDag-Erling Smørgrav } 7066888a9beSDag-Erling Smørgrav buffer_clear(§); 7076888a9beSDag-Erling Smørgrav RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) { 7086888a9beSDag-Erling Smørgrav debug3("%s: hash len %u ", __func__, rb->len); 7096888a9beSDag-Erling Smørgrav buffer_put_string(§, rb->blob, rb->len); 7106888a9beSDag-Erling Smørgrav } 7116888a9beSDag-Erling Smørgrav if (buffer_len(§) != 0) { 7126888a9beSDag-Erling Smørgrav buffer_put_char(buf, KRL_SECTION_FINGERPRINT_SHA1); 7136888a9beSDag-Erling Smørgrav buffer_put_string(buf, buffer_ptr(§), 7146888a9beSDag-Erling Smørgrav buffer_len(§)); 7156888a9beSDag-Erling Smørgrav } 7166888a9beSDag-Erling Smørgrav 7176888a9beSDag-Erling Smørgrav for (i = 0; i < nsign_keys; i++) { 7186888a9beSDag-Erling Smørgrav if (key_to_blob(sign_keys[i], &kblob, &klen) == 0) 7196888a9beSDag-Erling Smørgrav goto out; 7206888a9beSDag-Erling Smørgrav 7216888a9beSDag-Erling Smørgrav debug3("%s: signature key len %u", __func__, klen); 7226888a9beSDag-Erling Smørgrav buffer_put_char(buf, KRL_SECTION_SIGNATURE); 7236888a9beSDag-Erling Smørgrav buffer_put_string(buf, kblob, klen); 7246888a9beSDag-Erling Smørgrav 7256888a9beSDag-Erling Smørgrav if (key_sign(sign_keys[i], &sblob, &slen, 7266888a9beSDag-Erling Smørgrav buffer_ptr(buf), buffer_len(buf)) == -1) 7276888a9beSDag-Erling Smørgrav goto out; 7286888a9beSDag-Erling Smørgrav debug3("%s: signature sig len %u", __func__, slen); 7296888a9beSDag-Erling Smørgrav buffer_put_string(buf, sblob, slen); 7306888a9beSDag-Erling Smørgrav } 7316888a9beSDag-Erling Smørgrav 7326888a9beSDag-Erling Smørgrav r = 0; 7336888a9beSDag-Erling Smørgrav out: 7346888a9beSDag-Erling Smørgrav free(kblob); 7356888a9beSDag-Erling Smørgrav free(sblob); 7366888a9beSDag-Erling Smørgrav buffer_free(§); 7376888a9beSDag-Erling Smørgrav return r; 7386888a9beSDag-Erling Smørgrav } 7396888a9beSDag-Erling Smørgrav 7406888a9beSDag-Erling Smørgrav static void 7416888a9beSDag-Erling Smørgrav format_timestamp(u_int64_t timestamp, char *ts, size_t nts) 7426888a9beSDag-Erling Smørgrav { 7436888a9beSDag-Erling Smørgrav time_t t; 7446888a9beSDag-Erling Smørgrav struct tm *tm; 7456888a9beSDag-Erling Smørgrav 7466888a9beSDag-Erling Smørgrav t = timestamp; 7476888a9beSDag-Erling Smørgrav tm = localtime(&t); 7486888a9beSDag-Erling Smørgrav *ts = '\0'; 7496888a9beSDag-Erling Smørgrav strftime(ts, nts, "%Y%m%dT%H%M%S", tm); 7506888a9beSDag-Erling Smørgrav } 7516888a9beSDag-Erling Smørgrav 7526888a9beSDag-Erling Smørgrav static int 7536888a9beSDag-Erling Smørgrav parse_revoked_certs(Buffer *buf, struct ssh_krl *krl) 7546888a9beSDag-Erling Smørgrav { 7556888a9beSDag-Erling Smørgrav int ret = -1, nbits; 7566888a9beSDag-Erling Smørgrav u_char type, *blob; 7576888a9beSDag-Erling Smørgrav u_int blen; 7586888a9beSDag-Erling Smørgrav Buffer subsect; 7596888a9beSDag-Erling Smørgrav u_int64_t serial, serial_lo, serial_hi; 7606888a9beSDag-Erling Smørgrav BIGNUM *bitmap = NULL; 7616888a9beSDag-Erling Smørgrav char *key_id = NULL; 7626888a9beSDag-Erling Smørgrav Key *ca_key = NULL; 7636888a9beSDag-Erling Smørgrav 7646888a9beSDag-Erling Smørgrav buffer_init(&subsect); 7656888a9beSDag-Erling Smørgrav 7666888a9beSDag-Erling Smørgrav if ((blob = buffer_get_string_ptr_ret(buf, &blen)) == NULL || 7676888a9beSDag-Erling Smørgrav buffer_get_string_ptr_ret(buf, NULL) == NULL) { /* reserved */ 7686888a9beSDag-Erling Smørgrav error("%s: buffer error", __func__); 7696888a9beSDag-Erling Smørgrav goto out; 7706888a9beSDag-Erling Smørgrav } 7716888a9beSDag-Erling Smørgrav if ((ca_key = key_from_blob(blob, blen)) == NULL) 7726888a9beSDag-Erling Smørgrav goto out; 7736888a9beSDag-Erling Smørgrav 7746888a9beSDag-Erling Smørgrav while (buffer_len(buf) > 0) { 7756888a9beSDag-Erling Smørgrav if (buffer_get_char_ret(&type, buf) != 0 || 7766888a9beSDag-Erling Smørgrav (blob = buffer_get_string_ptr_ret(buf, &blen)) == NULL) { 7776888a9beSDag-Erling Smørgrav error("%s: buffer error", __func__); 7786888a9beSDag-Erling Smørgrav goto out; 7796888a9beSDag-Erling Smørgrav } 7806888a9beSDag-Erling Smørgrav buffer_clear(&subsect); 7816888a9beSDag-Erling Smørgrav buffer_append(&subsect, blob, blen); 7826888a9beSDag-Erling Smørgrav debug3("%s: subsection type 0x%02x", __func__, type); 7836888a9beSDag-Erling Smørgrav /* buffer_dump(&subsect); */ 7846888a9beSDag-Erling Smørgrav 7856888a9beSDag-Erling Smørgrav switch (type) { 7866888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_LIST: 7876888a9beSDag-Erling Smørgrav while (buffer_len(&subsect) > 0) { 7886888a9beSDag-Erling Smørgrav if (buffer_get_int64_ret(&serial, 7896888a9beSDag-Erling Smørgrav &subsect) != 0) { 7906888a9beSDag-Erling Smørgrav error("%s: buffer error", __func__); 7916888a9beSDag-Erling Smørgrav goto out; 7926888a9beSDag-Erling Smørgrav } 7936888a9beSDag-Erling Smørgrav if (ssh_krl_revoke_cert_by_serial(krl, ca_key, 7946888a9beSDag-Erling Smørgrav serial) != 0) { 7956888a9beSDag-Erling Smørgrav error("%s: update failed", __func__); 7966888a9beSDag-Erling Smørgrav goto out; 7976888a9beSDag-Erling Smørgrav } 7986888a9beSDag-Erling Smørgrav } 7996888a9beSDag-Erling Smørgrav break; 8006888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_RANGE: 8016888a9beSDag-Erling Smørgrav if (buffer_get_int64_ret(&serial_lo, &subsect) != 0 || 8026888a9beSDag-Erling Smørgrav buffer_get_int64_ret(&serial_hi, &subsect) != 0) { 8036888a9beSDag-Erling Smørgrav error("%s: buffer error", __func__); 8046888a9beSDag-Erling Smørgrav goto out; 8056888a9beSDag-Erling Smørgrav } 8066888a9beSDag-Erling Smørgrav if (ssh_krl_revoke_cert_by_serial_range(krl, ca_key, 8076888a9beSDag-Erling Smørgrav serial_lo, serial_hi) != 0) { 8086888a9beSDag-Erling Smørgrav error("%s: update failed", __func__); 8096888a9beSDag-Erling Smørgrav goto out; 8106888a9beSDag-Erling Smørgrav } 8116888a9beSDag-Erling Smørgrav break; 8126888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_SERIAL_BITMAP: 8136888a9beSDag-Erling Smørgrav if ((bitmap = BN_new()) == NULL) { 8146888a9beSDag-Erling Smørgrav error("%s: BN_new", __func__); 8156888a9beSDag-Erling Smørgrav goto out; 8166888a9beSDag-Erling Smørgrav } 8176888a9beSDag-Erling Smørgrav if (buffer_get_int64_ret(&serial_lo, &subsect) != 0 || 8186888a9beSDag-Erling Smørgrav buffer_get_bignum2_ret(&subsect, bitmap) != 0) { 8196888a9beSDag-Erling Smørgrav error("%s: buffer error", __func__); 8206888a9beSDag-Erling Smørgrav goto out; 8216888a9beSDag-Erling Smørgrav } 8226888a9beSDag-Erling Smørgrav if ((nbits = BN_num_bits(bitmap)) < 0) { 8236888a9beSDag-Erling Smørgrav error("%s: bitmap bits < 0", __func__); 8246888a9beSDag-Erling Smørgrav goto out; 8256888a9beSDag-Erling Smørgrav } 8266888a9beSDag-Erling Smørgrav for (serial = 0; serial < (u_int)nbits; serial++) { 8276888a9beSDag-Erling Smørgrav if (serial > 0 && serial_lo + serial == 0) { 8286888a9beSDag-Erling Smørgrav error("%s: bitmap wraps u64", __func__); 8296888a9beSDag-Erling Smørgrav goto out; 8306888a9beSDag-Erling Smørgrav } 8316888a9beSDag-Erling Smørgrav if (!BN_is_bit_set(bitmap, serial)) 8326888a9beSDag-Erling Smørgrav continue; 8336888a9beSDag-Erling Smørgrav if (ssh_krl_revoke_cert_by_serial(krl, ca_key, 8346888a9beSDag-Erling Smørgrav serial_lo + serial) != 0) { 8356888a9beSDag-Erling Smørgrav error("%s: update failed", __func__); 8366888a9beSDag-Erling Smørgrav goto out; 8376888a9beSDag-Erling Smørgrav } 8386888a9beSDag-Erling Smørgrav } 8396888a9beSDag-Erling Smørgrav BN_free(bitmap); 8406888a9beSDag-Erling Smørgrav bitmap = NULL; 8416888a9beSDag-Erling Smørgrav break; 8426888a9beSDag-Erling Smørgrav case KRL_SECTION_CERT_KEY_ID: 8436888a9beSDag-Erling Smørgrav while (buffer_len(&subsect) > 0) { 8446888a9beSDag-Erling Smørgrav if ((key_id = buffer_get_cstring_ret(&subsect, 8456888a9beSDag-Erling Smørgrav NULL)) == NULL) { 8466888a9beSDag-Erling Smørgrav error("%s: buffer error", __func__); 8476888a9beSDag-Erling Smørgrav goto out; 8486888a9beSDag-Erling Smørgrav } 8496888a9beSDag-Erling Smørgrav if (ssh_krl_revoke_cert_by_key_id(krl, ca_key, 8506888a9beSDag-Erling Smørgrav key_id) != 0) { 8516888a9beSDag-Erling Smørgrav error("%s: update failed", __func__); 8526888a9beSDag-Erling Smørgrav goto out; 8536888a9beSDag-Erling Smørgrav } 8546888a9beSDag-Erling Smørgrav free(key_id); 8556888a9beSDag-Erling Smørgrav key_id = NULL; 8566888a9beSDag-Erling Smørgrav } 8576888a9beSDag-Erling Smørgrav break; 8586888a9beSDag-Erling Smørgrav default: 8596888a9beSDag-Erling Smørgrav error("Unsupported KRL certificate section %u", type); 8606888a9beSDag-Erling Smørgrav goto out; 8616888a9beSDag-Erling Smørgrav } 8626888a9beSDag-Erling Smørgrav if (buffer_len(&subsect) > 0) { 8636888a9beSDag-Erling Smørgrav error("KRL certificate section contains unparsed data"); 8646888a9beSDag-Erling Smørgrav goto out; 8656888a9beSDag-Erling Smørgrav } 8666888a9beSDag-Erling Smørgrav } 8676888a9beSDag-Erling Smørgrav 8686888a9beSDag-Erling Smørgrav ret = 0; 8696888a9beSDag-Erling Smørgrav out: 8706888a9beSDag-Erling Smørgrav if (ca_key != NULL) 8716888a9beSDag-Erling Smørgrav key_free(ca_key); 8726888a9beSDag-Erling Smørgrav if (bitmap != NULL) 8736888a9beSDag-Erling Smørgrav BN_free(bitmap); 8746888a9beSDag-Erling Smørgrav free(key_id); 8756888a9beSDag-Erling Smørgrav buffer_free(&subsect); 8766888a9beSDag-Erling Smørgrav return ret; 8776888a9beSDag-Erling Smørgrav } 8786888a9beSDag-Erling Smørgrav 8796888a9beSDag-Erling Smørgrav 8806888a9beSDag-Erling Smørgrav /* Attempt to parse a KRL, checking its signature (if any) with sign_ca_keys. */ 8816888a9beSDag-Erling Smørgrav int 8826888a9beSDag-Erling Smørgrav ssh_krl_from_blob(Buffer *buf, struct ssh_krl **krlp, 8836888a9beSDag-Erling Smørgrav const Key **sign_ca_keys, u_int nsign_ca_keys) 8846888a9beSDag-Erling Smørgrav { 8856888a9beSDag-Erling Smørgrav Buffer copy, sect; 8866888a9beSDag-Erling Smørgrav struct ssh_krl *krl; 8876888a9beSDag-Erling Smørgrav char timestamp[64]; 8886888a9beSDag-Erling Smørgrav int ret = -1, r, sig_seen; 8896888a9beSDag-Erling Smørgrav Key *key = NULL, **ca_used = NULL; 890*e4a9863fSDag-Erling Smørgrav u_char type, *blob, *rdata = NULL; 891*e4a9863fSDag-Erling Smørgrav u_int i, j, sig_off, sects_off, rlen, blen, format_version, nca_used; 8926888a9beSDag-Erling Smørgrav 893*e4a9863fSDag-Erling Smørgrav nca_used = 0; 8946888a9beSDag-Erling Smørgrav *krlp = NULL; 8956888a9beSDag-Erling Smørgrav if (buffer_len(buf) < sizeof(KRL_MAGIC) - 1 || 8966888a9beSDag-Erling Smørgrav memcmp(buffer_ptr(buf), KRL_MAGIC, sizeof(KRL_MAGIC) - 1) != 0) { 8976888a9beSDag-Erling Smørgrav debug3("%s: not a KRL", __func__); 8986888a9beSDag-Erling Smørgrav /* 8996888a9beSDag-Erling Smørgrav * Return success but a NULL *krlp here to signal that the 9006888a9beSDag-Erling Smørgrav * file might be a simple list of keys. 9016888a9beSDag-Erling Smørgrav */ 9026888a9beSDag-Erling Smørgrav return 0; 9036888a9beSDag-Erling Smørgrav } 9046888a9beSDag-Erling Smørgrav 9056888a9beSDag-Erling Smørgrav /* Take a copy of the KRL buffer so we can verify its signature later */ 9066888a9beSDag-Erling Smørgrav buffer_init(©); 9076888a9beSDag-Erling Smørgrav buffer_append(©, buffer_ptr(buf), buffer_len(buf)); 9086888a9beSDag-Erling Smørgrav 9096888a9beSDag-Erling Smørgrav buffer_init(§); 9106888a9beSDag-Erling Smørgrav buffer_consume(©, sizeof(KRL_MAGIC) - 1); 9116888a9beSDag-Erling Smørgrav 9126888a9beSDag-Erling Smørgrav if ((krl = ssh_krl_init()) == NULL) { 9136888a9beSDag-Erling Smørgrav error("%s: alloc failed", __func__); 9146888a9beSDag-Erling Smørgrav goto out; 9156888a9beSDag-Erling Smørgrav } 9166888a9beSDag-Erling Smørgrav 9176888a9beSDag-Erling Smørgrav if (buffer_get_int_ret(&format_version, ©) != 0) { 9186888a9beSDag-Erling Smørgrav error("%s: KRL truncated", __func__); 9196888a9beSDag-Erling Smørgrav goto out; 9206888a9beSDag-Erling Smørgrav } 9216888a9beSDag-Erling Smørgrav if (format_version != KRL_FORMAT_VERSION) { 9226888a9beSDag-Erling Smørgrav error("%s: KRL unsupported format version %u", 9236888a9beSDag-Erling Smørgrav __func__, format_version); 9246888a9beSDag-Erling Smørgrav goto out; 9256888a9beSDag-Erling Smørgrav } 9266888a9beSDag-Erling Smørgrav if (buffer_get_int64_ret(&krl->krl_version, ©) != 0 || 9276888a9beSDag-Erling Smørgrav buffer_get_int64_ret(&krl->generated_date, ©) != 0 || 9286888a9beSDag-Erling Smørgrav buffer_get_int64_ret(&krl->flags, ©) != 0 || 9296888a9beSDag-Erling Smørgrav buffer_get_string_ptr_ret(©, NULL) == NULL || /* reserved */ 9306888a9beSDag-Erling Smørgrav (krl->comment = buffer_get_cstring_ret(©, NULL)) == NULL) { 9316888a9beSDag-Erling Smørgrav error("%s: buffer error", __func__); 9326888a9beSDag-Erling Smørgrav goto out; 9336888a9beSDag-Erling Smørgrav } 9346888a9beSDag-Erling Smørgrav 9356888a9beSDag-Erling Smørgrav format_timestamp(krl->generated_date, timestamp, sizeof(timestamp)); 93659928918SDag-Erling Smørgrav debug("KRL version %llu generated at %s%s%s", 937*e4a9863fSDag-Erling Smørgrav (long long unsigned)krl->krl_version, timestamp, 93859928918SDag-Erling Smørgrav *krl->comment ? ": " : "", krl->comment); 9396888a9beSDag-Erling Smørgrav 9406888a9beSDag-Erling Smørgrav /* 9416888a9beSDag-Erling Smørgrav * 1st pass: verify signatures, if any. This is done to avoid 9426888a9beSDag-Erling Smørgrav * detailed parsing of data whose provenance is unverified. 9436888a9beSDag-Erling Smørgrav */ 9446888a9beSDag-Erling Smørgrav sig_seen = 0; 9456888a9beSDag-Erling Smørgrav sects_off = buffer_len(buf) - buffer_len(©); 9466888a9beSDag-Erling Smørgrav while (buffer_len(©) > 0) { 9476888a9beSDag-Erling Smørgrav if (buffer_get_char_ret(&type, ©) != 0 || 9486888a9beSDag-Erling Smørgrav (blob = buffer_get_string_ptr_ret(©, &blen)) == NULL) { 9496888a9beSDag-Erling Smørgrav error("%s: buffer error", __func__); 9506888a9beSDag-Erling Smørgrav goto out; 9516888a9beSDag-Erling Smørgrav } 9526888a9beSDag-Erling Smørgrav debug3("%s: first pass, section 0x%02x", __func__, type); 9536888a9beSDag-Erling Smørgrav if (type != KRL_SECTION_SIGNATURE) { 9546888a9beSDag-Erling Smørgrav if (sig_seen) { 9556888a9beSDag-Erling Smørgrav error("KRL contains non-signature section " 9566888a9beSDag-Erling Smørgrav "after signature"); 9576888a9beSDag-Erling Smørgrav goto out; 9586888a9beSDag-Erling Smørgrav } 9596888a9beSDag-Erling Smørgrav /* Not interested for now. */ 9606888a9beSDag-Erling Smørgrav continue; 9616888a9beSDag-Erling Smørgrav } 9626888a9beSDag-Erling Smørgrav sig_seen = 1; 9636888a9beSDag-Erling Smørgrav /* First string component is the signing key */ 9646888a9beSDag-Erling Smørgrav if ((key = key_from_blob(blob, blen)) == NULL) { 9656888a9beSDag-Erling Smørgrav error("%s: invalid signature key", __func__); 9666888a9beSDag-Erling Smørgrav goto out; 9676888a9beSDag-Erling Smørgrav } 9686888a9beSDag-Erling Smørgrav sig_off = buffer_len(buf) - buffer_len(©); 9696888a9beSDag-Erling Smørgrav /* Second string component is the signature itself */ 9706888a9beSDag-Erling Smørgrav if ((blob = buffer_get_string_ptr_ret(©, &blen)) == NULL) { 9716888a9beSDag-Erling Smørgrav error("%s: buffer error", __func__); 9726888a9beSDag-Erling Smørgrav goto out; 9736888a9beSDag-Erling Smørgrav } 9746888a9beSDag-Erling Smørgrav /* Check signature over entire KRL up to this point */ 9756888a9beSDag-Erling Smørgrav if (key_verify(key, blob, blen, 976*e4a9863fSDag-Erling Smørgrav buffer_ptr(buf), buffer_len(buf) - sig_off) != 1) { 9776888a9beSDag-Erling Smørgrav error("bad signaure on KRL"); 9786888a9beSDag-Erling Smørgrav goto out; 9796888a9beSDag-Erling Smørgrav } 9806888a9beSDag-Erling Smørgrav /* Check if this key has already signed this KRL */ 9816888a9beSDag-Erling Smørgrav for (i = 0; i < nca_used; i++) { 9826888a9beSDag-Erling Smørgrav if (key_equal(ca_used[i], key)) { 9836888a9beSDag-Erling Smørgrav error("KRL signed more than once with " 9846888a9beSDag-Erling Smørgrav "the same key"); 9856888a9beSDag-Erling Smørgrav goto out; 9866888a9beSDag-Erling Smørgrav } 9876888a9beSDag-Erling Smørgrav } 9886888a9beSDag-Erling Smørgrav /* Record keys used to sign the KRL */ 9896888a9beSDag-Erling Smørgrav ca_used = xrealloc(ca_used, nca_used + 1, sizeof(*ca_used)); 9906888a9beSDag-Erling Smørgrav ca_used[nca_used++] = key; 9916888a9beSDag-Erling Smørgrav key = NULL; 9926888a9beSDag-Erling Smørgrav break; 9936888a9beSDag-Erling Smørgrav } 9946888a9beSDag-Erling Smørgrav 9956888a9beSDag-Erling Smørgrav /* 9966888a9beSDag-Erling Smørgrav * 2nd pass: parse and load the KRL, skipping the header to the point 9976888a9beSDag-Erling Smørgrav * where the section start. 9986888a9beSDag-Erling Smørgrav */ 9996888a9beSDag-Erling Smørgrav buffer_append(©, (u_char*)buffer_ptr(buf) + sects_off, 10006888a9beSDag-Erling Smørgrav buffer_len(buf) - sects_off); 10016888a9beSDag-Erling Smørgrav while (buffer_len(©) > 0) { 10026888a9beSDag-Erling Smørgrav if (buffer_get_char_ret(&type, ©) != 0 || 10036888a9beSDag-Erling Smørgrav (blob = buffer_get_string_ptr_ret(©, &blen)) == NULL) { 10046888a9beSDag-Erling Smørgrav error("%s: buffer error", __func__); 10056888a9beSDag-Erling Smørgrav goto out; 10066888a9beSDag-Erling Smørgrav } 10076888a9beSDag-Erling Smørgrav debug3("%s: second pass, section 0x%02x", __func__, type); 10086888a9beSDag-Erling Smørgrav buffer_clear(§); 10096888a9beSDag-Erling Smørgrav buffer_append(§, blob, blen); 10106888a9beSDag-Erling Smørgrav 10116888a9beSDag-Erling Smørgrav switch (type) { 10126888a9beSDag-Erling Smørgrav case KRL_SECTION_CERTIFICATES: 10136888a9beSDag-Erling Smørgrav if ((r = parse_revoked_certs(§, krl)) != 0) 10146888a9beSDag-Erling Smørgrav goto out; 10156888a9beSDag-Erling Smørgrav break; 10166888a9beSDag-Erling Smørgrav case KRL_SECTION_EXPLICIT_KEY: 10176888a9beSDag-Erling Smørgrav case KRL_SECTION_FINGERPRINT_SHA1: 10186888a9beSDag-Erling Smørgrav while (buffer_len(§) > 0) { 1019*e4a9863fSDag-Erling Smørgrav if ((rdata = buffer_get_string_ret(§, 1020*e4a9863fSDag-Erling Smørgrav &rlen)) == NULL) { 10216888a9beSDag-Erling Smørgrav error("%s: buffer error", __func__); 10226888a9beSDag-Erling Smørgrav goto out; 10236888a9beSDag-Erling Smørgrav } 10246888a9beSDag-Erling Smørgrav if (type == KRL_SECTION_FINGERPRINT_SHA1 && 1025*e4a9863fSDag-Erling Smørgrav rlen != 20) { 10266888a9beSDag-Erling Smørgrav error("%s: bad SHA1 length", __func__); 10276888a9beSDag-Erling Smørgrav goto out; 10286888a9beSDag-Erling Smørgrav } 10296888a9beSDag-Erling Smørgrav if (revoke_blob( 10306888a9beSDag-Erling Smørgrav type == KRL_SECTION_EXPLICIT_KEY ? 10316888a9beSDag-Erling Smørgrav &krl->revoked_keys : &krl->revoked_sha1s, 1032*e4a9863fSDag-Erling Smørgrav rdata, rlen) != 0) 1033*e4a9863fSDag-Erling Smørgrav goto out; 1034*e4a9863fSDag-Erling Smørgrav rdata = NULL; /* revoke_blob frees blob */ 10356888a9beSDag-Erling Smørgrav } 10366888a9beSDag-Erling Smørgrav break; 10376888a9beSDag-Erling Smørgrav case KRL_SECTION_SIGNATURE: 10386888a9beSDag-Erling Smørgrav /* Handled above, but still need to stay in synch */ 10396888a9beSDag-Erling Smørgrav buffer_clear(§); 10406888a9beSDag-Erling Smørgrav if ((blob = buffer_get_string_ptr_ret(©, 10416888a9beSDag-Erling Smørgrav &blen)) == NULL) { 10426888a9beSDag-Erling Smørgrav error("%s: buffer error", __func__); 10436888a9beSDag-Erling Smørgrav goto out; 10446888a9beSDag-Erling Smørgrav } 10456888a9beSDag-Erling Smørgrav break; 10466888a9beSDag-Erling Smørgrav default: 10476888a9beSDag-Erling Smørgrav error("Unsupported KRL section %u", type); 10486888a9beSDag-Erling Smørgrav goto out; 10496888a9beSDag-Erling Smørgrav } 10506888a9beSDag-Erling Smørgrav if (buffer_len(§) > 0) { 10516888a9beSDag-Erling Smørgrav error("KRL section contains unparsed data"); 10526888a9beSDag-Erling Smørgrav goto out; 10536888a9beSDag-Erling Smørgrav } 10546888a9beSDag-Erling Smørgrav } 10556888a9beSDag-Erling Smørgrav 10566888a9beSDag-Erling Smørgrav /* Check that the key(s) used to sign the KRL weren't revoked */ 10576888a9beSDag-Erling Smørgrav sig_seen = 0; 10586888a9beSDag-Erling Smørgrav for (i = 0; i < nca_used; i++) { 10596888a9beSDag-Erling Smørgrav if (ssh_krl_check_key(krl, ca_used[i]) == 0) 10606888a9beSDag-Erling Smørgrav sig_seen = 1; 10616888a9beSDag-Erling Smørgrav else { 10626888a9beSDag-Erling Smørgrav key_free(ca_used[i]); 10636888a9beSDag-Erling Smørgrav ca_used[i] = NULL; 10646888a9beSDag-Erling Smørgrav } 10656888a9beSDag-Erling Smørgrav } 10666888a9beSDag-Erling Smørgrav if (nca_used && !sig_seen) { 10676888a9beSDag-Erling Smørgrav error("All keys used to sign KRL were revoked"); 10686888a9beSDag-Erling Smørgrav goto out; 10696888a9beSDag-Erling Smørgrav } 10706888a9beSDag-Erling Smørgrav 10716888a9beSDag-Erling Smørgrav /* If we have CA keys, then verify that one was used to sign the KRL */ 10726888a9beSDag-Erling Smørgrav if (sig_seen && nsign_ca_keys != 0) { 10736888a9beSDag-Erling Smørgrav sig_seen = 0; 10746888a9beSDag-Erling Smørgrav for (i = 0; !sig_seen && i < nsign_ca_keys; i++) { 10756888a9beSDag-Erling Smørgrav for (j = 0; j < nca_used; j++) { 10766888a9beSDag-Erling Smørgrav if (ca_used[j] == NULL) 10776888a9beSDag-Erling Smørgrav continue; 10786888a9beSDag-Erling Smørgrav if (key_equal(ca_used[j], sign_ca_keys[i])) { 10796888a9beSDag-Erling Smørgrav sig_seen = 1; 10806888a9beSDag-Erling Smørgrav break; 10816888a9beSDag-Erling Smørgrav } 10826888a9beSDag-Erling Smørgrav } 10836888a9beSDag-Erling Smørgrav } 10846888a9beSDag-Erling Smørgrav if (!sig_seen) { 10856888a9beSDag-Erling Smørgrav error("KRL not signed with any trusted key"); 10866888a9beSDag-Erling Smørgrav goto out; 10876888a9beSDag-Erling Smørgrav } 10886888a9beSDag-Erling Smørgrav } 10896888a9beSDag-Erling Smørgrav 10906888a9beSDag-Erling Smørgrav *krlp = krl; 10916888a9beSDag-Erling Smørgrav ret = 0; 10926888a9beSDag-Erling Smørgrav out: 10936888a9beSDag-Erling Smørgrav if (ret != 0) 10946888a9beSDag-Erling Smørgrav ssh_krl_free(krl); 10956888a9beSDag-Erling Smørgrav for (i = 0; i < nca_used; i++) { 10966888a9beSDag-Erling Smørgrav if (ca_used[i] != NULL) 10976888a9beSDag-Erling Smørgrav key_free(ca_used[i]); 10986888a9beSDag-Erling Smørgrav } 10996888a9beSDag-Erling Smørgrav free(ca_used); 1100*e4a9863fSDag-Erling Smørgrav free(rdata); 11016888a9beSDag-Erling Smørgrav if (key != NULL) 11026888a9beSDag-Erling Smørgrav key_free(key); 11036888a9beSDag-Erling Smørgrav buffer_free(©); 11046888a9beSDag-Erling Smørgrav buffer_free(§); 11056888a9beSDag-Erling Smørgrav return ret; 11066888a9beSDag-Erling Smørgrav } 11076888a9beSDag-Erling Smørgrav 11086888a9beSDag-Erling Smørgrav /* Checks whether a given key/cert is revoked. Does not check its CA */ 11096888a9beSDag-Erling Smørgrav static int 11106888a9beSDag-Erling Smørgrav is_key_revoked(struct ssh_krl *krl, const Key *key) 11116888a9beSDag-Erling Smørgrav { 11126888a9beSDag-Erling Smørgrav struct revoked_blob rb, *erb; 11136888a9beSDag-Erling Smørgrav struct revoked_serial rs, *ers; 11146888a9beSDag-Erling Smørgrav struct revoked_key_id rki, *erki; 11156888a9beSDag-Erling Smørgrav struct revoked_certs *rc; 11166888a9beSDag-Erling Smørgrav 11176888a9beSDag-Erling Smørgrav /* Check explicitly revoked hashes first */ 11186888a9beSDag-Erling Smørgrav bzero(&rb, sizeof(rb)); 11196888a9beSDag-Erling Smørgrav if ((rb.blob = key_fingerprint_raw(key, SSH_FP_SHA1, &rb.len)) == NULL) 11206888a9beSDag-Erling Smørgrav return -1; 11216888a9beSDag-Erling Smørgrav erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha1s, &rb); 11226888a9beSDag-Erling Smørgrav free(rb.blob); 11236888a9beSDag-Erling Smørgrav if (erb != NULL) { 11246888a9beSDag-Erling Smørgrav debug("%s: revoked by key SHA1", __func__); 11256888a9beSDag-Erling Smørgrav return -1; 11266888a9beSDag-Erling Smørgrav } 11276888a9beSDag-Erling Smørgrav 11286888a9beSDag-Erling Smørgrav /* Next, explicit keys */ 11296888a9beSDag-Erling Smørgrav bzero(&rb, sizeof(rb)); 11306888a9beSDag-Erling Smørgrav if (plain_key_blob(key, &rb.blob, &rb.len) != 0) 11316888a9beSDag-Erling Smørgrav return -1; 11326888a9beSDag-Erling Smørgrav erb = RB_FIND(revoked_blob_tree, &krl->revoked_keys, &rb); 11336888a9beSDag-Erling Smørgrav free(rb.blob); 11346888a9beSDag-Erling Smørgrav if (erb != NULL) { 11356888a9beSDag-Erling Smørgrav debug("%s: revoked by explicit key", __func__); 11366888a9beSDag-Erling Smørgrav return -1; 11376888a9beSDag-Erling Smørgrav } 11386888a9beSDag-Erling Smørgrav 11396888a9beSDag-Erling Smørgrav if (!key_is_cert(key)) 11406888a9beSDag-Erling Smørgrav return 0; 11416888a9beSDag-Erling Smørgrav 11426888a9beSDag-Erling Smørgrav /* Check cert revocation */ 11436888a9beSDag-Erling Smørgrav if (revoked_certs_for_ca_key(krl, key->cert->signature_key, 11446888a9beSDag-Erling Smørgrav &rc, 0) != 0) 11456888a9beSDag-Erling Smørgrav return -1; 11466888a9beSDag-Erling Smørgrav if (rc == NULL) 11476888a9beSDag-Erling Smørgrav return 0; /* No entry for this CA */ 11486888a9beSDag-Erling Smørgrav 11496888a9beSDag-Erling Smørgrav /* Check revocation by cert key ID */ 11506888a9beSDag-Erling Smørgrav bzero(&rki, sizeof(rki)); 11516888a9beSDag-Erling Smørgrav rki.key_id = key->cert->key_id; 11526888a9beSDag-Erling Smørgrav erki = RB_FIND(revoked_key_id_tree, &rc->revoked_key_ids, &rki); 11536888a9beSDag-Erling Smørgrav if (erki != NULL) { 11546888a9beSDag-Erling Smørgrav debug("%s: revoked by key ID", __func__); 11556888a9beSDag-Erling Smørgrav return -1; 11566888a9beSDag-Erling Smørgrav } 11576888a9beSDag-Erling Smørgrav 11586888a9beSDag-Erling Smørgrav /* 11596888a9beSDag-Erling Smørgrav * Legacy cert formats lack serial numbers. Zero serials numbers 11606888a9beSDag-Erling Smørgrav * are ignored (it's the default when the CA doesn't specify one). 11616888a9beSDag-Erling Smørgrav */ 11626888a9beSDag-Erling Smørgrav if (key_cert_is_legacy(key) || key->cert->serial == 0) 11636888a9beSDag-Erling Smørgrav return 0; 11646888a9beSDag-Erling Smørgrav 11656888a9beSDag-Erling Smørgrav bzero(&rs, sizeof(rs)); 11666888a9beSDag-Erling Smørgrav rs.lo = rs.hi = key->cert->serial; 11676888a9beSDag-Erling Smørgrav ers = RB_FIND(revoked_serial_tree, &rc->revoked_serials, &rs); 11686888a9beSDag-Erling Smørgrav if (ers != NULL) { 11696888a9beSDag-Erling Smørgrav KRL_DBG(("%s: %llu matched %llu:%llu", __func__, 11706888a9beSDag-Erling Smørgrav key->cert->serial, ers->lo, ers->hi)); 11716888a9beSDag-Erling Smørgrav debug("%s: revoked by serial", __func__); 11726888a9beSDag-Erling Smørgrav return -1; 11736888a9beSDag-Erling Smørgrav } 11746888a9beSDag-Erling Smørgrav KRL_DBG(("%s: %llu no match", __func__, key->cert->serial)); 11756888a9beSDag-Erling Smørgrav 11766888a9beSDag-Erling Smørgrav return 0; 11776888a9beSDag-Erling Smørgrav } 11786888a9beSDag-Erling Smørgrav 11796888a9beSDag-Erling Smørgrav int 11806888a9beSDag-Erling Smørgrav ssh_krl_check_key(struct ssh_krl *krl, const Key *key) 11816888a9beSDag-Erling Smørgrav { 11826888a9beSDag-Erling Smørgrav int r; 11836888a9beSDag-Erling Smørgrav 11846888a9beSDag-Erling Smørgrav debug2("%s: checking key", __func__); 11856888a9beSDag-Erling Smørgrav if ((r = is_key_revoked(krl, key)) != 0) 11866888a9beSDag-Erling Smørgrav return r; 11876888a9beSDag-Erling Smørgrav if (key_is_cert(key)) { 11886888a9beSDag-Erling Smørgrav debug2("%s: checking CA key", __func__); 11896888a9beSDag-Erling Smørgrav if ((r = is_key_revoked(krl, key->cert->signature_key)) != 0) 11906888a9beSDag-Erling Smørgrav return r; 11916888a9beSDag-Erling Smørgrav } 11926888a9beSDag-Erling Smørgrav debug3("%s: key okay", __func__); 11936888a9beSDag-Erling Smørgrav return 0; 11946888a9beSDag-Erling Smørgrav } 11956888a9beSDag-Erling Smørgrav 11966888a9beSDag-Erling Smørgrav /* Returns 0 on success, -1 on error or key revoked, -2 if path is not a KRL */ 11976888a9beSDag-Erling Smørgrav int 11986888a9beSDag-Erling Smørgrav ssh_krl_file_contains_key(const char *path, const Key *key) 11996888a9beSDag-Erling Smørgrav { 12006888a9beSDag-Erling Smørgrav Buffer krlbuf; 12016888a9beSDag-Erling Smørgrav struct ssh_krl *krl; 12026888a9beSDag-Erling Smørgrav int revoked, fd; 12036888a9beSDag-Erling Smørgrav 12046888a9beSDag-Erling Smørgrav if (path == NULL) 12056888a9beSDag-Erling Smørgrav return 0; 12066888a9beSDag-Erling Smørgrav 12076888a9beSDag-Erling Smørgrav if ((fd = open(path, O_RDONLY)) == -1) { 12086888a9beSDag-Erling Smørgrav error("open %s: %s", path, strerror(errno)); 12096888a9beSDag-Erling Smørgrav error("Revoked keys file not accessible - refusing public key " 12106888a9beSDag-Erling Smørgrav "authentication"); 12116888a9beSDag-Erling Smørgrav return -1; 12126888a9beSDag-Erling Smørgrav } 12136888a9beSDag-Erling Smørgrav buffer_init(&krlbuf); 12146888a9beSDag-Erling Smørgrav if (!key_load_file(fd, path, &krlbuf)) { 12156888a9beSDag-Erling Smørgrav close(fd); 12166888a9beSDag-Erling Smørgrav buffer_free(&krlbuf); 12176888a9beSDag-Erling Smørgrav error("Revoked keys file not readable - refusing public key " 12186888a9beSDag-Erling Smørgrav "authentication"); 12196888a9beSDag-Erling Smørgrav return -1; 12206888a9beSDag-Erling Smørgrav } 12216888a9beSDag-Erling Smørgrav close(fd); 12226888a9beSDag-Erling Smørgrav if (ssh_krl_from_blob(&krlbuf, &krl, NULL, 0) != 0) { 12236888a9beSDag-Erling Smørgrav buffer_free(&krlbuf); 12246888a9beSDag-Erling Smørgrav error("Invalid KRL, refusing public key " 12256888a9beSDag-Erling Smørgrav "authentication"); 12266888a9beSDag-Erling Smørgrav return -1; 12276888a9beSDag-Erling Smørgrav } 12286888a9beSDag-Erling Smørgrav buffer_free(&krlbuf); 12296888a9beSDag-Erling Smørgrav if (krl == NULL) { 12306888a9beSDag-Erling Smørgrav debug3("%s: %s is not a KRL file", __func__, path); 12316888a9beSDag-Erling Smørgrav return -2; 12326888a9beSDag-Erling Smørgrav } 12336888a9beSDag-Erling Smørgrav debug2("%s: checking KRL %s", __func__, path); 12346888a9beSDag-Erling Smørgrav revoked = ssh_krl_check_key(krl, key) != 0; 12356888a9beSDag-Erling Smørgrav ssh_krl_free(krl); 12366888a9beSDag-Erling Smørgrav return revoked ? -1 : 0; 12376888a9beSDag-Erling Smørgrav } 1238