summaryrefslogtreecommitdiffstats
path: root/lib/fuzzing
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /lib/fuzzing
parentInitial commit. (diff)
downloadsamba-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 'lib/fuzzing')
-rw-r--r--lib/fuzzing/README.md87
-rw-r--r--lib/fuzzing/afl-fuzz-main.c66
-rwxr-xr-xlib/fuzzing/decode_ndr_X_crash137
-rw-r--r--lib/fuzzing/fuzz_cli_credentials_parse_string.c63
-rw-r--r--lib/fuzzing/fuzz_conditional_ace_blob.c156
-rw-r--r--lib/fuzzing/fuzz_dcerpc_parse_binding.c76
-rw-r--r--lib/fuzzing/fuzz_ldap_decode.c66
-rw-r--r--lib/fuzzing/fuzz_ldb_comparison_fold.c58
-rw-r--r--lib/fuzzing/fuzz_ldb_dn_explode.c53
-rw-r--r--lib/fuzzing/fuzz_ldb_ldif_read.c56
-rw-r--r--lib/fuzzing/fuzz_ldb_parse_binary_decode.c55
-rw-r--r--lib/fuzzing/fuzz_ldb_parse_control.c55
-rw-r--r--lib/fuzzing/fuzz_ldb_parse_tree.c53
-rw-r--r--lib/fuzzing/fuzz_lzxpress.c35
-rw-r--r--lib/fuzzing/fuzz_lzxpress_compress.c35
-rw-r--r--lib/fuzzing/fuzz_lzxpress_huffman_compress.c58
-rw-r--r--lib/fuzzing/fuzz_lzxpress_huffman_decompress.c48
-rw-r--r--lib/fuzzing/fuzz_lzxpress_huffman_round_trip.c68
-rw-r--r--lib/fuzzing/fuzz_lzxpress_round_trip.c56
-rw-r--r--lib/fuzzing/fuzz_ndr_X.c337
-rw-r--r--lib/fuzzing/fuzz_nmblib_parse_packet.c62
-rw-r--r--lib/fuzzing/fuzz_oLschema2ldif.c71
-rw-r--r--lib/fuzzing/fuzz_parse_lpq_entry.c65
-rw-r--r--lib/fuzzing/fuzz_reg_parse.c46
-rw-r--r--lib/fuzzing/fuzz_regfio.c68
-rw-r--r--lib/fuzzing/fuzz_sddl_access_check.c206
-rw-r--r--lib/fuzzing/fuzz_sddl_conditional_ace.c121
-rw-r--r--lib/fuzzing/fuzz_sddl_parse.c110
-rw-r--r--lib/fuzzing/fuzz_security_token_vs_descriptor.c78
-rw-r--r--lib/fuzzing/fuzz_sess_crypt_blob.c55
-rw-r--r--lib/fuzzing/fuzz_stable_sort.c88
-rw-r--r--lib/fuzzing/fuzz_stable_sort_r.c69
-rw-r--r--lib/fuzzing/fuzz_tiniparser.c51
-rw-r--r--lib/fuzzing/fuzzing.c21
-rw-r--r--lib/fuzzing/fuzzing.h30
-rwxr-xr-xlib/fuzzing/oss-fuzz/build_image.sh7
-rwxr-xr-xlib/fuzzing/oss-fuzz/build_samba.sh25
-rwxr-xr-xlib/fuzzing/oss-fuzz/check_build.sh48
-rwxr-xr-xlib/fuzzing/oss-fuzz/do_build.sh294
-rw-r--r--lib/fuzzing/patches/collect-access-check-seeds.txt256
-rw-r--r--lib/fuzzing/wscript_build236
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)