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