summaryrefslogtreecommitdiffstats
path: root/lib/fuzzing/patches/collect-access-check-seeds.txt
blob: db85f40c22f751242c7d577baa2908889b4f753b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
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