Home | History | Annotate | Line # | Download | only in dist
      1 /*	$NetBSD: krl.c,v 1.26 2025/10/11 15:45:06 christos Exp $	*/
      2 /* $OpenBSD: krl.c,v 1.62 2025/09/15 04:41:20 djm Exp $ */
      3 
      4 /*
      5  * Copyright (c) 2012 Damien Miller <djm (at) mindrot.org>
      6  *
      7  * Permission to use, copy, modify, and distribute this software for any
      8  * purpose with or without fee is hereby granted, provided that the above
      9  * copyright notice and this permission notice appear in all copies.
     10  *
     11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18  */
     19 
     20 #include "includes.h"
     21 __RCSID("$NetBSD: krl.c,v 1.26 2025/10/11 15:45:06 christos Exp $");
     22 
     23 #include <sys/types.h>
     24 #include <sys/tree.h>
     25 #include <sys/queue.h>
     26 
     27 #include <errno.h>
     28 #include <fcntl.h>
     29 #include <limits.h>
     30 #include <stdlib.h>
     31 #include <string.h>
     32 #include <time.h>
     33 #include <unistd.h>
     34 
     35 #include "sshbuf.h"
     36 #include "ssherr.h"
     37 #include "sshkey.h"
     38 #include "authfile.h"
     39 #include "misc.h"
     40 #include "log.h"
     41 #include "digest.h"
     42 #include "bitmap.h"
     43 #include "utf8.h"
     44 
     45 #include "krl.h"
     46 
     47 /* #define DEBUG_KRL */
     48 #ifdef DEBUG_KRL
     49 # define KRL_DBG(x) debug3_f x
     50 #else
     51 # define KRL_DBG(x)
     52 #endif
     53 
     54 /*
     55  * Trees of revoked serial numbers, key IDs and keys. This allows
     56  * quick searching, querying and producing lists in canonical order.
     57  */
     58 
     59 /* Tree of serial numbers. XXX make smarter: really need a real sparse bitmap */
     60 struct revoked_serial {
     61 	u_int64_t lo, hi;
     62 	RB_ENTRY(revoked_serial) tree_entry;
     63 };
     64 static int serial_cmp(struct revoked_serial *a, struct revoked_serial *b);
     65 RB_HEAD(revoked_serial_tree, revoked_serial);
     66 RB_GENERATE_STATIC(revoked_serial_tree, revoked_serial, tree_entry, serial_cmp)
     67 
     68 /* Tree of key IDs */
     69 struct revoked_key_id {
     70 	char *key_id;
     71 	RB_ENTRY(revoked_key_id) tree_entry;
     72 };
     73 static int key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b);
     74 RB_HEAD(revoked_key_id_tree, revoked_key_id);
     75 RB_GENERATE_STATIC(revoked_key_id_tree, revoked_key_id, tree_entry, key_id_cmp)
     76 
     77 /* Tree of blobs (used for keys and fingerprints) */
     78 struct revoked_blob {
     79 	u_char *blob;
     80 	size_t len;
     81 	RB_ENTRY(revoked_blob) tree_entry;
     82 };
     83 static int blob_cmp(struct revoked_blob *a, struct revoked_blob *b);
     84 RB_HEAD(revoked_blob_tree, revoked_blob);
     85 RB_GENERATE_STATIC(revoked_blob_tree, revoked_blob, tree_entry, blob_cmp)
     86 
     87 /* Tracks revoked certs for a single CA */
     88 struct revoked_certs {
     89 	struct sshkey *ca_key;
     90 	struct revoked_serial_tree revoked_serials;
     91 	struct revoked_key_id_tree revoked_key_ids;
     92 	TAILQ_ENTRY(revoked_certs) entry;
     93 };
     94 TAILQ_HEAD(revoked_certs_list, revoked_certs);
     95 
     96 struct ssh_krl {
     97 	u_int64_t krl_version;
     98 	u_int64_t generated_date;
     99 	u_int64_t flags;
    100 	char *comment;
    101 	struct revoked_blob_tree revoked_keys;
    102 	struct revoked_blob_tree revoked_sha1s;
    103 	struct revoked_blob_tree revoked_sha256s;
    104 	struct revoked_certs_list revoked_certs;
    105 };
    106 
    107 /* Return equal if a and b overlap */
    108 static int
    109 serial_cmp(struct revoked_serial *a, struct revoked_serial *b)
    110 {
    111 	if (a->hi >= b->lo && a->lo <= b->hi)
    112 		return 0;
    113 	return a->lo < b->lo ? -1 : 1;
    114 }
    115 
    116 static int
    117 key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b)
    118 {
    119 	return strcmp(a->key_id, b->key_id);
    120 }
    121 
    122 static int
    123 blob_cmp(struct revoked_blob *a, struct revoked_blob *b)
    124 {
    125 	int r;
    126 
    127 	if (a->len != b->len) {
    128 		if ((r = memcmp(a->blob, b->blob, MINIMUM(a->len, b->len))) != 0)
    129 			return r;
    130 		return a->len > b->len ? 1 : -1;
    131 	} else
    132 		return memcmp(a->blob, b->blob, a->len);
    133 }
    134 
    135 struct ssh_krl *
    136 ssh_krl_init(void)
    137 {
    138 	struct ssh_krl *krl;
    139 
    140 	if ((krl = calloc(1, sizeof(*krl))) == NULL)
    141 		return NULL;
    142 	RB_INIT(&krl->revoked_keys);
    143 	RB_INIT(&krl->revoked_sha1s);
    144 	RB_INIT(&krl->revoked_sha256s);
    145 	TAILQ_INIT(&krl->revoked_certs);
    146 	return krl;
    147 }
    148 
    149 static void
    150 revoked_certs_free(struct revoked_certs *rc)
    151 {
    152 	struct revoked_serial *rs, *trs;
    153 	struct revoked_key_id *rki, *trki;
    154 
    155 	if (rc == NULL)
    156 		return;
    157 	RB_FOREACH_SAFE(rs, revoked_serial_tree, &rc->revoked_serials, trs) {
    158 		RB_REMOVE(revoked_serial_tree, &rc->revoked_serials, rs);
    159 		free(rs);
    160 	}
    161 	RB_FOREACH_SAFE(rki, revoked_key_id_tree, &rc->revoked_key_ids, trki) {
    162 		RB_REMOVE(revoked_key_id_tree, &rc->revoked_key_ids, rki);
    163 		free(rki->key_id);
    164 		free(rki);
    165 	}
    166 	sshkey_free(rc->ca_key);
    167 	freezero(rc, sizeof(*rc));
    168 }
    169 
    170 void
    171 ssh_krl_free(struct ssh_krl *krl)
    172 {
    173 	struct revoked_blob *rb, *trb;
    174 	struct revoked_certs *rc, *trc;
    175 
    176 	if (krl == NULL)
    177 		return;
    178 
    179 	free(krl->comment);
    180 	RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_keys, trb) {
    181 		RB_REMOVE(revoked_blob_tree, &krl->revoked_keys, rb);
    182 		free(rb->blob);
    183 		free(rb);
    184 	}
    185 	RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_sha1s, trb) {
    186 		RB_REMOVE(revoked_blob_tree, &krl->revoked_sha1s, rb);
    187 		free(rb->blob);
    188 		free(rb);
    189 	}
    190 	RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_sha256s, trb) {
    191 		RB_REMOVE(revoked_blob_tree, &krl->revoked_sha256s, rb);
    192 		free(rb->blob);
    193 		free(rb);
    194 	}
    195 	TAILQ_FOREACH_SAFE(rc, &krl->revoked_certs, entry, trc) {
    196 		TAILQ_REMOVE(&krl->revoked_certs, rc, entry);
    197 		revoked_certs_free(rc);
    198 	}
    199 	free(krl);
    200 }
    201 
    202 void
    203 ssh_krl_set_version(struct ssh_krl *krl, u_int64_t version)
    204 {
    205 	krl->krl_version = version;
    206 }
    207 
    208 int
    209 ssh_krl_set_comment(struct ssh_krl *krl, const char *comment)
    210 {
    211 	free(krl->comment);
    212 	if ((krl->comment = strdup(comment)) == NULL)
    213 		return SSH_ERR_ALLOC_FAIL;
    214 	return 0;
    215 }
    216 
    217 /*
    218  * Find the revoked_certs struct for a CA key. If allow_create is set then
    219  * create a new one in the tree if one did not exist already.
    220  */
    221 static int
    222 revoked_certs_for_ca_key(struct ssh_krl *krl, const struct sshkey *ca_key,
    223     struct revoked_certs **rcp, int allow_create)
    224 {
    225 	struct revoked_certs *rc;
    226 	int r;
    227 
    228 	*rcp = NULL;
    229 	TAILQ_FOREACH(rc, &krl->revoked_certs, entry) {
    230 		if ((ca_key == NULL && rc->ca_key == NULL) ||
    231 		    sshkey_equal(rc->ca_key, ca_key)) {
    232 			*rcp = rc;
    233 			return 0;
    234 		}
    235 	}
    236 	if (!allow_create)
    237 		return 0;
    238 	/* If this CA doesn't exist in the list then add it now */
    239 	if ((rc = calloc(1, sizeof(*rc))) == NULL)
    240 		return SSH_ERR_ALLOC_FAIL;
    241 	if (ca_key == NULL)
    242 		rc->ca_key = NULL;
    243 	else if ((r = sshkey_from_private(ca_key, &rc->ca_key)) != 0) {
    244 		free(rc);
    245 		return r;
    246 	}
    247 	RB_INIT(&rc->revoked_serials);
    248 	RB_INIT(&rc->revoked_key_ids);
    249 	TAILQ_INSERT_TAIL(&krl->revoked_certs, rc, entry);
    250 	KRL_DBG(("new CA %s", ca_key == NULL ? "*" : sshkey_type(ca_key)));
    251 	*rcp = rc;
    252 	return 0;
    253 }
    254 
    255 static int
    256 insert_serial_range(struct revoked_serial_tree *rt, u_int64_t lo, u_int64_t hi)
    257 {
    258 	struct revoked_serial rs, *ers, *crs, *irs;
    259 
    260 	KRL_DBG(("insert %"PRIu64":%"PRIu64, lo, hi));
    261 	memset(&rs, 0, sizeof(rs));
    262 	rs.lo = lo;
    263 	rs.hi = hi;
    264 	ers = RB_NFIND(revoked_serial_tree, rt, &rs);
    265 	if (ers == NULL || serial_cmp(ers, &rs) != 0) {
    266 		/* No entry matches. Just insert */
    267 		if ((irs = malloc(sizeof(rs))) == NULL)
    268 			return SSH_ERR_ALLOC_FAIL;
    269 		memcpy(irs, &rs, sizeof(*irs));
    270 		ers = RB_INSERT(revoked_serial_tree, rt, irs);
    271 		if (ers != NULL) {
    272 			KRL_DBG(("bad: ers != NULL"));
    273 			/* Shouldn't happen */
    274 			free(irs);
    275 			return SSH_ERR_INTERNAL_ERROR;
    276 		}
    277 		ers = irs;
    278 	} else {
    279 		KRL_DBG(("overlap found %"PRIu64":%"PRIu64,
    280 		    ers->lo, ers->hi));
    281 		/*
    282 		 * The inserted entry overlaps an existing one. Grow the
    283 		 * existing entry.
    284 		 */
    285 		if (ers->lo > lo)
    286 			ers->lo = lo;
    287 		if (ers->hi < hi)
    288 			ers->hi = hi;
    289 	}
    290 
    291 	/*
    292 	 * The inserted or revised range might overlap or abut adjacent ones;
    293 	 * coalesce as necessary.
    294 	 */
    295 
    296 	/* Check predecessors */
    297 	while ((crs = RB_PREV(revoked_serial_tree, rt, ers)) != NULL) {
    298 		KRL_DBG(("pred %"PRIu64":%"PRIu64,
    299 		    crs->lo, crs->hi));
    300 		if (ers->lo != 0 && crs->hi < ers->lo - 1)
    301 			break;
    302 		/* This entry overlaps. */
    303 		if (crs->lo < ers->lo) {
    304 			ers->lo = crs->lo;
    305 			KRL_DBG(("pred extend %"PRIu64":%"PRIu64,
    306 			    ers->lo, ers->hi));
    307 		}
    308 		RB_REMOVE(revoked_serial_tree, rt, crs);
    309 		free(crs);
    310 	}
    311 	/* Check successors */
    312 	while ((crs = RB_NEXT(revoked_serial_tree, rt, ers)) != NULL) {
    313 		KRL_DBG(("succ %"PRIu64":%"PRIu64, crs->lo, crs->hi));
    314 		if (ers->hi != (u_int64_t)-1 && crs->lo > ers->hi + 1)
    315 			break;
    316 		/* This entry overlaps. */
    317 		if (crs->hi > ers->hi) {
    318 			ers->hi = crs->hi;
    319 			KRL_DBG(("succ extend %"PRIu64":%"PRIu64,
    320 			    ers->lo, ers->hi));
    321 		}
    322 		RB_REMOVE(revoked_serial_tree, rt, crs);
    323 		free(crs);
    324 	}
    325 	KRL_DBG(("done, final %"PRIu64":%"PRIu64, ers->lo, ers->hi));
    326 	return 0;
    327 }
    328 
    329 int
    330 ssh_krl_revoke_cert_by_serial(struct ssh_krl *krl, const struct sshkey *ca_key,
    331     u_int64_t serial)
    332 {
    333 	return ssh_krl_revoke_cert_by_serial_range(krl, ca_key, serial, serial);
    334 }
    335 
    336 int
    337 ssh_krl_revoke_cert_by_serial_range(struct ssh_krl *krl,
    338     const struct sshkey *ca_key, u_int64_t lo, u_int64_t hi)
    339 {
    340 	struct revoked_certs *rc;
    341 	int r;
    342 
    343 	if (lo > hi || lo == 0)
    344 		return SSH_ERR_INVALID_ARGUMENT;
    345 	if ((r = revoked_certs_for_ca_key(krl, ca_key, &rc, 1)) != 0)
    346 		return r;
    347 	return insert_serial_range(&rc->revoked_serials, lo, hi);
    348 }
    349 
    350 int
    351 ssh_krl_revoke_cert_by_key_id(struct ssh_krl *krl, const struct sshkey *ca_key,
    352     const char *key_id)
    353 {
    354 	struct revoked_key_id *rki, *erki;
    355 	struct revoked_certs *rc;
    356 	int r;
    357 
    358 	if ((r = revoked_certs_for_ca_key(krl, ca_key, &rc, 1)) != 0)
    359 		return r;
    360 
    361 	KRL_DBG(("revoke %s", key_id));
    362 	if ((rki = calloc(1, sizeof(*rki))) == NULL ||
    363 	    (rki->key_id = strdup(key_id)) == NULL) {
    364 		free(rki);
    365 		return SSH_ERR_ALLOC_FAIL;
    366 	}
    367 	erki = RB_INSERT(revoked_key_id_tree, &rc->revoked_key_ids, rki);
    368 	if (erki != NULL) {
    369 		free(rki->key_id);
    370 		free(rki);
    371 	}
    372 	return 0;
    373 }
    374 
    375 /* Convert "key" to a public key blob without any certificate information */
    376 static int
    377 plain_key_blob(const struct sshkey *key, u_char **blob, size_t *blen)
    378 {
    379 	struct sshkey *kcopy;
    380 	int r;
    381 
    382 	if ((r = sshkey_from_private(key, &kcopy)) != 0)
    383 		return r;
    384 	if (sshkey_is_cert(kcopy)) {
    385 		if ((r = sshkey_drop_cert(kcopy)) != 0) {
    386 			sshkey_free(kcopy);
    387 			return r;
    388 		}
    389 	}
    390 	r = sshkey_to_blob(kcopy, blob, blen);
    391 	sshkey_free(kcopy);
    392 	return r;
    393 }
    394 
    395 /* Revoke a key blob. Ownership of blob is transferred to the tree */
    396 static int
    397 revoke_blob(struct revoked_blob_tree *rbt, u_char *blob, size_t len)
    398 {
    399 	struct revoked_blob *rb, *erb;
    400 
    401 	if ((rb = calloc(1, sizeof(*rb))) == NULL)
    402 		return SSH_ERR_ALLOC_FAIL;
    403 	rb->blob = blob;
    404 	rb->len = len;
    405 	erb = RB_INSERT(revoked_blob_tree, rbt, rb);
    406 	if (erb != NULL) {
    407 		free(rb->blob);
    408 		free(rb);
    409 	}
    410 	return 0;
    411 }
    412 
    413 int
    414 ssh_krl_revoke_key_explicit(struct ssh_krl *krl, const struct sshkey *key)
    415 {
    416 	u_char *blob;
    417 	size_t len;
    418 	int r;
    419 
    420 	debug3_f("revoke type %s", sshkey_type(key));
    421 	if ((r = plain_key_blob(key, &blob, &len)) != 0)
    422 		return r;
    423 	return revoke_blob(&krl->revoked_keys, blob, len);
    424 }
    425 
    426 static int
    427 revoke_by_hash(struct revoked_blob_tree *target, const u_char *p, size_t len)
    428 {
    429 	u_char *blob;
    430 	int r;
    431 
    432 	/* need to copy hash, as revoke_blob steals ownership */
    433 	if ((blob = malloc(len)) == NULL)
    434 		return SSH_ERR_SYSTEM_ERROR;
    435 	memcpy(blob, p, len);
    436 	if ((r = revoke_blob(target, blob, len)) != 0) {
    437 		free(blob);
    438 		return r;
    439 	}
    440 	return 0;
    441 }
    442 
    443 int
    444 ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const u_char *p, size_t len)
    445 {
    446 	debug3_f("revoke by sha1");
    447 	if (len != 20)
    448 		return SSH_ERR_INVALID_FORMAT;
    449 	return revoke_by_hash(&krl->revoked_sha1s, p, len);
    450 }
    451 
    452 int
    453 ssh_krl_revoke_key_sha256(struct ssh_krl *krl, const u_char *p, size_t len)
    454 {
    455 	debug3_f("revoke by sha256");
    456 	if (len != 32)
    457 		return SSH_ERR_INVALID_FORMAT;
    458 	return revoke_by_hash(&krl->revoked_sha256s, p, len);
    459 }
    460 
    461 int
    462 ssh_krl_revoke_key(struct ssh_krl *krl, const struct sshkey *key)
    463 {
    464 	/* XXX replace with SHA256? */
    465 	if (!sshkey_is_cert(key))
    466 		return ssh_krl_revoke_key_explicit(krl, key);
    467 
    468 	if (key->cert->serial == 0) {
    469 		return ssh_krl_revoke_cert_by_key_id(krl,
    470 		    key->cert->signature_key,
    471 		    key->cert->key_id);
    472 	} else {
    473 		return ssh_krl_revoke_cert_by_serial(krl,
    474 		    key->cert->signature_key,
    475 		    key->cert->serial);
    476 	}
    477 }
    478 
    479 /*
    480  * Select the most compact section type to emit next in a KRL based on
    481  * the current section type, the run length of contiguous revoked serial
    482  * numbers and the gaps from the last and to the next revoked serial.
    483  * Applies a mostly-accurate bit cost model to select the section type
    484  * that will minimise the size of the resultant KRL.
    485  */
    486 static int
    487 choose_next_state(int current_state, u_int64_t contig, int final,
    488     u_int64_t last_gap, u_int64_t next_gap, int *force_new_section)
    489 {
    490 	int new_state;
    491 	u_int64_t cost, cost_list, cost_range, cost_bitmap, cost_bitmap_restart;
    492 
    493 	/*
    494 	 * Avoid unsigned overflows.
    495 	 * The limits are high enough to avoid confusing the calculations.
    496 	 */
    497 	contig = MINIMUM(contig, 1ULL<<31);
    498 	last_gap = MINIMUM(last_gap, 1ULL<<31);
    499 	next_gap = MINIMUM(next_gap, 1ULL<<31);
    500 
    501 	/*
    502 	 * Calculate the cost to switch from the current state to candidates.
    503 	 * NB. range sections only ever contain a single range, so their
    504 	 * switching cost is independent of the current_state.
    505 	 */
    506 	cost_list = cost_bitmap = cost_bitmap_restart = 0;
    507 	cost_range = 8;
    508 	switch (current_state) {
    509 	case KRL_SECTION_CERT_SERIAL_LIST:
    510 		cost_bitmap_restart = cost_bitmap = 8 + 64;
    511 		break;
    512 	case KRL_SECTION_CERT_SERIAL_BITMAP:
    513 		cost_list = 8;
    514 		cost_bitmap_restart = 8 + 64;
    515 		break;
    516 	case KRL_SECTION_CERT_SERIAL_RANGE:
    517 	case 0:
    518 		cost_bitmap_restart = cost_bitmap = 8 + 64;
    519 		cost_list = 8;
    520 	}
    521 
    522 	/* Estimate base cost in bits of each section type */
    523 	cost_list += 64 * contig + (final ? 0 : 8+64);
    524 	cost_range += (2 * 64) + (final ? 0 : 8+64);
    525 	cost_bitmap += last_gap + contig + (final ? 0 : MINIMUM(next_gap, 8+64));
    526 	cost_bitmap_restart += contig + (final ? 0 : MINIMUM(next_gap, 8+64));
    527 
    528 	/* Convert to byte costs for actual comparison */
    529 	cost_list = (cost_list + 7) / 8;
    530 	cost_bitmap = (cost_bitmap + 7) / 8;
    531 	cost_bitmap_restart = (cost_bitmap_restart + 7) / 8;
    532 	cost_range = (cost_range + 7) / 8;
    533 
    534 	/* Now pick the best choice */
    535 	*force_new_section = 0;
    536 	new_state = KRL_SECTION_CERT_SERIAL_BITMAP;
    537 	cost = cost_bitmap;
    538 	if (cost_range < cost) {
    539 		new_state = KRL_SECTION_CERT_SERIAL_RANGE;
    540 		cost = cost_range;
    541 	}
    542 	if (cost_list < cost) {
    543 		new_state = KRL_SECTION_CERT_SERIAL_LIST;
    544 		cost = cost_list;
    545 	}
    546 	if (cost_bitmap_restart < cost) {
    547 		new_state = KRL_SECTION_CERT_SERIAL_BITMAP;
    548 		*force_new_section = 1;
    549 		cost = cost_bitmap_restart;
    550 	}
    551 	KRL_DBG(("contig %llu last_gap %llu next_gap %llu final %d, costs:"
    552 	    "list %llu range %llu bitmap %llu new bitmap %llu, "
    553 	    "selected 0x%02x%s", (long long unsigned)contig,
    554 	    (long long unsigned)last_gap, (long long unsigned)next_gap, final,
    555 	    (long long unsigned)cost_list, (long long unsigned)cost_range,
    556 	    (long long unsigned)cost_bitmap,
    557 	    (long long unsigned)cost_bitmap_restart, new_state,
    558 	    *force_new_section ? " restart" : ""));
    559 	return new_state;
    560 }
    561 
    562 static int
    563 put_bitmap(struct sshbuf *buf, struct bitmap *bitmap)
    564 {
    565 	size_t len;
    566 	u_char *blob;
    567 	int r;
    568 
    569 	len = bitmap_nbytes(bitmap);
    570 	if ((blob = malloc(len)) == NULL)
    571 		return SSH_ERR_ALLOC_FAIL;
    572 	if (bitmap_to_string(bitmap, blob, len) != 0) {
    573 		free(blob);
    574 		return SSH_ERR_INTERNAL_ERROR;
    575 	}
    576 	r = sshbuf_put_bignum2_bytes(buf, blob, len);
    577 	free(blob);
    578 	return r;
    579 }
    580 
    581 /* Generate a KRL_SECTION_CERTIFICATES KRL section */
    582 static int
    583 revoked_certs_generate(struct revoked_certs *rc, struct sshbuf *buf)
    584 {
    585 	int final, force_new_sect, r = SSH_ERR_INTERNAL_ERROR;
    586 	u_int64_t i, contig, gap, last = 0, bitmap_start = 0;
    587 	struct revoked_serial *rs, *nrs;
    588 	struct revoked_key_id *rki;
    589 	int next_state, state = 0;
    590 	struct sshbuf *sect;
    591 	struct bitmap *bitmap = NULL;
    592 
    593 	if ((sect = sshbuf_new()) == NULL)
    594 		return SSH_ERR_ALLOC_FAIL;
    595 
    596 	/* Store the header: optional CA scope key, reserved */
    597 	if (rc->ca_key == NULL) {
    598 		if ((r = sshbuf_put_string(buf, NULL, 0)) != 0)
    599 			goto out;
    600 	} else {
    601 		if ((r = sshkey_puts(rc->ca_key, buf)) != 0)
    602 			goto out;
    603 	}
    604 	if ((r = sshbuf_put_string(buf, NULL, 0)) != 0)
    605 		goto out;
    606 
    607 	/* Store the revoked serials.  */
    608 	for (rs = RB_MIN(revoked_serial_tree, &rc->revoked_serials);
    609 	     rs != NULL;
    610 	     rs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs)) {
    611 		KRL_DBG(("serial %llu:%llu state 0x%02x",
    612 		    (long long unsigned)rs->lo, (long long unsigned)rs->hi,
    613 		    state));
    614 
    615 		/* Check contiguous length and gap to next section (if any) */
    616 		nrs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs);
    617 		final = nrs == NULL;
    618 		gap = nrs == NULL ? 0 : nrs->lo - rs->hi;
    619 		contig = 1 + (rs->hi - rs->lo);
    620 
    621 		/* Choose next state based on these */
    622 		next_state = choose_next_state(state, contig, final,
    623 		    state == 0 ? 0 : rs->lo - last, gap, &force_new_sect);
    624 
    625 		/*
    626 		 * If the current section is a range section or has a different
    627 		 * type to the next section, then finish it off now.
    628 		 */
    629 		if (state != 0 && (force_new_sect || next_state != state ||
    630 		    state == KRL_SECTION_CERT_SERIAL_RANGE)) {
    631 			KRL_DBG(("finish state 0x%02x", state));
    632 			switch (state) {
    633 			case KRL_SECTION_CERT_SERIAL_LIST:
    634 			case KRL_SECTION_CERT_SERIAL_RANGE:
    635 				break;
    636 			case KRL_SECTION_CERT_SERIAL_BITMAP:
    637 				if ((r = put_bitmap(sect, bitmap)) != 0)
    638 					goto out;
    639 				bitmap_free(bitmap);
    640 				bitmap = NULL;
    641 				break;
    642 			}
    643 			if ((r = sshbuf_put_u8(buf, state)) != 0 ||
    644 			    (r = sshbuf_put_stringb(buf, sect)) != 0)
    645 				goto out;
    646 			sshbuf_reset(sect);
    647 		}
    648 
    649 		/* If we are starting a new section then prepare it now */
    650 		if (next_state != state || force_new_sect) {
    651 			KRL_DBG(("start state 0x%02x",
    652 			    next_state));
    653 			state = next_state;
    654 			sshbuf_reset(sect);
    655 			switch (state) {
    656 			case KRL_SECTION_CERT_SERIAL_LIST:
    657 			case KRL_SECTION_CERT_SERIAL_RANGE:
    658 				break;
    659 			case KRL_SECTION_CERT_SERIAL_BITMAP:
    660 				if ((bitmap = bitmap_new()) == NULL) {
    661 					r = SSH_ERR_ALLOC_FAIL;
    662 					goto out;
    663 				}
    664 				bitmap_start = rs->lo;
    665 				if ((r = sshbuf_put_u64(sect,
    666 				    bitmap_start)) != 0)
    667 					goto out;
    668 				break;
    669 			}
    670 		}
    671 
    672 		/* Perform section-specific processing */
    673 		switch (state) {
    674 		case KRL_SECTION_CERT_SERIAL_LIST:
    675 			for (i = 0; i < contig; i++) {
    676 				if ((r = sshbuf_put_u64(sect, rs->lo + i)) != 0)
    677 					goto out;
    678 			}
    679 			break;
    680 		case KRL_SECTION_CERT_SERIAL_RANGE:
    681 			if ((r = sshbuf_put_u64(sect, rs->lo)) != 0 ||
    682 			    (r = sshbuf_put_u64(sect, rs->hi)) != 0)
    683 				goto out;
    684 			break;
    685 		case KRL_SECTION_CERT_SERIAL_BITMAP:
    686 			if (rs->lo - bitmap_start > INT_MAX) {
    687 				r = SSH_ERR_INVALID_FORMAT;
    688 				error_f("insane bitmap gap");
    689 				goto out;
    690 			}
    691 			for (i = 0; i < contig; i++) {
    692 				if (bitmap_set_bit(bitmap,
    693 				    rs->lo + i - bitmap_start) != 0) {
    694 					r = SSH_ERR_ALLOC_FAIL;
    695 					goto out;
    696 				}
    697 			}
    698 			break;
    699 		}
    700 		last = rs->hi;
    701 	}
    702 	/* Flush the remaining section, if any */
    703 	if (state != 0) {
    704 		KRL_DBG(("serial final flush for state 0x%02x", state));
    705 		switch (state) {
    706 		case KRL_SECTION_CERT_SERIAL_LIST:
    707 		case KRL_SECTION_CERT_SERIAL_RANGE:
    708 			break;
    709 		case KRL_SECTION_CERT_SERIAL_BITMAP:
    710 			if ((r = put_bitmap(sect, bitmap)) != 0)
    711 				goto out;
    712 			bitmap_free(bitmap);
    713 			bitmap = NULL;
    714 			break;
    715 		}
    716 		if ((r = sshbuf_put_u8(buf, state)) != 0 ||
    717 		    (r = sshbuf_put_stringb(buf, sect)) != 0)
    718 			goto out;
    719 	}
    720 	KRL_DBG(("serial done "));
    721 
    722 	/* Now output a section for any revocations by key ID */
    723 	sshbuf_reset(sect);
    724 	RB_FOREACH(rki, revoked_key_id_tree, &rc->revoked_key_ids) {
    725 		KRL_DBG(("key ID %s", rki->key_id));
    726 		if ((r = sshbuf_put_cstring(sect, rki->key_id)) != 0)
    727 			goto out;
    728 	}
    729 	if (sshbuf_len(sect) != 0) {
    730 		if ((r = sshbuf_put_u8(buf, KRL_SECTION_CERT_KEY_ID)) != 0 ||
    731 		    (r = sshbuf_put_stringb(buf, sect)) != 0)
    732 			goto out;
    733 	}
    734 	r = 0;
    735  out:
    736 	bitmap_free(bitmap);
    737 	sshbuf_free(sect);
    738 	return r;
    739 }
    740 
    741 int
    742 ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf)
    743 {
    744 	int r = SSH_ERR_INTERNAL_ERROR;
    745 	struct revoked_certs *rc;
    746 	struct revoked_blob *rb;
    747 	struct sshbuf *sect;
    748 	u_char *sblob = NULL;
    749 
    750 	if (krl->generated_date == 0)
    751 		krl->generated_date = time(NULL);
    752 
    753 	if ((sect = sshbuf_new()) == NULL)
    754 		return SSH_ERR_ALLOC_FAIL;
    755 
    756 	/* Store the header */
    757 	if ((r = sshbuf_put(buf, KRL_MAGIC, sizeof(KRL_MAGIC) - 1)) != 0 ||
    758 	    (r = sshbuf_put_u32(buf, KRL_FORMAT_VERSION)) != 0 ||
    759 	    (r = sshbuf_put_u64(buf, krl->krl_version)) != 0 ||
    760 	    (r = sshbuf_put_u64(buf, krl->generated_date)) != 0 ||
    761 	    (r = sshbuf_put_u64(buf, krl->flags)) != 0 ||
    762 	    (r = sshbuf_put_string(buf, NULL, 0)) != 0 ||
    763 	    (r = sshbuf_put_cstring(buf, krl->comment)) != 0)
    764 		goto out;
    765 
    766 	/* Store sections for revoked certificates */
    767 	TAILQ_FOREACH(rc, &krl->revoked_certs, entry) {
    768 		sshbuf_reset(sect);
    769 		if ((r = revoked_certs_generate(rc, sect)) != 0)
    770 			goto out;
    771 		if ((r = sshbuf_put_u8(buf, KRL_SECTION_CERTIFICATES)) != 0 ||
    772 		    (r = sshbuf_put_stringb(buf, sect)) != 0)
    773 			goto out;
    774 	}
    775 
    776 	/* Finally, output sections for revocations by public key/hash */
    777 	sshbuf_reset(sect);
    778 	RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) {
    779 		KRL_DBG(("key len %zu ", rb->len));
    780 		if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0)
    781 			goto out;
    782 	}
    783 	if (sshbuf_len(sect) != 0) {
    784 		if ((r = sshbuf_put_u8(buf, KRL_SECTION_EXPLICIT_KEY)) != 0 ||
    785 		    (r = sshbuf_put_stringb(buf, sect)) != 0)
    786 			goto out;
    787 	}
    788 	sshbuf_reset(sect);
    789 	RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) {
    790 		KRL_DBG(("hash len %zu ", rb->len));
    791 		if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0)
    792 			goto out;
    793 	}
    794 	if (sshbuf_len(sect) != 0) {
    795 		if ((r = sshbuf_put_u8(buf,
    796 		    KRL_SECTION_FINGERPRINT_SHA1)) != 0 ||
    797 		    (r = sshbuf_put_stringb(buf, sect)) != 0)
    798 			goto out;
    799 	}
    800 	sshbuf_reset(sect);
    801 	RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha256s) {
    802 		KRL_DBG(("hash len %zu ", rb->len));
    803 		if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0)
    804 			goto out;
    805 	}
    806 	if (sshbuf_len(sect) != 0) {
    807 		if ((r = sshbuf_put_u8(buf,
    808 		    KRL_SECTION_FINGERPRINT_SHA256)) != 0 ||
    809 		    (r = sshbuf_put_stringb(buf, sect)) != 0)
    810 			goto out;
    811 	}
    812 	/* success */
    813 	r = 0;
    814  out:
    815 	free(sblob);
    816 	sshbuf_free(sect);
    817 	return r;
    818 }
    819 
    820 static void
    821 format_timestamp(u_int64_t timestamp, char *ts, size_t nts)
    822 {
    823 	time_t t;
    824 	struct tm *tm;
    825 
    826 	t = timestamp;
    827 	tm = localtime(&t);
    828 	if (tm == NULL)
    829 		strlcpy(ts, "<INVALID>", nts);
    830 	else {
    831 		*ts = '\0';
    832 		strftime(ts, nts, "%Y%m%dT%H%M%S", tm);
    833 	}
    834 }
    835 
    836 static int
    837 cert_extension_subsection(struct sshbuf *subsect, struct ssh_krl *krl)
    838 {
    839 	int r = SSH_ERR_INTERNAL_ERROR;
    840 	u_char critical = 1;
    841 	struct sshbuf *value = NULL;
    842 	char *name = NULL;
    843 
    844 	if ((r = sshbuf_get_cstring(subsect, &name, NULL)) != 0 ||
    845 	    (r = sshbuf_get_u8(subsect, &critical)) != 0 ||
    846 	    (r = sshbuf_froms(subsect, &value)) != 0) {
    847 		debug_fr(r, "parse");
    848 		error("KRL has invalid certificate extension subsection");
    849 		r = SSH_ERR_INVALID_FORMAT;
    850 		goto out;
    851 	}
    852 	if (sshbuf_len(subsect) != 0) {
    853 		error("KRL has invalid certificate extension subsection: "
    854 		    "trailing data");
    855 		r = SSH_ERR_INVALID_FORMAT;
    856 		goto out;
    857 	}
    858 	debug_f("cert extension %s critical %u len %zu",
    859 	    name, critical, sshbuf_len(value));
    860 	/* no extensions are currently supported */
    861 	if (critical) {
    862 		error("KRL contains unsupported critical certificate "
    863 		    "subsection \"%s\"", name);
    864 		r = SSH_ERR_FEATURE_UNSUPPORTED;
    865 		goto out;
    866 	}
    867 	/* success */
    868 	r = 0;
    869  out:
    870 	free(name);
    871 	sshbuf_free(value);
    872 	return r;
    873 }
    874 
    875 static int
    876 parse_revoked_certs(struct sshbuf *buf, struct ssh_krl *krl)
    877 {
    878 	int r = SSH_ERR_INTERNAL_ERROR;
    879 	u_char type;
    880 	const u_char *blob;
    881 	size_t blen, nbits;
    882 	struct sshbuf *subsect = NULL;
    883 	u_int64_t serial, serial_lo, serial_hi;
    884 	struct bitmap *bitmap = NULL;
    885 	char *key_id = NULL;
    886 	struct sshkey *ca_key = NULL;
    887 
    888 	if ((subsect = sshbuf_new()) == NULL)
    889 		return SSH_ERR_ALLOC_FAIL;
    890 
    891 	/* Header: key, reserved */
    892 	if ((r = sshbuf_get_string_direct(buf, &blob, &blen)) != 0 ||
    893 	    (r = sshbuf_skip_string(buf)) != 0)
    894 		goto out;
    895 	if (blen != 0 && (r = sshkey_from_blob(blob, blen, &ca_key)) != 0)
    896 		goto out;
    897 
    898 	while (sshbuf_len(buf) > 0) {
    899 		sshbuf_free(subsect);
    900 		subsect = NULL;
    901 		if ((r = sshbuf_get_u8(buf, &type)) != 0 ||
    902 		    (r = sshbuf_froms(buf, &subsect)) != 0)
    903 			goto out;
    904 		KRL_DBG(("subsection type 0x%02x", type));
    905 		/* sshbuf_dump(subsect, stderr); */
    906 
    907 		switch (type) {
    908 		case KRL_SECTION_CERT_SERIAL_LIST:
    909 			while (sshbuf_len(subsect) > 0) {
    910 				if ((r = sshbuf_get_u64(subsect, &serial)) != 0)
    911 					goto out;
    912 				if ((r = ssh_krl_revoke_cert_by_serial(krl,
    913 				    ca_key, serial)) != 0)
    914 					goto out;
    915 			}
    916 			break;
    917 		case KRL_SECTION_CERT_SERIAL_RANGE:
    918 			if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 ||
    919 			    (r = sshbuf_get_u64(subsect, &serial_hi)) != 0)
    920 				goto out;
    921 			if ((r = ssh_krl_revoke_cert_by_serial_range(krl,
    922 			    ca_key, serial_lo, serial_hi)) != 0)
    923 				goto out;
    924 			break;
    925 		case KRL_SECTION_CERT_SERIAL_BITMAP:
    926 			if ((bitmap = bitmap_new()) == NULL) {
    927 				r = SSH_ERR_ALLOC_FAIL;
    928 				goto out;
    929 			}
    930 			if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 ||
    931 			    (r = sshbuf_get_bignum2_bytes_direct(subsect,
    932 			    &blob, &blen)) != 0)
    933 				goto out;
    934 			if (bitmap_from_string(bitmap, blob, blen) != 0) {
    935 				r = SSH_ERR_INVALID_FORMAT;
    936 				goto out;
    937 			}
    938 			nbits = bitmap_nbits(bitmap);
    939 			for (serial = 0; serial < (u_int64_t)nbits; serial++) {
    940 				if (serial > 0 && serial_lo + serial == 0) {
    941 					error_f("bitmap wraps u64");
    942 					r = SSH_ERR_INVALID_FORMAT;
    943 					goto out;
    944 				}
    945 				if (!bitmap_test_bit(bitmap, serial))
    946 					continue;
    947 				if ((r = ssh_krl_revoke_cert_by_serial(krl,
    948 				    ca_key, serial_lo + serial)) != 0)
    949 					goto out;
    950 			}
    951 			bitmap_free(bitmap);
    952 			bitmap = NULL;
    953 			break;
    954 		case KRL_SECTION_CERT_KEY_ID:
    955 			while (sshbuf_len(subsect) > 0) {
    956 				if ((r = sshbuf_get_cstring(subsect,
    957 				    &key_id, NULL)) != 0)
    958 					goto out;
    959 				if ((r = ssh_krl_revoke_cert_by_key_id(krl,
    960 				    ca_key, key_id)) != 0)
    961 					goto out;
    962 				free(key_id);
    963 				key_id = NULL;
    964 			}
    965 			break;
    966 		case KRL_SECTION_CERT_EXTENSION:
    967 			if ((r = cert_extension_subsection(subsect, krl)) != 0)
    968 				goto out;
    969 			break;
    970 		default:
    971 			error("Unsupported KRL certificate section %u", type);
    972 			r = SSH_ERR_INVALID_FORMAT;
    973 			goto out;
    974 		}
    975 		if (sshbuf_len(subsect) > 0) {
    976 			error("KRL certificate section contains unparsed data");
    977 			r = SSH_ERR_INVALID_FORMAT;
    978 			goto out;
    979 		}
    980 	}
    981 
    982 	r = 0;
    983  out:
    984 	if (bitmap != NULL)
    985 		bitmap_free(bitmap);
    986 	free(key_id);
    987 	sshkey_free(ca_key);
    988 	sshbuf_free(subsect);
    989 	return r;
    990 }
    991 
    992 static int
    993 blob_section(struct sshbuf *sect, struct revoked_blob_tree *target_tree,
    994     size_t expected_len)
    995 {
    996 	u_char *rdata = NULL;
    997 	size_t rlen = 0;
    998 	int r;
    999 
   1000 	while (sshbuf_len(sect) > 0) {
   1001 		if ((r = sshbuf_get_string(sect, &rdata, &rlen)) != 0)
   1002 			return r;
   1003 		if (expected_len != 0 && rlen != expected_len) {
   1004 			error_f("bad length");
   1005 			free(rdata);
   1006 			return SSH_ERR_INVALID_FORMAT;
   1007 		}
   1008 		if ((r = revoke_blob(target_tree, rdata, rlen)) != 0) {
   1009 			free(rdata);
   1010 			return r;
   1011 		}
   1012 	}
   1013 	return 0;
   1014 }
   1015 
   1016 static int
   1017 extension_section(struct sshbuf *sect, struct ssh_krl *krl)
   1018 {
   1019 	int r = SSH_ERR_INTERNAL_ERROR;
   1020 	u_char critical = 1;
   1021 	struct sshbuf *value = NULL;
   1022 	char *name = NULL;
   1023 
   1024 	if ((r = sshbuf_get_cstring(sect, &name, NULL)) != 0 ||
   1025 	    (r = sshbuf_get_u8(sect, &critical)) != 0 ||
   1026 	    (r = sshbuf_froms(sect, &value)) != 0) {
   1027 		debug_fr(r, "parse");
   1028 		error("KRL has invalid extension section");
   1029 		r = SSH_ERR_INVALID_FORMAT;
   1030 		goto out;
   1031 	}
   1032 	if (sshbuf_len(sect) != 0) {
   1033 		error("KRL has invalid extension section: trailing data");
   1034 		r = SSH_ERR_INVALID_FORMAT;
   1035 		goto out;
   1036 	}
   1037 	debug_f("extension %s critical %u len %zu",
   1038 	    name, critical, sshbuf_len(value));
   1039 	/* no extensions are currently supported */
   1040 	if (critical) {
   1041 		error("KRL contains unsupported critical section \"%s\"", name);
   1042 		r = SSH_ERR_FEATURE_UNSUPPORTED;
   1043 		goto out;
   1044 	}
   1045 	/* success */
   1046 	r = 0;
   1047  out:
   1048 	free(name);
   1049 	sshbuf_free(value);
   1050 	return r;
   1051 }
   1052 
   1053 /* Attempt to parse a KRL */
   1054 int
   1055 ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp)
   1056 {
   1057 	struct sshbuf *copy = NULL, *sect = NULL;
   1058 	struct ssh_krl *krl = NULL;
   1059 	char timestamp[64];
   1060 	int r = SSH_ERR_INTERNAL_ERROR;
   1061 	u_char type;
   1062 	u_int format_version;
   1063 
   1064 	*krlp = NULL;
   1065 
   1066 	/* KRL must begin with magic string */
   1067 	if ((r = sshbuf_cmp(buf, 0, KRL_MAGIC, sizeof(KRL_MAGIC) - 1)) != 0) {
   1068 		debug2_f("bad KRL magic header");
   1069 		return SSH_ERR_KRL_BAD_MAGIC;
   1070 	}
   1071 
   1072 	if ((krl = ssh_krl_init()) == NULL) {
   1073 		r = SSH_ERR_ALLOC_FAIL;
   1074 		error_f("alloc failed");
   1075 		goto out;
   1076 	}
   1077 	/* Don't modify buffer */
   1078 	if ((copy = sshbuf_fromb(buf)) == NULL) {
   1079 		r = SSH_ERR_ALLOC_FAIL;
   1080 		goto out;
   1081 	}
   1082 	if ((r = sshbuf_consume(copy, sizeof(KRL_MAGIC) - 1)) != 0 ||
   1083 	    (r = sshbuf_get_u32(copy, &format_version)) != 0)
   1084 		goto out;
   1085 	if (format_version != KRL_FORMAT_VERSION) {
   1086 		error_f("unsupported KRL format version %u", format_version);
   1087 		r = SSH_ERR_INVALID_FORMAT;
   1088 		goto out;
   1089 	}
   1090 	if ((r = sshbuf_get_u64(copy, &krl->krl_version)) != 0 ||
   1091 	    (r = sshbuf_get_u64(copy, &krl->generated_date)) != 0 ||
   1092 	    (r = sshbuf_get_u64(copy, &krl->flags)) != 0 ||
   1093 	    (r = sshbuf_skip_string(copy)) != 0 ||
   1094 	    (r = sshbuf_get_cstring(copy, &krl->comment, NULL)) != 0) {
   1095 		error_fr(r, "parse KRL header");
   1096 		goto out;
   1097 	}
   1098 	format_timestamp(krl->generated_date, timestamp, sizeof(timestamp));
   1099 	debug("KRL version %llu generated at %s%s%s",
   1100 	    (long long unsigned)krl->krl_version, timestamp,
   1101 	    *krl->comment ? ": " : "", krl->comment);
   1102 
   1103 	/* Parse and load the KRL sections. */
   1104 	while (sshbuf_len(copy) > 0) {
   1105 		sshbuf_free(sect);
   1106 		sect = NULL;
   1107 		if ((r = sshbuf_get_u8(copy, &type)) != 0 ||
   1108 		    (r = sshbuf_froms(copy, &sect)) != 0)
   1109 			goto out;
   1110 		KRL_DBG(("section 0x%02x", type));
   1111 
   1112 		switch (type) {
   1113 		case KRL_SECTION_CERTIFICATES:
   1114 			if ((r = parse_revoked_certs(sect, krl)) != 0)
   1115 				goto out;
   1116 			break;
   1117 		case KRL_SECTION_EXPLICIT_KEY:
   1118 			if ((r = blob_section(sect,
   1119 			    &krl->revoked_keys, 0)) != 0)
   1120 				goto out;
   1121 			break;
   1122 		case KRL_SECTION_FINGERPRINT_SHA1:
   1123 			if ((r = blob_section(sect,
   1124 			    &krl->revoked_sha1s, 20)) != 0)
   1125 				goto out;
   1126 			break;
   1127 		case KRL_SECTION_FINGERPRINT_SHA256:
   1128 			if ((r = blob_section(sect,
   1129 			    &krl->revoked_sha256s, 32)) != 0)
   1130 				goto out;
   1131 			break;
   1132 		case KRL_SECTION_EXTENSION:
   1133 			if ((r = extension_section(sect, krl)) != 0)
   1134 				goto out;
   1135 			break;
   1136 		case KRL_SECTION_SIGNATURE:
   1137 			/* Handled above, but still need to stay in synch */
   1138 			sshbuf_free(sect);
   1139 			sect = NULL;
   1140 			if ((r = sshbuf_skip_string(copy)) != 0)
   1141 				goto out;
   1142 			break;
   1143 		default:
   1144 			error("Unsupported KRL section %u", type);
   1145 			r = SSH_ERR_INVALID_FORMAT;
   1146 			goto out;
   1147 		}
   1148 		if (sect != NULL && sshbuf_len(sect) > 0) {
   1149 			error("KRL section contains unparsed data");
   1150 			r = SSH_ERR_INVALID_FORMAT;
   1151 			goto out;
   1152 		}
   1153 	}
   1154 
   1155 	/* Success */
   1156 	*krlp = krl;
   1157 	r = 0;
   1158  out:
   1159 	if (r != 0)
   1160 		ssh_krl_free(krl);
   1161 	sshbuf_free(copy);
   1162 	sshbuf_free(sect);
   1163 	return r;
   1164 }
   1165 
   1166 /* Checks certificate serial number and key ID revocation */
   1167 static int
   1168 is_cert_revoked(const struct sshkey *key, struct revoked_certs *rc)
   1169 {
   1170 	struct revoked_serial rs, *ers;
   1171 	struct revoked_key_id rki, *erki;
   1172 
   1173 	/* Check revocation by cert key ID */
   1174 	memset(&rki, 0, sizeof(rki));
   1175 	rki.key_id = key->cert->key_id;
   1176 	erki = RB_FIND(revoked_key_id_tree, &rc->revoked_key_ids, &rki);
   1177 	if (erki != NULL) {
   1178 		KRL_DBG(("revoked by key ID"));
   1179 		return SSH_ERR_KEY_REVOKED;
   1180 	}
   1181 
   1182 	/*
   1183 	 * Zero serials numbers are ignored (it's the default when the
   1184 	 * CA doesn't specify one).
   1185 	 */
   1186 	if (key->cert->serial == 0)
   1187 		return 0;
   1188 
   1189 	memset(&rs, 0, sizeof(rs));
   1190 	rs.lo = rs.hi = key->cert->serial;
   1191 	ers = RB_FIND(revoked_serial_tree, &rc->revoked_serials, &rs);
   1192 	if (ers != NULL) {
   1193 		KRL_DBG(("revoked serial %llu matched %llu:%llu",
   1194 		    key->cert->serial, ers->lo, ers->hi));
   1195 		return SSH_ERR_KEY_REVOKED;
   1196 	}
   1197 	return 0;
   1198 }
   1199 
   1200 /* Checks whether a given key/cert is revoked. Does not check its CA */
   1201 static int
   1202 is_key_revoked(struct ssh_krl *krl, const struct sshkey *key)
   1203 {
   1204 	struct revoked_blob rb, *erb;
   1205 	struct revoked_certs *rc;
   1206 	int r;
   1207 
   1208 	/* Check explicitly revoked hashes first */
   1209 	memset(&rb, 0, sizeof(rb));
   1210 	if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA1,
   1211 	    &rb.blob, &rb.len)) != 0)
   1212 		return r;
   1213 	erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha1s, &rb);
   1214 	free(rb.blob);
   1215 	if (erb != NULL) {
   1216 		KRL_DBG(("revoked by key SHA1"));
   1217 		return SSH_ERR_KEY_REVOKED;
   1218 	}
   1219 	memset(&rb, 0, sizeof(rb));
   1220 	if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA256,
   1221 	    &rb.blob, &rb.len)) != 0)
   1222 		return r;
   1223 	erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha256s, &rb);
   1224 	free(rb.blob);
   1225 	if (erb != NULL) {
   1226 		KRL_DBG(("revoked by key SHA256"));
   1227 		return SSH_ERR_KEY_REVOKED;
   1228 	}
   1229 
   1230 	/* Next, explicit keys */
   1231 	memset(&rb, 0, sizeof(rb));
   1232 	if ((r = plain_key_blob(key, &rb.blob, &rb.len)) != 0)
   1233 		return r;
   1234 	erb = RB_FIND(revoked_blob_tree, &krl->revoked_keys, &rb);
   1235 	free(rb.blob);
   1236 	if (erb != NULL) {
   1237 		KRL_DBG(("revoked by explicit key"));
   1238 		return SSH_ERR_KEY_REVOKED;
   1239 	}
   1240 
   1241 	if (!sshkey_is_cert(key))
   1242 		return 0;
   1243 
   1244 	/* Check cert revocation for the specified CA */
   1245 	if ((r = revoked_certs_for_ca_key(krl, key->cert->signature_key,
   1246 	    &rc, 0)) != 0)
   1247 		return r;
   1248 	if (rc != NULL) {
   1249 		if ((r = is_cert_revoked(key, rc)) != 0)
   1250 			return r;
   1251 	}
   1252 	/* Check cert revocation for the wildcard CA */
   1253 	if ((r = revoked_certs_for_ca_key(krl, NULL, &rc, 0)) != 0)
   1254 		return r;
   1255 	if (rc != NULL) {
   1256 		if ((r = is_cert_revoked(key, rc)) != 0)
   1257 			return r;
   1258 	}
   1259 
   1260 	KRL_DBG(("%llu no match", key->cert->serial));
   1261 	return 0;
   1262 }
   1263 
   1264 int
   1265 ssh_krl_check_key(struct ssh_krl *krl, const struct sshkey *key)
   1266 {
   1267 	int r;
   1268 
   1269 	KRL_DBG(("checking key"));
   1270 	if ((r = is_key_revoked(krl, key)) != 0)
   1271 		return r;
   1272 	if (sshkey_is_cert(key)) {
   1273 		debug2_f("checking CA key");
   1274 		if ((r = is_key_revoked(krl, key->cert->signature_key)) != 0)
   1275 			return r;
   1276 	}
   1277 	KRL_DBG(("key okay"));
   1278 	return 0;
   1279 }
   1280 
   1281 int
   1282 ssh_krl_file_contains_key(const char *path, const struct sshkey *key)
   1283 {
   1284 	struct sshbuf *krlbuf = NULL;
   1285 	struct ssh_krl *krl = NULL;
   1286 	int oerrno = 0, r;
   1287 
   1288 	if (path == NULL)
   1289 		return 0;
   1290 	if ((r = sshbuf_load_file(path, &krlbuf)) != 0) {
   1291 		oerrno = errno;
   1292 		goto out;
   1293 	}
   1294 	if ((r = ssh_krl_from_blob(krlbuf, &krl)) != 0)
   1295 		goto out;
   1296 	debug2_f("checking KRL %s", path);
   1297 	r = ssh_krl_check_key(krl, key);
   1298  out:
   1299 	sshbuf_free(krlbuf);
   1300 	ssh_krl_free(krl);
   1301 	if (r != 0)
   1302 		errno = oerrno;
   1303 	return r;
   1304 }
   1305 
   1306 int
   1307 krl_dump(struct ssh_krl *krl, FILE *f)
   1308 {
   1309 	struct sshkey *key = NULL;
   1310 	struct revoked_blob *rb;
   1311 	struct revoked_certs *rc;
   1312 	struct revoked_serial *rs;
   1313 	struct revoked_key_id *rki;
   1314 	int r, ret = 0;
   1315 	char *fp, timestamp[64];
   1316 
   1317 	/* Try to print in a KRL spec-compatible format */
   1318 	format_timestamp(krl->generated_date, timestamp, sizeof(timestamp));
   1319 	fprintf(f, "# KRL version %llu\n",
   1320 	    (unsigned long long)krl->krl_version);
   1321 	fprintf(f, "# Generated at %s\n", timestamp);
   1322 	if (krl->comment != NULL && *krl->comment != '\0') {
   1323 		r = INT_MAX;
   1324 		asmprintf(&fp, INT_MAX, &r, "%s", krl->comment);
   1325 		fprintf(f, "# Comment: %s\n", fp);
   1326 		free(fp);
   1327 	}
   1328 	fputc('\n', f);
   1329 
   1330 	RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) {
   1331 		if ((r = sshkey_from_blob(rb->blob, rb->len, &key)) != 0) {
   1332 			ret = SSH_ERR_INVALID_FORMAT;
   1333 			error_r(r, "parse KRL key");
   1334 			continue;
   1335 		}
   1336 		if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT,
   1337 		    SSH_FP_DEFAULT)) == NULL) {
   1338 			ret = SSH_ERR_INVALID_FORMAT;
   1339 			error("sshkey_fingerprint failed");
   1340 			continue;
   1341 		}
   1342 		fprintf(f, "hash: %s # %s\n", fp, sshkey_ssh_name(key));
   1343 		free(fp);
   1344 		free(key);
   1345 	}
   1346 	RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha256s) {
   1347 		fp = tohex(rb->blob, rb->len);
   1348 		fprintf(f, "hash: SHA256:%s\n", fp);
   1349 		free(fp);
   1350 	}
   1351 	RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) {
   1352 		/*
   1353 		 * There is not KRL spec keyword for raw SHA1 hashes, so
   1354 		 * print them as comments.
   1355 		 */
   1356 		fp = tohex(rb->blob, rb->len);
   1357 		fprintf(f, "# hash SHA1:%s\n", fp);
   1358 		free(fp);
   1359 	}
   1360 
   1361 	TAILQ_FOREACH(rc, &krl->revoked_certs, entry) {
   1362 		fputc('\n', f);
   1363 		if (rc->ca_key == NULL)
   1364 			fprintf(f, "# Wildcard CA\n");
   1365 		else {
   1366 			if ((fp = sshkey_fingerprint(rc->ca_key,
   1367 			    SSH_FP_HASH_DEFAULT, SSH_FP_DEFAULT)) == NULL) {
   1368 				ret = SSH_ERR_INVALID_FORMAT;
   1369 				error("sshkey_fingerprint failed");
   1370 				continue;
   1371 			}
   1372 			fprintf(f, "# CA key %s %s\n",
   1373 			    sshkey_ssh_name(rc->ca_key), fp);
   1374 			free(fp);
   1375 		}
   1376 		RB_FOREACH(rs, revoked_serial_tree, &rc->revoked_serials) {
   1377 			if (rs->lo == rs->hi) {
   1378 				fprintf(f, "serial: %llu\n",
   1379 				    (unsigned long long)rs->lo);
   1380 			} else {
   1381 				fprintf(f, "serial: %llu-%llu\n",
   1382 				    (unsigned long long)rs->lo,
   1383 				    (unsigned long long)rs->hi);
   1384 			}
   1385 		}
   1386 		RB_FOREACH(rki, revoked_key_id_tree, &rc->revoked_key_ids) {
   1387 			/*
   1388 			 * We don't want key IDs with embedded newlines to
   1389 			 * mess up the display.
   1390 			 */
   1391 			r = INT_MAX;
   1392 			asmprintf(&fp, INT_MAX, &r, "%s", rki->key_id);
   1393 			fprintf(f, "id: %s\n", fp);
   1394 			free(fp);
   1395 		}
   1396 	}
   1397 	return ret;
   1398 }
   1399