1 /*********************************************************************
3 * File : $Source: /cvsroot/ijbswa/current/fuzz.c,v $
5 * Purpose : Fuzz-related functions for Privoxy.
7 * Copyright : Written by and Copyright (C) 2014-16 by
8 * Fabian Keil <fk@fabiankeil.de>
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.
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.
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.
28 *********************************************************************/
35 #include <sys/types.h>
49 #include "jbsockets.h"
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);
69 const char *expected_input;
70 const int stdin_support;
71 int (* const handler)(struct client_state *csp, char *input_file);
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 },
89 /*********************************************************************
91 * Function : load_fuzz_input_from_stdin
93 * Description : Loads stdin into a buffer.
96 * 1 : csp = Used to store the data.
98 * Returns : JB_ERR_OK in case of success,
100 *********************************************************************/
101 static jb_err load_fuzz_input_from_stdin(struct client_state *csp)
103 static char buf[BUFFER_SIZE];
106 while (0 < (ret = read_socket(0, buf, sizeof(buf))))
108 log_error(LOG_LEVEL_INFO,
109 "Got %d bytes from stdin: %E. They look like this: %N",
112 if (add_to_iob(csp->iob, csp->config->buffer_limit, buf, ret))
114 log_error(LOG_LEVEL_FATAL, "Failed to buffer them.");
118 log_error(LOG_LEVEL_INFO, "Read %d bytes from stdin",
119 csp->iob->eod -csp->iob->cur);
124 /*********************************************************************
126 * Function : load_fuzz_input_from_file
128 * Description : Loads file content into a buffer.
131 * 1 : csp = Used to store the data.
132 * 2 : filename = Name of the file to be loaded.
134 * Returns : JB_ERR_OK in case of success,
136 *********************************************************************/
137 static jb_err load_fuzz_input_from_file(struct client_state *csp, const char *filename)
143 fp = fopen(filename, "rb");
146 log_error(LOG_LEVEL_FATAL, "Failed to open %s: %E", filename);
149 /* Get file length */
150 if (fseek(fp, 0, SEEK_END))
152 log_error(LOG_LEVEL_FATAL,
153 "Unexpected error while fseek()ing to the end of %s: %E",
159 log_error(LOG_LEVEL_FATAL,
160 "Unexpected ftell() error while loading %s: %E",
163 length = (size_t)ret;
165 /* Go back to the beginning. */
166 if (fseek(fp, 0, SEEK_SET))
168 log_error(LOG_LEVEL_FATAL,
169 "Unexpected error while fseek()ing to the beginning of %s: %E",
173 csp->iob->size = length + 1;
175 csp->iob->buf = malloc_or_die(csp->iob->size);
176 csp->iob->cur = csp->iob->buf;
177 csp->iob->eod = csp->iob->buf + length;
179 if (1 != fread(csp->iob->cur, length, 1, fp))
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.
186 log_error(LOG_LEVEL_FATAL,
187 "Couldn't completely read file %s.", filename);
189 *csp->iob->eod = '\0';
197 /*********************************************************************
199 * Function : load_fuzz_input
201 * Description : Loads a file into a buffer. XXX: Reverse argument order
204 * 1 : csp = Used to store the data.
205 * 2 : filename = Name of the file to be loaded.
207 * Returns : JB_ERR_OK in case of success,
209 *********************************************************************/
210 jb_err load_fuzz_input(struct client_state *csp, const char *filename)
212 if (strcmp(filename, "-") == 0)
214 return load_fuzz_input_from_stdin(csp);
217 return load_fuzz_input_from_file(csp, filename);
221 /*********************************************************************
223 * Function : remove_forbidden_bytes
225 * Description : Sanitizes fuzzed data to decrease the likelihood of
226 * premature parse abortions.
229 * 1 : csp = Used to store the data.
233 *********************************************************************/
234 static void remove_forbidden_bytes(struct client_state *csp)
236 char *p = csp->iob->cur;
237 char first_valid_byte = ' ';
239 while (p < csp->iob->eod)
243 first_valid_byte = *p;
250 while (p < csp->iob->eod)
254 *p = first_valid_byte;
261 /*********************************************************************
263 * Function : fuzz_action
265 * Description : Treat the fuzzed input as action file.
268 * 1 : csp = Used to store the data.
269 * 2 : fuzz_input_file = File to read the input from.
271 * Returns : Result of fuzzed function
273 *********************************************************************/
274 int fuzz_action(struct client_state *csp, char *fuzz_input_file)
276 csp->config->actions_file[0] = fuzz_input_file;
278 return(load_action_files(csp));
282 /*********************************************************************
284 * Function : fuzz_client_header
286 * Description : Treat the fuzzed input as a client header.
289 * 1 : csp = Used to store the data.
290 * 2 : fuzz_input_file = File to read the input from.
292 * Returns : Result of fuzzed function
294 *********************************************************************/
295 int fuzz_client_header(struct client_state *csp, char *fuzz_input_file)
299 header = get_header(csp->iob);
305 if (JB_ERR_OK != enlist(csp->headers, header))
311 * Silence an insightful client_host_adder() warning
312 * about ignored weirdness.
314 csp->flags |= CSP_FLAG_HOST_HEADER_IS_SET;
315 /* Adding headers doesn't depend on the fuzzed input */
316 csp->flags |= CSP_FLAG_CLIENT_CONNECTION_HEADER_SET;
318 /* +hide-if-modified-since{+60} */
319 csp->action->flags |= ACTION_HIDE_IF_MODIFIED_SINCE;
320 csp->action->string[ACTION_STRING_IF_MODIFIED_SINCE] = "+60";
322 /* XXX: Enable more actions. */
324 return(sed(csp, FILTER_CLIENT_HEADERS));
328 /*********************************************************************
330 * Function : fuzz_filter
332 * Description : Treat the fuzzed input as filter file.
335 * 1 : csp = Used to store the data.
336 * 2 : fuzz_input_file = File to read the input from.
338 * Returns : Result of fuzzed function
340 *********************************************************************/
341 int fuzz_filter(struct client_state *csp, char *fuzz_input_file)
343 csp->config->re_filterfile[0] = fuzz_input_file;
344 return (load_one_re_filterfile(csp, 0));
348 /*********************************************************************
350 * Function : fuzz_deflate
352 * Description : Treat the fuzzed input as data to deflate.
355 * 1 : csp = Used to store the data.
356 * 2 : fuzz_input_file = File to read the input from.
358 * Returns : Result of fuzzed function
360 *********************************************************************/
361 static int fuzz_deflate(struct client_state *csp, char *fuzz_input_file)
363 csp->content_type = CT_DEFLATE;
364 return(JB_ERR_OK == decompress_iob(csp));
368 /*********************************************************************
370 * Function : fuzz_gif
372 * Description : Treat the fuzzed input as a gif to deanimate.
375 * 1 : csp = Used to store the data.
376 * 2 : fuzz_input_file = File to read the input from.
378 * Returns : Result of fuzzed function
380 *********************************************************************/
381 static int fuzz_gif(struct client_state *csp, char *fuzz_input_file)
383 char *deanimated_gif;
385 if (6 < csp->iob->size)
387 /* Why yes of course, officer, this is a gif. */
388 memcpy(csp->iob->cur, "GIF87a", 6);
391 /* Using the last image requires parsing of all images */
392 csp->action->string[ACTION_STRING_DEANIMATE] = "last";
393 deanimated_gif = gif_deanimate_response(csp);
394 if (NULL != deanimated_gif)
396 free(deanimated_gif);
405 /*********************************************************************
407 * Function : fuzz_gzip
409 * Description : Treat the fuzzed input as data to unzip
412 * 1 : csp = Used to store the data.
413 * 2 : fuzz_input_file = File to read the input from.
415 * Returns : Result of fuzzed function
417 *********************************************************************/
418 static int fuzz_gzip(struct client_state *csp, char *fuzz_input_file)
420 csp->content_type = CT_GZIP;
422 return(JB_ERR_OK == decompress_iob(csp));
427 /*********************************************************************
429 * Function : fuzz_socks
431 * Description : Treat the fuzzed input as a socks response.
432 * XXX: This is pretty useless as parsing socks repsonse
436 * 1 : csp = Used to store the data.
437 * 2 : fuzz_input_file = File to read the input from.
439 * Returns : Result of fuzzed function
441 *********************************************************************/
442 static int fuzz_socks(struct client_state *csp, char *fuzz_input_file)
444 return(JB_ERR_OK == socks_fuzz(csp));
448 /*********************************************************************
450 * Function : fuzz_pcrs_substitute
452 * Description : Treat the fuzzed input as a pcrs substitute.
455 * 1 : csp = Used to store the data.
456 * 2 : fuzz_input_file = File to read the input from.
458 * Returns : Result of fuzzed function
460 *********************************************************************/
461 static int fuzz_pcrs_substitute(struct client_state *csp, char *fuzz_input_file)
463 static pcrs_substitute *result;
466 remove_forbidden_bytes(csp);
467 result = pcrs_compile_fuzzed_replacement(csp->iob->cur, &err);
470 log_error(LOG_LEVEL_ERROR,
471 "Failed to compile pcrs replacement. Error: %s", pcrs_strerror(err));
474 log_error(LOG_LEVEL_INFO, "%s", pcrs_strerror(err));
481 /*********************************************************************
483 * Function : fuzz_server_header
485 * Description : Treat the fuzzed input as a server header.
488 * 1 : csp = Used to store the data.
489 * 2 : fuzz_input_file = File to read the input from.
491 * Returns : Result of fuzzed function
493 *********************************************************************/
494 int fuzz_server_header(struct client_state *csp, char *fuzz_input_file)
498 header = get_header(csp->iob);
504 if (JB_ERR_OK != enlist(csp->headers, header))
509 /* Adding headers doesn't depend on the fuzzed input */
510 csp->flags |= CSP_FLAG_CLIENT_HEADER_PARSING_DONE;
511 csp->flags |= CSP_FLAG_SERVER_CONNECTION_HEADER_SET;
513 /* +overwrite-last-modified{randomize} */
514 csp->action->flags |= ACTION_OVERWRITE_LAST_MODIFIED;
515 csp->action->string[ACTION_STRING_LAST_MODIFIED] = "randomize";
517 /* +limit-cookie-lifetime{60} */
518 csp->action->flags |= ACTION_LIMIT_COOKIE_LIFETIME;
519 csp->action->string[ACTION_STRING_LIMIT_COOKIE_LIFETIME] = "60";
521 /* XXX: Enable more actions. */
523 return(sed(csp, FILTER_SERVER_HEADERS));
526 /*********************************************************************
528 * Function : process_fuzzed_input
530 * Description : Process the fuzzed input in a specified file treating
531 * it like the input type specified.
534 * 1 : fuzz_input_type = Type of input.
535 * 2 : fuzz_input_file = File to read the input from.
537 * Returns : Return value of the fuzzed function
539 *********************************************************************/
540 int process_fuzzed_input(char *fuzz_input_type, char *fuzz_input_file)
542 static struct client_state csp_stack_storage;
543 static struct configuration_spec config_stack_storage;
544 struct client_state *csp;
547 csp = &csp_stack_storage;
548 csp->config = &config_stack_storage;
549 csp->config->buffer_limit = 4096 * 1024;
550 csp->config->receive_buffer_size = 4096;
552 /* In --stfu mode, these will be ignored ... */
553 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);
555 csp->flags |= CSP_FLAG_FUZZED_INPUT;
556 csp->config->feature_flags |= RUNTIME_FEATURE_ACCEPT_INTERCEPTED_REQUESTS;
558 #ifdef FEATURE_CLIENT_TAGS
559 csp->config->trust_x_forwarded_for = 1;
562 for (i = 0; i < SZ(fuzz_modes); i++)
564 if (strcmp(fuzz_modes[i].name, fuzz_input_type) == 0)
566 if (fuzz_modes[i].stdin_support &&
567 (strcmp(fuzz_input_type, "client-request") != 0) &&
568 (strcmp(fuzz_input_type, "server-response") != 0) &&
569 (strcmp(fuzz_input_type, "socks") != 0))
571 load_fuzz_input(csp, fuzz_input_file);
573 return (fuzz_modes[i].handler(csp, fuzz_input_file));
577 log_error(LOG_LEVEL_FATAL,
578 "Unrecognized fuzz type %s for input file %s. You may need --help.",
579 fuzz_input_type, fuzz_input_file);
587 /*********************************************************************
589 * Function : show_fuzz_usage
591 * Description : Shows the --fuzz usage. D'oh.
593 * Parameters : Pointer to argv[0] for identifying ourselves
597 *********************************************************************/
598 void show_fuzz_usage(const char *name)
602 printf("%s%s --fuzz fuzz-mode ./path/to/fuzzed/input [--stfu]\n\n",
605 printf("Supported fuzz modes and the expected input:\n");
606 for (i = 0; i < SZ(fuzz_modes); i++)
608 printf(" %s: %s\n", fuzz_modes[i].name, fuzz_modes[i].expected_input);
612 printf("The following fuzz modes read data from stdin if the 'file' is '-'\n");
613 for (i = 0; i < SZ(fuzz_modes); i++)
615 if (fuzz_modes[i].stdin_support)
617 printf(" %s\n", fuzz_modes[i].name);