/src/samba/lib/fuzzing/fuzz_sddl_access_check.c
Line | Count | Source |
1 | | /* |
2 | | Fuzz access check using SDDL strings and a known token |
3 | | Copyright (C) Catalyst IT 2023 |
4 | | |
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. |
9 | | |
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. |
14 | | |
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/>. |
17 | | */ |
18 | | |
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" |
28 | | |
29 | | |
30 | | static struct security_token token = {0}; |
31 | | |
32 | | static struct dom_sid dom_sid = {0}; |
33 | | |
34 | | /* |
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. |
39 | | */ |
40 | | |
41 | | int LLVMFuzzerInitialize(int *argc, char ***argv) |
42 | 4 | { |
43 | 4 | size_t i; |
44 | 4 | TALLOC_CTX *mem_ctx = talloc_new(NULL); |
45 | 4 | struct dom_sid *sid = NULL; |
46 | | |
47 | 4 | struct claim_def { |
48 | 4 | const char *type; |
49 | 4 | const char *name; |
50 | 4 | const char *claim_sddl; |
51 | 4 | } claims[] = { |
52 | 4 | { |
53 | 4 | "user", |
54 | 4 | "shoe size", |
55 | 4 | "44" |
56 | 4 | }, |
57 | 4 | { |
58 | 4 | "user", |
59 | 4 | "©", |
60 | 4 | "{\"unknown\", \"\", \" ←ā\"}" |
61 | 4 | }, |
62 | 4 | { |
63 | 4 | "device", |
64 | 4 | "©", |
65 | 4 | "{\"unknown\", \" \", \" ←ā\"}" |
66 | 4 | }, |
67 | 4 | { |
68 | 4 | "device", |
69 | 4 | "least favourite groups", |
70 | 4 | "{SID(S-1-1-0),SID(S-1-5-3),SID(S-1-57777-333-33-33-2)}" |
71 | 4 | }, |
72 | 4 | { |
73 | 4 | "local", |
74 | 4 | "birds", |
75 | 4 | "{\"tern\"}" |
76 | 4 | }, |
77 | 4 | }; |
78 | | |
79 | 4 | const char * device_sids[] = { |
80 | 4 | "S-1-1-0", |
81 | 4 | "S-1-333-66", |
82 | 4 | "S-1-2-3-4-5-6-7-8-9", |
83 | 4 | }; |
84 | 4 | const char * user_sids[] = { |
85 | 4 | "S-1-333-66", |
86 | 4 | "S-1-16-8448", |
87 | 4 | "S-1-9-8-7", |
88 | 4 | }; |
89 | | |
90 | 16 | for (i = 0; i < ARRAY_SIZE(user_sids); i++) { |
91 | 12 | sid = sddl_decode_sid(mem_ctx, &user_sids[i], NULL); |
92 | 12 | if (sid == NULL) { |
93 | 0 | abort(); |
94 | 0 | } |
95 | 12 | add_sid_to_array(mem_ctx, sid, |
96 | 12 | &token.sids, |
97 | 12 | &token.num_sids); |
98 | 12 | } |
99 | | |
100 | 16 | for (i = 0; i < ARRAY_SIZE(device_sids); i++) { |
101 | 12 | sid = sddl_decode_sid(mem_ctx, &device_sids[i], NULL); |
102 | 12 | if (sid == NULL) { |
103 | 0 | abort(); |
104 | 0 | } |
105 | 12 | add_sid_to_array(mem_ctx, sid, |
106 | 12 | &token.device_sids, |
107 | 12 | &token.num_device_sids); |
108 | 12 | } |
109 | | |
110 | 24 | for (i = 0; i < ARRAY_SIZE(claims); i++) { |
111 | 20 | struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim = NULL; |
112 | 20 | struct claim_def c = claims[i]; |
113 | | |
114 | 20 | claim = parse_sddl_literal_as_claim(mem_ctx, |
115 | 20 | c.name, |
116 | 20 | c.claim_sddl); |
117 | 20 | if (claim == NULL) { |
118 | 0 | abort(); |
119 | 0 | } |
120 | 20 | add_claim_to_token(mem_ctx, &token, claim, c.type); |
121 | 20 | } |
122 | | |
123 | | /* we also need a global domain SID */ |
124 | 4 | string_to_sid(&dom_sid, device_sids[2]); |
125 | 4 | return 0; |
126 | 4 | } |
127 | | |
128 | | |
129 | | int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) |
130 | 9.88k | { |
131 | 9.88k | TALLOC_CTX *mem_ctx = NULL; |
132 | 9.88k | struct security_descriptor *sd = NULL; |
133 | 9.88k | uint32_t access_desired; |
134 | 9.88k | uint32_t access_granted; |
135 | 9.88k | const char *sddl; |
136 | 9.88k | ssize_t i; |
137 | 9.88k | if (len < 5) { |
138 | 7 | return 0; |
139 | 7 | } |
140 | 9.87k | access_desired = PULL_LE_U32(input + len - 4, 0); |
141 | | |
142 | | /* |
143 | | * check there is a '\0'. |
144 | | * |
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: |
149 | | * |
150 | | * 1. the desired access mask doesn't usually matter much. |
151 | | * |
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. |
155 | | * |
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. |
159 | | * |
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. |
166 | | */ |
167 | 5.60M | for (i = len - 1; i >= 0; i--) { |
168 | 5.60M | if (input[i] == 0) { |
169 | 9.85k | break; |
170 | 9.85k | } |
171 | 5.60M | } |
172 | 9.87k | if (i < 0) { |
173 | 17 | return 0; |
174 | 17 | } |
175 | | |
176 | 9.85k | sddl = (const char *)input; |
177 | 9.85k | mem_ctx = talloc_new(NULL); |
178 | | |
179 | 9.85k | sd = sddl_decode(mem_ctx, sddl, &dom_sid); |
180 | 9.85k | if (sd == NULL) { |
181 | 9.07k | goto end; |
182 | 9.07k | } |
183 | | |
184 | | #ifdef FUZZ_SEC_ACCESS_CHECK_DS |
185 | | /* |
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. |
188 | | * |
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. |
192 | | */ |
193 | | sec_access_check_ds(sd, |
194 | | &token, |
195 | | access_desired, |
196 | | &access_granted, |
197 | | NULL, |
198 | | NULL); |
199 | | #else |
200 | 787 | se_access_check(sd, &token, access_desired, &access_granted); |
201 | 787 | #endif |
202 | | |
203 | 9.85k | end: |
204 | | talloc_free(mem_ctx); |
205 | 9.85k | return 0; |
206 | 787 | } |