xref: /freebsd/crypto/openssh/regress/misc/fuzz-harness/kex_fuzz.cc (revision 0fdf8fae8b569bf9fff3b5171e669dcd7cf9c79e)
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_ECDSA, 256) != 0 ||
148 	    prepare_key(st, KEY_ED25519, 256) != 0) {
149 		error_f("key prepare failed");
150 		return -1;
151 	}
152 	return 0;
153 }
154 
155 static struct sshkey *
get_pubkey(struct shared_state * st,int keytype)156 get_pubkey(struct shared_state *st, int keytype)
157 {
158 	if (st == NULL || keytype < 0 || (size_t)keytype >= st->nkeys ||
159 	    st->pubkeys == NULL || st->pubkeys[keytype] == NULL)
160 		abort();
161 	return st->pubkeys[keytype];
162 }
163 
164 static struct sshkey *
get_privkey(struct shared_state * st,int keytype)165 get_privkey(struct shared_state *st, int keytype)
166 {
167 	if (st == NULL || keytype < 0 || (size_t)keytype >= st->nkeys ||
168 	    st->privkeys == NULL || st->privkeys[keytype] == NULL)
169 		abort();
170 	return st->privkeys[keytype];
171 }
172 
173 static int
do_kex_with_key(struct shared_state * st,struct test_state * ts,const char * kex,int keytype)174 do_kex_with_key(struct shared_state *st, struct test_state *ts,
175     const char *kex, int keytype)
176 {
177 	struct ssh *client = NULL, *server = NULL;
178 	struct sshkey *privkey = NULL, *pubkey = NULL;
179 	struct sshbuf *state = NULL;
180 	struct kex_params kex_params;
181 	const char *ccp, *proposal[PROPOSAL_MAX] = { KEX_CLIENT };
182 	char *myproposal[PROPOSAL_MAX] = {0}, *keyname = NULL;
183 	int i, r;
184 
185 	ts->cin = ts->sin = NULL;
186 	if (ts->c_template != NULL &&
187 	    (ts->cin = sshbuf_fromb(ts->c_template)) == NULL)
188 		abort();
189 	if (ts->s_template != NULL &&
190 	    (ts->sin = sshbuf_fromb(ts->s_template)) == NULL)
191 		abort();
192 
193 	pubkey = get_pubkey(st, keytype);
194 	privkey = get_privkey(st, keytype);
195 	keyname = xstrdup(sshkey_ssh_name(privkey));
196 	if (ts->cin != NULL) {
197 		debug_f("%s %s clobber client %zu", kex, keyname,
198 		    sshbuf_len(ts->cin));
199 	} else if (ts->sin != NULL) {
200 		debug_f("%s %s clobber server %zu", kex, keyname,
201 		    sshbuf_len(ts->sin));
202 	} else
203 		debug_f("%s %s noclobber", kex, keyname);
204 
205 	for (i = 0; i < PROPOSAL_MAX; i++) {
206 		ccp = proposal[i];
207 #ifdef CIPHER_NONE_AVAIL
208 		if (i == PROPOSAL_ENC_ALGS_CTOS || i == PROPOSAL_ENC_ALGS_STOC)
209 			ccp = "none";
210 #endif
211 		if (i == PROPOSAL_SERVER_HOST_KEY_ALGS)
212 			ccp = keyname;
213 		else if (i == PROPOSAL_KEX_ALGS && kex != NULL)
214 			ccp = kex;
215 		if ((myproposal[i] = strdup(ccp)) == NULL) {
216 			error_f("strdup prop %d", i);
217 			goto fail;
218 		}
219 	}
220 	memcpy(kex_params.proposal, myproposal, sizeof(myproposal));
221 	if ((r = ssh_init(&client, 0, &kex_params)) != 0) {
222 		error_fr(r, "init client");
223 		goto fail;
224 	}
225 	if ((r = ssh_init(&server, 1, &kex_params)) != 0) {
226 		error_fr(r, "init server");
227 		goto fail;
228 	}
229 	if ((r = ssh_add_hostkey(server, privkey)) != 0 ||
230 	    (r = ssh_add_hostkey(client, pubkey)) != 0) {
231 		error_fr(r, "add hostkeys");
232 		goto fail;
233 	}
234 	if ((r = run_kex(ts, client, server)) != 0) {
235 		error_fr(r, "kex");
236 		goto fail;
237 	}
238 	/* XXX rekex, set_state, etc */
239  fail:
240 	for (i = 0; i < PROPOSAL_MAX; i++)
241 		free(myproposal[i]);
242 	sshbuf_free(ts->sin);
243 	sshbuf_free(ts->cin);
244 	sshbuf_free(state);
245 	ssh_free(client);
246 	ssh_free(server);
247 	free(keyname);
248 	return r;
249 }
250 
251 static int
prepare_key(struct shared_state * st,int kt,int bits)252 prepare_key(struct shared_state *st, int kt, int bits)
253 {
254 	const char *pubstr = NULL;
255 	const char *privstr = NULL;
256 	char *tmp, *cp;
257 	struct sshkey *privkey = NULL, *pubkey = NULL;
258 	struct sshbuf *b = NULL;
259 	int r;
260 
261 	switch (kt) {
262 	case KEY_RSA:
263 		pubstr = PUB_RSA;
264 		privstr = PRIV_RSA;
265 		break;
266 	case KEY_ECDSA:
267 		pubstr = PUB_ECDSA;
268 		privstr = PRIV_ECDSA;
269 		break;
270 	case KEY_ED25519:
271 		pubstr = PUB_ED25519;
272 		privstr = PRIV_ED25519;
273 		break;
274 	default:
275 		abort();
276 	}
277 	if ((b = sshbuf_from(privstr, strlen(privstr))) == NULL)
278 		abort();
279 	if ((r = sshkey_parse_private_fileblob(b, "", &privkey, NULL)) != 0) {
280 		error_fr(r, "priv %d", kt);
281 		abort();
282 	}
283 	sshbuf_free(b);
284 	tmp = cp = xstrdup(pubstr);
285 	if ((pubkey = sshkey_new(KEY_UNSPEC)) == NULL)
286 		abort();
287 	if ((r = sshkey_read(pubkey, &cp)) != 0) {
288 		error_fr(r, "pub %d", kt);
289 		abort();
290 	}
291 	free(tmp);
292 
293 	store_key(st, pubkey, privkey);
294 	return 0;
295 }
296 
297 #if defined(STANDALONE)
298 
299 #if 0 /* use this if generating new keys to embed above */
300 static int
301 prepare_key(struct shared_state *st, int keytype, int bits)
302 {
303 	struct sshkey *privkey = NULL, *pubkey = NULL;
304 	int r;
305 
306 	if ((r = sshkey_generate(keytype, bits, &privkey)) != 0) {
307 		error_fr(r, "generate");
308 		abort();
309 	}
310 	if ((r = sshkey_from_private(privkey, &pubkey)) != 0) {
311 		error_fr(r, "make pubkey");
312 		abort();
313 	}
314 	store_key(st, pubkey, privkey);
315 	return 0;
316 }
317 #endif
318 
main(void)319 int main(void)
320 {
321 	static struct shared_state *st;
322 	struct test_state *ts;
323 	const int keytypes[] = { KEY_RSA, KEY_ECDSA, KEY_ED25519, -1 };
324 	static const char * const kextypes[] = {
325 		"sntrup761x25519-sha512@openssh.com",
326 		"curve25519-sha256@libssh.org",
327 		"ecdh-sha2-nistp256",
328 		"diffie-hellman-group1-sha1",
329 		"diffie-hellman-group-exchange-sha1",
330 		NULL,
331 	};
332 	int i, j;
333 	char *path;
334 	FILE *f;
335 
336 	log_init("kex_fuzz", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 1);
337 
338 	if (st == NULL) {
339 		st = (struct shared_state *)xcalloc(1, sizeof(*st));
340 		prepare_keys(st);
341 	}
342 	/* Run each kex method for each key and save client/server packets */
343 	for (i = 0; keytypes[i] != -1; i++) {
344 		for (j = 0; kextypes[j] != NULL; j++) {
345 			ts = (struct test_state *)xcalloc(1, sizeof(*ts));
346 			ts->smsgs = sshbuf_new();
347 			ts->cmsgs = sshbuf_new();
348 			do_kex_with_key(st, ts, kextypes[j], keytypes[i]);
349 			xasprintf(&path, "S2C-%s-%s",
350 			    kextypes[j], sshkey_type(st->pubkeys[keytypes[i]]));
351 			debug_f("%s", path);
352 			if ((f = fopen(path, "wb+")) == NULL)
353 				abort();
354 			if (fwrite(sshbuf_ptr(ts->smsgs), 1,
355 			    sshbuf_len(ts->smsgs), f) != sshbuf_len(ts->smsgs))
356 				abort();
357 			fclose(f);
358 			free(path);
359 			//sshbuf_dump(ts->smsgs, stderr);
360 			xasprintf(&path, "C2S-%s-%s",
361 			    kextypes[j], sshkey_type(st->pubkeys[keytypes[i]]));
362 			debug_f("%s", path);
363 			if ((f = fopen(path, "wb+")) == NULL)
364 				abort();
365 			if (fwrite(sshbuf_ptr(ts->cmsgs), 1,
366 			    sshbuf_len(ts->cmsgs), f) != sshbuf_len(ts->cmsgs))
367 				abort();
368 			fclose(f);
369 			free(path);
370 			//sshbuf_dump(ts->cmsgs, stderr);
371 			sshbuf_free(ts->smsgs);
372 			sshbuf_free(ts->cmsgs);
373 			free(ts);
374 		}
375 	}
376 	for (i = 0; keytypes[i] != -1; i++) {
377 		xasprintf(&path, "%s.priv",
378 		    sshkey_type(st->privkeys[keytypes[i]]));
379 		debug_f("%s", path);
380 		if (sshkey_save_private(st->privkeys[keytypes[i]], path,
381 		    "", "", SSHKEY_PRIVATE_OPENSSH, NULL, 0) != 0)
382 			abort();
383 		free(path);
384 		xasprintf(&path, "%s.pub",
385 		    sshkey_type(st->pubkeys[keytypes[i]]));
386 		debug_f("%s", path);
387 		if (sshkey_save_public(st->pubkeys[keytypes[i]], path, "") != 0)
388 			abort();
389 		free(path);
390 	}
391 }
392 #else /* !STANDALONE */
393 static void
do_kex(struct shared_state * st,struct test_state * ts,const char * kex)394 do_kex(struct shared_state *st, struct test_state *ts, const char *kex)
395 {
396 	do_kex_with_key(st, ts, kex, KEY_RSA);
397 	do_kex_with_key(st, ts, kex, KEY_ECDSA);
398 	do_kex_with_key(st, ts, kex, KEY_ED25519);
399 }
400 
401 static void
kex_tests(struct shared_state * st,struct test_state * ts)402 kex_tests(struct shared_state *st, struct test_state *ts)
403 {
404 	do_kex(st, ts, "sntrup761x25519-sha512@openssh.com");
405 	do_kex(st, ts, "curve25519-sha256@libssh.org");
406 	do_kex(st, ts, "ecdh-sha2-nistp256");
407 	do_kex(st, ts, "diffie-hellman-group1-sha1");
408 	do_kex(st, ts, "diffie-hellman-group-exchange-sha1");
409 }
410 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)411 int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
412 {
413 	static struct shared_state *st;
414 	struct test_state *ts;
415 	u_char crbuf[SSH_MAX_PRE_BANNER_LINES * 4];
416 	u_char zbuf[4096] = {0};
417 	static LogLevel loglevel = SYSLOG_LEVEL_INFO;
418 
419 	if (st == NULL) {
420 		if (getenv("DEBUG") != NULL || getenv("KEX_FUZZ_DEBUG") != NULL)
421 			loglevel = SYSLOG_LEVEL_DEBUG3;
422 		log_init("kex_fuzz",
423 		    loglevel, SYSLOG_FACILITY_AUTH, 1);
424 		st = (struct shared_state *)xcalloc(1, sizeof(*st));
425 		prepare_keys(st);
426 	}
427 
428 	/* Ensure that we can complete (fail) banner exchange at least */
429 	memset(crbuf, '\n', sizeof(crbuf));
430 
431 	ts = (struct test_state *)xcalloc(1, sizeof(*ts));
432 	if ((ts->s_template = sshbuf_new()) == NULL ||
433 	    sshbuf_put(ts->s_template, data, size) != 0 ||
434 	    sshbuf_put(ts->s_template, crbuf, sizeof(crbuf)) != 0 ||
435 	    sshbuf_put(ts->s_template, zbuf, sizeof(zbuf)) != 0)
436 		abort();
437 	kex_tests(st, ts);
438 	sshbuf_free(ts->s_template);
439 	free(ts);
440 
441 	ts = (struct test_state *)xcalloc(1, sizeof(*ts));
442 	if ((ts->c_template = sshbuf_new()) == NULL ||
443 	    sshbuf_put(ts->c_template, data, size) != 0 ||
444 	    sshbuf_put(ts->c_template, crbuf, sizeof(crbuf)) != 0 ||
445 	    sshbuf_put(ts->c_template, zbuf, sizeof(zbuf)) != 0)
446 		abort();
447 	kex_tests(st, ts);
448 	sshbuf_free(ts->c_template);
449 	free(ts);
450 
451 	return 0;
452 }
453 #endif /* STANDALONE */
454 } /* extern "C" */
455