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