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