blob: 5a5cdde02f6961f94c51ab4e3d9d5ad26d78f6d5 [file] [log] [blame]
Damien Millerf3747bf2013-01-18 11:44:04 +11001/*
2 * Copyright (c) 2012 Damien Miller <djm@mindrot.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
djm@openbsd.org74de2542014-12-04 01:49:59 +000017/* $OpenBSD: krl.c,v 1.20 2014/12/04 01:49:59 djm Exp $ */
Damien Millerf3747bf2013-01-18 11:44:04 +110018
19#include "includes.h"
20
21#include <sys/types.h>
22#include <sys/param.h>
Damien Millerd60b2102013-01-20 22:49:58 +110023#include <openbsd-compat/sys-tree.h>
24#include <openbsd-compat/sys-queue.h>
Damien Millerf3747bf2013-01-18 11:44:04 +110025
26#include <errno.h>
27#include <fcntl.h>
28#include <limits.h>
29#include <string.h>
30#include <time.h>
31#include <unistd.h>
32
djm@openbsd.org74de2542014-12-04 01:49:59 +000033#include "sshbuf.h"
34#include "sshkey.h"
Damien Millerf3747bf2013-01-18 11:44:04 +110035#include "authfile.h"
Damien Millerf3747bf2013-01-18 11:44:04 +110036#include "misc.h"
37#include "log.h"
djm@openbsd.org74de2542014-12-04 01:49:59 +000038#include "ssherr.h"
Damien Millerf3747bf2013-01-18 11:44:04 +110039
40#include "krl.h"
41
42/* #define DEBUG_KRL */
43#ifdef DEBUG_KRL
44# define KRL_DBG(x) debug3 x
45#else
46# define KRL_DBG(x)
47#endif
48
49/*
50 * Trees of revoked serial numbers, key IDs and keys. This allows
51 * quick searching, querying and producing lists in canonical order.
52 */
53
54/* Tree of serial numbers. XXX make smarter: really need a real sparse bitmap */
55struct revoked_serial {
56 u_int64_t lo, hi;
57 RB_ENTRY(revoked_serial) tree_entry;
58};
59static int serial_cmp(struct revoked_serial *a, struct revoked_serial *b);
60RB_HEAD(revoked_serial_tree, revoked_serial);
61RB_GENERATE_STATIC(revoked_serial_tree, revoked_serial, tree_entry, serial_cmp);
62
63/* Tree of key IDs */
64struct revoked_key_id {
65 char *key_id;
66 RB_ENTRY(revoked_key_id) tree_entry;
67};
68static int key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b);
69RB_HEAD(revoked_key_id_tree, revoked_key_id);
70RB_GENERATE_STATIC(revoked_key_id_tree, revoked_key_id, tree_entry, key_id_cmp);
71
72/* Tree of blobs (used for keys and fingerprints) */
73struct revoked_blob {
74 u_char *blob;
djm@openbsd.org74de2542014-12-04 01:49:59 +000075 size_t len;
Damien Millerf3747bf2013-01-18 11:44:04 +110076 RB_ENTRY(revoked_blob) tree_entry;
77};
78static int blob_cmp(struct revoked_blob *a, struct revoked_blob *b);
79RB_HEAD(revoked_blob_tree, revoked_blob);
80RB_GENERATE_STATIC(revoked_blob_tree, revoked_blob, tree_entry, blob_cmp);
81
82/* Tracks revoked certs for a single CA */
83struct revoked_certs {
djm@openbsd.org74de2542014-12-04 01:49:59 +000084 struct sshkey *ca_key;
Damien Millerf3747bf2013-01-18 11:44:04 +110085 struct revoked_serial_tree revoked_serials;
86 struct revoked_key_id_tree revoked_key_ids;
87 TAILQ_ENTRY(revoked_certs) entry;
88};
89TAILQ_HEAD(revoked_certs_list, revoked_certs);
90
91struct ssh_krl {
92 u_int64_t krl_version;
93 u_int64_t generated_date;
94 u_int64_t flags;
95 char *comment;
96 struct revoked_blob_tree revoked_keys;
97 struct revoked_blob_tree revoked_sha1s;
98 struct revoked_certs_list revoked_certs;
99};
100
101/* Return equal if a and b overlap */
102static int
103serial_cmp(struct revoked_serial *a, struct revoked_serial *b)
104{
105 if (a->hi >= b->lo && a->lo <= b->hi)
106 return 0;
107 return a->lo < b->lo ? -1 : 1;
108}
109
110static int
111key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b)
112{
113 return strcmp(a->key_id, b->key_id);
114}
115
116static int
117blob_cmp(struct revoked_blob *a, struct revoked_blob *b)
118{
119 int r;
120
121 if (a->len != b->len) {
122 if ((r = memcmp(a->blob, b->blob, MIN(a->len, b->len))) != 0)
123 return r;
124 return a->len > b->len ? 1 : -1;
125 } else
126 return memcmp(a->blob, b->blob, a->len);
127}
128
129struct ssh_krl *
130ssh_krl_init(void)
131{
132 struct ssh_krl *krl;
133
134 if ((krl = calloc(1, sizeof(*krl))) == NULL)
135 return NULL;
136 RB_INIT(&krl->revoked_keys);
137 RB_INIT(&krl->revoked_sha1s);
138 TAILQ_INIT(&krl->revoked_certs);
139 return krl;
140}
141
142static void
143revoked_certs_free(struct revoked_certs *rc)
144{
145 struct revoked_serial *rs, *trs;
146 struct revoked_key_id *rki, *trki;
147
148 RB_FOREACH_SAFE(rs, revoked_serial_tree, &rc->revoked_serials, trs) {
149 RB_REMOVE(revoked_serial_tree, &rc->revoked_serials, rs);
150 free(rs);
151 }
152 RB_FOREACH_SAFE(rki, revoked_key_id_tree, &rc->revoked_key_ids, trki) {
153 RB_REMOVE(revoked_key_id_tree, &rc->revoked_key_ids, rki);
154 free(rki->key_id);
155 free(rki);
156 }
157 if (rc->ca_key != NULL)
djm@openbsd.org74de2542014-12-04 01:49:59 +0000158 sshkey_free(rc->ca_key);
Damien Millerf3747bf2013-01-18 11:44:04 +1100159}
160
161void
162ssh_krl_free(struct ssh_krl *krl)
163{
164 struct revoked_blob *rb, *trb;
165 struct revoked_certs *rc, *trc;
166
167 if (krl == NULL)
168 return;
169
170 free(krl->comment);
171 RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_keys, trb) {
172 RB_REMOVE(revoked_blob_tree, &krl->revoked_keys, rb);
173 free(rb->blob);
174 free(rb);
175 }
176 RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_sha1s, trb) {
177 RB_REMOVE(revoked_blob_tree, &krl->revoked_sha1s, rb);
178 free(rb->blob);
179 free(rb);
180 }
181 TAILQ_FOREACH_SAFE(rc, &krl->revoked_certs, entry, trc) {
182 TAILQ_REMOVE(&krl->revoked_certs, rc, entry);
183 revoked_certs_free(rc);
184 }
185}
186
187void
188ssh_krl_set_version(struct ssh_krl *krl, u_int64_t version)
189{
190 krl->krl_version = version;
191}
192
djm@openbsd.org74de2542014-12-04 01:49:59 +0000193int
Damien Millerf3747bf2013-01-18 11:44:04 +1100194ssh_krl_set_comment(struct ssh_krl *krl, const char *comment)
195{
196 free(krl->comment);
197 if ((krl->comment = strdup(comment)) == NULL)
djm@openbsd.org74de2542014-12-04 01:49:59 +0000198 return SSH_ERR_ALLOC_FAIL;
199 return 0;
Damien Millerf3747bf2013-01-18 11:44:04 +1100200}
201
202/*
203 * Find the revoked_certs struct for a CA key. If allow_create is set then
204 * create a new one in the tree if one did not exist already.
205 */
206static int
djm@openbsd.org74de2542014-12-04 01:49:59 +0000207revoked_certs_for_ca_key(struct ssh_krl *krl, const struct sshkey *ca_key,
Damien Millerf3747bf2013-01-18 11:44:04 +1100208 struct revoked_certs **rcp, int allow_create)
209{
210 struct revoked_certs *rc;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000211 int r;
Damien Millerf3747bf2013-01-18 11:44:04 +1100212
213 *rcp = NULL;
214 TAILQ_FOREACH(rc, &krl->revoked_certs, entry) {
djm@openbsd.org74de2542014-12-04 01:49:59 +0000215 if (sshkey_equal(rc->ca_key, ca_key)) {
Damien Millerf3747bf2013-01-18 11:44:04 +1100216 *rcp = rc;
217 return 0;
218 }
219 }
220 if (!allow_create)
221 return 0;
222 /* If this CA doesn't exist in the list then add it now */
223 if ((rc = calloc(1, sizeof(*rc))) == NULL)
djm@openbsd.org74de2542014-12-04 01:49:59 +0000224 return SSH_ERR_ALLOC_FAIL;
225 if ((r = sshkey_from_private(ca_key, &rc->ca_key)) != 0) {
Damien Millerf3747bf2013-01-18 11:44:04 +1100226 free(rc);
djm@openbsd.org74de2542014-12-04 01:49:59 +0000227 return r;
Damien Millerf3747bf2013-01-18 11:44:04 +1100228 }
229 RB_INIT(&rc->revoked_serials);
230 RB_INIT(&rc->revoked_key_ids);
231 TAILQ_INSERT_TAIL(&krl->revoked_certs, rc, entry);
djm@openbsd.org74de2542014-12-04 01:49:59 +0000232 debug3("%s: new CA %s", __func__, sshkey_type(ca_key));
Damien Millerf3747bf2013-01-18 11:44:04 +1100233 *rcp = rc;
234 return 0;
235}
236
237static int
238insert_serial_range(struct revoked_serial_tree *rt, u_int64_t lo, u_int64_t hi)
239{
240 struct revoked_serial rs, *ers, *crs, *irs;
241
242 KRL_DBG(("%s: insert %llu:%llu", __func__, lo, hi));
Damien Miller1d2c4562014-02-04 11:18:20 +1100243 memset(&rs, 0, sizeof(rs));
Damien Millerf3747bf2013-01-18 11:44:04 +1100244 rs.lo = lo;
245 rs.hi = hi;
246 ers = RB_NFIND(revoked_serial_tree, rt, &rs);
247 if (ers == NULL || serial_cmp(ers, &rs) != 0) {
248 /* No entry matches. Just insert */
249 if ((irs = malloc(sizeof(rs))) == NULL)
djm@openbsd.org74de2542014-12-04 01:49:59 +0000250 return SSH_ERR_ALLOC_FAIL;
Damien Millerf3747bf2013-01-18 11:44:04 +1100251 memcpy(irs, &rs, sizeof(*irs));
252 ers = RB_INSERT(revoked_serial_tree, rt, irs);
253 if (ers != NULL) {
254 KRL_DBG(("%s: bad: ers != NULL", __func__));
255 /* Shouldn't happen */
Damien Millera7522d92013-01-20 22:35:31 +1100256 free(irs);
djm@openbsd.org74de2542014-12-04 01:49:59 +0000257 return SSH_ERR_ALLOC_FAIL;
Damien Millerf3747bf2013-01-18 11:44:04 +1100258 }
259 ers = irs;
260 } else {
261 KRL_DBG(("%s: overlap found %llu:%llu", __func__,
262 ers->lo, ers->hi));
263 /*
264 * The inserted entry overlaps an existing one. Grow the
265 * existing entry.
266 */
267 if (ers->lo > lo)
268 ers->lo = lo;
269 if (ers->hi < hi)
270 ers->hi = hi;
271 }
272 /*
273 * The inserted or revised range might overlap or abut adjacent ones;
274 * coalesce as necessary.
275 */
276
277 /* Check predecessors */
278 while ((crs = RB_PREV(revoked_serial_tree, rt, ers)) != NULL) {
279 KRL_DBG(("%s: pred %llu:%llu", __func__, crs->lo, crs->hi));
280 if (ers->lo != 0 && crs->hi < ers->lo - 1)
281 break;
282 /* This entry overlaps. */
283 if (crs->lo < ers->lo) {
284 ers->lo = crs->lo;
285 KRL_DBG(("%s: pred extend %llu:%llu", __func__,
286 ers->lo, ers->hi));
287 }
288 RB_REMOVE(revoked_serial_tree, rt, crs);
289 free(crs);
290 }
291 /* Check successors */
292 while ((crs = RB_NEXT(revoked_serial_tree, rt, ers)) != NULL) {
293 KRL_DBG(("%s: succ %llu:%llu", __func__, crs->lo, crs->hi));
294 if (ers->hi != (u_int64_t)-1 && crs->lo > ers->hi + 1)
295 break;
296 /* This entry overlaps. */
297 if (crs->hi > ers->hi) {
298 ers->hi = crs->hi;
299 KRL_DBG(("%s: succ extend %llu:%llu", __func__,
300 ers->lo, ers->hi));
301 }
302 RB_REMOVE(revoked_serial_tree, rt, crs);
303 free(crs);
304 }
305 KRL_DBG(("%s: done, final %llu:%llu", __func__, ers->lo, ers->hi));
306 return 0;
307}
308
309int
djm@openbsd.org74de2542014-12-04 01:49:59 +0000310ssh_krl_revoke_cert_by_serial(struct ssh_krl *krl, const struct sshkey *ca_key,
Damien Millerf3747bf2013-01-18 11:44:04 +1100311 u_int64_t serial)
312{
313 return ssh_krl_revoke_cert_by_serial_range(krl, ca_key, serial, serial);
314}
315
316int
djm@openbsd.org74de2542014-12-04 01:49:59 +0000317ssh_krl_revoke_cert_by_serial_range(struct ssh_krl *krl, const struct sshkey *ca_key,
Damien Millerf3747bf2013-01-18 11:44:04 +1100318 u_int64_t lo, u_int64_t hi)
319{
320 struct revoked_certs *rc;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000321 int r;
Damien Millerf3747bf2013-01-18 11:44:04 +1100322
323 if (lo > hi || lo == 0)
324 return -1;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000325 if ((r = revoked_certs_for_ca_key(krl, ca_key, &rc, 1)) != 0)
326 return r;
Damien Millerf3747bf2013-01-18 11:44:04 +1100327 return insert_serial_range(&rc->revoked_serials, lo, hi);
328}
329
330int
djm@openbsd.org74de2542014-12-04 01:49:59 +0000331ssh_krl_revoke_cert_by_key_id(struct ssh_krl *krl, const struct sshkey *ca_key,
Damien Millerf3747bf2013-01-18 11:44:04 +1100332 const char *key_id)
333{
334 struct revoked_key_id *rki, *erki;
335 struct revoked_certs *rc;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000336 int r;
Damien Millerf3747bf2013-01-18 11:44:04 +1100337
djm@openbsd.org74de2542014-12-04 01:49:59 +0000338 if ((r = revoked_certs_for_ca_key(krl, ca_key, &rc, 1)) != 0)
339 return r;
Damien Millerf3747bf2013-01-18 11:44:04 +1100340
341 debug3("%s: revoke %s", __func__, key_id);
342 if ((rki = calloc(1, sizeof(*rki))) == NULL ||
343 (rki->key_id = strdup(key_id)) == NULL) {
344 free(rki);
djm@openbsd.org74de2542014-12-04 01:49:59 +0000345 return SSH_ERR_ALLOC_FAIL;
Damien Millerf3747bf2013-01-18 11:44:04 +1100346 }
347 erki = RB_INSERT(revoked_key_id_tree, &rc->revoked_key_ids, rki);
348 if (erki != NULL) {
349 free(rki->key_id);
350 free(rki);
351 }
352 return 0;
353}
354
355/* Convert "key" to a public key blob without any certificate information */
356static int
djm@openbsd.org74de2542014-12-04 01:49:59 +0000357plain_key_blob(const struct sshkey *key, u_char **blob, size_t *blen)
Damien Millerf3747bf2013-01-18 11:44:04 +1100358{
djm@openbsd.org74de2542014-12-04 01:49:59 +0000359 struct sshkey *kcopy;
Damien Millerf3747bf2013-01-18 11:44:04 +1100360 int r;
361
djm@openbsd.org74de2542014-12-04 01:49:59 +0000362 if ((r = sshkey_from_private(key, &kcopy)) != 0)
363 return r;
364 if (sshkey_is_cert(kcopy)) {
365 if ((r = sshkey_drop_cert(kcopy)) != 0) {
366 sshkey_free(kcopy);
367 return r;
Damien Millerf3747bf2013-01-18 11:44:04 +1100368 }
369 }
djm@openbsd.org74de2542014-12-04 01:49:59 +0000370 r = sshkey_to_blob(kcopy, blob, blen);
Damien Millerf3747bf2013-01-18 11:44:04 +1100371 free(kcopy);
Damien Miller86687062014-07-02 15:28:02 +1000372 return r;
Damien Millerf3747bf2013-01-18 11:44:04 +1100373}
374
375/* Revoke a key blob. Ownership of blob is transferred to the tree */
376static int
377revoke_blob(struct revoked_blob_tree *rbt, u_char *blob, u_int len)
378{
379 struct revoked_blob *rb, *erb;
380
381 if ((rb = calloc(1, sizeof(*rb))) == NULL)
djm@openbsd.org74de2542014-12-04 01:49:59 +0000382 return SSH_ERR_ALLOC_FAIL;
Damien Millerf3747bf2013-01-18 11:44:04 +1100383 rb->blob = blob;
384 rb->len = len;
385 erb = RB_INSERT(revoked_blob_tree, rbt, rb);
386 if (erb != NULL) {
387 free(rb->blob);
388 free(rb);
389 }
390 return 0;
391}
392
393int
djm@openbsd.org74de2542014-12-04 01:49:59 +0000394ssh_krl_revoke_key_explicit(struct ssh_krl *krl, const struct sshkey *key)
Damien Millerf3747bf2013-01-18 11:44:04 +1100395{
396 u_char *blob;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000397 size_t len;
398 int r;
Damien Millerf3747bf2013-01-18 11:44:04 +1100399
djm@openbsd.org74de2542014-12-04 01:49:59 +0000400 debug3("%s: revoke type %s", __func__, sshkey_type(key));
401 if ((r = plain_key_blob(key, &blob, &len)) != 0)
402 return r;
Damien Millerf3747bf2013-01-18 11:44:04 +1100403 return revoke_blob(&krl->revoked_keys, blob, len);
404}
405
406int
djm@openbsd.org74de2542014-12-04 01:49:59 +0000407ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const struct sshkey *key)
Damien Millerf3747bf2013-01-18 11:44:04 +1100408{
409 u_char *blob;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000410 size_t len;
411 int r;
Damien Millerf3747bf2013-01-18 11:44:04 +1100412
djm@openbsd.org74de2542014-12-04 01:49:59 +0000413 debug3("%s: revoke type %s by sha1", __func__, sshkey_type(key));
414 if ((r = sshkey_fingerprint_raw(key, SSH_FP_SHA1, &blob, &len)) != 0)
415 return r;
Damien Millerf3747bf2013-01-18 11:44:04 +1100416 return revoke_blob(&krl->revoked_sha1s, blob, len);
417}
418
419int
djm@openbsd.org74de2542014-12-04 01:49:59 +0000420ssh_krl_revoke_key(struct ssh_krl *krl, const struct sshkey *key)
Damien Millerf3747bf2013-01-18 11:44:04 +1100421{
djm@openbsd.org74de2542014-12-04 01:49:59 +0000422 if (!sshkey_is_cert(key))
Damien Millerf3747bf2013-01-18 11:44:04 +1100423 return ssh_krl_revoke_key_sha1(krl, key);
424
djm@openbsd.org74de2542014-12-04 01:49:59 +0000425 if (sshkey_cert_is_legacy(key) || key->cert->serial == 0) {
Damien Millerf3747bf2013-01-18 11:44:04 +1100426 return ssh_krl_revoke_cert_by_key_id(krl,
427 key->cert->signature_key,
428 key->cert->key_id);
429 } else {
430 return ssh_krl_revoke_cert_by_serial(krl,
431 key->cert->signature_key,
432 key->cert->serial);
433 }
434}
435
436/*
djm@openbsd.org74de2542014-12-04 01:49:59 +0000437 * Select the most compact section type to emit next in a KRL based on
438 * the current section type, the run length of contiguous revoked serial
Damien Millerf3747bf2013-01-18 11:44:04 +1100439 * numbers and the gaps from the last and to the next revoked serial.
440 * Applies a mostly-accurate bit cost model to select the section type
441 * that will minimise the size of the resultant KRL.
442 */
443static int
444choose_next_state(int current_state, u_int64_t contig, int final,
445 u_int64_t last_gap, u_int64_t next_gap, int *force_new_section)
446{
447 int new_state;
448 u_int64_t cost, cost_list, cost_range, cost_bitmap, cost_bitmap_restart;
449
450 /*
451 * Avoid unsigned overflows.
452 * The limits are high enough to avoid confusing the calculations.
453 */
454 contig = MIN(contig, 1ULL<<31);
455 last_gap = MIN(last_gap, 1ULL<<31);
456 next_gap = MIN(next_gap, 1ULL<<31);
457
458 /*
459 * Calculate the cost to switch from the current state to candidates.
460 * NB. range sections only ever contain a single range, so their
461 * switching cost is independent of the current_state.
462 */
463 cost_list = cost_bitmap = cost_bitmap_restart = 0;
464 cost_range = 8;
465 switch (current_state) {
466 case KRL_SECTION_CERT_SERIAL_LIST:
467 cost_bitmap_restart = cost_bitmap = 8 + 64;
468 break;
469 case KRL_SECTION_CERT_SERIAL_BITMAP:
470 cost_list = 8;
471 cost_bitmap_restart = 8 + 64;
472 break;
473 case KRL_SECTION_CERT_SERIAL_RANGE:
474 case 0:
475 cost_bitmap_restart = cost_bitmap = 8 + 64;
476 cost_list = 8;
477 }
478
479 /* Estimate base cost in bits of each section type */
480 cost_list += 64 * contig + (final ? 0 : 8+64);
481 cost_range += (2 * 64) + (final ? 0 : 8+64);
482 cost_bitmap += last_gap + contig + (final ? 0 : MIN(next_gap, 8+64));
483 cost_bitmap_restart += contig + (final ? 0 : MIN(next_gap, 8+64));
484
485 /* Convert to byte costs for actual comparison */
486 cost_list = (cost_list + 7) / 8;
487 cost_bitmap = (cost_bitmap + 7) / 8;
488 cost_bitmap_restart = (cost_bitmap_restart + 7) / 8;
489 cost_range = (cost_range + 7) / 8;
490
491 /* Now pick the best choice */
492 *force_new_section = 0;
493 new_state = KRL_SECTION_CERT_SERIAL_BITMAP;
494 cost = cost_bitmap;
495 if (cost_range < cost) {
496 new_state = KRL_SECTION_CERT_SERIAL_RANGE;
497 cost = cost_range;
498 }
499 if (cost_list < cost) {
500 new_state = KRL_SECTION_CERT_SERIAL_LIST;
501 cost = cost_list;
502 }
503 if (cost_bitmap_restart < cost) {
504 new_state = KRL_SECTION_CERT_SERIAL_BITMAP;
505 *force_new_section = 1;
506 cost = cost_bitmap_restart;
507 }
508 debug3("%s: contig %llu last_gap %llu next_gap %llu final %d, costs:"
509 "list %llu range %llu bitmap %llu new bitmap %llu, "
Damien Millerd677ad12013-04-23 15:18:51 +1000510 "selected 0x%02x%s", __func__, (long long unsigned)contig,
511 (long long unsigned)last_gap, (long long unsigned)next_gap, final,
512 (long long unsigned)cost_list, (long long unsigned)cost_range,
513 (long long unsigned)cost_bitmap,
514 (long long unsigned)cost_bitmap_restart, new_state,
Damien Millerf3747bf2013-01-18 11:44:04 +1100515 *force_new_section ? " restart" : "");
516 return new_state;
517}
518
519/* Generate a KRL_SECTION_CERTIFICATES KRL section */
520static int
djm@openbsd.org74de2542014-12-04 01:49:59 +0000521revoked_certs_generate(struct revoked_certs *rc, struct sshbuf *buf)
Damien Millerf3747bf2013-01-18 11:44:04 +1100522{
523 int final, force_new_sect, r = -1;
524 u_int64_t i, contig, gap, last = 0, bitmap_start = 0;
525 struct revoked_serial *rs, *nrs;
526 struct revoked_key_id *rki;
527 int next_state, state = 0;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000528 struct sshbuf *sect;
Damien Millerf3747bf2013-01-18 11:44:04 +1100529 BIGNUM *bitmap = NULL;
530
djm@openbsd.org74de2542014-12-04 01:49:59 +0000531 if ((sect = sshbuf_new()) == NULL)
532 return SSH_ERR_ALLOC_FAIL;
Damien Millerf3747bf2013-01-18 11:44:04 +1100533
djm@openbsd.org74de2542014-12-04 01:49:59 +0000534 /* Store the header: CA scope key, reserved */
535 if ((r = sshkey_to_blob_buf(rc->ca_key, sect)) != 0 ||
536 (r = sshbuf_put_stringb(buf, sect)) != 0 ||
537 (r = sshbuf_put_string(buf, NULL, 0)) != 0)
538 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100539
djm@openbsd.org74de2542014-12-04 01:49:59 +0000540 sshbuf_reset(sect);
Damien Millerf3747bf2013-01-18 11:44:04 +1100541
542 /* Store the revoked serials. */
543 for (rs = RB_MIN(revoked_serial_tree, &rc->revoked_serials);
544 rs != NULL;
545 rs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs)) {
546 debug3("%s: serial %llu:%llu state 0x%02x", __func__,
Damien Millerd677ad12013-04-23 15:18:51 +1000547 (long long unsigned)rs->lo, (long long unsigned)rs->hi,
548 state);
Damien Millerf3747bf2013-01-18 11:44:04 +1100549
550 /* Check contiguous length and gap to next section (if any) */
551 nrs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs);
552 final = nrs == NULL;
553 gap = nrs == NULL ? 0 : nrs->lo - rs->hi;
554 contig = 1 + (rs->hi - rs->lo);
555
556 /* Choose next state based on these */
557 next_state = choose_next_state(state, contig, final,
558 state == 0 ? 0 : rs->lo - last, gap, &force_new_sect);
559
560 /*
561 * If the current section is a range section or has a different
562 * type to the next section, then finish it off now.
563 */
564 if (state != 0 && (force_new_sect || next_state != state ||
565 state == KRL_SECTION_CERT_SERIAL_RANGE)) {
566 debug3("%s: finish state 0x%02x", __func__, state);
567 switch (state) {
568 case KRL_SECTION_CERT_SERIAL_LIST:
569 case KRL_SECTION_CERT_SERIAL_RANGE:
570 break;
571 case KRL_SECTION_CERT_SERIAL_BITMAP:
djm@openbsd.org74de2542014-12-04 01:49:59 +0000572 if ((r = sshbuf_put_bignum2(sect, bitmap)) != 0)
573 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100574 BN_free(bitmap);
575 bitmap = NULL;
576 break;
577 }
djm@openbsd.org74de2542014-12-04 01:49:59 +0000578 if ((r = sshbuf_put_u8(buf, state)) != 0 ||
579 (r = sshbuf_put_stringb(buf, sect)) != 0)
580 goto out;
581 sshbuf_reset(sect);
Damien Millerf3747bf2013-01-18 11:44:04 +1100582 }
583
584 /* If we are starting a new section then prepare it now */
585 if (next_state != state || force_new_sect) {
586 debug3("%s: start state 0x%02x", __func__, next_state);
587 state = next_state;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000588 sshbuf_reset(sect);
Damien Millerf3747bf2013-01-18 11:44:04 +1100589 switch (state) {
590 case KRL_SECTION_CERT_SERIAL_LIST:
591 case KRL_SECTION_CERT_SERIAL_RANGE:
592 break;
593 case KRL_SECTION_CERT_SERIAL_BITMAP:
djm@openbsd.org74de2542014-12-04 01:49:59 +0000594 if ((bitmap = BN_new()) == NULL) {
595 r = SSH_ERR_ALLOC_FAIL;
Damien Millerf3747bf2013-01-18 11:44:04 +1100596 goto out;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000597 }
Damien Millerf3747bf2013-01-18 11:44:04 +1100598 bitmap_start = rs->lo;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000599 if ((r = sshbuf_put_u64(sect,
600 bitmap_start)) != 0)
601 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100602 break;
603 }
604 }
605
606 /* Perform section-specific processing */
607 switch (state) {
608 case KRL_SECTION_CERT_SERIAL_LIST:
djm@openbsd.org74de2542014-12-04 01:49:59 +0000609 for (i = 0; i < contig; i++) {
610 if ((r = sshbuf_put_u64(sect, rs->lo + i)) != 0)
611 goto out;
612 }
Damien Millerf3747bf2013-01-18 11:44:04 +1100613 break;
614 case KRL_SECTION_CERT_SERIAL_RANGE:
djm@openbsd.org74de2542014-12-04 01:49:59 +0000615 if ((r = sshbuf_put_u64(sect, rs->lo)) != 0 ||
616 (r = sshbuf_put_u64(sect, rs->hi)) != 0)
617 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100618 break;
619 case KRL_SECTION_CERT_SERIAL_BITMAP:
620 if (rs->lo - bitmap_start > INT_MAX) {
621 error("%s: insane bitmap gap", __func__);
622 goto out;
623 }
624 for (i = 0; i < contig; i++) {
625 if (BN_set_bit(bitmap,
djm@openbsd.org74de2542014-12-04 01:49:59 +0000626 rs->lo + i - bitmap_start) != 1) {
627 r = SSH_ERR_ALLOC_FAIL;
Damien Millerf3747bf2013-01-18 11:44:04 +1100628 goto out;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000629 }
Damien Millerf3747bf2013-01-18 11:44:04 +1100630 }
631 break;
632 }
633 last = rs->hi;
634 }
635 /* Flush the remaining section, if any */
636 if (state != 0) {
637 debug3("%s: serial final flush for state 0x%02x",
638 __func__, state);
639 switch (state) {
640 case KRL_SECTION_CERT_SERIAL_LIST:
641 case KRL_SECTION_CERT_SERIAL_RANGE:
642 break;
643 case KRL_SECTION_CERT_SERIAL_BITMAP:
djm@openbsd.org74de2542014-12-04 01:49:59 +0000644 if ((r = sshbuf_put_bignum2(sect, bitmap)) != 0)
645 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100646 BN_free(bitmap);
647 bitmap = NULL;
648 break;
649 }
djm@openbsd.org74de2542014-12-04 01:49:59 +0000650 if ((r = sshbuf_put_u8(buf, state)) != 0 ||
651 (r = sshbuf_put_stringb(buf, sect)) != 0)
652 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100653 }
654 debug3("%s: serial done ", __func__);
655
656 /* Now output a section for any revocations by key ID */
djm@openbsd.org74de2542014-12-04 01:49:59 +0000657 sshbuf_reset(sect);
Damien Millerf3747bf2013-01-18 11:44:04 +1100658 RB_FOREACH(rki, revoked_key_id_tree, &rc->revoked_key_ids) {
659 debug3("%s: key ID %s", __func__, rki->key_id);
djm@openbsd.org74de2542014-12-04 01:49:59 +0000660 if ((r = sshbuf_put_cstring(sect, rki->key_id)) != 0)
661 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100662 }
djm@openbsd.org74de2542014-12-04 01:49:59 +0000663 if (sshbuf_len(sect) != 0) {
664 if ((r = sshbuf_put_u8(buf, KRL_SECTION_CERT_KEY_ID)) != 0 ||
665 (r = sshbuf_put_stringb(buf, sect)) != 0)
666 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100667 }
668 r = 0;
669 out:
670 if (bitmap != NULL)
671 BN_free(bitmap);
djm@openbsd.org74de2542014-12-04 01:49:59 +0000672 sshbuf_free(sect);
Damien Millerf3747bf2013-01-18 11:44:04 +1100673 return r;
674}
675
676int
djm@openbsd.org74de2542014-12-04 01:49:59 +0000677ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf,
678 const struct sshkey **sign_keys, u_int nsign_keys)
Damien Millerf3747bf2013-01-18 11:44:04 +1100679{
680 int r = -1;
681 struct revoked_certs *rc;
682 struct revoked_blob *rb;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000683 struct sshbuf *sect;
684 u_char *sblob = NULL;
685 size_t slen, i;
Damien Millerf3747bf2013-01-18 11:44:04 +1100686
687 if (krl->generated_date == 0)
688 krl->generated_date = time(NULL);
689
djm@openbsd.org74de2542014-12-04 01:49:59 +0000690 if ((sect = sshbuf_new()) == NULL)
691 return SSH_ERR_ALLOC_FAIL;
Damien Millerf3747bf2013-01-18 11:44:04 +1100692
693 /* Store the header */
djm@openbsd.org74de2542014-12-04 01:49:59 +0000694 if ((r = sshbuf_put(buf, KRL_MAGIC, sizeof(KRL_MAGIC) - 1)) != 0 ||
695 (r = sshbuf_put_u32(buf, KRL_FORMAT_VERSION)) != 0 ||
696 (r = sshbuf_put_u64(buf, krl->krl_version)) != 0 ||
697 (r = sshbuf_put_u64(buf, krl->generated_date) != 0) ||
698 (r = sshbuf_put_u64(buf, krl->flags)) != 0 ||
699 (r = sshbuf_put_string(buf, NULL, 0)) != 0 ||
700 (r = sshbuf_put_cstring(buf, krl->comment)) != 0)
701 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100702
703 /* Store sections for revoked certificates */
704 TAILQ_FOREACH(rc, &krl->revoked_certs, entry) {
djm@openbsd.org74de2542014-12-04 01:49:59 +0000705 sshbuf_reset(sect);
706 if ((r = revoked_certs_generate(rc, sect)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +1100707 goto out;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000708 if ((r = sshbuf_put_u8(buf, KRL_SECTION_CERTIFICATES)) != 0 ||
709 (r = sshbuf_put_stringb(buf, sect)) != 0)
710 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100711 }
712
713 /* Finally, output sections for revocations by public key/hash */
djm@openbsd.org74de2542014-12-04 01:49:59 +0000714 sshbuf_reset(sect);
Damien Millerf3747bf2013-01-18 11:44:04 +1100715 RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) {
djm@openbsd.org74de2542014-12-04 01:49:59 +0000716 debug3("%s: key len %zu ", __func__, rb->len);
717 if ((sshbuf_put_string(sect, rb->blob, rb->len)) != 0)
718 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100719 }
djm@openbsd.org74de2542014-12-04 01:49:59 +0000720 if (sshbuf_len(sect) != 0) {
721 if ((r = sshbuf_put_u8(buf, KRL_SECTION_EXPLICIT_KEY)) != 0 ||
722 (r = sshbuf_put_stringb(buf, sect)) != 0)
723 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100724 }
djm@openbsd.org74de2542014-12-04 01:49:59 +0000725 sshbuf_reset(sect);
Damien Millerf3747bf2013-01-18 11:44:04 +1100726 RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) {
djm@openbsd.org74de2542014-12-04 01:49:59 +0000727 debug3("%s: hash len %zu ", __func__, rb->len);
728 if ((sshbuf_put_string(sect, rb->blob, rb->len)) != 0)
729 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100730 }
djm@openbsd.org74de2542014-12-04 01:49:59 +0000731 if (sshbuf_len(sect) != 0) {
732 if ((r = sshbuf_put_u8(buf,
733 KRL_SECTION_FINGERPRINT_SHA1)) != 0 ||
734 (r = sshbuf_put_stringb(buf, sect)) != 0)
735 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100736 }
737
738 for (i = 0; i < nsign_keys; i++) {
djm@openbsd.org74de2542014-12-04 01:49:59 +0000739 sshbuf_reset(sect);
740 if ((r = sshkey_to_blob_buf(sign_keys[i], sect)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +1100741 goto out;
742
djm@openbsd.org74de2542014-12-04 01:49:59 +0000743 debug3("%s: signature key len %zu", __func__, sshbuf_len(sect));
744 if ((r = sshbuf_put_u8(buf, KRL_SECTION_SIGNATURE)) != 0 ||
745 (r = sshbuf_put_stringb(buf, sect)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +1100746 goto out;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000747
748 if ((r = sshkey_sign(sign_keys[i], &sblob, &slen,
749 sshbuf_ptr(buf), sshbuf_len(buf), 0)) == -1)
750 goto out;
751 debug3("%s: signature sig len %zu", __func__, slen);
752 if ((r = sshbuf_put_string(buf, sblob, slen)) != 0)
753 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100754 }
755
756 r = 0;
757 out:
Damien Millerf3747bf2013-01-18 11:44:04 +1100758 free(sblob);
djm@openbsd.org74de2542014-12-04 01:49:59 +0000759 sshbuf_free(sect);
Damien Millerf3747bf2013-01-18 11:44:04 +1100760 return r;
761}
762
763static void
764format_timestamp(u_int64_t timestamp, char *ts, size_t nts)
765{
766 time_t t;
767 struct tm *tm;
768
769 t = timestamp;
770 tm = localtime(&t);
djm@openbsd.orgb6de5ac2014-11-21 01:00:38 +0000771 if (tm == NULL)
772 strlcpy(ts, "<INVALID>", sizeof(nts));
773 else {
774 *ts = '\0';
775 strftime(ts, nts, "%Y%m%dT%H%M%S", tm);
776 }
Damien Millerf3747bf2013-01-18 11:44:04 +1100777}
778
779static int
djm@openbsd.org74de2542014-12-04 01:49:59 +0000780parse_revoked_certs(struct sshbuf *buf, struct ssh_krl *krl)
Damien Millerf3747bf2013-01-18 11:44:04 +1100781{
djm@openbsd.org74de2542014-12-04 01:49:59 +0000782 int r = -1, nbits;
Damien Miller633de332014-05-15 13:48:26 +1000783 u_char type;
784 const u_char *blob;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000785 size_t blen;
786 struct sshbuf *subsect = NULL;
Damien Millerf3747bf2013-01-18 11:44:04 +1100787 u_int64_t serial, serial_lo, serial_hi;
788 BIGNUM *bitmap = NULL;
789 char *key_id = NULL;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000790 struct sshkey *ca_key = NULL;
Damien Millerf3747bf2013-01-18 11:44:04 +1100791
djm@openbsd.org74de2542014-12-04 01:49:59 +0000792 if ((subsect = sshbuf_new()) == NULL)
793 return SSH_ERR_ALLOC_FAIL;
Damien Millerf3747bf2013-01-18 11:44:04 +1100794
djm@openbsd.org74de2542014-12-04 01:49:59 +0000795 /* Header: key, reserved */
796 if ((r = sshbuf_get_string_direct(buf, &blob, &blen)) != 0 ||
797 (r = sshbuf_skip_string(buf)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +1100798 goto out;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000799 if ((r = sshkey_from_blob(blob, blen, &ca_key)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +1100800 goto out;
801
djm@openbsd.org74de2542014-12-04 01:49:59 +0000802 while (sshbuf_len(buf) > 0) {
803 if (subsect != NULL) {
804 sshbuf_free(subsect);
805 subsect = NULL;
Damien Millerf3747bf2013-01-18 11:44:04 +1100806 }
djm@openbsd.org74de2542014-12-04 01:49:59 +0000807 if ((r = sshbuf_get_u8(buf, &type)) != 0 ||
808 (r = sshbuf_froms(buf, &subsect)) != 0)
809 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100810 debug3("%s: subsection type 0x%02x", __func__, type);
Damien Millerf3747bf2013-01-18 11:44:04 +1100811
812 switch (type) {
813 case KRL_SECTION_CERT_SERIAL_LIST:
djm@openbsd.org74de2542014-12-04 01:49:59 +0000814 while (sshbuf_len(subsect) > 0) {
815 if ((r = sshbuf_get_u64(subsect, &serial)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +1100816 goto out;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000817 if ((r = ssh_krl_revoke_cert_by_serial(krl,
818 ca_key, serial)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +1100819 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100820 }
821 break;
822 case KRL_SECTION_CERT_SERIAL_RANGE:
djm@openbsd.org74de2542014-12-04 01:49:59 +0000823 if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 ||
824 (r = sshbuf_get_u64(subsect, &serial_hi)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +1100825 goto out;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000826 if ((r = ssh_krl_revoke_cert_by_serial_range(krl,
827 ca_key, serial_lo, serial_hi)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +1100828 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100829 break;
830 case KRL_SECTION_CERT_SERIAL_BITMAP:
831 if ((bitmap = BN_new()) == NULL) {
djm@openbsd.org74de2542014-12-04 01:49:59 +0000832 r = SSH_ERR_ALLOC_FAIL;
Damien Millerf3747bf2013-01-18 11:44:04 +1100833 goto out;
834 }
djm@openbsd.org74de2542014-12-04 01:49:59 +0000835 if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 ||
836 (r = sshbuf_get_bignum2(subsect, bitmap)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +1100837 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100838 if ((nbits = BN_num_bits(bitmap)) < 0) {
839 error("%s: bitmap bits < 0", __func__);
djm@openbsd.org74de2542014-12-04 01:49:59 +0000840 r = SSH_ERR_INVALID_FORMAT;
Damien Millerf3747bf2013-01-18 11:44:04 +1100841 goto out;
842 }
843 for (serial = 0; serial < (u_int)nbits; serial++) {
844 if (serial > 0 && serial_lo + serial == 0) {
845 error("%s: bitmap wraps u64", __func__);
djm@openbsd.org74de2542014-12-04 01:49:59 +0000846 r = SSH_ERR_INVALID_FORMAT;
Damien Millerf3747bf2013-01-18 11:44:04 +1100847 goto out;
848 }
849 if (!BN_is_bit_set(bitmap, serial))
850 continue;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000851 if ((r = ssh_krl_revoke_cert_by_serial(krl,
852 ca_key, serial_lo + serial)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +1100853 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100854 }
855 BN_free(bitmap);
856 bitmap = NULL;
857 break;
858 case KRL_SECTION_CERT_KEY_ID:
djm@openbsd.org74de2542014-12-04 01:49:59 +0000859 while (sshbuf_len(subsect) > 0) {
860 if ((r = sshbuf_get_cstring(subsect,
861 &key_id, NULL)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +1100862 goto out;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000863 if ((r = ssh_krl_revoke_cert_by_key_id(krl,
864 ca_key, key_id)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +1100865 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100866 free(key_id);
867 key_id = NULL;
868 }
869 break;
870 default:
871 error("Unsupported KRL certificate section %u", type);
djm@openbsd.org74de2542014-12-04 01:49:59 +0000872 r = SSH_ERR_INVALID_FORMAT;
Damien Millerf3747bf2013-01-18 11:44:04 +1100873 goto out;
874 }
djm@openbsd.org74de2542014-12-04 01:49:59 +0000875 if (sshbuf_len(subsect) > 0) {
Damien Millerf3747bf2013-01-18 11:44:04 +1100876 error("KRL certificate section contains unparsed data");
djm@openbsd.org74de2542014-12-04 01:49:59 +0000877 r = SSH_ERR_INVALID_FORMAT;
Damien Millerf3747bf2013-01-18 11:44:04 +1100878 goto out;
879 }
880 }
881
djm@openbsd.org74de2542014-12-04 01:49:59 +0000882 r = 0;
Damien Millerf3747bf2013-01-18 11:44:04 +1100883 out:
Damien Millerf3747bf2013-01-18 11:44:04 +1100884 if (bitmap != NULL)
885 BN_free(bitmap);
886 free(key_id);
djm@openbsd.org74de2542014-12-04 01:49:59 +0000887 sshkey_free(ca_key);
888 sshbuf_free(subsect);
889 return r;
Damien Millerf3747bf2013-01-18 11:44:04 +1100890}
891
892
893/* Attempt to parse a KRL, checking its signature (if any) with sign_ca_keys. */
894int
djm@openbsd.org74de2542014-12-04 01:49:59 +0000895ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp,
896 const struct sshkey **sign_ca_keys, u_int nsign_ca_keys)
Damien Millerf3747bf2013-01-18 11:44:04 +1100897{
djm@openbsd.org74de2542014-12-04 01:49:59 +0000898 struct sshbuf *copy = NULL, *sect = NULL;
899 struct ssh_krl *krl = NULL;
Damien Millerf3747bf2013-01-18 11:44:04 +1100900 char timestamp[64];
djm@openbsd.org74de2542014-12-04 01:49:59 +0000901 int r = -1, sig_seen;
902 struct sshkey *key = NULL, **ca_used = NULL, **tmp_ca_used;
Damien Miller633de332014-05-15 13:48:26 +1000903 u_char type, *rdata = NULL;
904 const u_char *blob;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000905 size_t i, j, sig_off, sects_off, rlen, blen, nca_used;
906 u_int format_version;
Damien Millerf3747bf2013-01-18 11:44:04 +1100907
Damien Miller30710702013-07-18 16:09:44 +1000908 nca_used = 0;
Damien Millerf3747bf2013-01-18 11:44:04 +1100909 *krlp = NULL;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000910 if (sshbuf_len(buf) < sizeof(KRL_MAGIC) - 1 ||
911 memcmp(sshbuf_ptr(buf), KRL_MAGIC, sizeof(KRL_MAGIC) - 1) != 0) {
Damien Millerf3747bf2013-01-18 11:44:04 +1100912 debug3("%s: not a KRL", __func__);
djm@openbsd.org74de2542014-12-04 01:49:59 +0000913 return SSH_ERR_KRL_BAD_MAGIC;
Damien Millerf3747bf2013-01-18 11:44:04 +1100914 }
915
916 /* Take a copy of the KRL buffer so we can verify its signature later */
djm@openbsd.org74de2542014-12-04 01:49:59 +0000917 if ((copy = sshbuf_fromb(buf)) == NULL) {
918 r = SSH_ERR_ALLOC_FAIL;
919 goto out;
920 }
921 if ((r = sshbuf_consume(copy, sizeof(KRL_MAGIC) - 1)) != 0)
922 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100923
924 if ((krl = ssh_krl_init()) == NULL) {
925 error("%s: alloc failed", __func__);
926 goto out;
927 }
928
djm@openbsd.org74de2542014-12-04 01:49:59 +0000929 if ((r = sshbuf_get_u32(copy, &format_version)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +1100930 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100931 if (format_version != KRL_FORMAT_VERSION) {
djm@openbsd.org74de2542014-12-04 01:49:59 +0000932 r = SSH_ERR_INVALID_FORMAT;
Damien Millerf3747bf2013-01-18 11:44:04 +1100933 goto out;
934 }
djm@openbsd.org74de2542014-12-04 01:49:59 +0000935 if ((r = sshbuf_get_u64(copy, &krl->krl_version)) != 0 ||
936 (r = sshbuf_get_u64(copy, &krl->generated_date)) != 0 ||
937 (r = sshbuf_get_u64(copy, &krl->flags)) != 0 ||
938 (r = sshbuf_skip_string(copy)) != 0 ||
939 (r = sshbuf_get_cstring(copy, &krl->comment, NULL)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +1100940 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100941
942 format_timestamp(krl->generated_date, timestamp, sizeof(timestamp));
Damien Millerd677ad12013-04-23 15:18:51 +1000943 debug("KRL version %llu generated at %s%s%s",
944 (long long unsigned)krl->krl_version, timestamp,
945 *krl->comment ? ": " : "", krl->comment);
Damien Millerf3747bf2013-01-18 11:44:04 +1100946
947 /*
948 * 1st pass: verify signatures, if any. This is done to avoid
949 * detailed parsing of data whose provenance is unverified.
950 */
951 sig_seen = 0;
djm@openbsd.org74de2542014-12-04 01:49:59 +0000952 if (sshbuf_len(buf) < sshbuf_len(copy)) {
953 /* Shouldn't happen */
954 r = SSH_ERR_INTERNAL_ERROR;
955 goto out;
956 }
957 sects_off = sshbuf_len(buf) - sshbuf_len(copy);
958 while (sshbuf_len(copy) > 0) {
959 if ((r = sshbuf_get_u8(copy, &type)) != 0 ||
960 (r = sshbuf_get_string_direct(copy, &blob, &blen)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +1100961 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +1100962 debug3("%s: first pass, section 0x%02x", __func__, type);
963 if (type != KRL_SECTION_SIGNATURE) {
964 if (sig_seen) {
djm@openbsd.org74de2542014-12-04 01:49:59 +0000965 r = SSH_ERR_INVALID_FORMAT;
Damien Millerf3747bf2013-01-18 11:44:04 +1100966 error("KRL contains non-signature section "
967 "after signature");
968 goto out;
969 }
970 /* Not interested for now. */
971 continue;
972 }
973 sig_seen = 1;
974 /* First string component is the signing key */
djm@openbsd.org74de2542014-12-04 01:49:59 +0000975 if ((r = sshkey_from_blob(blob, blen, &key)) != 0) {
976 r = SSH_ERR_INVALID_FORMAT;
Damien Millerf3747bf2013-01-18 11:44:04 +1100977 error("%s: invalid signature key", __func__);
978 goto out;
979 }
djm@openbsd.org74de2542014-12-04 01:49:59 +0000980 if (sshbuf_len(buf) < sshbuf_len(copy)) {
981 /* Shouldn't happen */
982 r = SSH_ERR_INTERNAL_ERROR;
983 goto out;
984 }
985 sig_off = sshbuf_len(buf) - sshbuf_len(copy);
Damien Millerf3747bf2013-01-18 11:44:04 +1100986 /* Second string component is the signature itself */
djm@openbsd.org74de2542014-12-04 01:49:59 +0000987 if ((r = sshbuf_get_string_direct(copy, &blob, &blen)) != 0) {
988 r = SSH_ERR_INVALID_FORMAT;
Damien Millerf3747bf2013-01-18 11:44:04 +1100989 goto out;
990 }
991 /* Check signature over entire KRL up to this point */
djm@openbsd.org74de2542014-12-04 01:49:59 +0000992 if ((r = sshkey_verify(key, blob, blen,
993 sshbuf_ptr(buf), sshbuf_len(buf) - sig_off, 0)) != 0) {
Damien Millerf3747bf2013-01-18 11:44:04 +1100994 error("bad signaure on KRL");
995 goto out;
996 }
997 /* Check if this key has already signed this KRL */
998 for (i = 0; i < nca_used; i++) {
djm@openbsd.org74de2542014-12-04 01:49:59 +0000999 if (sshkey_equal(ca_used[i], key)) {
Damien Millerf3747bf2013-01-18 11:44:04 +11001000 error("KRL signed more than once with "
1001 "the same key");
djm@openbsd.org74de2542014-12-04 01:49:59 +00001002 r = SSH_ERR_SIGNATURE_INVALID;
Damien Millerf3747bf2013-01-18 11:44:04 +11001003 goto out;
1004 }
1005 }
1006 /* Record keys used to sign the KRL */
djm@openbsd.org74de2542014-12-04 01:49:59 +00001007 tmp_ca_used = reallocarray(ca_used, nca_used + 1,
1008 sizeof(*ca_used));
1009 if (tmp_ca_used == NULL) {
1010 r = SSH_ERR_ALLOC_FAIL;
1011 goto out;
1012 }
1013 ca_used = tmp_ca_used;
Damien Millerf3747bf2013-01-18 11:44:04 +11001014 ca_used[nca_used++] = key;
1015 key = NULL;
1016 break;
1017 }
1018
djm@openbsd.org74de2542014-12-04 01:49:59 +00001019 if (sshbuf_len(copy) != 0) {
1020 /* Shouldn't happen */
1021 r = SSH_ERR_INTERNAL_ERROR;
1022 goto out;
1023 }
1024
Damien Millerf3747bf2013-01-18 11:44:04 +11001025 /*
1026 * 2nd pass: parse and load the KRL, skipping the header to the point
1027 * where the section start.
1028 */
djm@openbsd.org74de2542014-12-04 01:49:59 +00001029 sshbuf_free(copy);
1030 if ((copy = sshbuf_fromb(buf)) == NULL) {
1031 r = SSH_ERR_ALLOC_FAIL;
1032 goto out;
1033 }
1034 if ((r = sshbuf_consume(copy, sects_off)) != 0)
1035 goto out;
1036 while (sshbuf_len(copy) > 0) {
1037 if (sect != NULL) {
1038 sshbuf_free(sect);
1039 sect = NULL;
1040 }
1041 if ((r = sshbuf_get_u8(copy, &type)) != 0 ||
1042 (r = sshbuf_froms(copy, &sect)) != 0) {
Damien Millerf3747bf2013-01-18 11:44:04 +11001043 goto out;
1044 }
1045 debug3("%s: second pass, section 0x%02x", __func__, type);
Damien Millerf3747bf2013-01-18 11:44:04 +11001046
1047 switch (type) {
1048 case KRL_SECTION_CERTIFICATES:
djm@openbsd.org74de2542014-12-04 01:49:59 +00001049 if ((r = parse_revoked_certs(sect, krl)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +11001050 goto out;
1051 break;
1052 case KRL_SECTION_EXPLICIT_KEY:
1053 case KRL_SECTION_FINGERPRINT_SHA1:
djm@openbsd.org74de2542014-12-04 01:49:59 +00001054 while (sshbuf_len(sect) > 0) {
1055 if ((r = sshbuf_get_string(sect,
1056 &rdata, &rlen)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +11001057 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +11001058 if (type == KRL_SECTION_FINGERPRINT_SHA1 &&
Damien Miller30710702013-07-18 16:09:44 +10001059 rlen != 20) {
Damien Millerf3747bf2013-01-18 11:44:04 +11001060 error("%s: bad SHA1 length", __func__);
djm@openbsd.org74de2542014-12-04 01:49:59 +00001061 r = SSH_ERR_INVALID_FORMAT;
Damien Millerf3747bf2013-01-18 11:44:04 +11001062 goto out;
1063 }
djm@openbsd.org74de2542014-12-04 01:49:59 +00001064 if ((r = revoke_blob(
Damien Millerf3747bf2013-01-18 11:44:04 +11001065 type == KRL_SECTION_EXPLICIT_KEY ?
1066 &krl->revoked_keys : &krl->revoked_sha1s,
djm@openbsd.org74de2542014-12-04 01:49:59 +00001067 rdata, rlen)) != 0)
Damien Miller30710702013-07-18 16:09:44 +10001068 goto out;
1069 rdata = NULL; /* revoke_blob frees blob */
Damien Millerf3747bf2013-01-18 11:44:04 +11001070 }
1071 break;
1072 case KRL_SECTION_SIGNATURE:
1073 /* Handled above, but still need to stay in synch */
djm@openbsd.org74de2542014-12-04 01:49:59 +00001074 sshbuf_reset(sect);
1075 sect = NULL;
1076 if ((r = sshbuf_skip_string(copy)) != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +11001077 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +11001078 break;
1079 default:
1080 error("Unsupported KRL section %u", type);
djm@openbsd.org74de2542014-12-04 01:49:59 +00001081 r = SSH_ERR_INVALID_FORMAT;
Damien Millerf3747bf2013-01-18 11:44:04 +11001082 goto out;
1083 }
djm@openbsd.org74de2542014-12-04 01:49:59 +00001084 if (sshbuf_len(sect) > 0) {
Damien Millerf3747bf2013-01-18 11:44:04 +11001085 error("KRL section contains unparsed data");
djm@openbsd.org74de2542014-12-04 01:49:59 +00001086 r = SSH_ERR_INVALID_FORMAT;
Damien Millerf3747bf2013-01-18 11:44:04 +11001087 goto out;
1088 }
1089 }
1090
1091 /* Check that the key(s) used to sign the KRL weren't revoked */
1092 sig_seen = 0;
1093 for (i = 0; i < nca_used; i++) {
1094 if (ssh_krl_check_key(krl, ca_used[i]) == 0)
1095 sig_seen = 1;
1096 else {
djm@openbsd.org74de2542014-12-04 01:49:59 +00001097 sshkey_free(ca_used[i]);
Damien Millerf3747bf2013-01-18 11:44:04 +11001098 ca_used[i] = NULL;
1099 }
1100 }
1101 if (nca_used && !sig_seen) {
djm@openbsd.org74de2542014-12-04 01:49:59 +00001102 r = SSH_ERR_SIGNATURE_INVALID;
Damien Millerf3747bf2013-01-18 11:44:04 +11001103 error("All keys used to sign KRL were revoked");
1104 goto out;
1105 }
1106
1107 /* If we have CA keys, then verify that one was used to sign the KRL */
1108 if (sig_seen && nsign_ca_keys != 0) {
1109 sig_seen = 0;
1110 for (i = 0; !sig_seen && i < nsign_ca_keys; i++) {
1111 for (j = 0; j < nca_used; j++) {
1112 if (ca_used[j] == NULL)
1113 continue;
djm@openbsd.org74de2542014-12-04 01:49:59 +00001114 if (sshkey_equal(ca_used[j], sign_ca_keys[i])) {
Damien Millerf3747bf2013-01-18 11:44:04 +11001115 sig_seen = 1;
1116 break;
1117 }
1118 }
1119 }
1120 if (!sig_seen) {
djm@openbsd.org74de2542014-12-04 01:49:59 +00001121 r = SSH_ERR_SIGNATURE_INVALID;
Damien Millerf3747bf2013-01-18 11:44:04 +11001122 error("KRL not signed with any trusted key");
1123 goto out;
1124 }
1125 }
1126
1127 *krlp = krl;
djm@openbsd.org74de2542014-12-04 01:49:59 +00001128 r = 0;
Damien Millerf3747bf2013-01-18 11:44:04 +11001129 out:
djm@openbsd.org74de2542014-12-04 01:49:59 +00001130 if (r != 0)
Damien Millerf3747bf2013-01-18 11:44:04 +11001131 ssh_krl_free(krl);
djm@openbsd.org74de2542014-12-04 01:49:59 +00001132 for (i = 0; i < nca_used; i++)
1133 sshkey_free(ca_used[i]);
Damien Millerf3747bf2013-01-18 11:44:04 +11001134 free(ca_used);
Damien Miller30710702013-07-18 16:09:44 +10001135 free(rdata);
djm@openbsd.org74de2542014-12-04 01:49:59 +00001136 sshkey_free(key);
1137 sshbuf_free(copy);
1138 sshbuf_free(sect);
1139 return r;
Damien Millerf3747bf2013-01-18 11:44:04 +11001140}
1141
1142/* Checks whether a given key/cert is revoked. Does not check its CA */
1143static int
djm@openbsd.org74de2542014-12-04 01:49:59 +00001144is_key_revoked(struct ssh_krl *krl, const struct sshkey *key)
Damien Millerf3747bf2013-01-18 11:44:04 +11001145{
1146 struct revoked_blob rb, *erb;
1147 struct revoked_serial rs, *ers;
1148 struct revoked_key_id rki, *erki;
1149 struct revoked_certs *rc;
djm@openbsd.org74de2542014-12-04 01:49:59 +00001150 int r;
Damien Millerf3747bf2013-01-18 11:44:04 +11001151
1152 /* Check explicitly revoked hashes first */
Damien Miller1d2c4562014-02-04 11:18:20 +11001153 memset(&rb, 0, sizeof(rb));
djm@openbsd.org74de2542014-12-04 01:49:59 +00001154 if ((r = sshkey_fingerprint_raw(key, SSH_FP_SHA1,
1155 &rb.blob, &rb.len)) != 0)
1156 return r;
Damien Millerf3747bf2013-01-18 11:44:04 +11001157 erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha1s, &rb);
1158 free(rb.blob);
1159 if (erb != NULL) {
1160 debug("%s: revoked by key SHA1", __func__);
djm@openbsd.org74de2542014-12-04 01:49:59 +00001161 return SSH_ERR_KEY_REVOKED;
Damien Millerf3747bf2013-01-18 11:44:04 +11001162 }
1163
1164 /* Next, explicit keys */
Damien Miller1d2c4562014-02-04 11:18:20 +11001165 memset(&rb, 0, sizeof(rb));
djm@openbsd.org74de2542014-12-04 01:49:59 +00001166 if ((r = plain_key_blob(key, &rb.blob, &rb.len)) != 0)
1167 return r;
Damien Millerf3747bf2013-01-18 11:44:04 +11001168 erb = RB_FIND(revoked_blob_tree, &krl->revoked_keys, &rb);
1169 free(rb.blob);
1170 if (erb != NULL) {
1171 debug("%s: revoked by explicit key", __func__);
djm@openbsd.org74de2542014-12-04 01:49:59 +00001172 return SSH_ERR_KEY_REVOKED;
Damien Millerf3747bf2013-01-18 11:44:04 +11001173 }
1174
djm@openbsd.org74de2542014-12-04 01:49:59 +00001175 if (!sshkey_is_cert(key))
Damien Millerf3747bf2013-01-18 11:44:04 +11001176 return 0;
1177
1178 /* Check cert revocation */
djm@openbsd.org74de2542014-12-04 01:49:59 +00001179 if ((r = revoked_certs_for_ca_key(krl, key->cert->signature_key,
1180 &rc, 0)) != 0)
1181 return r;
Damien Millerf3747bf2013-01-18 11:44:04 +11001182 if (rc == NULL)
1183 return 0; /* No entry for this CA */
1184
1185 /* Check revocation by cert key ID */
Damien Miller1d2c4562014-02-04 11:18:20 +11001186 memset(&rki, 0, sizeof(rki));
Damien Millerf3747bf2013-01-18 11:44:04 +11001187 rki.key_id = key->cert->key_id;
1188 erki = RB_FIND(revoked_key_id_tree, &rc->revoked_key_ids, &rki);
1189 if (erki != NULL) {
1190 debug("%s: revoked by key ID", __func__);
djm@openbsd.org74de2542014-12-04 01:49:59 +00001191 return SSH_ERR_KEY_REVOKED;
Damien Millerf3747bf2013-01-18 11:44:04 +11001192 }
1193
Damien Miller60565bc2013-02-12 10:56:42 +11001194 /*
1195 * Legacy cert formats lack serial numbers. Zero serials numbers
1196 * are ignored (it's the default when the CA doesn't specify one).
1197 */
djm@openbsd.org74de2542014-12-04 01:49:59 +00001198 if (sshkey_cert_is_legacy(key) || key->cert->serial == 0)
Damien Millerf3747bf2013-01-18 11:44:04 +11001199 return 0;
1200
Damien Miller1d2c4562014-02-04 11:18:20 +11001201 memset(&rs, 0, sizeof(rs));
Damien Millerf3747bf2013-01-18 11:44:04 +11001202 rs.lo = rs.hi = key->cert->serial;
1203 ers = RB_FIND(revoked_serial_tree, &rc->revoked_serials, &rs);
1204 if (ers != NULL) {
1205 KRL_DBG(("%s: %llu matched %llu:%llu", __func__,
1206 key->cert->serial, ers->lo, ers->hi));
1207 debug("%s: revoked by serial", __func__);
djm@openbsd.org74de2542014-12-04 01:49:59 +00001208 return SSH_ERR_KEY_REVOKED;
Damien Millerf3747bf2013-01-18 11:44:04 +11001209 }
1210 KRL_DBG(("%s: %llu no match", __func__, key->cert->serial));
1211
1212 return 0;
1213}
1214
1215int
djm@openbsd.org74de2542014-12-04 01:49:59 +00001216ssh_krl_check_key(struct ssh_krl *krl, const struct sshkey *key)
Damien Millerf3747bf2013-01-18 11:44:04 +11001217{
1218 int r;
1219
1220 debug2("%s: checking key", __func__);
1221 if ((r = is_key_revoked(krl, key)) != 0)
1222 return r;
djm@openbsd.org74de2542014-12-04 01:49:59 +00001223 if (sshkey_is_cert(key)) {
Damien Millerf3747bf2013-01-18 11:44:04 +11001224 debug2("%s: checking CA key", __func__);
1225 if ((r = is_key_revoked(krl, key->cert->signature_key)) != 0)
1226 return r;
1227 }
1228 debug3("%s: key okay", __func__);
1229 return 0;
1230}
1231
Damien Millerf3747bf2013-01-18 11:44:04 +11001232int
djm@openbsd.org74de2542014-12-04 01:49:59 +00001233ssh_krl_file_contains_key(const char *path, const struct sshkey *key)
Damien Millerf3747bf2013-01-18 11:44:04 +11001234{
djm@openbsd.org74de2542014-12-04 01:49:59 +00001235 struct sshbuf *krlbuf = NULL;
1236 struct ssh_krl *krl = NULL;
1237 int oerrno = 0, r, fd;
Damien Millerf3747bf2013-01-18 11:44:04 +11001238
1239 if (path == NULL)
1240 return 0;
1241
djm@openbsd.org74de2542014-12-04 01:49:59 +00001242 if ((krlbuf = sshbuf_new()) == NULL)
1243 return SSH_ERR_ALLOC_FAIL;
Damien Millerf3747bf2013-01-18 11:44:04 +11001244 if ((fd = open(path, O_RDONLY)) == -1) {
djm@openbsd.org74de2542014-12-04 01:49:59 +00001245 r = SSH_ERR_SYSTEM_ERROR;
1246 oerrno = errno;
1247 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +11001248 }
djm@openbsd.org74de2542014-12-04 01:49:59 +00001249 if ((r = sshkey_load_file(fd, path, krlbuf)) != 0) {
1250 oerrno = errno;
1251 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +11001252 }
djm@openbsd.org74de2542014-12-04 01:49:59 +00001253 if ((r = ssh_krl_from_blob(krlbuf, &krl, NULL, 0)) != 0)
1254 goto out;
Damien Millerf3747bf2013-01-18 11:44:04 +11001255 debug2("%s: checking KRL %s", __func__, path);
djm@openbsd.org74de2542014-12-04 01:49:59 +00001256 r = ssh_krl_check_key(krl, key);
1257 out:
1258 close(fd);
1259 sshbuf_free(krlbuf);
Damien Millerf3747bf2013-01-18 11:44:04 +11001260 ssh_krl_free(krl);
djm@openbsd.org74de2542014-12-04 01:49:59 +00001261 if (r != 0)
1262 errno = oerrno;
1263 return r;
Damien Millerf3747bf2013-01-18 11:44:04 +11001264}