summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.hgignore2
-rw-r--r--Makefile.in56
-rw-r--r--buffer.c4
-rw-r--r--common-kex.c22
-rw-r--r--common-session.c35
-rw-r--r--configure.ac15
-rw-r--r--dbhelpers.c12
-rw-r--r--dbmalloc.c144
-rw-r--r--dbmalloc.h16
-rw-r--r--dbrandom.c32
-rw-r--r--dbutil.c64
-rw-r--r--dbutil.h5
-rw-r--r--debug.h1
-rw-r--r--fuzz-common.c132
-rw-r--r--fuzz-harness.c41
-rw-r--r--fuzz-hostkeys.c129
-rw-r--r--fuzz-wrapfd.c244
-rw-r--r--fuzz-wrapfd.h25
-rw-r--r--fuzz.h67
-rw-r--r--fuzzer-preauth.c53
-rw-r--r--fuzzer-pubkey.c44
-rw-r--r--fuzzer-verify.c44
-rw-r--r--includes.h8
-rw-r--r--libtomcrypt/src/headers/tomcrypt_custom.h11
-rw-r--r--libtommath/tommath_class.h11
-rw-r--r--netio.c14
-rw-r--r--packet.c26
-rw-r--r--signkey.c20
-rw-r--r--svr-auth.c6
-rw-r--r--svr-authpubkey.c208
-rw-r--r--svr-runopts.c1
-rw-r--r--svr-session.c13
32 files changed, 1331 insertions, 174 deletions
diff --git a/.hgignore b/.hgignore
new file mode 100644
index 0000000..3938238
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,2 @@
+.*\.o
+.*~
diff --git a/Makefile.in b/Makefile.in
index dbd4745..0ed124d 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -34,7 +34,8 @@ 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
+ dbmalloc.o \
+ gensignkey.o gendss.o genrsa.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 \
@@ -57,6 +58,10 @@ CONVERTOBJS=dropbearconvert.o keyimport.o
SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o compat.o
+ifeq (@DROPBEAR_FUZZ@, 1)
+ COMMONOBJS += fuzz-common.o fuzz-wrapfd.o
+endif
+
HEADERS=options.h dbutil.h session.h packet.h algo.h ssh.h buffer.h kex.h \
dss.h bignum.h signkey.h rsa.h dbrandom.h service.h auth.h \
debug.h channel.h chansession.h config.h queue.h sshpty.h \
@@ -220,7 +225,7 @@ clean: ltc-clean ltm-clean thisclean
thisclean:
-rm -f dropbear dbclient dropbearkey dropbearconvert scp scp-progress \
- dropbearmulti *.o *.da *.bb *.bbg *.prof
+ dropbearmulti *.o *.da *.bb *.bbg *.prof
distclean: clean tidy
-rm -f config.h
@@ -236,3 +241,50 @@ default_options.h: default_options.h.in
echo >> $@.tmp
$(srcdir)/ifndef_wrapper.sh < $^ > $@.tmp
mv $@.tmp $@
+
+## Fuzzing targets
+
+# list of fuzz targets
+FUZZ_TARGETS=fuzzer-preauth fuzzer-pubkey fuzzer-verify
+
+FUZZER_OPTIONS = $(addsuffix .options, $(FUZZ_TARGETS))
+
+list-fuzz-targets:
+ @echo $(FUZZ_TARGETS)
+
+# fuzzers that don't use libfuzzer, just a standalone harness that feeds inputs
+fuzzstandalone: FUZZLIB=fuzz-harness.o
+fuzzstandalone: fuzz-harness.o fuzz-targets
+
+# exclude svr-main.o to avoid duplicate main
+svrfuzzobjs=$(subst svr-main.o, ,$(dropbearobjs))
+
+# build all the fuzzers. This will require fail to link unless built with
+# make fuzzers LIBS=-lFuzzer.a
+# or similar - the library provides main().
+fuzz-targets: $(FUZZ_TARGETS) $(FUZZER_OPTIONS)
+
+fuzzer-preauth: fuzzer-preauth.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs)
+ $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
+
+fuzzer-pubkey: fuzzer-pubkey.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs)
+ $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
+
+fuzzer-verify: fuzzer-verify.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs)
+ $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
+
+fuzzer-%.options: Makefile
+ echo "[libfuzzer]" > $@
+ echo "max_len = 50000" >> $@
+
+# 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
+ echo > hostkeys.c
+ /usr/bin/xxd -i -a keyr >> hostkeys.c
+ /usr/bin/xxd -i -a keye >> hostkeys.c
+ /usr/bin/xxd -i -a keyd >> hostkeys.c
+
diff --git a/buffer.c b/buffer.c
index 0ca50b4..a462374 100644
--- a/buffer.c
+++ b/buffer.c
@@ -209,6 +209,7 @@ char* buf_getstring(buffer* buf, unsigned int *retlen) {
unsigned int len;
char* ret;
+ void* src = NULL;
len = buf_getint(buf);
if (len > MAX_STRING_LEN) {
dropbear_exit("String too long");
@@ -217,8 +218,9 @@ char* buf_getstring(buffer* buf, unsigned int *retlen) {
if (retlen != NULL) {
*retlen = len;
}
+ src = buf_getptr(buf, len);
ret = m_malloc(len+1);
- memcpy(ret, buf_getptr(buf, len), len);
+ memcpy(ret, src, len);
buf_incrpos(buf, len);
ret[len] = '\0';
diff --git a/common-kex.c b/common-kex.c
index d60b9ea..fc2daad 100644
--- a/common-kex.c
+++ b/common-kex.c
@@ -391,6 +391,14 @@ int is_compress_recv() {
&& ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
}
+static void* dropbear_zalloc(void* UNUSED(opaque), uInt items, uInt size) {
+ return m_calloc(items, size);
+}
+
+static void dropbear_zfree(void* UNUSED(opaque), void* ptr) {
+ m_free(ptr);
+}
+
/* Set up new zlib compression streams, close the old ones. Only
* called from gen_new_keys() */
static void gen_new_zstream_recv() {
@@ -399,8 +407,8 @@ static void gen_new_zstream_recv() {
if (ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB
|| ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
ses.newkeys->recv.zstream = (z_streamp)m_malloc(sizeof(z_stream));
- ses.newkeys->recv.zstream->zalloc = Z_NULL;
- ses.newkeys->recv.zstream->zfree = Z_NULL;
+ ses.newkeys->recv.zstream->zalloc = dropbear_zalloc;
+ ses.newkeys->recv.zstream->zfree = dropbear_zfree;
if (inflateInit(ses.newkeys->recv.zstream) != Z_OK) {
dropbear_exit("zlib error");
@@ -423,8 +431,8 @@ static void gen_new_zstream_trans() {
if (ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB
|| ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
ses.newkeys->trans.zstream = (z_streamp)m_malloc(sizeof(z_stream));
- ses.newkeys->trans.zstream->zalloc = Z_NULL;
- ses.newkeys->trans.zstream->zfree = Z_NULL;
+ ses.newkeys->trans.zstream->zalloc = dropbear_zalloc;
+ ses.newkeys->trans.zstream->zfree = dropbear_zfree;
if (deflateInit2(ses.newkeys->trans.zstream, Z_DEFAULT_COMPRESSION,
Z_DEFLATED, DROPBEAR_ZLIB_WINDOW_BITS,
@@ -943,6 +951,12 @@ static void read_kex_algos() {
ses.newkeys->trans.algo_comp = s2c_comp_algo->val;
}
+#ifdef DROPBEAR_FUZZ
+ if (fuzz.fuzzing) {
+ fuzz_kex_fakealgos();
+ }
+#endif
+
/* reserved for future extensions */
buf_getint(ses.payload);
diff --git a/common-session.c b/common-session.c
index 99a5470..aa0dddb 100644
--- a/common-session.c
+++ b/common-session.c
@@ -82,14 +82,18 @@ void common_session_init(int sock_in, int sock_out) {
ses.last_packet_time_any_sent = 0;
ses.last_packet_time_keepalive_sent = 0;
- if (pipe(ses.signal_pipe) < 0) {
- dropbear_exit("Signal pipe failed");
+#ifdef DROPBEAR_FUZZ
+ if (!fuzz.fuzzing)
+#endif
+ {
+ if (pipe(ses.signal_pipe) < 0) {
+ dropbear_exit("Signal pipe failed");
+ }
+ setnonblocking(ses.signal_pipe[0]);
+ setnonblocking(ses.signal_pipe[1]);
+ ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[0]);
+ ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[1]);
}
- setnonblocking(ses.signal_pipe[0]);
- setnonblocking(ses.signal_pipe[1]);
-
- ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[0]);
- ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[1]);
ses.writepayload = buf_new(TRANS_MAX_PAYLOAD_LEN);
ses.transseq = 0;
@@ -161,7 +165,12 @@ void session_loop(void(*loophandler)()) {
/* We get woken up when signal handlers write to this pipe.
SIGCHLD in svr-chansession is the only one currently. */
- FD_SET(ses.signal_pipe[0], &readfd);
+#ifdef DROPBEAR_FUZZ
+ if (!fuzz.fuzzing)
+#endif
+ {
+ FD_SET(ses.signal_pipe[0], &readfd);
+ }
ses.channel_signal_pending = 0;
/* set up for channels which can be read/written */
@@ -306,6 +315,16 @@ void session_cleanup() {
buf_free(dequeue(&ses.writequeue));
}
+ m_free(ses.newkeys);
+#ifndef DISABLE_ZLIB
+ if (ses.keys->recv.zstream != NULL) {
+ if (inflateEnd(ses.keys->recv.zstream) == Z_STREAM_ERROR) {
+ dropbear_exit("Crypto error");
+ }
+ m_free(ses.keys->recv.zstream);
+ }
+#endif
+
m_free(ses.remoteident);
m_free(ses.authstate.pw_dir);
m_free(ses.authstate.pw_name);
diff --git a/configure.ac b/configure.ac
index 767bd05..e2ab814 100644
--- a/configure.ac
+++ b/configure.ac
@@ -217,7 +217,20 @@ AC_ARG_ENABLE(shadow,
AC_MSG_NOTICE(Using shadow passwords if available)
]
)
-
+
+AC_ARG_ENABLE(fuzz,
+ [ --enable-fuzz Build fuzzing],
+ [
+ AC_DEFINE(DROPBEAR_FUZZ, 1, Fuzzing)
+ AC_MSG_NOTICE(Enabling fuzzing)
+ DROPBEAR_FUZZ=1
+ ],
+ [
+ DROPBEAR_FUZZ=0
+ ]
+
+)
+AC_SUBST(DROPBEAR_FUZZ)
# Checks for header files.
AC_HEADER_STDC
diff --git a/dbhelpers.c b/dbhelpers.c
index f7461d9..bb9c2fc 100644
--- a/dbhelpers.c
+++ b/dbhelpers.c
@@ -9,16 +9,8 @@ void m_burn(void *data, unsigned int len) {
#elif defined(HAVE_EXPLICIT_BZERO)
explicit_bzero(data, len);
#else
-/* Based on the method in David Wheeler's
- * "Secure Programming for Linux and Unix HOWTO". May not be safe
- * against link-time optimisation. */
- volatile char *p = data;
-
- if (data == NULL)
- return;
- while (len--) {
- *p++ = 0x0;
- }
+ volatile void *p = data;
+ memset(p, 0x0, len);
#endif
}
diff --git a/dbmalloc.c b/dbmalloc.c
new file mode 100644
index 0000000..e887d36
--- /dev/null
+++ b/dbmalloc.c
@@ -0,0 +1,144 @@
+#include "dbmalloc.h"
+#include "dbutil.h"
+
+struct dbmalloc_header {
+ unsigned int epoch;
+ struct dbmalloc_header *prev;
+ struct dbmalloc_header *next;
+};
+
+static void put_alloc(struct dbmalloc_header *header);
+static void remove_alloc(struct dbmalloc_header *header);
+
+/* end of the linked list */
+static struct dbmalloc_header* staple;
+
+unsigned int current_epoch = 0;
+
+void m_malloc_set_epoch(unsigned int epoch) {
+ current_epoch = epoch;
+}
+
+void m_malloc_free_epoch(unsigned int epoch, int dofree) {
+ struct dbmalloc_header* header;
+ struct dbmalloc_header* nextheader = NULL;
+ struct dbmalloc_header* oldstaple = staple;
+ staple = NULL;
+ /* free allocations from this epoch, create a new staple-anchored list from
+ the remainder */
+ for (header = oldstaple; header; header = nextheader)
+ {
+ nextheader = header->next;
+ if (header->epoch == epoch) {
+ if (dofree) {
+ free(header);
+ }
+ } else {
+ header->prev = NULL;
+ header->next = NULL;
+ put_alloc(header);
+ }
+ }
+}
+
+static void put_alloc(struct dbmalloc_header *header) {
+ assert(header->next == NULL);
+ assert(header->prev == NULL);
+ if (staple) {
+ staple->prev = header;
+ }
+ header->next = staple;
+ staple = header;
+}
+
+static void remove_alloc(struct dbmalloc_header *header) {
+ if (header->prev) {
+ header->prev->next = header->next;
+ }
+ if (header->next) {
+ header->next->prev = header->prev;
+ }
+ if (staple == header) {
+ staple = header->next;
+ }
+ header->prev = NULL;
+ header->next = NULL;
+}
+
+static struct dbmalloc_header* get_header(void* ptr) {
+ char* bptr = ptr;
+ return (struct dbmalloc_header*)&bptr[-sizeof(struct dbmalloc_header)];
+}
+
+void * m_malloc(size_t size) {
+ char* mem = NULL;
+ struct dbmalloc_header* header = NULL;
+
+ if (size == 0 || size > 1e9) {
+ dropbear_exit("m_malloc failed");
+ }
+
+ size = size + sizeof(struct dbmalloc_header);
+
+ mem = calloc(1, size);
+ if (mem == NULL) {
+ dropbear_exit("m_malloc failed");
+ }
+ header = (struct dbmalloc_header*)mem;
+ put_alloc(header);
+ header->epoch = current_epoch;
+ return &mem[sizeof(struct dbmalloc_header)];
+}
+
+void * m_calloc(size_t nmemb, size_t size) {
+ if (SIZE_T_MAX / nmemb < size) {
+ dropbear_exit("m_calloc failed");
+ }
+ return m_malloc(nmemb*size);
+}
+
+void * m_realloc(void* ptr, size_t size) {
+ char* mem = NULL;
+ struct dbmalloc_header* header = NULL;
+ if (size == 0 || size > 1e9) {
+ dropbear_exit("m_realloc failed");
+ }
+
+ header = get_header(ptr);
+ remove_alloc(header);
+
+ size = size + sizeof(struct dbmalloc_header);
+ mem = realloc(header, size);
+ if (mem == NULL) {
+ dropbear_exit("m_realloc failed");
+ }
+
+ header = (struct dbmalloc_header*)mem;
+ put_alloc(header);
+ return &mem[sizeof(struct dbmalloc_header)];
+}
+
+void m_free_direct(void* ptr) {
+ struct dbmalloc_header* header = NULL;
+ if (!ptr) {
+ return;
+ }
+ header = get_header(ptr);
+ remove_alloc(header);
+ free(header);
+}
+
+void * m_strdup(const char * str) {
+ char* ret;
+ unsigned int len;
+ len = strlen(str);
+
+ ret = m_malloc(len+1);
+ if (ret == NULL) {
+ dropbear_exit("m_strdup failed");
+ }
+ memcpy(ret, str, len+1);
+ return ret;
+}
+
+
diff --git a/dbmalloc.h b/dbmalloc.h
new file mode 100644
index 0000000..f05f8f5
--- /dev/null
+++ b/dbmalloc.h
@@ -0,0 +1,16 @@
+#ifndef DBMALLOC_H_
+#define DBMALLOC_H_
+
+#include "includes.h"
+
+void * m_malloc(size_t size);
+void * m_calloc(size_t nmemb, size_t size);
+void * m_strdup(const char * str);
+void * m_realloc(void* ptr, size_t size);
+void m_free_direct(void* ptr);
+#define m_free(X) do {m_free_direct(X); (X) = NULL;} while (0)
+
+void m_malloc_set_epoch(unsigned int epoch);
+void m_malloc_free_epoch(unsigned int epoch, int dofree);
+
+#endif /* DBMALLOC_H_ */
diff --git a/dbrandom.c b/dbrandom.c
index 239b044..71f25ce 100644
--- a/dbrandom.c
+++ b/dbrandom.c
@@ -27,7 +27,7 @@
#include "dbutil.h"
#include "bignum.h"
#include "dbrandom.h"
-
+#include "runopts.h"
/* this is used to generate unique output from the same hashpool */
static uint32_t counter = 0;
@@ -145,6 +145,12 @@ void addrandom(unsigned char * buf, unsigned int len)
{
hash_state hs;
+#ifdef DROPBEAR_FUZZ
+ if (fuzz.fuzzing || fuzz.recordf) {
+ return;
+ }
+#endif
+
/* hash in the new seed data */
sha1_init(&hs);
/* existing state (zeroes on startup) */
@@ -157,6 +163,11 @@ void addrandom(unsigned char * buf, unsigned int len)
static void write_urandom()
{
+#ifdef DROPBEAR_FUZZ
+ if (fuzz.fuzzing || fuzz.recordf) {
+ return;
+ }
+#endif
#ifndef DROPBEAR_PRNGD_SOCKET
/* This is opportunistic, don't worry about failure */
unsigned char buf[INIT_SEED_SIZE];
@@ -170,6 +181,18 @@ static void write_urandom()
#endif
}
+#ifdef DROPBEAR_FUZZ
+void fuzz_seed(void) {
+ hash_state hs;
+ sha1_init(&hs);
+ sha1_process(&hs, "fuzzfuzzfuzz", strlen("fuzzfuzzfuzz"));
+ sha1_done(&hs, hashpool);
+
+ counter = 0;
+ donerandinit = 1;
+}
+#endif
+
/* Initialise the prng from /dev/urandom or prngd. This function can
* be called multiple times */
void seedrandom() {
@@ -180,8 +203,15 @@ void seedrandom() {
struct timeval tv;
clock_t clockval;
+#ifdef DROPBEAR_FUZZ
+ if (fuzz.fuzzing || fuzz.recordf) {
+ return;
+ }
+#endif
+
/* hash in the new seed data */
sha1_init(&hs);
+
/* existing state */
sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
diff --git a/dbutil.c b/dbutil.c
index 830e8d2..aadc20e 100644
--- a/dbutil.c
+++ b/dbutil.c
@@ -120,6 +120,13 @@ static void generic_dropbear_exit(int exitcode, const char* format,
_dropbear_log(LOG_INFO, fmtbuf, param);
+#ifdef DROPBEAR_FUZZ
+ // longjmp before cleaning up svr_opts
+ if (fuzz.do_jmp) {
+ longjmp(fuzz.jmp, 1);
+ }
+#endif
+
exit(exitcode);
}
@@ -520,57 +527,26 @@ void m_close(int fd) {
}
}
-void * m_malloc(size_t size) {
-
- void* ret;
-
- if (size == 0) {
- dropbear_exit("m_malloc failed");
- }
- ret = calloc(1, size);
- if (ret == NULL) {
- dropbear_exit("m_malloc failed");
- }
- return ret;
-
-}
-
-void * m_strdup(const char * str) {
- char* ret;
-
- ret = strdup(str);
- if (ret == NULL) {
- dropbear_exit("m_strdup failed");
- }
- return ret;
-}
-
-void * m_realloc(void* ptr, size_t size) {
-
- void *ret;
-
- if (size == 0) {
- dropbear_exit("m_realloc failed");
- }
- ret = realloc(ptr, size);
- if (ret == NULL) {
- dropbear_exit("m_realloc failed");
- }
- return ret;
-}
-
void setnonblocking(int fd) {
TRACE(("setnonblocking: %d", fd))
+#ifdef DROPBEAR_FUZZ
+ if (fuzz.fuzzing) {
+ return;
+ }
+#endif
+
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
if (errno == ENODEV) {
/* Some devices (like /dev/null redirected in)
* can't be set to non-blocking */
TRACE(("ignoring ENODEV for setnonblocking"))
} else {
+ {
dropbear_exit("Couldn't set nonblocking");
}
+ }
}
TRACE(("leave setnonblocking"))
}
@@ -648,7 +624,14 @@ static clockid_t get_linux_clock_source() {
#endif
time_t monotonic_now() {
+#ifdef DROPBEAR_FUZZ
+ if (fuzz.fuzzing) {
+ /* time stands still when fuzzing */
+ return 5;
+ }
+#endif
#if defined(__linux__) && defined(SYS_clock_gettime)
+ {
static clockid_t clock_source = -2;
if (clock_source == -2) {
@@ -665,9 +648,11 @@ time_t monotonic_now() {
}
return ts.tv_sec;
}
+ }
#endif /* linux clock_gettime */
#if defined(HAVE_MACH_ABSOLUTE_TIME)
+ {
/* OS X, see https://developer.apple.com/library/mac/qa/qa1398/_index.html */
static mach_timebase_info_data_t timebase_info;
if (timebase_info.denom == 0) {
@@ -675,6 +660,7 @@ time_t monotonic_now() {
}
return mach_absolute_time() * timebase_info.numer / timebase_info.denom
/ 1e9;
+ }
#endif /* osx mach_absolute_time */
/* Fallback for everything else - this will sometimes go backwards */
diff --git a/dbutil.h b/dbutil.h
index d83b20a..3b3705e 100644
--- a/dbutil.h
+++ b/dbutil.h
@@ -30,6 +30,7 @@
#include "buffer.h"
#include "queue.h"
#include "dbhelpers.h"
+#include "dbmalloc.h"
#ifndef DISABLE_SYSLOG
void startsyslog(const char *ident);
@@ -66,10 +67,6 @@ int buf_readfile(buffer* buf, const char* filename);
int buf_getline(buffer * line, FILE * authfile);
void m_close(int fd);
-void * m_malloc(size_t size);
-void * m_strdup(const char * str);
-void * m_realloc(void* ptr, size_t size);
-#define m_free(X) do {free(X); (X) = NULL;} while (0)
void setnonblocking(int fd);
void disallow_core(void);
int m_str_to_uint(const char* str, unsigned int *val);
diff --git a/debug.h b/debug.h
index 93fbc1f..0cd611d 100644
--- a/debug.h
+++ b/debug.h
@@ -64,6 +64,7 @@
/* you don't need to touch this block */
#if DEBUG_TRACE
+extern int debug_trace;
#define TRACE(X) dropbear_trace X;
#define TRACE2(X) dropbear_trace2 X;
#else /*DEBUG_TRACE*/
diff --git a/fuzz-common.c b/fuzz-common.c
new file mode 100644
index 0000000..ac42def
--- /dev/null
+++ b/fuzz-common.c
@@ -0,0 +1,132 @@
+#include "includes.h"
+
+#include "includes.h"
+#include "fuzz.h"
+#include "dbutil.h"
+#include "runopts.h"
+#include "crypto_desc.h"
+#include "session.h"
+#include "dbrandom.h"
+#include "fuzz-wrapfd.h"
+
+struct dropbear_fuzz_options fuzz;
+
+static void fuzz_dropbear_log(int UNUSED(priority), const char* format, va_list param);
+static void load_fixed_hostkeys(void);
+
+void common_setup_fuzzer(void) {
+ fuzz.fuzzing = 1;
+ fuzz.wrapfds = 1;
+ fuzz.do_jmp = 1;
+ fuzz.input = m_malloc(sizeof(buffer));
+ _dropbear_log = fuzz_dropbear_log;
+ crypto_init();
+}
+
+int fuzzer_set_input(const uint8_t *Data, size_t Size) {
+
+ fuzz.input->data = (unsigned char*)Data;
+ fuzz.input->size = Size;
+ fuzz.input->len = Size;
+ fuzz.input->pos = 0;
+
+ memset(&ses, 0x0, sizeof(ses));
+ memset(&svr_ses, 0x0, sizeof(svr_ses));
+ wrapfd_setup();
+
+ fuzz_seed();
+
+ return DROPBEAR_SUCCESS;
+}
+
+static void fuzz_dropbear_log(int UNUSED(priority), const char* format, va_list param) {
+
+ char printbuf[1024];
+
+#if DEBUG_TRACE
+ if (debug_trace) {
+ vsnprintf(printbuf, sizeof(printbuf), format, param);
+ fprintf(stderr, "%s\n", printbuf);
+ }
+#endif
+}
+
+void svr_setup_fuzzer(void) {
+ struct passwd *pw;
+
+ common_setup_fuzzer();
+
+ _dropbear_exit = svr_dropbear_exit;
+
+ char *argv[] = {
+ "-E",
+ };
+
+ int argc = sizeof(argv) / sizeof(*argv);
+ svr_getopts(argc, argv);
+
+ /* user lookups might be slow, cache it */
+ fuzz.pw_name = m_strdup("person");
+ fuzz.pw_dir = m_strdup("/tmp");
+ fuzz.pw_shell = m_strdup("/bin/zsh");
+ 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);
+}
+
+void fuzz_kex_fakealgos(void) {
+ ses.newkeys->recv.crypt_mode = &dropbear_mode_none;
+}
+
+void fuzz_get_socket_address(int UNUSED(fd), char **local_host, char **local_port,
+ char **remote_host, char **remote_port, int UNUSED(host_lookup)) {
+ if (local_host) {
+ *local_host = m_strdup("fuzzlocalhost");
+ }
+ if (local_port) {
+ *local_port = m_strdup("1234");
+ }
+ if (remote_host) {
+ *remote_host = m_strdup("fuzzremotehost");
+ }
+ if (remote_port) {
+ *remote_port = m_strdup("9876");
+ }
+}
diff --git a/fuzz-harness.c b/fuzz-harness.c
new file mode 100644
index 0000000..8f370ca
--- /dev/null
+++ b/fuzz-harness.c
@@ -0,0 +1,41 @@
+#include "includes.h"
+#include "buffer.h"
+#include "dbutil.h"
+
+extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size);
+
+int main(int argc, char ** argv) {
+ int i;
+ buffer *input = buf_new(100000);
+
+ for (i = 1; i < argc; i++) {
+#if DEBUG_TRACE
+ if (strcmp(argv[i], "-v") == 0) {
+ debug_trace = 1;
+ TRACE(("debug printing on"))
+ }
+#endif
+ }
+
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ // ignore arguments
+ continue;
+ }
+
+ char* fn = argv[i];
+ buf_setlen(input, 0);
+ buf_readfile(input, fn);
+ buf_setpos(input, 0);
+
+ printf("Running %s once \n", fn);
+ LLVMFuzzerTestOneInput(input->data, input->len);
+ printf("Running %s twice \n", fn);
+ LLVMFuzzerTestOneInput(input->data, input->len);
+ printf("Done %s\n", fn);
+ }
+
+ printf("Finished\n");
+
+ return 0;
+}
diff --git a/fuzz-hostkeys.c b/fuzz-hostkeys.c
new file mode 100644
index 0000000..dc1615d
--- /dev/null
+++ b/fuzz-hostkeys.c
@@ -0,0 +1,129 @@
+
+unsigned char keyr[] = {
+ 0x00, 0x00, 0x00, 0x07, 0x73, 0x73, 0x68, 0x2d, 0x72, 0x73, 0x61, 0x00,
+ 0x00, 0x00, 0x03, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0xb1,
+ 0x06, 0x95, 0xc9, 0xa8, 0x38, 0xb9, 0x99, 0x91, 0xb5, 0x17, 0x39, 0xb9,
+ 0xfa, 0xa4, 0x49, 0xf8, 0x2a, 0x4c, 0x14, 0xbd, 0xb6, 0x85, 0xdb, 0x38,
+ 0x99, 0x44, 0xfa, 0xd6, 0xaa, 0x67, 0xef, 0x00, 0x75, 0x2b, 0x6a, 0x5c,
+ 0x1b, 0x50, 0xa8, 0x52, 0xf9, 0xa7, 0xee, 0xe2, 0xb3, 0x80, 0x38, 0x92,
+ 0x20, 0x86, 0x7c, 0xe5, 0x89, 0xb3, 0x06, 0xe4, 0x3b, 0xd1, 0xe2, 0x45,
+ 0xea, 0xc1, 0xd5, 0x8e, 0x05, 0xfb, 0x90, 0x29, 0xd9, 0x41, 0xb3, 0x05,
+ 0x31, 0x1e, 0xcc, 0xeb, 0x89, 0xdc, 0xd2, 0x6a, 0x99, 0x23, 0xbd, 0x7a,
+ 0xbe, 0x8c, 0xe3, 0x3f, 0xa1, 0xe8, 0xf5, 0xb4, 0x51, 0x40, 0xb4, 0xb1,
+ 0xc1, 0x16, 0x9f, 0x07, 0xbb, 0x99, 0xaa, 0x4b, 0x8f, 0x11, 0x19, 0x3c,
+ 0x18, 0xbd, 0x6e, 0xce, 0x14, 0x54, 0x2c, 0x16, 0x4a, 0x5f, 0x89, 0xe4,
+ 0x6b, 0x9f, 0x55, 0x68, 0xcc, 0x09, 0x8e, 0x4b, 0x92, 0xc8, 0x87, 0xfe,
+ 0x09, 0xed, 0x53, 0x6e, 0xff, 0x5f, 0x15, 0x0d, 0x19, 0x9d, 0xa6, 0x54,
+ 0xd2, 0xea, 0x59, 0x4f, 0xa1, 0x7c, 0xf6, 0xf5, 0x7f, 0x32, 0x23, 0xed,
+ 0x72, 0xa8, 0x96, 0x17, 0x87, 0x06, 0xf2, 0xc7, 0xcd, 0xda, 0x4a, 0x10,
+ 0xd1, 0xfd, 0xb8, 0xf1, 0xaf, 0x25, 0x55, 0x32, 0x45, 0x39, 0x95, 0xec,
+ 0x0c, 0xa9, 0xf0, 0x47, 0x8b, 0x66, 0xe0, 0xb7, 0xa2, 0xf6, 0x35, 0x50,
+ 0x27, 0xe7, 0x2f, 0x90, 0x35, 0x5b, 0xd5, 0x62, 0x19, 0xb4, 0x41, 0xd4,
+ 0x52, 0xe7, 0x7f, 0x97, 0xfc, 0x5b, 0x4a, 0x5b, 0x19, 0x06, 0x65, 0x2d,
+ 0x23, 0x29, 0x15, 0x8b, 0x05, 0xaf, 0xbe, 0xd3, 0x4a, 0x27, 0x5b, 0xc9,
+ 0xc0, 0xd0, 0xd2, 0xba, 0x8b, 0x00, 0x7a, 0x2f, 0x39, 0xa0, 0x13, 0xb9,
+ 0xe6, 0xf5, 0x4b, 0x21, 0x54, 0x57, 0xb3, 0xf9, 0x6c, 0x6f, 0xd0, 0x17,
+ 0xf4, 0x50, 0x9d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xf2, 0xda, 0x5f, 0xfb,
+ 0xe2, 0xda, 0xfc, 0xe0, 0xdf, 0x3a, 0x0e, 0x14, 0x18, 0xc1, 0xd9, 0x1f,
+ 0x43, 0xe3, 0x65, 0x3e, 0x07, 0xe7, 0x8d, 0xdc, 0x1d, 0x11, 0xc1, 0xd6,
+ 0xc0, 0xd8, 0xda, 0x53, 0xf5, 0x04, 0x73, 0x51, 0x1b, 0x26, 0xef, 0x4e,
+ 0xf5, 0xce, 0x3d, 0x77, 0x21, 0x94, 0xd0, 0xc7, 0xc1, 0xda, 0x19, 0x7d,
+ 0xf8, 0xc5, 0x4c, 0xc8, 0xee, 0x7d, 0xd1, 0xbb, 0x02, 0x90, 0x2b, 0xff,
+ 0x4e, 0x4d, 0xd7, 0x9d, 0x72, 0x0c, 0x60, 0x0f, 0x4b, 0x83, 0xf5, 0xc2,
+ 0x26, 0xd6, 0x22, 0xb8, 0x60, 0x3a, 0xf9, 0x2f, 0x92, 0x2a, 0x2e, 0x14,
+ 0xa7, 0x56, 0x1c, 0x56, 0x05, 0x41, 0x92, 0xac, 0xb1, 0x4e, 0x44, 0x1e,
+ 0x70, 0x42, 0xda, 0xc7, 0xc8, 0x9c, 0xae, 0x29, 0x2d, 0x0c, 0x3a, 0xff,
+ 0x9b, 0xb6, 0xad, 0xb4, 0xfb, 0x49, 0x28, 0x96, 0x74, 0xf5, 0x94, 0x74,
+ 0xb7, 0x40, 0x93, 0x2b, 0x34, 0x29, 0xd2, 0x8a, 0xf3, 0x99, 0xf9, 0xe9,
+ 0xd8, 0xcc, 0x48, 0x1d, 0x3e, 0xc1, 0x82, 0x35, 0x4f, 0xef, 0xb1, 0x81,
+ 0x3c, 0xe1, 0xa1, 0x03, 0x65, 0xac, 0x21, 0x21, 0x40, 0x61, 0xfb, 0xd3,
+ 0x54, 0xac, 0xa1, 0xf2, 0xf0, 0x61, 0xd9, 0x01, 0x4e, 0xc2, 0x28, 0xb1,
+ 0x7c, 0x27, 0x6e, 0x56, 0x68, 0x69, 0x8f, 0xc5, 0xfd, 0xca, 0x39, 0x6e,
+ 0x22, 0x09, 0xf1, 0xb4, 0xd5, 0xac, 0xb8, 0xe0, 0x1b, 0x21, 0x86, 0xf4,
+ 0xc8, 0x15, 0xc6, 0x1f, 0x21, 0xae, 0xcb, 0xab, 0x5a, 0x09, 0x30, 0x9e,
+ 0xdd, 0x6c, 0x38, 0x59, 0xec, 0x59, 0x3a, 0x08, 0xee, 0x46, 0x7b, 0x78,
+ 0x23, 0xbc, 0xfc, 0xe2, 0xda, 0xe8, 0x1a, 0x65, 0xe6, 0xe0, 0x78, 0xd3,
+ 0xb0, 0x03, 0x2e, 0xf1, 0xb8, 0xca, 0x8e, 0x90, 0x75, 0xaf, 0xf7, 0xa8,
+ 0x48, 0xed, 0x82, 0xc9, 0xcf, 0x44, 0x56, 0xfc, 0x05, 0xfd, 0x6b, 0x00,
+ 0x00, 0x00, 0x81, 0x00, 0xfc, 0x94, 0xdf, 0x42, 0xc7, 0x9a, 0xa2, 0xff,
+ 0x32, 0xdf, 0x06, 0xb6, 0x4d, 0x90, 0x31, 0x28, 0x28, 0xdb, 0x03, 0xf9,
+ 0xa6, 0xb3, 0xa2, 0x91, 0x4c, 0xdf, 0x6e, 0xf6, 0xb9, 0x44, 0x3b, 0xdd,
+ 0x17, 0xc1, 0xc8, 0x1d, 0xd1, 0xc0, 0xc0, 0x30, 0x22, 0xbe, 0x24, 0x2e,
+ 0x0e, 0xdf, 0xe0, 0x18, 0x37, 0x3e, 0xb8, 0x7f, 0xb2, 0x50, 0x34, 0xc4,
+ 0x08, 0x5e, 0x69, 0x1f, 0xd5, 0xc9, 0xce, 0x47, 0x7d, 0x75, 0x5e, 0x3b,
+ 0x87, 0xdd, 0x46, 0x35, 0x01, 0x0f, 0x17, 0x8a, 0xf1, 0xf1, 0xc4, 0xa9,
+ 0x94, 0xa7, 0x6e, 0xce, 0x80, 0xe3, 0x17, 0x2e, 0xb0, 0xef, 0x63, 0xa7,
+ 0x11, 0x86, 0x96, 0x4a, 0x63, 0x2d, 0x9e, 0x92, 0x62, 0x43, 0x43, 0x72,
+ 0xa5, 0xdc, 0xa0, 0xcd, 0x19, 0x93, 0xd7, 0xe0, 0x80, 0x41, 0x27, 0xea,
+ 0xe4, 0xe8, 0xc1, 0x91, 0x9e, 0x13, 0xb3, 0x9c, 0xd1, 0xed, 0xcb, 0xbf,
+ 0x00, 0x00, 0x00, 0x81, 0x00, 0xb3, 0x6b, 0xee, 0xa4, 0x70, 0x4e, 0xfb,
+ 0xf9, 0x7e, 0x2e, 0x74, 0x5d, 0x3e, 0x8b, 0x3f, 0xff, 0x8c, 0xde, 0x68,
+ 0x38, 0xda, 0xce, 0xc0, 0x66, 0x4b, 0xca, 0x35, 0xc3, 0x97, 0xa8, 0xf0,
+ 0x00, 0x8e, 0xb3, 0x46, 0x60, 0xd0, 0x4d, 0x7e, 0x7b, 0xdf, 0x17, 0x7b,
+ 0x2f, 0xc4, 0x16, 0xee, 0x45, 0xdb, 0xa5, 0x5d, 0xc0, 0x72, 0xe9, 0xc6,
+ 0x91, 0x0f, 0xd9, 0x30, 0x74, 0x6c, 0xde, 0x93, 0xb5, 0xb6, 0xaf, 0x52,
+ 0x53, 0x3c, 0x08, 0x55, 0xea, 0xb8, 0x66, 0x07, 0xbe, 0xce, 0xf9, 0x80,
+ 0x8d, 0xe0, 0xca, 0xdc, 0x63, 0xe8, 0x58, 0x94, 0x22, 0x4f, 0x08, 0x66,
+ 0x13, 0x9e, 0x63, 0x2e, 0x92, 0x7a, 0xb6, 0x66, 0x94, 0x9b, 0x71, 0x66,
+ 0xd3, 0x08, 0xc9, 0x89, 0xea, 0x78, 0x35, 0x0d, 0xf2, 0x25, 0x55, 0xd4,
+ 0xb0, 0x9b, 0xea, 0x18, 0x77, 0xf6, 0x25, 0x02, 0xb4, 0x5e, 0x71, 0xea,
+ 0xa3
+};
+unsigned int keyr_len = 805;
+unsigned char keye[] = {
+ 0x00, 0x00, 0x00, 0x13, 0x65, 0x63, 0x64, 0x73, 0x61, 0x2d, 0x73, 0x68,
+ 0x61, 0x32, 0x2d, 0x6e, 0x69, 0x73, 0x74, 0x70, 0x32, 0x35, 0x36, 0x00,
+ 0x00, 0x00, 0x08, 0x6e, 0x69, 0x73, 0x74, 0x70, 0x32, 0x35, 0x36, 0x00,
+ 0x00, 0x00, 0x41, 0x04, 0x0a, 0x00, 0x6c, 0x7c, 0x1c, 0xc4, 0x03, 0x44,
+ 0x46, 0x70, 0xba, 0x00, 0x7c, 0x79, 0x89, 0x7b, 0xc3, 0xd6, 0x32, 0x98,
+ 0x34, 0xe7, 0x1c, 0x60, 0x04, 0x73, 0xd9, 0xb5, 0x7e, 0x94, 0x04, 0x04,
+ 0xea, 0xc8, 0xb8, 0xfb, 0xd4, 0x70, 0x9f, 0x29, 0xa7, 0x8d, 0x9a, 0x64,
+ 0x3a, 0x8c, 0x45, 0x23, 0x37, 0x5a, 0x2b, 0x4f, 0x54, 0x91, 0x80, 0xf1,
+ 0xac, 0x3a, 0xf5, 0x6d, 0xfa, 0xe8, 0x76, 0x20, 0x00, 0x00, 0x00, 0x21,
+ 0x00, 0xc2, 0xaf, 0xbe, 0xdc, 0x06, 0xff, 0x3d, 0x08, 0x9b, 0x73, 0xe0,
+ 0x3c, 0x58, 0x28, 0x70, 0x9b, 0x23, 0x39, 0x51, 0xd7, 0xbc, 0xa7, 0x1a,
+ 0xf5, 0xb4, 0x23, 0xd3, 0xf6, 0x17, 0xa6, 0x9c, 0x02
+};
+unsigned int keye_len = 141;
+unsigned char keyd[] = {
+ 0x00, 0x00, 0x00, 0x07, 0x73, 0x73, 0x68, 0x2d, 0x64, 0x73, 0x73, 0x00,
+ 0x00, 0x00, 0x81, 0x00, 0xb0, 0x02, 0x19, 0x8b, 0xf3, 0x46, 0xf9, 0xc5,
+ 0x47, 0x78, 0x3d, 0x7f, 0x04, 0x10, 0x0a, 0x43, 0x8e, 0x00, 0x9e, 0xa4,
+ 0x30, 0xfd, 0x47, 0xb9, 0x05, 0x9e, 0x95, 0xaa, 0x37, 0x9a, 0x91, 0xbf,
+ 0xf8, 0xb9, 0xe0, 0x8d, 0x97, 0x49, 0x87, 0xe2, 0xe6, 0x90, 0xc1, 0xe4,
+ 0x61, 0x57, 0x77, 0xfd, 0x91, 0x1d, 0xe1, 0x4b, 0xa0, 0xb2, 0xbc, 0xa1,
+ 0x6a, 0x6a, 0xdd, 0x31, 0xda, 0xe7, 0x54, 0x03, 0xfd, 0x48, 0x62, 0x8a,
+ 0x1d, 0x1d, 0xe2, 0x26, 0x76, 0x29, 0x08, 0xab, 0x65, 0x88, 0x74, 0x02,
+ 0x1e, 0xa9, 0x29, 0x1b, 0x69, 0x3b, 0xb4, 0x5f, 0x62, 0x80, 0xa3, 0xa6,
+ 0x4b, 0xc3, 0x0e, 0x89, 0x24, 0xe4, 0x8a, 0x31, 0xae, 0x89, 0x7a, 0x7a,
+ 0x58, 0x44, 0x46, 0x77, 0x62, 0x33, 0xa2, 0x5d, 0x17, 0x0e, 0x0b, 0x64,
+ 0xee, 0x1a, 0x02, 0xbd, 0xf8, 0x27, 0x86, 0xe1, 0x87, 0x92, 0x84, 0xc7,
+ 0x00, 0x00, 0x00, 0x15, 0x00, 0xb3, 0x8b, 0x81, 0x39, 0x9c, 0xba, 0xe1,
+ 0x1d, 0x9a, 0x8b, 0x89, 0xb3, 0x08, 0x9b, 0x12, 0xa8, 0x7b, 0xea, 0x25,
+ 0x8d, 0x00, 0x00, 0x00, 0x80, 0x76, 0x3f, 0x72, 0xb2, 0xef, 0xc3, 0x16,
+ 0xd8, 0x09, 0x36, 0x23, 0x03, 0xf9, 0x5c, 0xac, 0x8b, 0x51, 0x35, 0x2e,
+ 0x36, 0xba, 0x39, 0xd0, 0x57, 0x19, 0x4f, 0x14, 0x8b, 0xea, 0x32, 0xfc,
+ 0x86, 0x41, 0xea, 0x85, 0x71, 0x4d, 0x52, 0x0c, 0xff, 0xc1, 0xd3, 0xd5,
+ 0xcd, 0x2e, 0x37, 0xcc, 0xe1, 0xcc, 0x22, 0x38, 0xa8, 0x47, 0x16, 0x34,
+ 0x3b, 0x32, 0x9c, 0x2f, 0x0f, 0xcd, 0x5f, 0x7f, 0x06, 0x64, 0x89, 0xc5,
+ 0x02, 0x4f, 0x9a, 0x70, 0x11, 0xf0, 0xaa, 0xe1, 0x7a, 0x75, 0x49, 0x8d,
+ 0x0f, 0x8d, 0x5b, 0x54, 0xe2, 0xe7, 0x10, 0x6e, 0xe5, 0xbd, 0xb7, 0x62,
+ 0xf7, 0x40, 0x59, 0x39, 0x31, 0xd9, 0x13, 0x7b, 0xa3, 0xdf, 0x0d, 0x31,
+ 0x52, 0x43, 0xe0, 0xaf, 0x19, 0x12, 0x15, 0x12, 0x34, 0x01, 0x6f, 0xcf,
+ 0x62, 0x21, 0xe4, 0xc8, 0x34, 0x69, 0xc9, 0x85, 0xe3, 0xde, 0xd7, 0x0c,
+ 0xac, 0x00, 0x00, 0x00, 0x80, 0x41, 0xa3, 0xc5, 0xa4, 0x89, 0x86, 0xc8,
+ 0x17, 0xf3, 0x8e, 0x68, 0x72, 0xbe, 0x13, 0x8b, 0x63, 0xe3, 0x07, 0xe3,
+ 0xd5, 0xa4, 0xa2, 0xd3, 0x2c, 0x2f, 0xbe, 0x16, 0x71, 0xc9, 0x79, 0x64,
+ 0x5a, 0x1e, 0x19, 0x82, 0x07, 0xe2, 0x93, 0xda, 0x22, 0xcf, 0x6d, 0xdd,
+ 0x38, 0xcb, 0x6e, 0x6b, 0x0f, 0x95, 0x8d, 0xfa, 0x3f, 0xbb, 0xb8, 0x6a,
+ 0x7d, 0xc3, 0x22, 0x1e, 0x49, 0xcf, 0x98, 0x73, 0x05, 0x5d, 0x97, 0xfa,
+ 0x4c, 0xf2, 0x82, 0x3d, 0x98, 0x61, 0x4e, 0x96, 0x80, 0x26, 0x79, 0xda,
+ 0x24, 0xf8, 0xa1, 0x9c, 0x71, 0x82, 0xe6, 0xc7, 0xdc, 0xc2, 0xa5, 0xd0,
+ 0xf4, 0x36, 0xba, 0xaa, 0xee, 0xd3, 0x43, 0x46, 0x1d, 0xaa, 0x53, 0xea,
+ 0x85, 0x2c, 0x1b, 0xc8, 0x7c, 0x3c, 0xe7, 0x06, 0x44, 0xab, 0x16, 0xad,
+ 0xc6, 0x54, 0x91, 0x9a, 0xb9, 0xc0, 0xeb, 0x93, 0x8c, 0xca, 0x39, 0xcf,
+ 0x6f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x90, 0x26, 0x0a, 0xfc, 0x15, 0x99,
+ 0x7b, 0xac, 0xaa, 0x0c, 0xa2, 0xca, 0x7b, 0xa8, 0xd4, 0xdf, 0x68, 0x56,
+ 0xf9, 0x39
+};
+unsigned int keyd_len = 458;
diff --git a/fuzz-wrapfd.c b/fuzz-wrapfd.c
new file mode 100644
index 0000000..dde8dde
--- /dev/null
+++ b/fuzz-wrapfd.c
@@ -0,0 +1,244 @@
+#define FUZZ_SKIP_WRAP 1
+#include "includes.h"
+#include "fuzz-wrapfd.h"
+
+#include "fuzz.h"
+
+static const int IOWRAP_MAXFD = FD_SETSIZE-1;
+static const int MAX_RANDOM_IN = 50000;
+static const double CHANCE_CLOSE = 1.0 / 300;
+static const double CHANCE_INTR = 1.0 / 200;
+static const double CHANCE_READ1 = 0.6;
+static const double CHANCE_READ2 = 0.3;
+static const double CHANCE_WRITE1 = 0.8;
+static const double CHANCE_WRITE2 = 0.3;
+
+struct fdwrap {
+ enum wrapfd_mode mode;
+ buffer *buf;
+ int closein;
+ int closeout;
+};
+
+static struct fdwrap wrap_fds[IOWRAP_MAXFD+1];
+// for quick selection of in-use descriptors
+static int wrap_used[IOWRAP_MAXFD+1];
+static unsigned int nused;
+static unsigned short rand_state[3];
+
+void wrapfd_setup() {
+ TRACE(("wrapfd_setup"))
+ nused = 0;
+ memset(wrap_fds, 0x0, sizeof(wrap_fds));
+ memset(wrap_used, 0x0, sizeof(wrap_used));
+
+ memset(rand_state, 0x0, sizeof(rand_state));
+ wrapfd_setseed(50);
+}
+
+void wrapfd_setseed(uint32_t seed) {
+ *((uint32_t*)rand_state) = seed;
+ nrand48(rand_state);
+}
+
+void wrapfd_add(int fd, buffer *buf, enum wrapfd_mode mode) {
+ TRACE(("wrapfd_add %d buf %p mode %d", fd, buf, mode))
+ assert(fd >= 0);
+ assert(fd <= IOWRAP_MAXFD);
+ assert(wrap_fds[fd].mode == UNUSED);
+ assert(buf || mode == RANDOMIN);
+
+ wrap_fds[fd].mode = mode;
+ wrap_fds[fd].buf = buf;
+ wrap_fds[fd].closein = 0;
+ wrap_fds[fd].closeout = 0;
+ wrap_used[nused] = fd;
+
+ nused++;
+}
+
+void wrapfd_remove(int fd) {
+ unsigned int i, j;
+ TRACE(("wrapfd_remove %d", fd))
+ assert(fd >= 0);
+ assert(fd <= IOWRAP_MAXFD);
+ assert(wrap_fds[fd].mode != UNUSED);
+ wrap_fds[fd].mode = UNUSED;
+
+
+ // remove from used list
+ for (i = 0, j = 0; i < nused; i++) {
+ if (wrap_used[i] != fd) {
+ wrap_used[j] = wrap_used[i];
+ j++;
+ }
+ }
+ nused--;
+}
+
+int wrapfd_close(int fd) {
+ if (fd >= 0 && fd <= IOWRAP_MAXFD && wrap_fds[fd].mode != UNUSED)
+ {
+ wrapfd_remove(fd);
+ return 0;
+ }
+ else {
+ return close(fd);
+ }
+}
+
+int wrapfd_read(int fd, void *out, size_t count) {
+ size_t maxread;
+ buffer *buf;
+
+ if (!fuzz.wrapfds) {
+ return read(fd, out, count);
+ }
+
+ if (fd < 0 || fd > IOWRAP_MAXFD || wrap_fds[fd].mode == UNUSED) {
+ // XXX - assertion failure?
+ TRACE(("Bad read descriptor %d\n", fd))
+ errno = EBADF;
+ return -1;
+ }
+
+ assert(count != 0);
+
+ if (wrap_fds[fd].closein || erand48(rand_state) < CHANCE_CLOSE) {
+ wrap_fds[fd].closein = 1;
+ errno = ECONNRESET;
+ return -1;
+ }
+
+ if (erand48(rand_state) < CHANCE_INTR) {
+ errno = EINTR;
+ return -1;
+ }
+
+ buf = wrap_fds[fd].buf;
+ if (buf) {
+ maxread = MIN(buf->len - buf->pos, count);
+ // returns 0 if buf is EOF, as intended
+ if (maxread > 0) {
+ maxread = nrand48(rand_state) % maxread + 1;
+ }
+ memcpy(out, buf_getptr(buf, maxread), maxread);
+ buf_incrpos(buf, maxread);
+ return maxread;
+ }
+
+ maxread = MIN(MAX_RANDOM_IN, count);
+ maxread = nrand48(rand_state) % maxread + 1;
+ memset(out, 0xef, maxread);
+ return maxread;
+}
+
+int wrapfd_write(int fd, const void* in, size_t count) {
+ unsigned const volatile char* volin = in;
+ unsigned int i;
+
+ if (!fuzz.wrapfds) {
+ return write(fd, in, count);
+ }
+
+ if (fd < 0 || fd > IOWRAP_MAXFD || wrap_fds[fd].mode == UNUSED) {
+ // XXX - assertion failure?
+ TRACE(("Bad read descriptor %d\n", fd))
+ errno = EBADF;
+ return -1;
+ }
+
+ assert(count != 0);
+
+ // force read to exercise sanitisers
+ for (i = 0; i < count; i++) {
+ (void)volin[i];
+ }
+
+ if (wrap_fds[fd].closeout || erand48(rand_state) < CHANCE_CLOSE) {
+ wrap_fds[fd].closeout = 1;
+ errno = ECONNRESET;
+ return -1;
+ }
+
+ if (erand48(rand_state) < CHANCE_INTR) {
+ errno = EINTR;
+ return -1;
+ }
+
+ return nrand48(rand_state) % (count+1);
+}
+
+int wrapfd_select(int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, struct timeval *timeout) {
+ int i, nset, sel;
+ int ret = 0;
+ int fdlist[IOWRAP_MAXFD+1] = {0};
+
+ if (!fuzz.wrapfds) {
+ return select(nfds, readfds, writefds, exceptfds, timeout);
+ }
+
+ assert(nfds <= IOWRAP_MAXFD+1);
+
+ if (erand48(rand_state) < CHANCE_INTR) {
+ errno = EINTR;
+ return -1;
+ }
+
+ // read
+ if (readfds != NULL && erand48(rand_state) < CHANCE_READ1) {
+ for (i = 0, nset = 0; i < nfds; i++) {
+ if (FD_ISSET(i, readfds)) {
+ assert(wrap_fds[i].mode != UNUSED);
+ fdlist[nset] = i;
+ nset++;
+ }
+ }
+ FD_ZERO(readfds);
+
+ if (nset > 0) {
+ // set one
+ sel = fdlist[nrand48(rand_state) % nset];
+ FD_SET(sel, readfds);
+ ret++;
+
+ if (erand48(rand_state) < CHANCE_READ2) {
+ sel = fdlist[nrand48(rand_state) % nset];
+ if (!FD_ISSET(sel, readfds)) {
+ FD_SET(sel, readfds);
+ ret++;
+ }
+ }
+ }
+ }
+
+ // write
+ if (writefds != NULL && erand48(rand_state) < CHANCE_WRITE1) {
+ for (i = 0, nset = 0; i < nfds; i++) {
+ if (FD_ISSET(i, writefds)) {
+ assert(wrap_fds[i].mode != UNUSED);
+ fdlist[nset] = i;
+ nset++;
+ }
+ }
+ FD_ZERO(writefds);
+
+ // set one
+ if (nset > 0) {
+ sel = fdlist[nrand48(rand_state) % nset];
+ FD_SET(sel, writefds);
+ ret++;
+
+ if (erand48(rand_state) < CHANCE_WRITE2) {
+ sel = fdlist[nrand48(rand_state) % nset];
+ if (!FD_ISSET(sel, writefds)) {
+ FD_SET(sel, writefds);
+ ret++;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
diff --git a/fuzz-wrapfd.h b/fuzz-wrapfd.h
new file mode 100644
index 0000000..04477b9
--- /dev/null
+++ b/fuzz-wrapfd.h
@@ -0,0 +1,25 @@
+#ifndef FUZZ_WRAPFD_H
+#define FUZZ_WRAPFD_H
+
+#include "buffer.h"
+
+enum wrapfd_mode {
+ UNUSED = 0,
+ PLAIN,
+ INPROGRESS,
+ RANDOMIN,
+};
+
+void wrapfd_setup();
+void wrapfd_setseed(uint32_t seed);
+// doesn't take ownership of buf. buf is optional.
+void wrapfd_add(int fd, buffer *buf, enum wrapfd_mode mode);
+
+// called via #defines for read/write/select
+int wrapfd_read(int fd, void *out, size_t count);
+int wrapfd_write(int fd, const void* in, size_t count);
+int wrapfd_select(int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, struct timeval *timeout);
+int wrapfd_close(int fd);
+
+#endif // FUZZ_WRAPFD_H
diff --git a/fuzz.h b/fuzz.h
new file mode 100644
index 0000000..e3bcb7f
--- /dev/null
+++ b/fuzz.h
@@ -0,0 +1,67 @@
+#ifndef DROPBEAR_FUZZ_H
+#define DROPBEAR_FUZZ_H
+
+#include "config.h"
+#ifdef DROPBEAR_FUZZ
+
+#include "includes.h"
+#include "buffer.h"
+#include "algo.h"
+#include "fuzz-wrapfd.h"
+
+// once per process
+void common_setup_fuzzer(void);
+void svr_setup_fuzzer(void);
+
+// must be called once per fuzz iteration.
+// returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE
+int fuzzer_set_input(const uint8_t *Data, size_t Size);
+
+// fuzzer functions that intrude into general code
+void fuzz_kex_fakealgos(void);
+int fuzz_checkpubkey_line(buffer* line, int line_num, char* filename,
+ const char* algo, unsigned int algolen,
+ const unsigned char* keyblob, unsigned int keybloblen);
+extern const char * const * fuzz_signkey_names;
+void fuzz_seed(void);
+void fuzz_get_socket_address(int fd, char **local_host, char **local_port,
+ char **remote_host, char **remote_port, int host_lookup);
+
+// fake IO wrappers
+#ifndef FUZZ_SKIP_WRAP
+#define select(nfds, readfds, writefds, exceptfds, timeout) \
+ wrapfd_select(nfds, readfds, writefds, exceptfds, timeout)
+#define write(fd, buf, count) wrapfd_write(fd, buf, count)
+#define read(fd, buf, count) wrapfd_read(fd, buf, count)
+#define close(fd) wrapfd_close(fd)
+#endif // FUZZ_SKIP_WRAP
+
+struct dropbear_fuzz_options {
+ int fuzzing;
+
+ // to record an unencrypted stream
+ FILE* recordf;
+
+ // fuzzing input
+ buffer *input;
+ struct dropbear_cipher recv_cipher;
+ struct dropbear_hash recv_mac;
+ int wrapfds;
+
+ // dropbear_exit() jumps back
+ int do_jmp;
+ 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 // DROPBEAR_FUZZ
+
+#endif /* DROPBEAR_FUZZ_H */
diff --git a/fuzzer-preauth.c b/fuzzer-preauth.c
new file mode 100644
index 0000000..f7ced9d
--- /dev/null
+++ b/fuzzer-preauth.c
@@ -0,0 +1,53 @@
+#include "fuzz.h"
+#include "session.h"
+#include "fuzz-wrapfd.h"
+#include "debug.h"
+
+static void setup_fuzzer(void) {
+ svr_setup_fuzzer();
+ //debug_trace = 1;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ static int once = 0;
+ if (!once) {
+ setup_fuzzer();
+ once = 1;
+ }
+
+ if (fuzzer_set_input(Data, Size) == DROPBEAR_FAILURE) {
+ return 0;
+ }
+
+ // get prefix. input format is
+ // string prefix
+ // uint32 wrapfd seed
+ // ... to be extended later
+ // [bytes] ssh input stream
+
+ // be careful to avoid triggering buffer.c assertions
+ if (fuzz.input->len < 8) {
+ return 0;
+ }
+ size_t prefix_size = buf_getint(fuzz.input);
+ if (prefix_size != 4) {
+ return 0;
+ }
+ uint32_t wrapseed = buf_getint(fuzz.input);
+ wrapfd_setseed(wrapseed);
+
+ int fakesock = 20;
+ wrapfd_add(fakesock, fuzz.input, PLAIN);
+
+ m_malloc_set_epoch(1);
+ if (setjmp(fuzz.jmp) == 0) {
+ svr_session(fakesock, fakesock);
+ m_malloc_free_epoch(1, 0);
+ } else {
+ m_malloc_free_epoch(1, 1);
+ TRACE(("dropbear_exit longjmped"))
+ // dropbear_exit jumped here
+ }
+
+ return 0;
+}
diff --git a/fuzzer-pubkey.c b/fuzzer-pubkey.c
new file mode 100644
index 0000000..0b7c0f3
--- /dev/null
+++ b/fuzzer-pubkey.c
@@ -0,0 +1,44 @@
+#include "fuzz.h"
+#include "session.h"
+#include "fuzz-wrapfd.h"
+#include "debug.h"
+
+static void setup_fuzzer(void) {
+ common_setup_fuzzer();
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ static int once = 0;
+ if (!once) {
+ setup_fuzzer();
+ once = 1;
+ }
+
+ if (fuzzer_set_input(Data, Size) == DROPBEAR_FAILURE) {
+ return 0;
+ }
+
+ m_malloc_set_epoch(1);
+
+ // choose a keytype based on input
+ uint8_t b = 0;
+ size_t i;
+ for (i = 0; i < Size; i++) {
+ b ^= Data[i];
+ }
+ const char* algoname = fuzz_signkey_names[b%DROPBEAR_SIGNKEY_NUM_NAMED];
+ const char* keyblob = "blob"; // keep short
+
+ if (setjmp(fuzz.jmp) == 0) {
+ fuzz_checkpubkey_line(fuzz.input, 5, "/home/me/authorized_keys",
+ algoname, strlen(algoname),
+ (unsigned char*)keyblob, strlen(keyblob));
+ m_malloc_free_epoch(1, 0);
+ } else {
+ m_malloc_free_epoch(1, 1);
+ TRACE(("dropbear_exit longjmped"))
+ // dropbear_exit jumped here
+ }
+
+ return 0;
+}
diff --git a/fuzzer-verify.c b/fuzzer-verify.c
new file mode 100644
index 0000000..7e23bc9
--- /dev/null
+++ b/fuzzer-verify.c
@@ -0,0 +1,44 @@
+#include "fuzz.h"
+#include "session.h"
+#include "fuzz-wrapfd.h"
+#include "debug.h"
+
+static void setup_fuzzer(void) {
+ common_setup_fuzzer();
+}
+
+static buffer *verifydata;
+
+/* Tests reading a public key and verifying a signature */
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ static int once = 0;
+ if (!once) {
+ setup_fuzzer();
+ verifydata = buf_new(30);
+ buf_putstring(verifydata, "x", 1);
+ once = 1;
+ }
+
+ if (fuzzer_set_input(Data, Size) == DROPBEAR_FAILURE) {
+ return 0;
+ }
+
+ m_malloc_set_epoch(1);
+
+ if (setjmp(fuzz.jmp) == 0) {
+ sign_key *key = new_sign_key();
+ enum signkey_type type = DROPBEAR_SIGNKEY_ANY;
+ if (buf_get_pub_key(fuzz.input, key, &type) == DROPBEAR_SUCCESS) {
+ /* Don't expect random fuzz input to verify */
+ assert(buf_verify(fuzz.input, key, verifydata) == DROPBEAR_FAILURE);
+ }
+ sign_key_free(key);
+ m_malloc_free_epoch(1, 0);
+ } else {
+ m_malloc_free_epoch(1, 1);
+ TRACE(("dropbear_exit longjmped"))
+ // dropbear_exit jumped here
+ }
+
+ return 0;
+}
diff --git a/includes.h b/includes.h
index f91a2c2..0dd1417 100644
--- a/includes.h
+++ b/includes.h
@@ -57,6 +57,7 @@
#include <stdarg.h>
#include <dirent.h>
#include <time.h>
+#include <setjmp.h>
#ifdef HAVE_UTMP_H
#include <utmp.h>
@@ -132,7 +133,6 @@
#include <tommath.h>
#endif
-
#include "compat.h"
#ifndef HAVE_U_INT8_T
@@ -156,6 +156,10 @@ typedef unsigned int u_int32_t;
typedef u_int32_t uint32_t;
#endif /* HAVE_UINT32_T */
+#ifndef SIZE_T_MAX
+#define SIZE_T_MAX ULONG_MAX
+#endif /* SIZE_T_MAX */
+
#ifdef SO_PRIORITY
#include <linux/types.h>
#include <linux/pkt_sched.h>
@@ -163,6 +167,8 @@ typedef u_int32_t uint32_t;
#include "fake-rfc2553.h"
+#include "fuzz.h"
+
#ifndef LOG_AUTHPRIV
#define LOG_AUTHPRIV LOG_AUTH
#endif
diff --git a/libtomcrypt/src/headers/tomcrypt_custom.h b/libtomcrypt/src/headers/tomcrypt_custom.h
index 82cb26e..7f13dbd 100644
--- a/libtomcrypt/src/headers/tomcrypt_custom.h
+++ b/libtomcrypt/src/headers/tomcrypt_custom.h
@@ -4,6 +4,17 @@
/* compile options depend on Dropbear options.h */
#include "options.h"
+void * m_malloc(size_t size);
+/* m_calloc is limited in size, enough for libtomcrypt */
+void * m_calloc(size_t nmemb, size_t size);
+void * m_realloc(void* ptr, size_t size);
+void m_free_direct(void* ptr);
+
+#define XMALLOC m_malloc
+#define XFREE m_free_direct
+#define XREALLOC m_realloc
+#define XCALLOC m_calloc
+
/* macros for various libc functions you can change for embedded targets */
#ifndef XMALLOC
#ifdef malloc
diff --git a/libtommath/tommath_class.h b/libtommath/tommath_class.h
index a4e275a..ea7fa5c 100644
--- a/libtommath/tommath_class.h
+++ b/libtommath/tommath_class.h
@@ -1000,6 +1000,17 @@
#undef BN_MP_TOOM_MUL_C
#undef BN_MP_TOOM_SQR_C
+void * m_malloc(size_t size);
+/* m_calloc is limited in size, enough for libtomcrypt */
+void * m_calloc(size_t nmemb, size_t size);
+void * m_realloc(void* ptr, size_t size);
+void m_free_direct(void* ptr);
+
+#define XMALLOC m_malloc
+#define XFREE m_free_direct
+#define XREALLOC m_realloc
+#define XCALLOC m_calloc
+
/* $Source: /cvs/libtom/libtommath/tommath_class.h,v $ */
/* $Revision: 1.3 $ */
/* $Date: 2005/07/28 11:59:32 $ */
diff --git a/netio.c b/netio.c
index 2e57534..f5e287a 100644
--- a/netio.c
+++ b/netio.c
@@ -195,6 +195,7 @@ void set_connect_fds(fd_set *writefd) {
}
iter = next_iter;
}
+ TRACE(("leave set_connect_fds"))
}
void handle_connect_fds(fd_set *writefd) {
@@ -310,6 +311,12 @@ void set_sock_priority(int sock, enum dropbear_prio prio) {
int so_prio_val = 0;
#endif
+#ifdef DROPBEAR_FUZZ
+ if (fuzz.fuzzing) {
+ TRACE(("fuzzing skips set_sock_prio"))
+ return;
+ }
+#endif
/* Don't log ENOTSOCK errors so that this can harmlessly be called
* on a client '-J' proxy pipe */
@@ -481,6 +488,13 @@ void get_socket_address(int fd, char **local_host, char **local_port,
{
struct sockaddr_storage addr;
socklen_t addrlen;
+
+#if DROPBEAR_FUZZ
+ if (fuzz.fuzzing) {
+ fuzz_get_socket_address(fd, local_host, local_port, remote_host, remote_port, host_lookup);
+ return;
+ }
+#endif
if (local_host || local_port) {
addrlen = sizeof(addr);
diff --git a/packet.c b/packet.c
index 924554d..63c813b 100644
--- a/packet.c
+++ b/packet.c
@@ -35,6 +35,7 @@
#include "auth.h"
#include "channel.h"
#include "netio.h"
+#include "runopts.h"
static int read_packet_init(void);
static void make_mac(unsigned int seqno, const struct key_context_directional * key_state,
@@ -76,6 +77,15 @@ void write_packet() {
/* This may return EAGAIN. The main loop sometimes
calls write_packet() without bothering to test with select() since
it's likely to be necessary */
+#ifdef DROPBEAR_FUZZ
+ 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;
+ }
+ else
+#endif
+ {
written = writev(ses.sock_out, iov, iov_count);
if (written < 0) {
if (errno == EINTR || errno == EAGAIN) {
@@ -85,6 +95,7 @@ void write_packet() {
dropbear_exit("Error writing: %s", strerror(errno));
}
}
+ }
packet_queue_consume(&ses.writequeue, written);
ses.writequeue_len -= written;
@@ -94,6 +105,9 @@ void write_packet() {
}
#else /* No writev () */
+#ifdef DROPBEAR_FUZZ
+ _Static_assert(0, "No fuzzing code for no-writev writes");
+#endif
/* Get the next buffer in the queue of encrypted packets to write*/
writebuf = (buffer*)examine(&ses.writequeue);
@@ -356,6 +370,18 @@ static int checkmac() {
buf_setpos(ses.readbuf, 0);
make_mac(ses.recvseq, &ses.keys->recv, ses.readbuf, contents_len, mac_bytes);
+#ifdef DROPBEAR_FUZZ
+ if (fuzz.fuzzing) {
+ // fail 1 in 2000 times to test error path.
+ // note that mac_bytes is all zero prior to kex, so don't test ==0 !
+ unsigned int value = *((unsigned int*)&mac_bytes);
+ if (value % 2000 == 99) {
+ return DROPBEAR_FAILURE;
+ }
+ return DROPBEAR_SUCCESS;
+ }
+#endif
+
/* compare the hash */
buf_setpos(ses.readbuf, contents_len);
if (constant_time_memcmp(mac_bytes, buf_getptr(ses.readbuf, mac_size), mac_size) != 0) {
diff --git a/signkey.c b/signkey.c
index bc63632..f0c0f97 100644
--- a/signkey.c
+++ b/signkey.c
@@ -102,7 +102,8 @@ enum signkey_type signkey_type_from_name(const char* name, unsigned int namelen)
return DROPBEAR_SIGNKEY_NONE;
}
-/* Returns a pointer to the key part specific to "type" */
+/* Returns a pointer to the key part specific to "type".
+Be sure to check both (ret != NULL) and (*ret != NULL) */
void **
signkey_key_ptr(sign_key *key, enum signkey_type type) {
switch (type) {
@@ -297,7 +298,7 @@ void buf_put_pub_key(buffer* buf, sign_key *key, enum signkey_type type) {
#if DROPBEAR_ECDSA
if (signkey_is_ecdsa(type)) {
ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type);
- if (eck) {
+ if (eck && *eck) {
buf_put_ecdsa_pub_key(pubkeys, *eck);
}
}
@@ -334,7 +335,7 @@ void buf_put_priv_key(buffer* buf, sign_key *key, enum signkey_type type) {
#if DROPBEAR_ECDSA
if (signkey_is_ecdsa(type)) {
ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type);
- if (eck) {
+ if (eck && *eck) {
buf_put_ecdsa_priv_key(buf, *eck);
TRACE(("leave buf_put_priv_key: ecdsa done"))
return;
@@ -498,7 +499,7 @@ void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type type,
#if DROPBEAR_ECDSA
if (signkey_is_ecdsa(type)) {
ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type);
- if (eck) {
+ if (eck && *eck) {
buf_put_ecdsa_sign(sigblob, *eck, data_buf);
}
}
@@ -549,7 +550,7 @@ int buf_verify(buffer * buf, sign_key *key, buffer *data_buf) {
#if DROPBEAR_ECDSA
if (signkey_is_ecdsa(type)) {
ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type);
- if (eck) {
+ if (eck && *eck) {
return buf_ecdsa_verify(buf, *eck, data_buf);
}
}
@@ -579,6 +580,10 @@ int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen,
/* now we have the actual data */
len = line->len - line->pos;
+ if (len == 0) {
+ /* base64_decode doesn't like NULL argument */
+ return DROPBEAR_FAILURE;
+ }
decodekeylen = len * 2; /* big to be safe */
decodekey = buf_new(decodekeylen);
@@ -622,3 +627,8 @@ out:
return ret;
}
#endif
+
+#ifdef DROPBEAR_FUZZ
+const char * const * fuzz_signkey_names = signkey_names;
+
+#endif
diff --git a/svr-auth.c b/svr-auth.c
index 4dc280c..ac1086d 100644
--- a/svr-auth.c
+++ b/svr-auth.c
@@ -358,7 +358,11 @@ void send_msg_userauth_failure(int partial, int incrfail) {
genrandom((unsigned char*)&delay, sizeof(delay));
/* We delay for 300ms +- 50ms */
delay = 250000 + (delay % 100000);
- usleep(delay);
+#ifndef DROPBEAR_FUZZ
+ if (!fuzz.fuzzing) {
+ usleep(delay);
+ }
+#endif
ses.authstate.failcount++;
}
diff --git a/svr-authpubkey.c b/svr-authpubkey.c
index acc660d..fbee63f 100644
--- a/svr-authpubkey.c
+++ b/svr-authpubkey.c
@@ -188,6 +188,107 @@ static void send_msg_userauth_pk_ok(char* algo, unsigned int algolen,
}
+static int checkpubkey_line(buffer* line, int line_num, char* filename,
+ const char* algo, unsigned int algolen,
+ const unsigned char* keyblob, unsigned int keybloblen) {
+ buffer *options_buf = NULL;
+ unsigned int pos, len;
+ int ret = DROPBEAR_FAILURE;
+
+ if (line->len < MIN_AUTHKEYS_LINE || line->len > MAX_AUTHKEYS_LINE) {
+ TRACE(("checkpubkey: bad line length %d", line->len))
+ return DROPBEAR_FAILURE;
+ }
+
+ /* compare the algorithm. +3 so we have enough bytes to read a space and some base64 characters too. */
+ if (line->pos + algolen+3 > line->len) {
+ goto out;
+ }
+ /* check the key type */
+ if (strncmp((const char *) buf_getptr(line, algolen), algo, algolen) != 0) {
+ int is_comment = 0;
+ unsigned char *options_start = NULL;
+ int options_len = 0;
+ int escape, quoted;
+
+ /* skip over any comments or leading whitespace */
+ while (line->pos < line->len) {
+ const char c = buf_getbyte(line);
+ if (c == ' ' || c == '\t') {
+ continue;
+ } else if (c == '#') {
+ is_comment = 1;
+ break;
+ }
+ buf_incrpos(line, -1);
+ break;
+ }
+ if (is_comment) {
+ /* next line */
+ goto out;
+ }
+
+ /* remember start of options */
+ options_start = buf_getptr(line, 1);
+ quoted = 0;
+ escape = 0;
+ options_len = 0;
+
+ /* figure out where the options are */
+ while (line->pos < line->len) {
+ const char c = buf_getbyte(line);
+ if (!quoted && (c == ' ' || c == '\t')) {
+ break;
+ }
+ escape = (!escape && c == '\\');
+ if (!escape && c == '"') {
+ quoted = !quoted;
+ }
+ options_len++;
+ }
+ options_buf = buf_new(options_len);
+ buf_putbytes(options_buf, options_start, options_len);
+
+ /* compare the algorithm. +3 so we have enough bytes to read a space and some base64 characters too. */
+ if (line->pos + algolen+3 > line->len) {
+ goto out;
+ }
+ if (strncmp((const char *) buf_getptr(line, algolen), algo, algolen) != 0) {
+ goto out;
+ }
+ }
+ buf_incrpos(line, algolen);
+
+ /* check for space (' ') character */
+ if (buf_getbyte(line) != ' ') {
+ TRACE(("checkpubkey: space character expected, isn't there"))
+ goto out;
+ }
+
+ /* truncate the line at the space after the base64 data */
+ pos = line->pos;
+ for (len = 0; line->pos < line->len; len++) {
+ if (buf_getbyte(line) == ' ') break;
+ }
+ buf_setpos(line, pos);
+ buf_setlen(line, line->pos + len);
+
+ TRACE(("checkpubkey: line pos = %d len = %d", line->pos, line->len))
+
+ ret = cmp_base64_key(keyblob, keybloblen, (const unsigned char *) algo, algolen, line, NULL);
+
+ if (ret == DROPBEAR_SUCCESS && options_buf) {
+ ret = svr_add_pubkey_options(options_buf, line_num, filename);
+ }
+
+out:
+ if (options_buf) {
+ buf_free(options_buf);
+ }
+ return ret;
+}
+
+
/* Checks whether a specified publickey (and associated algorithm) is an
* acceptable key for authentication */
/* Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */
@@ -198,8 +299,7 @@ static int checkpubkey(char* algo, unsigned int algolen,
char * filename = NULL;
int ret = DROPBEAR_FAILURE;
buffer * line = NULL;
- unsigned int len, pos;
- buffer * options_buf = NULL;
+ unsigned int len;
int line_num;
uid_t origuid;
gid_t origgid;
@@ -254,12 +354,6 @@ static int checkpubkey(char* algo, unsigned int algolen,
/* iterate through the lines */
do {
- /* new line : potentially new options */
- if (options_buf) {
- buf_free(options_buf);
- options_buf = NULL;
- }
-
if (buf_getline(line, authfile) == DROPBEAR_FAILURE) {
/* EOF reached */
TRACE(("checkpubkey: authorized_keys EOF reached"))
@@ -267,91 +361,8 @@ static int checkpubkey(char* algo, unsigned int algolen,
}
line_num++;
- if (line->len < MIN_AUTHKEYS_LINE) {
- TRACE(("checkpubkey: line too short"))
- continue; /* line is too short for it to be a valid key */
- }
-
- /* check the key type - will fail if there are options */
- TRACE(("a line!"))
-
- if (strncmp((const char *) buf_getptr(line, algolen), algo, algolen) != 0) {
- int is_comment = 0;
- unsigned char *options_start = NULL;
- int options_len = 0;
- int escape, quoted;
-
- /* skip over any comments or leading whitespace */
- while (line->pos < line->len) {
- const char c = buf_getbyte(line);
- if (c == ' ' || c == '\t') {
- continue;
- } else if (c == '#') {
- is_comment = 1;
- break;
- }
- buf_incrpos(line, -1);
- break;
- }
- if (is_comment) {
- /* next line */
- continue;
- }
-
- /* remember start of options */
- options_start = buf_getptr(line, 1);
- quoted = 0;
- escape = 0;
- options_len = 0;
-
- /* figure out where the options are */
- while (line->pos < line->len) {
- const char c = buf_getbyte(line);
- if (!quoted && (c == ' ' || c == '\t')) {
- break;
- }
- escape = (!escape && c == '\\');
- if (!escape && c == '"') {
- quoted = !quoted;
- }
- options_len++;
- }
- options_buf = buf_new(options_len);
- buf_putbytes(options_buf, options_start, options_len);
-
- /* compare the algorithm. +3 so we have enough bytes to read a space and some base64 characters too. */
- if (line->pos + algolen+3 > line->len) {
- continue;
- }
- if (strncmp((const char *) buf_getptr(line, algolen), algo, algolen) != 0) {
- continue;
- }
- }
- buf_incrpos(line, algolen);
-
- /* check for space (' ') character */
- if (buf_getbyte(line) != ' ') {
- TRACE(("checkpubkey: space character expected, isn't there"))
- continue;
- }
-
- /* truncate the line at the space after the base64 data */
- pos = line->pos;
- for (len = 0; line->pos < line->len; len++) {
- if (buf_getbyte(line) == ' ') break;
- }
- buf_setpos(line, pos);
- buf_setlen(line, line->pos + len);
-
- TRACE(("checkpubkey: line pos = %d len = %d", line->pos, line->len))
-
- ret = cmp_base64_key(keyblob, keybloblen, (const unsigned char *) algo, algolen, line, NULL);
-
- if (ret == DROPBEAR_SUCCESS && options_buf) {
- ret = svr_add_pubkey_options(options_buf, line_num, filename);
- }
-
- if (ret == DROPBEAR_SUCCESS) {
+ if (checkpubkey_line(line, line_num, filename,
+ algo, algolen, keyblob, keybloblen) == DROPBEAR_SUCCESS) {
break;
}
@@ -367,9 +378,6 @@ out:
buf_free(line);
}
m_free(filename);
- if (options_buf) {
- buf_free(options_buf);
- }
TRACE(("leave checkpubkey: ret=%d", ret))
return ret;
}
@@ -465,4 +473,12 @@ static int checkfileperm(char * filename) {
return DROPBEAR_SUCCESS;
}
+#ifdef DROPBEAR_FUZZ
+int fuzz_checkpubkey_line(buffer* line, int line_num, char* filename,
+ const char* algo, unsigned int algolen,
+ const unsigned char* keyblob, unsigned int keybloblen) {
+ return checkpubkey_line(line, line_num, filename, algo, algolen, keyblob, keybloblen);
+}
+#endif
+
#endif
diff --git a/svr-runopts.c b/svr-runopts.c
index dea4a55..93058f7 100644
--- a/svr-runopts.c
+++ b/svr-runopts.c
@@ -480,6 +480,7 @@ static void addhostkey(const char *keyfile) {
svr_opts.num_hostkey_files++;
}
+
void load_all_hostkeys() {
int i;
int disable_unset_keys = 1;
diff --git a/svr-session.c b/svr-session.c
index 297d07f..c0dba5d 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.do_jmp) {
+ longjmp(fuzz.jmp, 1);
+ }
+#endif
+
if (svr_opts.hostkey) {
sign_key_free(svr_opts.hostkey);
svr_opts.hostkey = NULL;
@@ -191,6 +199,7 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) {
m_free(svr_opts.ports[i]);
}
+
exit(exitcode);
}
@@ -236,7 +245,9 @@ void svr_dropbear_log(int priority, const char* format, va_list param) {
static void svr_remoteclosed() {
m_close(ses.sock_in);
- m_close(ses.sock_out);
+ if (ses.sock_in != ses.sock_out) {
+ m_close(ses.sock_out);
+ }
ses.sock_in = -1;
ses.sock_out = -1;
dropbear_close("Exited normally");