Annotation of src/usr.bin/ssh/krl.c, Revision 1.22
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.22 ! djm 17: /* $OpenBSD: krl.c,v 1.21 2014/12/21 22:27:56 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"
32: #include "sshkey.h"
1.1 djm 33: #include "authfile.h"
34: #include "misc.h"
35: #include "log.h"
1.20 djm 36: #include "ssherr.h"
1.21 djm 37: #include "digest.h"
1.1 djm 38:
39: #include "krl.h"
40:
41: /* #define DEBUG_KRL */
42: #ifdef DEBUG_KRL
43: # define KRL_DBG(x) debug3 x
44: #else
45: # define KRL_DBG(x)
46: #endif
47:
48: /*
49: * Trees of revoked serial numbers, key IDs and keys. This allows
50: * quick searching, querying and producing lists in canonical order.
51: */
52:
53: /* Tree of serial numbers. XXX make smarter: really need a real sparse bitmap */
54: struct revoked_serial {
55: u_int64_t lo, hi;
56: RB_ENTRY(revoked_serial) tree_entry;
57: };
58: static int serial_cmp(struct revoked_serial *a, struct revoked_serial *b);
59: RB_HEAD(revoked_serial_tree, revoked_serial);
60: RB_GENERATE_STATIC(revoked_serial_tree, revoked_serial, tree_entry, serial_cmp);
61:
62: /* Tree of key IDs */
63: struct revoked_key_id {
64: char *key_id;
65: RB_ENTRY(revoked_key_id) tree_entry;
66: };
67: static int key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b);
68: RB_HEAD(revoked_key_id_tree, revoked_key_id);
69: RB_GENERATE_STATIC(revoked_key_id_tree, revoked_key_id, tree_entry, key_id_cmp);
70:
71: /* Tree of blobs (used for keys and fingerprints) */
72: struct revoked_blob {
73: u_char *blob;
1.20 djm 74: size_t len;
1.1 djm 75: RB_ENTRY(revoked_blob) tree_entry;
76: };
77: static int blob_cmp(struct revoked_blob *a, struct revoked_blob *b);
78: RB_HEAD(revoked_blob_tree, revoked_blob);
79: RB_GENERATE_STATIC(revoked_blob_tree, revoked_blob, tree_entry, blob_cmp);
80:
81: /* Tracks revoked certs for a single CA */
82: struct revoked_certs {
1.20 djm 83: struct sshkey *ca_key;
1.1 djm 84: struct revoked_serial_tree revoked_serials;
85: struct revoked_key_id_tree revoked_key_ids;
86: TAILQ_ENTRY(revoked_certs) entry;
87: };
88: TAILQ_HEAD(revoked_certs_list, revoked_certs);
89:
90: struct ssh_krl {
91: u_int64_t krl_version;
92: u_int64_t generated_date;
93: u_int64_t flags;
94: char *comment;
95: struct revoked_blob_tree revoked_keys;
96: struct revoked_blob_tree revoked_sha1s;
97: struct revoked_certs_list revoked_certs;
98: };
99:
100: /* Return equal if a and b overlap */
101: static int
102: serial_cmp(struct revoked_serial *a, struct revoked_serial *b)
103: {
104: if (a->hi >= b->lo && a->lo <= b->hi)
105: return 0;
106: return a->lo < b->lo ? -1 : 1;
107: }
108:
109: static int
110: key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b)
111: {
112: return strcmp(a->key_id, b->key_id);
113: }
114:
115: static int
116: blob_cmp(struct revoked_blob *a, struct revoked_blob *b)
117: {
118: int r;
119:
120: if (a->len != b->len) {
121: if ((r = memcmp(a->blob, b->blob, MIN(a->len, b->len))) != 0)
122: return r;
123: return a->len > b->len ? 1 : -1;
124: } else
125: return memcmp(a->blob, b->blob, a->len);
126: }
127:
128: struct ssh_krl *
129: ssh_krl_init(void)
130: {
131: struct ssh_krl *krl;
132:
133: if ((krl = calloc(1, sizeof(*krl))) == NULL)
134: return NULL;
135: RB_INIT(&krl->revoked_keys);
136: RB_INIT(&krl->revoked_sha1s);
137: TAILQ_INIT(&krl->revoked_certs);
138: return krl;
139: }
140:
141: static void
142: revoked_certs_free(struct revoked_certs *rc)
143: {
144: struct revoked_serial *rs, *trs;
145: struct revoked_key_id *rki, *trki;
146:
147: RB_FOREACH_SAFE(rs, revoked_serial_tree, &rc->revoked_serials, trs) {
148: RB_REMOVE(revoked_serial_tree, &rc->revoked_serials, rs);
149: free(rs);
150: }
151: RB_FOREACH_SAFE(rki, revoked_key_id_tree, &rc->revoked_key_ids, trki) {
152: RB_REMOVE(revoked_key_id_tree, &rc->revoked_key_ids, rki);
153: free(rki->key_id);
154: free(rki);
155: }
156: if (rc->ca_key != NULL)
1.20 djm 157: sshkey_free(rc->ca_key);
1.1 djm 158: }
159:
160: void
161: ssh_krl_free(struct ssh_krl *krl)
162: {
163: struct revoked_blob *rb, *trb;
164: struct revoked_certs *rc, *trc;
165:
166: if (krl == NULL)
167: return;
168:
169: free(krl->comment);
170: RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_keys, trb) {
171: RB_REMOVE(revoked_blob_tree, &krl->revoked_keys, rb);
172: free(rb->blob);
173: free(rb);
174: }
175: RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_sha1s, trb) {
176: RB_REMOVE(revoked_blob_tree, &krl->revoked_sha1s, rb);
177: free(rb->blob);
178: free(rb);
179: }
180: TAILQ_FOREACH_SAFE(rc, &krl->revoked_certs, entry, trc) {
181: TAILQ_REMOVE(&krl->revoked_certs, rc, entry);
182: revoked_certs_free(rc);
183: }
184: }
185:
186: void
187: ssh_krl_set_version(struct ssh_krl *krl, u_int64_t version)
188: {
189: krl->krl_version = version;
190: }
191:
1.20 djm 192: int
1.1 djm 193: ssh_krl_set_comment(struct ssh_krl *krl, const char *comment)
194: {
195: free(krl->comment);
196: if ((krl->comment = strdup(comment)) == NULL)
1.20 djm 197: return SSH_ERR_ALLOC_FAIL;
198: return 0;
1.1 djm 199: }
200:
201: /*
202: * Find the revoked_certs struct for a CA key. If allow_create is set then
203: * create a new one in the tree if one did not exist already.
204: */
205: static int
1.20 djm 206: revoked_certs_for_ca_key(struct ssh_krl *krl, const struct sshkey *ca_key,
1.1 djm 207: struct revoked_certs **rcp, int allow_create)
208: {
209: struct revoked_certs *rc;
1.20 djm 210: int r;
1.1 djm 211:
212: *rcp = NULL;
213: TAILQ_FOREACH(rc, &krl->revoked_certs, entry) {
1.20 djm 214: if (sshkey_equal(rc->ca_key, ca_key)) {
1.1 djm 215: *rcp = rc;
216: return 0;
217: }
218: }
219: if (!allow_create)
220: return 0;
221: /* If this CA doesn't exist in the list then add it now */
222: if ((rc = calloc(1, sizeof(*rc))) == NULL)
1.20 djm 223: return SSH_ERR_ALLOC_FAIL;
224: if ((r = sshkey_from_private(ca_key, &rc->ca_key)) != 0) {
1.1 djm 225: free(rc);
1.20 djm 226: return r;
1.1 djm 227: }
228: RB_INIT(&rc->revoked_serials);
229: RB_INIT(&rc->revoked_key_ids);
230: TAILQ_INSERT_TAIL(&krl->revoked_certs, rc, entry);
1.20 djm 231: debug3("%s: new CA %s", __func__, sshkey_type(ca_key));
1.1 djm 232: *rcp = rc;
233: return 0;
234: }
235:
236: static int
237: insert_serial_range(struct revoked_serial_tree *rt, u_int64_t lo, u_int64_t hi)
238: {
239: struct revoked_serial rs, *ers, *crs, *irs;
240:
241: KRL_DBG(("%s: insert %llu:%llu", __func__, lo, hi));
1.14 tedu 242: memset(&rs, 0, sizeof(rs));
1.1 djm 243: rs.lo = lo;
244: rs.hi = hi;
245: ers = RB_NFIND(revoked_serial_tree, rt, &rs);
246: if (ers == NULL || serial_cmp(ers, &rs) != 0) {
247: /* No entry matches. Just insert */
248: if ((irs = malloc(sizeof(rs))) == NULL)
1.20 djm 249: return SSH_ERR_ALLOC_FAIL;
1.1 djm 250: memcpy(irs, &rs, sizeof(*irs));
251: ers = RB_INSERT(revoked_serial_tree, rt, irs);
252: if (ers != NULL) {
253: KRL_DBG(("%s: bad: ers != NULL", __func__));
254: /* Shouldn't happen */
1.4 markus 255: free(irs);
1.20 djm 256: return SSH_ERR_ALLOC_FAIL;
1.1 djm 257: }
258: ers = irs;
259: } else {
260: KRL_DBG(("%s: overlap found %llu:%llu", __func__,
261: ers->lo, ers->hi));
262: /*
263: * The inserted entry overlaps an existing one. Grow the
264: * existing entry.
265: */
266: if (ers->lo > lo)
267: ers->lo = lo;
268: if (ers->hi < hi)
269: ers->hi = hi;
270: }
271: /*
272: * The inserted or revised range might overlap or abut adjacent ones;
273: * coalesce as necessary.
274: */
275:
276: /* Check predecessors */
277: while ((crs = RB_PREV(revoked_serial_tree, rt, ers)) != NULL) {
278: KRL_DBG(("%s: pred %llu:%llu", __func__, crs->lo, crs->hi));
279: if (ers->lo != 0 && crs->hi < ers->lo - 1)
280: break;
281: /* This entry overlaps. */
282: if (crs->lo < ers->lo) {
283: ers->lo = crs->lo;
284: KRL_DBG(("%s: pred extend %llu:%llu", __func__,
285: ers->lo, ers->hi));
286: }
287: RB_REMOVE(revoked_serial_tree, rt, crs);
288: free(crs);
289: }
290: /* Check successors */
291: while ((crs = RB_NEXT(revoked_serial_tree, rt, ers)) != NULL) {
292: KRL_DBG(("%s: succ %llu:%llu", __func__, crs->lo, crs->hi));
293: if (ers->hi != (u_int64_t)-1 && crs->lo > ers->hi + 1)
294: break;
295: /* This entry overlaps. */
296: if (crs->hi > ers->hi) {
297: ers->hi = crs->hi;
298: KRL_DBG(("%s: succ extend %llu:%llu", __func__,
299: ers->lo, ers->hi));
300: }
301: RB_REMOVE(revoked_serial_tree, rt, crs);
302: free(crs);
303: }
304: KRL_DBG(("%s: done, final %llu:%llu", __func__, ers->lo, ers->hi));
305: return 0;
306: }
307:
308: int
1.20 djm 309: ssh_krl_revoke_cert_by_serial(struct ssh_krl *krl, const struct sshkey *ca_key,
1.1 djm 310: u_int64_t serial)
311: {
312: return ssh_krl_revoke_cert_by_serial_range(krl, ca_key, serial, serial);
313: }
314:
315: int
1.20 djm 316: ssh_krl_revoke_cert_by_serial_range(struct ssh_krl *krl, const struct sshkey *ca_key,
1.1 djm 317: u_int64_t lo, u_int64_t hi)
318: {
319: struct revoked_certs *rc;
1.20 djm 320: int r;
1.1 djm 321:
322: if (lo > hi || lo == 0)
323: return -1;
1.20 djm 324: if ((r = revoked_certs_for_ca_key(krl, ca_key, &rc, 1)) != 0)
325: return r;
1.1 djm 326: return insert_serial_range(&rc->revoked_serials, lo, hi);
327: }
328:
329: int
1.20 djm 330: ssh_krl_revoke_cert_by_key_id(struct ssh_krl *krl, const struct sshkey *ca_key,
1.1 djm 331: const char *key_id)
332: {
333: struct revoked_key_id *rki, *erki;
334: struct revoked_certs *rc;
1.20 djm 335: int r;
1.1 djm 336:
1.20 djm 337: if ((r = revoked_certs_for_ca_key(krl, ca_key, &rc, 1)) != 0)
338: return r;
1.1 djm 339:
340: debug3("%s: revoke %s", __func__, key_id);
341: if ((rki = calloc(1, sizeof(*rki))) == NULL ||
342: (rki->key_id = strdup(key_id)) == NULL) {
343: free(rki);
1.20 djm 344: return SSH_ERR_ALLOC_FAIL;
1.1 djm 345: }
346: erki = RB_INSERT(revoked_key_id_tree, &rc->revoked_key_ids, rki);
347: if (erki != NULL) {
348: free(rki->key_id);
349: free(rki);
350: }
351: return 0;
352: }
353:
354: /* Convert "key" to a public key blob without any certificate information */
355: static int
1.20 djm 356: plain_key_blob(const struct sshkey *key, u_char **blob, size_t *blen)
1.1 djm 357: {
1.20 djm 358: struct sshkey *kcopy;
1.1 djm 359: int r;
360:
1.20 djm 361: if ((r = sshkey_from_private(key, &kcopy)) != 0)
362: return r;
363: if (sshkey_is_cert(kcopy)) {
364: if ((r = sshkey_drop_cert(kcopy)) != 0) {
365: sshkey_free(kcopy);
366: return r;
1.1 djm 367: }
368: }
1.20 djm 369: r = sshkey_to_blob(kcopy, blob, blen);
1.1 djm 370: free(kcopy);
1.17 djm 371: return r;
1.1 djm 372: }
373:
374: /* Revoke a key blob. Ownership of blob is transferred to the tree */
375: static int
376: revoke_blob(struct revoked_blob_tree *rbt, u_char *blob, u_int len)
377: {
378: struct revoked_blob *rb, *erb;
379:
380: if ((rb = calloc(1, sizeof(*rb))) == NULL)
1.20 djm 381: return SSH_ERR_ALLOC_FAIL;
1.1 djm 382: rb->blob = blob;
383: rb->len = len;
384: erb = RB_INSERT(revoked_blob_tree, rbt, rb);
385: if (erb != NULL) {
386: free(rb->blob);
387: free(rb);
388: }
389: return 0;
390: }
391:
392: int
1.20 djm 393: ssh_krl_revoke_key_explicit(struct ssh_krl *krl, const struct sshkey *key)
1.1 djm 394: {
395: u_char *blob;
1.20 djm 396: size_t len;
397: int r;
1.1 djm 398:
1.20 djm 399: debug3("%s: revoke type %s", __func__, sshkey_type(key));
400: if ((r = plain_key_blob(key, &blob, &len)) != 0)
401: return r;
1.1 djm 402: return revoke_blob(&krl->revoked_keys, blob, len);
403: }
404:
405: int
1.20 djm 406: ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const struct sshkey *key)
1.1 djm 407: {
408: u_char *blob;
1.20 djm 409: size_t len;
410: int r;
1.1 djm 411:
1.20 djm 412: debug3("%s: revoke type %s by sha1", __func__, sshkey_type(key));
1.21 djm 413: if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA1,
414: &blob, &len)) != 0)
1.20 djm 415: return r;
1.1 djm 416: return revoke_blob(&krl->revoked_sha1s, blob, len);
417: }
418:
419: int
1.20 djm 420: ssh_krl_revoke_key(struct ssh_krl *krl, const struct sshkey *key)
1.1 djm 421: {
1.20 djm 422: if (!sshkey_is_cert(key))
1.1 djm 423: return ssh_krl_revoke_key_sha1(krl, key);
424:
1.20 djm 425: if (sshkey_cert_is_legacy(key) || key->cert->serial == 0) {
1.1 djm 426: 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: /*
1.20 djm 437: * 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
1.1 djm 439: * 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: */
443: static int
444: choose_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, "
1.11 djm 510: "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,
1.1 djm 515: *force_new_section ? " restart" : "");
516: return new_state;
517: }
518:
519: /* Generate a KRL_SECTION_CERTIFICATES KRL section */
520: static int
1.20 djm 521: revoked_certs_generate(struct revoked_certs *rc, struct sshbuf *buf)
1.1 djm 522: {
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;
1.20 djm 528: struct sshbuf *sect;
1.1 djm 529: BIGNUM *bitmap = NULL;
530:
1.20 djm 531: if ((sect = sshbuf_new()) == NULL)
532: return SSH_ERR_ALLOC_FAIL;
1.1 djm 533:
1.20 djm 534: /* 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;
1.1 djm 539:
1.20 djm 540: sshbuf_reset(sect);
1.1 djm 541:
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__,
1.11 djm 547: (long long unsigned)rs->lo, (long long unsigned)rs->hi,
548: state);
1.1 djm 549:
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:
1.20 djm 572: if ((r = sshbuf_put_bignum2(sect, bitmap)) != 0)
573: goto out;
1.1 djm 574: BN_free(bitmap);
575: bitmap = NULL;
576: break;
577: }
1.20 djm 578: if ((r = sshbuf_put_u8(buf, state)) != 0 ||
579: (r = sshbuf_put_stringb(buf, sect)) != 0)
580: goto out;
581: sshbuf_reset(sect);
1.1 djm 582: }
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;
1.20 djm 588: sshbuf_reset(sect);
1.1 djm 589: 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:
1.20 djm 594: if ((bitmap = BN_new()) == NULL) {
595: r = SSH_ERR_ALLOC_FAIL;
1.1 djm 596: goto out;
1.20 djm 597: }
1.1 djm 598: bitmap_start = rs->lo;
1.20 djm 599: if ((r = sshbuf_put_u64(sect,
600: bitmap_start)) != 0)
601: goto out;
1.1 djm 602: break;
603: }
604: }
605:
606: /* Perform section-specific processing */
607: switch (state) {
608: case KRL_SECTION_CERT_SERIAL_LIST:
1.20 djm 609: for (i = 0; i < contig; i++) {
610: if ((r = sshbuf_put_u64(sect, rs->lo + i)) != 0)
611: goto out;
612: }
1.1 djm 613: break;
614: case KRL_SECTION_CERT_SERIAL_RANGE:
1.20 djm 615: if ((r = sshbuf_put_u64(sect, rs->lo)) != 0 ||
616: (r = sshbuf_put_u64(sect, rs->hi)) != 0)
617: goto out;
1.1 djm 618: 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,
1.20 djm 626: rs->lo + i - bitmap_start) != 1) {
627: r = SSH_ERR_ALLOC_FAIL;
1.1 djm 628: goto out;
1.20 djm 629: }
1.1 djm 630: }
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:
1.20 djm 644: if ((r = sshbuf_put_bignum2(sect, bitmap)) != 0)
645: goto out;
1.1 djm 646: BN_free(bitmap);
647: bitmap = NULL;
648: break;
649: }
1.20 djm 650: if ((r = sshbuf_put_u8(buf, state)) != 0 ||
651: (r = sshbuf_put_stringb(buf, sect)) != 0)
652: goto out;
1.1 djm 653: }
654: debug3("%s: serial done ", __func__);
655:
656: /* Now output a section for any revocations by key ID */
1.20 djm 657: sshbuf_reset(sect);
1.1 djm 658: RB_FOREACH(rki, revoked_key_id_tree, &rc->revoked_key_ids) {
659: debug3("%s: key ID %s", __func__, rki->key_id);
1.20 djm 660: if ((r = sshbuf_put_cstring(sect, rki->key_id)) != 0)
661: goto out;
1.1 djm 662: }
1.20 djm 663: 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;
1.1 djm 667: }
668: r = 0;
669: out:
670: if (bitmap != NULL)
671: BN_free(bitmap);
1.20 djm 672: sshbuf_free(sect);
1.1 djm 673: return r;
674: }
675:
676: int
1.20 djm 677: ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf,
678: const struct sshkey **sign_keys, u_int nsign_keys)
1.1 djm 679: {
680: int r = -1;
681: struct revoked_certs *rc;
682: struct revoked_blob *rb;
1.20 djm 683: struct sshbuf *sect;
684: u_char *sblob = NULL;
685: size_t slen, i;
1.1 djm 686:
687: if (krl->generated_date == 0)
688: krl->generated_date = time(NULL);
689:
1.20 djm 690: if ((sect = sshbuf_new()) == NULL)
691: return SSH_ERR_ALLOC_FAIL;
1.1 djm 692:
693: /* Store the header */
1.20 djm 694: 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;
1.1 djm 702:
703: /* Store sections for revoked certificates */
704: TAILQ_FOREACH(rc, &krl->revoked_certs, entry) {
1.20 djm 705: sshbuf_reset(sect);
706: if ((r = revoked_certs_generate(rc, sect)) != 0)
707: goto out;
708: if ((r = sshbuf_put_u8(buf, KRL_SECTION_CERTIFICATES)) != 0 ||
709: (r = sshbuf_put_stringb(buf, sect)) != 0)
1.1 djm 710: goto out;
711: }
712:
713: /* Finally, output sections for revocations by public key/hash */
1.20 djm 714: sshbuf_reset(sect);
1.1 djm 715: RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) {
1.20 djm 716: debug3("%s: key len %zu ", __func__, rb->len);
717: if ((sshbuf_put_string(sect, rb->blob, rb->len)) != 0)
718: goto out;
1.1 djm 719: }
1.20 djm 720: 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;
1.1 djm 724: }
1.20 djm 725: sshbuf_reset(sect);
1.1 djm 726: RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) {
1.20 djm 727: debug3("%s: hash len %zu ", __func__, rb->len);
728: if ((sshbuf_put_string(sect, rb->blob, rb->len)) != 0)
729: goto out;
1.1 djm 730: }
1.20 djm 731: 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;
1.1 djm 736: }
737:
738: for (i = 0; i < nsign_keys; i++) {
1.20 djm 739: sshbuf_reset(sect);
740: if ((r = sshkey_to_blob_buf(sign_keys[i], sect)) != 0)
1.1 djm 741: goto out;
742:
1.20 djm 743: 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)
746: goto out;
1.1 djm 747:
1.20 djm 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)
1.1 djm 753: goto out;
754: }
755:
756: r = 0;
757: out:
758: free(sblob);
1.20 djm 759: sshbuf_free(sect);
1.1 djm 760: return r;
761: }
762:
763: static void
764: format_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);
1.19 djm 771: 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: }
1.1 djm 777: }
778:
779: static int
1.20 djm 780: parse_revoked_certs(struct sshbuf *buf, struct ssh_krl *krl)
1.1 djm 781: {
1.20 djm 782: int r = -1, nbits;
1.15 djm 783: u_char type;
784: const u_char *blob;
1.20 djm 785: size_t blen;
786: struct sshbuf *subsect = NULL;
1.1 djm 787: u_int64_t serial, serial_lo, serial_hi;
788: BIGNUM *bitmap = NULL;
789: char *key_id = NULL;
1.20 djm 790: struct sshkey *ca_key = NULL;
1.1 djm 791:
1.20 djm 792: if ((subsect = sshbuf_new()) == NULL)
793: return SSH_ERR_ALLOC_FAIL;
1.1 djm 794:
1.20 djm 795: /* Header: key, reserved */
796: if ((r = sshbuf_get_string_direct(buf, &blob, &blen)) != 0 ||
797: (r = sshbuf_skip_string(buf)) != 0)
1.1 djm 798: goto out;
1.20 djm 799: if ((r = sshkey_from_blob(blob, blen, &ca_key)) != 0)
1.1 djm 800: goto out;
801:
1.20 djm 802: while (sshbuf_len(buf) > 0) {
803: if (subsect != NULL) {
804: sshbuf_free(subsect);
805: subsect = NULL;
806: }
807: if ((r = sshbuf_get_u8(buf, &type)) != 0 ||
808: (r = sshbuf_froms(buf, &subsect)) != 0)
1.1 djm 809: goto out;
810: debug3("%s: subsection type 0x%02x", __func__, type);
811:
812: switch (type) {
813: case KRL_SECTION_CERT_SERIAL_LIST:
1.20 djm 814: while (sshbuf_len(subsect) > 0) {
815: if ((r = sshbuf_get_u64(subsect, &serial)) != 0)
1.1 djm 816: goto out;
1.20 djm 817: if ((r = ssh_krl_revoke_cert_by_serial(krl,
818: ca_key, serial)) != 0)
1.1 djm 819: goto out;
820: }
821: break;
822: case KRL_SECTION_CERT_SERIAL_RANGE:
1.20 djm 823: if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 ||
824: (r = sshbuf_get_u64(subsect, &serial_hi)) != 0)
1.1 djm 825: goto out;
1.20 djm 826: if ((r = ssh_krl_revoke_cert_by_serial_range(krl,
827: ca_key, serial_lo, serial_hi)) != 0)
1.1 djm 828: goto out;
829: break;
830: case KRL_SECTION_CERT_SERIAL_BITMAP:
831: if ((bitmap = BN_new()) == NULL) {
1.20 djm 832: r = SSH_ERR_ALLOC_FAIL;
1.1 djm 833: goto out;
834: }
1.20 djm 835: if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 ||
836: (r = sshbuf_get_bignum2(subsect, bitmap)) != 0)
1.1 djm 837: goto out;
838: if ((nbits = BN_num_bits(bitmap)) < 0) {
839: error("%s: bitmap bits < 0", __func__);
1.20 djm 840: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 841: 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__);
1.20 djm 846: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 847: goto out;
848: }
849: if (!BN_is_bit_set(bitmap, serial))
850: continue;
1.20 djm 851: if ((r = ssh_krl_revoke_cert_by_serial(krl,
852: ca_key, serial_lo + serial)) != 0)
1.1 djm 853: goto out;
854: }
855: BN_free(bitmap);
856: bitmap = NULL;
857: break;
858: case KRL_SECTION_CERT_KEY_ID:
1.20 djm 859: while (sshbuf_len(subsect) > 0) {
860: if ((r = sshbuf_get_cstring(subsect,
861: &key_id, NULL)) != 0)
1.1 djm 862: goto out;
1.20 djm 863: if ((r = ssh_krl_revoke_cert_by_key_id(krl,
864: ca_key, key_id)) != 0)
1.1 djm 865: goto out;
866: free(key_id);
867: key_id = NULL;
868: }
869: break;
870: default:
871: error("Unsupported KRL certificate section %u", type);
1.20 djm 872: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 873: goto out;
874: }
1.20 djm 875: if (sshbuf_len(subsect) > 0) {
1.1 djm 876: error("KRL certificate section contains unparsed data");
1.20 djm 877: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 878: goto out;
879: }
880: }
881:
1.20 djm 882: r = 0;
1.1 djm 883: out:
884: if (bitmap != NULL)
885: BN_free(bitmap);
886: free(key_id);
1.20 djm 887: sshkey_free(ca_key);
888: sshbuf_free(subsect);
889: return r;
1.1 djm 890: }
891:
892:
893: /* Attempt to parse a KRL, checking its signature (if any) with sign_ca_keys. */
894: int
1.20 djm 895: ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp,
896: const struct sshkey **sign_ca_keys, u_int nsign_ca_keys)
1.1 djm 897: {
1.20 djm 898: struct sshbuf *copy = NULL, *sect = NULL;
899: struct ssh_krl *krl = NULL;
1.1 djm 900: char timestamp[64];
1.20 djm 901: int r = -1, sig_seen;
902: struct sshkey *key = NULL, **ca_used = NULL, **tmp_ca_used;
1.15 djm 903: u_char type, *rdata = NULL;
904: const u_char *blob;
1.20 djm 905: size_t i, j, sig_off, sects_off, rlen, blen, nca_used;
906: u_int format_version;
1.1 djm 907:
1.12 markus 908: nca_used = 0;
1.1 djm 909: *krlp = NULL;
1.20 djm 910: if (sshbuf_len(buf) < sizeof(KRL_MAGIC) - 1 ||
911: memcmp(sshbuf_ptr(buf), KRL_MAGIC, sizeof(KRL_MAGIC) - 1) != 0) {
1.1 djm 912: debug3("%s: not a KRL", __func__);
1.20 djm 913: return SSH_ERR_KRL_BAD_MAGIC;
1.1 djm 914: }
915:
916: /* Take a copy of the KRL buffer so we can verify its signature later */
1.20 djm 917: 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;
1.1 djm 923:
924: if ((krl = ssh_krl_init()) == NULL) {
925: error("%s: alloc failed", __func__);
926: goto out;
927: }
928:
1.20 djm 929: if ((r = sshbuf_get_u32(copy, &format_version)) != 0)
1.1 djm 930: goto out;
931: if (format_version != KRL_FORMAT_VERSION) {
1.20 djm 932: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 933: goto out;
934: }
1.20 djm 935: 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)
1.1 djm 940: goto out;
941:
942: format_timestamp(krl->generated_date, timestamp, sizeof(timestamp));
1.11 djm 943: debug("KRL version %llu generated at %s%s%s",
944: (long long unsigned)krl->krl_version, timestamp,
945: *krl->comment ? ": " : "", krl->comment);
1.1 djm 946:
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;
1.20 djm 952: 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)
1.1 djm 961: goto out;
962: debug3("%s: first pass, section 0x%02x", __func__, type);
963: if (type != KRL_SECTION_SIGNATURE) {
964: if (sig_seen) {
1.20 djm 965: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 966: 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 */
1.20 djm 975: if ((r = sshkey_from_blob(blob, blen, &key)) != 0) {
976: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 977: error("%s: invalid signature key", __func__);
978: goto out;
979: }
1.20 djm 980: 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);
1.1 djm 986: /* Second string component is the signature itself */
1.20 djm 987: if ((r = sshbuf_get_string_direct(copy, &blob, &blen)) != 0) {
988: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 989: goto out;
990: }
991: /* Check signature over entire KRL up to this point */
1.20 djm 992: if ((r = sshkey_verify(key, blob, blen,
993: sshbuf_ptr(buf), sshbuf_len(buf) - sig_off, 0)) != 0) {
1.1 djm 994: 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++) {
1.20 djm 999: if (sshkey_equal(ca_used[i], key)) {
1.1 djm 1000: error("KRL signed more than once with "
1001: "the same key");
1.20 djm 1002: r = SSH_ERR_SIGNATURE_INVALID;
1.1 djm 1003: goto out;
1004: }
1005: }
1006: /* Record keys used to sign the KRL */
1.20 djm 1007: 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;
1.1 djm 1014: ca_used[nca_used++] = key;
1015: key = NULL;
1016: break;
1017: }
1018:
1.20 djm 1019: if (sshbuf_len(copy) != 0) {
1020: /* Shouldn't happen */
1021: r = SSH_ERR_INTERNAL_ERROR;
1022: goto out;
1023: }
1024:
1.1 djm 1025: /*
1026: * 2nd pass: parse and load the KRL, skipping the header to the point
1027: * where the section start.
1028: */
1.20 djm 1029: 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, §)) != 0) {
1.1 djm 1043: goto out;
1044: }
1045: debug3("%s: second pass, section 0x%02x", __func__, type);
1046:
1047: switch (type) {
1048: case KRL_SECTION_CERTIFICATES:
1.20 djm 1049: if ((r = parse_revoked_certs(sect, krl)) != 0)
1.1 djm 1050: goto out;
1051: break;
1052: case KRL_SECTION_EXPLICIT_KEY:
1053: case KRL_SECTION_FINGERPRINT_SHA1:
1.20 djm 1054: while (sshbuf_len(sect) > 0) {
1055: if ((r = sshbuf_get_string(sect,
1056: &rdata, &rlen)) != 0)
1.1 djm 1057: goto out;
1058: if (type == KRL_SECTION_FINGERPRINT_SHA1 &&
1.12 markus 1059: rlen != 20) {
1.1 djm 1060: error("%s: bad SHA1 length", __func__);
1.20 djm 1061: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 1062: goto out;
1063: }
1.20 djm 1064: if ((r = revoke_blob(
1.1 djm 1065: type == KRL_SECTION_EXPLICIT_KEY ?
1066: &krl->revoked_keys : &krl->revoked_sha1s,
1.20 djm 1067: rdata, rlen)) != 0)
1.12 markus 1068: goto out;
1069: rdata = NULL; /* revoke_blob frees blob */
1.1 djm 1070: }
1071: break;
1072: case KRL_SECTION_SIGNATURE:
1073: /* Handled above, but still need to stay in synch */
1.20 djm 1074: sshbuf_reset(sect);
1075: sect = NULL;
1076: if ((r = sshbuf_skip_string(copy)) != 0)
1.1 djm 1077: goto out;
1078: break;
1079: default:
1080: error("Unsupported KRL section %u", type);
1.20 djm 1081: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 1082: goto out;
1083: }
1.20 djm 1084: if (sshbuf_len(sect) > 0) {
1.1 djm 1085: error("KRL section contains unparsed data");
1.20 djm 1086: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 1087: 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 {
1.20 djm 1097: sshkey_free(ca_used[i]);
1.1 djm 1098: ca_used[i] = NULL;
1099: }
1100: }
1101: if (nca_used && !sig_seen) {
1.20 djm 1102: r = SSH_ERR_SIGNATURE_INVALID;
1.1 djm 1103: 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;
1.20 djm 1114: if (sshkey_equal(ca_used[j], sign_ca_keys[i])) {
1.1 djm 1115: sig_seen = 1;
1116: break;
1117: }
1118: }
1119: }
1120: if (!sig_seen) {
1.20 djm 1121: r = SSH_ERR_SIGNATURE_INVALID;
1.1 djm 1122: error("KRL not signed with any trusted key");
1123: goto out;
1124: }
1125: }
1126:
1127: *krlp = krl;
1.20 djm 1128: r = 0;
1.1 djm 1129: out:
1.20 djm 1130: if (r != 0)
1.1 djm 1131: ssh_krl_free(krl);
1.20 djm 1132: for (i = 0; i < nca_used; i++)
1133: sshkey_free(ca_used[i]);
1.1 djm 1134: free(ca_used);
1.12 markus 1135: free(rdata);
1.20 djm 1136: sshkey_free(key);
1137: sshbuf_free(copy);
1138: sshbuf_free(sect);
1139: return r;
1.1 djm 1140: }
1141:
1142: /* Checks whether a given key/cert is revoked. Does not check its CA */
1143: static int
1.20 djm 1144: is_key_revoked(struct ssh_krl *krl, const struct sshkey *key)
1.1 djm 1145: {
1146: struct revoked_blob rb, *erb;
1147: struct revoked_serial rs, *ers;
1148: struct revoked_key_id rki, *erki;
1149: struct revoked_certs *rc;
1.20 djm 1150: int r;
1.1 djm 1151:
1152: /* Check explicitly revoked hashes first */
1.14 tedu 1153: memset(&rb, 0, sizeof(rb));
1.21 djm 1154: if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA1,
1.20 djm 1155: &rb.blob, &rb.len)) != 0)
1156: return r;
1.1 djm 1157: 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__);
1.20 djm 1161: return SSH_ERR_KEY_REVOKED;
1.1 djm 1162: }
1163:
1164: /* Next, explicit keys */
1.14 tedu 1165: memset(&rb, 0, sizeof(rb));
1.20 djm 1166: if ((r = plain_key_blob(key, &rb.blob, &rb.len)) != 0)
1167: return r;
1.1 djm 1168: 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__);
1.20 djm 1172: return SSH_ERR_KEY_REVOKED;
1.1 djm 1173: }
1174:
1.20 djm 1175: if (!sshkey_is_cert(key))
1.1 djm 1176: return 0;
1177:
1178: /* Check cert revocation */
1.20 djm 1179: if ((r = revoked_certs_for_ca_key(krl, key->cert->signature_key,
1180: &rc, 0)) != 0)
1181: return r;
1.1 djm 1182: if (rc == NULL)
1183: return 0; /* No entry for this CA */
1184:
1185: /* Check revocation by cert key ID */
1.14 tedu 1186: memset(&rki, 0, sizeof(rki));
1.1 djm 1187: 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__);
1.20 djm 1191: return SSH_ERR_KEY_REVOKED;
1.1 djm 1192: }
1193:
1.8 djm 1194: /*
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: */
1.20 djm 1198: if (sshkey_cert_is_legacy(key) || key->cert->serial == 0)
1.1 djm 1199: return 0;
1200:
1.14 tedu 1201: memset(&rs, 0, sizeof(rs));
1.1 djm 1202: 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__);
1.20 djm 1208: return SSH_ERR_KEY_REVOKED;
1.1 djm 1209: }
1210: KRL_DBG(("%s: %llu no match", __func__, key->cert->serial));
1211:
1212: return 0;
1213: }
1214:
1215: int
1.20 djm 1216: ssh_krl_check_key(struct ssh_krl *krl, const struct sshkey *key)
1.1 djm 1217: {
1218: int r;
1219:
1220: debug2("%s: checking key", __func__);
1221: if ((r = is_key_revoked(krl, key)) != 0)
1222: return r;
1.20 djm 1223: if (sshkey_is_cert(key)) {
1.1 djm 1224: 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:
1232: int
1.20 djm 1233: ssh_krl_file_contains_key(const char *path, const struct sshkey *key)
1.1 djm 1234: {
1.20 djm 1235: struct sshbuf *krlbuf = NULL;
1236: struct ssh_krl *krl = NULL;
1237: int oerrno = 0, r, fd;
1.1 djm 1238:
1239: if (path == NULL)
1240: return 0;
1241:
1.20 djm 1242: if ((krlbuf = sshbuf_new()) == NULL)
1243: return SSH_ERR_ALLOC_FAIL;
1.1 djm 1244: if ((fd = open(path, O_RDONLY)) == -1) {
1.20 djm 1245: r = SSH_ERR_SYSTEM_ERROR;
1246: oerrno = errno;
1247: goto out;
1.1 djm 1248: }
1.22 ! djm 1249: if ((r = sshkey_load_file(fd, krlbuf)) != 0) {
1.20 djm 1250: oerrno = errno;
1251: goto out;
1.1 djm 1252: }
1.20 djm 1253: if ((r = ssh_krl_from_blob(krlbuf, &krl, NULL, 0)) != 0)
1254: goto out;
1255: debug2("%s: checking KRL %s", __func__, path);
1256: r = ssh_krl_check_key(krl, key);
1257: out:
1.1 djm 1258: close(fd);
1.20 djm 1259: sshbuf_free(krlbuf);
1.1 djm 1260: ssh_krl_free(krl);
1.20 djm 1261: if (r != 0)
1262: errno = oerrno;
1263: return r;
1.1 djm 1264: }