diff --git a/.hgignore b/.hgignore new file mode 100644 index 000000000..393823816 --- /dev/null +++ b/.hgignore @@ -0,0 +1,2 @@ +.*\.o +.*~ diff --git a/Makefile.in b/Makefile.in index 286e25a79..d94f87ba2 100644 --- a/Makefile.in +++ b/Makefile.in @@ -30,7 +30,7 @@ COMMONOBJS=dbutil.o buffer.o dbhelpers.o \ queue.o \ atomicio.o compat.o fake-rfc2553.o \ ltc_prng.o ecc.o ecdsa.o crypto_desc.o \ - gensignkey.o gendss.o genrsa.o + gensignkey.o gendss.o genrsa.o fuzz-common.o SVROBJS=svr-kex.o svr-auth.o sshpty.o \ svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.o svr-session.o svr-service.o \ @@ -224,8 +224,27 @@ distclean: clean tidy tidy: -rm -f *~ *.gcov */*~ -# run this manually for fuzzing. hostkeys.c is checked in. -hostkeys: +## Fuzzing targets + +# exclude svr-main.o to avoid duplicate main +svrfuzzobjs=$(subst svr-main.o, ,$(dropbearobjs)) +CLANG=clang + +# fuzzers that don't use libfuzzer, just a standalone harness that feeds inputs +fuzzstandalone: LIBS+=fuzz-harness.o +fuzzstandalone: fuzz-harness.o fuzzers + +# build all the fuzzers. This will require fail to link unless built with +# make fuzzers LIBS=-lFuzzer.a +# or similar - the library provides main(). +fuzzers: fuzzer-preauth + +fuzzer-preauth: fuzzer-preauth.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs) + $(CC) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) @CRYPTLIB@ + +# run this to update hardcoded hostkeys for for fuzzing. +# hostkeys.c is checked in to hg. +fuzz-hostkeys: dropbearkey -t rsa -f keyr dropbearkey -t dss -f keyd dropbearkey -t ecdsa -size 256 -f keye diff --git a/dbrandom.c b/dbrandom.c index f4fc94d5b..01974113d 100644 --- a/dbrandom.c +++ b/dbrandom.c @@ -28,6 +28,7 @@ #include "bignum.h" #include "dbrandom.h" #include "runopts.h" +#include "fuzz.h" /* this is used to generate unique output from the same hashpool */ @@ -147,7 +148,7 @@ void addrandom(unsigned char * buf, unsigned int len) hash_state hs; #ifdef DROPBEAR_FUZZ - if (opts.fuzz.fuzzing || opts.fuzz.recordf) { + if (fuzz.fuzzing || fuzz.recordf) { return; } #endif @@ -165,7 +166,7 @@ void addrandom(unsigned char * buf, unsigned int len) static void write_urandom() { #ifdef DROPBEAR_FUZZ - if (opts.fuzz.fuzzing || opts.fuzz.recordf) { + if (fuzz.fuzzing || fuzz.recordf) { return; } #endif @@ -203,7 +204,7 @@ void seedrandom() { clock_t clockval; #ifdef DROPBEAR_FUZZ - if (opts.fuzz.fuzzing || opts.fuzz.recordf) { + if (fuzz.fuzzing || fuzz.recordf) { seedfuzz(); return; } diff --git a/fuzz-common.c b/fuzz-common.c new file mode 100644 index 000000000..cc6c12546 --- /dev/null +++ b/fuzz-common.c @@ -0,0 +1,78 @@ +#include "includes.h" + +#ifdef DROPBEAR_FUZZ + +#include "includes.h" +#include "fuzz.h" +#include "dbutil.h" +#include "runopts.h" + +struct dropbear_fuzz_options fuzz; + +static void load_fixed_hostkeys(void); + +static void common_setup_fuzzer(void) { + fuzz.fuzzing = 1; +} + +void svr_setup_fuzzer(void) { + struct passwd *pw; + + common_setup_fuzzer(); + + char *argv[] = { + "-E", + }; + + int argc = sizeof(argv) / sizeof(*argv); + svr_getopts(argc, argv); + + /* user lookups might be slow, cache it */ + pw = getpwuid(getuid()); + dropbear_assert(pw); + fuzz.pw_name = m_strdup(pw->pw_name); + fuzz.pw_dir = m_strdup(pw->pw_dir); + fuzz.pw_shell = m_strdup(pw->pw_shell); + fuzz.pw_passwd = m_strdup("!!zzznope"); + + load_fixed_hostkeys(); +} + +static void load_fixed_hostkeys(void) { +#include "fuzz-hostkeys.c" + + buffer *b = buf_new(3000); + enum signkey_type type; + + TRACE(("load fixed hostkeys")) + + svr_opts.hostkey = new_sign_key(); + + buf_setlen(b, 0); + buf_putbytes(b, keyr, keyr_len); + buf_setpos(b, 0); + type = DROPBEAR_SIGNKEY_RSA; + if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) { + dropbear_exit("failed fixed rsa hostkey"); + } + + buf_setlen(b, 0); + buf_putbytes(b, keyd, keyd_len); + buf_setpos(b, 0); + type = DROPBEAR_SIGNKEY_DSS; + if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) { + dropbear_exit("failed fixed dss hostkey"); + } + + buf_setlen(b, 0); + buf_putbytes(b, keye, keye_len); + buf_setpos(b, 0); + type = DROPBEAR_SIGNKEY_ECDSA_NISTP256; + if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) { + dropbear_exit("failed fixed ecdsa hostkey"); + } + + buf_free(b); +} + +#endif /* DROPBEAR_FUZZ */ diff --git a/fuzz-harness.c b/fuzz-harness.c new file mode 100644 index 000000000..19b01e368 --- /dev/null +++ b/fuzz-harness.c @@ -0,0 +1,8 @@ +#include "includes.h" + +extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size); + +int main(int argc, char ** argv) { + LLVMFuzzerTestOneInput("hello", 5); + return 0; +} diff --git a/hostkeys.c b/fuzz-hostkeys.c similarity index 100% rename from hostkeys.c rename to fuzz-hostkeys.c diff --git a/fuzz.h b/fuzz.h new file mode 100644 index 000000000..e7360e33b --- /dev/null +++ b/fuzz.h @@ -0,0 +1,35 @@ +#ifndef DROPBEAR_FUZZ_H +#define DROPBEAR_FUZZ_H + +#include "includes.h" +#include "buffer.h" + +#ifdef DROPBEAR_FUZZ + +void svr_setup_fuzzer(void); + +struct dropbear_fuzz_options { + int fuzzing; + + // to record an unencrypted stream + FILE* recordf; + + // fuzzing input + buffer input; + + // dropbear_exit() jumps back + sigjmp_buf jmp; + + uid_t pw_uid; + gid_t pw_gid; + char* pw_name; + char* pw_dir; + char* pw_shell; + char* pw_passwd; +}; + +extern struct dropbear_fuzz_options fuzz; + +#endif + +#endif /* DROPBEAR_FUZZ_H */ diff --git a/fuzzer-preauth.c b/fuzzer-preauth.c new file mode 100644 index 000000000..6a40108dc --- /dev/null +++ b/fuzzer-preauth.c @@ -0,0 +1,31 @@ +#include "fuzz.h" +#include "dbrandom.h" +#include "session.h" + +static int setup_fuzzer(void) { + svr_setup_fuzzer(); + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + static int once = 0; + if (!once) { + setup_fuzzer(); + once = 1; + } + + fuzz.input.data = (unsigned char*)Data; + fuzz.input.size = Size; + fuzz.input.len = Size; + fuzz.input.pos = 0; + + seedrandom(); + + if (setjmp(fuzz.jmp) == 0) { + svr_session(-1, -1); + } else { + // dropbear_exit jumped here + } + + return 0; +} diff --git a/packet.c b/packet.c index f10e63911..235069b03 100644 --- a/packet.c +++ b/packet.c @@ -36,6 +36,7 @@ #include "channel.h" #include "netio.h" #include "runopts.h" +#include "fuzz.h" static int read_packet_init(void); static void make_mac(unsigned int seqno, const struct key_context_directional * key_state, @@ -78,7 +79,7 @@ void write_packet() { calls write_packet() without bothering to test with select() since it's likely to be necessary */ #ifdef DROPBEAR_FUZZ - if (opts.fuzz.fuzzing) { + if (fuzz.fuzzing) { // pretend to write one packet at a time // TODO(fuzz): randomise amount written based on the fuzz input written = iov[0].iov_len; diff --git a/runopts.h b/runopts.h index 1f51b1606..f7c869da8 100644 --- a/runopts.h +++ b/runopts.h @@ -58,29 +58,6 @@ typedef struct runopts { char *mac_list; #endif -#ifdef DROPBEAR_FUZZ - struct { - int fuzzing; - - // to record an unencrypted stream - FILE* recordf; - - // fuzzing input - buffer *input; - - // dropbear_exit() jumps back - sigjmp_buf jmp; - - uid_t pw_uid; - gid_t pw_gid; - char* pw_name; - char* pw_dir; - char* pw_shell; - char* pw_passwd; - - } fuzz; -#endif - } runopts; extern runopts opts; diff --git a/svr-auth.c b/svr-auth.c index d00fa7a5a..9156d771d 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -359,7 +359,7 @@ void send_msg_userauth_failure(int partial, int incrfail) { /* We delay for 300ms +- 50ms */ delay = 250000 + (delay % 100000); #ifndef DROPBEAR_FUZZ - if (!opts.fuzz.fuzzing) { + if (!fuzz.fuzzing) { usleep(delay); } #endif diff --git a/svr-runopts.c b/svr-runopts.c index c0b7bf279..295b6537f 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -346,19 +346,6 @@ void svr_getopts(int argc, char ** argv) { } opts.idle_timeout_secs = val; } - -#ifdef DROPBEAR_FUZZ - if (opts.fuzz.fuzzing) { - struct passwd *pw; - /* user lookups might be slow, cache it */ - pw = getpwuid(getuid()); - dropbear_assert(pw); - opts.fuzz.pw_name = m_strdup(pw->pw_name); - opts.fuzz.pw_dir = m_strdup(pw->pw_dir); - opts.fuzz.pw_shell = m_strdup(pw->pw_shell); - opts.fuzz.pw_passwd = m_strdup("!!zzznope"); - } -#endif } static void addportandaddress(const char* spec) { @@ -488,57 +475,12 @@ static void addhostkey(const char *keyfile) { svr_opts.num_hostkey_files++; } -#ifdef DROPBEAR_FUZZ -static void load_fixed_hostkeys() { -#include "hostkeys.c" - - buffer *b = buf_new(3000); - enum signkey_type type; - - TRACE(("load fixed hostkeys")) - - svr_opts.hostkey = new_sign_key(); - - buf_setlen(b, 0); - buf_putbytes(b, keyr, keyr_len); - buf_setpos(b, 0); - type = DROPBEAR_SIGNKEY_RSA; - if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) { - dropbear_exit("failed fixed rsa hostkey"); - } - - buf_setlen(b, 0); - buf_putbytes(b, keyd, keyd_len); - buf_setpos(b, 0); - type = DROPBEAR_SIGNKEY_DSS; - if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) { - dropbear_exit("failed fixed dss hostkey"); - } - - buf_setlen(b, 0); - buf_putbytes(b, keye, keye_len); - buf_setpos(b, 0); - type = DROPBEAR_SIGNKEY_ECDSA_NISTP256; - if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) { - dropbear_exit("failed fixed ecdsa hostkey"); - } - - buf_free(b); -} -#endif // DROPBEAR_FUZZ void load_all_hostkeys() { int i; int disable_unset_keys = 1; int any_keys = 0; -#ifdef DROPBEAR_FUZZ - if (opts.fuzz.fuzzing) { - load_fixed_hostkeys(); - return; - } -#endif - svr_opts.hostkey = new_sign_key(); for (i = 0; i < svr_opts.num_hostkey_files; i++) { diff --git a/svr-session.c b/svr-session.c index 0e6a9e803..41571bb8e 100644 --- a/svr-session.c +++ b/svr-session.c @@ -40,6 +40,7 @@ #include "auth.h" #include "runopts.h" #include "crypto_desc.h" +#include "fuzz.h" static void svr_remoteclosed(void); @@ -182,6 +183,13 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) { session_cleanup(); } +#ifdef DROPBEAR_FUZZ + // longjmp before cleaning up svr_opts + if (fuzz.fuzzing) { + longjmp(fuzz.jmp, 1); + } +#endif + if (svr_opts.hostkey) { sign_key_free(svr_opts.hostkey); svr_opts.hostkey = NULL; @@ -191,11 +199,6 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) { m_free(svr_opts.ports[i]); } -#ifdef DROPBEAR_FUZZ - if (opts.fuzz.fuzzing) { - longjmp(opts.fuzz.jmp, 1); - } -#endif exit(exitcode);