Coverage Report

Created: 2024-06-18 06:23

/src/hpn-ssh/regress/misc/fuzz-harness/kex_fuzz.cc
Line
Count
Source (jump to first uncovered line)
1
// libfuzzer driver for key exchange fuzzing.
2
3
4
#include <sys/types.h>
5
#include <stdio.h>
6
#include <stdint.h>
7
#include <stdlib.h>
8
#include <string.h>
9
10
extern "C" {
11
12
#include "includes.h"
13
#include "ssherr.h"
14
#include "ssh_api.h"
15
#include "sshbuf.h"
16
#include "packet.h"
17
#include "myproposal.h"
18
#include "xmalloc.h"
19
#include "authfile.h"
20
#include "log.h"
21
22
#include "fixed-keys.h"
23
24
// Define if you want to generate traces.
25
/* #define STANDALONE 1 */
26
27
static int prepare_key(struct shared_state *st, int keytype, int bits);
28
29
struct shared_state {
30
  size_t nkeys;
31
  struct sshkey **privkeys, **pubkeys;
32
};
33
34
struct test_state {
35
  struct sshbuf *smsgs, *cmsgs; /* output, for standalone mode */
36
  struct sshbuf *sin, *cin; /* input; setup per-test in do_kex_with_key */
37
  struct sshbuf *s_template, *c_template; /* main copy of input */
38
};
39
40
static int
41
do_send_and_receive(struct ssh *from, struct ssh *to,
42
    struct sshbuf *store, int clobber, size_t *n)
43
52.5k
{
44
52.5k
  u_char type;
45
52.5k
  size_t len;
46
52.5k
  const u_char *buf;
47
52.5k
  int r;
48
49
90.1k
  for (*n = 0;; (*n)++) {
50
90.1k
    if ((r = ssh_packet_next(from, &type)) != 0) {
51
35.0k
      debug_fr(r, "ssh_packet_next");
52
35.0k
      return r;
53
35.0k
    }
54
55.1k
    if (type != 0)
55
0
      return 0;
56
55.1k
    buf = ssh_output_ptr(from, &len);
57
55.1k
    debug_f("%zu%s", len, clobber ? " ignore" : "");
58
55.1k
    if (len == 0)
59
17.5k
      return 0;
60
37.5k
    if ((r = ssh_output_consume(from, len)) != 0) {
61
0
      debug_fr(r, "ssh_output_consume");
62
0
      return r;
63
0
    }
64
37.5k
    if (store != NULL && (r = sshbuf_put(store, buf, len)) != 0) {
65
0
      debug_fr(r, "sshbuf_put");
66
0
      return r;
67
0
    }
68
37.5k
    if (!clobber && (r = ssh_input_append(to, buf, len)) != 0) {
69
0
      debug_fr(r, "ssh_input_append");
70
0
      return r;
71
0
    }
72
37.5k
  }
73
52.5k
}
74
75
static int
76
run_kex(struct test_state *ts, struct ssh *client, struct ssh *server)
77
35.0k
{
78
35.0k
  int r = 0;
79
35.0k
  size_t cn, sn;
80
81
  /* If fuzzing, replace server/client input */
82
35.0k
  if (ts->sin != NULL) {
83
17.5k
    if ((r = ssh_input_append(server, sshbuf_ptr(ts->sin),
84
17.5k
        sshbuf_len(ts->sin))) != 0) {
85
0
      error_fr(r, "ssh_input_append");
86
0
      return r;
87
0
    }
88
17.5k
    sshbuf_reset(ts->sin);
89
17.5k
  }
90
35.0k
  if (ts->cin != NULL) {
91
17.5k
    if ((r = ssh_input_append(client, sshbuf_ptr(ts->cin),
92
17.5k
        sshbuf_len(ts->cin))) != 0) {
93
0
      error_fr(r, "ssh_input_append");
94
0
      return r;
95
0
    }
96
17.5k
    sshbuf_reset(ts->cin);
97
17.5k
  }
98
35.0k
  while (!server->kex->done || !client->kex->done) {
99
35.0k
    cn = sn = 0;
100
35.0k
    debug_f("S:");
101
35.0k
    if ((r = do_send_and_receive(server, client,
102
35.0k
        ts->smsgs, ts->cin != NULL, &sn)) != 0) {
103
17.5k
      debug_fr(r, "S->C");
104
17.5k
      break;
105
17.5k
    }
106
17.5k
    debug_f("C:");
107
17.5k
    if ((r = do_send_and_receive(client, server,
108
17.5k
        ts->cmsgs, ts->sin != NULL, &cn)) != 0) {
109
17.5k
      debug_fr(r, "C->S");
110
17.5k
      break;
111
17.5k
    }
112
0
    if (cn == 0 && sn == 0) {
113
0
      debug_f("kex stalled");
114
0
      r = SSH_ERR_PROTOCOL_ERROR;
115
0
      break;
116
0
    }
117
0
  }
118
35.0k
  debug_fr(r, "done");
119
35.0k
  return r;
120
35.0k
}
121
122
static void
123
store_key(struct shared_state *st, struct sshkey *pubkey,
124
    struct sshkey *privkey)
125
4
{
126
4
  if (st == NULL || pubkey->type < 0 || pubkey->type > INT_MAX ||
127
4
      privkey->type != pubkey->type ||
128
4
      ((size_t)pubkey->type < st->nkeys &&
129
4
       st->pubkeys[pubkey->type] != NULL))
130
0
    abort();
131
4
  if ((size_t)pubkey->type >= st->nkeys) {
132
4
    st->pubkeys = (struct sshkey **)xrecallocarray(st->pubkeys,
133
4
        st->nkeys, pubkey->type + 1, sizeof(*st->pubkeys));
134
4
    st->privkeys = (struct sshkey **)xrecallocarray(st->privkeys,
135
4
        st->nkeys, privkey->type + 1, sizeof(*st->privkeys));
136
4
    st->nkeys = privkey->type + 1;
137
4
  }
138
4
  debug_f("store %s at %d", sshkey_ssh_name(pubkey), pubkey->type);
139
4
  st->pubkeys[pubkey->type] = pubkey;
140
4
  st->privkeys[privkey->type] = privkey;
141
4
}
142
143
static int
144
prepare_keys(struct shared_state *st)
145
1
{
146
1
  if (prepare_key(st, KEY_RSA, 2048) != 0 ||
147
1
      prepare_key(st, KEY_DSA, 1024) != 0 ||
148
1
      prepare_key(st, KEY_ECDSA, 256) != 0 ||
149
1
      prepare_key(st, KEY_ED25519, 256) != 0) {
150
0
    error_f("key prepare failed");
151
0
    return -1;
152
0
  }
153
1
  return 0;
154
1
}
155
156
static struct sshkey *
157
get_pubkey(struct shared_state *st, int keytype)
158
35.0k
{
159
35.0k
  if (st == NULL || keytype < 0 || (size_t)keytype >= st->nkeys ||
160
35.0k
      st->pubkeys == NULL || st->pubkeys[keytype] == NULL)
161
0
    abort();
162
35.0k
  return st->pubkeys[keytype];
163
35.0k
}
164
165
static struct sshkey *
166
get_privkey(struct shared_state *st, int keytype)
167
35.0k
{
168
35.0k
  if (st == NULL || keytype < 0 || (size_t)keytype >= st->nkeys ||
169
35.0k
      st->privkeys == NULL || st->privkeys[keytype] == NULL)
170
0
    abort();
171
35.0k
  return st->privkeys[keytype];
172
35.0k
}
173
174
static int
175
do_kex_with_key(struct shared_state *st, struct test_state *ts,
176
    const char *kex, int keytype)
177
35.0k
{
178
35.0k
  struct ssh *client = NULL, *server = NULL;
179
35.0k
  struct sshkey *privkey = NULL, *pubkey = NULL;
180
35.0k
  struct sshbuf *state = NULL;
181
35.0k
  struct kex_params kex_params;
182
35.0k
  const char *ccp, *proposal[PROPOSAL_MAX] = { KEX_CLIENT };
183
35.0k
  char *myproposal[PROPOSAL_MAX] = {0}, *keyname = NULL;
184
35.0k
  int i, r;
185
186
35.0k
  ts->cin = ts->sin = NULL;
187
35.0k
  if (ts->c_template != NULL &&
188
35.0k
      (ts->cin = sshbuf_fromb(ts->c_template)) == NULL)
189
0
    abort();
190
35.0k
  if (ts->s_template != NULL &&
191
35.0k
      (ts->sin = sshbuf_fromb(ts->s_template)) == NULL)
192
0
    abort();
193
194
35.0k
  pubkey = get_pubkey(st, keytype);
195
35.0k
  privkey = get_privkey(st, keytype);
196
35.0k
  keyname = xstrdup(sshkey_ssh_name(privkey));
197
35.0k
  if (ts->cin != NULL) {
198
17.5k
    debug_f("%s %s clobber client %zu", kex, keyname,
199
17.5k
        sshbuf_len(ts->cin));
200
17.5k
  } else if (ts->sin != NULL) {
201
17.5k
    debug_f("%s %s clobber server %zu", kex, keyname,
202
17.5k
        sshbuf_len(ts->sin));
203
17.5k
  } else
204
0
    debug_f("%s %s noclobber", kex, keyname);
205
206
385k
  for (i = 0; i < PROPOSAL_MAX; i++) {
207
350k
    ccp = proposal[i];
208
350k
#ifdef CIPHER_NONE_AVAIL
209
350k
    if (i == PROPOSAL_ENC_ALGS_CTOS || i == PROPOSAL_ENC_ALGS_STOC)
210
70.0k
      ccp = "none";
211
350k
#endif
212
350k
    if (i == PROPOSAL_SERVER_HOST_KEY_ALGS)
213
35.0k
      ccp = keyname;
214
315k
    else if (i == PROPOSAL_KEX_ALGS && kex != NULL)
215
35.0k
      ccp = kex;
216
350k
    if ((myproposal[i] = strdup(ccp)) == NULL) {
217
0
      error_f("strdup prop %d", i);
218
0
      goto fail;
219
0
    }
220
350k
  }
221
35.0k
  memcpy(kex_params.proposal, myproposal, sizeof(myproposal));
222
35.0k
  if ((r = ssh_init(&client, 0, &kex_params)) != 0) {
223
0
    error_fr(r, "init client");
224
0
    goto fail;
225
0
  }
226
35.0k
  if ((r = ssh_init(&server, 1, &kex_params)) != 0) {
227
0
    error_fr(r, "init server");
228
0
    goto fail;
229
0
  }
230
35.0k
  if ((r = ssh_add_hostkey(server, privkey)) != 0 ||
231
35.0k
      (r = ssh_add_hostkey(client, pubkey)) != 0) {
232
0
    error_fr(r, "add hostkeys");
233
0
    goto fail;
234
0
  }
235
35.0k
  if ((r = run_kex(ts, client, server)) != 0) {
236
35.0k
    error_fr(r, "kex");
237
35.0k
    goto fail;
238
35.0k
  }
239
  /* XXX rekex, set_state, etc */
240
35.0k
 fail:
241
385k
  for (i = 0; i < PROPOSAL_MAX; i++)
242
350k
    free(myproposal[i]);
243
35.0k
  sshbuf_free(ts->sin);
244
35.0k
  sshbuf_free(ts->cin);
245
35.0k
  sshbuf_free(state);
246
35.0k
  ssh_free(client);
247
35.0k
  ssh_free(server);
248
35.0k
  free(keyname);
249
35.0k
  return r;
250
35.0k
}
251
252
static int
253
prepare_key(struct shared_state *st, int kt, int bits)
254
4
{
255
4
  const char *pubstr = NULL;
256
4
  const char *privstr = NULL;
257
4
  char *tmp, *cp;
258
4
  struct sshkey *privkey = NULL, *pubkey = NULL;
259
4
  struct sshbuf *b = NULL;
260
4
  int r;
261
262
4
  switch (kt) {
263
1
  case KEY_RSA:
264
1
    pubstr = PUB_RSA;
265
1
    privstr = PRIV_RSA;
266
1
    break;
267
1
  case KEY_DSA:
268
1
    pubstr = PUB_DSA;
269
1
    privstr = PRIV_DSA;
270
1
    break;
271
1
  case KEY_ECDSA:
272
1
    pubstr = PUB_ECDSA;
273
1
    privstr = PRIV_ECDSA;
274
1
    break;
275
1
  case KEY_ED25519:
276
1
    pubstr = PUB_ED25519;
277
1
    privstr = PRIV_ED25519;
278
1
    break;
279
0
  default:
280
0
    abort();
281
4
  }
282
4
  if ((b = sshbuf_from(privstr, strlen(privstr))) == NULL)
283
0
    abort();
284
4
  if ((r = sshkey_parse_private_fileblob(b, "", &privkey, NULL)) != 0) {
285
0
    error_fr(r, "priv %d", kt);
286
0
    abort();
287
0
  }
288
4
  sshbuf_free(b);
289
4
  tmp = cp = xstrdup(pubstr);
290
4
  if ((pubkey = sshkey_new(KEY_UNSPEC)) == NULL)
291
0
    abort();
292
4
  if ((r = sshkey_read(pubkey, &cp)) != 0) {
293
0
    error_fr(r, "pub %d", kt);
294
0
    abort();
295
0
  }
296
4
  free(tmp);
297
298
4
  store_key(st, pubkey, privkey);
299
4
  return 0;
300
4
}
301
302
#if defined(STANDALONE)
303
304
#if 0 /* use this if generating new keys to embed above */
305
static int
306
prepare_key(struct shared_state *st, int keytype, int bits)
307
{
308
  struct sshkey *privkey = NULL, *pubkey = NULL;
309
  int r;
310
311
  if ((r = sshkey_generate(keytype, bits, &privkey)) != 0) {
312
    error_fr(r, "generate");
313
    abort();
314
  }
315
  if ((r = sshkey_from_private(privkey, &pubkey)) != 0) {
316
    error_fr(r, "make pubkey");
317
    abort();
318
  }
319
  store_key(st, pubkey, privkey);
320
  return 0;
321
}
322
#endif
323
324
int main(void)
325
{
326
  static struct shared_state *st;
327
  struct test_state *ts;
328
  const int keytypes[] = { KEY_RSA, KEY_DSA, KEY_ECDSA, KEY_ED25519, -1 };
329
  static const char * const kextypes[] = {
330
    "sntrup761x25519-sha512@openssh.com",
331
    "curve25519-sha256@libssh.org",
332
    "ecdh-sha2-nistp256",
333
    "diffie-hellman-group1-sha1",
334
    "diffie-hellman-group-exchange-sha1",
335
    NULL,
336
  };
337
  int i, j;
338
  char *path;
339
  FILE *f;
340
341
  log_init("kex_fuzz", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 1);
342
343
  if (st == NULL) {
344
    st = (struct shared_state *)xcalloc(1, sizeof(*st));
345
    prepare_keys(st);
346
  }
347
  /* Run each kex method for each key and save client/server packets */
348
  for (i = 0; keytypes[i] != -1; i++) {
349
    for (j = 0; kextypes[j] != NULL; j++) {
350
      ts = (struct test_state *)xcalloc(1, sizeof(*ts));
351
      ts->smsgs = sshbuf_new();
352
      ts->cmsgs = sshbuf_new();
353
      do_kex_with_key(st, ts, kextypes[j], keytypes[i]);
354
      xasprintf(&path, "S2C-%s-%s",
355
          kextypes[j], sshkey_type(st->pubkeys[keytypes[i]]));
356
      debug_f("%s", path);
357
      if ((f = fopen(path, "wb+")) == NULL)
358
        abort();
359
      if (fwrite(sshbuf_ptr(ts->smsgs), 1,
360
          sshbuf_len(ts->smsgs), f) != sshbuf_len(ts->smsgs))
361
        abort();
362
      fclose(f);
363
      free(path);
364
      //sshbuf_dump(ts->smsgs, stderr);
365
      xasprintf(&path, "C2S-%s-%s",
366
          kextypes[j], sshkey_type(st->pubkeys[keytypes[i]]));
367
      debug_f("%s", path);
368
      if ((f = fopen(path, "wb+")) == NULL)
369
        abort();
370
      if (fwrite(sshbuf_ptr(ts->cmsgs), 1,
371
          sshbuf_len(ts->cmsgs), f) != sshbuf_len(ts->cmsgs))
372
        abort();
373
      fclose(f);
374
      free(path);
375
      //sshbuf_dump(ts->cmsgs, stderr);
376
      sshbuf_free(ts->smsgs);
377
      sshbuf_free(ts->cmsgs);
378
      free(ts);
379
    }
380
  }
381
  for (i = 0; keytypes[i] != -1; i++) {
382
    xasprintf(&path, "%s.priv",
383
        sshkey_type(st->privkeys[keytypes[i]]));
384
    debug_f("%s", path);
385
    if (sshkey_save_private(st->privkeys[keytypes[i]], path,
386
        "", "", SSHKEY_PRIVATE_OPENSSH, NULL, 0) != 0)
387
      abort();
388
    free(path);
389
    xasprintf(&path, "%s.pub",
390
        sshkey_type(st->pubkeys[keytypes[i]]));
391
    debug_f("%s", path);
392
    if (sshkey_save_public(st->pubkeys[keytypes[i]], path, "") != 0)
393
      abort();
394
    free(path);
395
  }
396
}
397
#else /* !STANDALONE */
398
static void
399
do_kex(struct shared_state *st, struct test_state *ts, const char *kex)
400
8.76k
{
401
8.76k
  do_kex_with_key(st, ts, kex, KEY_RSA);
402
8.76k
  do_kex_with_key(st, ts, kex, KEY_DSA);
403
8.76k
  do_kex_with_key(st, ts, kex, KEY_ECDSA);
404
8.76k
  do_kex_with_key(st, ts, kex, KEY_ED25519);
405
8.76k
}
406
407
static void
408
kex_tests(struct shared_state *st, struct test_state *ts)
409
1.75k
{
410
1.75k
  do_kex(st, ts, "sntrup761x25519-sha512@openssh.com");
411
1.75k
  do_kex(st, ts, "curve25519-sha256@libssh.org");
412
1.75k
  do_kex(st, ts, "ecdh-sha2-nistp256");
413
1.75k
  do_kex(st, ts, "diffie-hellman-group1-sha1");
414
1.75k
  do_kex(st, ts, "diffie-hellman-group-exchange-sha1");
415
1.75k
}
416
417
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
418
876
{
419
876
  static struct shared_state *st;
420
876
  struct test_state *ts;
421
876
  u_char crbuf[SSH_MAX_PRE_BANNER_LINES * 4];
422
876
  u_char zbuf[4096] = {0};
423
876
  static LogLevel loglevel = SYSLOG_LEVEL_INFO;
424
425
876
  if (st == NULL) {
426
1
    if (getenv("DEBUG") != NULL || getenv("KEX_FUZZ_DEBUG") != NULL)
427
0
      loglevel = SYSLOG_LEVEL_DEBUG3;
428
1
    log_init("kex_fuzz",
429
1
        loglevel, SYSLOG_FACILITY_AUTH, 1);
430
1
    st = (struct shared_state *)xcalloc(1, sizeof(*st));
431
1
    prepare_keys(st);
432
1
  }
433
434
  /* Ensure that we can complete (fail) banner exchange at least */
435
876
  memset(crbuf, '\n', sizeof(crbuf));
436
437
876
  ts = (struct test_state *)xcalloc(1, sizeof(*ts));
438
876
  if ((ts->s_template = sshbuf_new()) == NULL ||
439
876
      sshbuf_put(ts->s_template, data, size) != 0 ||
440
876
      sshbuf_put(ts->s_template, crbuf, sizeof(crbuf)) != 0 ||
441
876
      sshbuf_put(ts->s_template, zbuf, sizeof(zbuf)) != 0)
442
0
    abort();
443
876
  kex_tests(st, ts);
444
876
  sshbuf_free(ts->s_template);
445
876
  free(ts);
446
447
876
  ts = (struct test_state *)xcalloc(1, sizeof(*ts));
448
876
  if ((ts->c_template = sshbuf_new()) == NULL ||
449
876
      sshbuf_put(ts->c_template, data, size) != 0 ||
450
876
      sshbuf_put(ts->c_template, crbuf, sizeof(crbuf)) != 0 ||
451
876
      sshbuf_put(ts->c_template, zbuf, sizeof(zbuf)) != 0)
452
0
    abort();
453
876
  kex_tests(st, ts);
454
876
  sshbuf_free(ts->c_template);
455
876
  free(ts);
456
457
876
  return 0;
458
876
}
459
#endif /* STANDALONE */
460
} /* extern "C" */