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