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