cgi_show_status: Don't leak memory when no filter files are specified
[privoxy.git] / fuzz.c
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 static jb_err load_fuzz_input_from_stdin(struct client_state *csp)
102 {
103    static char buf[BUFFER_SIZE];
104    int ret;
105
106    while (0 < (ret = read_socket(0, buf, sizeof(buf))))
107    {
108       log_error(LOG_LEVEL_INFO,
109          "Got %d bytes from stdin: %E. They look like this: %N",
110          ret, ret, buf);
111
112       if (add_to_iob(csp->iob, csp->config->buffer_limit, buf, ret))
113       {
114          log_error(LOG_LEVEL_FATAL, "Failed to buffer them.");
115       }
116    }
117
118    log_error(LOG_LEVEL_INFO, "Read %d bytes from stdin",
119       csp->iob->eod -csp->iob->cur);
120
121    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 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    fp = fopen(filename, "rb");
144    if (NULL == fp)
145    {
146       log_error(LOG_LEVEL_FATAL, "Failed to open %s: %E", filename);
147    }
148
149    /* Get file length */
150    if (fseek(fp, 0, SEEK_END))
151    {
152       log_error(LOG_LEVEL_FATAL,
153          "Unexpected error while fseek()ing to the end of %s: %E",
154          filename);
155    }
156    ret = ftell(fp);
157    if (-1 == ret)
158    {
159       log_error(LOG_LEVEL_FATAL,
160          "Unexpected ftell() error while loading %s: %E",
161          filename);
162    }
163    length = (size_t)ret;
164
165    /* Go back to the beginning. */
166    if (fseek(fp, 0, SEEK_SET))
167    {
168       log_error(LOG_LEVEL_FATAL,
169          "Unexpected error while fseek()ing to the beginning of %s: %E",
170          filename);
171    }
172
173    csp->iob->size = length + 1;
174
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;
178
179    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       log_error(LOG_LEVEL_FATAL,
187          "Couldn't completely read file %s.", filename);
188    }
189    *csp->iob->eod = '\0';
190
191    fclose(fp);
192
193    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 jb_err load_fuzz_input(struct client_state *csp, const char *filename)
211 {
212    if (strcmp(filename, "-") == 0)
213    {
214       return load_fuzz_input_from_stdin(csp);
215    }
216
217    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 static void remove_forbidden_bytes(struct client_state *csp)
235 {
236    char *p = csp->iob->cur;
237    char first_valid_byte = ' ';
238
239    while (p < csp->iob->eod)
240    {
241       if (*p != '\0')
242       {
243          first_valid_byte = *p;
244          break;
245       }
246       p++;
247    }
248
249    p = csp->iob->cur;
250    while (p < csp->iob->eod)
251    {
252       if (*p == '\0')
253       {
254          *p = first_valid_byte;
255       }
256       p++;
257    }
258 }
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 int fuzz_action(struct client_state *csp, char *fuzz_input_file)
275 {
276    csp->config->actions_file[0] = fuzz_input_file;
277
278    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 int fuzz_client_header(struct client_state *csp, char *fuzz_input_file)
296 {
297    char *header;
298
299    header = get_header(csp->iob);
300
301    if (NULL == header)
302    {
303       return 1;
304    }
305    if (JB_ERR_OK != enlist(csp->headers, header))
306    {
307       return 1;
308    }
309
310    /*
311     * Silence an insightful client_host_adder() warning
312     * about ignored weirdness.
313     */
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;
317
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";
321
322    /* XXX: Enable more actions. */
323
324    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 int fuzz_filter(struct client_state *csp, char *fuzz_input_file)
342 {
343    csp->config->re_filterfile[0] = fuzz_input_file;
344    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 static int fuzz_deflate(struct client_state *csp, char *fuzz_input_file)
362 {
363    csp->content_type = CT_DEFLATE;
364    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 static int fuzz_gif(struct client_state *csp, char *fuzz_input_file)
382 {
383    char *deanimated_gif;
384
385    if (6 < csp->iob->size)
386    {
387       /* Why yes of course, officer, this is a gif. */
388       memcpy(csp->iob->cur, "GIF87a", 6);
389    }
390
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)
395    {
396       free(deanimated_gif);
397       return 0;
398    }
399
400    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 static int fuzz_gzip(struct client_state *csp, char *fuzz_input_file)
419 {
420    csp->content_type = CT_GZIP;
421
422    return(JB_ERR_OK == decompress_iob(csp));
423
424 }
425
426
427 /*********************************************************************
428  *
429  * Function    :  fuzz_socks
430  *
431  * Description :  Treat the fuzzed input as a socks response.
432  *                XXX: This is pretty useless as parsing socks response
433  *                     is trivial.
434  *
435  * Parameters  :
436  *          1  :  csp      = Used to store the data.
437  *          2  :  fuzz_input_file = File to read the input from.
438  *
439  * Returns     : Result of fuzzed function
440  *
441  *********************************************************************/
442 static int fuzz_socks(struct client_state *csp, char *fuzz_input_file)
443 {
444    return(JB_ERR_OK == socks_fuzz(csp));
445 }
446
447
448 /*********************************************************************
449  *
450  * Function    :  fuzz_pcrs_substitute
451  *
452  * Description :  Treat the fuzzed input as a pcrs substitute.
453  *
454  * Parameters  :
455  *          1  :  csp      = Used to store the data.
456  *          2  :  fuzz_input_file = File to read the input from.
457  *
458  * Returns     : Result of fuzzed function
459  *
460  *********************************************************************/
461 static int fuzz_pcrs_substitute(struct client_state *csp, char *fuzz_input_file)
462 {
463    static pcrs_substitute *result;
464    int err;
465
466    remove_forbidden_bytes(csp);
467    result = pcrs_compile_fuzzed_replacement(csp->iob->cur, &err);
468    if (NULL == result)
469    {
470       log_error(LOG_LEVEL_ERROR,
471          "Failed to compile pcrs replacement. Error: %s", pcrs_strerror(err));
472       return 1;
473    }
474    log_error(LOG_LEVEL_INFO, "%s", pcrs_strerror(err));
475    free(result->text);
476    freez(result);
477    return 0;
478 }
479
480
481 /*********************************************************************
482  *
483  * Function    :  fuzz_server_header
484  *
485  * Description :  Treat the fuzzed input as a server header.
486  *
487  * Parameters  :
488  *          1  :  csp      = Used to store the data.
489  *          2  :  fuzz_input_file = File to read the input from.
490  *
491  * Returns     : Result of fuzzed function
492  *
493  *********************************************************************/
494 int fuzz_server_header(struct client_state *csp, char *fuzz_input_file)
495 {
496    char *header;
497
498    header = get_header(csp->iob);
499
500    if (NULL == header)
501    {
502       return 1;
503    }
504    if (JB_ERR_OK != enlist(csp->headers, header))
505    {
506       return 1;
507    }
508
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;
512
513    /* +overwrite-last-modified{randomize} */
514    csp->action->flags |= ACTION_OVERWRITE_LAST_MODIFIED;
515    csp->action->string[ACTION_STRING_LAST_MODIFIED] = "randomize";
516
517    /* +limit-cookie-lifetime{60} */
518    csp->action->flags |= ACTION_LIMIT_COOKIE_LIFETIME;
519    csp->action->string[ACTION_STRING_LIMIT_COOKIE_LIFETIME] = "60";
520
521    /* XXX: Enable more actions. */
522
523    return(sed(csp, FILTER_SERVER_HEADERS));
524 }
525
526 /*********************************************************************
527  *
528  * Function    :  process_fuzzed_input
529  *
530  * Description :  Process the fuzzed input in a specified file treating
531  *                it like the input type specified.
532  *
533  * Parameters  :
534  *          1  :  fuzz_input_type = Type of input.
535  *          2  :  fuzz_input_file = File to read the input from.
536  *
537  * Returns     : Return value of the fuzzed function
538  *
539  *********************************************************************/
540 int process_fuzzed_input(char *fuzz_input_type, char *fuzz_input_file)
541 {
542    static struct client_state csp_stack_storage;
543    static struct configuration_spec config_stack_storage;
544    struct client_state *csp;
545    int i;
546
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;
551
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);
554
555    csp->flags |= CSP_FLAG_FUZZED_INPUT;
556    csp->config->feature_flags |= RUNTIME_FEATURE_ACCEPT_INTERCEPTED_REQUESTS;
557
558 #ifdef FEATURE_CLIENT_TAGS
559    csp->config->trust_x_forwarded_for = 1;
560 #endif
561
562    for (i = 0; i < SZ(fuzz_modes); i++)
563    {
564       if (strcmp(fuzz_modes[i].name, fuzz_input_type) == 0)
565       {
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))
570          {
571             load_fuzz_input(csp, fuzz_input_file);
572          }
573          return (fuzz_modes[i].handler(csp, fuzz_input_file));
574       }
575    }
576
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);
580
581    /* Not reached. */
582    return 1;
583
584 }
585
586
587 /*********************************************************************
588  *
589  * Function    :  show_fuzz_usage
590  *
591  * Description :  Shows the --fuzz usage. D'oh.
592  *
593  * Parameters  :  Pointer to argv[0] for identifying ourselves
594  *
595  * Returns     :  void
596  *
597  *********************************************************************/
598 void show_fuzz_usage(const char *name)
599 {
600    int i;
601
602    printf("%s%s --fuzz fuzz-mode ./path/to/fuzzed/input [--stfu]\n\n",
603       "       ", name);
604
605    printf("Supported fuzz modes and the expected input:\n");
606    for (i = 0; i < SZ(fuzz_modes); i++)
607    {
608       printf(" %s: %s\n", fuzz_modes[i].name, fuzz_modes[i].expected_input);
609    }
610    printf("\n");
611
612    printf("The following fuzz modes read data from stdin if the 'file' is '-'\n");
613    for (i = 0; i < SZ(fuzz_modes); i++)
614    {
615       if (fuzz_modes[i].stdin_support)
616       {
617          printf(" %s\n", fuzz_modes[i].name);
618       }
619    }
620    printf("\n");
621 }
622 #endif