/src/ndpi/fuzz/fuzz_ndpi_reader_payload_analyzer.c
Line | Count | Source |
1 | | #include "reader_util.h" |
2 | | #include "ndpi_api.h" |
3 | | #include "fuzz_common_code.h" |
4 | | |
5 | | #include <pcap/pcap.h> |
6 | | |
7 | | #include <errno.h> |
8 | | #include <stdint.h> |
9 | | #include <stdio.h> |
10 | | #include <assert.h> |
11 | | #include <libgen.h> |
12 | | |
13 | | #ifdef ENABLE_PCAP_L7_MUTATOR |
14 | | #include "pl7m.h" |
15 | | #endif |
16 | | |
17 | | #ifdef ENABLE_NALLOC |
18 | | #include "nallocinc.c" |
19 | | #endif |
20 | | |
21 | | struct ndpi_workflow_prefs *prefs = NULL; |
22 | | struct ndpi_workflow *workflow = NULL; |
23 | | struct ndpi_global_context *g_ctx; |
24 | | |
25 | | u_int8_t enable_payload_analyzer = 0; |
26 | | u_int8_t enable_flow_stats = 1; |
27 | | u_int8_t human_readeable_string_len = 5; |
28 | | u_int8_t max_num_udp_dissected_pkts = 0, max_num_tcp_dissected_pkts = 0; /* Disable limits at application layer */; |
29 | | int malloc_size_stats = 0; |
30 | | FILE *fingerprint_fp = NULL; |
31 | | char *addr_dump_path = NULL; |
32 | | int monitoring_enabled = 1; |
33 | | u_int8_t enable_doh_dot_detection = 0; |
34 | | |
35 | | static char *path = NULL; |
36 | | |
37 | | extern void ndpi_report_payload_stats(FILE *out); |
38 | | |
39 | | #ifdef CRYPT_FORCE_NO_AESNI |
40 | | extern int force_no_aesni; |
41 | | #endif |
42 | | |
43 | | #ifdef ENABLE_PCAP_L7_MUTATOR |
44 | | size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, |
45 | | size_t MaxSize, unsigned int Seed) { |
46 | | return pl7m_mutator(Data, Size, MaxSize, Seed); |
47 | | } |
48 | | #endif |
49 | | |
50 | 36 | int LLVMFuzzerInitialize(int *argc, char ***argv) { |
51 | 36 | (void)argc; |
52 | | |
53 | 36 | path = dirname(strdup(*argv[0])); /* No errors; no free! */ |
54 | 36 | return 0; |
55 | 36 | } |
56 | | |
57 | 467k | static void node_cleanup_walker(const void *node, ndpi_VISIT which, int depth, void *user_data) { |
58 | 467k | struct ndpi_flow_info *flow = *(struct ndpi_flow_info **) node; |
59 | | |
60 | 467k | (void)depth; |
61 | 467k | (void)user_data; |
62 | | |
63 | 467k | if((which == ndpi_preorder) || (which == ndpi_leaf)) { /* Avoid walking the same node multiple times */ |
64 | 225k | if((!flow->detection_completed) && flow->ndpi_flow) { |
65 | 172k | flow->detected_protocol = ndpi_detection_giveup(workflow->ndpi_struct, |
66 | 172k | flow->ndpi_flow); |
67 | 172k | } |
68 | | |
69 | 225k | process_ndpi_collected_info(workflow, flow); |
70 | 225k | } |
71 | 467k | } |
72 | | |
73 | | static void fn_flow_callback(struct ndpi_workflow *w, struct ndpi_flow_info *f, void *d) |
74 | 53.2k | { |
75 | 53.2k | (void)w; |
76 | 53.2k | (void)f; |
77 | 53.2k | (void)d; |
78 | 53.2k | } |
79 | | |
80 | 256k | int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { |
81 | 256k | pcap_t * pkts; |
82 | 256k | const u_char *pkt; |
83 | 256k | struct pcap_pkthdr *header; |
84 | 256k | int r; |
85 | 256k | char errbuf[PCAP_ERRBUF_SIZE]; |
86 | 256k | u_int i; |
87 | 256k | FILE *fd; |
88 | 256k | char name[256]; |
89 | | |
90 | 256k | if (prefs == NULL) { |
91 | 9 | prefs = calloc(sizeof(struct ndpi_workflow_prefs), 1); /* No failure here */ |
92 | 9 | prefs->decode_tunnels = 1; |
93 | 9 | prefs->num_roots = 16; |
94 | 9 | prefs->max_ndpi_flows = 16 * 1024 * 1024; |
95 | 9 | prefs->quiet_mode = 0; |
96 | | |
97 | 9 | #ifdef ENABLE_MEM_ALLOC_FAILURES |
98 | 9 | fuzz_set_alloc_callbacks(); |
99 | 9 | #endif |
100 | | |
101 | 9 | g_ctx = ndpi_global_init(); |
102 | | |
103 | 9 | workflow = ndpi_workflow_init(prefs, NULL /* pcap handler will be set later */, 0, ndpi_serialization_format_json, g_ctx); |
104 | | |
105 | 9 | ndpi_workflow_set_flow_callback(workflow, fn_flow_callback, NULL); |
106 | | |
107 | 9 | ndpi_set_config(workflow->ndpi_struct, NULL, "log.level", "3"); |
108 | 9 | ndpi_set_config(workflow->ndpi_struct, "all", "log", "1"); |
109 | | |
110 | 9 | sprintf(name, "%s/public_suffix_list.dat", path); |
111 | 9 | assert(ndpi_load_domain_suffixes(workflow->ndpi_struct, name) >= 0); |
112 | 9 | sprintf(name, "%s/lists/", path); |
113 | 9 | assert(ndpi_load_categories_dir(workflow->ndpi_struct, name) >= 0); |
114 | 9 | sprintf(name, "%s/lists/protocols/", path); |
115 | 9 | assert(ndpi_load_protocols_dir(workflow->ndpi_struct, name) >= 0); |
116 | 9 | sprintf(name, "%s/protos.txt", path); |
117 | 9 | assert(ndpi_load_protocols_file(workflow->ndpi_struct, name) >= 0); |
118 | 9 | sprintf(name, "%s/categories.txt", path); |
119 | 9 | assert(ndpi_load_categories_file(workflow->ndpi_struct, name, NULL) >= 0); |
120 | 9 | sprintf(name, "%s/risky_domains.txt", path); |
121 | 9 | assert(ndpi_load_risk_domain_file(workflow->ndpi_struct, name) >= 0); |
122 | 9 | sprintf(name, "%s/ja4_fingerprints.csv", path); |
123 | 9 | assert(ndpi_load_malicious_ja4_file(workflow->ndpi_struct, name) >= 0); |
124 | 9 | sprintf(name, "%s/tcp_fingerprints.csv", path); |
125 | 9 | assert(ndpi_load_tcp_fingerprint_file(workflow->ndpi_struct, name) >= 0); |
126 | 9 | sprintf(name, "%s/sha1_fingerprints.csv", path); |
127 | 9 | assert(ndpi_load_malicious_sha1_file(workflow->ndpi_struct, name) >= 0); |
128 | 9 | sprintf(name, "%s", path); |
129 | 9 | assert(ndpi_load_protocol_plugins(workflow->ndpi_struct, name) >= 0); /* Plugins are not really used while fuzzing, yet */ |
130 | | |
131 | | #ifdef ENABLE_ONLY_SUBCLASSIFICATION |
132 | | sprintf(name, "%s/config_only_classification.txt", path); |
133 | | assert(ndpi_set_config(workflow->ndpi_struct, NULL, "filename.config", name) == NDPI_CFG_OK); |
134 | | #else |
135 | | |
136 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, NULL, "packets_limit_per_flow", "255") == NDPI_CFG_OK); |
137 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, NULL, "flow.track_payload", "1") == NDPI_CFG_OK); |
138 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, NULL, "tcp_ack_payload_heuristic", "1") == NDPI_CFG_OK); |
139 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, NULL, "fully_encrypted_heuristic", "1") == NDPI_CFG_OK); |
140 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "dns", "subclassification", "1") == NDPI_CFG_OK); |
141 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "tls", "application_blocks_tracking", "1") == NDPI_CFG_OK); |
142 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "tls", "max_num_blocks_to_analyze", "8") == NDPI_CFG_OK); |
143 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "tls", "tls_blocks_show_timing", "0") == NDPI_CFG_OK); |
144 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ja_ignore_ephemeral_tls_extn", "1") == NDPI_CFG_OK); |
145 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ndpifp_ignore_sni_tls_extn", "1") == NDPI_CFG_OK); |
146 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ja_data", "1") == NDPI_CFG_OK); |
147 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "ssh", "metadata.ssh_data", "1") == NDPI_CFG_OK); |
148 | 9 | #ifndef ENABLE_CONFIG2 |
149 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "stun", "max_packets_extra_dissection", "40") == NDPI_CFG_OK); |
150 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "zoom", "max_packets_extra_dissection", "255") == NDPI_CFG_OK); |
151 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "rtp", "search_for_stun", "1") == NDPI_CFG_OK); |
152 | 9 | #endif |
153 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "openvpn", "dpi.heuristics", "0x01") == NDPI_CFG_OK); |
154 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "openvpn", "dpi.heuristics.num_messages", "20") == NDPI_CFG_OK); |
155 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ja4r_fingerprint", "1") == NDPI_CFG_OK); |
156 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "tls", "dpi.heuristics", "0x07") == NDPI_CFG_OK); |
157 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "tls", "dpi.heuristics.max_packets_extra_dissection", "40") == NDPI_CFG_OK); |
158 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, "all", "monitoring", "1") == NDPI_CFG_OK); |
159 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, NULL, "dpi.address_cache_size", "8192") == NDPI_CFG_OK); |
160 | | |
161 | | /* Roaring code doesn't handle memory allocation failures */ |
162 | | #ifdef ENABLE_NALLOC |
163 | | assert(ndpi_set_config(workflow->ndpi_struct, NULL, "hostname_dns_check", "0") == NDPI_CFG_OK); |
164 | | #else |
165 | 9 | assert(ndpi_set_config(workflow->ndpi_struct, NULL, "hostname_dns_check", "1") == NDPI_CFG_OK); |
166 | 9 | #endif |
167 | | |
168 | | #ifdef ENABLE_CONFIG2 |
169 | | assert(ndpi_set_config(workflow->ndpi_struct, NULL, "flow_risk.all.info", "0") == NDPI_CFG_OK); |
170 | | assert(ndpi_set_config(workflow->ndpi_struct, NULL, "metadata.tcp_fingerprint_format", "1") == NDPI_CFG_OK); |
171 | | assert(ndpi_set_config(workflow->ndpi_struct, NULL, "metadata.ndpi_fingerprint_format", "1") == NDPI_CFG_OK); |
172 | | assert(ndpi_set_config(workflow->ndpi_struct, NULL, "metadata.ndpi_fingerprint_ignore_tcp_fp", "1") == NDPI_CFG_OK); |
173 | | assert(ndpi_set_config(workflow->ndpi_struct, "tls", "max_num_blocks_to_analyze", "8") == NDPI_CFG_OK); |
174 | | assert(ndpi_set_config(workflow->ndpi_struct, "tls", "tls_blocks_show_timing", "0") == NDPI_CFG_OK); |
175 | | assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ja_ignore_ephemeral_tls_extn", "1") == NDPI_CFG_OK); |
176 | | assert(ndpi_set_config(workflow->ndpi_struct, "tls", "metadata.ndpifp_ignore_sni_tls_extn", "1") == NDPI_CFG_OK); |
177 | | |
178 | | addr_dump_path = "/tmp/"; |
179 | | #endif |
180 | | |
181 | 9 | #endif /* ENABLE_ONLY_SUBCLASSIFICATION */ |
182 | | |
183 | 9 | ndpi_finalize_initialization(workflow->ndpi_struct); |
184 | | |
185 | | #ifdef ENABLE_FINGERPRINT_FP |
186 | | fingerprint_fp = stdout; |
187 | | #endif |
188 | | |
189 | | #ifdef CRYPT_FORCE_NO_AESNI |
190 | | force_no_aesni = 1; |
191 | | #endif |
192 | | |
193 | 9 | #ifdef ENABLE_PAYLOAD_ANALYZER |
194 | 9 | enable_payload_analyzer = 1; |
195 | 9 | #endif |
196 | 9 | } |
197 | | |
198 | 256k | #ifdef ENABLE_MEM_ALLOC_FAILURES |
199 | | /* Don't fail memory allocations until init phase is done */ |
200 | 256k | fuzz_set_alloc_callbacks_and_seed(Size); |
201 | 256k | #endif |
202 | | |
203 | 256k | fd = buffer_to_file(Data, Size); |
204 | 256k | if (fd == NULL) |
205 | 0 | return 0; |
206 | | |
207 | 256k | pkts = pcap_fopen_offline(fd, errbuf); |
208 | 256k | if (pkts == NULL) { |
209 | 25 | fclose(fd); |
210 | 25 | return 0; |
211 | 25 | } |
212 | 256k | if (ndpi_is_datalink_supported(pcap_datalink(pkts)) == 0) |
213 | 332 | { |
214 | | /* Do not fail if the datalink type is not supported (may happen often during fuzzing). */ |
215 | 332 | pcap_close(pkts); |
216 | 332 | return 0; |
217 | 332 | } |
218 | | |
219 | 256k | workflow->pcap_handle = pkts; |
220 | | /* Init flow tree */ |
221 | 256k | workflow->ndpi_flows_root = ndpi_calloc(workflow->prefs.num_roots, sizeof(void *)); |
222 | 256k | if(!workflow->ndpi_flows_root) { |
223 | 2 | pcap_close(pkts); |
224 | 2 | return 0; |
225 | 2 | } |
226 | | |
227 | | #ifdef ENABLE_NALLOC |
228 | | nalloc_init("nalloc"); |
229 | | nalloc_start(Data, Size); |
230 | | #endif |
231 | | |
232 | 256k | header = NULL; |
233 | 256k | r = pcap_next_ex(pkts, &header, &pkt); |
234 | 14.0M | while (r > 0) { |
235 | | /* allocate an exact size buffer to check overflows */ |
236 | 13.7M | uint8_t *packet_checked = malloc(header->caplen); |
237 | | |
238 | 13.7M | if(packet_checked) { |
239 | 13.5M | ndpi_risk flow_risk; |
240 | 13.5M | struct ndpi_flow_info *flow = NULL; /* unused */ |
241 | | |
242 | 13.5M | memcpy(packet_checked, pkt, header->caplen); |
243 | 13.5M | ndpi_workflow_process_packet(workflow, header, packet_checked, &flow_risk, &flow); |
244 | 13.5M | free(packet_checked); |
245 | 13.5M | } |
246 | | |
247 | 13.7M | r = pcap_next_ex(pkts, &header, &pkt); |
248 | 13.7M | } |
249 | 256k | pcap_close(pkts); |
250 | | |
251 | | /* Free flow trees */ |
252 | 4.36M | for(i = 0; i < workflow->prefs.num_roots; i++) { |
253 | 4.10M | ndpi_twalk(workflow->ndpi_flows_root[i], node_cleanup_walker, NULL); |
254 | 4.10M | ndpi_tdestroy(workflow->ndpi_flows_root[i], ndpi_flow_info_freer); |
255 | 4.10M | } |
256 | 256k | ndpi_free(workflow->ndpi_flows_root); |
257 | | /* Free payload analyzer data */ |
258 | 256k | if(enable_payload_analyzer) |
259 | 26.7k | ndpi_report_payload_stats(stdout); |
260 | | |
261 | 256k | #ifdef ENABLE_PAYLOAD_ANALYZER |
262 | 256k | ndpi_update_params(SPLT_PARAM_TYPE, "splt_param.txt"); |
263 | 256k | ndpi_update_params(BD_PARAM_TYPE, "bd_param.txt"); |
264 | 256k | ndpi_update_params(2, ""); /* invalid */ |
265 | 256k | #endif |
266 | | |
267 | | #ifdef ENABLE_NALLOC |
268 | | nalloc_end(); |
269 | | #endif |
270 | | |
271 | 256k | return 0; |
272 | 256k | } |