view fuzz/fuzz-common.c @ 1857:6022df862942

Use DSCP for IP QoS traffic classes The previous TOS values are deprecated and not used by modern traffic classifiers. This sets AF21 for "interactive" traffic (with a tty). Non-tty traffic sets AF11 - that indicates high throughput but is not lowest priority (which would be CS1 or LE). This differs from the CS1 used by OpenSSH, it lets interactive git over SSH have higher priority than background least effort traffic. Dropbear's settings here should be suitable with the diffservs used by CAKE qdisc.
author Matt Johnston <matt@ucc.asn.au>
date Tue, 25 Jan 2022 17:32:20 +0800
parents 19b28d2fbe30
children
line wrap: on
line source

#define FUZZ_NO_REPLACE_STDERR
#define FUZZ_NO_REPLACE_GETPW
#include "includes.h"

#include "includes.h"
#include "dbutil.h"
#include "runopts.h"
#include "crypto_desc.h"
#include "session.h"
#include "dbrandom.h"
#include "bignum.h"
#include "atomicio.h"
#include "fuzz-wrapfd.h"
#include "fuzz.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);
static void load_fixed_client_key(void);

// This runs automatically before main, due to contructor attribute in fuzz.h
void fuzz_early_setup(void) {
    /* Set stderr to point to normal stderr by default */
    fuzz.fake_stderr = stderr;
}

void fuzz_common_setup(void) {
	disallow_core();
    fuzz.fuzzing = 1;
    fuzz.wrapfds = 1;
    fuzz.do_jmp = 1;
    fuzz.input = m_malloc(sizeof(buffer));
    _dropbear_log = fuzz_dropbear_log;
    crypto_init();
    fuzz_seed("start", 5);
    /* let any messages get flushed */
    setlinebuf(stdout);
#if DEBUG_TRACE
    if (debug_trace)
    {
        fprintf(stderr, "Dropbear fuzzer: -v specified, not disabling stderr output\n");
    }
    else
#endif
    if (getenv("DROPBEAR_KEEP_STDERR")) {
        fprintf(stderr, "Dropbear fuzzer: DROPBEAR_KEEP_STDERR, not disabling stderr output\n");
    } 
    else 
    {
        fprintf(stderr, "Dropbear fuzzer: Disabling stderr output\n");
        fuzz.fake_stderr = fopen("/dev/null", "w");
        assert(fuzz.fake_stderr);
    }
}

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));
    memset(&cli_ses, 0x0, sizeof(cli_ses));
    wrapfd_setup(fuzz.input);
    // printhex("input", fuzz.input->data, fuzz.input->len);

    fuzz_seed(fuzz.input->data, MIN(fuzz.input->len, 16));

    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[] = { 
		"dropbear",
        "-E", 
    };

    int argc = sizeof(argv) / sizeof(*argv);
    svr_getopts(argc, argv);

    load_fixed_hostkeys();
}

void fuzz_svr_hook_preloop() {
    if (fuzz.svr_postauth) {
        ses.authstate.authdone = 1;
        fill_passwd("root");
    }
}

void fuzz_cli_setup(void) {
    fuzz_common_setup();
    
	_dropbear_exit = cli_dropbear_exit;
	_dropbear_log = cli_dropbear_log;

    char *argv[] = { 
		"dbclient",
		"-y",
        "localhost",
        "uptime"
    };

    int argc = sizeof(argv) / sizeof(*argv);
    cli_getopts(argc, argv);

    load_fixed_client_key();
    /* Avoid password prompt */
    setenv(DROPBEAR_PASSWORD_ENV, "password", 1);
}

#include "fuzz-hostkeys.c"   

static void load_fixed_client_key(void) {

    buffer *b = buf_new(3000);
    sign_key *key;
    enum signkey_type keytype;

    key = new_sign_key();
    keytype = DROPBEAR_SIGNKEY_ANY;
    buf_putbytes(b, keyed25519, keyed25519_len);
    buf_setpos(b, 0);
    if (buf_get_priv_key(b, key, &keytype) == DROPBEAR_FAILURE) {
        dropbear_exit("failed fixed ed25519 hostkey");
    }
    list_append(cli_opts.privkeys, key);

    buf_free(b);
}

static void load_fixed_hostkeys(void) {

    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_setlen(b, 0);
    buf_putbytes(b, keyed25519, keyed25519_len);
    buf_setpos(b, 0);
    type = DROPBEAR_SIGNKEY_ED25519;
    if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) {
        dropbear_exit("failed fixed ed25519 hostkey");
    }

    buf_free(b);
}

void fuzz_kex_fakealgos(void) {
    ses.newkeys->recv.crypt_mode = &dropbear_mode_none;
    ses.newkeys->recv.algo_mac = &dropbear_nohash;
}

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_ul(ses.dh_K, 12345678uL);
    finish_kexhashbuf();
}

/* fake version of spawn_command() */
int fuzz_spawn_command(int *ret_writefd, int *ret_readfd, int *ret_errfd, pid_t *ret_pid) {
    *ret_writefd = wrapfd_new_dummy();
    *ret_readfd = wrapfd_new_dummy();
    if (ret_errfd) {
        *ret_errfd = wrapfd_new_dummy();
    }
    if (*ret_writefd == -1 || *ret_readfd == -1 || (ret_errfd && *ret_errfd == -1)) {
        m_close(*ret_writefd);
        m_close(*ret_readfd);
        if (ret_errfd) {
            m_close(*ret_errfd);
        }
        return DROPBEAR_FAILURE;
    } else {
        *ret_pid = 999;
        return DROPBEAR_SUCCESS;

    }
}

/* Fake dropbear_listen, always returns failure for now.
TODO make it sometimes return success with wrapfd_new_dummy() sockets.
Making the listeners fake a new incoming connection will be harder. */
/* Listen on address:port. 
 * Special cases are address of "" listening on everything,
 * and address of NULL listening on localhost only.
 * Returns the number of sockets bound on success, or -1 on failure. On
 * failure, if errstring wasn't NULL, it'll be a newly malloced error
 * string.*/
int fuzz_dropbear_listen(const char* UNUSED(address), const char* UNUSED(port),
        int *UNUSED(socks), unsigned int UNUSED(sockcount), char **errstring, int *UNUSED(maxfd)) {
    if (errstring) {
        *errstring = m_strdup("fuzzing can't listen (yet)");
    }
    return -1;
}

int fuzz_run_server(const uint8_t *Data, size_t Size, int skip_kexmaths, int postauth) {
    static int once = 0;
    if (!once) {
        fuzz_svr_setup();
        fuzz.skip_kexmaths = skip_kexmaths;
        once = 1;
    }

    fuzz.svr_postauth = postauth;

    if (fuzz_set_input(Data, Size) == DROPBEAR_FAILURE) {
        return 0;
    }

    uint32_t wrapseed;
    genrandom((void*)&wrapseed, sizeof(wrapseed));
    wrapfd_setseed(wrapseed);

    int fakesock = wrapfd_new_fuzzinput();

    m_malloc_set_epoch(1);
    fuzz.do_jmp = 1;
    if (setjmp(fuzz.jmp) == 0) {
        svr_session(fakesock, fakesock);
        m_malloc_free_epoch(1, 0);
    } else {
        fuzz.do_jmp = 0;
        m_malloc_free_epoch(1, 1);
        TRACE(("dropbear_exit longjmped"))
        /* dropbear_exit jumped here */
    }

    return 0;
}

int fuzz_run_client(const uint8_t *Data, size_t Size, int skip_kexmaths) {
    static int once = 0;
    if (!once) {
        fuzz_cli_setup();
        fuzz.skip_kexmaths = skip_kexmaths;
        once = 1;
    }

    if (fuzz_set_input(Data, Size) == DROPBEAR_FAILURE) {
        return 0;
    }

    // Allow to proceed sooner
    ses.kexstate.donefirstkex = 1;

    uint32_t wrapseed;
    genrandom((void*)&wrapseed, sizeof(wrapseed));
    wrapfd_setseed(wrapseed);

    int fakesock = wrapfd_new_fuzzinput();

    m_malloc_set_epoch(1);
    fuzz.do_jmp = 1;
    if (setjmp(fuzz.jmp) == 0) {
        cli_session(fakesock, fakesock, NULL, 0);
        m_malloc_free_epoch(1, 0);
    } else {
        fuzz.do_jmp = 0;
        m_malloc_free_epoch(1, 1);
        TRACE(("dropbear_exit longjmped"))
        /* dropbear_exit jumped here */
    }

    return 0;
}

const void* fuzz_get_algo(const algo_type *algos, const char* name) {
    const algo_type *t;
    for (t = algos; t->name; t++) {
        if (strcmp(t->name, name) == 0) {
            return t->data;
        }
    }
    assert(0);
}

void fuzz_dump(const unsigned char* data, size_t len) {
    if (fuzz.dumping) {
        TRACE(("dump %zu", len))
        assert(atomicio(vwrite, fuzz.recv_dumpfd, (void*)data, len) == len);
    }
}

static struct passwd pwd_root = {
    .pw_name = "root",
    .pw_passwd = "!",
    .pw_uid = 0,
    .pw_gid = 0,
    .pw_dir = "/root",
    .pw_shell = "/bin/sh",
};

static struct passwd pwd_other = {
    .pw_name = "other",
    .pw_passwd = "!",
    .pw_uid = 100,
    .pw_gid = 100,
    .pw_dir = "/home/other",
    .pw_shell = "/bin/sh",
};


/* oss-fuzz runs fuzzers under minijail, without /etc/passwd.
We provide sufficient values for the fuzzers to run */
struct passwd* fuzz_getpwnam(const char *login) {
    if (!fuzz.fuzzing) {
        return getpwnam(login);
    }
    if (strcmp(login, pwd_other.pw_name) == 0) {
        return &pwd_other;
    }
    if (strcmp(login, pwd_root.pw_name) == 0) {
        return &pwd_root;
    }
    return NULL;
}

struct passwd* fuzz_getpwuid(uid_t uid) {
    if (!fuzz.fuzzing) {
        return getpwuid(uid);
    }
    if (uid == pwd_other.pw_uid) {
        return &pwd_other;
    }
    if (uid == pwd_root.pw_uid) {
        return &pwd_root;
    }
    return NULL;
}