Skip to content

Commit

Permalink
Integrate cfg with libfuzzer testing
Browse files Browse the repository at this point in the history
- split-input format: add trailing blob for config file

  The corpus needs some update.

- __wrap_open extended to handle the configuration file.

  The configuration file, mutated by the fuzzer, is made
  available to the cfg.c implementation, analogously to
  the handling of authfile_fd.

  The conf_file_path variable will hold the expected file
  name.  If the conf_file= option does not appear in the
  fuzzed argv, the conf_file_path variable is set with
  the CFG_DEFAULT_PATH.

  conf_file_path might also be NULL if the test does not
  involve the use of a configuration file: this happens in
  the fuzz_format_parsers case.
  • Loading branch information
dacav committed Dec 17, 2024
1 parent cf6d8e7 commit 3eea550
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 9 deletions.
1 change: 1 addition & 0 deletions fuzz/Makefile.am
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright (C) 2020 Yubico AB - See COPYING
AM_CFLAGS = $(CWFLAGS) $(CSFLAGS) -fsanitize=fuzzer
AM_CPPFLAGS = $(LIBFIDO2_CFLAGS) $(LIBCRYPTO_CFLAGS) -I$(srcdir)/..
AM_CPPFLAGS += -D SYSCONFDIR='"$(sysconfdir)"'
AM_LDFLAGS = -no-install -fsanitize=fuzzer

fuzz_format_parsers_SOURCES = fuzz_format_parsers.c
Expand Down
2 changes: 2 additions & 0 deletions fuzz/export.sym
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ set_authfile
set_conv
set_user
set_wiredata
set_conf_file_fd
set_conf_file_path
2 changes: 2 additions & 0 deletions fuzz/fuzz.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ void set_wiredata(uint8_t *, size_t);
void set_user(const char *);
void set_conv(struct pam_conv *);
void set_authfile(int);
void set_conf_file_path(const char *);
void set_conf_file_fd(int);

int pack_u32(uint8_t **, size_t *, uint32_t);
int unpack_u32(const uint8_t **, size_t *, uint32_t *);
Expand Down
90 changes: 81 additions & 9 deletions fuzz/fuzz_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <string.h>
#include <unistd.h>

#include "cfg.h"
#include "fuzz/fuzz.h"
#include "fuzz/wiredata.h"
#include "fuzz/authfile.h"
Expand All @@ -32,6 +33,7 @@ struct param {
char conv[MAXSTR];
struct blob authfile;
struct blob wiredata;
struct blob conf_file;
};

struct conv_appdata {
Expand All @@ -48,6 +50,29 @@ static const char dummy_authfile[] = AUTHFILE_SSH;
/* module configuration split by fuzzer on semicolon */
static const char *dummy_conf = "sshformat;pinverification=0;manual;";

/* module configuration file */
static const char dummy_conf_file[] = "max_devices=10\n"
"manual\n"
"debug\n"
"nouserok\n"
"openasuser\n"
"alwaysok\n"
"interactive\n"
"cue\n"
"nodetect\n"
"expand\n"
"userpresence=0\n"
"userverification=0\n"
"pinverification=0\n"
"authfile=/foo/bar\n"
"sshformat\n"
"authpending_file=/baz/quux\n"
"origin=pam://lolcalhost\n"
"appid=pam://lolcalhost\n"
"prompt=hello\n"
"cue_prompt=howdy\n"
"debug_file=stdout\n";

/* conversation dummy for manual authentication */
static const char *dummy_conv =
"94/ZgCC5htEl9SRmTRfUffKCzU/2ScRJYNFSlC5U+ik=\n"
Expand All @@ -72,7 +97,8 @@ static size_t pack(uint8_t *data, size_t len, const struct param *p) {
pack_string(&data, &len, p->conf) != 1 ||
pack_string(&data, &len, p->conv) != 1 ||
pack_blob(&data, &len, &p->authfile) != 1 ||
pack_blob(&data, &len, &p->wiredata) != 1) {
pack_blob(&data, &len, &p->wiredata) != 1 ||
pack_blob(&data, &len, &p->conf_file) != 1) {
return 0;
}

Expand Down Expand Up @@ -106,7 +132,8 @@ static size_t pack_dummy(uint8_t *data, size_t len) {
!set_string(dummy.conf, dummy_conf, MAXSTR) ||
!set_string(dummy.conv, dummy_conv, MAXSTR) ||
!set_blob(&dummy.authfile, dummy_authfile, sizeof(dummy_authfile)) ||
!set_blob(&dummy.wiredata, dummy_wiredata, sizeof(dummy_wiredata))) {
!set_blob(&dummy.wiredata, dummy_wiredata, sizeof(dummy_wiredata)) ||
!set_blob(&dummy.conf_file, dummy_conf_file, sizeof(dummy_conf_file))) {
assert(0); /* dummy couldn't be prepared */
return 0;
}
Expand All @@ -125,7 +152,8 @@ static struct param *unpack(const uint8_t *data, size_t len) {
unpack_string(&data, &len, p->conf) != 1 ||
unpack_string(&data, &len, p->conv) != 1 ||
unpack_blob(&data, &len, &p->authfile) != 1 ||
unpack_blob(&data, &len, &p->wiredata) != 1) {
unpack_blob(&data, &len, &p->wiredata) != 1 ||
unpack_blob(&data, &len, &p->conf_file) != 1) {
free(p);
return NULL;
}
Expand Down Expand Up @@ -153,6 +181,7 @@ static void mutate(struct param *p, uint32_t seed) {
mutate_string(p->conf, MAXSTR);
mutate_string(p->conv, MAXSTR);
mutate_blob(&p->authfile);
mutate_blob(&p->conf_file);
}
if (flags & MUTATE_WIREDATA)
mutate_blob(&p->wiredata);
Expand Down Expand Up @@ -229,14 +258,47 @@ static int prepare_authfile(const unsigned char *data, size_t len) {
return fd;
}

static int prepare_conf_file(const struct blob *conf_file, int argc,
const char **argv, const char **conf_file_path) {
int i, fd;
ssize_t w;

*conf_file_path = CFG_DEFAULT_PATH;
for (i = 0; i < argc; i++) {
const char *value;

if (strncmp(argv[i], "conf_file=", strlen("conf_file=")))
continue;

value = argv[i] + strlen("conf_file=");
*conf_file_path = value;
}

if ((fd = memfd_create("pam_u2f.conf", MFD_CLOEXEC)) == -1)
return -1;

w = write(fd, conf_file->body, conf_file->len);
if (w == -1 || (size_t) w != conf_file->len)
goto fail;

if (lseek(fd, 0, SEEK_SET) == -1)
goto fail;

return fd;

fail:
close(fd);
return -1;
}

int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {

struct param *param = NULL;
struct pam_conv conv;
struct conv_appdata conv_data;
const char *argv[32];
const char *argv[32], *conf_file_path;
int argc = 32;
int fd = -1;
int authfile_fd = -1, conf_file_fd = -1;

memset(&argv, 0, sizeof(*argv));
memset(&conv, 0, sizeof(conv));
Expand All @@ -254,16 +316,26 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
set_user(param->user);
set_wiredata(param->wiredata.body, param->wiredata.len);

if ((fd = prepare_authfile(param->authfile.body, param->authfile.len)) == -1)
if ((authfile_fd =
prepare_authfile(param->authfile.body, param->authfile.len)) == -1)
goto err;
set_authfile(fd);
set_authfile(authfile_fd);

prepare_argv(param->conf, &argv[0], &argc);

if ((conf_file_fd = prepare_conf_file(&param->conf_file, argc, argv,
&conf_file_path)) == -1)
goto err;
set_conf_file_path(conf_file_path);
set_conf_file_fd(conf_file_fd);

pam_sm_authenticate((void *) FUZZ_PAM_HANDLE, 0, argc, argv);

err:
if (fd != -1)
close(fd);
if (authfile_fd != -1)
close(authfile_fd);
if (conf_file_fd != -1)
close(conf_file_fd);
free(param);
return 0;
}
Expand Down
15 changes: 15 additions & 0 deletions fuzz/wrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <fido.h>

#include "drop_privs.h"
#include "cfg.h"
#include "fuzz/fuzz.h"

extern int prng_up;
Expand All @@ -34,6 +35,8 @@ static const char *user_ptr = NULL;
static struct pam_conv *conv_ptr = NULL;
static uint8_t *wiredata_ptr = NULL;
static size_t wiredata_len = 0;
static const char *conf_file_path = NULL;
static int conf_file_fd = -1;
static int authfile_fd = -1;
static char env[] = "value";

Expand All @@ -55,6 +58,8 @@ void set_wiredata(uint8_t *data, size_t len) {
}
void set_user(const char *user) { user_ptr = user; }
void set_conv(struct pam_conv *conv) { conv_ptr = conv; }
void set_conf_file_path(const char *path) { conf_file_path = path; }
void set_conf_file_fd(int fd) { conf_file_fd = fd; }
void set_authfile(int fd) { authfile_fd = fd; }

WRAP(int, close, (int fd), -1, (fd))
Expand Down Expand Up @@ -100,17 +105,27 @@ extern uid_t __wrap_geteuid(void) {
extern int __real_open(const char *pathname, int flags);
extern int __wrap_open(const char *pathname, int flags);
extern int __wrap_open(const char *pathname, int flags) {

if (prng_up && uniform_random(400) < 1)
return -1;

/* open write-only files as /dev/null */
if ((flags & O_ACCMODE) == O_WRONLY)
return __real_open("/dev/null", flags);

/* FIXME: special handling for /dev/random */
if (strcmp(pathname, "/dev/urandom") == 0)
return __real_open(pathname, flags);

if (conf_file_path && strcmp(pathname, conf_file_path) == 0) {
assert(*pathname == '/'); /* should not load config from relative path */
return dup(conf_file_fd);
}

/* open read-only files using a shared fd for the authfile */
if ((flags & O_ACCMODE) == O_RDONLY)
return dup(authfile_fd);

assert(0); /* unsupported */
return -1;
}
Expand Down

0 comments on commit 3eea550

Please sign in to comment.