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 { 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 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 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 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 * 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 * 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 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 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 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 { 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 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 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