Annotation of src/usr.bin/ssh/krl.c, Revision 1.33
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.33 ! djm 17: /* $OpenBSD: krl.c,v 1.32 2015/06/24 23:47:23 djm 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 ||
724: (r = sshbuf_put_u64(buf, krl->generated_date) != 0) ||
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.32 djm 773: sshbuf_ptr(buf), sshbuf_len(buf), 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) {
827: if (subsect != NULL) {
828: sshbuf_free(subsect);
829: subsect = NULL;
830: }
831: if ((r = sshbuf_get_u8(buf, &type)) != 0 ||
832: (r = sshbuf_froms(buf, &subsect)) != 0)
1.1 djm 833: goto out;
1.25 djm 834: KRL_DBG(("%s: subsection type 0x%02x", __func__, type));
835: /* sshbuf_dump(subsect, stderr); */
1.1 djm 836:
837: switch (type) {
838: case KRL_SECTION_CERT_SERIAL_LIST:
1.20 djm 839: while (sshbuf_len(subsect) > 0) {
840: if ((r = sshbuf_get_u64(subsect, &serial)) != 0)
1.1 djm 841: goto out;
1.20 djm 842: if ((r = ssh_krl_revoke_cert_by_serial(krl,
843: ca_key, serial)) != 0)
1.1 djm 844: goto out;
845: }
846: break;
847: case KRL_SECTION_CERT_SERIAL_RANGE:
1.20 djm 848: if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 ||
849: (r = sshbuf_get_u64(subsect, &serial_hi)) != 0)
1.1 djm 850: goto out;
1.20 djm 851: if ((r = ssh_krl_revoke_cert_by_serial_range(krl,
852: ca_key, serial_lo, serial_hi)) != 0)
1.1 djm 853: goto out;
854: break;
855: case KRL_SECTION_CERT_SERIAL_BITMAP:
1.26 djm 856: if ((bitmap = bitmap_new()) == NULL) {
1.20 djm 857: r = SSH_ERR_ALLOC_FAIL;
1.1 djm 858: goto out;
859: }
1.20 djm 860: if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 ||
1.26 djm 861: (r = sshbuf_get_bignum2_bytes_direct(subsect,
862: &blob, &blen)) != 0)
1.1 djm 863: goto out;
1.26 djm 864: if (bitmap_from_string(bitmap, blob, blen) != 0) {
1.20 djm 865: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 866: goto out;
867: }
1.26 djm 868: nbits = bitmap_nbits(bitmap);
1.25 djm 869: for (serial = 0; serial < (u_int64_t)nbits; serial++) {
1.1 djm 870: if (serial > 0 && serial_lo + serial == 0) {
871: error("%s: bitmap wraps u64", __func__);
1.20 djm 872: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 873: goto out;
874: }
1.26 djm 875: if (!bitmap_test_bit(bitmap, serial))
1.1 djm 876: continue;
1.20 djm 877: if ((r = ssh_krl_revoke_cert_by_serial(krl,
878: ca_key, serial_lo + serial)) != 0)
1.1 djm 879: goto out;
880: }
1.26 djm 881: bitmap_free(bitmap);
1.1 djm 882: bitmap = NULL;
883: break;
884: case KRL_SECTION_CERT_KEY_ID:
1.20 djm 885: while (sshbuf_len(subsect) > 0) {
886: if ((r = sshbuf_get_cstring(subsect,
887: &key_id, NULL)) != 0)
1.1 djm 888: goto out;
1.20 djm 889: if ((r = ssh_krl_revoke_cert_by_key_id(krl,
890: ca_key, key_id)) != 0)
1.1 djm 891: goto out;
892: free(key_id);
893: key_id = NULL;
894: }
895: break;
896: default:
897: error("Unsupported KRL certificate section %u", type);
1.20 djm 898: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 899: goto out;
900: }
1.20 djm 901: if (sshbuf_len(subsect) > 0) {
1.1 djm 902: error("KRL certificate section contains unparsed data");
1.20 djm 903: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 904: goto out;
905: }
906: }
907:
1.20 djm 908: r = 0;
1.1 djm 909: out:
910: if (bitmap != NULL)
1.26 djm 911: bitmap_free(bitmap);
1.1 djm 912: free(key_id);
1.20 djm 913: sshkey_free(ca_key);
914: sshbuf_free(subsect);
915: return r;
1.1 djm 916: }
917:
918:
919: /* Attempt to parse a KRL, checking its signature (if any) with sign_ca_keys. */
920: int
1.20 djm 921: ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp,
1.25 djm 922: const struct sshkey **sign_ca_keys, size_t nsign_ca_keys)
1.1 djm 923: {
1.20 djm 924: struct sshbuf *copy = NULL, *sect = NULL;
925: struct ssh_krl *krl = NULL;
1.1 djm 926: char timestamp[64];
1.25 djm 927: int r = SSH_ERR_INTERNAL_ERROR, sig_seen;
1.20 djm 928: struct sshkey *key = NULL, **ca_used = NULL, **tmp_ca_used;
1.15 djm 929: u_char type, *rdata = NULL;
930: const u_char *blob;
1.20 djm 931: size_t i, j, sig_off, sects_off, rlen, blen, nca_used;
932: u_int format_version;
1.1 djm 933:
1.12 markus 934: nca_used = 0;
1.1 djm 935: *krlp = NULL;
1.20 djm 936: if (sshbuf_len(buf) < sizeof(KRL_MAGIC) - 1 ||
937: memcmp(sshbuf_ptr(buf), KRL_MAGIC, sizeof(KRL_MAGIC) - 1) != 0) {
1.1 djm 938: debug3("%s: not a KRL", __func__);
1.20 djm 939: return SSH_ERR_KRL_BAD_MAGIC;
1.1 djm 940: }
941:
942: /* Take a copy of the KRL buffer so we can verify its signature later */
1.20 djm 943: if ((copy = sshbuf_fromb(buf)) == NULL) {
944: r = SSH_ERR_ALLOC_FAIL;
945: goto out;
946: }
947: if ((r = sshbuf_consume(copy, sizeof(KRL_MAGIC) - 1)) != 0)
948: goto out;
1.1 djm 949:
950: if ((krl = ssh_krl_init()) == NULL) {
951: error("%s: alloc failed", __func__);
952: goto out;
953: }
954:
1.20 djm 955: if ((r = sshbuf_get_u32(copy, &format_version)) != 0)
1.1 djm 956: goto out;
957: if (format_version != KRL_FORMAT_VERSION) {
1.20 djm 958: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 959: goto out;
960: }
1.20 djm 961: if ((r = sshbuf_get_u64(copy, &krl->krl_version)) != 0 ||
962: (r = sshbuf_get_u64(copy, &krl->generated_date)) != 0 ||
963: (r = sshbuf_get_u64(copy, &krl->flags)) != 0 ||
964: (r = sshbuf_skip_string(copy)) != 0 ||
965: (r = sshbuf_get_cstring(copy, &krl->comment, NULL)) != 0)
1.1 djm 966: goto out;
967:
968: format_timestamp(krl->generated_date, timestamp, sizeof(timestamp));
1.11 djm 969: debug("KRL version %llu generated at %s%s%s",
970: (long long unsigned)krl->krl_version, timestamp,
971: *krl->comment ? ": " : "", krl->comment);
1.1 djm 972:
973: /*
974: * 1st pass: verify signatures, if any. This is done to avoid
975: * detailed parsing of data whose provenance is unverified.
976: */
977: sig_seen = 0;
1.20 djm 978: if (sshbuf_len(buf) < sshbuf_len(copy)) {
979: /* Shouldn't happen */
980: r = SSH_ERR_INTERNAL_ERROR;
981: goto out;
982: }
983: sects_off = sshbuf_len(buf) - sshbuf_len(copy);
984: while (sshbuf_len(copy) > 0) {
985: if ((r = sshbuf_get_u8(copy, &type)) != 0 ||
986: (r = sshbuf_get_string_direct(copy, &blob, &blen)) != 0)
1.1 djm 987: goto out;
1.25 djm 988: KRL_DBG(("%s: first pass, section 0x%02x", __func__, type));
1.1 djm 989: if (type != KRL_SECTION_SIGNATURE) {
990: if (sig_seen) {
991: error("KRL contains non-signature section "
992: "after signature");
1.25 djm 993: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 994: goto out;
995: }
996: /* Not interested for now. */
997: continue;
998: }
999: sig_seen = 1;
1000: /* First string component is the signing key */
1.20 djm 1001: if ((r = sshkey_from_blob(blob, blen, &key)) != 0) {
1002: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 1003: goto out;
1004: }
1.20 djm 1005: if (sshbuf_len(buf) < sshbuf_len(copy)) {
1006: /* Shouldn't happen */
1007: r = SSH_ERR_INTERNAL_ERROR;
1008: goto out;
1009: }
1010: sig_off = sshbuf_len(buf) - sshbuf_len(copy);
1.1 djm 1011: /* Second string component is the signature itself */
1.20 djm 1012: if ((r = sshbuf_get_string_direct(copy, &blob, &blen)) != 0) {
1013: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 1014: goto out;
1015: }
1016: /* Check signature over entire KRL up to this point */
1.20 djm 1017: if ((r = sshkey_verify(key, blob, blen,
1.25 djm 1018: sshbuf_ptr(buf), sshbuf_len(buf) - sig_off, 0)) != 0)
1.1 djm 1019: goto out;
1020: /* Check if this key has already signed this KRL */
1021: for (i = 0; i < nca_used; i++) {
1.20 djm 1022: if (sshkey_equal(ca_used[i], key)) {
1.1 djm 1023: error("KRL signed more than once with "
1024: "the same key");
1.25 djm 1025: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 1026: goto out;
1027: }
1028: }
1029: /* Record keys used to sign the KRL */
1.20 djm 1030: tmp_ca_used = reallocarray(ca_used, nca_used + 1,
1031: sizeof(*ca_used));
1032: if (tmp_ca_used == NULL) {
1033: r = SSH_ERR_ALLOC_FAIL;
1034: goto out;
1035: }
1036: ca_used = tmp_ca_used;
1.1 djm 1037: ca_used[nca_used++] = key;
1038: key = NULL;
1039: break;
1040: }
1041:
1.20 djm 1042: if (sshbuf_len(copy) != 0) {
1043: /* Shouldn't happen */
1044: r = SSH_ERR_INTERNAL_ERROR;
1045: goto out;
1046: }
1047:
1.1 djm 1048: /*
1049: * 2nd pass: parse and load the KRL, skipping the header to the point
1050: * where the section start.
1051: */
1.20 djm 1052: sshbuf_free(copy);
1053: if ((copy = sshbuf_fromb(buf)) == NULL) {
1054: r = SSH_ERR_ALLOC_FAIL;
1055: goto out;
1056: }
1057: if ((r = sshbuf_consume(copy, sects_off)) != 0)
1058: goto out;
1059: while (sshbuf_len(copy) > 0) {
1060: if (sect != NULL) {
1061: sshbuf_free(sect);
1062: sect = NULL;
1063: }
1064: if ((r = sshbuf_get_u8(copy, &type)) != 0 ||
1.25 djm 1065: (r = sshbuf_froms(copy, §)) != 0)
1.1 djm 1066: goto out;
1.25 djm 1067: KRL_DBG(("%s: second pass, section 0x%02x", __func__, type));
1.1 djm 1068:
1069: switch (type) {
1070: case KRL_SECTION_CERTIFICATES:
1.20 djm 1071: if ((r = parse_revoked_certs(sect, krl)) != 0)
1.1 djm 1072: goto out;
1073: break;
1074: case KRL_SECTION_EXPLICIT_KEY:
1075: case KRL_SECTION_FINGERPRINT_SHA1:
1.20 djm 1076: while (sshbuf_len(sect) > 0) {
1077: if ((r = sshbuf_get_string(sect,
1078: &rdata, &rlen)) != 0)
1.1 djm 1079: goto out;
1080: if (type == KRL_SECTION_FINGERPRINT_SHA1 &&
1.12 markus 1081: rlen != 20) {
1.1 djm 1082: error("%s: bad SHA1 length", __func__);
1.20 djm 1083: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 1084: goto out;
1085: }
1.20 djm 1086: if ((r = revoke_blob(
1.1 djm 1087: type == KRL_SECTION_EXPLICIT_KEY ?
1088: &krl->revoked_keys : &krl->revoked_sha1s,
1.20 djm 1089: rdata, rlen)) != 0)
1.12 markus 1090: goto out;
1.25 djm 1091: rdata = NULL; /* revoke_blob frees rdata */
1.1 djm 1092: }
1093: break;
1094: case KRL_SECTION_SIGNATURE:
1095: /* Handled above, but still need to stay in synch */
1.20 djm 1096: sshbuf_reset(sect);
1097: sect = NULL;
1098: if ((r = sshbuf_skip_string(copy)) != 0)
1.1 djm 1099: goto out;
1100: break;
1101: default:
1102: error("Unsupported KRL section %u", type);
1.20 djm 1103: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 1104: goto out;
1105: }
1.20 djm 1106: if (sshbuf_len(sect) > 0) {
1.1 djm 1107: error("KRL section contains unparsed data");
1.20 djm 1108: r = SSH_ERR_INVALID_FORMAT;
1.1 djm 1109: goto out;
1110: }
1111: }
1112:
1113: /* Check that the key(s) used to sign the KRL weren't revoked */
1114: sig_seen = 0;
1115: for (i = 0; i < nca_used; i++) {
1116: if (ssh_krl_check_key(krl, ca_used[i]) == 0)
1117: sig_seen = 1;
1118: else {
1.20 djm 1119: sshkey_free(ca_used[i]);
1.1 djm 1120: ca_used[i] = NULL;
1121: }
1122: }
1123: if (nca_used && !sig_seen) {
1124: error("All keys used to sign KRL were revoked");
1.25 djm 1125: r = SSH_ERR_KEY_REVOKED;
1.1 djm 1126: goto out;
1127: }
1128:
1129: /* If we have CA keys, then verify that one was used to sign the KRL */
1130: if (sig_seen && nsign_ca_keys != 0) {
1131: sig_seen = 0;
1132: for (i = 0; !sig_seen && i < nsign_ca_keys; i++) {
1133: for (j = 0; j < nca_used; j++) {
1134: if (ca_used[j] == NULL)
1135: continue;
1.20 djm 1136: if (sshkey_equal(ca_used[j], sign_ca_keys[i])) {
1.1 djm 1137: sig_seen = 1;
1138: break;
1139: }
1140: }
1141: }
1142: if (!sig_seen) {
1.20 djm 1143: r = SSH_ERR_SIGNATURE_INVALID;
1.1 djm 1144: error("KRL not signed with any trusted key");
1145: goto out;
1146: }
1147: }
1148:
1149: *krlp = krl;
1.20 djm 1150: r = 0;
1.1 djm 1151: out:
1.20 djm 1152: if (r != 0)
1.1 djm 1153: ssh_krl_free(krl);
1.20 djm 1154: for (i = 0; i < nca_used; i++)
1155: sshkey_free(ca_used[i]);
1.1 djm 1156: free(ca_used);
1.12 markus 1157: free(rdata);
1.20 djm 1158: sshkey_free(key);
1159: sshbuf_free(copy);
1160: sshbuf_free(sect);
1161: return r;
1.1 djm 1162: }
1163:
1.31 djm 1164: /* Checks certificate serial number and key ID revocation */
1165: static int
1166: is_cert_revoked(const struct sshkey *key, struct revoked_certs *rc)
1167: {
1168: struct revoked_serial rs, *ers;
1169: struct revoked_key_id rki, *erki;
1170:
1171: /* Check revocation by cert key ID */
1172: memset(&rki, 0, sizeof(rki));
1173: rki.key_id = key->cert->key_id;
1174: erki = RB_FIND(revoked_key_id_tree, &rc->revoked_key_ids, &rki);
1175: if (erki != NULL) {
1176: KRL_DBG(("%s: revoked by key ID", __func__));
1177: return SSH_ERR_KEY_REVOKED;
1178: }
1179:
1180: /*
1.33 ! djm 1181: * Zero serials numbers are ignored (it's the default when the
! 1182: * CA doesn't specify one).
1.31 djm 1183: */
1.33 ! djm 1184: if (key->cert->serial == 0)
1.31 djm 1185: return 0;
1186:
1187: memset(&rs, 0, sizeof(rs));
1188: rs.lo = rs.hi = key->cert->serial;
1189: ers = RB_FIND(revoked_serial_tree, &rc->revoked_serials, &rs);
1190: if (ers != NULL) {
1191: KRL_DBG(("%s: revoked serial %llu matched %llu:%llu", __func__,
1192: key->cert->serial, ers->lo, ers->hi));
1193: return SSH_ERR_KEY_REVOKED;
1194: }
1195: return 0;
1196: }
1197:
1.1 djm 1198: /* Checks whether a given key/cert is revoked. Does not check its CA */
1199: static int
1.20 djm 1200: is_key_revoked(struct ssh_krl *krl, const struct sshkey *key)
1.1 djm 1201: {
1202: struct revoked_blob rb, *erb;
1203: struct revoked_certs *rc;
1.20 djm 1204: int r;
1.1 djm 1205:
1206: /* Check explicitly revoked hashes first */
1.14 tedu 1207: memset(&rb, 0, sizeof(rb));
1.21 djm 1208: if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA1,
1.20 djm 1209: &rb.blob, &rb.len)) != 0)
1210: return r;
1.1 djm 1211: erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha1s, &rb);
1212: free(rb.blob);
1213: if (erb != NULL) {
1.25 djm 1214: KRL_DBG(("%s: revoked by key SHA1", __func__));
1.20 djm 1215: return SSH_ERR_KEY_REVOKED;
1.1 djm 1216: }
1217:
1218: /* Next, explicit keys */
1.14 tedu 1219: memset(&rb, 0, sizeof(rb));
1.20 djm 1220: if ((r = plain_key_blob(key, &rb.blob, &rb.len)) != 0)
1221: return r;
1.1 djm 1222: erb = RB_FIND(revoked_blob_tree, &krl->revoked_keys, &rb);
1223: free(rb.blob);
1224: if (erb != NULL) {
1.25 djm 1225: KRL_DBG(("%s: revoked by explicit key", __func__));
1.20 djm 1226: return SSH_ERR_KEY_REVOKED;
1.1 djm 1227: }
1228:
1.20 djm 1229: if (!sshkey_is_cert(key))
1.1 djm 1230: return 0;
1231:
1.31 djm 1232: /* Check cert revocation for the specified CA */
1.20 djm 1233: if ((r = revoked_certs_for_ca_key(krl, key->cert->signature_key,
1234: &rc, 0)) != 0)
1235: return r;
1.31 djm 1236: if (rc != NULL) {
1237: if ((r = is_cert_revoked(key, rc)) != 0)
1238: return r;
1239: }
1240: /* Check cert revocation for the wildcard CA */
1241: if ((r = revoked_certs_for_ca_key(krl, NULL, &rc, 0)) != 0)
1242: return r;
1243: if (rc != NULL) {
1244: if ((r = is_cert_revoked(key, rc)) != 0)
1245: return r;
1.1 djm 1246: }
1247:
1248: KRL_DBG(("%s: %llu no match", __func__, key->cert->serial));
1249: return 0;
1250: }
1251:
1252: int
1.20 djm 1253: ssh_krl_check_key(struct ssh_krl *krl, const struct sshkey *key)
1.1 djm 1254: {
1255: int r;
1256:
1.25 djm 1257: KRL_DBG(("%s: checking key", __func__));
1.1 djm 1258: if ((r = is_key_revoked(krl, key)) != 0)
1259: return r;
1.20 djm 1260: if (sshkey_is_cert(key)) {
1.1 djm 1261: debug2("%s: checking CA key", __func__);
1262: if ((r = is_key_revoked(krl, key->cert->signature_key)) != 0)
1263: return r;
1264: }
1.25 djm 1265: KRL_DBG(("%s: key okay", __func__));
1.1 djm 1266: return 0;
1267: }
1268:
1269: int
1.20 djm 1270: ssh_krl_file_contains_key(const char *path, const struct sshkey *key)
1.1 djm 1271: {
1.20 djm 1272: struct sshbuf *krlbuf = NULL;
1273: struct ssh_krl *krl = NULL;
1274: int oerrno = 0, r, fd;
1.1 djm 1275:
1276: if (path == NULL)
1277: return 0;
1278:
1.20 djm 1279: if ((krlbuf = sshbuf_new()) == NULL)
1280: return SSH_ERR_ALLOC_FAIL;
1.1 djm 1281: if ((fd = open(path, O_RDONLY)) == -1) {
1.20 djm 1282: r = SSH_ERR_SYSTEM_ERROR;
1283: oerrno = errno;
1284: goto out;
1.1 djm 1285: }
1.22 djm 1286: if ((r = sshkey_load_file(fd, krlbuf)) != 0) {
1.20 djm 1287: oerrno = errno;
1288: goto out;
1.1 djm 1289: }
1.20 djm 1290: if ((r = ssh_krl_from_blob(krlbuf, &krl, NULL, 0)) != 0)
1291: goto out;
1292: debug2("%s: checking KRL %s", __func__, path);
1293: r = ssh_krl_check_key(krl, key);
1294: out:
1.1 djm 1295: close(fd);
1.20 djm 1296: sshbuf_free(krlbuf);
1.1 djm 1297: ssh_krl_free(krl);
1.20 djm 1298: if (r != 0)
1299: errno = oerrno;
1300: return r;
1.1 djm 1301: }