jcs's openbsd hax
openbsd
at jcs 1386 lines 36 kB view raw
1/* $OpenBSD: krl.c,v 1.63 2026/02/14 00:18:34 jsg Exp $ */ 2/* 3 * Copyright (c) 2012 Damien Miller <djm@mindrot.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/types.h> 19#include <sys/tree.h> 20#include <sys/queue.h> 21 22#include <errno.h> 23#include <limits.h> 24#include <stdlib.h> 25#include <string.h> 26#include <time.h> 27 28#include "sshbuf.h" 29#include "ssherr.h" 30#include "sshkey.h" 31#include "misc.h" 32#include "log.h" 33#include "digest.h" 34#include "bitmap.h" 35#include "utf8.h" 36 37#include "krl.h" 38 39/* #define DEBUG_KRL */ 40#ifdef DEBUG_KRL 41# define KRL_DBG(x) debug3_f x 42#else 43# define KRL_DBG(x) 44#endif 45 46/* 47 * Trees of revoked serial numbers, key IDs and keys. This allows 48 * quick searching, querying and producing lists in canonical order. 49 */ 50 51/* Tree of serial numbers. XXX make smarter: really need a real sparse bitmap */ 52struct revoked_serial { 53 u_int64_t lo, hi; 54 RB_ENTRY(revoked_serial) tree_entry; 55}; 56static int serial_cmp(struct revoked_serial *a, struct revoked_serial *b); 57RB_HEAD(revoked_serial_tree, revoked_serial); 58RB_GENERATE_STATIC(revoked_serial_tree, revoked_serial, tree_entry, serial_cmp) 59 60/* Tree of key IDs */ 61struct revoked_key_id { 62 char *key_id; 63 RB_ENTRY(revoked_key_id) tree_entry; 64}; 65static int key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b); 66RB_HEAD(revoked_key_id_tree, revoked_key_id); 67RB_GENERATE_STATIC(revoked_key_id_tree, revoked_key_id, tree_entry, key_id_cmp) 68 69/* Tree of blobs (used for keys and fingerprints) */ 70struct revoked_blob { 71 u_char *blob; 72 size_t len; 73 RB_ENTRY(revoked_blob) tree_entry; 74}; 75static int blob_cmp(struct revoked_blob *a, struct revoked_blob *b); 76RB_HEAD(revoked_blob_tree, revoked_blob); 77RB_GENERATE_STATIC(revoked_blob_tree, revoked_blob, tree_entry, blob_cmp) 78 79/* Tracks revoked certs for a single CA */ 80struct revoked_certs { 81 struct sshkey *ca_key; 82 struct revoked_serial_tree revoked_serials; 83 struct revoked_key_id_tree revoked_key_ids; 84 TAILQ_ENTRY(revoked_certs) entry; 85}; 86TAILQ_HEAD(revoked_certs_list, revoked_certs); 87 88struct ssh_krl { 89 u_int64_t krl_version; 90 u_int64_t generated_date; 91 u_int64_t flags; 92 char *comment; 93 struct revoked_blob_tree revoked_keys; 94 struct revoked_blob_tree revoked_sha1s; 95 struct revoked_blob_tree revoked_sha256s; 96 struct revoked_certs_list revoked_certs; 97}; 98 99/* Return equal if a and b overlap */ 100static int 101serial_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 108static int 109key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b) 110{ 111 return strcmp(a->key_id, b->key_id); 112} 113 114static int 115blob_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, MINIMUM(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 127struct ssh_krl * 128ssh_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 RB_INIT(&krl->revoked_sha256s); 137 TAILQ_INIT(&krl->revoked_certs); 138 return krl; 139} 140 141static void 142revoked_certs_free(struct revoked_certs *rc) 143{ 144 struct revoked_serial *rs, *trs; 145 struct revoked_key_id *rki, *trki; 146 147 if (rc == NULL) 148 return; 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 } 158 sshkey_free(rc->ca_key); 159 freezero(rc, sizeof(*rc)); 160} 161 162void 163ssh_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 } 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 } 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 free(krl); 192} 193 194void 195ssh_krl_set_version(struct ssh_krl *krl, u_int64_t version) 196{ 197 krl->krl_version = version; 198} 199 200int 201ssh_krl_set_comment(struct ssh_krl *krl, const char *comment) 202{ 203 free(krl->comment); 204 if ((krl->comment = strdup(comment)) == NULL) 205 return SSH_ERR_ALLOC_FAIL; 206 return 0; 207} 208 209/* 210 * Find the revoked_certs struct for a CA key. If allow_create is set then 211 * create a new one in the tree if one did not exist already. 212 */ 213static int 214revoked_certs_for_ca_key(struct ssh_krl *krl, const struct sshkey *ca_key, 215 struct revoked_certs **rcp, int allow_create) 216{ 217 struct revoked_certs *rc; 218 int r; 219 220 *rcp = NULL; 221 TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { 222 if ((ca_key == NULL && rc->ca_key == NULL) || 223 sshkey_equal(rc->ca_key, ca_key)) { 224 *rcp = rc; 225 return 0; 226 } 227 } 228 if (!allow_create) 229 return 0; 230 /* If this CA doesn't exist in the list then add it now */ 231 if ((rc = calloc(1, sizeof(*rc))) == NULL) 232 return SSH_ERR_ALLOC_FAIL; 233 if (ca_key == NULL) 234 rc->ca_key = NULL; 235 else if ((r = sshkey_from_private(ca_key, &rc->ca_key)) != 0) { 236 free(rc); 237 return r; 238 } 239 RB_INIT(&rc->revoked_serials); 240 RB_INIT(&rc->revoked_key_ids); 241 TAILQ_INSERT_TAIL(&krl->revoked_certs, rc, entry); 242 KRL_DBG(("new CA %s", ca_key == NULL ? "*" : sshkey_type(ca_key))); 243 *rcp = rc; 244 return 0; 245} 246 247static int 248insert_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(("insert %llu:%llu", lo, hi)); 253 memset(&rs, 0, sizeof(rs)); 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) 260 return SSH_ERR_ALLOC_FAIL; 261 memcpy(irs, &rs, sizeof(*irs)); 262 ers = RB_INSERT(revoked_serial_tree, rt, irs); 263 if (ers != NULL) { 264 KRL_DBG(("bad: ers != NULL")); 265 /* Shouldn't happen */ 266 free(irs); 267 return SSH_ERR_INTERNAL_ERROR; 268 } 269 ers = irs; 270 } else { 271 KRL_DBG(("overlap found %llu:%llu", 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 } 281 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(("pred %llu:%llu", 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(("pred extend %llu:%llu", ers->lo, ers->hi)); 296 } 297 RB_REMOVE(revoked_serial_tree, rt, crs); 298 free(crs); 299 } 300 /* Check successors */ 301 while ((crs = RB_NEXT(revoked_serial_tree, rt, ers)) != NULL) { 302 KRL_DBG(("succ %llu:%llu", crs->lo, crs->hi)); 303 if (ers->hi != (u_int64_t)-1 && crs->lo > ers->hi + 1) 304 break; 305 /* This entry overlaps. */ 306 if (crs->hi > ers->hi) { 307 ers->hi = crs->hi; 308 KRL_DBG(("succ extend %llu:%llu", ers->lo, ers->hi)); 309 } 310 RB_REMOVE(revoked_serial_tree, rt, crs); 311 free(crs); 312 } 313 KRL_DBG(("done, final %llu:%llu", ers->lo, ers->hi)); 314 return 0; 315} 316 317int 318ssh_krl_revoke_cert_by_serial(struct ssh_krl *krl, const struct sshkey *ca_key, 319 u_int64_t serial) 320{ 321 return ssh_krl_revoke_cert_by_serial_range(krl, ca_key, serial, serial); 322} 323 324int 325ssh_krl_revoke_cert_by_serial_range(struct ssh_krl *krl, 326 const struct sshkey *ca_key, u_int64_t lo, u_int64_t hi) 327{ 328 struct revoked_certs *rc; 329 int r; 330 331 if (lo > hi || lo == 0) 332 return SSH_ERR_INVALID_ARGUMENT; 333 if ((r = revoked_certs_for_ca_key(krl, ca_key, &rc, 1)) != 0) 334 return r; 335 return insert_serial_range(&rc->revoked_serials, lo, hi); 336} 337 338int 339ssh_krl_revoke_cert_by_key_id(struct ssh_krl *krl, const struct sshkey *ca_key, 340 const char *key_id) 341{ 342 struct revoked_key_id *rki, *erki; 343 struct revoked_certs *rc; 344 int r; 345 346 if ((r = revoked_certs_for_ca_key(krl, ca_key, &rc, 1)) != 0) 347 return r; 348 349 KRL_DBG(("revoke %s", key_id)); 350 if ((rki = calloc(1, sizeof(*rki))) == NULL || 351 (rki->key_id = strdup(key_id)) == NULL) { 352 free(rki); 353 return SSH_ERR_ALLOC_FAIL; 354 } 355 erki = RB_INSERT(revoked_key_id_tree, &rc->revoked_key_ids, rki); 356 if (erki != NULL) { 357 free(rki->key_id); 358 free(rki); 359 } 360 return 0; 361} 362 363/* Convert "key" to a public key blob without any certificate information */ 364static int 365plain_key_blob(const struct sshkey *key, u_char **blob, size_t *blen) 366{ 367 struct sshkey *kcopy; 368 int r; 369 370 if ((r = sshkey_from_private(key, &kcopy)) != 0) 371 return r; 372 if (sshkey_is_cert(kcopy)) { 373 if ((r = sshkey_drop_cert(kcopy)) != 0) { 374 sshkey_free(kcopy); 375 return r; 376 } 377 } 378 r = sshkey_to_blob(kcopy, blob, blen); 379 sshkey_free(kcopy); 380 return r; 381} 382 383/* Revoke a key blob. Ownership of blob is transferred to the tree */ 384static int 385revoke_blob(struct revoked_blob_tree *rbt, u_char *blob, size_t len) 386{ 387 struct revoked_blob *rb, *erb; 388 389 if ((rb = calloc(1, sizeof(*rb))) == NULL) 390 return SSH_ERR_ALLOC_FAIL; 391 rb->blob = blob; 392 rb->len = len; 393 erb = RB_INSERT(revoked_blob_tree, rbt, rb); 394 if (erb != NULL) { 395 free(rb->blob); 396 free(rb); 397 } 398 return 0; 399} 400 401int 402ssh_krl_revoke_key_explicit(struct ssh_krl *krl, const struct sshkey *key) 403{ 404 u_char *blob; 405 size_t len; 406 int r; 407 408 debug3_f("revoke type %s", sshkey_type(key)); 409 if ((r = plain_key_blob(key, &blob, &len)) != 0) 410 return r; 411 return revoke_blob(&krl->revoked_keys, blob, len); 412} 413 414static int 415revoke_by_hash(struct revoked_blob_tree *target, const u_char *p, size_t len) 416{ 417 u_char *blob; 418 int r; 419 420 /* need to copy hash, as revoke_blob steals ownership */ 421 if ((blob = malloc(len)) == NULL) 422 return SSH_ERR_SYSTEM_ERROR; 423 memcpy(blob, p, len); 424 if ((r = revoke_blob(target, blob, len)) != 0) { 425 free(blob); 426 return r; 427 } 428 return 0; 429} 430 431int 432ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const u_char *p, size_t len) 433{ 434 debug3_f("revoke by sha1"); 435 if (len != 20) 436 return SSH_ERR_INVALID_FORMAT; 437 return revoke_by_hash(&krl->revoked_sha1s, p, len); 438} 439 440int 441ssh_krl_revoke_key_sha256(struct ssh_krl *krl, const u_char *p, size_t len) 442{ 443 debug3_f("revoke by sha256"); 444 if (len != 32) 445 return SSH_ERR_INVALID_FORMAT; 446 return revoke_by_hash(&krl->revoked_sha256s, p, len); 447} 448 449int 450ssh_krl_revoke_key(struct ssh_krl *krl, const struct sshkey *key) 451{ 452 /* XXX replace with SHA256? */ 453 if (!sshkey_is_cert(key)) 454 return ssh_krl_revoke_key_explicit(krl, key); 455 456 if (key->cert->serial == 0) { 457 return ssh_krl_revoke_cert_by_key_id(krl, 458 key->cert->signature_key, 459 key->cert->key_id); 460 } else { 461 return ssh_krl_revoke_cert_by_serial(krl, 462 key->cert->signature_key, 463 key->cert->serial); 464 } 465} 466 467/* 468 * Select the most compact section type to emit next in a KRL based on 469 * the current section type, the run length of contiguous revoked serial 470 * numbers and the gaps from the last and to the next revoked serial. 471 * Applies a mostly-accurate bit cost model to select the section type 472 * that will minimise the size of the resultant KRL. 473 */ 474static int 475choose_next_state(int current_state, u_int64_t contig, int final, 476 u_int64_t last_gap, u_int64_t next_gap, int *force_new_section) 477{ 478 int new_state; 479 u_int64_t cost, cost_list, cost_range, cost_bitmap, cost_bitmap_restart; 480 481 /* 482 * Avoid unsigned overflows. 483 * The limits are high enough to avoid confusing the calculations. 484 */ 485 contig = MINIMUM(contig, 1ULL<<31); 486 last_gap = MINIMUM(last_gap, 1ULL<<31); 487 next_gap = MINIMUM(next_gap, 1ULL<<31); 488 489 /* 490 * Calculate the cost to switch from the current state to candidates. 491 * NB. range sections only ever contain a single range, so their 492 * switching cost is independent of the current_state. 493 */ 494 cost_list = cost_bitmap = cost_bitmap_restart = 0; 495 cost_range = 8; 496 switch (current_state) { 497 case KRL_SECTION_CERT_SERIAL_LIST: 498 cost_bitmap_restart = cost_bitmap = 8 + 64; 499 break; 500 case KRL_SECTION_CERT_SERIAL_BITMAP: 501 cost_list = 8; 502 cost_bitmap_restart = 8 + 64; 503 break; 504 case KRL_SECTION_CERT_SERIAL_RANGE: 505 case 0: 506 cost_bitmap_restart = cost_bitmap = 8 + 64; 507 cost_list = 8; 508 } 509 510 /* Estimate base cost in bits of each section type */ 511 cost_list += 64 * contig + (final ? 0 : 8+64); 512 cost_range += (2 * 64) + (final ? 0 : 8+64); 513 cost_bitmap += last_gap + contig + (final ? 0 : MINIMUM(next_gap, 8+64)); 514 cost_bitmap_restart += contig + (final ? 0 : MINIMUM(next_gap, 8+64)); 515 516 /* Convert to byte costs for actual comparison */ 517 cost_list = (cost_list + 7) / 8; 518 cost_bitmap = (cost_bitmap + 7) / 8; 519 cost_bitmap_restart = (cost_bitmap_restart + 7) / 8; 520 cost_range = (cost_range + 7) / 8; 521 522 /* Now pick the best choice */ 523 *force_new_section = 0; 524 new_state = KRL_SECTION_CERT_SERIAL_BITMAP; 525 cost = cost_bitmap; 526 if (cost_range < cost) { 527 new_state = KRL_SECTION_CERT_SERIAL_RANGE; 528 cost = cost_range; 529 } 530 if (cost_list < cost) { 531 new_state = KRL_SECTION_CERT_SERIAL_LIST; 532 cost = cost_list; 533 } 534 if (cost_bitmap_restart < cost) { 535 new_state = KRL_SECTION_CERT_SERIAL_BITMAP; 536 *force_new_section = 1; 537 cost = cost_bitmap_restart; 538 } 539 KRL_DBG(("contig %llu last_gap %llu next_gap %llu final %d, costs:" 540 "list %llu range %llu bitmap %llu new bitmap %llu, " 541 "selected 0x%02x%s", (long long unsigned)contig, 542 (long long unsigned)last_gap, (long long unsigned)next_gap, final, 543 (long long unsigned)cost_list, (long long unsigned)cost_range, 544 (long long unsigned)cost_bitmap, 545 (long long unsigned)cost_bitmap_restart, new_state, 546 *force_new_section ? " restart" : "")); 547 return new_state; 548} 549 550static int 551put_bitmap(struct sshbuf *buf, struct bitmap *bitmap) 552{ 553 size_t len; 554 u_char *blob; 555 int r; 556 557 len = bitmap_nbytes(bitmap); 558 if ((blob = malloc(len)) == NULL) 559 return SSH_ERR_ALLOC_FAIL; 560 if (bitmap_to_string(bitmap, blob, len) != 0) { 561 free(blob); 562 return SSH_ERR_INTERNAL_ERROR; 563 } 564 r = sshbuf_put_bignum2_bytes(buf, blob, len); 565 free(blob); 566 return r; 567} 568 569/* Generate a KRL_SECTION_CERTIFICATES KRL section */ 570static int 571revoked_certs_generate(struct revoked_certs *rc, struct sshbuf *buf) 572{ 573 int final, force_new_sect, r = SSH_ERR_INTERNAL_ERROR; 574 u_int64_t i, contig, gap, last = 0, bitmap_start = 0; 575 struct revoked_serial *rs, *nrs; 576 struct revoked_key_id *rki; 577 int next_state, state = 0; 578 struct sshbuf *sect; 579 struct bitmap *bitmap = NULL; 580 581 if ((sect = sshbuf_new()) == NULL) 582 return SSH_ERR_ALLOC_FAIL; 583 584 /* Store the header: optional CA scope key, reserved */ 585 if (rc->ca_key == NULL) { 586 if ((r = sshbuf_put_string(buf, NULL, 0)) != 0) 587 goto out; 588 } else { 589 if ((r = sshkey_puts(rc->ca_key, buf)) != 0) 590 goto out; 591 } 592 if ((r = sshbuf_put_string(buf, NULL, 0)) != 0) 593 goto out; 594 595 /* Store the revoked serials. */ 596 for (rs = RB_MIN(revoked_serial_tree, &rc->revoked_serials); 597 rs != NULL; 598 rs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs)) { 599 KRL_DBG(("serial %llu:%llu state 0x%02x", 600 (long long unsigned)rs->lo, (long long unsigned)rs->hi, 601 state)); 602 603 /* Check contiguous length and gap to next section (if any) */ 604 nrs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs); 605 final = nrs == NULL; 606 gap = nrs == NULL ? 0 : nrs->lo - rs->hi; 607 contig = 1 + (rs->hi - rs->lo); 608 609 /* Choose next state based on these */ 610 next_state = choose_next_state(state, contig, final, 611 state == 0 ? 0 : rs->lo - last, gap, &force_new_sect); 612 613 /* 614 * If the current section is a range section or has a different 615 * type to the next section, then finish it off now. 616 */ 617 if (state != 0 && (force_new_sect || next_state != state || 618 state == KRL_SECTION_CERT_SERIAL_RANGE)) { 619 KRL_DBG(("finish state 0x%02x", state)); 620 switch (state) { 621 case KRL_SECTION_CERT_SERIAL_LIST: 622 case KRL_SECTION_CERT_SERIAL_RANGE: 623 break; 624 case KRL_SECTION_CERT_SERIAL_BITMAP: 625 if ((r = put_bitmap(sect, bitmap)) != 0) 626 goto out; 627 bitmap_free(bitmap); 628 bitmap = NULL; 629 break; 630 } 631 if ((r = sshbuf_put_u8(buf, state)) != 0 || 632 (r = sshbuf_put_stringb(buf, sect)) != 0) 633 goto out; 634 sshbuf_reset(sect); 635 } 636 637 /* If we are starting a new section then prepare it now */ 638 if (next_state != state || force_new_sect) { 639 KRL_DBG(("start state 0x%02x", 640 next_state)); 641 state = next_state; 642 sshbuf_reset(sect); 643 switch (state) { 644 case KRL_SECTION_CERT_SERIAL_LIST: 645 case KRL_SECTION_CERT_SERIAL_RANGE: 646 break; 647 case KRL_SECTION_CERT_SERIAL_BITMAP: 648 if ((bitmap = bitmap_new()) == NULL) { 649 r = SSH_ERR_ALLOC_FAIL; 650 goto out; 651 } 652 bitmap_start = rs->lo; 653 if ((r = sshbuf_put_u64(sect, 654 bitmap_start)) != 0) 655 goto out; 656 break; 657 } 658 } 659 660 /* Perform section-specific processing */ 661 switch (state) { 662 case KRL_SECTION_CERT_SERIAL_LIST: 663 for (i = 0; i < contig; i++) { 664 if ((r = sshbuf_put_u64(sect, rs->lo + i)) != 0) 665 goto out; 666 } 667 break; 668 case KRL_SECTION_CERT_SERIAL_RANGE: 669 if ((r = sshbuf_put_u64(sect, rs->lo)) != 0 || 670 (r = sshbuf_put_u64(sect, rs->hi)) != 0) 671 goto out; 672 break; 673 case KRL_SECTION_CERT_SERIAL_BITMAP: 674 if (rs->lo - bitmap_start > INT_MAX) { 675 r = SSH_ERR_INVALID_FORMAT; 676 error_f("insane bitmap gap"); 677 goto out; 678 } 679 for (i = 0; i < contig; i++) { 680 if (bitmap_set_bit(bitmap, 681 rs->lo + i - bitmap_start) != 0) { 682 r = SSH_ERR_ALLOC_FAIL; 683 goto out; 684 } 685 } 686 break; 687 } 688 last = rs->hi; 689 } 690 /* Flush the remaining section, if any */ 691 if (state != 0) { 692 KRL_DBG(("serial final flush for state 0x%02x", state)); 693 switch (state) { 694 case KRL_SECTION_CERT_SERIAL_LIST: 695 case KRL_SECTION_CERT_SERIAL_RANGE: 696 break; 697 case KRL_SECTION_CERT_SERIAL_BITMAP: 698 if ((r = put_bitmap(sect, bitmap)) != 0) 699 goto out; 700 bitmap_free(bitmap); 701 bitmap = NULL; 702 break; 703 } 704 if ((r = sshbuf_put_u8(buf, state)) != 0 || 705 (r = sshbuf_put_stringb(buf, sect)) != 0) 706 goto out; 707 } 708 KRL_DBG(("serial done ")); 709 710 /* Now output a section for any revocations by key ID */ 711 sshbuf_reset(sect); 712 RB_FOREACH(rki, revoked_key_id_tree, &rc->revoked_key_ids) { 713 KRL_DBG(("key ID %s", rki->key_id)); 714 if ((r = sshbuf_put_cstring(sect, rki->key_id)) != 0) 715 goto out; 716 } 717 if (sshbuf_len(sect) != 0) { 718 if ((r = sshbuf_put_u8(buf, KRL_SECTION_CERT_KEY_ID)) != 0 || 719 (r = sshbuf_put_stringb(buf, sect)) != 0) 720 goto out; 721 } 722 r = 0; 723 out: 724 bitmap_free(bitmap); 725 sshbuf_free(sect); 726 return r; 727} 728 729int 730ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf) 731{ 732 int r = SSH_ERR_INTERNAL_ERROR; 733 struct revoked_certs *rc; 734 struct revoked_blob *rb; 735 struct sshbuf *sect; 736 u_char *sblob = NULL; 737 738 if (krl->generated_date == 0) 739 krl->generated_date = time(NULL); 740 741 if ((sect = sshbuf_new()) == NULL) 742 return SSH_ERR_ALLOC_FAIL; 743 744 /* Store the header */ 745 if ((r = sshbuf_put(buf, KRL_MAGIC, sizeof(KRL_MAGIC) - 1)) != 0 || 746 (r = sshbuf_put_u32(buf, KRL_FORMAT_VERSION)) != 0 || 747 (r = sshbuf_put_u64(buf, krl->krl_version)) != 0 || 748 (r = sshbuf_put_u64(buf, krl->generated_date)) != 0 || 749 (r = sshbuf_put_u64(buf, krl->flags)) != 0 || 750 (r = sshbuf_put_string(buf, NULL, 0)) != 0 || 751 (r = sshbuf_put_cstring(buf, krl->comment)) != 0) 752 goto out; 753 754 /* Store sections for revoked certificates */ 755 TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { 756 sshbuf_reset(sect); 757 if ((r = revoked_certs_generate(rc, sect)) != 0) 758 goto out; 759 if ((r = sshbuf_put_u8(buf, KRL_SECTION_CERTIFICATES)) != 0 || 760 (r = sshbuf_put_stringb(buf, sect)) != 0) 761 goto out; 762 } 763 764 /* Finally, output sections for revocations by public key/hash */ 765 sshbuf_reset(sect); 766 RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) { 767 KRL_DBG(("key len %zu ", rb->len)); 768 if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0) 769 goto out; 770 } 771 if (sshbuf_len(sect) != 0) { 772 if ((r = sshbuf_put_u8(buf, KRL_SECTION_EXPLICIT_KEY)) != 0 || 773 (r = sshbuf_put_stringb(buf, sect)) != 0) 774 goto out; 775 } 776 sshbuf_reset(sect); 777 RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) { 778 KRL_DBG(("hash len %zu ", rb->len)); 779 if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0) 780 goto out; 781 } 782 if (sshbuf_len(sect) != 0) { 783 if ((r = sshbuf_put_u8(buf, 784 KRL_SECTION_FINGERPRINT_SHA1)) != 0 || 785 (r = sshbuf_put_stringb(buf, sect)) != 0) 786 goto out; 787 } 788 sshbuf_reset(sect); 789 RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha256s) { 790 KRL_DBG(("hash len %zu ", rb->len)); 791 if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0) 792 goto out; 793 } 794 if (sshbuf_len(sect) != 0) { 795 if ((r = sshbuf_put_u8(buf, 796 KRL_SECTION_FINGERPRINT_SHA256)) != 0 || 797 (r = sshbuf_put_stringb(buf, sect)) != 0) 798 goto out; 799 } 800 /* success */ 801 r = 0; 802 out: 803 free(sblob); 804 sshbuf_free(sect); 805 return r; 806} 807 808static void 809format_timestamp(u_int64_t timestamp, char *ts, size_t nts) 810{ 811 time_t t; 812 struct tm *tm; 813 814 t = timestamp; 815 tm = localtime(&t); 816 if (tm == NULL) 817 strlcpy(ts, "<INVALID>", nts); 818 else { 819 *ts = '\0'; 820 strftime(ts, nts, "%Y%m%dT%H%M%S", tm); 821 } 822} 823 824static int 825cert_extension_subsection(struct sshbuf *subsect, struct ssh_krl *krl) 826{ 827 int r = SSH_ERR_INTERNAL_ERROR; 828 u_char critical = 1; 829 struct sshbuf *value = NULL; 830 char *name = NULL; 831 832 if ((r = sshbuf_get_cstring(subsect, &name, NULL)) != 0 || 833 (r = sshbuf_get_u8(subsect, &critical)) != 0 || 834 (r = sshbuf_froms(subsect, &value)) != 0) { 835 debug_fr(r, "parse"); 836 error("KRL has invalid certificate extension subsection"); 837 r = SSH_ERR_INVALID_FORMAT; 838 goto out; 839 } 840 if (sshbuf_len(subsect) != 0) { 841 error("KRL has invalid certificate extension subsection: " 842 "trailing data"); 843 r = SSH_ERR_INVALID_FORMAT; 844 goto out; 845 } 846 debug_f("cert extension %s critical %u len %zu", 847 name, critical, sshbuf_len(value)); 848 /* no extensions are currently supported */ 849 if (critical) { 850 error("KRL contains unsupported critical certificate " 851 "subsection \"%s\"", name); 852 r = SSH_ERR_FEATURE_UNSUPPORTED; 853 goto out; 854 } 855 /* success */ 856 r = 0; 857 out: 858 free(name); 859 sshbuf_free(value); 860 return r; 861} 862 863static int 864parse_revoked_certs(struct sshbuf *buf, struct ssh_krl *krl) 865{ 866 int r = SSH_ERR_INTERNAL_ERROR; 867 u_char type; 868 const u_char *blob; 869 size_t blen, nbits; 870 struct sshbuf *subsect = NULL; 871 u_int64_t serial, serial_lo, serial_hi; 872 struct bitmap *bitmap = NULL; 873 char *key_id = NULL; 874 struct sshkey *ca_key = NULL; 875 876 if ((subsect = sshbuf_new()) == NULL) 877 return SSH_ERR_ALLOC_FAIL; 878 879 /* Header: key, reserved */ 880 if ((r = sshbuf_get_string_direct(buf, &blob, &blen)) != 0 || 881 (r = sshbuf_skip_string(buf)) != 0) 882 goto out; 883 if (blen != 0 && (r = sshkey_from_blob(blob, blen, &ca_key)) != 0) 884 goto out; 885 886 while (sshbuf_len(buf) > 0) { 887 sshbuf_free(subsect); 888 subsect = NULL; 889 if ((r = sshbuf_get_u8(buf, &type)) != 0 || 890 (r = sshbuf_froms(buf, &subsect)) != 0) 891 goto out; 892 KRL_DBG(("subsection type 0x%02x", type)); 893 /* sshbuf_dump(subsect, stderr); */ 894 895 switch (type) { 896 case KRL_SECTION_CERT_SERIAL_LIST: 897 while (sshbuf_len(subsect) > 0) { 898 if ((r = sshbuf_get_u64(subsect, &serial)) != 0) 899 goto out; 900 if ((r = ssh_krl_revoke_cert_by_serial(krl, 901 ca_key, serial)) != 0) 902 goto out; 903 } 904 break; 905 case KRL_SECTION_CERT_SERIAL_RANGE: 906 if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 || 907 (r = sshbuf_get_u64(subsect, &serial_hi)) != 0) 908 goto out; 909 if ((r = ssh_krl_revoke_cert_by_serial_range(krl, 910 ca_key, serial_lo, serial_hi)) != 0) 911 goto out; 912 break; 913 case KRL_SECTION_CERT_SERIAL_BITMAP: 914 if ((bitmap = bitmap_new()) == NULL) { 915 r = SSH_ERR_ALLOC_FAIL; 916 goto out; 917 } 918 if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 || 919 (r = sshbuf_get_bignum2_bytes_direct(subsect, 920 &blob, &blen)) != 0) 921 goto out; 922 if (bitmap_from_string(bitmap, blob, blen) != 0) { 923 r = SSH_ERR_INVALID_FORMAT; 924 goto out; 925 } 926 nbits = bitmap_nbits(bitmap); 927 for (serial = 0; serial < (u_int64_t)nbits; serial++) { 928 if (serial > 0 && serial_lo + serial == 0) { 929 error_f("bitmap wraps u64"); 930 r = SSH_ERR_INVALID_FORMAT; 931 goto out; 932 } 933 if (!bitmap_test_bit(bitmap, serial)) 934 continue; 935 if ((r = ssh_krl_revoke_cert_by_serial(krl, 936 ca_key, serial_lo + serial)) != 0) 937 goto out; 938 } 939 bitmap_free(bitmap); 940 bitmap = NULL; 941 break; 942 case KRL_SECTION_CERT_KEY_ID: 943 while (sshbuf_len(subsect) > 0) { 944 if ((r = sshbuf_get_cstring(subsect, 945 &key_id, NULL)) != 0) 946 goto out; 947 if ((r = ssh_krl_revoke_cert_by_key_id(krl, 948 ca_key, key_id)) != 0) 949 goto out; 950 free(key_id); 951 key_id = NULL; 952 } 953 break; 954 case KRL_SECTION_CERT_EXTENSION: 955 if ((r = cert_extension_subsection(subsect, krl)) != 0) 956 goto out; 957 break; 958 default: 959 error("Unsupported KRL certificate section %u", type); 960 r = SSH_ERR_INVALID_FORMAT; 961 goto out; 962 } 963 if (sshbuf_len(subsect) > 0) { 964 error("KRL certificate section contains unparsed data"); 965 r = SSH_ERR_INVALID_FORMAT; 966 goto out; 967 } 968 } 969 970 r = 0; 971 out: 972 if (bitmap != NULL) 973 bitmap_free(bitmap); 974 free(key_id); 975 sshkey_free(ca_key); 976 sshbuf_free(subsect); 977 return r; 978} 979 980static int 981blob_section(struct sshbuf *sect, struct revoked_blob_tree *target_tree, 982 size_t expected_len) 983{ 984 u_char *rdata = NULL; 985 size_t rlen = 0; 986 int r; 987 988 while (sshbuf_len(sect) > 0) { 989 if ((r = sshbuf_get_string(sect, &rdata, &rlen)) != 0) 990 return r; 991 if (expected_len != 0 && rlen != expected_len) { 992 error_f("bad length"); 993 free(rdata); 994 return SSH_ERR_INVALID_FORMAT; 995 } 996 if ((r = revoke_blob(target_tree, rdata, rlen)) != 0) { 997 free(rdata); 998 return r; 999 } 1000 } 1001 return 0; 1002} 1003 1004static int 1005extension_section(struct sshbuf *sect, struct ssh_krl *krl) 1006{ 1007 int r = SSH_ERR_INTERNAL_ERROR; 1008 u_char critical = 1; 1009 struct sshbuf *value = NULL; 1010 char *name = NULL; 1011 1012 if ((r = sshbuf_get_cstring(sect, &name, NULL)) != 0 || 1013 (r = sshbuf_get_u8(sect, &critical)) != 0 || 1014 (r = sshbuf_froms(sect, &value)) != 0) { 1015 debug_fr(r, "parse"); 1016 error("KRL has invalid extension section"); 1017 r = SSH_ERR_INVALID_FORMAT; 1018 goto out; 1019 } 1020 if (sshbuf_len(sect) != 0) { 1021 error("KRL has invalid extension section: trailing data"); 1022 r = SSH_ERR_INVALID_FORMAT; 1023 goto out; 1024 } 1025 debug_f("extension %s critical %u len %zu", 1026 name, critical, sshbuf_len(value)); 1027 /* no extensions are currently supported */ 1028 if (critical) { 1029 error("KRL contains unsupported critical section \"%s\"", name); 1030 r = SSH_ERR_FEATURE_UNSUPPORTED; 1031 goto out; 1032 } 1033 /* success */ 1034 r = 0; 1035 out: 1036 free(name); 1037 sshbuf_free(value); 1038 return r; 1039} 1040 1041/* Attempt to parse a KRL */ 1042int 1043ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp) 1044{ 1045 struct sshbuf *copy = NULL, *sect = NULL; 1046 struct ssh_krl *krl = NULL; 1047 char timestamp[64]; 1048 int r = SSH_ERR_INTERNAL_ERROR; 1049 u_char type; 1050 u_int format_version; 1051 1052 *krlp = NULL; 1053 1054 /* KRL must begin with magic string */ 1055 if ((r = sshbuf_cmp(buf, 0, KRL_MAGIC, sizeof(KRL_MAGIC) - 1)) != 0) { 1056 debug2_f("bad KRL magic header"); 1057 return SSH_ERR_KRL_BAD_MAGIC; 1058 } 1059 1060 if ((krl = ssh_krl_init()) == NULL) { 1061 r = SSH_ERR_ALLOC_FAIL; 1062 error_f("alloc failed"); 1063 goto out; 1064 } 1065 /* Don't modify buffer */ 1066 if ((copy = sshbuf_fromb(buf)) == NULL) { 1067 r = SSH_ERR_ALLOC_FAIL; 1068 goto out; 1069 } 1070 if ((r = sshbuf_consume(copy, sizeof(KRL_MAGIC) - 1)) != 0 || 1071 (r = sshbuf_get_u32(copy, &format_version)) != 0) 1072 goto out; 1073 if (format_version != KRL_FORMAT_VERSION) { 1074 error_f("unsupported KRL format version %u", format_version); 1075 r = SSH_ERR_INVALID_FORMAT; 1076 goto out; 1077 } 1078 if ((r = sshbuf_get_u64(copy, &krl->krl_version)) != 0 || 1079 (r = sshbuf_get_u64(copy, &krl->generated_date)) != 0 || 1080 (r = sshbuf_get_u64(copy, &krl->flags)) != 0 || 1081 (r = sshbuf_skip_string(copy)) != 0 || 1082 (r = sshbuf_get_cstring(copy, &krl->comment, NULL)) != 0) { 1083 error_fr(r, "parse KRL header"); 1084 goto out; 1085 } 1086 format_timestamp(krl->generated_date, timestamp, sizeof(timestamp)); 1087 debug("KRL version %llu generated at %s%s%s", 1088 (long long unsigned)krl->krl_version, timestamp, 1089 *krl->comment ? ": " : "", krl->comment); 1090 1091 /* Parse and load the KRL sections. */ 1092 while (sshbuf_len(copy) > 0) { 1093 sshbuf_free(sect); 1094 sect = NULL; 1095 if ((r = sshbuf_get_u8(copy, &type)) != 0 || 1096 (r = sshbuf_froms(copy, &sect)) != 0) 1097 goto out; 1098 KRL_DBG(("section 0x%02x", type)); 1099 1100 switch (type) { 1101 case KRL_SECTION_CERTIFICATES: 1102 if ((r = parse_revoked_certs(sect, krl)) != 0) 1103 goto out; 1104 break; 1105 case KRL_SECTION_EXPLICIT_KEY: 1106 if ((r = blob_section(sect, 1107 &krl->revoked_keys, 0)) != 0) 1108 goto out; 1109 break; 1110 case KRL_SECTION_FINGERPRINT_SHA1: 1111 if ((r = blob_section(sect, 1112 &krl->revoked_sha1s, 20)) != 0) 1113 goto out; 1114 break; 1115 case KRL_SECTION_FINGERPRINT_SHA256: 1116 if ((r = blob_section(sect, 1117 &krl->revoked_sha256s, 32)) != 0) 1118 goto out; 1119 break; 1120 case KRL_SECTION_EXTENSION: 1121 if ((r = extension_section(sect, krl)) != 0) 1122 goto out; 1123 break; 1124 case KRL_SECTION_SIGNATURE: 1125 /* Handled above, but still need to stay in synch */ 1126 sshbuf_free(sect); 1127 sect = NULL; 1128 if ((r = sshbuf_skip_string(copy)) != 0) 1129 goto out; 1130 break; 1131 default: 1132 error("Unsupported KRL section %u", type); 1133 r = SSH_ERR_INVALID_FORMAT; 1134 goto out; 1135 } 1136 if (sect != NULL && sshbuf_len(sect) > 0) { 1137 error("KRL section contains unparsed data"); 1138 r = SSH_ERR_INVALID_FORMAT; 1139 goto out; 1140 } 1141 } 1142 1143 /* Success */ 1144 *krlp = krl; 1145 r = 0; 1146 out: 1147 if (r != 0) 1148 ssh_krl_free(krl); 1149 sshbuf_free(copy); 1150 sshbuf_free(sect); 1151 return r; 1152} 1153 1154/* Checks certificate serial number and key ID revocation */ 1155static int 1156is_cert_revoked(const struct sshkey *key, struct revoked_certs *rc) 1157{ 1158 struct revoked_serial rs, *ers; 1159 struct revoked_key_id rki, *erki; 1160 1161 /* Check revocation by cert key ID */ 1162 memset(&rki, 0, sizeof(rki)); 1163 rki.key_id = key->cert->key_id; 1164 erki = RB_FIND(revoked_key_id_tree, &rc->revoked_key_ids, &rki); 1165 if (erki != NULL) { 1166 KRL_DBG(("revoked by key ID")); 1167 return SSH_ERR_KEY_REVOKED; 1168 } 1169 1170 /* 1171 * Zero serials numbers are ignored (it's the default when the 1172 * CA doesn't specify one). 1173 */ 1174 if (key->cert->serial == 0) 1175 return 0; 1176 1177 memset(&rs, 0, sizeof(rs)); 1178 rs.lo = rs.hi = key->cert->serial; 1179 ers = RB_FIND(revoked_serial_tree, &rc->revoked_serials, &rs); 1180 if (ers != NULL) { 1181 KRL_DBG(("revoked serial %llu matched %llu:%llu", 1182 key->cert->serial, ers->lo, ers->hi)); 1183 return SSH_ERR_KEY_REVOKED; 1184 } 1185 return 0; 1186} 1187 1188/* Checks whether a given key/cert is revoked. Does not check its CA */ 1189static int 1190is_key_revoked(struct ssh_krl *krl, const struct sshkey *key) 1191{ 1192 struct revoked_blob rb, *erb; 1193 struct revoked_certs *rc; 1194 int r; 1195 1196 /* Check explicitly revoked hashes first */ 1197 memset(&rb, 0, sizeof(rb)); 1198 if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA1, 1199 &rb.blob, &rb.len)) != 0) 1200 return r; 1201 erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha1s, &rb); 1202 free(rb.blob); 1203 if (erb != NULL) { 1204 KRL_DBG(("revoked by key SHA1")); 1205 return SSH_ERR_KEY_REVOKED; 1206 } 1207 memset(&rb, 0, sizeof(rb)); 1208 if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA256, 1209 &rb.blob, &rb.len)) != 0) 1210 return r; 1211 erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha256s, &rb); 1212 free(rb.blob); 1213 if (erb != NULL) { 1214 KRL_DBG(("revoked by key SHA256")); 1215 return SSH_ERR_KEY_REVOKED; 1216 } 1217 1218 /* Next, explicit keys */ 1219 memset(&rb, 0, sizeof(rb)); 1220 if ((r = plain_key_blob(key, &rb.blob, &rb.len)) != 0) 1221 return r; 1222 erb = RB_FIND(revoked_blob_tree, &krl->revoked_keys, &rb); 1223 free(rb.blob); 1224 if (erb != NULL) { 1225 KRL_DBG(("revoked by explicit key")); 1226 return SSH_ERR_KEY_REVOKED; 1227 } 1228 1229 if (!sshkey_is_cert(key)) 1230 return 0; 1231 1232 /* Check cert revocation for the specified CA */ 1233 if ((r = revoked_certs_for_ca_key(krl, key->cert->signature_key, 1234 &rc, 0)) != 0) 1235 return r; 1236 if (rc != NULL) { 1237 if ((r = is_cert_revoked(key, rc)) != 0) 1238 return r; 1239 } 1240 /* Check cert revocation for the wildcard CA */ 1241 if ((r = revoked_certs_for_ca_key(krl, NULL, &rc, 0)) != 0) 1242 return r; 1243 if (rc != NULL) { 1244 if ((r = is_cert_revoked(key, rc)) != 0) 1245 return r; 1246 } 1247 1248 KRL_DBG(("%llu no match", key->cert->serial)); 1249 return 0; 1250} 1251 1252int 1253ssh_krl_check_key(struct ssh_krl *krl, const struct sshkey *key) 1254{ 1255 int r; 1256 1257 KRL_DBG(("checking key")); 1258 if ((r = is_key_revoked(krl, key)) != 0) 1259 return r; 1260 if (sshkey_is_cert(key)) { 1261 debug2_f("checking CA key"); 1262 if ((r = is_key_revoked(krl, key->cert->signature_key)) != 0) 1263 return r; 1264 } 1265 KRL_DBG(("key okay")); 1266 return 0; 1267} 1268 1269int 1270ssh_krl_file_contains_key(const char *path, const struct sshkey *key) 1271{ 1272 struct sshbuf *krlbuf = NULL; 1273 struct ssh_krl *krl = NULL; 1274 int oerrno = 0, r; 1275 1276 if (path == NULL) 1277 return 0; 1278 if ((r = sshbuf_load_file(path, &krlbuf)) != 0) { 1279 oerrno = errno; 1280 goto out; 1281 } 1282 if ((r = ssh_krl_from_blob(krlbuf, &krl)) != 0) 1283 goto out; 1284 debug2_f("checking KRL %s", path); 1285 r = ssh_krl_check_key(krl, key); 1286 out: 1287 sshbuf_free(krlbuf); 1288 ssh_krl_free(krl); 1289 if (r != 0) 1290 errno = oerrno; 1291 return r; 1292} 1293 1294int 1295krl_dump(struct ssh_krl *krl, FILE *f) 1296{ 1297 struct sshkey *key = NULL; 1298 struct revoked_blob *rb; 1299 struct revoked_certs *rc; 1300 struct revoked_serial *rs; 1301 struct revoked_key_id *rki; 1302 int r, ret = 0; 1303 char *fp, timestamp[64]; 1304 1305 /* Try to print in a KRL spec-compatible format */ 1306 format_timestamp(krl->generated_date, timestamp, sizeof(timestamp)); 1307 fprintf(f, "# KRL version %llu\n", 1308 (unsigned long long)krl->krl_version); 1309 fprintf(f, "# Generated at %s\n", timestamp); 1310 if (krl->comment != NULL && *krl->comment != '\0') { 1311 r = INT_MAX; 1312 asmprintf(&fp, INT_MAX, &r, "%s", krl->comment); 1313 fprintf(f, "# Comment: %s\n", fp); 1314 free(fp); 1315 } 1316 fputc('\n', f); 1317 1318 RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) { 1319 if ((r = sshkey_from_blob(rb->blob, rb->len, &key)) != 0) { 1320 ret = SSH_ERR_INVALID_FORMAT; 1321 error_r(r, "parse KRL key"); 1322 continue; 1323 } 1324 if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, 1325 SSH_FP_DEFAULT)) == NULL) { 1326 ret = SSH_ERR_INVALID_FORMAT; 1327 error("sshkey_fingerprint failed"); 1328 continue; 1329 } 1330 fprintf(f, "hash: %s # %s\n", fp, sshkey_ssh_name(key)); 1331 free(fp); 1332 free(key); 1333 } 1334 RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha256s) { 1335 fp = tohex(rb->blob, rb->len); 1336 fprintf(f, "hash: SHA256:%s\n", fp); 1337 free(fp); 1338 } 1339 RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) { 1340 /* 1341 * There is not KRL spec keyword for raw SHA1 hashes, so 1342 * print them as comments. 1343 */ 1344 fp = tohex(rb->blob, rb->len); 1345 fprintf(f, "# hash SHA1:%s\n", fp); 1346 free(fp); 1347 } 1348 1349 TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { 1350 fputc('\n', f); 1351 if (rc->ca_key == NULL) 1352 fprintf(f, "# Wildcard CA\n"); 1353 else { 1354 if ((fp = sshkey_fingerprint(rc->ca_key, 1355 SSH_FP_HASH_DEFAULT, SSH_FP_DEFAULT)) == NULL) { 1356 ret = SSH_ERR_INVALID_FORMAT; 1357 error("sshkey_fingerprint failed"); 1358 continue; 1359 } 1360 fprintf(f, "# CA key %s %s\n", 1361 sshkey_ssh_name(rc->ca_key), fp); 1362 free(fp); 1363 } 1364 RB_FOREACH(rs, revoked_serial_tree, &rc->revoked_serials) { 1365 if (rs->lo == rs->hi) { 1366 fprintf(f, "serial: %llu\n", 1367 (unsigned long long)rs->lo); 1368 } else { 1369 fprintf(f, "serial: %llu-%llu\n", 1370 (unsigned long long)rs->lo, 1371 (unsigned long long)rs->hi); 1372 } 1373 } 1374 RB_FOREACH(rki, revoked_key_id_tree, &rc->revoked_key_ids) { 1375 /* 1376 * We don't want key IDs with embedded newlines to 1377 * mess up the display. 1378 */ 1379 r = INT_MAX; 1380 asmprintf(&fp, INT_MAX, &r, "%s", rki->key_id); 1381 fprintf(f, "id: %s\n", fp); 1382 free(fp); 1383 } 1384 } 1385 return ret; 1386}