Merge fuzzing branch

This commit is contained in:
Matt Johnston 2018-02-28 22:12:05 +08:00
commit a1dcaf82a0
36 changed files with 1314 additions and 72 deletions

2
.hgignore Normal file
View File

@ -0,0 +1,2 @@
.*\.o
.*~

View File

@ -36,6 +36,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 \
dbmalloc.o \
gensignkey.o gendss.o genrsa.o
SVROBJS=svr-kex.o svr-auth.o sshpty.o \
@ -59,11 +60,23 @@ CONVERTOBJS=dropbearconvert.o keyimport.o
SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o compat.o
dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS)
dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS)
dropbearkeyobjs=$(COMMONOBJS) $(KEYOBJS)
dropbearconvertobjs=$(COMMONOBJS) $(CONVERTOBJS)
scpobjs=$(SCPOBJS)
ifeq (@DROPBEAR_FUZZ@, 1)
allobjs = $(COMMONOBJS) fuzz-common.o fuzz-wrapfd.o $(CLISVROBJS) $(CLIOBJS) $(SVROBJS)
allobjs:=$(subst svr-main.o, ,$(allobjs))
allobjs:=$(subst cli-main.o, ,$(allobjs))
allobjs:=$(sort $(allobjs))
dropbearobjs=$(allobjs) svr-main.o
dbclientobjs=$(allobjs) cli-main.o
dropbearkeyobjs=$(allobjs) $(KEYOBJS)
dropbearconvertobjs=$(allobjs) $(CONVERTOBJS)
else
dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS)
dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS)
dropbearkeyobjs=$(COMMONOBJS) $(KEYOBJS)
dropbearconvertobjs=$(COMMONOBJS) $(CONVERTOBJS)
scpobjs=$(SCPOBJS)
endif
VPATH=@srcdir@
srcdir=@srcdir@
@ -180,7 +193,7 @@ dbclient: $(HEADERS) $(LIBTOM_DEPS) Makefile
$(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS)
dropbearkey dropbearconvert: $(HEADERS) $(LIBTOM_DEPS) Makefile
$(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS)
$(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS)
# scp doesn't use the libs so is special.
scp: $(SCPOBJS) $(HEADERS) Makefile
@ -236,3 +249,53 @@ distclean: clean tidy
tidy:
-rm -f *~ *.gcov */*~
## Fuzzing targets
# list of fuzz targets
FUZZ_TARGETS=fuzzer-preauth fuzzer-pubkey fuzzer-verify fuzzer-preauth_nomaths
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 fuzz-targetsk FUZZLIB=-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-preauth_nomaths: fuzzer-preauth_nomaths.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

View File

@ -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';

View File

@ -48,7 +48,6 @@ static void read_kex_algos(void);
/* helper function for gen_new_keys */
static void hashkeys(unsigned char *out, unsigned int outlen,
const hash_state * hs, const unsigned char X);
static void finish_kexhashbuf(void);
/* Send our list of algorithms we can use */
@ -391,6 +390,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 +406,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 +430,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,
@ -761,8 +768,7 @@ void kexcurve25519_comb_key(const struct kex_curve25519_param *param, const buff
#endif /* DROPBEAR_CURVE25519 */
static void finish_kexhashbuf(void) {
void finish_kexhashbuf(void) {
hash_state hs;
const struct ltc_hash_descriptor *hash_desc = ses.newkeys->algo_kex->hash_desc;
@ -943,6 +949,12 @@ static void read_kex_algos() {
ses.newkeys->trans.algo_comp = s2c_comp_algo->val;
}
#if DROPBEAR_FUZZ
if (fuzz.fuzzing) {
fuzz_kex_fakealgos();
}
#endif
/* reserved for future extensions */
buf_getint(ses.payload);

View File

@ -75,14 +75,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 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]);
}
ses.writepayload = buf_new(TRANS_MAX_PAYLOAD_LEN);
ses.transseq = 0;
@ -154,7 +158,12 @@ void session_loop(void(*loophandler)(void)) {
/* We get woken up when signal handlers write to this pipe.
SIGCHLD in svr-chansession is the only one currently. */
#if DROPBEAR_FUZZ
if (!fuzz.fuzzing)
#endif
{
FD_SET(ses.signal_pipe[0], &readfd);
}
/* set up for channels which can be read/written */
setchannelfds(&readfd, &writefd, writequeue_has_space);
@ -298,6 +307,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);

View File

@ -9,6 +9,12 @@ AC_PREREQ(2.59)
AC_INIT
AC_CONFIG_SRCDIR(buffer.c)
# Record which revision is being built
if which -s hg && test -d "$srcdir/.hg"; then
hgrev=`hg id -i -R "$srcdir"`
AC_MSG_NOTICE([Source directory Mercurial base revision $hgrev])
fi
# Checks for programs.
AC_PROG_CC
@ -315,7 +321,21 @@ AC_ARG_ENABLE(shadow,
AC_MSG_NOTICE(Using shadow passwords if available)
]
)
AC_ARG_ENABLE(fuzz,
[ --enable-fuzz Build fuzzing. Not recommended for deployment.],
[
AC_DEFINE(DROPBEAR_FUZZ, 1, Fuzzing)
AC_MSG_NOTICE(Enabling fuzzing)
DROPBEAR_FUZZ=1
],
[
AC_DEFINE(DROPBEAR_FUZZ, 0, Fuzzing)
DROPBEAR_FUZZ=0
]
)
AC_SUBST(DROPBEAR_FUZZ)
# Checks for header files.
AC_HEADER_STDC

View File

@ -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
}

144
dbmalloc.c Normal file
View File

@ -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;
}

16
dbmalloc.h Normal file
View File

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

View File

@ -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(const unsigned char * buf, unsigned int len)
{
hash_state hs;
#if DROPBEAR_FUZZ
if (fuzz.fuzzing) {
return;
}
#endif
/* hash in the new seed data */
sha1_init(&hs);
/* existing state (zeroes on startup) */
@ -157,6 +163,11 @@ void addrandom(const unsigned char * buf, unsigned int len)
static void write_urandom()
{
#if DROPBEAR_FUZZ
if (fuzz.fuzzing) {
return;
}
#endif
#if !DROPBEAR_USE_PRNGD
/* This is opportunistic, don't worry about failure */
unsigned char buf[INIT_SEED_SIZE];
@ -170,6 +181,18 @@ static void write_urandom()
#endif
}
#if 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;
#if DROPBEAR_FUZZ
if (fuzz.fuzzing) {
return;
}
#endif
/* hash in the new seed data */
sha1_init(&hs);
/* existing state */
sha1_process(&hs, (void*)hashpool, sizeof(hashpool));

View File

@ -120,6 +120,13 @@ static void generic_dropbear_exit(int exitcode, const char* format,
_dropbear_log(LOG_INFO, fmtbuf, param);
#if DROPBEAR_FUZZ
/* longjmp before cleaning up svr_opts */
if (fuzz.do_jmp) {
longjmp(fuzz.jmp, 1);
}
#endif
exit(exitcode);
}
@ -392,6 +399,7 @@ void printhex(const char * label, const unsigned char * buf, int len) {
void printmpint(const char *label, mp_int *mp) {
buffer *buf = buf_new(1000);
buf_putmpint(buf, mp);
fprintf(stderr, "%d bits ", mp_count_bits(mp));
printhex(label, buf->data, buf->len);
buf_free(buf);
@ -520,57 +528,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))
#if 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"))
}
@ -652,7 +629,14 @@ static clockid_t get_linux_clock_source() {
#endif
time_t monotonic_now() {
#if 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) {
@ -669,9 +653,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) {
@ -679,6 +665,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 */

View File

@ -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);

View File

@ -39,7 +39,9 @@
/*#define CHECKCLEARTOWRITE() assert(ses.writepayload->len == 0 && \
ses.writepayload->pos == 0)*/
#ifndef CHECKCLEARTOWRITE
#define CHECKCLEARTOWRITE()
#endif
/* Define this, compile with -pg and set GMON_OUT_PREFIX=gmon to get gmon
* output when Dropbear forks. This will allow it gprof to be used.
@ -54,6 +56,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*/

25
dss.c
View File

@ -73,6 +73,18 @@ int buf_get_dss_pub_key(buffer* buf, dropbear_dss_key *key) {
goto out;
}
/* test 1 < g < p */
if (mp_cmp_d(key->g, 1) != MP_GT) {
dropbear_log(LOG_WARNING, "Bad DSS g");
ret = DROPBEAR_FAILURE;
goto out;
}
if (mp_cmp(key->g, key->p) != MP_LT) {
dropbear_log(LOG_WARNING, "Bad DSS g");
ret = DROPBEAR_FAILURE;
goto out;
}
ret = DROPBEAR_SUCCESS;
TRACE(("leave buf_get_dss_pub_key: success"))
out:
@ -172,6 +184,13 @@ int buf_dss_verify(buffer* buf, const dropbear_dss_key *key, const buffer *data_
goto out;
}
#if DEBUG_DSS_VERIFY
printmpint("dss verify p", key->p);
printmpint("dss verify q", key->q);
printmpint("dss verify g", key->g);
printmpint("dss verify y", key->y);
#endif
/* hash the data */
sha1_init(&hs);
sha1_process(&hs, data_buf->data, data_buf->len);
@ -181,6 +200,9 @@ int buf_dss_verify(buffer* buf, const dropbear_dss_key *key, const buffer *data_
/* w = (s')-1 mod q */
/* let val1 = s' */
bytes_to_mp(&val1, (const unsigned char*) &string[SHA1_HASH_SIZE], SHA1_HASH_SIZE);
#if DEBUG_DSS_VERIFY
printmpint("dss verify s'", &val1);
#endif
if (mp_cmp(&val1, key->q) != MP_LT) {
TRACE(("verify failed, s' >= q"))
@ -198,6 +220,9 @@ int buf_dss_verify(buffer* buf, const dropbear_dss_key *key, const buffer *data_
/* u1 = ((SHA(M')w) mod q */
/* let val1 = SHA(M') = msghash */
bytes_to_mp(&val1, msghash, SHA1_HASH_SIZE);
#if DEBUG_DSS_VERIFY
printmpint("dss verify r'", &val1);
#endif
/* let val3 = u1 = ((SHA(M')w) mod q */
if (mp_mulmod(&val1, &val2, key->q, &val3) != MP_OKAY) {

190
fuzz-common.c Normal file
View File

@ -0,0 +1,190 @@
#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 "bignum.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 fuzz_common_setup(void) {
fuzz.fuzzing = 1;
fuzz.wrapfds = 1;
fuzz.do_jmp = 1;
fuzz.input = m_malloc(sizeof(buffer));
_dropbear_log = fuzz_dropbear_log;
crypto_init();
/* let any messages get flushed */
setlinebuf(stdout);
}
int fuzz_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;
}
#if DEBUG_TRACE
static void fuzz_dropbear_log(int UNUSED(priority), const char* format, va_list param) {
if (debug_trace) {
char printbuf[1024];
vsnprintf(printbuf, sizeof(printbuf), format, param);
fprintf(stderr, "%s\n", printbuf);
}
}
#else
static void fuzz_dropbear_log(int UNUSED(priority), const char* UNUSED(format), va_list UNUSED(param)) {
/* No print */
}
#endif /* DEBUG_TRACE */
void fuzz_svr_setup(void) {
fuzz_common_setup();
_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");
}
}
/* cut down version of svr_send_msg_kexdh_reply() that skips slow maths. Still populates structures */
void fuzz_fake_send_kexdh_reply(void) {
assert(!ses.dh_K);
m_mp_alloc_init_multi(&ses.dh_K, NULL);
mp_set_int(ses.dh_K, 12345678);
finish_kexhashbuf();
}
int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths) {
static int once = 0;
if (!once) {
fuzz_svr_setup();
fuzz.skip_kexmaths = skip_kexmaths;
once = 1;
}
if (fuzz_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;
}

41
fuzz-harness.c Normal file
View File

@ -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;
}

129
fuzz-hostkeys.c Normal file
View File

@ -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;

244
fuzz-wrapfd.c Normal file
View File

@ -0,0 +1,244 @@
#define FUZZ_SKIP_WRAP 1
#include "includes.h"
#include "fuzz-wrapfd.h"
#include "fuzz.h"
#define 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) {
memcpy(rand_state, &seed, sizeof(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];
memset(fdlist, 0x0, sizeof(fdlist));
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;
}

25
fuzz-wrapfd.h Normal file
View File

@ -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

71
fuzz.h Normal file
View File

@ -0,0 +1,71 @@
#ifndef DROPBEAR_FUZZ_H
#define DROPBEAR_FUZZ_H
#include "config.h"
#if DROPBEAR_FUZZ
#include "includes.h"
#include "buffer.h"
#include "algo.h"
#include "fuzz-wrapfd.h"
// once per process
void fuzz_common_setup(void);
void fuzz_svr_setup(void);
// must be called once per fuzz iteration.
// returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE
int fuzz_set_input(const uint8_t *Data, size_t Size);
int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths);
// 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);
void fuzz_fake_send_kexdh_reply(void);
// 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;
// fuzzing input
buffer *input;
struct dropbear_cipher recv_cipher;
struct dropbear_hash recv_mac;
int wrapfds;
// whether to skip slow bignum maths
int skip_kexmaths;
// 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 */

6
fuzzer-preauth.c Normal file
View File

@ -0,0 +1,6 @@
#include "fuzz.h"
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
return fuzz_run_preauth(Data, Size, 0);
}

6
fuzzer-preauth_nomaths.c Normal file
View File

@ -0,0 +1,6 @@
#include "fuzz.h"
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
return fuzz_run_preauth(Data, Size, 1);
}

44
fuzzer-pubkey.c Normal file
View File

@ -0,0 +1,44 @@
#include "fuzz.h"
#include "session.h"
#include "fuzz-wrapfd.h"
#include "debug.h"
static void setup_fuzzer(void) {
fuzz_common_setup();
}
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
static int once = 0;
if (!once) {
setup_fuzzer();
once = 1;
}
if (fuzz_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;
}

64
fuzzer-verify.c Normal file
View File

@ -0,0 +1,64 @@
#include "fuzz.h"
#include "session.h"
#include "fuzz-wrapfd.h"
#include "debug.h"
static void setup_fuzzer(void) {
fuzz_common_setup();
}
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 (fuzz_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) {
if (buf_verify(fuzz.input, key, verifydata) == DROPBEAR_SUCCESS) {
/* The fuzzer is capable of generating keys with a signature to match.
We don't want false positives if the key is bogus, since a client/server
wouldn't be trusting a bogus key anyway */
int boguskey = 0;
if (type == DROPBEAR_SIGNKEY_DSS) {
/* So far have seen dss keys with bad p/q/g domain parameters */
int pprime, qprime;
assert(mp_prime_is_prime(key->dsskey->p, 5, &pprime) == MP_OKAY);
assert(mp_prime_is_prime(key->dsskey->q, 18, &qprime) == MP_OKAY);
boguskey = !(pprime && qprime);
/* Could also check g**q mod p == 1 */
}
if (!boguskey) {
printf("Random key/signature managed to verify!\n");
abort();
}
}
}
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;
}

View File

@ -56,6 +56,7 @@
#include <stdarg.h>
#include <dirent.h>
#include <time.h>
#include <setjmp.h>
#ifdef HAVE_UTMP_H
#include <utmp.h>
@ -131,7 +132,6 @@
#include <tommath.h>
#endif
#include "compat.h"
#ifndef HAVE_U_INT8_T
@ -155,6 +155,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 HAVE_LINUX_PKT_SCHED_H
#include <linux/types.h>
#include <linux/pkt_sched.h>
@ -162,6 +166,8 @@ typedef u_int32_t uint32_t;
#include "fake-rfc2553.h"
#include "fuzz.h"
#ifndef LOG_AUTHPRIV
#define LOG_AUTHPRIV LOG_AUTH
#endif

1
kex.h
View File

@ -34,6 +34,7 @@ void recv_msg_kexinit(void);
void send_msg_newkeys(void);
void recv_msg_newkeys(void);
void kexfirstinitialise(void);
void finish_kexhashbuf(void);
struct kex_dh_param *gen_kexdh_param(void);
void free_kexdh_param(struct kex_dh_param *param);

View File

@ -12,6 +12,17 @@
#include "tomcrypt_dropbear.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
#define XMALLOC malloc

View File

@ -1062,6 +1062,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$ */
/* $Revision$ */
/* $Date$ */

14
netio.c
View File

@ -245,6 +245,7 @@ void set_connect_fds(fd_set *writefd) {
}
iter = next_iter;
}
TRACE(("leave set_connect_fds"))
}
void handle_connect_fds(const fd_set *writefd) {
@ -360,6 +361,12 @@ void set_sock_priority(int sock, enum dropbear_prio prio) {
int so_prio_val = 0;
#endif
#if 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 */
@ -584,6 +591,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);

View File

@ -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 */
#if 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 () */
#if 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);
@ -352,6 +366,18 @@ static int checkmac() {
buf_setpos(ses.readbuf, 0);
make_mac(ses.recvseq, &ses.keys->recv, ses.readbuf, contents_len, mac_bytes);
#if 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) {

View File

@ -580,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);
@ -623,3 +627,8 @@ out:
return ret;
}
#endif
#if DROPBEAR_FUZZ
const char * const * fuzz_signkey_names = signkey_names;
#endif

View File

@ -395,7 +395,12 @@ void send_msg_userauth_failure(int partial, int incrfail) {
genrandom((unsigned char*)&delay, sizeof(delay));
/* We delay for 300ms +- 50ms */
delay = 250000 + (delay % 100000);
#if DROPBEAR_FUZZ
if (!fuzz.fuzzing)
#endif
{
usleep(delay);
}
ses.authstate.failcount++;
}

View File

@ -473,4 +473,12 @@ static int checkfileperm(char * filename) {
return DROPBEAR_SUCCESS;
}
#if 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

View File

@ -179,6 +179,13 @@ static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs) {
}
#endif
#if DROPBEAR_FUZZ
if (fuzz.fuzzing && fuzz.skip_kexmaths) {
fuzz_fake_send_kexdh_reply();
return;
}
#endif
buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY);
buf_put_pub_key(ses.writepayload, svr_opts.hostkey,
ses.newkeys->algo_hostkey);

View File

@ -523,6 +523,7 @@ static void addhostkey(const char *keyfile) {
svr_opts.num_hostkey_files++;
}
void load_all_hostkeys() {
int i;
int disable_unset_keys = 1;

View File

@ -40,6 +40,7 @@
#include "auth.h"
#include "runopts.h"
#include "crypto_desc.h"
#include "fuzz.h"
static void svr_remoteclosed(void);
static void svr_algos_initialise(void);
@ -184,6 +185,13 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) {
session_cleanup();
}
#if 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;
@ -193,6 +201,7 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) {
m_free(svr_opts.ports[i]);
}
exit(exitcode);
}
@ -238,7 +247,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");