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
do_send_and_receive(struct ssh * from,struct ssh * to,struct sshbuf * store,int clobber,size_t * n)41 do_send_and_receive(struct ssh *from, struct ssh *to,
42 struct sshbuf *store, int clobber, size_t *n)
43 {
44 u_char type;
45 size_t len;
46 const u_char *buf;
47 int r;
48
49 for (*n = 0;; (*n)++) {
50 if ((r = ssh_packet_next(from, &type)) != 0) {
51 debug_fr(r, "ssh_packet_next");
52 return r;
53 }
54 if (type != 0)
55 return 0;
56 buf = ssh_output_ptr(from, &len);
57 debug_f("%zu%s", len, clobber ? " ignore" : "");
58 if (len == 0)
59 return 0;
60 if ((r = ssh_output_consume(from, len)) != 0) {
61 debug_fr(r, "ssh_output_consume");
62 return r;
63 }
64 if (store != NULL && (r = sshbuf_put(store, buf, len)) != 0) {
65 debug_fr(r, "sshbuf_put");
66 return r;
67 }
68 if (!clobber && (r = ssh_input_append(to, buf, len)) != 0) {
69 debug_fr(r, "ssh_input_append");
70 return r;
71 }
72 }
73 }
74
75 static int
run_kex(struct test_state * ts,struct ssh * client,struct ssh * server)76 run_kex(struct test_state *ts, struct ssh *client, struct ssh *server)
77 {
78 int r = 0;
79 size_t cn, sn;
80
81 /* If fuzzing, replace server/client input */
82 if (ts->sin != NULL) {
83 if ((r = ssh_input_append(server, sshbuf_ptr(ts->sin),
84 sshbuf_len(ts->sin))) != 0) {
85 error_fr(r, "ssh_input_append");
86 return r;
87 }
88 sshbuf_reset(ts->sin);
89 }
90 if (ts->cin != NULL) {
91 if ((r = ssh_input_append(client, sshbuf_ptr(ts->cin),
92 sshbuf_len(ts->cin))) != 0) {
93 error_fr(r, "ssh_input_append");
94 return r;
95 }
96 sshbuf_reset(ts->cin);
97 }
98 while (!server->kex->done || !client->kex->done) {
99 cn = sn = 0;
100 debug_f("S:");
101 if ((r = do_send_and_receive(server, client,
102 ts->smsgs, ts->cin != NULL, &sn)) != 0) {
103 debug_fr(r, "S->C");
104 break;
105 }
106 debug_f("C:");
107 if ((r = do_send_and_receive(client, server,
108 ts->cmsgs, ts->sin != NULL, &cn)) != 0) {
109 debug_fr(r, "C->S");
110 break;
111 }
112 if (cn == 0 && sn == 0) {
113 debug_f("kex stalled");
114 r = SSH_ERR_PROTOCOL_ERROR;
115 break;
116 }
117 }
118 debug_fr(r, "done");
119 return r;
120 }
121
122 static void
store_key(struct shared_state * st,struct sshkey * pubkey,struct sshkey * privkey)123 store_key(struct shared_state *st, struct sshkey *pubkey,
124 struct sshkey *privkey)
125 {
126 if (st == NULL || pubkey->type < 0 || pubkey->type > INT_MAX ||
127 privkey->type != pubkey->type ||
128 ((size_t)pubkey->type < st->nkeys &&
129 st->pubkeys[pubkey->type] != NULL))
130 abort();
131 if ((size_t)pubkey->type >= st->nkeys) {
132 st->pubkeys = (struct sshkey **)xrecallocarray(st->pubkeys,
133 st->nkeys, pubkey->type + 1, sizeof(*st->pubkeys));
134 st->privkeys = (struct sshkey **)xrecallocarray(st->privkeys,
135 st->nkeys, privkey->type + 1, sizeof(*st->privkeys));
136 st->nkeys = privkey->type + 1;
137 }
138 debug_f("store %s at %d", sshkey_ssh_name(pubkey), pubkey->type);
139 st->pubkeys[pubkey->type] = pubkey;
140 st->privkeys[privkey->type] = privkey;
141 }
142
143 static int
prepare_keys(struct shared_state * st)144 prepare_keys(struct shared_state *st)
145 {
146 if (prepare_key(st, KEY_RSA, 2048) != 0 ||
147 prepare_key(st, KEY_DSA, 1024) != 0 ||
148 prepare_key(st, KEY_ECDSA, 256) != 0 ||
149 prepare_key(st, KEY_ED25519, 256) != 0) {
150 error_f("key prepare failed");
151 return -1;
152 }
153 return 0;
154 }
155
156 static struct sshkey *
get_pubkey(struct shared_state * st,int keytype)157 get_pubkey(struct shared_state *st, int keytype)
158 {
159 if (st == NULL || keytype < 0 || (size_t)keytype >= st->nkeys ||
160 st->pubkeys == NULL || st->pubkeys[keytype] == NULL)
161 abort();
162 return st->pubkeys[keytype];
163 }
164
165 static struct sshkey *
get_privkey(struct shared_state * st,int keytype)166 get_privkey(struct shared_state *st, int keytype)
167 {
168 if (st == NULL || keytype < 0 || (size_t)keytype >= st->nkeys ||
169 st->privkeys == NULL || st->privkeys[keytype] == NULL)
170 abort();
171 return st->privkeys[keytype];
172 }
173
174 static int
do_kex_with_key(struct shared_state * st,struct test_state * ts,const char * kex,int keytype)175 do_kex_with_key(struct shared_state *st, struct test_state *ts,
176 const char *kex, int keytype)
177 {
178 struct ssh *client = NULL, *server = NULL;
179 struct sshkey *privkey = NULL, *pubkey = NULL;
180 struct sshbuf *state = NULL;
181 struct kex_params kex_params;
182 const char *ccp, *proposal[PROPOSAL_MAX] = { KEX_CLIENT };
183 char *myproposal[PROPOSAL_MAX] = {0}, *keyname = NULL;
184 int i, r;
185
186 ts->cin = ts->sin = NULL;
187 if (ts->c_template != NULL &&
188 (ts->cin = sshbuf_fromb(ts->c_template)) == NULL)
189 abort();
190 if (ts->s_template != NULL &&
191 (ts->sin = sshbuf_fromb(ts->s_template)) == NULL)
192 abort();
193
194 pubkey = get_pubkey(st, keytype);
195 privkey = get_privkey(st, keytype);
196 keyname = xstrdup(sshkey_ssh_name(privkey));
197 if (ts->cin != NULL) {
198 debug_f("%s %s clobber client %zu", kex, keyname,
199 sshbuf_len(ts->cin));
200 } else if (ts->sin != NULL) {
201 debug_f("%s %s clobber server %zu", kex, keyname,
202 sshbuf_len(ts->sin));
203 } else
204 debug_f("%s %s noclobber", kex, keyname);
205
206 for (i = 0; i < PROPOSAL_MAX; i++) {
207 ccp = proposal[i];
208 #ifdef CIPHER_NONE_AVAIL
209 if (i == PROPOSAL_ENC_ALGS_CTOS || i == PROPOSAL_ENC_ALGS_STOC)
210 ccp = "none";
211 #endif
212 if (i == PROPOSAL_SERVER_HOST_KEY_ALGS)
213 ccp = keyname;
214 else if (i == PROPOSAL_KEX_ALGS && kex != NULL)
215 ccp = kex;
216 if ((myproposal[i] = strdup(ccp)) == NULL) {
217 error_f("strdup prop %d", i);
218 goto fail;
219 }
220 }
221 memcpy(kex_params.proposal, myproposal, sizeof(myproposal));
222 if ((r = ssh_init(&client, 0, &kex_params)) != 0) {
223 error_fr(r, "init client");
224 goto fail;
225 }
226 if ((r = ssh_init(&server, 1, &kex_params)) != 0) {
227 error_fr(r, "init server");
228 goto fail;
229 }
230 if ((r = ssh_add_hostkey(server, privkey)) != 0 ||
231 (r = ssh_add_hostkey(client, pubkey)) != 0) {
232 error_fr(r, "add hostkeys");
233 goto fail;
234 }
235 if ((r = run_kex(ts, client, server)) != 0) {
236 error_fr(r, "kex");
237 goto fail;
238 }
239 /* XXX rekex, set_state, etc */
240 fail:
241 for (i = 0; i < PROPOSAL_MAX; i++)
242 free(myproposal[i]);
243 sshbuf_free(ts->sin);
244 sshbuf_free(ts->cin);
245 sshbuf_free(state);
246 ssh_free(client);
247 ssh_free(server);
248 free(keyname);
249 return r;
250 }
251
252 static int
prepare_key(struct shared_state * st,int kt,int bits)253 prepare_key(struct shared_state *st, int kt, int bits)
254 {
255 const char *pubstr = NULL;
256 const char *privstr = NULL;
257 char *tmp, *cp;
258 struct sshkey *privkey = NULL, *pubkey = NULL;
259 struct sshbuf *b = NULL;
260 int r;
261
262 switch (kt) {
263 case KEY_RSA:
264 pubstr = PUB_RSA;
265 privstr = PRIV_RSA;
266 break;
267 case KEY_DSA:
268 pubstr = PUB_DSA;
269 privstr = PRIV_DSA;
270 break;
271 case KEY_ECDSA:
272 pubstr = PUB_ECDSA;
273 privstr = PRIV_ECDSA;
274 break;
275 case KEY_ED25519:
276 pubstr = PUB_ED25519;
277 privstr = PRIV_ED25519;
278 break;
279 default:
280 abort();
281 }
282 if ((b = sshbuf_from(privstr, strlen(privstr))) == NULL)
283 abort();
284 if ((r = sshkey_parse_private_fileblob(b, "", &privkey, NULL)) != 0) {
285 error_fr(r, "priv %d", kt);
286 abort();
287 }
288 sshbuf_free(b);
289 tmp = cp = xstrdup(pubstr);
290 if ((pubkey = sshkey_new(KEY_UNSPEC)) == NULL)
291 abort();
292 if ((r = sshkey_read(pubkey, &cp)) != 0) {
293 error_fr(r, "pub %d", kt);
294 abort();
295 }
296 free(tmp);
297
298 store_key(st, pubkey, privkey);
299 return 0;
300 }
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
main(void)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
do_kex(struct shared_state * st,struct test_state * ts,const char * kex)399 do_kex(struct shared_state *st, struct test_state *ts, const char *kex)
400 {
401 do_kex_with_key(st, ts, kex, KEY_RSA);
402 do_kex_with_key(st, ts, kex, KEY_DSA);
403 do_kex_with_key(st, ts, kex, KEY_ECDSA);
404 do_kex_with_key(st, ts, kex, KEY_ED25519);
405 }
406
407 static void
kex_tests(struct shared_state * st,struct test_state * ts)408 kex_tests(struct shared_state *st, struct test_state *ts)
409 {
410 do_kex(st, ts, "sntrup761x25519-sha512@openssh.com");
411 do_kex(st, ts, "curve25519-sha256@libssh.org");
412 do_kex(st, ts, "ecdh-sha2-nistp256");
413 do_kex(st, ts, "diffie-hellman-group1-sha1");
414 do_kex(st, ts, "diffie-hellman-group-exchange-sha1");
415 }
416
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)417 int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
418 {
419 static struct shared_state *st;
420 struct test_state *ts;
421 u_char crbuf[SSH_MAX_PRE_BANNER_LINES * 4];
422 u_char zbuf[4096] = {0};
423 static LogLevel loglevel = SYSLOG_LEVEL_INFO;
424
425 if (st == NULL) {
426 if (getenv("DEBUG") != NULL || getenv("KEX_FUZZ_DEBUG") != NULL)
427 loglevel = SYSLOG_LEVEL_DEBUG3;
428 log_init("kex_fuzz",
429 loglevel, SYSLOG_FACILITY_AUTH, 1);
430 st = (struct shared_state *)xcalloc(1, sizeof(*st));
431 prepare_keys(st);
432 }
433
434 /* Ensure that we can complete (fail) banner exchange at least */
435 memset(crbuf, '\n', sizeof(crbuf));
436
437 ts = (struct test_state *)xcalloc(1, sizeof(*ts));
438 if ((ts->s_template = sshbuf_new()) == NULL ||
439 sshbuf_put(ts->s_template, data, size) != 0 ||
440 sshbuf_put(ts->s_template, crbuf, sizeof(crbuf)) != 0 ||
441 sshbuf_put(ts->s_template, zbuf, sizeof(zbuf)) != 0)
442 abort();
443 kex_tests(st, ts);
444 sshbuf_free(ts->s_template);
445 free(ts);
446
447 ts = (struct test_state *)xcalloc(1, sizeof(*ts));
448 if ((ts->c_template = sshbuf_new()) == NULL ||
449 sshbuf_put(ts->c_template, data, size) != 0 ||
450 sshbuf_put(ts->c_template, crbuf, sizeof(crbuf)) != 0 ||
451 sshbuf_put(ts->c_template, zbuf, sizeof(zbuf)) != 0)
452 abort();
453 kex_tests(st, ts);
454 sshbuf_free(ts->c_template);
455 free(ts);
456
457 return 0;
458 }
459 #endif /* STANDALONE */
460 } /* extern "C" */
461