Line data Source code
1 : /*********************************************************************
2 : *
3 : * File : $Source: /cvsroot/ijbswa/current/fuzz.c,v $
4 : *
5 : * Purpose : Fuzz-related functions for Privoxy.
6 : *
7 : * Copyright : Written by and Copyright (C) 2014-16 by
8 : * Fabian Keil <fk@fabiankeil.de>
9 : *
10 : * This program is free software; you can redistribute it
11 : * and/or modify it under the terms of the GNU General
12 : * Public License as published by the Free Software
13 : * Foundation; either version 2 of the License, or (at
14 : * your option) any later version.
15 : *
16 : * This program is distributed in the hope that it will
17 : * be useful, but WITHOUT ANY WARRANTY; without even the
18 : * implied warranty of MERCHANTABILITY or FITNESS FOR A
19 : * PARTICULAR PURPOSE. See the GNU General Public
20 : * License for more details.
21 : *
22 : * The GNU General Public License should be included with
23 : * this file. If not, you can view it at
24 : * http://www.gnu.org/copyleft/gpl.html
25 : * or write to the Free Software Foundation, Inc., 59
26 : * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 : *
28 : *********************************************************************/
29 :
30 :
31 : #include "config.h"
32 :
33 : #include <stdio.h>
34 : #include <string.h>
35 : #include <sys/types.h>
36 : #include <unistd.h>
37 :
38 : #include "project.h"
39 : #include "filters.h"
40 : #include "loaders.h"
41 : #include "parsers.h"
42 : #include "miscutil.h"
43 : #include "errlog.h"
44 : #include "actions.h"
45 : #include "cgi.h"
46 : #include "loadcfg.h"
47 : #include "urlmatch.h"
48 : #include "filters.h"
49 : #include "jbsockets.h"
50 : #include "gateway.h"
51 : #include "jcc.h"
52 : #include "list.h"
53 :
54 :
55 : #ifdef FUZZ
56 : static int fuzz_action(struct client_state *csp, char *fuzz_input_file);
57 : static int fuzz_client_header(struct client_state *csp, char *fuzz_input_file);
58 : static int fuzz_deflate(struct client_state *csp, char *fuzz_input_file);
59 : static int fuzz_filter(struct client_state *csp, char *fuzz_input_file);
60 : static int fuzz_gif(struct client_state *csp, char *fuzz_input_file);
61 : static int fuzz_gzip(struct client_state *csp, char *fuzz_input_file);
62 : static int fuzz_socks(struct client_state *csp, char *fuzz_input_file);
63 : static int fuzz_pcrs_substitute(struct client_state *csp, char *fuzz_input_file);
64 : static int fuzz_server_header(struct client_state *csp, char *fuzz_input_file);
65 :
66 : struct fuzz_mode
67 : {
68 : const char *name;
69 : const char *expected_input;
70 : const int stdin_support;
71 : int (* const handler)(struct client_state *csp, char *input_file);
72 : };
73 :
74 : static const struct fuzz_mode fuzz_modes[] = {
75 : { "action", "Text to parse as action file.", 0, fuzz_action },
76 : { "client-request", "Client request to parse. Currently incomplete", 1, fuzz_client_request },
77 : { "client-header", "Client header to parse.", 1, fuzz_client_header },
78 : { "chunked-transfer-encoding", "Chunk-encoded data to dechunk.", 1, fuzz_chunked_transfer_encoding },
79 : { "deflate", "deflate-compressed data to decompress.", 1, fuzz_deflate },
80 : { "filter", "Text to parse as filter file.", 0, fuzz_filter },
81 : { "gif", "gif to deanimate.", 1, fuzz_gif },
82 : { "gzip", "gzip-compressed data to decompress.", 1, fuzz_gzip },
83 : { "pcrs-substitute", "A pcrs-substitute to compile. Not a whole pcrs job! Example: Bla $1 bla \x43 $3 blah.", 1, fuzz_pcrs_substitute },
84 : { "server-header", "Server header to parse.", 1, fuzz_server_header },
85 : { "server-response", "Server response to parse.", 1, fuzz_server_response },
86 : { "socks", "A socks server response. Only reads from stdin!", 1, fuzz_socks },
87 : };
88 :
89 : /*********************************************************************
90 : *
91 : * Function : load_fuzz_input_from_stdin
92 : *
93 : * Description : Loads stdin into a buffer.
94 : *
95 : * Parameters :
96 : * 1 : csp = Used to store the data.
97 : *
98 : * Returns : JB_ERR_OK in case of success,
99 : *
100 : *********************************************************************/
101 0 : static jb_err load_fuzz_input_from_stdin(struct client_state *csp)
102 : {
103 : static char buf[BUFFER_SIZE];
104 : int ret;
105 :
106 0 : while (0 < (ret = read_socket(0, buf, sizeof(buf))))
107 : {
108 0 : log_error(LOG_LEVEL_INFO,
109 : "Got %d bytes from stdin: %E. They look like this: %N",
110 : ret, ret, buf);
111 :
112 0 : if (add_to_iob(csp->iob, csp->config->buffer_limit, buf, ret))
113 : {
114 0 : log_error(LOG_LEVEL_FATAL, "Failed to buffer them.");
115 : }
116 : }
117 :
118 0 : log_error(LOG_LEVEL_INFO, "Read %d bytes from stdin",
119 0 : csp->iob->eod -csp->iob->cur);
120 :
121 0 : return JB_ERR_OK;
122 : }
123 :
124 : /*********************************************************************
125 : *
126 : * Function : load_fuzz_input_from_file
127 : *
128 : * Description : Loads file content into a buffer.
129 : *
130 : * Parameters :
131 : * 1 : csp = Used to store the data.
132 : * 2 : filename = Name of the file to be loaded.
133 : *
134 : * Returns : JB_ERR_OK in case of success,
135 : *
136 : *********************************************************************/
137 0 : static jb_err load_fuzz_input_from_file(struct client_state *csp, const char *filename)
138 : {
139 : FILE *fp;
140 : size_t length;
141 : long ret;
142 :
143 0 : fp = fopen(filename, "rb");
144 0 : if (NULL == fp)
145 : {
146 0 : log_error(LOG_LEVEL_FATAL, "Failed to open %s: %E", filename);
147 : }
148 :
149 : /* Get file length */
150 0 : if (fseek(fp, 0, SEEK_END))
151 : {
152 0 : log_error(LOG_LEVEL_FATAL,
153 : "Unexpected error while fseek()ing to the end of %s: %E",
154 : filename);
155 : }
156 0 : ret = ftell(fp);
157 0 : if (-1 == ret)
158 : {
159 0 : log_error(LOG_LEVEL_FATAL,
160 : "Unexpected ftell() error while loading %s: %E",
161 : filename);
162 : }
163 0 : length = (size_t)ret;
164 :
165 : /* Go back to the beginning. */
166 0 : if (fseek(fp, 0, SEEK_SET))
167 : {
168 0 : log_error(LOG_LEVEL_FATAL,
169 : "Unexpected error while fseek()ing to the beginning of %s: %E",
170 : filename);
171 : }
172 :
173 0 : csp->iob->size = length + 1;
174 :
175 0 : csp->iob->buf = malloc_or_die(csp->iob->size);
176 0 : csp->iob->cur = csp->iob->buf;
177 0 : csp->iob->eod = csp->iob->buf + length;
178 :
179 0 : if (1 != fread(csp->iob->cur, length, 1, fp))
180 : {
181 : /*
182 : * May theoretically happen if the file size changes between
183 : * fseek() and fread() because it's edited in-place. Privoxy
184 : * and common text editors don't do that, thus we just fail.
185 : */
186 0 : log_error(LOG_LEVEL_FATAL,
187 : "Couldn't completely read file %s.", filename);
188 : }
189 0 : *csp->iob->eod = '\0';
190 :
191 0 : fclose(fp);
192 :
193 0 : return JB_ERR_OK;
194 :
195 : }
196 :
197 : /*********************************************************************
198 : *
199 : * Function : load_fuzz_input
200 : *
201 : * Description : Loads a file into a buffer. XXX: Reverse argument order
202 : *
203 : * Parameters :
204 : * 1 : csp = Used to store the data.
205 : * 2 : filename = Name of the file to be loaded.
206 : *
207 : * Returns : JB_ERR_OK in case of success,
208 : *
209 : *********************************************************************/
210 0 : jb_err load_fuzz_input(struct client_state *csp, const char *filename)
211 : {
212 0 : if (strcmp(filename, "-") == 0)
213 : {
214 0 : return load_fuzz_input_from_stdin(csp);
215 : }
216 :
217 0 : return load_fuzz_input_from_file(csp, filename);
218 : }
219 :
220 :
221 : /*********************************************************************
222 : *
223 : * Function : remove_forbidden_bytes
224 : *
225 : * Description : Sanitizes fuzzed data to decrease the likelihood of
226 : * premature parse abortions.
227 : *
228 : * Parameters :
229 : * 1 : csp = Used to store the data.
230 : *
231 : * Returns : N/A
232 : *
233 : *********************************************************************/
234 0 : static void remove_forbidden_bytes(struct client_state *csp)
235 : {
236 0 : char *p = csp->iob->cur;
237 0 : char first_valid_byte = ' ';
238 :
239 0 : while (p < csp->iob->eod)
240 : {
241 0 : if (*p != '\0')
242 : {
243 0 : first_valid_byte = *p;
244 0 : break;
245 : }
246 0 : p++;
247 : }
248 :
249 0 : p = csp->iob->cur;
250 0 : while (p < csp->iob->eod)
251 : {
252 0 : if (*p == '\0')
253 : {
254 0 : *p = first_valid_byte;
255 : }
256 0 : p++;
257 : }
258 0 : }
259 :
260 :
261 : /*********************************************************************
262 : *
263 : * Function : fuzz_action
264 : *
265 : * Description : Treat the fuzzed input as action file.
266 : *
267 : * Parameters :
268 : * 1 : csp = Used to store the data.
269 : * 2 : fuzz_input_file = File to read the input from.
270 : *
271 : * Returns : Result of fuzzed function
272 : *
273 : *********************************************************************/
274 0 : int fuzz_action(struct client_state *csp, char *fuzz_input_file)
275 : {
276 0 : csp->config->actions_file[0] = fuzz_input_file;
277 :
278 0 : return(load_action_files(csp));
279 : }
280 :
281 :
282 : /*********************************************************************
283 : *
284 : * Function : fuzz_client_header
285 : *
286 : * Description : Treat the fuzzed input as a client header.
287 : *
288 : * Parameters :
289 : * 1 : csp = Used to store the data.
290 : * 2 : fuzz_input_file = File to read the input from.
291 : *
292 : * Returns : Result of fuzzed function
293 : *
294 : *********************************************************************/
295 0 : int fuzz_client_header(struct client_state *csp, char *fuzz_input_file)
296 : {
297 : char *header;
298 :
299 0 : header = get_header(csp->iob);
300 :
301 0 : if (NULL == header)
302 : {
303 0 : return 1;
304 : }
305 0 : if (JB_ERR_OK != enlist(csp->headers, header))
306 : {
307 0 : return 1;
308 : }
309 :
310 : /*
311 : * Silence an insightful client_host_adder() warning
312 : * about ignored weirdness.
313 : */
314 0 : csp->flags |= CSP_FLAG_HOST_HEADER_IS_SET;
315 : /* Adding headers doesn't depend on the fuzzed input */
316 0 : csp->flags |= CSP_FLAG_CLIENT_CONNECTION_HEADER_SET;
317 :
318 : /* +hide-if-modified-since{+60} */
319 0 : csp->action->flags |= ACTION_HIDE_IF_MODIFIED_SINCE;
320 0 : csp->action->string[ACTION_STRING_IF_MODIFIED_SINCE] = "+60";
321 :
322 : /* XXX: Enable more actions. */
323 :
324 0 : return(sed(csp, FILTER_CLIENT_HEADERS));
325 : }
326 :
327 :
328 : /*********************************************************************
329 : *
330 : * Function : fuzz_filter
331 : *
332 : * Description : Treat the fuzzed input as filter file.
333 : *
334 : * Parameters :
335 : * 1 : csp = Used to store the data.
336 : * 2 : fuzz_input_file = File to read the input from.
337 : *
338 : * Returns : Result of fuzzed function
339 : *
340 : *********************************************************************/
341 0 : int fuzz_filter(struct client_state *csp, char *fuzz_input_file)
342 : {
343 0 : csp->config->re_filterfile[0] = fuzz_input_file;
344 0 : return (load_one_re_filterfile(csp, 0));
345 : }
346 :
347 :
348 : /*********************************************************************
349 : *
350 : * Function : fuzz_deflate
351 : *
352 : * Description : Treat the fuzzed input as data to deflate.
353 : *
354 : * Parameters :
355 : * 1 : csp = Used to store the data.
356 : * 2 : fuzz_input_file = File to read the input from.
357 : *
358 : * Returns : Result of fuzzed function
359 : *
360 : *********************************************************************/
361 0 : static int fuzz_deflate(struct client_state *csp, char *fuzz_input_file)
362 : {
363 0 : csp->content_type = CT_DEFLATE;
364 0 : return(JB_ERR_OK == decompress_iob(csp));
365 : }
366 :
367 :
368 : /*********************************************************************
369 : *
370 : * Function : fuzz_gif
371 : *
372 : * Description : Treat the fuzzed input as a gif to deanimate.
373 : *
374 : * Parameters :
375 : * 1 : csp = Used to store the data.
376 : * 2 : fuzz_input_file = File to read the input from.
377 : *
378 : * Returns : Result of fuzzed function
379 : *
380 : *********************************************************************/
381 0 : static int fuzz_gif(struct client_state *csp, char *fuzz_input_file)
382 : {
383 : char *deanimated_gif;
384 :
385 0 : if (6 < csp->iob->size)
386 : {
387 : /* Why yes of course, officer, this is a gif. */
388 0 : memcpy(csp->iob->cur, "GIF87a", 6);
389 : }
390 :
391 : /* Using the last image requires parsing of all images */
392 0 : csp->action->string[ACTION_STRING_DEANIMATE] = "last";
393 0 : deanimated_gif = gif_deanimate_response(csp);
394 0 : if (NULL != deanimated_gif)
395 : {
396 0 : free(deanimated_gif);
397 0 : return 0;
398 : }
399 :
400 0 : return 1;
401 :
402 : }
403 :
404 :
405 : /*********************************************************************
406 : *
407 : * Function : fuzz_gzip
408 : *
409 : * Description : Treat the fuzzed input as data to unzip
410 : *
411 : * Parameters :
412 : * 1 : csp = Used to store the data.
413 : * 2 : fuzz_input_file = File to read the input from.
414 : *
415 : * Returns : Result of fuzzed function
416 : *
417 : *********************************************************************/
418 0 : static int fuzz_gzip(struct client_state *csp, char *fuzz_input_file)
419 : {
420 0 : csp->content_type = CT_GZIP;
421 0 : return(JB_ERR_OK == decompress_iob(csp));
422 :
423 : }
424 :
425 :
426 : /*********************************************************************
427 : *
428 : * Function : fuzz_socks
429 : *
430 : * Description : Treat the fuzzed input as a socks response.
431 : * XXX: This is pretty useless as parsing socks response
432 : * is trivial.
433 : *
434 : * Parameters :
435 : * 1 : csp = Used to store the data.
436 : * 2 : fuzz_input_file = File to read the input from.
437 : *
438 : * Returns : Result of fuzzed function
439 : *
440 : *********************************************************************/
441 0 : static int fuzz_socks(struct client_state *csp, char *fuzz_input_file)
442 : {
443 0 : return(JB_ERR_OK == socks_fuzz(csp));
444 : }
445 :
446 :
447 : /*********************************************************************
448 : *
449 : * Function : fuzz_pcrs_substitute
450 : *
451 : * Description : Treat the fuzzed input as a pcrs substitute.
452 : *
453 : * Parameters :
454 : * 1 : csp = Used to store the data.
455 : * 2 : fuzz_input_file = File to read the input from.
456 : *
457 : * Returns : Result of fuzzed function
458 : *
459 : *********************************************************************/
460 0 : static int fuzz_pcrs_substitute(struct client_state *csp, char *fuzz_input_file)
461 : {
462 : static pcrs_substitute *result;
463 : int err;
464 :
465 0 : remove_forbidden_bytes(csp);
466 0 : result = pcrs_compile_fuzzed_replacement(csp->iob->cur, &err);
467 0 : if (NULL == result)
468 : {
469 0 : log_error(LOG_LEVEL_ERROR,
470 : "Failed to compile pcrs replacement. Error: %s", pcrs_strerror(err));
471 0 : return 1;
472 : }
473 0 : log_error(LOG_LEVEL_INFO, "%s", pcrs_strerror(err));
474 0 : free(result->text);
475 0 : freez(result);
476 0 : return 0;
477 : }
478 :
479 :
480 : /*********************************************************************
481 : *
482 : * Function : fuzz_server_header
483 : *
484 : * Description : Treat the fuzzed input as a server header.
485 : *
486 : * Parameters :
487 : * 1 : csp = Used to store the data.
488 : * 2 : fuzz_input_file = File to read the input from.
489 : *
490 : * Returns : Result of fuzzed function
491 : *
492 : *********************************************************************/
493 0 : int fuzz_server_header(struct client_state *csp, char *fuzz_input_file)
494 : {
495 : char *header;
496 :
497 0 : header = get_header(csp->iob);
498 :
499 0 : if (NULL == header)
500 : {
501 0 : return 1;
502 : }
503 0 : if (JB_ERR_OK != enlist(csp->headers, header))
504 : {
505 0 : return 1;
506 : }
507 :
508 : /* Adding headers doesn't depend on the fuzzed input */
509 0 : csp->flags |= CSP_FLAG_CLIENT_HEADER_PARSING_DONE;
510 0 : csp->flags |= CSP_FLAG_SERVER_CONNECTION_HEADER_SET;
511 :
512 : /* +overwrite-last-modified{randomize} */
513 0 : csp->action->flags |= ACTION_OVERWRITE_LAST_MODIFIED;
514 0 : csp->action->string[ACTION_STRING_LAST_MODIFIED] = "randomize";
515 :
516 : /* +limit-cookie-lifetime{60} */
517 0 : csp->action->flags |= ACTION_LIMIT_COOKIE_LIFETIME;
518 0 : csp->action->string[ACTION_STRING_LIMIT_COOKIE_LIFETIME] = "60";
519 :
520 : /* XXX: Enable more actions. */
521 :
522 0 : return(sed(csp, FILTER_SERVER_HEADERS));
523 : }
524 :
525 : /*********************************************************************
526 : *
527 : * Function : process_fuzzed_input
528 : *
529 : * Description : Process the fuzzed input in a specified file treating
530 : * it like the input type specified.
531 : *
532 : * Parameters :
533 : * 1 : fuzz_input_type = Type of input.
534 : * 2 : fuzz_input_file = File to read the input from.
535 : *
536 : * Returns : Return value of the fuzzed function
537 : *
538 : *********************************************************************/
539 3098 : int process_fuzzed_input(char *fuzz_input_type, char *fuzz_input_file)
540 : {
541 :
542 : static struct client_state csp_stack_storage;
543 : struct client_state *csp;
544 : int i;
545 3098 : csp = &csp_stack_storage;
546 3098 : csp->config = load_config();
547 3098 : run_loader(csp);
548 3098 : csp->config->buffer_limit = 4096 * 1024;
549 3098 : csp->config->receive_buffer_size = 4096;
550 :
551 : /* In --stfu mode, these will be ignored ... */
552 3098 : set_debug_level(LOG_LEVEL_ACTIONS|LOG_LEVEL_CONNECT|LOG_LEVEL_DEANIMATE|LOG_LEVEL_INFO|LOG_LEVEL_ERROR|LOG_LEVEL_RE_FILTER|LOG_LEVEL_HEADER|LOG_LEVEL_WRITING|LOG_LEVEL_RECEIVED);
553 :
554 3098 : csp->flags |= CSP_FLAG_FUZZED_INPUT;
555 3098 : csp->flags |= CSP_FLAG_TOGGLED_ON;
556 3098 : csp->config->feature_flags |= RUNTIME_FEATURE_ACCEPT_INTERCEPTED_REQUESTS;
557 3098 : csp->flags |= CSP_FLAG_ACTIVE;
558 3098 : csp->server_connection.sfd = JB_INVALID_SOCKET;
559 :
560 :
561 : #ifdef FEATURE_CLIENT_TAGS
562 3098 : csp->config->trust_x_forwarded_for = 1;
563 : #endif
564 3098 : initialize_reusable_connections();
565 3098 : cgi_init_error_messages();
566 : __AFL_INIT();
567 : {
568 40274 : for (i = 0; i < SZ(fuzz_modes); i++)
569 : {
570 37176 : if (strcmp(fuzz_modes[i].name, fuzz_input_type) == 0)
571 : {
572 3098 : if (fuzz_modes[i].stdin_support &&
573 3098 : (strcmp(fuzz_input_type, "client-request") != 0) &&
574 0 : (strcmp(fuzz_input_type, "server-response") != 0) &&
575 0 : (strcmp(fuzz_input_type, "socks") != 0))
576 : {
577 0 : load_fuzz_input(csp, fuzz_input_file);
578 : }
579 3098 : fuzz_modes[i].handler(csp, fuzz_input_file);
580 : }
581 : }
582 : }
583 :
584 3098 : return 1;
585 :
586 : }
587 :
588 : /*********************************************************************
589 : *
590 : * Function : show_fuzz_usage
591 : *
592 : * Description : Shows the --fuzz usage. D'oh.
593 : *
594 : * Parameters : Pointer to argv[0] for identifying ourselves
595 : *
596 : * Returns : void
597 : *
598 : *********************************************************************/
599 0 : void show_fuzz_usage(const char *name)
600 : {
601 : int i;
602 :
603 0 : printf("%s%s --fuzz fuzz-mode ./path/to/fuzzed/input [--stfu]\n\n",
604 : " ", name);
605 :
606 0 : printf("Supported fuzz modes and the expected input:\n");
607 0 : for (i = 0; i < SZ(fuzz_modes); i++)
608 : {
609 0 : printf(" %s: %s\n", fuzz_modes[i].name, fuzz_modes[i].expected_input);
610 : }
611 0 : printf("\n");
612 :
613 0 : printf("The following fuzz modes read data from stdin if the 'file' is '-'\n");
614 0 : for (i = 0; i < SZ(fuzz_modes); i++)
615 : {
616 0 : if (fuzz_modes[i].stdin_support)
617 : {
618 0 : printf(" %s\n", fuzz_modes[i].name);
619 : }
620 : }
621 0 : printf("\n");
622 0 : }
623 : #endif
|