diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /lib/fuzzing | |
parent | Initial commit. (diff) | |
download | samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip |
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
41 files changed, 3625 insertions, 0 deletions
diff --git a/lib/fuzzing/README.md b/lib/fuzzing/README.md new file mode 100644 index 0000000..d3e34bd --- /dev/null +++ b/lib/fuzzing/README.md @@ -0,0 +1,87 @@ +# Fuzzing Samba + +See also https://wiki.samba.org/index.php/Fuzzing + +Fuzzing supplies valid, invalid, unexpected or random data as input to a piece +of code. Instrumentation, usually compiler-implemented, is used to monitor for +exceptions such as crashes, assertions or memory corruption. + +See [Wikipedia article on fuzzing](https://en.wikipedia.org/wiki/Fuzzing) for +more information. + +# Honggfuzz + +## Configure with fuzzing + +Example command line to build binaries for use with +[honggfuzz](https://github.com/google/honggfuzz/): + +```sh +./configure -C --without-gettext --enable-debug --enable-developer \ + --address-sanitizer --enable-libfuzzer --abi-check-disable \ + CC=.../honggfuzz/hfuzz_cc/hfuzz-clang \ + LINK_CC=.../honggfuzz/hfuzz_cc/hfuzz-clang +``` + + +## Fuzzing tiniparser + +Example for fuzzing `tiniparser` using `honggfuzz` (see `--help` for more +options): + +```sh +make bin/fuzz_tiniparser && \ +.../honggfuzz/honggfuzz --sanitizers --timeout 3 --max_file_size 256 \ + --rlimit_rss 100 -f .../tiniparser-corpus -- bin/fuzz_tiniparser +``` + +# AFL (american fuzzy lop) + +## Configure with fuzzing + +Example command line to build binaries for use with +[afl](http://lcamtuf.coredump.cx/afl/) + +```sh +./configure -C --without-gettext --enable-debug --enable-developer \ + --enable-afl-fuzzer --abi-check-disable \ + CC=afl-gcc +``` + +## Fuzzing tiniparser + +Example for fuzzing `tiniparser` using `afl-fuzz` (see `--help` for more +options): + +```sh +make bin/fuzz_tiniparser build && \ +afl-fuzz -m 200 -i inputdir -o outputdir -- bin/fuzz_tiniparser +``` + +# oss-fuzz + +Samba can be fuzzed by Google's oss-fuzz system. Assuming you have an +oss-fuzz checkout from https://github.com/google/oss-fuzz with Samba's +metadata in projects/samba, the following guides will help: + +## Testing locally + +https://google.github.io/oss-fuzz/getting-started/new-project-guide/#testing-locally + +## Debugging oss-fuzz + +See https://google.github.io/oss-fuzz/advanced-topics/debugging/ + +## Samba-specific hints + +A typical debugging workflow is: + +oss-fuzz$ python infra/helper.py shell samba +git fetch $REMOTE $BRANCH +git checkout FETCH_HEAD +lib/fuzzing/oss-fuzz/build_image.sh +compile + +This will pull in any new Samba deps and build Samba's fuzzers. + +# vim: set sw=8 sts=8 ts=8 tw=79 : diff --git a/lib/fuzzing/afl-fuzz-main.c b/lib/fuzzing/afl-fuzz-main.c new file mode 100644 index 0000000..e0a1d26 --- /dev/null +++ b/lib/fuzzing/afl-fuzz-main.c @@ -0,0 +1,66 @@ +/* + Unix SMB/CIFS implementation. + + Fuzz driver (AFL style) + + Copyright (C) Andrew Bartlett 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/util/samba_util.h" +#include "fuzzing.h" + +int main(int argc, char *argv[]) { + int ret; + size_t size = 0; + int i; + + ret = LLVMFuzzerInitialize(&argc, &argv); + if (ret != 0) { + printf("LLVMFuzzerInitialize returned %d\n", ret); + return ret; + } + + +#ifdef __AFL_LOOP + while (__AFL_LOOP(1000)) +#else + for (i = 1; i < argc; i++) { + uint8_t *buf = (uint8_t *)file_load(argv[i], + &size, + 0, + NULL); + ret = LLVMFuzzerTestOneInput(buf, size); + TALLOC_FREE(buf); + if (ret != 0) { + printf("LLVMFuzzerTestOneInput returned %d on argument %d\n", + ret, i); + return ret; + } + } + if (i == 1) +#endif + { + uint8_t *buf = (uint8_t *)fd_load(0, &size, 0, NULL); + if (buf == NULL) { + exit(1); + } + + ret = LLVMFuzzerTestOneInput(buf, size); + TALLOC_FREE(buf); + } + return ret; +} diff --git a/lib/fuzzing/decode_ndr_X_crash b/lib/fuzzing/decode_ndr_X_crash new file mode 100755 index 0000000..63c3cd7 --- /dev/null +++ b/lib/fuzzing/decode_ndr_X_crash @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +# +# Interpret a file that crashes an fuzz_ndr_X binary. +# +# Copyright (C) Catalyst IT Ltd. 2019 + + +import sys +import os +from base64 import b64encode +import struct +import argparse +import re + +TYPE_MASK = 3 +TYPES = ['struct', 'in', 'out'] + +FLAGS = [ + (4, 'ndr64', '--ndr64'), +] + + +def print_if_verbose(*args, **kwargs): + if verbose: + print(*args, **kwargs) + + +def process_one_file(f): + print_if_verbose(f.name) + print_if_verbose('-' * len(f.name)) + + b = f.read() + flags, function = struct.unpack('<HH', b[:4]) + if opnum is not None and opnum != function: + return + + t = TYPES[flags & TYPE_MASK] + if ndr_type and ndr_type != t: + return + + payload = b[4:] + data64 = b64encode(payload).decode('utf-8') + + cmd = ['bin/ndrdump', + pipe, + str(function), + t, + '--base64-input', + '--input', data64, + ] + + for flag, name, option in FLAGS: + if flags & flag: + print_if_verbose("flag: %s" % name) + cmd.append(option) + + print_if_verbose("length: %d\n" % len(payload)) + print(' '.join(cmd)) + print_if_verbose() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-p', '--pipe', default='$PIPE', + help='pipe name (for output command line)') + parser.add_argument('-t', '--type', default=None, choices=TYPES, + help='restrict to this type') + parser.add_argument('-o', '--opnum', default=None, type=int, + help='restrict to this function/struct number') + parser.add_argument('FILES', nargs='*', default=(), + help="read from these files") + parser.add_argument('-k', '--ignore-errors', action='store_true', + help='do not stop on errors') + parser.add_argument('-v', '--verbose', action='store_true', + help='say more') + parser.add_argument('-H', '--honggfuzz-file', + help="extract crashes from this honggfuzz report") + parser.add_argument('-f', '--crash-filter', + help="only print crashes matching this rexexp") + + args = parser.parse_args() + + global pipe, opnum, ndr_type, verbose + pipe = args.pipe + opnum = args.opnum + ndr_type = args.type + verbose = args.verbose + + if not args.FILES and not args.honggfuzz_file: + parser.print_usage() + sys.exit(1) + + for fn in args.FILES: + if args.crash_filter is not None: + if not re.search(args.crash_filter, fn): + print_if_verbose(f"skipping {fn}") + continue + try: + if fn == '-': + process_one_file(sys.stdin) + else: + with open(fn, 'rb') as f: + process_one_file(f) + except Exception: + print_if_verbose("Error processing %s\n" % fn) + if args.ignore_errors: + continue + raise + + if args.honggfuzz_file: + print_if_verbose(f"looking at {args.honggfuzz_file}") + with open(args.honggfuzz_file) as f: + pipe = None + crash = None + for line in f: + m = re.match(r'^\s*fuzzTarget\s*:\s*bin/fuzz_ndr_(\w+)\s*$', line) + if m: + pipe = m.group(1).split('_TYPE_', 1)[0] + print_if_verbose(f"found pipe {pipe}") + m = re.match(r'^FUZZ_FNAME: (\S+)$', line) + if m: + crash = m.group(1) + if args.crash_filter is not None: + if not re.search(args.crash_filter, crash): + print_if_verbose(f"skipping {crash}") + pipe = None + crash = None + continue + print_if_verbose(f"found crash {crash}") + if pipe is not None and crash is not None: + with open(crash, 'rb') as f: + process_one_file(f) + pipe = None + crash = None + + +main() diff --git a/lib/fuzzing/fuzz_cli_credentials_parse_string.c b/lib/fuzzing/fuzz_cli_credentials_parse_string.c new file mode 100644 index 0000000..0b0f97a --- /dev/null +++ b/lib/fuzzing/fuzz_cli_credentials_parse_string.c @@ -0,0 +1,63 @@ +/* + Fuzz cli_credentials_parse_string + Copyright (C) Catalyst IT 2020 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "includes.h" +#include "auth/credentials/credentials.h" +#include "fuzzing/fuzzing.h" + +#define MAX_LENGTH (1024 * 10) +char buf[MAX_LENGTH + 1]; + +const enum credentials_obtained obtained = CRED_UNINITIALISED; + + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) +{ + TALLOC_CTX *mem_ctx = NULL; + struct cli_credentials *credentials = NULL; + bool anon; + const char *username; + const char *domain; + + if (len > MAX_LENGTH) { + return 0; + } + + memcpy(buf, input, len); + buf[len] = '\0'; + + mem_ctx = talloc_new(NULL); + credentials = cli_credentials_init(mem_ctx); + + cli_credentials_parse_string(credentials, buf, obtained); + + anon = cli_credentials_is_anonymous(credentials); + + cli_credentials_get_ntlm_username_domain(credentials, + mem_ctx, + &username, + &domain); + + talloc_free(mem_ctx); + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} diff --git a/lib/fuzzing/fuzz_conditional_ace_blob.c b/lib/fuzzing/fuzz_conditional_ace_blob.c new file mode 100644 index 0000000..ebbd908 --- /dev/null +++ b/lib/fuzzing/fuzz_conditional_ace_blob.c @@ -0,0 +1,156 @@ +/* + Fuzz conditional ace decoding and encoding + Copyright (C) Catalyst IT 2023 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "libcli/security/security.h" +#include "lib/util/attr.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/conditional_ace.h" +#include "librpc/gen_ndr/conditional_ace.h" +#include "fuzzing/fuzzing.h" + + +#define MAX_LENGTH (1024 * 1024 - 1) + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) +{ + TALLOC_CTX *mem_ctx = NULL; + bool ok; + struct ace_condition_script *s1 = NULL; + struct ace_condition_script *s2 = NULL; + const char *message = NULL; + size_t message_offset; + const char *sddl = NULL; + DATA_BLOB e1, e2; + size_t length; + + if (len > MAX_LENGTH) { + return 0; + } + + /* + * In this one we are treating the input data as an ACE blob, + * and decoding it into the structure and thence SDDL. + * + * This doesn't run the conditional ACE, for which we would + * need a security token. + */ + + e1.data = input; + e1.length = len; + + mem_ctx = talloc_new(NULL); + + s1 = parse_conditional_ace(mem_ctx, e1); + if (s1 == NULL) { + /* no worries, it was nonsense */ + TALLOC_FREE(mem_ctx); + return 0; + } + + /* back to blob form */ + ok = conditional_ace_encode_binary(mem_ctx, s1, &e2); + if (! ok) { + if (e1.length == CONDITIONAL_ACE_MAX_LENGTH) { + /* + * This is an edge case where the encoder and + * decoder treat the boundary slightly + * differently, and the encoder refuses to + * encode to the maximum length. This is not + * an issue in the real world. + */ + TALLOC_FREE(mem_ctx); + return 0; + } + abort(); + } + + if (data_blob_cmp(&e1, &e2) != 0) { + abort(); + } + + sddl = sddl_from_conditional_ace(mem_ctx, s1); + if (sddl == NULL) { + /* + * we can't call this a failure, because the blob + * could easily have nonsensical programs that the + * SDDL decompiler is unwilling to countenance. For + * example, it could have an operator that requires + * arguments as the first token, when of course the + * arguments need to come first. + */ + TALLOC_FREE(mem_ctx); + return 0; + } + + s2 = ace_conditions_compile_sddl(mem_ctx, + ACE_CONDITION_FLAG_ALLOW_DEVICE, + sddl, + &message, + &message_offset, + &length); + if (s2 == NULL) { + /* + * We also don't complain when the SDDL decompiler + * produces an uncompilable program, because the + * decompiler is meant to be a display tool, not a + * verifier in itself. + */ + TALLOC_FREE(mem_ctx); + return 0; + } + + ok = conditional_ace_encode_binary(mem_ctx, s2, &e2); + if (! ok) { + abort(); + } + + /* + * It would be nice here to go: + * + * if (data_blob_cmp(&e1, &e2) != 0) { + * abort(); + * } + * + * but that isn't really fair. The decompilation into SDDL + * does not make thorough sanity checks because that is not + * its job -- it is just trying to depict what is there -- and + * there are many ambiguous decompilations. + * + * For example, a blob with a single literal integer token, + * say 42, can only really be shown in the SDDL syntax as + * "(42)", but when the compiler reads that it knows that a + * literal number is invalid except in a RHS argument, so it + * assumes "42" is a local attribute name. + * + * Even if the decompiler was a perfect verifier, a round trip + * through SDDL could not be guaranteed because, for example, + * an 8 bit integer can only be displayed in SDDL in the form + * that compiles to a 64 bit integer. + */ + + TALLOC_FREE(mem_ctx); + return 0; +} diff --git a/lib/fuzzing/fuzz_dcerpc_parse_binding.c b/lib/fuzzing/fuzz_dcerpc_parse_binding.c new file mode 100644 index 0000000..dcbf440 --- /dev/null +++ b/lib/fuzzing/fuzz_dcerpc_parse_binding.c @@ -0,0 +1,76 @@ +/* + Fuzz dcerpc_parse_binding + Copyright (C) Catalyst IT 2020 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "includes.h" +#include "librpc/gen_ndr/ndr_epmapper.h" +#include "librpc/rpc/dcerpc.h" +#include "fuzzing/fuzzing.h" + +#define MAX_LENGTH (1024 * 10) +char buf[MAX_LENGTH + 1]; + + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) +{ + TALLOC_CTX *mem_ctx = NULL; + struct dcerpc_binding *binding = NULL; + struct dcerpc_binding *dup = NULL; + struct epm_tower tower; + NTSTATUS status; + struct GUID guid; + + if (len > MAX_LENGTH) { + return 0; + } + + memcpy(buf, input, len); + buf[len] = '\0'; + + mem_ctx = talloc_new(NULL); + status = dcerpc_parse_binding(mem_ctx, buf, &binding); + + if (! NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return 0; + } + + /* If the string parses, we try manipulating it a bit */ + + dcerpc_binding_string(mem_ctx, binding); + dcerpc_binding_get_abstract_syntax(binding); + dup = dcerpc_binding_dup(mem_ctx, binding); + + status = dcerpc_binding_build_tower(mem_ctx, + binding, + &tower); + if (NT_STATUS_IS_OK(status)) { + status = dcerpc_binding_from_tower(mem_ctx, + &tower, + &dup); + } + + guid = dcerpc_binding_get_object(binding); + + talloc_free(mem_ctx); + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} diff --git a/lib/fuzzing/fuzz_ldap_decode.c b/lib/fuzzing/fuzz_ldap_decode.c new file mode 100644 index 0000000..be286ea --- /dev/null +++ b/lib/fuzzing/fuzz_ldap_decode.c @@ -0,0 +1,66 @@ +/* + Fuzzing for ldap_decode. + Copyright (C) Michael Hanselmann 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "lib/util/asn1.h" +#include "libcli/ldap/ldap_message.h" +#include "libcli/ldap/ldap_proto.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + TALLOC_CTX *mem_ctx = talloc_init(__FUNCTION__); + struct asn1_data *asn1; + struct ldap_message *ldap_msg; + struct ldap_request_limits limits = { + /* + * The default size is currently 256000 bytes + */ + .max_search_size = 256000 + }; + NTSTATUS status; + + /* + * Need to limit the max parse tree depth to 250 to prevent + * ASAN detecting stack overflows. + */ + asn1 = asn1_init(mem_ctx, 250); + if (!asn1) { + goto out; + } + + asn1_load_nocopy(asn1, buf, len); + + ldap_msg = talloc(mem_ctx, struct ldap_message); + if (!ldap_msg) { + goto out; + } + + status = ldap_decode( + asn1, &limits, samba_ldap_control_handlers(), ldap_msg); + +out: + talloc_free(mem_ctx); + + return 0; +} diff --git a/lib/fuzzing/fuzz_ldb_comparison_fold.c b/lib/fuzzing/fuzz_ldb_comparison_fold.c new file mode 100644 index 0000000..08b2f59 --- /dev/null +++ b/lib/fuzzing/fuzz_ldb_comparison_fold.c @@ -0,0 +1,58 @@ +/* + Fuzzing ldb_comparison_fold() + Copyright (C) Catalyst IT 2020 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "lib/ldb/include/ldb.h" +#include "lib/ldb/include/ldb_handlers.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) +{ + struct ldb_val v1, v2; + struct ldb_context *ldb = NULL; + + if (len < 2) { + return 0; + } + + v1.length = PULL_LE_U16(input, 0); + if (v1.length > len - 2) { + /* the exact case of v2.length == 0 is still available */ + return 0; + } + len -= 2; + input += 2; + + ldb = ldb_init(NULL, NULL); + if (ldb == NULL) { + return 0; + } + + v1.data = talloc_memdup(ldb, input, v1.length); + v2.length = len - v1.length; + v2.data = talloc_memdup(ldb, input + v1.length, v2.length); + + ldb_comparison_fold(ldb, ldb, &v1, &v2); + talloc_free(ldb); + return 0; +} diff --git a/lib/fuzzing/fuzz_ldb_dn_explode.c b/lib/fuzzing/fuzz_ldb_dn_explode.c new file mode 100644 index 0000000..0e1560e --- /dev/null +++ b/lib/fuzzing/fuzz_ldb_dn_explode.c @@ -0,0 +1,53 @@ +/* + Fuzzing ldb_dn_explode + Copyright (C) Catalyst IT 2020 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "ldb.h" + + +#define MAX_LENGTH (2 * 1024 * 1024 - 1) +char buf[MAX_LENGTH + 1] = {0}; + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) +{ + struct ldb_dn *dn = NULL; + struct ldb_context *ldb = ldb_init(NULL, NULL); + if (ldb == NULL) { + return 0; + } + /* + * We copy the buffer in order to NUL-terminate, because running off + * the end of the string would be an uninteresting crash. + */ + if (len > MAX_LENGTH) { + len = MAX_LENGTH; + } + memcpy(buf, input, len); + buf[len] = 0; + + dn = ldb_dn_new(ldb, ldb, buf); + ldb_dn_validate(dn); + TALLOC_FREE(ldb); + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} diff --git a/lib/fuzzing/fuzz_ldb_ldif_read.c b/lib/fuzzing/fuzz_ldb_ldif_read.c new file mode 100644 index 0000000..3c48b6a --- /dev/null +++ b/lib/fuzzing/fuzz_ldb_ldif_read.c @@ -0,0 +1,56 @@ +/* + Fuzzing ldb_ldif_read_string + Copyright (C) Catalyst IT 2020 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "ldb_private.h" + + +#define MAX_LENGTH (2 * 1024 * 1024 - 1) +char buf[MAX_LENGTH + 1] = {0}; + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) +{ + struct ldb_ldif *ldif = NULL; + const char *s = NULL; + struct ldb_context *ldb = ldb_init(NULL, NULL); + if (ldb == NULL) { + return 0; + } + + if (len > MAX_LENGTH) { + len = MAX_LENGTH; + } + memcpy(buf, input, len); + buf[len] = 0; + s = buf; + + ldif = ldb_ldif_read_string(ldb, &s); + + if(ldif != NULL) { + ldb_ldif_write_string(ldb, ldb, ldif); + ldb_ldif_write_redacted_trace_string(ldb, ldb, ldif); + } + TALLOC_FREE(ldb); + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} diff --git a/lib/fuzzing/fuzz_ldb_parse_binary_decode.c b/lib/fuzzing/fuzz_ldb_parse_binary_decode.c new file mode 100644 index 0000000..ac2e436 --- /dev/null +++ b/lib/fuzzing/fuzz_ldb_parse_binary_decode.c @@ -0,0 +1,55 @@ +/* + Fuzzing ldb_binary_decode and ldb_binary_encode_string + Copyright (C) Catalyst IT 2020 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "ldb_private.h" + + +#define MAX_LENGTH (2 * 1024 * 1024 - 1) +char buf[MAX_LENGTH + 1] = {0}; + +static char * possibly_truncate(uint8_t *input, size_t len) +{ + if (len > MAX_LENGTH) { + len = MAX_LENGTH; + } + memcpy(buf, input, len); + buf[len] = 0; + return buf; +} + + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) +{ + TALLOC_CTX *mem_ctx = talloc_init(__FUNCTION__); + struct ldb_val val = {0}; + const char *s = possibly_truncate(input, len); + + /* we treat the same string to encoding and decoding, not + * round-tripping. */ + val = ldb_binary_decode(mem_ctx, s); + ldb_binary_encode_string(mem_ctx, s); + TALLOC_FREE(mem_ctx); + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} diff --git a/lib/fuzzing/fuzz_ldb_parse_control.c b/lib/fuzzing/fuzz_ldb_parse_control.c new file mode 100644 index 0000000..722d9f9 --- /dev/null +++ b/lib/fuzzing/fuzz_ldb_parse_control.c @@ -0,0 +1,55 @@ +/* + Fuzzing ldb_parse_control_from_string + Copyright (C) Catalyst IT 2020 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "ldb_private.h" + + +#define MAX_LENGTH (2 * 1024 * 1024 - 1) +char buf[MAX_LENGTH + 1] = {0}; + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) +{ + struct ldb_control *control = NULL; + struct ldb_context *ldb = ldb_init(NULL, NULL); + if (ldb == NULL) { + return 0; + } + /* + * We copy the buffer in order to NUL-terminate, because running off + * the end of the string would be an uninteresting crash. + */ + if (len > MAX_LENGTH) { + len = MAX_LENGTH; + } + memcpy(buf, input, len); + buf[len] = 0; + + control = ldb_parse_control_from_string(ldb, ldb, buf); + if (control != NULL) { + ldb_control_to_string(ldb, control); + } + TALLOC_FREE(ldb); + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} diff --git a/lib/fuzzing/fuzz_ldb_parse_tree.c b/lib/fuzzing/fuzz_ldb_parse_tree.c new file mode 100644 index 0000000..1e64940 --- /dev/null +++ b/lib/fuzzing/fuzz_ldb_parse_tree.c @@ -0,0 +1,53 @@ +/* + Fuzzing for ldb_parse_tree + Copyright (C) Michael Hanselmann 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "ldb.h" +#include "ldb_module.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + TALLOC_CTX *mem_ctx = talloc_init(__FUNCTION__); + struct ldb_parse_tree *tree; + char *filter; + + if (len < 1) { + goto out; + } + + filter = talloc_strndup(mem_ctx, (const char*)buf, len); + + if (filter == NULL) { + goto out; + } + + tree = ldb_parse_tree(mem_ctx, filter); + + (void)ldb_filter_from_tree(mem_ctx, tree); + +out: + talloc_free(mem_ctx); + + return 0; +} diff --git a/lib/fuzzing/fuzz_lzxpress.c b/lib/fuzzing/fuzz_lzxpress.c new file mode 100644 index 0000000..ddc7aa0 --- /dev/null +++ b/lib/fuzzing/fuzz_lzxpress.c @@ -0,0 +1,35 @@ +/* + Fuzzing for lzxpress_decompress + Copyright (C) Michael Hanselmann 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "lzxpress.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + static uint8_t output[1024 * 1024] = {0}; + + lzxpress_decompress(buf, len, output, sizeof(output)); + + return 0; +} diff --git a/lib/fuzzing/fuzz_lzxpress_compress.c b/lib/fuzzing/fuzz_lzxpress_compress.c new file mode 100644 index 0000000..9e5c647 --- /dev/null +++ b/lib/fuzzing/fuzz_lzxpress_compress.c @@ -0,0 +1,35 @@ +/* + Fuzzing for lzxpress_decompress + Copyright (C) Michael Hanselmann 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "lzxpress.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + static uint8_t output[1024 * 1024] = {0}; + + lzxpress_compress(buf, len, output, sizeof(output)); + + return 0; +} diff --git a/lib/fuzzing/fuzz_lzxpress_huffman_compress.c b/lib/fuzzing/fuzz_lzxpress_huffman_compress.c new file mode 100644 index 0000000..165244c --- /dev/null +++ b/lib/fuzzing/fuzz_lzxpress_huffman_compress.c @@ -0,0 +1,58 @@ +/* + Fuzzing for lzxpress_huffman_compress_talloc + Copyright (C) Michael Hanselmann 2019 + Copyright (C) Douglas Bagnall 2022 <dbagnall@samba.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "compression/lzxpress_huffman.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + + +#define MAX_SIZE (1024 * 1024) + +int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + static uint8_t *output; + size_t output_len; + TALLOC_CTX *mem_ctx = NULL; + struct lzxhuff_compressor_mem cmp_mem; + + /* + * The round-trip fuzzer checks the compressor with an unconstrained + * output buffer; here we see what happens if the buffer is possibly too + * small. + */ + if (len < 3) { + return 0; + } + output_len = MIN(MAX_SIZE, buf[0] | (buf[1] << 8) | (buf[2] << 16)); + buf += 3; + len -= 3; + mem_ctx = talloc_new(NULL); + + output = talloc_array(mem_ctx, uint8_t, output_len); + + lzxpress_huffman_compress(&cmp_mem, buf, len, output, output_len); + + talloc_free(mem_ctx); + return 0; +} diff --git a/lib/fuzzing/fuzz_lzxpress_huffman_decompress.c b/lib/fuzzing/fuzz_lzxpress_huffman_decompress.c new file mode 100644 index 0000000..d475ff6 --- /dev/null +++ b/lib/fuzzing/fuzz_lzxpress_huffman_decompress.c @@ -0,0 +1,48 @@ +/* + Fuzzing for lzxpress_decompress + Copyright (C) Michael Hanselmann 2019 + Copyright (C) Douglas Bagnall 2022 <dbagnall@samba.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "compression/lzxpress_huffman.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + +static uint8_t output[1024 * 1024] = {0}; + +int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + size_t target_len; + if (len < 4) { + return 0; + } + /* + * An exact target length is required, which we store in the first 24 + * bits. + */ + target_len = MIN(sizeof(output), buf[0] | (buf[1] << 8) | (buf[2] << 16)); + buf += 3; + len -= 3; + + lzxpress_huffman_decompress(buf, len, output, target_len); + + return 0; +} diff --git a/lib/fuzzing/fuzz_lzxpress_huffman_round_trip.c b/lib/fuzzing/fuzz_lzxpress_huffman_round_trip.c new file mode 100644 index 0000000..adb8fbf --- /dev/null +++ b/lib/fuzzing/fuzz_lzxpress_huffman_round_trip.c @@ -0,0 +1,68 @@ +/* + Fuzzing for lzxpress_huffman{_decompress,_compress} round trip + Copyright (C) Michael Hanselmann 2019 + Copyright (C) Douglas Bagnall 2022 <dbagnall@samba.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "compression/lzxpress_huffman.h" + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + + +int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + /* + * we allow compressed to be 25% bigger than decompressed. + */ + static uint8_t compressed[1024 * (1024 + 256)]; + static uint8_t decompressed[1024 * 1024]; + ssize_t compressed_size; + ssize_t decompressed_size; + struct lzxhuff_compressor_mem cmp; + + if (len > sizeof(decompressed) || len == 0) { + return 0; + } + + compressed_size = lzxpress_huffman_compress(&cmp, + buf, + len, + compressed, + sizeof(compressed)); + if (compressed_size < 0) { + abort(); + } + + decompressed_size = lzxpress_huffman_decompress(compressed, + compressed_size, + decompressed, + len); + + if (decompressed_size != len) { + abort(); + } + if (memcmp(buf, decompressed, len) != 0) { + abort(); + } + + return 0; +} diff --git a/lib/fuzzing/fuzz_lzxpress_round_trip.c b/lib/fuzzing/fuzz_lzxpress_round_trip.c new file mode 100644 index 0000000..a4043c2 --- /dev/null +++ b/lib/fuzzing/fuzz_lzxpress_round_trip.c @@ -0,0 +1,56 @@ +/* + Fuzzing for lzxpress_decompress + Copyright (C) Michael Hanselmann 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "lzxpress.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + static uint8_t compressed[1024 * 1280] = {0}; + static uint8_t decompressed[1024 * 1024] = {0}; + ssize_t compressed_size; + ssize_t decompressed_size; + + if (len > sizeof(decompressed)) { + return 0; + } + + compressed_size = lzxpress_compress(buf, len, + compressed, sizeof(compressed)); + if (compressed_size < 0) { + abort(); + } + + decompressed_size = lzxpress_decompress(compressed, compressed_size, + decompressed, sizeof(decompressed)); + + if (decompressed_size != len) { + abort(); + } + if (memcmp(buf, decompressed, len) != 0) { + abort(); + } + + return 0; +} diff --git a/lib/fuzzing/fuzz_ndr_X.c b/lib/fuzzing/fuzz_ndr_X.c new file mode 100644 index 0000000..16109cc --- /dev/null +++ b/lib/fuzzing/fuzz_ndr_X.c @@ -0,0 +1,337 @@ +/* + Unix SMB/CIFS implementation. + Fuzzer for pidl-generated NDR pipes. + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Jelmer Vernooij 2006 + Copyright (C) Andrew Bartlett 2019 + Copyright (C) Catalyst.NET Ltd 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "system/locale.h" +#include "librpc/ndr/libndr.h" +#include "librpc/gen_ndr/ndr_dcerpc.h" +#include "util/byteorder.h" +#include "fuzzing/fuzzing.h" + +extern const struct ndr_interface_table FUZZ_PIPE_TABLE; + +#define FLAG_NDR64 4 + +enum { + TYPE_STRUCT = 0, + TYPE_IN, + TYPE_OUT +}; + +/* + * header design (little endian): + * + * struct { + * uint16_t flags; + * uint16_t function_or_struct_no; + * }; + */ + +/* + * We want an even number here to ensure 4-byte alignment later + * not just for efficiency but because the fuzzers are known to guess + * that numbers will be 4-byte aligned + */ +#define HEADER_SIZE 4 + +#define INVALID_FLAGS (~(FLAG_NDR64 | 3)) + +static const struct ndr_interface_call *find_function( + const struct ndr_interface_table *p, + unsigned int function_no) +{ + if (function_no >= p->num_calls) { + return NULL; + } + return &p->calls[function_no]; +} + +/* + * Get a public structure by number and return it as if it were + * a function. + */ +static const struct ndr_interface_call *find_struct( + const struct ndr_interface_table *p, + unsigned int struct_no, + struct ndr_interface_call *out_buffer) +{ + const struct ndr_interface_public_struct *s = NULL; + + if (struct_no >= p->num_public_structs) { + return NULL; + } + + s = &p->public_structs[struct_no]; + + *out_buffer = (struct ndr_interface_call) { + .name = s->name, + .struct_size = s->struct_size, + .ndr_pull = s->ndr_pull, + .ndr_push = s->ndr_push, + .ndr_print = s->ndr_print + }; + return out_buffer; +} + + +static NTSTATUS pull_chunks(struct ndr_pull *ndr_pull, + const struct ndr_interface_call_pipes *pipes) +{ + enum ndr_err_code ndr_err; + uint32_t i; + + for (i=0; i < pipes->num_pipes; i++) { + while (true) { + void *saved_mem_ctx; + uint32_t *count; + void *c; + + c = talloc_zero_size(ndr_pull, pipes->pipes[i].chunk_struct_size); + if (c == NULL) { + return NT_STATUS_NO_MEMORY; + } + /* + * Note: the first struct member is always + * 'uint32_t count;' + */ + count = (uint32_t *)c; + + saved_mem_ctx = ndr_pull->current_mem_ctx; + ndr_pull->current_mem_ctx = c; + ndr_err = pipes->pipes[i].ndr_pull(ndr_pull, NDR_SCALARS, c); + ndr_pull->current_mem_ctx = saved_mem_ctx; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(c); + return ndr_map_error2ntstatus(ndr_err); + } + if (*count == 0) { + talloc_free(c); + break; + } + talloc_free(c); + } + } + + return NT_STATUS_OK; +} + +static void ndr_print_nothing(struct ndr_print *ndr, const char *format, ...) +{ + /* + * This is here so that we walk the tree but don't output anything. + * This helps find buggy ndr_print routines + */ + + /* + * TODO: consider calling snprinf() to find strings without NULL + * terminators (for example) + */ +} + + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + uint8_t type; + ndr_flags_type pull_push_print_flags; + uint16_t fuzz_packet_flags, function; + TALLOC_CTX *mem_ctx = NULL; + libndr_flags ndr_flags = 0; + struct ndr_push *ndr_push; + enum ndr_err_code ndr_err; + struct ndr_interface_call f_buffer; + const struct ndr_interface_call *f = NULL; + NTSTATUS status; + +/* + * This allows us to build binaries to fuzz just one target function + * + * In this mode the input becomes the 'stub data', there is no prefix. + * + * There is no NDR64 support in this mode at this time. + */ +#if defined(FUZZ_TYPE) && defined(FUZZ_FUNCTION) +#undef HEADER_SIZE +#define HEADER_SIZE 0 + fuzz_packet_flags = 0; + type = FUZZ_TYPE; + function = FUZZ_FUNCTION; +#else + if (size < HEADER_SIZE) { + /* + * the first few bytes decide what is being fuzzed -- + * if they aren't all there we do nothing. + */ + return 0; + } + + fuzz_packet_flags = SVAL(data, 0); + if (fuzz_packet_flags & INVALID_FLAGS) { + return 0; + } + + function = SVAL(data, 2); + + type = fuzz_packet_flags & 3; + +#ifdef FUZZ_TYPE + /* + * Fuzz targets should have as small an interface as possible. + * This allows us to create 3 binaries for most pipes, + * TYPE_IN, TYPE_OUT and TYPE_STRUCT + * + * We keep the header format, and just exit early if it does + * not match. + */ + if (type != FUZZ_TYPE) { + return 0; + } +#endif +#endif + + switch (type) { + case TYPE_STRUCT: + pull_push_print_flags = NDR_SCALARS|NDR_BUFFERS; + f = find_struct(&FUZZ_PIPE_TABLE, function, &f_buffer); + break; + case TYPE_IN: + pull_push_print_flags = NDR_IN; + f = find_function(&FUZZ_PIPE_TABLE, function); + break; + case TYPE_OUT: + pull_push_print_flags = NDR_OUT; + f = find_function(&FUZZ_PIPE_TABLE, function); + break; + default: + return 0; + } + + if (f == NULL) { + return 0; + } + if (fuzz_packet_flags & FLAG_NDR64) { + ndr_flags |= LIBNDR_FLAG_NDR64; + } + + mem_ctx = talloc_init("ndrfuzz"); + + { + /* + * f->struct_size is well-controlled, it is essentially + * defined in the IDL + */ + uint8_t st[f->struct_size]; + + DATA_BLOB blob = data_blob_const(data + HEADER_SIZE, + size - HEADER_SIZE); + struct ndr_pull *ndr_pull = ndr_pull_init_blob(&blob, + mem_ctx); + + if (ndr_pull == NULL) { + perror("ndr_pull_init_blob"); + TALLOC_FREE(mem_ctx); + return 0; + } + + /* + * We must initialise the buffer (even if we would + * prefer not to for the sake of eg valgrind) as + * otherwise the special handler for 'out pointer with + * [size_is()] refers to in value with [ref]' fails to + * trigger + */ + memset(st, '\0', sizeof(st)); + + ndr_pull->flags |= LIBNDR_FLAG_REF_ALLOC; + ndr_pull->global_max_recursion = 128; + + if (type == TYPE_OUT) { + status = pull_chunks(ndr_pull, + &f->out_pipes); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(mem_ctx); + return 0; + } + } + + ndr_err = f->ndr_pull(ndr_pull, + pull_push_print_flags, + st); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + TALLOC_FREE(mem_ctx); + return 0; + } + + if (type == TYPE_IN) { + status = pull_chunks(ndr_pull, + &f->in_pipes); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(mem_ctx); + return 0; + } + } + + ndr_push = ndr_push_init_ctx(mem_ctx); + if (ndr_push == NULL) { + TALLOC_FREE(mem_ctx); + return 0; + } + + ndr_push->flags |= ndr_flags; + + /* + * Now push what was pulled, just in case we generated an + * invalid structure in memory, this should notice + */ + ndr_err = f->ndr_push(ndr_push, + pull_push_print_flags, + st); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + TALLOC_FREE(mem_ctx); + return 0; + } + + { + struct ndr_print *ndr_print = talloc_zero(mem_ctx, struct ndr_print); + ndr_print->print = ndr_print_nothing; + ndr_print->depth = 1; + + /* + * Finally print (to nowhere) the structure, this may also + * notice invalid memory + */ + f->ndr_print(ndr_print, + f->name, + pull_push_print_flags, + st); + } + } + TALLOC_FREE(mem_ctx); + + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} diff --git a/lib/fuzzing/fuzz_nmblib_parse_packet.c b/lib/fuzzing/fuzz_nmblib_parse_packet.c new file mode 100644 index 0000000..c8a2d03 --- /dev/null +++ b/lib/fuzzing/fuzz_nmblib_parse_packet.c @@ -0,0 +1,62 @@ +/* + Fuzz NMB parse_packet + Copyright (C) Catalyst IT 2020 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "../../source3/include/includes.h" +#include "libsmb/libsmb.h" +#include "libsmb/nmblib.h" +#include "fuzzing/fuzzing.h" + +#define PORT 138 +#define MAX_LENGTH (1024 * 1024) +char buf[MAX_LENGTH + 1]; + + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) +{ + struct packet_struct *p = NULL; + struct in_addr ip = { + 0x0100007f /* 127.0.0.1 */ + }; + + p = parse_packet((char *)input, + len, + NMB_PACKET, + ip, + PORT); + /* + * We expect NULL (parse failure) most of the time. + * + * When it is not NULL we want to ensure the parsed packet is + * reasonably sound. + */ + + if (p != NULL) { + struct nmb_packet *nmb = &p->packet.nmb; + pull_ascii_nstring(buf, MAX_LENGTH, + nmb->question.question_name.name); + build_packet(buf, MAX_LENGTH, p); + free_packet(p); + } + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} diff --git a/lib/fuzzing/fuzz_oLschema2ldif.c b/lib/fuzzing/fuzz_oLschema2ldif.c new file mode 100644 index 0000000..3bff8cf --- /dev/null +++ b/lib/fuzzing/fuzz_oLschema2ldif.c @@ -0,0 +1,71 @@ +/* + Fuzzing for oLschema2ldif + Copyright (C) Michael Hanselmann 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "fuzzing.h" +#include "utils/oLschema2ldif/lib.h" + +static FILE *devnull; + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + devnull = fopen("/dev/null", "w"); + + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + TALLOC_CTX *mem_ctx; + struct conv_options opt; + + if (len == 0) { + /* + * Otherwise fmemopen() will return null and set errno + * to EINVAL + */ + return 0; + } + + mem_ctx = talloc_init(__FUNCTION__); + if (mem_ctx == NULL) { + return 0; + } + + opt.in = fmemopen(buf, len, "r"); + opt.out = devnull; + opt.ldb_ctx = ldb_init(mem_ctx, NULL); + if (opt.ldb_ctx == NULL || opt.in == NULL) { + talloc_free(mem_ctx); + return 0; + } + + opt.basedn = ldb_dn_new(mem_ctx, opt.ldb_ctx, ""); + if (opt.basedn == NULL) { + talloc_free(mem_ctx); + return 0; + } + + process_file(mem_ctx, &opt); + + fclose(opt.in); + + talloc_free(mem_ctx); + + return 0; +} diff --git a/lib/fuzzing/fuzz_parse_lpq_entry.c b/lib/fuzzing/fuzz_parse_lpq_entry.c new file mode 100644 index 0000000..3537ce5 --- /dev/null +++ b/lib/fuzzing/fuzz_parse_lpq_entry.c @@ -0,0 +1,65 @@ +/* + Fuzzing parse_lpq_entry + Copyright (C) Douglas Bagnall <dbagnall@samba.org> 2021 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "../../source3/include/includes.h" +#include "printing.h" +#include "fuzzing/fuzzing.h" + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + +#define MAX_LENGTH (1024 * 1024) +char line[MAX_LENGTH + 1]; + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) +{ + enum printing_types printing_type; + print_queue_struct pq_buf = {0}; + print_status_struct status = {0}; + bool first; + unsigned x; + TALLOC_CTX *frame = NULL; + + if (len < 1 || len > MAX_LENGTH) { + return 0; + } + + x = input[0]; + input++; + len--; + + /* There are 14 types, default goes to bsd */ + printing_type = x & 15; + first = (x & 16) ? true : false; + + memcpy(line, input, len); + line[len] = '\0'; + + /* parse_lpq_bsd requires a stackframe */ + frame = talloc_stackframe(); + + parse_lpq_entry(printing_type, + line, + &pq_buf, /* out */ + &status, /* out */ + first); + talloc_free(frame); + return 0; +} diff --git a/lib/fuzzing/fuzz_reg_parse.c b/lib/fuzzing/fuzz_reg_parse.c new file mode 100644 index 0000000..bbc62ea --- /dev/null +++ b/lib/fuzzing/fuzz_reg_parse.c @@ -0,0 +1,46 @@ +/* + * Fuzzing for reg_parse + * Copyright (C) Michael Hanselmann 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "lib/util/fault.h" +#include "registry.h" +#include "registry/reg_parse.h" + +static FILE *fp; + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + fp = tmpfile(); + + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + const reg_parse_callback cb = {0}; + + rewind(fp); + (void)fwrite(buf, len, 1, fp); + (void)fflush(fp); + rewind(fp); + + (void)reg_parse_fd(fileno(fp), &cb, ""); + + return 0; +} diff --git a/lib/fuzzing/fuzz_regfio.c b/lib/fuzzing/fuzz_regfio.c new file mode 100644 index 0000000..588efb0 --- /dev/null +++ b/lib/fuzzing/fuzz_regfio.c @@ -0,0 +1,68 @@ +/* + * Unix SMB/CIFS implementation. + * Windows NT registry I/O library + * Copyright (C) Michael Hanselmann 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "system/filesys.h" +#include "lib/util/fault.h" +#include "registry/reg_objects.h" +#include "registry/regfio.h" + +static FILE *fp; +static char filename[128]; + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + fp = tmpfile(); + + (void)snprintf(filename, sizeof(filename), "/proc/self/fd/%d", fileno(fp)); + + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + REGF_FILE* regfile; + REGF_NK_REC *nk, *subkey; + + rewind(fp); + (void)fwrite(buf, len, 1, fp); + (void)fflush(fp); + + regfile = regfio_open(filename, O_RDONLY, 0600); + if (!regfile) { + goto out; + } + + regfile->ignore_checksums = true; + + nk = regfio_rootkey(regfile); + if (nk != NULL) { + nk->subkey_index = 0; + while ((subkey = regfio_fetch_subkey(regfile, nk))) { + } + } + +out: + if (regfile != NULL) { + regfio_close(regfile); + } + + return 0; +} diff --git a/lib/fuzzing/fuzz_sddl_access_check.c b/lib/fuzzing/fuzz_sddl_access_check.c new file mode 100644 index 0000000..a7bf7b3 --- /dev/null +++ b/lib/fuzzing/fuzz_sddl_access_check.c @@ -0,0 +1,206 @@ +/* + Fuzz access check using SDDL strings and a known token + Copyright (C) Catalyst IT 2023 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "libcli/security/security.h" +#include "libcli/security/conditional_ace.h" +#include "libcli/security/claims-conversions.h" +#include "lib/util/attr.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "librpc/gen_ndr/ndr_conditional_ace.h" +#include "lib/util/bytearray.h" +#include "fuzzing/fuzzing.h" + + +static struct security_token token = {0}; + +static struct dom_sid dom_sid = {0}; + +/* + * For this one we initialise a security token to have a few claims + * and SIDs. The fuzz strings contain SDDL that will be tested against + * this token in se_access_check() or sec_access_check_ds() -- + * supposing they compile. + */ + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + size_t i; + TALLOC_CTX *mem_ctx = talloc_new(NULL); + struct dom_sid *sid = NULL; + + struct claim_def { + const char *type; + const char *name; + const char *claim_sddl; + } claims[] = { + { + "user", + "shoe size", + "44" + }, + { + "user", + "©", + "{\"unknown\", \"\", \" ←ā\"}" + }, + { + "device", + "©", + "{\"unknown\", \" \", \" ←ā\"}" + }, + { + "device", + "least favourite groups", + "{SID(S-1-1-0),SID(S-1-5-3),SID(S-1-57777-333-33-33-2)}" + }, + { + "local", + "birds", + "{\"tern\"}" + }, + }; + + const char * device_sids[] = { + "S-1-1-0", + "S-1-333-66", + "S-1-2-3-4-5-6-7-8-9", + }; + const char * user_sids[] = { + "S-1-333-66", + "S-1-16-8448", + "S-1-9-8-7", + }; + + for (i = 0; i < ARRAY_SIZE(user_sids); i++) { + sid = sddl_decode_sid(mem_ctx, &user_sids[i], NULL); + if (sid == NULL) { + abort(); + } + add_sid_to_array(mem_ctx, sid, + &token.sids, + &token.num_sids); + } + + for (i = 0; i < ARRAY_SIZE(device_sids); i++) { + sid = sddl_decode_sid(mem_ctx, &device_sids[i], NULL); + if (sid == NULL) { + abort(); + } + add_sid_to_array(mem_ctx, sid, + &token.device_sids, + &token.num_device_sids); + } + + for (i = 0; i < ARRAY_SIZE(claims); i++) { + struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim = NULL; + struct claim_def c = claims[i]; + + claim = parse_sddl_literal_as_claim(mem_ctx, + c.name, + c.claim_sddl); + if (claim == NULL) { + abort(); + } + add_claim_to_token(mem_ctx, &token, claim, c.type); + } + + /* we also need a global domain SID */ + string_to_sid(&dom_sid, device_sids[2]); + return 0; +} + + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) +{ + TALLOC_CTX *mem_ctx = NULL; + struct security_descriptor *sd = NULL; + uint32_t access_desired; + uint32_t access_granted; + const char *sddl; + ssize_t i; + if (len < 5) { + return 0; + } + access_desired = PULL_LE_U32(input + len - 4, 0); + + /* + * check there is a '\0'. + * + * Note this allows double-dealing for the last 4 bytes: they are used + * as the access_desired mask (see just above) but also *could* be + * part of the sddl string. But this doesn't matter, for three + * reasons: + * + * 1. the desired access mask doesn't usually matter much. + * + * 2. the final '\0' is rarely the operative one. Usually the + * effective string ends a long time before the end of the input, and + * the tail is just junk that comes along for the ride. + * + * 3. Even if there is a case where the end of the SDDL is part of the + * mask, the evolution strategy is very likely to try a different mask, + * because it likes to add junk on the end. + * + * But still, you ask, WHY? So that the seeds from here can be shared + * back and forth with the fuzz_sddl_parse seeds, which have the same + * form of a null-terminated-string-with-trailing-junk. If we started + * the loop at `len - 5` instead of `len - 1`, there might be + * interesting seeds that are valid there that would fail here. That's + * all. + */ + for (i = len - 1; i >= 0; i--) { + if (input[i] == 0) { + break; + } + } + if (i < 0) { + return 0; + } + + sddl = (const char *)input; + mem_ctx = talloc_new(NULL); + + sd = sddl_decode(mem_ctx, sddl, &dom_sid); + if (sd == NULL) { + goto end; + } + +#ifdef FUZZ_SEC_ACCESS_CHECK_DS + /* + * The sec_access_check_ds() function has two arguments not found in + * se_access_check, and also not found in our fuzzing examples. + * + * One is a struct object_tree, which is used for object ACE types. + * The other is a SID, which is used as a default if an ACE lacks a + * SID. + */ + sec_access_check_ds(sd, + &token, + access_desired, + &access_granted, + NULL, + NULL); +#else + se_access_check(sd, &token, access_desired, &access_granted); +#endif + +end: + talloc_free(mem_ctx); + return 0; +} diff --git a/lib/fuzzing/fuzz_sddl_conditional_ace.c b/lib/fuzzing/fuzz_sddl_conditional_ace.c new file mode 100644 index 0000000..636ebf1 --- /dev/null +++ b/lib/fuzzing/fuzz_sddl_conditional_ace.c @@ -0,0 +1,121 @@ +/* + Fuzz sddl conditional ace decoding and encoding + Copyright (C) Catalyst IT 2023 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "libcli/security/security.h" +#include "lib/util/attr.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/conditional_ace.h" +#include "librpc/gen_ndr/conditional_ace.h" +#include "fuzzing/fuzzing.h" + + +#define MAX_LENGTH (1024 * 1024 - 1) +static char sddl_string[MAX_LENGTH + 1] = {0}; + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) +{ + TALLOC_CTX *mem_ctx = NULL; + bool ok; + struct ace_condition_script *s1 = NULL; + struct ace_condition_script *s2 = NULL; + const char *message = NULL; + size_t message_offset; + const char *resddl = NULL; + DATA_BLOB e1, e2, e3; + size_t length; + + if (len > MAX_LENGTH) { + return 0; + } + + memcpy(sddl_string, input, len); + sddl_string[len] = '\0'; + + mem_ctx = talloc_new(NULL); + + s1 = ace_conditions_compile_sddl(mem_ctx, + ACE_CONDITION_FLAG_ALLOW_DEVICE, + sddl_string, + &message, + &message_offset, + &length); + if (s1 == NULL) { + /* could assert message is non-empty */ + TALLOC_FREE(mem_ctx); + return 0; + } + + ok = conditional_ace_encode_binary(mem_ctx, s1, &e1); + if (! ok) { + abort(); + } + + s2 = parse_conditional_ace(mem_ctx, e1); + if (s2 == NULL) { + abort(); + } + + ok = conditional_ace_encode_binary(mem_ctx, s2, &e2); + if (! ok) { + abort(); + } + if (data_blob_cmp(&e1, &e2) != 0) { + abort(); + } + + /* + * We know now the SDDL representation compiles to a valid structure + * that survives a round trip through serialisation. + * + * A remaining question is whether it can be re-rendered as SDDL that + * compiles to the same blob. + */ + resddl = sddl_from_conditional_ace(mem_ctx, s2); + if (resddl == NULL) { + abort(); + } + + s2 = ace_conditions_compile_sddl(mem_ctx, + ACE_CONDITION_FLAG_ALLOW_DEVICE, + resddl, + &message, + &message_offset, + &length); + if (s2 == NULL) { + abort(); + } + + ok = conditional_ace_encode_binary(mem_ctx, s2, &e3); + if (! ok) { + abort(); + } + if (data_blob_cmp(&e1, &e3) != 0) { + abort(); + } + + TALLOC_FREE(mem_ctx); + return 0; +} diff --git a/lib/fuzzing/fuzz_sddl_parse.c b/lib/fuzzing/fuzz_sddl_parse.c new file mode 100644 index 0000000..05900b0 --- /dev/null +++ b/lib/fuzzing/fuzz_sddl_parse.c @@ -0,0 +1,110 @@ +/* + Fuzz sddl decoding and encoding + Copyright (C) Catalyst IT 2023 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/conditional_ace.h" +#include "fuzzing/fuzzing.h" +#include "util/charset/charset.h" + +#define MAX_LENGTH (100 * 1024 - 1) +static char sddl_string[MAX_LENGTH + 1] = {0}; +static struct dom_sid dom_sid = {0}; + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + string_to_sid(&dom_sid, + "S-1-5-21-2470180966-3899876309-2637894779"); + return 0; +} + + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) +{ + TALLOC_CTX *mem_ctx = NULL; + struct security_descriptor *sd1 = NULL; + struct security_descriptor *sd2 = NULL; + char *result = NULL; + bool ok; + + if (len > MAX_LENGTH) { + return 0; + } + + memcpy(sddl_string, input, len); + sddl_string[len] = '\0'; + + mem_ctx = talloc_new(NULL); + + sd1 = sddl_decode(mem_ctx, sddl_string, &dom_sid); + if (sd1 == NULL) { + goto end; + } + result = sddl_encode(mem_ctx, sd1, &dom_sid); + if (result == NULL) { + /* + * Because Samba currently doesn't enforce strict + * utf-8 parsing, illegal utf-8 sequences in + * sddl_string could have ferried bad characters + * through into the security descriptor conditions + * that we then find we can't encode. + * + * The proper solution is strict UTF-8 enforcement in + * sddl_decode, but for now we forgive unencodable + * security descriptors made from bad utf-8. + */ + size_t byte_len, char_len, utf16_len; + ok = utf8_check(sddl_string, len, + &byte_len, &char_len, &utf16_len); + if (!ok) { + goto end; + } + /* utf-8 was fine, but we couldn't encode! */ + abort(); + } + + sd2 = sddl_decode(mem_ctx, result, &dom_sid); + if (sd2 == NULL) { + if (strlen(result) > CONDITIONAL_ACE_MAX_LENGTH) { + /* + * This could fail if a unicode string or + * attribute name that contains escapable + * bytes (e.g '\x0b') in an unescaped form in + * the original string ends up with them in + * the escaped form ("%000b") in the result + * string, making the entire attribute name + * too long for the arbitrary limit we set for + * SDDL attribute names. + * + * We could increase that arbitrary limit (to, + * say, CONDITIONAL_ACE_MAX_LENGTH * 5), but + * that is getting very far from real world + * needs. + */ + goto end; + } + abort(); + } + ok = security_descriptor_equal(sd1, sd2); + if (!ok) { + abort(); + } +end: + talloc_free(mem_ctx); + return 0; +} diff --git a/lib/fuzzing/fuzz_security_token_vs_descriptor.c b/lib/fuzzing/fuzz_security_token_vs_descriptor.c new file mode 100644 index 0000000..f9b2552 --- /dev/null +++ b/lib/fuzzing/fuzz_security_token_vs_descriptor.c @@ -0,0 +1,78 @@ +/* + Fuzz a security token and descriptor through an access check + Copyright (C) Catalyst IT 2023 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/security.h" +#include "fuzzing/fuzzing.h" + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) +{ + TALLOC_CTX *mem_ctx = NULL; + struct security_token_descriptor_fuzzing_pair p = {0}; + enum ndr_err_code ndr_err; + uint32_t access_granted; + + DATA_BLOB blob = { + .data = input, + .length = len + }; + + mem_ctx = talloc_new(NULL); + + ndr_err = ndr_pull_struct_blob( + &blob, mem_ctx, &p, + (ndr_pull_flags_fn_t)ndr_pull_security_token_descriptor_fuzzing_pair); + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + goto end; + } + +#ifdef FUZZ_SEC_ACCESS_CHECK_DS + /* + * The sec_access_check_ds() function has two arguments not found in + * se_access_check, and also not found in our fuzzing examples. + * + * One is a struct object_tree, which is used for object ACE types. + * The other is a SID, which is used as a default if an ACE lacks a + * SID. + */ + sec_access_check_ds(&p.sd, + &p.token, + p.access_desired, + &access_granted, + NULL, + NULL); +#else + se_access_check(&p.sd, + &p.token, + p.access_desired, + &access_granted); +#endif + +end: + talloc_free(mem_ctx); + return 0; +} diff --git a/lib/fuzzing/fuzz_sess_crypt_blob.c b/lib/fuzzing/fuzz_sess_crypt_blob.c new file mode 100644 index 0000000..bed697e --- /dev/null +++ b/lib/fuzzing/fuzz_sess_crypt_blob.c @@ -0,0 +1,55 @@ +/* + Fuzzing sess_*crypt_blob + Copyright (C) Catalyst IT 2020 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "libcli/auth/libcli_auth.h" +#include "session.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) +{ + TALLOC_CTX *mem_ctx = NULL; + DATA_BLOB blob, session_key, out; + size_t slen; + if (len < 1) { + return 0; + } + + slen = input[0]; + if (len < slen + 1) { + return 0; + } + + session_key.data = input + 1; + session_key.length = slen; + blob.data = input + 1 + slen; + blob.length = len - slen - 1; + + mem_ctx = talloc_new(NULL); + + out = sess_encrypt_blob(mem_ctx, &blob, &session_key); + sess_decrypt_blob(mem_ctx, &blob, &session_key, &out); + + TALLOC_FREE(mem_ctx); + return 0; +} diff --git a/lib/fuzzing/fuzz_stable_sort.c b/lib/fuzzing/fuzz_stable_sort.c new file mode 100644 index 0000000..e2195cb --- /dev/null +++ b/lib/fuzzing/fuzz_stable_sort.c @@ -0,0 +1,88 @@ +/* + Fuzzing for stable_sort + Copyright © Catalyst IT + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "talloc.h" +#include "util/stable_sort.h" + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + + +#define CMP_FN(type) static int cmp_ ## type (type *a, type *b) \ +{\ + if (*a > *b) {\ + return 1;\ + }\ + if (*a < *b) {\ + return -1;\ + }\ + return 0;\ +} + +CMP_FN(uint8_t) +CMP_FN(uint16_t) +CMP_FN(uint32_t) +CMP_FN(uint64_t) + +#define MAX_SIZE (1024 * 1024) + +int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + TALLOC_CTX *mem_ctx = NULL; + samba_compare_fn_t fn; + size_t s, i; + uint8_t buf2[MAX_SIZE]; + + if (len < 1 || len > MAX_SIZE) { + return 0; + } + s = 1 << (buf[0] & 3); + if (s == 1) { + fn = (samba_compare_fn_t)cmp_uint8_t; + } else if (s == 2) { + fn = (samba_compare_fn_t)cmp_uint16_t; + } else if (s == 4) { + fn = (samba_compare_fn_t)cmp_uint32_t; + } else { + fn = (samba_compare_fn_t)cmp_uint64_t; + } + buf++; + len--; + len -= len & (s - 1); + + mem_ctx = talloc_new(NULL); + memcpy(buf2, buf, len); + + stable_sort_talloc(mem_ctx, buf2, len / s, s, fn); + + talloc_free(mem_ctx); + + for (i = s; i < len; i += s) { + int c = fn(&buf2[i - s], &buf2[i]); + if (c > 0) { + abort(); + } + } + + return 0; +} diff --git a/lib/fuzzing/fuzz_stable_sort_r.c b/lib/fuzzing/fuzz_stable_sort_r.c new file mode 100644 index 0000000..68be73b --- /dev/null +++ b/lib/fuzzing/fuzz_stable_sort_r.c @@ -0,0 +1,69 @@ +/* + Fuzzing for stable_sort + Copyright © Catalyst IT + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "util/stable_sort.h" + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + +/* + * For a "context" we use a byte that the values are XORed with before + * comparison, for a non-obvious but stable sort order. + */ +static int cmp_int8(int8_t *a, int8_t *b, int8_t *c) +{ + return (*a ^ *c) - (*b ^ *c); +} + + +#define MAX_SIZE (1024 * 1024) + +int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + size_t i; + int8_t buf2[MAX_SIZE]; + int8_t aux[MAX_SIZE]; + int8_t context; + + if (len < 1 || len > MAX_SIZE) { + return 0; + } + context = (int8_t)buf[0]; + buf++; + len--; + + memcpy(buf2, buf, len); + + stable_sort_r(buf2, aux, len, 1, + (samba_compare_with_context_fn_t)cmp_int8, + &context); + + for (i = 1; i < len; i++) { + int c = cmp_int8(&buf2[i - 1], &buf2[i], &context); + if (c > 0) { + abort(); + } + } + + return 0; +} diff --git a/lib/fuzzing/fuzz_tiniparser.c b/lib/fuzzing/fuzz_tiniparser.c new file mode 100644 index 0000000..a5c9310 --- /dev/null +++ b/lib/fuzzing/fuzz_tiniparser.c @@ -0,0 +1,51 @@ +/* + Fuzzing for trivial smb.conf parsing code. + Copyright (C) Michael Hanselmann 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "fuzzing.h" +#include "lib/util/tiniparser.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + FILE *fp = NULL; + struct tiniparser_dictionary *d = NULL; + + if (len == 0) { + /* + * Otherwise fmemopen() will return null and set errno + * to EINVAL + */ + return 0; + } + + fp = fmemopen(buf, len, "r"); + + d = tiniparser_load_stream(fp); + if (d != NULL) { + tiniparser_freedict(d); + } + + fclose(fp); + + return 0; +} diff --git a/lib/fuzzing/fuzzing.c b/lib/fuzzing/fuzzing.c new file mode 100644 index 0000000..f0d7fb4 --- /dev/null +++ b/lib/fuzzing/fuzzing.c @@ -0,0 +1,21 @@ +/* + Unix SMB/CIFS implementation. + Fuzzing utility functions + Copyright (C) Michael Hanselmann 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "fuzzing/fuzzing.h" diff --git a/lib/fuzzing/fuzzing.h b/lib/fuzzing/fuzzing.h new file mode 100644 index 0000000..0d3cb70 --- /dev/null +++ b/lib/fuzzing/fuzzing.h @@ -0,0 +1,30 @@ +/* + Unix SMB/CIFS implementation. + Fuzzing utility functions + Copyright (C) Michael Hanselmann 2019 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _SAMBA_FUZZING_H +#define _SAMBA_FUZZING_H + +#include <stddef.h> +#include <stdint.h> + +/* Prototypes for fuzzing interface */ +int LLVMFuzzerInitialize(int *argc, char ***argv); +int LLVMFuzzerTestOneInput(const uint8_t * buf, size_t len); + +#endif /* _SAMBA_FUZZING_H */ diff --git a/lib/fuzzing/oss-fuzz/build_image.sh b/lib/fuzzing/oss-fuzz/build_image.sh new file mode 100755 index 0000000..62a626b --- /dev/null +++ b/lib/fuzzing/oss-fuzz/build_image.sh @@ -0,0 +1,7 @@ +#!/bin/sh -e + +DIST=ubuntu2004 +SCRIPT_DIR=$(dirname $0) + +$SCRIPT_DIR/../../../bootstrap/generated-dists/$DIST/bootstrap.sh +$SCRIPT_DIR/../../../bootstrap/generated-dists/$DIST/locale.sh diff --git a/lib/fuzzing/oss-fuzz/build_samba.sh b/lib/fuzzing/oss-fuzz/build_samba.sh new file mode 100755 index 0000000..90c1733 --- /dev/null +++ b/lib/fuzzing/oss-fuzz/build_samba.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# This is not a general-purpose build script, but instead one specific +# to the Google oss-fuzz compile environment. +# +# https://google.github.io/oss-fuzz/getting-started/new-project-guide/#Requirements +# +# https://github.com/google/oss-fuzz/blob/master/infra/base-images/base-builder/README.md#provided-environment-variables +# +# This file is run by +# https://github.com/google/oss-fuzz/blob/master/projects/samba/build.sh +# which does nothing else. +# +# Additional arguments are passed to configure, to allow this to be +# tested in autobuild.py +# + +# Ensure we give good trace info, fail right away and fail with unset +# variables +set -e +set -x +set -u + +"$(dirname "${0}")"/do_build.sh "$@" +"$(dirname "${0}")"/check_build.sh "${OUT}"
\ No newline at end of file diff --git a/lib/fuzzing/oss-fuzz/check_build.sh b/lib/fuzzing/oss-fuzz/check_build.sh new file mode 100755 index 0000000..487addf --- /dev/null +++ b/lib/fuzzing/oss-fuzz/check_build.sh @@ -0,0 +1,48 @@ +#!/bin/sh -eux +# +# A very simple check script to confirm we still provide binaries +# that look like the targets oss-fuzz wants. +# +# A much stronger check is available in oss-fuzz via +# infra/helper.py check_build samba +# + +# oss-fuzz provides an OUT variable, so for clarity this script +# uses the same. See build_samba.sh +OUT=$1 + +# build_samba.sh will have put a non-zero number of fuzzers here. If +# there are none, this will fail as it becomes literally fuzz_* + +seeds_found=no + +for bin in $OUT/fuzz_*; do + # we only want to look at the elf files, not the zips + if [ ${bin%_seed_corpus.zip} != $bin ]; then + continue + fi + # Confirm that the chrpath was reset to lib/ in the same directory + # as the binary. RPATH (not RUNPATH) is critical, otherwise + # libraries used by libraries won't be found on the oss-fuzz + # target host. + chrpath -l $bin | grep 'RPATH=$ORIGIN/lib' + + # Confirm that we link to at least some libraries in this + # directory (shows that the libraries were found and copied). + ldd $bin | grep "$OUT/lib" + num_libs=$(ldd $bin | grep -v ld-linux | grep -v linux-vdso | grep -v "$OUT/lib" | wc -l) + + if [ 0$num_libs -ne 0 ]; then + echo "some libraries not linked to $ORIGIN/lib, oss-fuzz will fail!" + exit 1 + fi + + if [ -f ${bin}_seed_corpus.zip ]; then + seeds_found=yes + fi +done + +if [ $seeds_found = no ]; then + echo "no seed zip files were found!" + exit 1 +fi diff --git a/lib/fuzzing/oss-fuzz/do_build.sh b/lib/fuzzing/oss-fuzz/do_build.sh new file mode 100755 index 0000000..29a6ceb --- /dev/null +++ b/lib/fuzzing/oss-fuzz/do_build.sh @@ -0,0 +1,294 @@ +#!/bin/sh +# +# This is not a general-purpose build script, but instead one specific +# to the Google oss-fuzz compile environment. +# +# https://google.github.io/oss-fuzz/getting-started/new-project-guide/#Requirements +# +# https://github.com/google/oss-fuzz/blob/master/infra/base-images/base-builder/README.md#provided-environment-variables +# +# This file is run by build_samba.sh, which is run by +# https://github.com/google/oss-fuzz/blob/master/projects/samba/build.sh +# which does nothing else. +# +# We have to push to oss-fuzz CFLAGS into the waf ADDITIONAL_CFLAGS +# as otherwise waf's configure fails linking the first test binary +# +# CFLAGS are supplied by the caller, eg the oss-fuzz compile command +# +# Additional arguments are passed to configure, to allow this to be +# tested in autobuild.py +# + +# Ensure we give good trace info, fail right away and fail with unset +# variables +set -e +set -x +set -u + +# It is critical that this script, just as the rest of Samba's GitLab +# CI docker has LANG set to en_US.utf8 (oss-fuzz fails to set this) +if [ -f /etc/default/locale ]; then + . /etc/default/locale +elif [ -f /etc/locale.conf ]; then + . /etc/locale.conf +fi +export LANG +export LC_ALL + +ADDITIONAL_CFLAGS="$CFLAGS" +export ADDITIONAL_CFLAGS +CFLAGS="" +export CFLAGS +LD="$CXX" +export LD + +# Use the system Python, not the OSS-Fuzz provided statically linked +# and instrumented Python, because we can't statically link. + +PYTHON=/usr/bin/python3 +export PYTHON + +# $SANITIZER is provided by the oss-fuzz "compile" command +# +# We need to add the waf configure option as otherwise when we also +# get (eg) -fsanitize=address via the CFLAGS we will fail to link +# correctly + +case "$SANITIZER" in +address) + SANITIZER_ARG='--address-sanitizer' + ;; +undefined) + SANITIZER_ARG='--undefined-sanitizer' + ;; +coverage) + # Thankfully clang operating as ld has no objection to the + # cc style options, so we can just set ADDITIONAL_LDFLAGS + # to ensure the coverage build is done, despite waf splitting + # the compile and link phases. + ADDITIONAL_LDFLAGS="${ADDITIONAL_LDFLAGS:-} $COVERAGE_FLAGS" + export ADDITIONAL_LDFLAGS + + SANITIZER_ARG='' + ;; +esac + +# $LIB_FUZZING_ENGINE is provided by the oss-fuzz "compile" command +# + +# --disable-new-dtags linker flag creates fuzzer binaries with RPATH +# header instead of RUNPATH header. Modern linkers use RUNPATH by +# default. +./configure -C --without-gettext --enable-debug --enable-developer \ + --enable-libfuzzer \ + $SANITIZER_ARG \ + --disable-warnings-as-errors \ + --abi-check-disable \ + "--fuzz-target-ldflags=-Wl,--disable-new-dtags $LIB_FUZZING_ENGINE" \ + --nonshared-binary=ALL \ + "$@" \ + LINK_CC="$CXX" + +make -j + +# Make a directory for the system shared libraries to be copied into +mkdir -p $OUT/lib + +# oss-fuzz would prefer for all the binaries put into $OUT to be +# statically linked. +# +# We can't static link to all the system libs with waf, so copy the +# libraries we need to $OUT/lib and set the rpath to point there. +# This is similar to how firefox handles this. +# +# NOTE on RPATH vs RUNPATH: +# +# RUNPATH appears to be the more modern version, and so modern ld.bfd +# and ld.gold only set RUNPATH. RUNPATH makes sense on most systems, +# but not for our hack. +# +# If we use RUNPATH, we can get an error like this: +# Step #6: Error occurred while running fuzz_nmblib_parse_packet: +# Step #6: /workspace/out/coverage/fuzz_nmblib_parse_packet: error while loading shared libraries: libavahi-common.so.3: cannot open shared object file: No such file or directory +# +# This is because the full contents of $OUT are copied to yet another +# host, which otherwise does not have much of linux at all. oss-fuzz +# prefers a static binary because that will 'just work', but we can't +# do that, so we need to use linker tricks. +# +# If the linker used RUNPATH (eg ld.bfd on Ubuntu 18.04 and later, ld.gold): +# * bin=fuzz_nmblib_parse_packet +# * OUT=/tmp/3/b12207/prefix/samba-fuzz +# * chrpath -r '$ORIGIN/lib' $OUT/$bin' +# * ldd $OUT/$bin +# linux-vdso.so.1 (0x00007ffd4b7a5000) +# libasan.so.5 => /tmp/3/b12207/prefix/samba-fuzz/lib/libasan.so.5 (0x00007ff25bdd0000) +# libldap_r-2.4.so.2 => /tmp/3/b12207/prefix/samba-fuzz/lib/libldap_r-2.4.so.2 (0x00007ff25bd7a000) +# liblber-2.4.so.2 => /tmp/3/b12207/prefix/samba-fuzz/lib/liblber-2.4.so.2 (0x00007ff25bd69000) +# libunwind-x86_64.so.8 => /tmp/3/b12207/prefix/samba-fuzz/lib/libunwind-x86_64.so.8 (0x00007ff25bd47000) +# libunwind.so.8 => /tmp/3/b12207/prefix/samba-fuzz/lib/libunwind.so.8 (0x00007ff25bd2a000) +# libgnutls.so.30 => /tmp/3/b12207/prefix/samba-fuzz/lib/libgnutls.so.30 (0x00007ff25bb54000) +# libdl.so.2 => /tmp/3/b12207/prefix/samba-fuzz/lib/libdl.so.2 (0x00007ff25bb4c000) +# libz.so.1 => /tmp/3/b12207/prefix/samba-fuzz/lib/libz.so.1 (0x00007ff25bb30000) +# libjansson.so.4 => /tmp/3/b12207/prefix/samba-fuzz/lib/libjansson.so.4 (0x00007ff25bb21000) +# libresolv.so.2 => /tmp/3/b12207/prefix/samba-fuzz/lib/libresolv.so.2 (0x00007ff25bb05000) +# libsystemd.so.0 => /tmp/3/b12207/prefix/samba-fuzz/lib/libsystemd.so.0 (0x00007ff25ba58000) +# libpthread.so.0 => /tmp/3/b12207/prefix/samba-fuzz/lib/libpthread.so.0 (0x00007ff25ba35000) +# libicuuc.so.66 => /tmp/3/b12207/prefix/samba-fuzz/lib/libicuuc.so.66 (0x00007ff25b84d000) +# libicui18n.so.66 => /tmp/3/b12207/prefix/samba-fuzz/lib/libicui18n.so.66 (0x00007ff25b54e000) +# libcap.so.2 => /tmp/3/b12207/prefix/samba-fuzz/lib/libcap.so.2 (0x00007ff25b545000) +# libbsd.so.0 => /tmp/3/b12207/prefix/samba-fuzz/lib/libbsd.so.0 (0x00007ff25b52b000) +# libnsl.so.1 => /tmp/3/b12207/prefix/samba-fuzz/lib/libnsl.so.1 (0x00007ff25b50e000) +# libc.so.6 => /tmp/3/b12207/prefix/samba-fuzz/lib/libc.so.6 (0x00007ff25b31c000) +# librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007ff25b2f2000) +# libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ff25b1a3000) +# libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007ff25b188000) +# libsasl2.so.2 => /usr/lib/x86_64-linux-gnu/libsasl2.so.2 (0x00007ff25b16b000) +# libgssapi.so.3 => /usr/lib/x86_64-linux-gnu/libgssapi.so.3 (0x00007ff25b126000) +# liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x00007ff25b0fd000) +# /lib64/ld-linux-x86-64.so.2 (0x00007ff25ea0c000) +# libp11-kit.so.0 => /usr/lib/x86_64-linux-gnu/libp11-kit.so.0 (0x00007ff25afc5000) +# libidn2.so.0 => /usr/lib/x86_64-linux-gnu/libidn2.so.0 (0x00007ff25afa4000) +# libunistring.so.2 => /usr/lib/x86_64-linux-gnu/libunistring.so.2 (0x00007ff25ae22000) +# libtasn1.so.6 => /usr/lib/x86_64-linux-gnu/libtasn1.so.6 (0x00007ff25ae0c000) +# libnettle.so.7 => /usr/lib/x86_64-linux-gnu/libnettle.so.7 (0x00007ff25add2000) +# libhogweed.so.5 => /usr/lib/x86_64-linux-gnu/libhogweed.so.5 (0x00007ff25ad9a000) +# libgmp.so.10 => /usr/lib/x86_64-linux-gnu/libgmp.so.10 (0x00007ff25ad14000) +# liblz4.so.1 => /usr/lib/x86_64-linux-gnu/liblz4.so.1 (0x00007ff25acf3000) +# libgcrypt.so.20 => /usr/lib/x86_64-linux-gnu/libgcrypt.so.20 (0x00007ff25abd5000) +# libicudata.so.66 => /usr/lib/x86_64-linux-gnu/libicudata.so.66 (0x00007ff259114000) +# libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007ff258f33000) +# libheimntlm.so.0 => /usr/lib/x86_64-linux-gnu/libheimntlm.so.0 (0x00007ff258f25000) +# libkrb5.so.26 => /usr/lib/x86_64-linux-gnu/libkrb5.so.26 (0x00007ff258e92000) +# libasn1.so.8 => /usr/lib/x86_64-linux-gnu/libasn1.so.8 (0x00007ff258deb000) +# libcom_err.so.2 => /lib/x86_64-linux-gnu/libcom_err.so.2 (0x00007ff258de4000) +# libhcrypto.so.4 => /usr/lib/x86_64-linux-gnu/libhcrypto.so.4 (0x00007ff258dac000) +# libroken.so.18 => /usr/lib/x86_64-linux-gnu/libroken.so.18 (0x00007ff258d93000) +# libffi.so.7 => /usr/lib/x86_64-linux-gnu/libffi.so.7 (0x00007ff258d85000) +# libgpg-error.so.0 => /lib/x86_64-linux-gnu/libgpg-error.so.0 (0x00007ff258d62000) +# libwind.so.0 => /usr/lib/x86_64-linux-gnu/libwind.so.0 (0x00007ff258d38000) +# libheimbase.so.1 => /usr/lib/x86_64-linux-gnu/libheimbase.so.1 (0x00007ff258d26000) +# libhx509.so.5 => /usr/lib/x86_64-linux-gnu/libhx509.so.5 (0x00007ff258cd8000) +# libsqlite3.so.0 => /usr/lib/x86_64-linux-gnu/libsqlite3.so.0 (0x00007ff258bad000) +# libcrypt.so.1 => /lib/x86_64-linux-gnu/libcrypt.so.1 (0x00007ff258b72000) +# +# Note how all the dependencies of libc and gnutls are not forced to +# $OUT/lib (via the magic $ORIGIN variable, meaning the directory of +# the binary). These will not be found on the target system! +# +# If the linker used RPATH however +# * bin=fuzz_nmblib_parse_packet +# * OUT=/tmp/3/b22/prefix/samba-fuzz +# * chrpath -r '$ORIGIN/lib' $OUT/$bin' +# * ldd $OUT/$bin +# linux-vdso.so.1 => (0x00007ffef85c7000) +# libasan.so.2 => /tmp/3/b22/prefix/samba-fuzz/lib/libasan.so.2 (0x00007f3668b4f000) +# libldap_r-2.4.so.2 => /tmp/3/b22/prefix/samba-fuzz/lib/libldap_r-2.4.so.2 (0x00007f36688fe000) +# liblber-2.4.so.2 => /tmp/3/b22/prefix/samba-fuzz/lib/liblber-2.4.so.2 (0x00007f36686ef000) +# libunwind-x86_64.so.8 => /tmp/3/b22/prefix/samba-fuzz/lib/libunwind-x86_64.so.8 (0x00007f36684d0000) +# libunwind.so.8 => /tmp/3/b22/prefix/samba-fuzz/lib/libunwind.so.8 (0x00007f36682b5000) +# libgnutls.so.30 => /tmp/3/b22/prefix/samba-fuzz/lib/libgnutls.so.30 (0x00007f3667f85000) +# libdl.so.2 => /tmp/3/b22/prefix/samba-fuzz/lib/libdl.so.2 (0x00007f3667d81000) +# libz.so.1 => /tmp/3/b22/prefix/samba-fuzz/lib/libz.so.1 (0x00007f3667b67000) +# libjansson.so.4 => /tmp/3/b22/prefix/samba-fuzz/lib/libjansson.so.4 (0x00007f366795a000) +# libresolv.so.2 => /tmp/3/b22/prefix/samba-fuzz/lib/libresolv.so.2 (0x00007f366773f000) +# libsystemd.so.0 => /tmp/3/b22/prefix/samba-fuzz/lib/libsystemd.so.0 (0x00007f366be2f000) +# libpthread.so.0 => /tmp/3/b22/prefix/samba-fuzz/lib/libpthread.so.0 (0x00007f3667522000) +# libicuuc.so.55 => /tmp/3/b22/prefix/samba-fuzz/lib/libicuuc.so.55 (0x00007f366718e000) +# libicui18n.so.55 => /tmp/3/b22/prefix/samba-fuzz/lib/libicui18n.so.55 (0x00007f3666d2c000) +# libcap.so.2 => /tmp/3/b22/prefix/samba-fuzz/lib/libcap.so.2 (0x00007f3666b26000) +# libbsd.so.0 => /tmp/3/b22/prefix/samba-fuzz/lib/libbsd.so.0 (0x00007f3666911000) +# libnsl.so.1 => /tmp/3/b22/prefix/samba-fuzz/lib/libnsl.so.1 (0x00007f36666f8000) +# libc.so.6 => /tmp/3/b22/prefix/samba-fuzz/lib/libc.so.6 (0x00007f366632e000) +# libm.so.6 => /tmp/3/b22/prefix/samba-fuzz/lib/libm.so.6 (0x00007f3666025000) +# libgcc_s.so.1 => /tmp/3/b22/prefix/samba-fuzz/lib/libgcc_s.so.1 (0x00007f3665e0f000) +# libsasl2.so.2 => /tmp/3/b22/prefix/samba-fuzz/lib/libsasl2.so.2 (0x00007f3665bf4000) +# libgssapi.so.3 => /tmp/3/b22/prefix/samba-fuzz/lib/libgssapi.so.3 (0x00007f36659b3000) +# liblzma.so.5 => /tmp/3/b22/prefix/samba-fuzz/lib/liblzma.so.5 (0x00007f3665791000) +# /lib64/ld-linux-x86-64.so.2 (0x00007f366bc93000) +# libp11-kit.so.0 => /tmp/3/b22/prefix/samba-fuzz/lib/libp11-kit.so.0 (0x00007f366552d000) +# libidn.so.11 => /tmp/3/b22/prefix/samba-fuzz/lib/libidn.so.11 (0x00007f36652fa000) +# libtasn1.so.6 => /tmp/3/b22/prefix/samba-fuzz/lib/libtasn1.so.6 (0x00007f36650e7000) +# libnettle.so.6 => /tmp/3/b22/prefix/samba-fuzz/lib/libnettle.so.6 (0x00007f3664eb1000) +# libhogweed.so.4 => /tmp/3/b22/prefix/samba-fuzz/lib/libhogweed.so.4 (0x00007f3664c7e000) +# libgmp.so.10 => /tmp/3/b22/prefix/samba-fuzz/lib/libgmp.so.10 (0x00007f36649fe000) +# libselinux.so.1 => /tmp/3/b22/prefix/samba-fuzz/lib/libselinux.so.1 (0x00007f36647dc000) +# librt.so.1 => /tmp/3/b22/prefix/samba-fuzz/lib/librt.so.1 (0x00007f36645d4000) +# libgcrypt.so.20 => /tmp/3/b22/prefix/samba-fuzz/lib/libgcrypt.so.20 (0x00007f36642f3000) +# libicudata.so.55 => /tmp/3/b22/prefix/samba-fuzz/lib/libicudata.so.55 (0x00007f366283c000) +# libstdc++.so.6 => /tmp/3/b22/prefix/samba-fuzz/lib/libstdc++.so.6 (0x00007f36624ba000) +# libheimntlm.so.0 => /tmp/3/b22/prefix/samba-fuzz/lib/libheimntlm.so.0 (0x00007f36622b1000) +# libkrb5.so.26 => /tmp/3/b22/prefix/samba-fuzz/lib/libkrb5.so.26 (0x00007f3662027000) +# libasn1.so.8 => /tmp/3/b22/prefix/samba-fuzz/lib/libasn1.so.8 (0x00007f3661d85000) +# libcom_err.so.2 => /tmp/3/b22/prefix/samba-fuzz/lib/libcom_err.so.2 (0x00007f3661b81000) +# libhcrypto.so.4 => /tmp/3/b22/prefix/samba-fuzz/lib/libhcrypto.so.4 (0x00007f366194e000) +# libroken.so.18 => /tmp/3/b22/prefix/samba-fuzz/lib/libroken.so.18 (0x00007f3661738000) +# libffi.so.6 => /tmp/3/b22/prefix/samba-fuzz/lib/libffi.so.6 (0x00007f3661530000) +# libpcre.so.3 => /tmp/3/b22/prefix/samba-fuzz/lib/libpcre.so.3 (0x00007f36612c0000) +# libgpg-error.so.0 => /tmp/3/b22/prefix/samba-fuzz/lib/libgpg-error.so.0 (0x00007f36610ac000) +# libwind.so.0 => /tmp/3/b22/prefix/samba-fuzz/lib/libwind.so.0 (0x00007f3660e83000) +# libheimbase.so.1 => /tmp/3/b22/prefix/samba-fuzz/lib/libheimbase.so.1 (0x00007f3660c74000) +# libhx509.so.5 => /tmp/3/b22/prefix/samba-fuzz/lib/libhx509.so.5 (0x00007f3660a29000) +# libsqlite3.so.0 => /tmp/3/b22/prefix/samba-fuzz/lib/libsqlite3.so.0 (0x00007f3660754000) +# libcrypt.so.1 => /tmp/3/b22/prefix/samba-fuzz/lib/libcrypt.so.1 (0x00007f366051c000) +# +# See how the runtime linker seems to honour the RPATH for +# dependencies of dependencies in this case. This helps us us lot. + +for x in bin/fuzz_*; do + # Copy any system libraries needed by this fuzzer to $OUT/lib. + + # We run ldd on $x, the fuzz_binary in bin/ which has not yet had + # the RPATH altered. This is clearer for debugging in local + # development builds as $OUT is not cleaned between runs. + # + # Otherwise trying to re-run this can see cp can fail with: + # cp: '/out/lib/libgcc_s.so.1' and '/out/lib/libgcc_s.so.1' are the same file + # which is really confusing! + + # The cut for ( and ' ' removes the special case references to: + # linux-vdso.so.1 => (0x00007ffe8f2b2000) + # /lib64/ld-linux-x86-64.so.2 (0x00007fc63ea6f000) + + ldd $x | cut -f 2 -d '>' | cut -f 1 -d \( | cut -f 2 -d ' ' | xargs -i cp \{\} $OUT/lib/ + + cp $x $OUT/ + bin=$(basename $x) + + # This means the copied libraries are found on the runner. + # + # The binaries should we built with RPATH, not RUNPATH, to allow + # libraries used by libraries to be found. This command retains the + # RPATH/RUNPATH header and only changes the path. We later verify this + # in the check_build.sh script. + chrpath -r '$ORIGIN/lib' $OUT/$bin + + # Truncate the original binary to save space + echo -n >$x + +done + +# Strip RUNPATH: or RPATH: entries from shared libraries copied over to $OUT/lib. +# When those libraries get loaded and have further dependencies, a RUNPATH: header +# will cause the dynamic linker to search in the runpath, and not in $OUT/lib, +# and there's no way it will be found in the fuzzing env. +# +# So how is the indirect dependency found in $OUT/lib? Well, suppose the fuzzer binary +# links library A which links library B. During linking, both A and B as listed in the +# executable file's runtime dependencies (This was pioneered in Fedora 13 in 2010, but +# is common behavior now). So we have the fuzzer binary with RPATH set to $OUT/lib, and +# a dependency on library B, and it will therefore find library B in $OUT/lib. On the +# hand, if we keep the RUNPATH in library A, and load A first, it will try loading +# library B as a dependency of A from the wrong place. +chrpath -d $OUT/lib/* + +# Grab the seeds dictionary from github and put the seed zips in place +# beside their executables. + +wget https://gitlab.com/samba-team/samba-fuzz-seeds/-/jobs/artifacts/master/download?job=zips \ + -O seeds.zip + +# We might not have unzip, but we do have python +$PYTHON -mzipfile -e seeds.zip $OUT +rm -f seeds.zip diff --git a/lib/fuzzing/patches/collect-access-check-seeds.txt b/lib/fuzzing/patches/collect-access-check-seeds.txt new file mode 100644 index 0000000..db85f40 --- /dev/null +++ b/lib/fuzzing/patches/collect-access-check-seeds.txt @@ -0,0 +1,256 @@ +From b461fdf28c71b54ad5ebe663ea09212856e61973 Mon Sep 17 00:00:00 2001 +From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> +Date: Mon, 17 Jul 2023 16:17:16 +1200 +Subject: [PATCH 1/2] libcli/security: save access check attempts for fuzz + examples + +If this patch is applied to a Samba tree, and the +SAMBA_SAVE_ACCESS_CHECK_DIR environment variable points to a +directory, the tokens and descriptors of all access checks will be +stored in that directory in the form used by +fuzz_security_token_vs_descriptor. This can be used to build up a +corpus of seeds for the fuzzer. + +The steps to create the corpus go something like this: + +$ export SAMBA_SAVE_ACCESS_CHECK_DIR=/tmp/samba-seeds +$ mkdir $SAMBA_SAVE_ACCESS_CHECK_DIR +$ mkdir /tmp/final-seeds-go-here +$ make test + +at this point you'd want to do something like this: + +$ for f in $SAMBA_SAVE_ACCESS_CHECK_DIR/*; do \ + cp -n $f /tmp/final-seeds-go-here/$(md5sum $f | cut -d' ' -f 1) \ + done + +but it takes way too long, so use the script in the second patch in +this series, like so: + +$ script/find-unique-access-seeds \ + $SAMBA_SAVE_ACCESS_CHECK_DIR \ + /tmp/final-seeds-go-here/ + +Think before applying this patch in production. It won't slow things +down much, but it will capture your SIDs and ACLs. + +Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> +--- + libcli/security/access_check.c | 79 ++++++++++++++++++++++++++++++++++ + 1 file changed, 79 insertions(+) + +diff --git a/libcli/security/access_check.c b/libcli/security/access_check.c +index 1364a15f4dd..d79a247455a 100644 +--- a/libcli/security/access_check.c ++++ b/libcli/security/access_check.c +@@ -26,6 +26,8 @@ + #include "libcli/security/security.h" + #include "librpc/gen_ndr/conditional_ace.h" + #include "libcli/security/conditional_ace.h" ++#include "ndr/libndr.h" ++#include "gen_ndr/ndr_security.h" + + /* Map generic access rights to object specific rights. This technique is + used to give meaning to assigning read, write, execute and all access to +@@ -105,6 +107,77 @@ void se_map_standard(uint32_t *access_mask, const struct standard_mapping *mappi + } + } + ++ ++static bool write_token_and_descriptor(const struct security_descriptor *sd, ++ const struct security_token *token, ++ uint32_t access_desired) ++{ ++ /* ++ * You should not be seeing this function in master or a release ++ * branch! It should only be here if you have patched Samba to ++ * generate fuzz seeds for fuzz_security_token_vs_descriptor. ++ * ++ * It hooks into access_check functions, saving copies of each access ++ * request in a structure for use as a fuzz seed, into the directory ++ * specified by the SAMBA_SAVE_ACCESS_CHECK_DIR environment variable. ++ * ++ * If the environment variable is not set, nothing will happen. ++ * ++ * A full `make test` saves about four million files, but only about ++ * forty thousand of them are unique. ++ */ ++ FILE *f = NULL; ++ char buf[200]; ++ int len; ++ DATA_BLOB blob = {0}; ++ uint pid; ++ struct security_token_descriptor_fuzzing_pair p = { ++ .token = *token, ++ .sd = *sd, ++ .access_desired = access_desired ++ }; ++ static size_t n = 0; ++ enum ndr_err_code ndr_err; ++ static const char *dir = NULL; ++ TALLOC_CTX *tmp_ctx = NULL; ++ ++ if (dir == NULL) { ++ if (n == SIZE_MAX) { ++ return true; ++ } ++ dir = getenv("SAMBA_SAVE_ACCESS_CHECK_DIR"); ++ if (dir == NULL) { ++ n = SIZE_MAX; ++ return false; ++ } ++ } ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return false; ++ } ++ ++ n++; ++ ndr_err = ndr_push_struct_blob( ++ &blob, tmp_ctx, &p, ++ (ndr_push_flags_fn_t)ndr_push_security_token_descriptor_fuzzing_pair); ++ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { ++ TALLOC_FREE(tmp_ctx); ++ return false; ++ } ++ pid = getpid(); ++ len = snprintf(buf, sizeof(buf), "%s/%08u-%05zu.seed", dir, pid, n); ++ if (len >= sizeof(buf)) { ++ TALLOC_FREE(tmp_ctx); ++ return false; ++ } ++ f = fopen(buf, "w"); ++ fwrite(blob.data, 1, blob.length, f); ++ fclose(f); ++ TALLOC_FREE(tmp_ctx); ++ return true; ++} ++ ++ + /* + perform a SEC_FLAG_MAXIMUM_ALLOWED access check + */ +@@ -117,6 +190,8 @@ static uint32_t access_check_max_allowed(const struct security_descriptor *sd, + bool have_owner_rights_ace = false; + unsigned i; + ++ write_token_and_descriptor(sd, token, SEC_FLAG_MAXIMUM_ALLOWED); ++ + if (sd->dacl == NULL) { + if (security_token_has_sid(token, sd->owner_sid)) { + switch (implicit_owner_rights) { +@@ -222,6 +297,8 @@ static NTSTATUS se_access_check_implicit_owner(const struct security_descriptor + bool am_owner = false; + bool have_owner_rights_ace = false; + ++ write_token_and_descriptor(sd, token, access_desired); ++ + *access_granted = access_desired; + bits_remaining = access_desired; + +@@ -613,6 +690,8 @@ NTSTATUS sec_access_check_ds_implicit_owner(const struct security_descriptor *sd + uint32_t bits_remaining; + struct dom_sid self_sid; + ++ write_token_and_descriptor(sd, token, access_desired); ++ + dom_sid_parse(SID_NT_SELF, &self_sid); + + *access_granted = access_desired; +-- +2.34.1 + + +From 12bf242cece202658fe61f1c7408709d092632ea Mon Sep 17 00:00:00 2001 +From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> +Date: Tue, 18 Jul 2023 16:07:11 +1200 +Subject: [PATCH 2/2] scripts: a script for deduplicating fuzz-seeds + +The previous patch adds a way to collect two million fuzz seeds, only +a few thousand of which are unique. This script finds the unique ones. + +Some fuzzers like seeds to have names based on md5 hashes, so we do that. + +The naive technique takes ages. + +Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> +--- + script/find-unique-access-seeds | 66 +++++++++++++++++++++++++++++++++ + 1 file changed, 66 insertions(+) + create mode 100755 script/find-unique-access-seeds + +diff --git a/script/find-unique-access-seeds b/script/find-unique-access-seeds +new file mode 100755 +index 00000000000..174e811ecd0 +--- /dev/null ++++ b/script/find-unique-access-seeds +@@ -0,0 +1,66 @@ ++#!/usr/bin/env python3 ++# ++# Copyright (C) Catalyst IT Ltd. 2023 ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see <http://www.gnu.org/licenses/>. ++# ++"""USAGE: find-unique-access-seeds SRCDIR DESTDIR ++ ++Copy the files in SRCDIR to DESTDIR with the name set to the ++md5sum of the contents. DESTDIR will thus have no duplicates. ++ ++This is the same as going: ++ ++ for f in $SRC/*; do ++ cp $f $DEST/$(md5sum $f | cut -d' ' -f 1) ++ done ++ ++but much more efficient. ++""" ++ ++ ++import sys ++import os ++from pathlib import Path ++from hashlib import md5 ++ ++ ++def usage(ret): ++ print(__doc__) ++ exit(ret) ++ ++ ++def main(): ++ if {'-h', '--help'}.intersection(sys.argv): ++ usage(0) ++ if len(sys.argv) != 3: ++ usage(1) ++ ++ src, dest = sys.argv[1:] ++ sp = Path(src) ++ dp = Path(dest) ++ ++ strings = set() ++ ++ for filename in sp.iterdir(): ++ with open(filename, 'rb') as f: ++ strings.add(f.read()) ++ ++ for s in strings: ++ name = md5(s).hexdigest() ++ with open(dp / name, "wb") as f: ++ f.write(s) ++ ++ ++main() +-- +2.34.1 + diff --git a/lib/fuzzing/wscript_build b/lib/fuzzing/wscript_build new file mode 100644 index 0000000..5a98013 --- /dev/null +++ b/lib/fuzzing/wscript_build @@ -0,0 +1,236 @@ +#!/usr/bin/env python + +from waflib import Build + +bld.SAMBA_SUBSYSTEM('fuzzing', + source='fuzzing.c', + deps='talloc') + +bld.SAMBA_SUBSYSTEM('afl-fuzz-main', + source='afl-fuzz-main.c', + deps='samba-util', + enabled=bld.env.enable_afl_fuzzer + ) + +bld.SAMBA_BINARY('fuzz_tiniparser', + source='fuzz_tiniparser.c', + deps='fuzzing tiniparser talloc afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_parse_lpq_entry', + source='fuzz_parse_lpq_entry.c', + deps='fuzzing afl-fuzz-main smbd_base PRINTING', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_oLschema2ldif', + source='fuzz_oLschema2ldif.c', + deps='fuzzing oLschema2ldif-lib afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_reg_parse', + source='fuzz_reg_parse.c', + deps='fuzzing samba3-util smbconf REGFIO afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_sddl_parse', + source='fuzz_sddl_parse.c', + deps='fuzzing samba-security afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_sess_crypt_blob', + source='fuzz_sess_crypt_blob.c', + deps='fuzzing samba-security LIBCLI_AUTH NDR_DSSETUP afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_conditional_ace_blob', + source='fuzz_conditional_ace_blob.c', + deps='fuzzing samba-security afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_nmblib_parse_packet', + source='fuzz_nmblib_parse_packet.c', + deps='fuzzing libsmb afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_sddl_access_check', + source='fuzz_sddl_access_check.c', + deps='fuzzing samba-security afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_sddl_access_check_ds', + cflags='-DFUZZ_SEC_ACCESS_CHECK_DS=1', + source='fuzz_sddl_access_check.c', + deps='fuzzing samba-security afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_regfio', + source='fuzz_regfio.c', + deps='fuzzing samba3-util smbconf REGFIO afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_lzxpress', + source='fuzz_lzxpress.c', + deps='fuzzing LZXPRESS afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_lzxpress_compress', + source='fuzz_lzxpress_compress.c', + deps='fuzzing LZXPRESS afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_lzxpress_round_trip', + source='fuzz_lzxpress_round_trip.c', + deps='fuzzing LZXPRESS afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_lzxpress_huffman_decompress', + source='fuzz_lzxpress_huffman_decompress.c', + deps='fuzzing LZXPRESS afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_lzxpress_huffman_compress', + source='fuzz_lzxpress_huffman_compress.c', + deps='fuzzing LZXPRESS afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_lzxpress_huffman_round_trip', + source='fuzz_lzxpress_huffman_round_trip.c', + deps='fuzzing LZXPRESS afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_ldap_decode', + source='fuzz_ldap_decode.c', + deps='fuzzing cli-ldap afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_ldb_parse_control', + source='fuzz_ldb_parse_control.c', + deps='fuzzing ldb afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_ldb_dn_explode', + source='fuzz_ldb_dn_explode.c', + deps='fuzzing ldb afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_ldb_ldif_read', + source='fuzz_ldb_ldif_read.c', + deps='fuzzing ldb afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_ldb_parse_binary_decode', + source='fuzz_ldb_parse_binary_decode.c', + deps='fuzzing ldb afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_ldb_parse_tree', + source='fuzz_ldb_parse_tree.c', + deps='fuzzing ldb afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_ldb_comparison_fold', + source='fuzz_ldb_comparison_fold.c', + deps='fuzzing ldb afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_dcerpc_parse_binding', + source='fuzz_dcerpc_parse_binding.c', + deps='fuzzing dcerpc afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_cli_credentials_parse_string', + source='fuzz_cli_credentials_parse_string.c', + deps='fuzzing samba-credentials afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_stable_sort', + source='fuzz_stable_sort.c', + deps='fuzzing stable_sort afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_stable_sort_r', + source='fuzz_stable_sort_r.c', + deps='fuzzing stable_sort afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_security_token_vs_descriptor', + source='fuzz_security_token_vs_descriptor.c', + deps='fuzzing samba-security afl-fuzz-main', + fuzzer=True) + +bld.SAMBA_BINARY('fuzz_security_token_vs_descriptor_ds', + cflags='-DFUZZ_SEC_ACCESS_CHECK_DS=1', + source='fuzz_security_token_vs_descriptor.c', + deps='fuzzing samba-security afl-fuzz-main', + fuzzer=True) + + +# The fuzz_type and fuzz_function parameters make the built +# fuzzer take the same input as ndrdump and so the same that +# could be sent to the client or server as the stub data. + +def SAMBA_NDR_FUZZ(bld, interface, auto_deps=False, + fuzz_type=None, fuzz_function=None): + name = "fuzz_ndr_%s" % (interface.lower()) + fuzz_dir = os.path.join(bld.env.srcdir, 'lib/fuzzing') + fuzz_reldir = os.path.relpath(fuzz_dir, bld.path.abspath()) + fuzz_src = os.path.join(fuzz_reldir, 'fuzz_ndr_X.c') + + cflags = "-D FUZZ_PIPE_TABLE=ndr_table_%s" % interface + if fuzz_type: + name += "_%s" % (fuzz_type) + cflags += " -D FUZZ_TYPE=%s " % (fuzz_type) + if fuzz_type and fuzz_function: + name += "_%d" % (fuzz_function) + cflags += " -D FUZZ_FUNCTION=%d" % (fuzz_function) + + fuzz_named_src = os.path.join(fuzz_reldir, + '%s.c' % (name)) + # Work around an issue that WAF is invoked from up to 3 different + # directories so doesn't create a unique name for the multiple .o + # files like it would if called from just one place. + bld.SAMBA_GENERATOR(fuzz_named_src, + source=fuzz_src, + target=fuzz_named_src, + rule='cp ${SRC} ${TGT}') + + if auto_deps: + deps = "afl-fuzz-main talloc ndr NDR_%s" % interface.upper() + else: + deps = "afl-fuzz-main ndr-table NDR_DCERPC" + + bld.SAMBA_BINARY(name, source=fuzz_named_src, + cflags = cflags, + deps = deps, + fuzzer=True) + +Build.BuildContext.SAMBA_NDR_FUZZ = SAMBA_NDR_FUZZ + +# fuzz_ndr_X is generated from the list of IDL fed to PIDL +# however there are exceptions to the normal pattern +bld.SAMBA_NDR_FUZZ('iremotewinspool') # winspool.idl +bld.SAMBA_NDR_FUZZ('FileServerVssAgent') # fsvrp.idl +bld.SAMBA_NDR_FUZZ('lsarpc') # lsa.idl +bld.SAMBA_NDR_FUZZ('netdfs') # dfs.idl +bld.SAMBA_NDR_FUZZ('nfs4acl_interface') # nfs4acl.idl +bld.SAMBA_NDR_FUZZ('rpcecho') # echo.idl + +# quota.idl +bld.SAMBA_NDR_FUZZ('file_quota') +bld.SAMBA_NDR_FUZZ('smb2_query_quota') +bld.SAMBA_NDR_FUZZ('smb1_nt_transact_query_quota') + +# ioctl.idl +bld.SAMBA_NDR_FUZZ('copychunk') +bld.SAMBA_NDR_FUZZ('compression') +bld.SAMBA_NDR_FUZZ('netinterface') +bld.SAMBA_NDR_FUZZ('sparse') +bld.SAMBA_NDR_FUZZ('resiliency') +bld.SAMBA_NDR_FUZZ('trim') + +# Specific struct or function on the interface + +bld.SAMBA_NDR_FUZZ('spoolss', + auto_deps=True, + fuzz_type="TYPE_IN", + fuzz_function=65) |