Annotation of src/usr.bin/ssh/krl.c, Revision 1.37
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.37 ! djm 17: /* $OpenBSD: krl.c,v 1.36 2015/12/11 04:21:12 mmcc Exp $ */
1.2 djm 18:
1.29 deraadt 19: #include <sys/param.h> /* MIN */
1.1 djm 20: #include <sys/types.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: }
1.31 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.31 djm 214: if ((ca_key == NULL && rc->ca_key == NULL) ||
215: 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;
1.31 djm 225: if (ca_key == NULL)
226: rc->ca_key = NULL;
227: else if ((r = sshkey_from_private(ca_key, &rc->ca_key)) != 0) {
1.1 djm 228: free(rc);
1.20 djm 229: return r;
1.1 djm 230: }
231: RB_INIT(&rc->revoked_serials);
232: RB_INIT(&rc->revoked_key_ids);
233: TAILQ_INSERT_TAIL(&krl->revoked_certs, rc, entry);
1.31 djm 234: KRL_DBG(("%s: new CA %s", __func__,
235: ca_key == NULL ? "*" : sshkey_type(ca_key)));
1.1 djm 236: *rcp = rc;
237: return 0;
238: }
239:
240: static int
241: insert_serial_range(struct revoked_serial_tree *rt, u_int64_t lo, u_int64_t hi)
242: {
243: struct revoked_serial rs, *ers, *crs, *irs;
244:
245: KRL_DBG(("%s: insert %llu:%llu", __func__, lo, hi));
1.14 tedu 246: memset(&rs, 0, sizeof(rs));
1.1 djm 247: rs.lo = lo;
248: rs.hi = hi;
249: ers = RB_NFIND(revoked_serial_tree, rt, &rs);
250: if (ers == NULL || serial_cmp(ers, &rs) != 0) {
251: /* No entry matches. Just insert */
252: if ((irs = malloc(sizeof(rs))) == NULL)
1.20 djm 253: return SSH_ERR_ALLOC_FAIL;
1.1 djm 254: memcpy(irs, &rs, sizeof(*irs));
255: ers = RB_INSERT(revoked_serial_tree, rt, irs);
256: if (ers != NULL) {
257: KRL_DBG(("%s: bad: ers != NULL", __func__));
258: /* Shouldn't happen */
1.4 markus 259: free(irs);
1.25 djm 260: return SSH_ERR_INTERNAL_ERROR;
1.1 djm 261: }
262: ers = irs;
263: } else {
264: KRL_DBG(("%s: overlap found %llu:%llu", __func__,
265: ers->lo, ers->hi));
266: /*
267: * The inserted entry overlaps an existing one. Grow the
268: * existing entry.
269: */
270: if (ers->lo > lo)
271: ers->lo = lo;
272: if (ers->hi < hi)
273: ers->hi = hi;
274: }
1.25 djm 275:
1.1 djm 276: /*
277: * The inserted or revised range might overlap or abut adjacent ones;
278: * coalesce as necessary.
279: */
280:
281: /* Check predecessors */
282: while ((crs = RB_PREV(revoked_serial_tree, rt, ers)) != NULL) {
283: KRL_DBG(("%s: pred %llu:%llu", __func__, crs->lo, crs->hi));
284: if (ers->lo != 0 && crs->hi < ers->lo - 1)
285: break;
286: /* This entry overlaps. */
287: if (crs->lo < ers->lo) {
288: ers->lo = crs->lo;
289: KRL_DBG(("%s: pred extend %llu:%llu", __func__,
290: ers->lo, ers->hi));
291: }
292: RB_REMOVE(revoked_serial_tree, rt, crs);
293: free(crs);
294: }
295: /* Check successors */
296: while ((crs = RB_NEXT(revoked_serial_tree, rt, ers)) != NULL) {
297: KRL_DBG(("%s: succ %llu:%llu", __func__, crs->lo, crs->hi));
298: if (ers->hi != (u_int64_t)-1 && crs->lo > ers->hi + 1)
299: break;
300: /* This entry overlaps. */
301: if (crs->hi > ers->hi) {
302: ers->hi = crs->hi;
303: KRL_DBG(("%s: succ extend %llu:%llu", __func__,
304: ers->lo, ers->hi));
305: }
306: RB_REMOVE(revoked_serial_tree, rt, crs);
307: free(crs);
308: }
309: KRL_DBG(("%s: done, final %llu:%llu", __func__, ers->lo, ers->hi));
310: return 0;
311: }
312:
313: int
1.20 djm 314: ssh_krl_revoke_cert_by_serial(struct ssh_krl *krl, const struct sshkey *ca_key,
1.1 djm 315: u_int64_t serial)
316: {
317: return ssh_krl_revoke_cert_by_serial_range(krl, ca_key, serial, serial);
318: }
319:
320: int
1.25 djm 321: ssh_krl_revoke_cert_by_serial_range(struct ssh_krl *krl,
322: const struct sshkey *ca_key, u_int64_t lo, u_int64_t hi)
1.1 djm 323: {
324: struct revoked_certs *rc;
1.20 djm 325: int r;
1.1 djm 326:
327: if (lo > hi || lo == 0)
1.25 djm 328: return SSH_ERR_INVALID_ARGUMENT;
1.20 djm 329: if ((r = revoked_certs_for_ca_key(krl, ca_key, &rc, 1)) != 0)
330: return r;
1.1 djm 331: return insert_serial_range(&rc->revoked_serials, lo, hi);
332: }
333:
334: int
1.20 djm 335: ssh_krl_revoke_cert_by_key_id(struct ssh_krl *krl, const struct sshkey *ca_key,
1.1 djm 336: const char *key_id)
337: {
338: struct revoked_key_id *rki, *erki;
339: struct revoked_certs *rc;
1.20 djm 340: int r;
1.1 djm 341:
1.20 djm 342: if ((r = revoked_certs_for_ca_key(krl, ca_key, &rc, 1)) != 0)
343: return r;
1.1 djm 344:
1.25 djm 345: KRL_DBG(("%s: revoke %s", __func__, key_id));
1.1 djm 346: if ((rki = calloc(1, sizeof(*rki))) == NULL ||
347: (rki->key_id = strdup(key_id)) == NULL) {
348: free(rki);
1.20 djm 349: return SSH_ERR_ALLOC_FAIL;
1.1 djm 350: }
351: erki = RB_INSERT(revoked_key_id_tree, &rc->revoked_key_ids, rki);
352: if (erki != NULL) {
353: free(rki->key_id);
354: free(rki);
355: }
356: return 0;
357: }
358:
359: /* Convert "key" to a public key blob without any certificate information */
360: static int
1.20 djm 361: plain_key_blob(const struct sshkey *key, u_char **blob, size_t *blen)
1.1 djm 362: {
1.20 djm 363: struct sshkey *kcopy;
1.1 djm 364: int r;
365:
1.20 djm 366: if ((r = sshkey_from_private(key, &kcopy)) != 0)
367: return r;
368: if (sshkey_is_cert(kcopy)) {
369: if ((r = sshkey_drop_cert(kcopy)) != 0) {
370: sshkey_free(kcopy);
371: return r;
1.1 djm 372: }
373: }
1.20 djm 374: r = sshkey_to_blob(kcopy, blob, blen);
1.23 markus 375: sshkey_free(kcopy);
1.17 djm 376: return r;
1.1 djm 377: }
378:
379: /* Revoke a key blob. Ownership of blob is transferred to the tree */
380: static int
1.25 djm 381: revoke_blob(struct revoked_blob_tree *rbt, u_char *blob, size_t len)
1.1 djm 382: {
383: struct revoked_blob *rb, *erb;
384:
385: if ((rb = calloc(1, sizeof(*rb))) == NULL)
1.20 djm 386: return SSH_ERR_ALLOC_FAIL;
1.1 djm 387: rb->blob = blob;
388: rb->len = len;
389: erb = RB_INSERT(revoked_blob_tree, rbt, rb);
390: if (erb != NULL) {
391: free(rb->blob);
392: free(rb);
393: }
394: return 0;
395: }
396:
397: int
1.20 djm 398: ssh_krl_revoke_key_explicit(struct ssh_krl *krl, const struct sshkey *key)
1.1 djm 399: {
400: u_char *blob;
1.20 djm 401: size_t len;
402: int r;
1.1 djm 403:
1.20 djm 404: debug3("%s: revoke type %s", __func__, sshkey_type(key));
405: if ((r = plain_key_blob(key, &blob, &len)) != 0)
406: return r;
1.1 djm 407: return revoke_blob(&krl->revoked_keys, blob, len);
408: }
409:
410: int
1.20 djm 411: ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const struct sshkey *key)
1.1 djm 412: {
413: u_char *blob;
1.20 djm 414: size_t len;
415: int r;
1.1 djm 416:
1.20 djm 417: debug3("%s: revoke type %s by sha1", __func__, sshkey_type(key));
1.21 djm 418: if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA1,
419: &blob, &len)) != 0)
1.20 djm 420: return r;
1.1 djm 421: return revoke_blob(&krl->revoked_sha1s, blob, len);
422: }
423:
424: int
1.20 djm 425: ssh_krl_revoke_key(struct ssh_krl *krl, const struct sshkey *key)
1.1 djm 426: {
1.20 djm 427: if (!sshkey_is_cert(key))
1.1 djm 428: return ssh_krl_revoke_key_sha1(krl, key);
429:
1.33 djm 430: if (key->cert->serial == 0) {
1.1 djm 431: return ssh_krl_revoke_cert_by_key_id(krl,
432: key->cert->signature_key,
433: key->cert->key_id);
434: } else {
435: return ssh_krl_revoke_cert_by_serial(krl,
436: key->cert->signature_key,
437: key->cert->serial);
438: }
439: }
440:
441: /*
1.20 djm 442: * Select the most compact section type to emit next in a KRL based on
443: * the current section type, the run length of contiguous revoked serial
1.1 djm 444: * numbers and the gaps from the last and to the next revoked serial.
445: * Applies a mostly-accurate bit cost model to select the section type
446: * that will minimise the size of the resultant KRL.
447: */
448: static int
449: choose_next_state(int current_state, u_int64_t contig, int final,
450: u_int64_t last_gap, u_int64_t next_gap, int *force_new_section)
451: {
452: int new_state;
453: u_int64_t cost, cost_list, cost_range, cost_bitmap, cost_bitmap_restart;
454:
455: /*
456: * Avoid unsigned overflows.
457: * The limits are high enough to avoid confusing the calculations.
458: */
459: contig = MIN(contig, 1ULL<<31);
460: last_gap = MIN(last_gap, 1ULL<<31);
461: next_gap = MIN(next_gap, 1ULL<<31);
462:
463: /*
464: * Calculate the cost to switch from the current state to candidates.
465: * NB. range sections only ever contain a single range, so their
466: * switching cost is independent of the current_state.
467: */
468: cost_list = cost_bitmap = cost_bitmap_restart = 0;
469: cost_range = 8;
470: switch (current_state) {
471: case KRL_SECTION_CERT_SERIAL_LIST:
472: cost_bitmap_restart = cost_bitmap = 8 + 64;
473: break;
474: case KRL_SECTION_CERT_SERIAL_BITMAP:
475: cost_list = 8;
476: cost_bitmap_restart = 8 + 64;
477: break;
478: case KRL_SECTION_CERT_SERIAL_RANGE:
479: case 0:
480: cost_bitmap_restart = cost_bitmap = 8 + 64;
481: cost_list = 8;
482: }
483:
484: /* Estimate base cost in bits of each section type */
485: cost_list += 64 * contig + (final ? 0 : 8+64);
486: cost_range += (2 * 64) + (final ? 0 : 8+64);
487: cost_bitmap += last_gap + contig + (final ? 0 : MIN(next_gap, 8+64));
488: cost_bitmap_restart += contig + (final ? 0 : MIN(next_gap, 8+64));
489:
490: /* Convert to byte costs for actual comparison */
491: cost_list = (cost_list + 7) / 8;
492: cost_bitmap = (cost_bitmap + 7) / 8;
493: cost_bitmap_restart = (cost_bitmap_restart + 7) / 8;
494: cost_range = (cost_range + 7) / 8;
495:
496: /* Now pick the best choice */
497: *force_new_section = 0;
498: new_state = KRL_SECTION_CERT_SERIAL_BITMAP;
499: cost = cost_bitmap;
500: if (cost_range < cost) {
501: new_state = KRL_SECTION_CERT_SERIAL_RANGE;
502: cost = cost_range;
503: }
504: if (cost_list < cost) {
505: new_state = KRL_SECTION_CERT_SERIAL_LIST;
506: cost = cost_list;
507: }
508: if (cost_bitmap_restart < cost) {
509: new_state = KRL_SECTION_CERT_SERIAL_BITMAP;
510: *force_new_section = 1;
511: cost = cost_bitmap_restart;
512: }
1.25 djm 513: KRL_DBG(("%s: contig %llu last_gap %llu next_gap %llu final %d, costs:"
1.1 djm 514: "list %llu range %llu bitmap %llu new bitmap %llu, "
1.11 djm 515: "selected 0x%02x%s", __func__, (long long unsigned)contig,
516: (long long unsigned)last_gap, (long long unsigned)next_gap, final,
517: (long long unsigned)cost_list, (long long unsigned)cost_range,
518: (long long unsigned)cost_bitmap,
519: (long long unsigned)cost_bitmap_restart, new_state,
1.25 djm 520: *force_new_section ? " restart" : ""));
1.1 djm 521: return new_state;
522: }
523:
1.26 djm 524: static int
525: put_bitmap(struct sshbuf *buf, struct bitmap *bitmap)
526: {
527: size_t len;
528: u_char *blob;
529: int r;
530:
531: len = bitmap_nbytes(bitmap);
532: if ((blob = malloc(len)) == NULL)
533: return SSH_ERR_ALLOC_FAIL;
534: if (bitmap_to_string(bitmap, blob, len) != 0) {
535: free(blob);
536: return SSH_ERR_INTERNAL_ERROR;
537: }
538: r = sshbuf_put_bignum2_bytes(buf, blob, len);
539: free(blob);
540: return r;
541: }
542:
1.1 djm 543: /* Generate a KRL_SECTION_CERTIFICATES KRL section */
544: static int
1.20 djm 545: revoked_certs_generate(struct revoked_certs *rc, struct sshbuf *buf)
1.1 djm 546: {
1.25 djm 547: int final, force_new_sect, r = SSH_ERR_INTERNAL_ERROR;
1.1 djm 548: u_int64_t i, contig, gap, last = 0, bitmap_start = 0;
549: struct revoked_serial *rs, *nrs;
550: struct revoked_key_id *rki;
551: int next_state, state = 0;
1.20 djm 552: struct sshbuf *sect;
1.26 djm 553: struct bitmap *bitmap = NULL;
1.1 djm 554:
1.20 djm 555: if ((sect = sshbuf_new()) == NULL)
556: return SSH_ERR_ALLOC_FAIL;
1.1 djm 557:
1.31 djm 558: /* Store the header: optional CA scope key, reserved */
559: if (rc->ca_key == NULL) {
560: if ((r = sshbuf_put_string(buf, NULL, 0)) != 0)
561: goto out;
562: } else {
563: if ((r = sshkey_puts(rc->ca_key, buf)) != 0)
564: goto out;
565: }
566: if ((r = sshbuf_put_string(buf, NULL, 0)) != 0)
1.20 djm 567: goto out;
1.1 djm 568:
569: /* Store the revoked serials. */
570: for (rs = RB_MIN(revoked_serial_tree, &rc->revoked_serials);
571: rs != NULL;
572: rs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs)) {
1.25 djm 573: KRL_DBG(("%s: serial %llu:%llu state 0x%02x", __func__,
1.11 djm 574: (long long unsigned)rs->lo, (long long unsigned)rs->hi,
1.25 djm 575: state));
1.1 djm 576:
577: /* Check contiguous length and gap to next section (if any) */
578: nrs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs);
579: final = nrs == NULL;
580: gap = nrs == NULL ? 0 : nrs->lo - rs->hi;
581: contig = 1 + (rs->hi - rs->lo);
582:
583: /* Choose next state based on these */
584: next_state = choose_next_state(state, contig, final,
585: state == 0 ? 0 : rs->lo - last, gap, &force_new_sect);
586:
587: /*
588: * If the current section is a range section or has a different
589: * type to the next section, then finish it off now.
590: */
591: if (state != 0 && (force_new_sect || next_state != state ||
592: state == KRL_SECTION_CERT_SERIAL_RANGE)) {
1.25 djm 593: KRL_DBG(("%s: finish state 0x%02x", __func__, state));
1.1 djm 594: switch (state) {
595: case KRL_SECTION_CERT_SERIAL_LIST:
596: case KRL_SECTION_CERT_SERIAL_RANGE:
597: break;
598: case KRL_SECTION_CERT_SERIAL_BITMAP:
1.26 djm 599: if ((r = put_bitmap(sect, bitmap)) != 0)
1.20 djm 600: goto out;
1.26 djm 601: bitmap_free(bitmap);
1.1 djm 602: bitmap = NULL;
603: break;
604: }
1.20 djm 605: if ((r = sshbuf_put_u8(buf, state)) != 0 ||
606: (r = sshbuf_put_stringb(buf, sect)) != 0)
607: goto out;
608: sshbuf_reset(sect);
1.1 djm 609: }
610:
611: /* If we are starting a new section then prepare it now */
612: if (next_state != state || force_new_sect) {
1.25 djm 613: KRL_DBG(("%s: start state 0x%02x", __func__,
614: next_state));
1.1 djm 615: state = next_state;
1.20 djm 616: sshbuf_reset(sect);
1.1 djm 617: switch (state) {
618: case KRL_SECTION_CERT_SERIAL_LIST:
619: case KRL_SECTION_CERT_SERIAL_RANGE:
620: break;
621: case KRL_SECTION_CERT_SERIAL_BITMAP:
1.26 djm 622: if ((bitmap = bitmap_new()) == NULL) {
1.20 djm 623: r = SSH_ERR_ALLOC_FAIL;
1.1 djm 624: goto out;
1.20 djm 625: }
1.1 djm 626: bitmap_start = rs->lo;
1.20 djm 627: if ((r = sshbuf_put_u64(sect,
628: bitmap_start)) != 0)
629: goto out;
1.1 djm 630: break;
631: }
632: }
633:
634: /* Perform section-specific processing */
635: switch (state) {
636: case KRL_SECTION_CERT_SERIAL_LIST:
1.20 djm 637: for (i = 0; i < contig; i++) {
638: if ((r = sshbuf_put_u64(sect, rs->lo + i)) != 0)
639: goto out;
640: }
1.1 djm 641: break;
642: case KRL_SECTION_CERT_SERIAL_RANGE:
1.20 djm 643: if ((r = sshbuf_put_u64(sect, rs->lo)) != 0 ||
644: (r = sshbuf_put_u64(sect, rs->hi)) != 0)
645: goto out;
1.1 djm 646: break;
647: case KRL_SECTION_CERT_SERIAL_BITMAP:
648: if (rs->lo - bitmap_start > INT_MAX) {
649: error("%s: insane bitmap gap", __func__);
650: goto out;
651: }
652: for (i = 0; i < contig; i++) {
1.26 djm 653: if (bitmap_set_bit(bitmap,
654: rs->lo + i - bitmap_start) != 0) {
1.20 djm 655: r = SSH_ERR_ALLOC_FAIL;
1.1 djm 656: goto out;
1.20 djm 657: }
1.1 djm 658: }
659: break;
660: }
661: last = rs->hi;
662: }
663: /* Flush the remaining section, if any */
664: if (state != 0) {
1.25 djm 665: KRL_DBG(("%s: serial final flush for state 0x%02x",
666: __func__, state));
1.1 djm 667: switch (state) {
668: case KRL_SECTION_CERT_SERIAL_LIST:
669: case KRL_SECTION_CERT_SERIAL_RANGE:
670: break;
671: case KRL_SECTION_CERT_SERIAL_BITMAP:
1.26 djm 672: if ((r = put_bitmap(sect, bitmap)) != 0)
1.20 djm 673: goto out;
1.26 djm 674: bitmap_free(bitmap);
1.1 djm 675: bitmap = NULL;
676: break;
677: }
1.20 djm 678: if ((r = sshbuf_put_u8(buf, state)) != 0 ||
679: (r = sshbuf_put_stringb(buf, sect)) != 0)
680: goto out;
1.1 djm 681: }
1.25 djm 682: KRL_DBG(("%s: serial done ", __func__));
1.1 djm 683:
684: /* Now output a section for any revocations by key ID */
1.20 djm 685: sshbuf_reset(sect);
1.1 djm 686: RB_FOREACH(rki, revoked_key_id_tree, &rc->revoked_key_ids) {
1.25 djm 687: KRL_DBG(("%s: key ID %s", __func__, rki->key_id));
1.20 djm 688: if ((r = sshbuf_put_cstring(sect, rki->key_id)) != 0)
689: goto out;
1.1 djm 690: }
1.20 djm 691: if (sshbuf_len(sect) != 0) {
692: if ((r = sshbuf_put_u8(buf, KRL_SECTION_CERT_KEY_ID)) != 0 ||
693: (r = sshbuf_put_stringb(buf, sect)) != 0)
694: goto out;
1.1 djm 695: }
696: r = 0;
697: out:
1.26 djm 698: bitmap_free(bitmap);
1.20 djm 699: sshbuf_free(sect);
1.1 djm 700: return r;
701: }
702:
703: int
1.20 djm 704: ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf,
705: const struct sshkey **sign_keys, u_int nsign_keys)
1.1 djm 706: {
1.25 djm 707: int r = SSH_ERR_INTERNAL_ERROR;
1.1 djm 708: struct revoked_certs *rc;
709: struct revoked_blob *rb;
1.20 djm 710: struct sshbuf *sect;
711: u_char *sblob = NULL;
712: size_t slen, i;
1.1 djm 713:
714: if (krl->generated_date == 0)
715: krl->generated_date = time(NULL);
716:
1.20 djm 717: if ((sect = sshbuf_new()) == NULL)
718: return SSH_ERR_ALLOC_FAIL;
1.1 djm 719:
720: /* Store the header */
1.20 djm 721: if ((r = sshbuf_put(buf, KRL_MAGIC, sizeof(KRL_MAGIC) - 1)) != 0 ||
722: (r = sshbuf_put_u32(buf, KRL_FORMAT_VERSION)) != 0 ||
723: (r = sshbuf_put_u64(buf, krl->krl_version)) != 0 ||
1.34 jsg 724: (r = sshbuf_put_u64(buf, krl->generated_date)) != 0 ||
1.20 djm 725: (r = sshbuf_put_u64(buf, krl->flags)) != 0 ||
726: (r = sshbuf_put_string(buf, NULL, 0)) != 0 ||
727: (r = sshbuf_put_cstring(buf, krl->comment)) != 0)
728: goto out;
1.1 djm 729:
730: /* Store sections for revoked certificates */
731: TAILQ_FOREACH(rc, &krl->revoked_certs, entry) {
1.20 djm 732: sshbuf_reset(sect);
733: if ((r = revoked_certs_generate(rc, sect)) != 0)
734: goto out;
735: if ((r = sshbuf_put_u8(buf, KRL_SECTION_CERTIFICATES)) != 0 ||
736: (r = sshbuf_put_stringb(buf, sect)) != 0)
1.1 djm 737: goto out;
738: }
739:
740: /* Finally, output sections for revocations by public key/hash */
1.20 djm 741: sshbuf_reset(sect);
1.1 djm 742: RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) {
1.28 djm 743: KRL_DBG(("%s: key len %zu ", __func__, rb->len));
1.24 markus 744: if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0)
1.20 djm 745: goto out;
1.1 djm 746: }
1.20 djm 747: if (sshbuf_len(sect) != 0) {
748: if ((r = sshbuf_put_u8(buf, KRL_SECTION_EXPLICIT_KEY)) != 0 ||
749: (r = sshbuf_put_stringb(buf, sect)) != 0)
750: goto out;
1.1 djm 751: }
1.20 djm 752: sshbuf_reset(sect);
1.1 djm 753: RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) {
1.28 djm 754: KRL_DBG(("%s: hash len %zu ", __func__, rb->len));
1.24 markus 755: if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0)
1.20 djm 756: goto out;
1.1 djm 757: }
1.20 djm 758: if (sshbuf_len(sect) != 0) {
759: if ((r = sshbuf_put_u8(buf,
760: KRL_SECTION_FINGERPRINT_SHA1)) != 0 ||
761: (r = sshbuf_put_stringb(buf, sect)) != 0)
762: goto out;
1.1 djm 763: }
764:
765: for (i = 0; i < nsign_keys; i++) {
1.30 djm 766: KRL_DBG(("%s: signature key %s", __func__,
767: sshkey_ssh_name(sign_keys[i])));
1.20 djm 768: if ((r = sshbuf_put_u8(buf, KRL_SECTION_SIGNATURE)) != 0 ||
1.30 djm 769: (r = sshkey_puts(sign_keys[i], buf)) != 0)
1.20 djm 770: goto out;
1.1 djm 771:
1.20 djm 772: if ((r = sshkey_sign(sign_keys[i], &sblob, &slen,
1.35 markus 773: sshbuf_ptr(buf), sshbuf_len(buf), NULL, 0)) != 0)
1.20 djm 774: goto out;
1.28 djm 775: KRL_DBG(("%s: signature sig len %zu", __func__, slen));
1.20 djm 776: if ((r = sshbuf_put_string(buf, sblob, slen)) != 0)
1.1 djm 777: goto out;
778: }
779:
780: r = 0;
781: out:
782: free(sblob);
1.20 djm 783: sshbuf_free(sect);
1.1 djm 784: return r;
785: }
786:
787: static void
788: format_timestamp(u_int64_t timestamp, char *ts, size_t nts)
789: {
790: time_t t;
791: struct tm *tm;
792:
793: t = timestamp;
794: tm = localtime(&t);
1.19 djm 795: if (tm == NULL)
1.27 deraadt 796: strlcpy(ts, "<INVALID>", nts);
1.19 djm 797: else {
798: *ts = '\0';
799: strftime(ts, nts, "%Y%m%dT%H%M%S", tm);
800: }
1.1 djm 801: }
802:
803: static int
1.20 djm 804: parse_revoked_certs(struct sshbuf *buf, struct ssh_krl *krl)
1.1 djm 805: {
1.26 djm 806: int r = SSH_ERR_INTERNAL_ERROR;
1.15 djm 807: u_char type;
808: const u_char *blob;
1.26 djm 809: size_t blen, nbits;
1.20 djm 810: struct sshbuf *subsect = NULL;
1.1 djm 811: u_int64_t serial, serial_lo, serial_hi;
1.26 djm 812: struct bitmap *bitmap = NULL;
1.1 djm 813: char *key_id = NULL;
1.20 djm 814: struct sshkey *ca_key = NULL;
1.1 djm 815:
1.20 djm 816: if ((subsect = sshbuf_new()) == NULL)
817: return SSH_ERR_ALLOC_FAIL;
1.1 djm 818:
1.20 djm 819: /* Header: key, reserved */
820: if ((r = sshbuf_get_string_direct(buf, &blob, &blen)) != 0 ||
821: (r = sshbuf_skip_string(buf)) != 0)
1.1 djm 822: goto out;
1.31 djm 823: if (blen != 0 && (r = sshkey_from_blob(blob, blen, &ca_key)) != 0)
1.1 djm 824: goto out;
825:
1.20 djm 826: while (sshbuf_len(buf) > 0) {
1.36 mmcc 827: sshbuf_free(subsect);
828: subsect = NULL;
1.20 djm 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.37 ! djm 1016: sshbuf_ptr(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: }
1038:
1.20 djm 1039: if (sshbuf_len(copy) != 0) {
1040: /* Shouldn't happen */
1041: r = SSH_ERR_INTERNAL_ERROR;
1042: goto out;
1043: }
1044:
1.1 djm 1045: /*
1046: * 2nd pass: parse and load the KRL, skipping the header to the point
1047: * where the section start.
1048: */
1.20 djm 1049: sshbuf_free(copy);
1050: if ((copy = sshbuf_fromb(buf)) == NULL) {
1051: r = SSH_ERR_ALLOC_FAIL;
1052: goto out;
1053: }
1054: if ((r = sshbuf_consume(copy, sects_off)) != 0)
1055: goto out;
1056: while (sshbuf_len(copy) > 0) {
1.36 mmcc 1057: sshbuf_free(sect);
1058: sect = NULL;
1.20 djm 1059: if ((r = sshbuf_get_u8(copy, &type)) != 0 ||
1.25 djm 1060: (r = sshbuf_froms(copy, §)) != 0)
1.1 djm 1061: goto out;
1.25 djm 1062: KRL_DBG(("%s: second pass, section 0x%02x", __func__, type));
1.1 djm 1063:
1064: switch (type) {
1065: case KRL_SECTION_CERTIFICATES:
1.20 djm 1066: if ((r = parse_revoked_certs(sect, krl)) != 0)
1.1 djm 1067: goto out;
1068: break;
1069: case KRL_SECTION_EXPLICIT_KEY:
1070: case KRL_SECTION_FINGERPRINT_SHA1:
1.20 djm 1071: while (sshbuf_len(sect) > 0) {
1072: if ((r = sshbuf_get_string(sect,
1073: &rdata, &rlen)) != 0)
1.1 djm 1074: goto out;
1075: if (type == KRL_SECTION_FINGERPRINT_SHA1 &&
1.12 markus 1076: rlen != 20) {
1.1 djm 1077: error("%s: bad SHA1 length", __func__);
1.20 djm 1078: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 1079: goto out;
1080: }
1.20 djm 1081: if ((r = revoke_blob(
1.1 djm 1082: type == KRL_SECTION_EXPLICIT_KEY ?
1083: &krl->revoked_keys : &krl->revoked_sha1s,
1.20 djm 1084: rdata, rlen)) != 0)
1.12 markus 1085: goto out;
1.25 djm 1086: rdata = NULL; /* revoke_blob frees rdata */
1.1 djm 1087: }
1088: break;
1089: case KRL_SECTION_SIGNATURE:
1090: /* Handled above, but still need to stay in synch */
1.20 djm 1091: sshbuf_reset(sect);
1092: sect = NULL;
1093: if ((r = sshbuf_skip_string(copy)) != 0)
1.1 djm 1094: goto out;
1095: break;
1096: default:
1097: error("Unsupported KRL section %u", type);
1.20 djm 1098: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 1099: goto out;
1100: }
1.37 ! djm 1101: if (sect != NULL && sshbuf_len(sect) > 0) {
1.1 djm 1102: error("KRL section contains unparsed data");
1.20 djm 1103: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 1104: goto out;
1105: }
1106: }
1107:
1108: /* Check that the key(s) used to sign the KRL weren't revoked */
1109: sig_seen = 0;
1110: for (i = 0; i < nca_used; i++) {
1111: if (ssh_krl_check_key(krl, ca_used[i]) == 0)
1112: sig_seen = 1;
1113: else {
1.20 djm 1114: sshkey_free(ca_used[i]);
1.1 djm 1115: ca_used[i] = NULL;
1116: }
1117: }
1118: if (nca_used && !sig_seen) {
1119: error("All keys used to sign KRL were revoked");
1.25 djm 1120: r = SSH_ERR_KEY_REVOKED;
1.1 djm 1121: goto out;
1122: }
1123:
1124: /* If we have CA keys, then verify that one was used to sign the KRL */
1125: if (sig_seen && nsign_ca_keys != 0) {
1126: sig_seen = 0;
1127: for (i = 0; !sig_seen && i < nsign_ca_keys; i++) {
1128: for (j = 0; j < nca_used; j++) {
1129: if (ca_used[j] == NULL)
1130: continue;
1.20 djm 1131: if (sshkey_equal(ca_used[j], sign_ca_keys[i])) {
1.1 djm 1132: sig_seen = 1;
1133: break;
1134: }
1135: }
1136: }
1137: if (!sig_seen) {
1.20 djm 1138: r = SSH_ERR_SIGNATURE_INVALID;
1.1 djm 1139: error("KRL not signed with any trusted key");
1140: goto out;
1141: }
1142: }
1143:
1144: *krlp = krl;
1.20 djm 1145: r = 0;
1.1 djm 1146: out:
1.20 djm 1147: if (r != 0)
1.1 djm 1148: ssh_krl_free(krl);
1.20 djm 1149: for (i = 0; i < nca_used; i++)
1150: sshkey_free(ca_used[i]);
1.1 djm 1151: free(ca_used);
1.12 markus 1152: free(rdata);
1.20 djm 1153: sshkey_free(key);
1154: sshbuf_free(copy);
1155: sshbuf_free(sect);
1156: return r;
1.1 djm 1157: }
1158:
1.31 djm 1159: /* Checks certificate serial number and key ID revocation */
1160: static int
1161: is_cert_revoked(const struct sshkey *key, struct revoked_certs *rc)
1162: {
1163: struct revoked_serial rs, *ers;
1164: struct revoked_key_id rki, *erki;
1165:
1166: /* Check revocation by cert key ID */
1167: memset(&rki, 0, sizeof(rki));
1168: rki.key_id = key->cert->key_id;
1169: erki = RB_FIND(revoked_key_id_tree, &rc->revoked_key_ids, &rki);
1170: if (erki != NULL) {
1171: KRL_DBG(("%s: revoked by key ID", __func__));
1172: return SSH_ERR_KEY_REVOKED;
1173: }
1174:
1175: /*
1.33 djm 1176: * Zero serials numbers are ignored (it's the default when the
1177: * CA doesn't specify one).
1.31 djm 1178: */
1.33 djm 1179: if (key->cert->serial == 0)
1.31 djm 1180: return 0;
1181:
1182: memset(&rs, 0, sizeof(rs));
1183: rs.lo = rs.hi = key->cert->serial;
1184: ers = RB_FIND(revoked_serial_tree, &rc->revoked_serials, &rs);
1185: if (ers != NULL) {
1186: KRL_DBG(("%s: revoked serial %llu matched %llu:%llu", __func__,
1187: key->cert->serial, ers->lo, ers->hi));
1188: return SSH_ERR_KEY_REVOKED;
1189: }
1190: return 0;
1191: }
1192:
1.1 djm 1193: /* Checks whether a given key/cert is revoked. Does not check its CA */
1194: static int
1.20 djm 1195: is_key_revoked(struct ssh_krl *krl, const struct sshkey *key)
1.1 djm 1196: {
1197: struct revoked_blob rb, *erb;
1198: struct revoked_certs *rc;
1.20 djm 1199: int r;
1.1 djm 1200:
1201: /* Check explicitly revoked hashes first */
1.14 tedu 1202: memset(&rb, 0, sizeof(rb));
1.21 djm 1203: if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA1,
1.20 djm 1204: &rb.blob, &rb.len)) != 0)
1205: return r;
1.1 djm 1206: erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha1s, &rb);
1207: free(rb.blob);
1208: if (erb != NULL) {
1.25 djm 1209: KRL_DBG(("%s: revoked by key SHA1", __func__));
1.20 djm 1210: return SSH_ERR_KEY_REVOKED;
1.1 djm 1211: }
1212:
1213: /* Next, explicit keys */
1.14 tedu 1214: memset(&rb, 0, sizeof(rb));
1.20 djm 1215: if ((r = plain_key_blob(key, &rb.blob, &rb.len)) != 0)
1216: return r;
1.1 djm 1217: erb = RB_FIND(revoked_blob_tree, &krl->revoked_keys, &rb);
1218: free(rb.blob);
1219: if (erb != NULL) {
1.25 djm 1220: KRL_DBG(("%s: revoked by explicit key", __func__));
1.20 djm 1221: return SSH_ERR_KEY_REVOKED;
1.1 djm 1222: }
1223:
1.20 djm 1224: if (!sshkey_is_cert(key))
1.1 djm 1225: return 0;
1226:
1.31 djm 1227: /* Check cert revocation for the specified CA */
1.20 djm 1228: if ((r = revoked_certs_for_ca_key(krl, key->cert->signature_key,
1229: &rc, 0)) != 0)
1230: return r;
1.31 djm 1231: if (rc != NULL) {
1232: if ((r = is_cert_revoked(key, rc)) != 0)
1233: return r;
1234: }
1235: /* Check cert revocation for the wildcard CA */
1236: if ((r = revoked_certs_for_ca_key(krl, NULL, &rc, 0)) != 0)
1237: return r;
1238: if (rc != NULL) {
1239: if ((r = is_cert_revoked(key, rc)) != 0)
1240: return r;
1.1 djm 1241: }
1242:
1243: KRL_DBG(("%s: %llu no match", __func__, key->cert->serial));
1244: return 0;
1245: }
1246:
1247: int
1.20 djm 1248: ssh_krl_check_key(struct ssh_krl *krl, const struct sshkey *key)
1.1 djm 1249: {
1250: int r;
1251:
1.25 djm 1252: KRL_DBG(("%s: checking key", __func__));
1.1 djm 1253: if ((r = is_key_revoked(krl, key)) != 0)
1254: return r;
1.20 djm 1255: if (sshkey_is_cert(key)) {
1.1 djm 1256: debug2("%s: checking CA key", __func__);
1257: if ((r = is_key_revoked(krl, key->cert->signature_key)) != 0)
1258: return r;
1259: }
1.25 djm 1260: KRL_DBG(("%s: key okay", __func__));
1.1 djm 1261: return 0;
1262: }
1263:
1264: int
1.20 djm 1265: ssh_krl_file_contains_key(const char *path, const struct sshkey *key)
1.1 djm 1266: {
1.20 djm 1267: struct sshbuf *krlbuf = NULL;
1268: struct ssh_krl *krl = NULL;
1269: int oerrno = 0, r, fd;
1.1 djm 1270:
1271: if (path == NULL)
1272: return 0;
1273:
1.20 djm 1274: if ((krlbuf = sshbuf_new()) == NULL)
1275: return SSH_ERR_ALLOC_FAIL;
1.1 djm 1276: if ((fd = open(path, O_RDONLY)) == -1) {
1.20 djm 1277: r = SSH_ERR_SYSTEM_ERROR;
1278: oerrno = errno;
1279: goto out;
1.1 djm 1280: }
1.22 djm 1281: if ((r = sshkey_load_file(fd, krlbuf)) != 0) {
1.20 djm 1282: oerrno = errno;
1283: goto out;
1.1 djm 1284: }
1.20 djm 1285: if ((r = ssh_krl_from_blob(krlbuf, &krl, NULL, 0)) != 0)
1286: goto out;
1287: debug2("%s: checking KRL %s", __func__, path);
1288: r = ssh_krl_check_key(krl, key);
1289: out:
1.1 djm 1290: close(fd);
1.20 djm 1291: sshbuf_free(krlbuf);
1.1 djm 1292: ssh_krl_free(krl);
1.20 djm 1293: if (r != 0)
1294: errno = oerrno;
1295: return r;
1.1 djm 1296: }