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