Coverage Report

Created: 2026-10-23 07:09

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