python: models: rename argument ldb to samdb
[samba.git] / lib / fuzzing / fuzz_sddl_access_check.c
bloba7bf7b306ab6f3153f3fc29bc9f5d12c9e59d9c9
1 /*
2 Fuzz access check using SDDL strings and a known token
3 Copyright (C) Catalyst IT 2023
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "replace.h"
20 #include "libcli/security/security.h"
21 #include "libcli/security/conditional_ace.h"
22 #include "libcli/security/claims-conversions.h"
23 #include "lib/util/attr.h"
24 #include "librpc/gen_ndr/ndr_security.h"
25 #include "librpc/gen_ndr/ndr_conditional_ace.h"
26 #include "lib/util/bytearray.h"
27 #include "fuzzing/fuzzing.h"
30 static struct security_token token = {0};
32 static struct dom_sid dom_sid = {0};
35 * For this one we initialise a security token to have a few claims
36 * and SIDs. The fuzz strings contain SDDL that will be tested against
37 * this token in se_access_check() or sec_access_check_ds() --
38 * supposing they compile.
41 int LLVMFuzzerInitialize(int *argc, char ***argv)
43 size_t i;
44 TALLOC_CTX *mem_ctx = talloc_new(NULL);
45 struct dom_sid *sid = NULL;
47 struct claim_def {
48 const char *type;
49 const char *name;
50 const char *claim_sddl;
51 } claims[] = {
53 "user",
54 "shoe size",
55 "44"
58 "user",
59 "©",
60 "{\"unknown\", \"\", \" ←ā\"}"
63 "device",
64 "©",
65 "{\"unknown\", \" \", \" ←ā\"}"
68 "device",
69 "least favourite groups",
70 "{SID(S-1-1-0),SID(S-1-5-3),SID(S-1-57777-333-33-33-2)}"
73 "local",
74 "birds",
75 "{\"tern\"}"
79 const char * device_sids[] = {
80 "S-1-1-0",
81 "S-1-333-66",
82 "S-1-2-3-4-5-6-7-8-9",
84 const char * user_sids[] = {
85 "S-1-333-66",
86 "S-1-16-8448",
87 "S-1-9-8-7",
90 for (i = 0; i < ARRAY_SIZE(user_sids); i++) {
91 sid = sddl_decode_sid(mem_ctx, &user_sids[i], NULL);
92 if (sid == NULL) {
93 abort();
95 add_sid_to_array(mem_ctx, sid,
96 &token.sids,
97 &token.num_sids);
100 for (i = 0; i < ARRAY_SIZE(device_sids); i++) {
101 sid = sddl_decode_sid(mem_ctx, &device_sids[i], NULL);
102 if (sid == NULL) {
103 abort();
105 add_sid_to_array(mem_ctx, sid,
106 &token.device_sids,
107 &token.num_device_sids);
110 for (i = 0; i < ARRAY_SIZE(claims); i++) {
111 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim = NULL;
112 struct claim_def c = claims[i];
114 claim = parse_sddl_literal_as_claim(mem_ctx,
115 c.name,
116 c.claim_sddl);
117 if (claim == NULL) {
118 abort();
120 add_claim_to_token(mem_ctx, &token, claim, c.type);
123 /* we also need a global domain SID */
124 string_to_sid(&dom_sid, device_sids[2]);
125 return 0;
129 int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len)
131 TALLOC_CTX *mem_ctx = NULL;
132 struct security_descriptor *sd = NULL;
133 uint32_t access_desired;
134 uint32_t access_granted;
135 const char *sddl;
136 ssize_t i;
137 if (len < 5) {
138 return 0;
140 access_desired = PULL_LE_U32(input + len - 4, 0);
143 * check there is a '\0'.
145 * Note this allows double-dealing for the last 4 bytes: they are used
146 * as the access_desired mask (see just above) but also *could* be
147 * part of the sddl string. But this doesn't matter, for three
148 * reasons:
150 * 1. the desired access mask doesn't usually matter much.
152 * 2. the final '\0' is rarely the operative one. Usually the
153 * effective string ends a long time before the end of the input, and
154 * the tail is just junk that comes along for the ride.
156 * 3. Even if there is a case where the end of the SDDL is part of the
157 * mask, the evolution strategy is very likely to try a different mask,
158 * because it likes to add junk on the end.
160 * But still, you ask, WHY? So that the seeds from here can be shared
161 * back and forth with the fuzz_sddl_parse seeds, which have the same
162 * form of a null-terminated-string-with-trailing-junk. If we started
163 * the loop at `len - 5` instead of `len - 1`, there might be
164 * interesting seeds that are valid there that would fail here. That's
165 * all.
167 for (i = len - 1; i >= 0; i--) {
168 if (input[i] == 0) {
169 break;
172 if (i < 0) {
173 return 0;
176 sddl = (const char *)input;
177 mem_ctx = talloc_new(NULL);
179 sd = sddl_decode(mem_ctx, sddl, &dom_sid);
180 if (sd == NULL) {
181 goto end;
184 #ifdef FUZZ_SEC_ACCESS_CHECK_DS
186 * The sec_access_check_ds() function has two arguments not found in
187 * se_access_check, and also not found in our fuzzing examples.
189 * One is a struct object_tree, which is used for object ACE types.
190 * The other is a SID, which is used as a default if an ACE lacks a
191 * SID.
193 sec_access_check_ds(sd,
194 &token,
195 access_desired,
196 &access_granted,
197 NULL,
198 NULL);
199 #else
200 se_access_check(sd, &token, access_desired, &access_granted);
201 #endif
203 end:
204 talloc_free(mem_ctx);
205 return 0;