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