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