Coverage Report

Created: 2023-03-20 06:28

/src/dropbear/fuzz/fuzz-common.c
Line
Count
Source (jump to first uncovered line)
1
#define FUZZ_NO_REPLACE_STDERR
2
#define FUZZ_NO_REPLACE_GETPW
3
#include "includes.h"
4
5
#include "includes.h"
6
#include "dbutil.h"
7
#include "runopts.h"
8
#include "crypto_desc.h"
9
#include "session.h"
10
#include "dbrandom.h"
11
#include "bignum.h"
12
#include "atomicio.h"
13
#include "fuzz-wrapfd.h"
14
#include "fuzz.h"
15
16
struct dropbear_fuzz_options fuzz;
17
18
static void fuzz_dropbear_log(int UNUSED(priority), const char* format, va_list param);
19
static void load_fixed_hostkeys(void);
20
static void load_fixed_client_key(void);
21
22
// This runs automatically before main, due to contructor attribute in fuzz.h
23
2
void fuzz_early_setup(void) {
24
    /* Set stderr to point to normal stderr by default */
25
2
    fuzz.fake_stderr = stderr;
26
2
}
27
28
1
void fuzz_common_setup(void) {
29
1
  disallow_core();
30
1
    fuzz.fuzzing = 1;
31
1
    fuzz.wrapfds = 1;
32
1
    fuzz.do_jmp = 1;
33
1
    fuzz.input = m_malloc(sizeof(buffer));
34
1
    _dropbear_log = fuzz_dropbear_log;
35
1
    crypto_init();
36
1
    fuzz_seed("start", 5);
37
    /* let any messages get flushed */
38
1
    setlinebuf(stdout);
39
#if DEBUG_TRACE
40
    if (debug_trace)
41
    {
42
        fprintf(stderr, "Dropbear fuzzer: -v specified, not disabling stderr output\n");
43
    }
44
    else
45
#endif
46
1
    if (getenv("DROPBEAR_KEEP_STDERR")) {
47
0
        fprintf(stderr, "Dropbear fuzzer: DROPBEAR_KEEP_STDERR, not disabling stderr output\n");
48
0
    } 
49
1
    else 
50
1
    {
51
1
        fprintf(stderr, "Dropbear fuzzer: Disabling stderr output\n");
52
1
        fuzz.fake_stderr = fopen("/dev/null", "w");
53
1
        assert(fuzz.fake_stderr);
54
1
    }
55
1
}
56
57
1.49k
int fuzz_set_input(const uint8_t *Data, size_t Size) {
58
59
1.49k
    fuzz.input->data = (unsigned char*)Data;
60
1.49k
    fuzz.input->size = Size;
61
1.49k
    fuzz.input->len = Size;
62
1.49k
    fuzz.input->pos = 0;
63
64
1.49k
    memset(&ses, 0x0, sizeof(ses));
65
1.49k
    memset(&svr_ses, 0x0, sizeof(svr_ses));
66
1.49k
    memset(&cli_ses, 0x0, sizeof(cli_ses));
67
1.49k
    wrapfd_setup(fuzz.input);
68
    // printhex("input", fuzz.input->data, fuzz.input->len);
69
70
1.49k
    fuzz_seed(fuzz.input->data, MIN(fuzz.input->len, 16));
71
72
1.49k
    return DROPBEAR_SUCCESS;
73
1.49k
}
74
75
#if DEBUG_TRACE
76
static void fuzz_dropbear_log(int UNUSED(priority), const char* format, va_list param) {
77
    if (debug_trace) {
78
        char printbuf[1024];
79
        vsnprintf(printbuf, sizeof(printbuf), format, param);
80
        fprintf(stderr, "%s\n", printbuf);
81
    }
82
}
83
#else
84
3.75k
static void fuzz_dropbear_log(int UNUSED(priority), const char* UNUSED(format), va_list UNUSED(param)) {
85
    /* No print */
86
3.75k
}
87
#endif /* DEBUG_TRACE */
88
89
1
void fuzz_svr_setup(void) {
90
1
    fuzz_common_setup();
91
    
92
1
    _dropbear_exit = svr_dropbear_exit;
93
94
1
    char *argv[] = { 
95
1
    "dropbear",
96
1
        "-E", 
97
1
    };
98
99
1
    int argc = sizeof(argv) / sizeof(*argv);
100
1
    svr_getopts(argc, argv);
101
102
1
    load_fixed_hostkeys();
103
1
}
104
105
1.49k
void fuzz_svr_hook_preloop() {
106
1.49k
    if (fuzz.svr_postauth) {
107
1.49k
        ses.authstate.authdone = 1;
108
1.49k
        fill_passwd("root");
109
1.49k
    }
110
1.49k
}
111
112
0
void fuzz_cli_setup(void) {
113
0
    fuzz_common_setup();
114
    
115
0
  _dropbear_exit = cli_dropbear_exit;
116
0
  _dropbear_log = cli_dropbear_log;
117
118
0
    char *argv[] = { 
119
0
    "dbclient",
120
0
    "-y",
121
0
        "localhost",
122
0
        "uptime"
123
0
    };
124
125
0
    int argc = sizeof(argv) / sizeof(*argv);
126
0
    cli_getopts(argc, argv);
127
128
0
    load_fixed_client_key();
129
    /* Avoid password prompt */
130
0
    setenv(DROPBEAR_PASSWORD_ENV, "password", 1);
131
0
}
132
133
#include "fuzz-hostkeys.c"   
134
135
0
static void load_fixed_client_key(void) {
136
137
0
    buffer *b = buf_new(3000);
138
0
    sign_key *key;
139
0
    enum signkey_type keytype;
140
141
0
    key = new_sign_key();
142
0
    keytype = DROPBEAR_SIGNKEY_ANY;
143
0
    buf_putbytes(b, keyed25519, keyed25519_len);
144
0
    buf_setpos(b, 0);
145
0
    if (buf_get_priv_key(b, key, &keytype) == DROPBEAR_FAILURE) {
146
0
        dropbear_exit("failed fixed ed25519 hostkey");
147
0
    }
148
0
    list_append(cli_opts.privkeys, key);
149
150
0
    buf_free(b);
151
0
}
152
153
1
static void load_fixed_hostkeys(void) {
154
155
1
    buffer *b = buf_new(3000);
156
1
    enum signkey_type type;
157
158
1
    TRACE(("load fixed hostkeys"))
159
160
1
    svr_opts.hostkey = new_sign_key();
161
162
1
    buf_setlen(b, 0);
163
1
    buf_putbytes(b, keyr, keyr_len);
164
1
    buf_setpos(b, 0);
165
1
    type = DROPBEAR_SIGNKEY_RSA;
166
1
    if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) {
167
0
        dropbear_exit("failed fixed rsa hostkey");
168
0
    }
169
170
1
    buf_setlen(b, 0);
171
1
    buf_putbytes(b, keyd, keyd_len);
172
1
    buf_setpos(b, 0);
173
1
    type = DROPBEAR_SIGNKEY_DSS;
174
1
    if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) {
175
0
        dropbear_exit("failed fixed dss hostkey");
176
0
    }
177
178
1
    buf_setlen(b, 0);
179
1
    buf_putbytes(b, keye, keye_len);
180
1
    buf_setpos(b, 0);
181
1
    type = DROPBEAR_SIGNKEY_ECDSA_NISTP256;
182
1
    if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) {
183
0
        dropbear_exit("failed fixed ecdsa hostkey");
184
0
    }
185
186
1
    buf_setlen(b, 0);
187
1
    buf_putbytes(b, keyed25519, keyed25519_len);
188
1
    buf_setpos(b, 0);
189
1
    type = DROPBEAR_SIGNKEY_ED25519;
190
1
    if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) {
191
0
        dropbear_exit("failed fixed ed25519 hostkey");
192
0
    }
193
194
1
    buf_free(b);
195
1
}
196
197
9.44k
void fuzz_kex_fakealgos(void) {
198
9.44k
    ses.newkeys->recv.crypt_mode = &dropbear_mode_none;
199
9.44k
    ses.newkeys->recv.algo_mac = &dropbear_nohash;
200
9.44k
}
201
202
void fuzz_get_socket_address(int UNUSED(fd), char **local_host, char **local_port,
203
5.57k
                        char **remote_host, char **remote_port, int UNUSED(host_lookup)) {
204
5.57k
    if (local_host) {
205
2.58k
        *local_host = m_strdup("fuzzlocalhost");
206
2.58k
    }
207
5.57k
    if (local_port) {
208
2.58k
        *local_port = m_strdup("1234");
209
2.58k
    }
210
5.57k
    if (remote_host) {
211
5.57k
        *remote_host = m_strdup("fuzzremotehost");
212
5.57k
    }
213
5.57k
    if (remote_port) {
214
4.08k
        *remote_port = m_strdup("9876");
215
4.08k
    }
216
5.57k
}
217
218
/* cut down version of svr_send_msg_kexdh_reply() that skips slow maths. Still populates structures */
219
9.27k
void fuzz_fake_send_kexdh_reply(void) {
220
9.27k
    assert(!ses.dh_K);
221
9.27k
    m_mp_alloc_init_multi(&ses.dh_K, NULL);
222
9.27k
    mp_set_ul(ses.dh_K, 12345678uL);
223
9.27k
    finish_kexhashbuf();
224
9.27k
}
225
226
/* fake version of spawn_command() */
227
1.55k
int fuzz_spawn_command(int *ret_writefd, int *ret_readfd, int *ret_errfd, pid_t *ret_pid) {
228
1.55k
    *ret_writefd = wrapfd_new_dummy();
229
1.55k
    *ret_readfd = wrapfd_new_dummy();
230
1.55k
    if (ret_errfd) {
231
1.55k
        *ret_errfd = wrapfd_new_dummy();
232
1.55k
    }
233
1.55k
    if (*ret_writefd == -1 || *ret_readfd == -1 || (ret_errfd && *ret_errfd == -1)) {
234
0
        m_close(*ret_writefd);
235
0
        m_close(*ret_readfd);
236
0
        if (ret_errfd) {
237
0
            m_close(*ret_errfd);
238
0
        }
239
0
        return DROPBEAR_FAILURE;
240
1.55k
    } else {
241
1.55k
        *ret_pid = 999;
242
1.55k
        return DROPBEAR_SUCCESS;
243
244
1.55k
    }
245
1.55k
}
246
247
/* Fake dropbear_listen, always returns failure for now.
248
TODO make it sometimes return success with wrapfd_new_dummy() sockets.
249
Making the listeners fake a new incoming connection will be harder. */
250
/* Listen on address:port. 
251
 * Special cases are address of "" listening on everything,
252
 * and address of NULL listening on localhost only.
253
 * Returns the number of sockets bound on success, or -1 on failure. On
254
 * failure, if errstring wasn't NULL, it'll be a newly malloced error
255
 * string.*/
256
int fuzz_dropbear_listen(const char* UNUSED(address), const char* UNUSED(port),
257
1.22k
        int *UNUSED(socks), unsigned int UNUSED(sockcount), char **errstring, int *UNUSED(maxfd)) {
258
1.22k
    if (errstring) {
259
1.22k
        *errstring = m_strdup("fuzzing can't listen (yet)");
260
1.22k
    }
261
1.22k
    return -1;
262
1.22k
}
263
264
1.49k
int fuzz_run_server(const uint8_t *Data, size_t Size, int skip_kexmaths, int postauth) {
265
1.49k
    static int once = 0;
266
1.49k
    if (!once) {
267
1
        fuzz_svr_setup();
268
1
        fuzz.skip_kexmaths = skip_kexmaths;
269
1
        once = 1;
270
1
    }
271
272
1.49k
    fuzz.svr_postauth = postauth;
273
274
1.49k
    if (fuzz_set_input(Data, Size) == DROPBEAR_FAILURE) {
275
0
        return 0;
276
0
    }
277
278
1.49k
    uint32_t wrapseed;
279
1.49k
    genrandom((void*)&wrapseed, sizeof(wrapseed));
280
1.49k
    wrapfd_setseed(wrapseed);
281
282
1.49k
    int fakesock = wrapfd_new_fuzzinput();
283
284
1.49k
    m_malloc_set_epoch(1);
285
1.49k
    fuzz.do_jmp = 1;
286
1.49k
    if (setjmp(fuzz.jmp) == 0) {
287
1.49k
        svr_session(fakesock, fakesock);
288
0
        m_malloc_free_epoch(1, 0);
289
0
    } else {
290
0
        fuzz.do_jmp = 0;
291
0
        m_malloc_free_epoch(1, 1);
292
0
        TRACE(("dropbear_exit longjmped"))
293
        /* dropbear_exit jumped here */
294
0
    }
295
296
0
    return 0;
297
1.49k
}
298
299
0
int fuzz_run_client(const uint8_t *Data, size_t Size, int skip_kexmaths) {
300
0
    static int once = 0;
301
0
    if (!once) {
302
0
        fuzz_cli_setup();
303
0
        fuzz.skip_kexmaths = skip_kexmaths;
304
0
        once = 1;
305
0
    }
306
307
0
    if (fuzz_set_input(Data, Size) == DROPBEAR_FAILURE) {
308
0
        return 0;
309
0
    }
310
311
    // Allow to proceed sooner
312
0
    ses.kexstate.donefirstkex = 1;
313
314
0
    uint32_t wrapseed;
315
0
    genrandom((void*)&wrapseed, sizeof(wrapseed));
316
0
    wrapfd_setseed(wrapseed);
317
318
0
    int fakesock = wrapfd_new_fuzzinput();
319
320
0
    m_malloc_set_epoch(1);
321
0
    fuzz.do_jmp = 1;
322
0
    if (setjmp(fuzz.jmp) == 0) {
323
0
        cli_session(fakesock, fakesock, NULL, 0);
324
0
        m_malloc_free_epoch(1, 0);
325
0
    } else {
326
0
        fuzz.do_jmp = 0;
327
0
        m_malloc_free_epoch(1, 1);
328
0
        TRACE(("dropbear_exit longjmped"))
329
        /* dropbear_exit jumped here */
330
0
    }
331
332
0
    return 0;
333
0
}
334
335
0
const void* fuzz_get_algo(const algo_type *algos, const char* name) {
336
0
    const algo_type *t;
337
0
    for (t = algos; t->name; t++) {
338
0
        if (strcmp(t->name, name) == 0) {
339
0
            return t->data;
340
0
        }
341
0
    }
342
0
    assert(0);
343
0
}
344
345
96.2k
void fuzz_dump(const unsigned char* data, size_t len) {
346
96.2k
    if (fuzz.dumping) {
347
0
        TRACE(("dump %zu", len))
348
0
        assert(atomicio(vwrite, fuzz.recv_dumpfd, (void*)data, len) == len);
349
0
    }
350
96.2k
}
351
352
static struct passwd pwd_root = {
353
    .pw_name = "root",
354
    .pw_passwd = "!",
355
    .pw_uid = 0,
356
    .pw_gid = 0,
357
    .pw_dir = "/root",
358
    .pw_shell = "/bin/sh",
359
};
360
361
static struct passwd pwd_other = {
362
    .pw_name = "other",
363
    .pw_passwd = "!",
364
    .pw_uid = 100,
365
    .pw_gid = 100,
366
    .pw_dir = "/home/other",
367
    .pw_shell = "/bin/sh",
368
};
369
370
371
/* oss-fuzz runs fuzzers under minijail, without /etc/passwd.
372
We provide sufficient values for the fuzzers to run */
373
1.50k
struct passwd* fuzz_getpwnam(const char *login) {
374
1.50k
    if (!fuzz.fuzzing) {
375
0
        return getpwnam(login);
376
0
    }
377
1.50k
    if (strcmp(login, pwd_other.pw_name) == 0) {
378
0
        return &pwd_other;
379
0
    }
380
1.50k
    if (strcmp(login, pwd_root.pw_name) == 0) {
381
1.50k
        return &pwd_root;
382
1.50k
    }
383
0
    return NULL;
384
1.50k
}
385
386
0
struct passwd* fuzz_getpwuid(uid_t uid) {
387
0
    if (!fuzz.fuzzing) {
388
0
        return getpwuid(uid);
389
0
    }
390
0
    if (uid == pwd_other.pw_uid) {
391
0
        return &pwd_other;
392
0
    }
393
0
    if (uid == pwd_root.pw_uid) {
394
0
        return &pwd_root;
395
0
    }
396
0
    return NULL;
397
0
}
398