Coverage Report

Created: 2025-11-03 07:12

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