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