/src/coturn/fuzzing/FuzzStun.c
Line | Count | Source |
1 | | /* |
2 | | * SPDX-License-Identifier: BSD-3-Clause |
3 | | * |
4 | | * https://opensource.org/license/bsd-3-clause |
5 | | * |
6 | | * Multi-harness libFuzzer entry point for server-side STUN parsing. |
7 | | * |
8 | | * Every iteration runs all sub-harnesses in sequence on the same input: |
9 | | * RFC 5769 integrity checks, multi-SHA/credential integrity, attribute |
10 | | * iteration, attribute serialization, and legacy (pre-RFC 5389) STUN |
11 | | * detection. Keeping everything behind a single binary allows the |
12 | | * upstream OSS-Fuzz build recipe to stay unchanged. |
13 | | */ |
14 | | |
15 | | #include <stdbool.h> |
16 | | #include <stdint.h> |
17 | | #include <string.h> |
18 | | |
19 | | #include "apputils.h" |
20 | | #include "ns_turn_msg.h" |
21 | | #include "ns_turn_msg_defs.h" |
22 | | #include "ns_turn_utils.h" |
23 | | #include "stun_buffer.h" |
24 | | |
25 | | /* ------------------------------------------------------------------ */ |
26 | | /* Integrity: SHA1 short-term + SHA256 long-term (original FuzzStun). */ |
27 | | /* ------------------------------------------------------------------ */ |
28 | 660 | static void harness_integrity_sha1(const uint8_t *Data, size_t Size) { |
29 | 660 | if (Size < 10 || Size > 5120) { |
30 | 24 | return; |
31 | 24 | } |
32 | | |
33 | 636 | stun_is_command_message_full_check_str((uint8_t *)Data, Size, 1, NULL); |
34 | | |
35 | 636 | uint8_t uname[STUN_MAX_USERNAME_SIZE + 1] = "fuzzuser"; |
36 | 636 | uint8_t realm[STUN_MAX_REALM_SIZE + 1] = "fuzz.realm"; |
37 | 636 | uint8_t upwd[STUN_MAX_PWD_SIZE + 1] = "VOkJxbRl1RmTxUk/WvJxBt"; |
38 | | |
39 | 636 | stun_check_message_integrity_str(TURN_CREDENTIALS_SHORT_TERM, (uint8_t *)Data, Size, uname, realm, upwd, |
40 | 636 | SHATYPE_SHA1); |
41 | 636 | stun_check_message_integrity_str(TURN_CREDENTIALS_LONG_TERM, (uint8_t *)Data, Size, uname, realm, upwd, |
42 | 636 | SHATYPE_SHA256); |
43 | 636 | } |
44 | | |
45 | | /* ------------------------------------------------------------------ */ |
46 | | /* Integrity across all SHA types + credential modes (FuzzStunIntegrity). */ |
47 | | /* ------------------------------------------------------------------ */ |
48 | 660 | static void harness_integrity_multi(const uint8_t *Data, size_t Size) { |
49 | 660 | if (Size < STUN_HEADER_LENGTH || Size > 5120) { |
50 | 33 | return; |
51 | 33 | } |
52 | | |
53 | 627 | uint8_t buf[5120]; |
54 | 627 | uint8_t uname[STUN_MAX_USERNAME_SIZE + 1] = "fuzzuser"; |
55 | 627 | uint8_t realm[STUN_MAX_REALM_SIZE + 1] = "fuzz.realm"; |
56 | 627 | uint8_t upwd[STUN_MAX_PWD_SIZE + 1] = "VOkJxbRl1RmTxUk/WvJxBt"; |
57 | | |
58 | 627 | static const SHATYPE sha_types[] = {SHATYPE_SHA1, SHATYPE_SHA256, SHATYPE_SHA384, SHATYPE_SHA512}; |
59 | 627 | const size_t num_sha = sizeof(sha_types) / sizeof(sha_types[0]); |
60 | | |
61 | 3.13k | for (size_t s = 0; s < num_sha; s++) { |
62 | 2.50k | memcpy(buf, Data, Size); |
63 | 2.50k | stun_is_command_message_full_check_str(buf, Size, 1, NULL); |
64 | 2.50k | stun_check_message_integrity_str(TURN_CREDENTIALS_SHORT_TERM, buf, Size, uname, realm, upwd, sha_types[s]); |
65 | | |
66 | 2.50k | memcpy(buf, Data, Size); |
67 | 2.50k | stun_check_message_integrity_str(TURN_CREDENTIALS_LONG_TERM, buf, Size, uname, realm, upwd, sha_types[s]); |
68 | 2.50k | } |
69 | 627 | } |
70 | | |
71 | | /* ------------------------------------------------------------------ */ |
72 | | /* Attribute TLV iteration + typed extraction (FuzzStunAttrIter). */ |
73 | | /* ------------------------------------------------------------------ */ |
74 | | static const uint16_t kAllAttrTypes[] = { |
75 | | STUN_ATTRIBUTE_MAPPED_ADDRESS, |
76 | | OLD_STUN_ATTRIBUTE_RESPONSE_ADDRESS, |
77 | | STUN_ATTRIBUTE_CHANGE_REQUEST, |
78 | | OLD_STUN_ATTRIBUTE_SOURCE_ADDRESS, |
79 | | OLD_STUN_ATTRIBUTE_CHANGED_ADDRESS, |
80 | | STUN_ATTRIBUTE_USERNAME, |
81 | | OLD_STUN_ATTRIBUTE_PASSWORD, |
82 | | STUN_ATTRIBUTE_MESSAGE_INTEGRITY, |
83 | | STUN_ATTRIBUTE_ERROR_CODE, |
84 | | STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES, |
85 | | OLD_STUN_ATTRIBUTE_REFLECTED_FROM, |
86 | | STUN_ATTRIBUTE_CHANNEL_NUMBER, |
87 | | STUN_ATTRIBUTE_LIFETIME, |
88 | | STUN_ATTRIBUTE_BANDWIDTH, |
89 | | STUN_ATTRIBUTE_XOR_PEER_ADDRESS, |
90 | | STUN_ATTRIBUTE_DATA, |
91 | | STUN_ATTRIBUTE_REALM, |
92 | | STUN_ATTRIBUTE_NONCE, |
93 | | STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, |
94 | | STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY, |
95 | | STUN_ATTRIBUTE_EVEN_PORT, |
96 | | STUN_ATTRIBUTE_REQUESTED_TRANSPORT, |
97 | | STUN_ATTRIBUTE_DONT_FRAGMENT, |
98 | | STUN_ATTRIBUTE_OAUTH_ACCESS_TOKEN, |
99 | | STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, |
100 | | OLD_STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, |
101 | | STUN_ATTRIBUTE_TIMER_VAL, |
102 | | STUN_ATTRIBUTE_RESERVATION_TOKEN, |
103 | | STUN_ATTRIBUTE_PRIORITY, |
104 | | STUN_ATTRIBUTE_PADDING, |
105 | | STUN_ATTRIBUTE_RESPONSE_PORT, |
106 | | STUN_ATTRIBUTE_CONNECTION_ID, |
107 | | STUN_ATTRIBUTE_ADDITIONAL_ADDRESS_FAMILY, |
108 | | STUN_ATTRIBUTE_ADDRESS_ERROR_CODE, |
109 | | STUN_ATTRIBUTE_SOFTWARE, |
110 | | STUN_ATTRIBUTE_ALTERNATE_SERVER, |
111 | | STUN_ATTRIBUTE_FINGERPRINT, |
112 | | STUN_ATTRIBUTE_ICE_CONTROLLED, |
113 | | STUN_ATTRIBUTE_RESPONSE_ORIGIN, |
114 | | STUN_ATTRIBUTE_OTHER_ADDRESS, |
115 | | STUN_ATTRIBUTE_THIRD_PARTY_AUTHORIZATION, |
116 | | STUN_ATTRIBUTE_ORIGIN, |
117 | | STUN_ATTRIBUTE_MOBILITY_TICKET, |
118 | | STUN_ATTRIBUTE_NEW_BANDWIDTH, |
119 | | }; |
120 | | |
121 | | static const uint16_t kAddrAttrs[] = { |
122 | | STUN_ATTRIBUTE_MAPPED_ADDRESS, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, OLD_STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, |
123 | | STUN_ATTRIBUTE_XOR_PEER_ADDRESS, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, STUN_ATTRIBUTE_ALTERNATE_SERVER, |
124 | | STUN_ATTRIBUTE_RESPONSE_ORIGIN, STUN_ATTRIBUTE_OTHER_ADDRESS, |
125 | | }; |
126 | | |
127 | 660 | static void harness_attr_iter(const uint8_t *Data, size_t Size) { |
128 | 660 | if (Size < STUN_HEADER_LENGTH || Size > 8192) { |
129 | 31 | return; |
130 | 31 | } |
131 | | |
132 | 629 | uint8_t buf[8192]; |
133 | 629 | memcpy(buf, Data, Size); |
134 | | |
135 | 629 | if (!stun_is_command_message_str(buf, Size)) { |
136 | 438 | return; |
137 | 438 | } |
138 | | |
139 | 191 | stun_attr_ref sar = stun_attr_get_first_str(buf, Size); |
140 | 1.48k | while (sar) { |
141 | 1.29k | (void)stun_attr_get_type(sar); |
142 | 1.29k | (void)stun_attr_get_len(sar); |
143 | 1.29k | (void)stun_attr_get_value(sar); |
144 | 1.29k | (void)stun_attr_is_addr(sar); |
145 | 1.29k | sar = stun_attr_get_next_str(buf, Size, sar); |
146 | 1.29k | } |
147 | | |
148 | 191 | ioa_addr addr; |
149 | 191 | const size_t num_addr_attrs = sizeof(kAddrAttrs) / sizeof(kAddrAttrs[0]); |
150 | 1.71k | for (size_t i = 0; i < num_addr_attrs; i++) { |
151 | 1.52k | sar = stun_attr_get_first_by_type_str(buf, Size, kAddrAttrs[i]); |
152 | 1.52k | if (sar) { |
153 | 7 | memset(&addr, 0, sizeof(addr)); |
154 | 7 | stun_attr_get_addr_str(buf, Size, sar, &addr, NULL); |
155 | 7 | } |
156 | 1.52k | memset(&addr, 0, sizeof(addr)); |
157 | 1.52k | stun_attr_get_first_addr_str(buf, Size, kAddrAttrs[i], &addr, NULL); |
158 | 1.52k | } |
159 | | |
160 | 191 | sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_CHANNEL_NUMBER); |
161 | 191 | if (sar) { |
162 | 1 | (void)stun_attr_get_channel_number(sar); |
163 | 1 | } |
164 | 191 | (void)stun_attr_get_first_channel_number_str(buf, Size); |
165 | | |
166 | 191 | sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY); |
167 | 191 | if (sar) { |
168 | 0 | (void)stun_get_requested_address_family(sar); |
169 | 0 | } |
170 | | |
171 | 191 | sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_ADDITIONAL_ADDRESS_FAMILY); |
172 | 191 | if (sar) { |
173 | 4 | (void)stun_get_requested_address_family(sar); |
174 | 4 | } |
175 | | |
176 | 191 | sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_EVEN_PORT); |
177 | 191 | if (sar) { |
178 | 0 | (void)stun_attr_get_even_port(sar); |
179 | 0 | } |
180 | | |
181 | 191 | sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_BANDWIDTH); |
182 | 191 | if (sar) { |
183 | 2 | (void)stun_attr_get_bandwidth(sar); |
184 | 2 | } |
185 | | |
186 | 191 | sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_NEW_BANDWIDTH); |
187 | 191 | if (sar) { |
188 | 0 | (void)stun_attr_get_bandwidth(sar); |
189 | 0 | } |
190 | | |
191 | 191 | sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_RESERVATION_TOKEN); |
192 | 191 | if (sar) { |
193 | 0 | (void)stun_attr_get_reservation_token_value(sar); |
194 | 0 | } |
195 | | |
196 | 191 | sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_CHANGE_REQUEST); |
197 | 191 | if (sar) { |
198 | 1 | bool change_ip = false, change_port = false; |
199 | 1 | stun_attr_get_change_request_str(sar, &change_ip, &change_port); |
200 | 1 | } |
201 | | |
202 | 191 | sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_RESPONSE_PORT); |
203 | 191 | if (sar) { |
204 | 0 | (void)stun_attr_get_response_port_str(sar); |
205 | 0 | } |
206 | | |
207 | 191 | sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_PADDING); |
208 | 191 | if (sar) { |
209 | 0 | (void)stun_attr_get_padding_len_str(sar); |
210 | 0 | } |
211 | | |
212 | 191 | { |
213 | 191 | int err_code = 0; |
214 | 191 | uint8_t err_msg[1024] = {0}; |
215 | 191 | stun_is_error_response_str(buf, Size, &err_code, err_msg, sizeof(err_msg)); |
216 | 191 | } |
217 | 191 | { |
218 | 191 | int err_code = 0; |
219 | 191 | uint8_t err_msg[1024] = {0}; |
220 | 191 | uint8_t chal_realm[STUN_MAX_REALM_SIZE + 1] = {0}; |
221 | 191 | uint8_t chal_nonce[STUN_MAX_NONCE_SIZE + 1] = {0}; |
222 | 191 | uint8_t server_name[STUN_MAX_SERVER_NAME_SIZE + 1] = {0}; |
223 | 191 | bool oauth = false; |
224 | 191 | stun_is_challenge_response_str(buf, Size, &err_code, err_msg, sizeof(err_msg), chal_realm, chal_nonce, server_name, |
225 | 191 | &oauth); |
226 | 191 | } |
227 | | |
228 | 191 | const size_t num_all_attrs = sizeof(kAllAttrTypes) / sizeof(kAllAttrTypes[0]); |
229 | 8.59k | for (size_t i = 0; i < num_all_attrs; i++) { |
230 | 8.40k | sar = stun_attr_get_first_by_type_str(buf, Size, kAllAttrTypes[i]); |
231 | 8.40k | if (sar) { |
232 | 151 | (void)stun_attr_get_type(sar); |
233 | 151 | (void)stun_attr_get_len(sar); |
234 | 151 | (void)stun_attr_get_value(sar); |
235 | 151 | } |
236 | 8.40k | } |
237 | 191 | } |
238 | | |
239 | | /* ------------------------------------------------------------------ */ |
240 | | /* Attribute serialization / append paths (FuzzStunAttrAdd). */ |
241 | | /* ------------------------------------------------------------------ */ |
242 | 660 | static void harness_attr_add(const uint8_t *Data, size_t Size) { |
243 | 660 | if (Size < STUN_HEADER_LENGTH || Size > 4096) { |
244 | 54 | return; |
245 | 54 | } |
246 | | |
247 | 606 | uint8_t buf[MAX_STUN_MESSAGE_SIZE] = {0}; |
248 | 606 | memcpy(buf, Data, Size); |
249 | 606 | size_t len = Size; |
250 | | |
251 | 606 | if (!stun_is_command_message_str(buf, len)) { |
252 | 418 | return; |
253 | 418 | } |
254 | | |
255 | 188 | uint8_t test_uname[] = "fuzzuser@fuzz.realm"; |
256 | 188 | stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_USERNAME, test_uname, (int)(sizeof(test_uname) - 1)); |
257 | | |
258 | 188 | uint8_t test_realm[] = "fuzz.realm"; |
259 | 188 | stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REALM, test_realm, (int)(sizeof(test_realm) - 1)); |
260 | | |
261 | 188 | uint8_t test_nonce[] = "fuzznonce0123456789abcdef"; |
262 | 188 | stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_NONCE, test_nonce, (int)(sizeof(test_nonce) - 1)); |
263 | | |
264 | 188 | uint8_t test_sw[] = "coturn-fuzz/1.0"; |
265 | 188 | stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_SOFTWARE, test_sw, (int)(sizeof(test_sw) - 1)); |
266 | | |
267 | 188 | uint8_t lifetime_val[4] = {0x00, 0x00, 0x02, 0x58}; |
268 | 188 | stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_LIFETIME, lifetime_val, 4); |
269 | | |
270 | 188 | uint8_t transport_val[4] = {STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE, 0x00, 0x00, 0x00}; |
271 | 188 | stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REQUESTED_TRANSPORT, transport_val, 4); |
272 | | |
273 | 188 | uint8_t af_val[4] = {STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4, 0x00, 0x00, 0x00}; |
274 | 188 | stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY, af_val, 4); |
275 | | |
276 | 188 | uint8_t even_port_val[1] = {0x80}; |
277 | 188 | stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_EVEN_PORT, even_port_val, 1); |
278 | | |
279 | 188 | stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_DONT_FRAGMENT, NULL, 0); |
280 | | |
281 | 188 | stun_attr_add_channel_number_str(buf, &len, 0x4001); |
282 | | |
283 | 188 | stun_attr_add_bandwidth_str(buf, &len, 1000000); |
284 | | |
285 | 188 | ioa_addr addr4 = {0}; |
286 | 188 | addr4.s4.sin_family = AF_INET; |
287 | 188 | addr4.s4.sin_port = htons(12345); |
288 | 188 | addr4.s4.sin_addr.s_addr = htonl(0xC0A80001); |
289 | 188 | stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr4); |
290 | 188 | stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &addr4); |
291 | 188 | stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &addr4); |
292 | 188 | stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_MAPPED_ADDRESS, &addr4); |
293 | | |
294 | 188 | ioa_addr addr6 = {0}; |
295 | 188 | addr6.s6.sin6_family = AF_INET6; |
296 | 188 | addr6.s6.sin6_port = htons(54321); |
297 | 188 | addr6.s6.sin6_addr.s6_addr[15] = 1; |
298 | 188 | stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr6); |
299 | 188 | stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &addr6); |
300 | | |
301 | 188 | stun_attr_add_address_error_code(buf, &len, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6, 440); |
302 | | |
303 | 188 | stun_attr_add_change_request_str(buf, &len, true, true); |
304 | 188 | stun_attr_add_response_port_str(buf, &len, 3479); |
305 | 188 | stun_attr_add_padding_str(buf, &len, 64); |
306 | | |
307 | 188 | if (Size > STUN_HEADER_LENGTH + 4) { |
308 | 163 | int data_len = (int)(Size - STUN_HEADER_LENGTH); |
309 | 163 | if (data_len > 1024) { |
310 | 12 | data_len = 1024; |
311 | 12 | } |
312 | 163 | stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_DATA, Data + STUN_HEADER_LENGTH, data_len); |
313 | 163 | } |
314 | | |
315 | 188 | stun_attr_add_fingerprint_str(buf, &len); |
316 | 188 | } |
317 | | |
318 | | /* ------------------------------------------------------------------ */ |
319 | | /* Legacy (pre-RFC 5389) STUN detection (FuzzOldStun). */ |
320 | | /* ------------------------------------------------------------------ */ |
321 | 660 | static void harness_old_stun(const uint8_t *Data, size_t Size) { |
322 | 660 | if (Size < STUN_HEADER_LENGTH || Size > 5120) { |
323 | 33 | return; |
324 | 33 | } |
325 | | |
326 | 627 | uint8_t buf[5120]; |
327 | 627 | memcpy(buf, Data, Size); |
328 | | |
329 | 627 | uint32_t cookie = 0; |
330 | 627 | bool is_old = old_stun_is_command_message_str(buf, Size, &cookie); |
331 | 627 | (void)stun_is_command_message_str(buf, Size); |
332 | | |
333 | 627 | if (is_old) { |
334 | 166 | (void)stun_get_msg_type_str(buf, Size); |
335 | 166 | (void)stun_get_method_str(buf, Size); |
336 | | |
337 | 166 | stun_is_request_str(buf, Size); |
338 | 166 | stun_is_indication_str(buf, Size); |
339 | 166 | stun_is_success_response_str(buf, Size); |
340 | | |
341 | 166 | int err_code = 0; |
342 | 166 | uint8_t err_msg[256] = {0}; |
343 | 166 | stun_is_error_response_str(buf, Size, &err_code, err_msg, sizeof(err_msg)); |
344 | | |
345 | 166 | int fp_present = 0; |
346 | 166 | stun_is_command_message_full_check_str(buf, Size, 1, &fp_present); |
347 | 166 | stun_is_command_message_full_check_str(buf, Size, 0, &fp_present); |
348 | | |
349 | 166 | stun_is_binding_request_str(buf, Size, 0); |
350 | 166 | stun_is_binding_response_str(buf, Size); |
351 | | |
352 | 166 | stun_attr_ref sar = stun_attr_get_first_str(buf, Size); |
353 | 404 | while (sar) { |
354 | 238 | (void)stun_attr_get_type(sar); |
355 | 238 | (void)stun_attr_get_len(sar); |
356 | 238 | sar = stun_attr_get_next_str(buf, Size, sar); |
357 | 238 | } |
358 | 166 | } |
359 | 627 | } |
360 | | |
361 | | /* ------------------------------------------------------------------ */ |
362 | | /* Challenge-response builder (FuzzStunChallengeResponse). */ |
363 | | /* */ |
364 | | /* stun_is_challenge_response_str only descends into its three inner */ |
365 | | /* stun_attr_get_first_by_type_str calls when: */ |
366 | | /* 1) the message is an error response, */ |
367 | | /* 2) err_code is 401 OR 438, */ |
368 | | /* 3) a REALM attribute is present, */ |
369 | | /* 4) a NONCE attribute is present. */ |
370 | | /* The OAuth branch additionally requires THIRD-PARTY-AUTHORIZATION. */ |
371 | | /* */ |
372 | | /* The fuzzer-driven harness in harness_attr_iter calls the function */ |
373 | | /* every iteration but the conjunction of conditions is too specific */ |
374 | | /* for libFuzzer to discover from binary mutation alone — OSS-Fuzz */ |
375 | | /* introspector flags 9 unreached callsites under */ |
376 | | /* stun_attr_get_first_by_type_str gated on this function. */ |
377 | | /* */ |
378 | | /* Build six deterministic message variants on every iteration so */ |
379 | | /* every internal branch is exercised regardless of the input bytes. */ |
380 | | /* Realm / nonce / server-name lengths are derived from fuzz bytes so */ |
381 | | /* each iteration is still meaningfully distinct. */ |
382 | | /* ------------------------------------------------------------------ */ |
383 | 660 | static void harness_challenge_response_builder(const uint8_t *Data, size_t Size) { |
384 | 660 | if (!Size) { |
385 | 0 | return; |
386 | 0 | } |
387 | | |
388 | 660 | static const uint16_t kMethods[] = { |
389 | 660 | STUN_METHOD_ALLOCATE, |
390 | 660 | STUN_METHOD_BINDING, |
391 | 660 | STUN_METHOD_REFRESH, |
392 | 660 | STUN_METHOD_CHANNEL_BIND, |
393 | 660 | }; |
394 | 660 | const uint16_t method = kMethods[Data[0] % (sizeof(kMethods) / sizeof(kMethods[0]))]; |
395 | | |
396 | | /* Length of each variable-length string is taken from a single fuzz byte |
397 | | * so libFuzzer still varies the inputs across iterations. Floors keep the |
398 | | * attribute non-empty so the inner copies in stun_is_challenge_response_str |
399 | | * actually populate the output buffers. */ |
400 | 660 | const size_t realm_len = 1 + (Size > 1 ? (Data[1] % 16) : 0); |
401 | 660 | const size_t nonce_len = 1 + (Size > 2 ? (Data[2] % 16) : 0); |
402 | 660 | const size_t server_len = 1 + (Size > 3 ? (Data[3] % 16) : 0); |
403 | | |
404 | 660 | uint8_t realm_buf[STUN_MAX_REALM_SIZE + 1] = {0}; |
405 | 660 | uint8_t nonce_buf[STUN_MAX_NONCE_SIZE + 1] = {0}; |
406 | 660 | uint8_t server_buf[STUN_MAX_SERVER_NAME_SIZE + 1] = {0}; |
407 | 4.35k | for (size_t i = 0; i < realm_len; ++i) { |
408 | 3.69k | realm_buf[i] = (uint8_t)('a' + (i % 26)); |
409 | 3.69k | } |
410 | 2.57k | for (size_t i = 0; i < nonce_len; ++i) { |
411 | 1.91k | nonce_buf[i] = (uint8_t)('0' + (i % 10)); |
412 | 1.91k | } |
413 | 5.52k | for (size_t i = 0; i < server_len; ++i) { |
414 | 4.86k | server_buf[i] = (uint8_t)('A' + (i % 26)); |
415 | 4.86k | } |
416 | | |
417 | 660 | stun_tid tid = {0}; |
418 | 8.58k | for (size_t i = 0; i < STUN_TID_SIZE; ++i) { |
419 | 7.92k | tid.tsx_id[i] = (uint8_t)(Size > i + 4 ? Data[i + 4] : (uint8_t)i); |
420 | 7.92k | } |
421 | | |
422 | | /* Each variant builds a fresh error response and runs it through the |
423 | | * predicate so the inner attribute lookups fire. */ |
424 | 660 | static const struct { |
425 | 660 | uint16_t err_code; |
426 | 660 | bool include_realm; |
427 | 660 | bool include_nonce; |
428 | 660 | bool include_third_party_auth; |
429 | 660 | } kVariants[] = { |
430 | 660 | {401, true, true, false}, /* canonical 401 challenge: REALM + NONCE */ |
431 | 660 | {401, true, true, true}, /* same + THIRD-PARTY-AUTHORIZATION (OAuth branch) */ |
432 | 660 | {438, true, true, false}, /* covers the (*err_code) == 438 disjunct */ |
433 | 660 | {401, true, false, false}, /* REALM present, NONCE missing — negative inner path */ |
434 | 660 | {401, false, false, false}, /* err_code matches but no REALM — outer negative path */ |
435 | 660 | {400, true, true, false}, /* err_code does not match 401/438 — early-out path */ |
436 | 660 | }; |
437 | | |
438 | 4.62k | for (size_t v = 0; v < sizeof(kVariants) / sizeof(kVariants[0]); ++v) { |
439 | 3.96k | uint8_t buf[MAX_STUN_MESSAGE_SIZE] = {0}; |
440 | 3.96k | size_t len = 0; |
441 | 3.96k | stun_init_error_response_str(method, buf, &len, kVariants[v].err_code, (const uint8_t *)"unauthorized", &tid, true); |
442 | | |
443 | 3.96k | if (kVariants[v].include_realm) { |
444 | 3.30k | stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REALM, realm_buf, (int)realm_len); |
445 | 3.30k | } |
446 | 3.96k | if (kVariants[v].include_third_party_auth) { |
447 | 660 | stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_THIRD_PARTY_AUTHORIZATION, server_buf, (int)server_len); |
448 | 660 | } |
449 | 3.96k | if (kVariants[v].include_nonce) { |
450 | 2.64k | stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_NONCE, nonce_buf, (int)nonce_len); |
451 | 2.64k | } |
452 | | |
453 | 3.96k | int err_code = 0; |
454 | 3.96k | uint8_t err_msg[1024] = {0}; |
455 | 3.96k | uint8_t out_realm[STUN_MAX_REALM_SIZE + 1] = {0}; |
456 | 3.96k | uint8_t out_nonce[STUN_MAX_NONCE_SIZE + 1] = {0}; |
457 | 3.96k | uint8_t out_server[STUN_MAX_SERVER_NAME_SIZE + 1] = {0}; |
458 | 3.96k | bool oauth = false; |
459 | 3.96k | (void)stun_is_challenge_response_str(buf, len, &err_code, err_msg, sizeof(err_msg), out_realm, out_nonce, |
460 | 3.96k | out_server, &oauth); |
461 | | |
462 | | /* Also exercise the NULL-oauth-pointer branch, which is reachable from |
463 | | * other call sites in the codebase. */ |
464 | 3.96k | (void)stun_is_challenge_response_str(buf, len, &err_code, err_msg, sizeof(err_msg), out_realm, out_nonce, |
465 | 3.96k | out_server, NULL); |
466 | 3.96k | } |
467 | 660 | } |
468 | | |
469 | | /* ------------------------------------------------------------------ */ |
470 | | /* libFuzzer entry point — run every harness on each input. */ |
471 | | /* ------------------------------------------------------------------ */ |
472 | 978 | extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { |
473 | 978 | harness_integrity_sha1(Data, Size); |
474 | 978 | harness_integrity_multi(Data, Size); |
475 | 978 | harness_attr_iter(Data, Size); |
476 | 978 | harness_attr_add(Data, Size); |
477 | 978 | harness_old_stun(Data, Size); |
478 | 978 | harness_challenge_response_builder(Data, Size); |
479 | 978 | return 0; |
480 | 978 | } |