Coverage Report

Created: 2025-07-01 06:04

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