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