lib/fuzzing: add fuzz_sddl_access_check
authorDouglas Bagnall <douglas.bagnall@catalyst.net.nz>
Wed, 12 Jul 2023 01:03:53 +0000 (13:03 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 19 Jul 2023 03:31:30 +0000 (03:31 +0000)
This fuzzer parses SDDL into a security descriptor and runs an access
check on it using a known security token. This is purely for crash
detection -- we don't know enough to assert whether the check should
succeed or not.

The seed strings used are compatible with those of fuzz_sddl_parse --
anything found by fuzz_sddl_parse is worth trying as a seed here, and
vice versa.

Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
lib/fuzzing/fuzz_sddl_access_check.c [new file with mode: 0644]
lib/fuzzing/wscript_build

diff --git a/lib/fuzzing/fuzz_sddl_access_check.c b/lib/fuzzing/fuzz_sddl_access_check.c
new file mode 100644 (file)
index 0000000..d424727
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+  Fuzz access chcek 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 "lib/util/attr.h"
+#include "librpc/gen_ndr/ndr_security.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 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.
+ *
+ * When we introduce conditional ACEs and claims (soon!), we'll also add some
+ * claims and device SIDs to the token.
+ */
+
+int LLVMFuzzerInitialize(int *argc, char ***argv)
+{
+       size_t i;
+       bool ok;
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+       struct dom_sid *sid = NULL;
+
+       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 = dom_sid_parse_talloc(mem_ctx, user_sids[i]);
+               if (sid == NULL) {
+                       abort();
+               }
+               add_sid_to_array(mem_ctx, sid,
+                                &token.sids,
+                                &token.num_sids);
+       }
+       return 0;
+}
+
+
+int LLVMFuzzerTestOneInput(uint8_t *input, size_t len)
+{
+       TALLOC_CTX *mem_ctx = NULL;
+       struct security_descriptor *sd = NULL;
+       NTSTATUS status;
+       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 stategy 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;
+       }
+       status = se_access_check(sd, &token, access_desired, &access_granted);
+end:
+       talloc_free(mem_ctx);
+       return 0;
+}
index 187c23c7cb8fcf678cba6530855d007a6835a2df..2326cb60f95d39645662e4e09fe7c0aa0aac3fab 100644 (file)
@@ -42,6 +42,11 @@ bld.SAMBA_BINARY('fuzz_nmblib_parse_packet',
                  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_regfio',
                  source='fuzz_regfio.c',
                  deps='fuzzing samba3-util smbconf REGFIO afl-fuzz-main',