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