/src/samba/lib/fuzzing/fuzz_conditional_ace_blob.c
Line | Count | Source |
1 | | /* |
2 | | Fuzz conditional ace decoding and encoding |
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 "lib/util/attr.h" |
22 | | #include "librpc/gen_ndr/ndr_security.h" |
23 | | #include "libcli/security/conditional_ace.h" |
24 | | #include "librpc/gen_ndr/conditional_ace.h" |
25 | | #include "fuzzing/fuzzing.h" |
26 | | |
27 | | |
28 | 5.26k | #define MAX_LENGTH (1024 * 1024 - 1) |
29 | | |
30 | | |
31 | | int LLVMFuzzerInitialize(int *argc, char ***argv) |
32 | 376 | { |
33 | 376 | return 0; |
34 | 376 | } |
35 | | |
36 | | |
37 | | int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) |
38 | 5.26k | { |
39 | 5.26k | TALLOC_CTX *mem_ctx = NULL; |
40 | 5.26k | bool ok; |
41 | 5.26k | struct ace_condition_script *s1 = NULL; |
42 | 5.26k | struct ace_condition_script *s2 = NULL; |
43 | 5.26k | const char *message = NULL; |
44 | 5.26k | size_t message_offset; |
45 | 5.26k | const char *sddl = NULL; |
46 | 5.26k | DATA_BLOB e1, e2; |
47 | 5.26k | size_t length; |
48 | | |
49 | 5.26k | if (len > MAX_LENGTH) { |
50 | 3 | return 0; |
51 | 3 | } |
52 | | |
53 | | /* |
54 | | * In this one we are treating the input data as an ACE blob, |
55 | | * and decoding it into the structure and thence SDDL. |
56 | | * |
57 | | * This doesn't run the conditional ACE, for which we would |
58 | | * need a security token. |
59 | | */ |
60 | | |
61 | 5.26k | e1.data = discard_const(input); |
62 | 5.26k | e1.length = len; |
63 | | |
64 | 5.26k | mem_ctx = talloc_new(NULL); |
65 | | |
66 | 5.26k | s1 = parse_conditional_ace(mem_ctx, e1); |
67 | 5.26k | if (s1 == NULL) { |
68 | | /* no worries, it was nonsense */ |
69 | 1.17k | TALLOC_FREE(mem_ctx); |
70 | 1.17k | return 0; |
71 | 1.17k | } |
72 | | |
73 | | /* back to blob form */ |
74 | 4.08k | ok = conditional_ace_encode_binary(mem_ctx, s1, &e2); |
75 | 4.08k | if (! ok) { |
76 | 2 | if (e1.length == CONDITIONAL_ACE_MAX_LENGTH) { |
77 | | /* |
78 | | * This is an edge case where the encoder and |
79 | | * decoder treat the boundary slightly |
80 | | * differently, and the encoder refuses to |
81 | | * encode to the maximum length. This is not |
82 | | * an issue in the real world. |
83 | | */ |
84 | 2 | TALLOC_FREE(mem_ctx); |
85 | 2 | return 0; |
86 | 2 | } |
87 | 0 | abort(); |
88 | 2 | } |
89 | | |
90 | 4.08k | if (data_blob_cmp(&e1, &e2) != 0) { |
91 | 0 | abort(); |
92 | 0 | } |
93 | | |
94 | 4.08k | sddl = sddl_from_conditional_ace(mem_ctx, s1); |
95 | 4.08k | if (sddl == NULL) { |
96 | | /* |
97 | | * we can't call this a failure, because the blob |
98 | | * could easily have nonsensical programs that the |
99 | | * SDDL decompiler is unwilling to countenance. For |
100 | | * example, it could have an operator that requires |
101 | | * arguments as the first token, when of course the |
102 | | * arguments need to come first. |
103 | | */ |
104 | 545 | TALLOC_FREE(mem_ctx); |
105 | 545 | return 0; |
106 | 545 | } |
107 | | |
108 | 3.53k | s2 = ace_conditions_compile_sddl(mem_ctx, |
109 | 3.53k | ACE_CONDITION_FLAG_ALLOW_DEVICE, |
110 | 3.53k | sddl, |
111 | 3.53k | &message, |
112 | 3.53k | &message_offset, |
113 | 3.53k | &length); |
114 | 3.53k | if (s2 == NULL) { |
115 | | /* |
116 | | * We also don't complain when the SDDL decompiler |
117 | | * produces an uncompilable program, because the |
118 | | * decompiler is meant to be a display tool, not a |
119 | | * verifier in itself. |
120 | | */ |
121 | 2.18k | TALLOC_FREE(mem_ctx); |
122 | 2.18k | return 0; |
123 | 2.18k | } |
124 | | |
125 | 1.35k | ok = conditional_ace_encode_binary(mem_ctx, s2, &e2); |
126 | 1.35k | if (! ok) { |
127 | 33 | if (len < CONDITIONAL_ACE_MAX_LENGTH / 4) { |
128 | | /* |
129 | | * long invalid ACEs can easily result in SDDL that |
130 | | * would compile to an over-long ACE, which fail |
131 | | * accordingly. |
132 | | * |
133 | | * But if the original ACE less than a few thousand |
134 | | * bytes, and it has been serialised into SDDL, that |
135 | | * SDDL should be parsable. |
136 | | */ |
137 | 0 | abort(); |
138 | 0 | } |
139 | 33 | } |
140 | | |
141 | | /* |
142 | | * It would be nice here to go: |
143 | | * |
144 | | * if (data_blob_cmp(&e1, &e2) != 0) { |
145 | | * abort(); |
146 | | * } |
147 | | * |
148 | | * but that isn't really fair. The decompilation into SDDL |
149 | | * does not make thorough sanity checks because that is not |
150 | | * its job -- it is just trying to depict what is there -- and |
151 | | * there are many ambiguous decompilations. |
152 | | * |
153 | | * For example, a blob with a single literal integer token, |
154 | | * say 42, can only really be shown in the SDDL syntax as |
155 | | * "(42)", but when the compiler reads that it knows that a |
156 | | * literal number is invalid except in a RHS argument, so it |
157 | | * assumes "42" is a local attribute name. |
158 | | * |
159 | | * Even if the decompiler was a perfect verifier, a round trip |
160 | | * through SDDL could not be guaranteed because, for example, |
161 | | * an 8 bit integer can only be displayed in SDDL in the form |
162 | | * that compiles to a 64 bit integer. |
163 | | */ |
164 | | |
165 | 1.35k | TALLOC_FREE(mem_ctx); |
166 | 1.35k | return 0; |
167 | 1.35k | } |