X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=filters.c;h=1a1da409823f7a6ed0df34740e5f3da4222b71b4;hp=cff05c4a9bf24d3dc8aacc0531c4b31721aba4e9;hb=7537bf8433682b12ccf1099e1e30c978f4bd52a2;hpb=91f8d1cfa59b145296b7b5dc4ee07bdb32517b8b diff --git a/filters.c b/filters.c index cff05c4a..1a1da409 100644 --- a/filters.c +++ b/filters.c @@ -1,4 +1,4 @@ -const char filters_rcs[] = "$Id: filters.c,v 1.53 2002/03/26 22:29:54 swa Exp $"; +const char filters_rcs[] = "$Id: filters.c,v 1.63 2006/08/31 10:11:28 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/filters.c,v $ @@ -9,9 +9,10 @@ const char filters_rcs[] = "$Id: filters.c,v 1.53 2002/03/26 22:29:54 swa Exp $" * `block_url', `url_actions', `domain_split', * `filter_popups', `forward_url', 'redirect_url', * `ij_untrusted_url', `intercept_url', `pcrs_filter_respose', - * 'ijb_send_banner', and `trust_url' + * `ijb_send_banner', `trust_url', `gif_deanimate_response', + * `jpeg_inspect_response' * - * Copyright : Written by and Copyright (C) 2001 the SourceForge + * Copyright : Written by and Copyright (C) 2001, 2004 the SourceForge * Privoxy team. http://www.privoxy.org/ * * Based on the Internet Junkbuster originally written @@ -38,6 +39,76 @@ const char filters_rcs[] = "$Id: filters.c,v 1.53 2002/03/26 22:29:54 swa Exp $" * * Revisions : * $Log: filters.c,v $ + * Revision 1.63 2006/08/31 10:11:28 fabiankeil + * Don't free p which is still in use and will be later + * freed by free_map(). Don't claim the referrer is unknown + * when the client didn't set one. + * + * Revision 1.62 2006/08/14 00:27:47 david__schmidt + * Feature request 595948: Re-Filter logging in single line + * + * Revision 1.61 2006/08/03 02:46:41 david__schmidt + * Incorporate Fabian Keil's patch work: http://www.fabiankeil.de/sourcecode/privoxy/ + * + * Revision 1.60 2006/07/18 14:48:46 david__schmidt + * Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch) + * with what was really the latest development (the v_3_0_branch branch) + * + * Revision 1.58.2.9 2006/01/29 23:10:56 david__schmidt + * Multiple filter file support + * + * Revision 1.58.2.8 2005/05/07 21:50:55 david__schmidt + * A few memory leaks plugged (mostly on error paths) + * + * Revision 1.58.2.7 2004/10/03 12:53:32 david__schmidt + * Add the ability to check jpeg images for invalid + * lengths of comment blocks. Defensive strategy + * against the exploit: + * Microsoft Security Bulletin MS04-028 + * Buffer Overrun in JPEG Processing (GDI+) Could + * Allow Code Execution (833987) + * Enabled with +inspect-jpegs in actions files. + * + * Revision 1.58.2.6 2003/12/06 22:18:27 gliptak + * Correcting compile problem with FEATURE_IMAGE_BLOCKING + * + * Revision 1.58.2.5 2003/11/11 13:10:31 oes + * Fixed bug #839859: "See why" link URL now gets url-encoded. + * + * Revision 1.58.2.4 2003/02/28 12:52:45 oes + * Fixed a typo + * + * Revision 1.58.2.3 2002/09/25 14:51:51 oes + * Added basic support for OPTIONS and TRACE HTTP methods: + * New function direct_response which handles OPTIONS and + * TRACE requests whose Max-Forwards header field is zero. + * + * Revision 1.58.2.2 2002/08/01 17:18:28 oes + * Fixed BR 537651 / SR 579724 (MSIE image detect improper for IE/Mac) + * + * Revision 1.58.2.1 2002/07/26 15:18:53 oes + * - Bugfix: Executing a filters without jobs no longer results in + * turing off *all* filters. + * - Security fix: Malicious web servers can't cause a seg fault + * through bogus chunk sizes anymore + * + * Revision 1.58 2002/04/24 02:11:17 oes + * Jon's multiple AF patch: url_actions now evaluates rules + * from all AFs. + * + * Revision 1.57 2002/04/08 20:38:34 swa + * fixed JB spelling + * + * Revision 1.56 2002/04/05 15:51:24 oes + * - bugfix: error-pages now get correct request protocol + * - fix for invalid HTML in trust info + * + * Revision 1.55 2002/04/02 16:13:51 oes + * Fix: No "Go there anyway" for SSL + * + * Revision 1.54 2002/04/02 14:55:56 oes + * Bugfix: is_untrusted_url() now depends on FEATURE_TRUST, not FEATURE_COOKIE_JAR + * * Revision 1.53 2002/03/26 22:29:54 swa * we have a new homepage! * @@ -360,7 +431,7 @@ const char filters_rcs[] = "$Id: filters.c,v 1.53 2002/03/26 22:29:54 swa Exp $" * a lot! ;-) * * Revision 1.3 2001/05/20 16:44:47 jongfoster - * Removing last hardcoded JunkBusters.com URLs. + * Removing last hardcoded Junkbusters.com URLs. * * Revision 1.2 2001/05/20 01:21:20 jongfoster * Version 2.9.4 checkin. @@ -664,10 +735,8 @@ int match_portlist(const char *portlist, int port) *********************************************************************/ struct http_response *block_url(struct client_state *csp) { -#ifdef FEATURE_IMAGE_BLOCKING - char *p; -#endif /* def FEATURE_IMAGE_BLOCKING */ struct http_response *rsp; + const char *new_content_type = NULL; /* * If it's not blocked, don't block it ;-) @@ -676,7 +745,10 @@ struct http_response *block_url(struct client_state *csp) { return NULL; } - + if (csp->action->flags & ACTION_REDIRECT) + { + log_error(LOG_LEVEL_ERROR, "redirect{} overruled by block."); + } /* * Else, prepare a response */ @@ -693,14 +765,25 @@ struct http_response *block_url(struct client_state *csp) if (((csp->action->flags & ACTION_IMAGE_BLOCKER) != 0) && is_imageurl(csp)) { + char *p; /* determine HOW images should be blocked */ p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER]; + if(csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT) + { + log_error(LOG_LEVEL_ERROR, "handle-as-empty-document overruled by handle-as-image."); + } #if 1 /* Two alternative strategies, use this one for now: */ /* and handle accordingly: */ if ((p == NULL) || (0 == strcmpic(p, "pattern"))) { + rsp->status = strdup("403 Request blocked by Privoxy"); + if (rsp->status == NULL) + { + free_http_response(rsp); + return cgi_error_memory(); + } rsp->body = bindup(image_pattern_data, image_pattern_length); if (rsp->body == NULL) { @@ -718,6 +801,12 @@ struct http_response *block_url(struct client_state *csp) else if (0 == strcmpic(p, "blank")) { + rsp->status = strdup("403 Request blocked by Privoxy"); + if (rsp->status == NULL) + { + free_http_response(rsp); + return cgi_error_memory(); + } rsp->body = bindup(image_blank_data, image_blank_length); if (rsp->body == NULL) { @@ -773,6 +862,34 @@ struct http_response *block_url(struct client_state *csp) return cgi_error_memory(); } #endif /* Preceeding code is disabled for now */ + } + else if(csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT) + { + /* + * Send empty document. + */ + new_content_type = csp->action->string[ACTION_STRING_CONTENT_TYPE]; + + freez(rsp->body); + rsp->body = strdup(" "); + rsp->content_length = 1; + + rsp->status = strdup("403 Request blocked by Privoxy"); + if (rsp->status == NULL) + { + free_http_response(rsp); + return cgi_error_memory(); + } + if (new_content_type != 0) + { + log_error(LOG_LEVEL_HEADER, "Overwriting Content-Type with %s", new_content_type); + if (enlist_unique_header(rsp->headers, "Content-Type", new_content_type)) + { + free_http_response(rsp); + return cgi_error_memory(); + } + } + } else #endif /* def FEATURE_IMAGE_BLOCKING */ @@ -783,6 +900,7 @@ struct http_response *block_url(struct client_state *csp) { jb_err err; struct map * exports; + char *p; /* * Workaround for stupid Netscape bug which prevents @@ -818,12 +936,16 @@ struct http_response *block_url(struct client_state *csp) #ifdef FEATURE_FORCE_LOAD err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1); -#else /* ifndef FEATURE_FORCE_LOAD */ - err = map_block_killer(exports, "force-support"); + if (csp->http->ssl != 0) #endif /* ndef FEATURE_FORCE_LOAD */ + { + err = map_block_killer(exports, "force-support"); + } + if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1); if (!err) err = map(exports, "hostport", 1, html_encode(csp->http->hostport), 0); if (!err) err = map(exports, "path", 1, html_encode(csp->http->path), 0); + if (!err) err = map(exports, "path-ue", 1, url_encode(csp->http->path), 0); if (err) { @@ -851,7 +973,7 @@ struct http_response *block_url(struct client_state *csp) * Function : trust_url FIXME: I should be called distrust_url * * Description : Calls is_untrusted_url to determine if the URL is trusted - * and if not, returns a HTTP 304 response with a reject message. + * and if not, returns a HTTP 403 response with a reject message. * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) @@ -885,17 +1007,19 @@ struct http_response *trust_url(struct client_state *csp) return cgi_error_memory(); } + rsp->status = strdup("403 Request blocked by Privoxy"); exports = default_exports(csp, NULL); - if (exports == NULL) + if (exports == NULL || rsp->status == NULL) { free_http_response(rsp); return cgi_error_memory(); } /* - * Export the host, port, and referrer information + * Export the protocol, host, port, and referrer information */ err = map(exports, "hostport", 1, csp->http->hostport, 1); + if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1); if (!err) err = map(exports, "path", 1, csp->http->path, 1); if (NULL != (p = get_header_value(csp->headers, "Referer:"))) @@ -904,7 +1028,7 @@ struct http_response *trust_url(struct client_state *csp) } else { - if (!err) err = map(exports, "referrer", 1, "unknown", 1); + if (!err) err = map(exports, "referrer", 1, "none set", 1); } if (err) @@ -942,7 +1066,7 @@ struct http_response *trust_url(struct client_state *csp) p = strdup(""); for (l = csp->config->trust_info->first; l ; l = l->next) { - sprintf(buf, "
  • %s
    \n",l->str, l->str); + sprintf(buf, "
  • %s
    \n",l->str, l->str); string_append(&p, buf); } err = map(exports, "trust-info", 1, p, 0); @@ -1008,18 +1132,55 @@ struct http_response *redirect_url(struct client_state *csp) { char *p, *q; struct http_response *rsp; + char *redirect_mode = NULL; + int x, y; - p = q = csp->http->path; - log_error(LOG_LEVEL_REDIRECTS, "checking path for redirects: %s", p); - - /* - * find the last URL encoded in the request - */ - while ((p = strstr(p, "http://")) != NULL) + if ((csp->action->flags & ACTION_REDIRECT)) { - q = p++; + q = csp->action->string[ACTION_STRING_REDIRECT]; } + else + { + redirect_mode = csp->action->string[ACTION_STRING_FAST_REDIRECTS]; + if (0 == strcmpic(redirect_mode, "check-decoded-url")) + { + p = q = csp->http->path; + log_error(LOG_LEVEL_REDIRECTS, "Decoding path: %s if necessary.", p); + while (*p) + { + if (*p == '%') /* Escape sequence? */ + { + /* Yes, translate from hexadecimal to decimal */ + p++; + /* First byte */ + x=((int)*p++)-48; + if (x>9) x-=7; + x<<=4; + /* Second byte */ + y=((int)*p++)-48; + if (y>9)y-=7; + /* Merge */ + *q++=(char)(x|y); + } + else + { + /* No, forward character. */ + *q++=*p++; + } + } + *q='\0'; + } + p = q = csp->http->path; + log_error(LOG_LEVEL_REDIRECTS, "Checking path for redirects: %s", p); + /* + * find the last URL encoded in the request + */ + while ((p = strstr(p, "http://")) != NULL) + { + q = p++; + } + } /* * if there was any, generate and return a HTTP redirect */ @@ -1057,8 +1218,9 @@ struct http_response *redirect_url(struct client_state *csp) * * Description : Given a URL, decide whether it is an image or not, * using either the info from a previous +image action - * or, #ifdef FEATURE_IMAGE_DETECT_MSIE, the info from - * the browser's accept header. + * or, #ifdef FEATURE_IMAGE_DETECT_MSIE, and the browser + * is MSIE and not on a Mac, tell from the browser's accept + * header. * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) @@ -1073,7 +1235,7 @@ int is_imageurl(struct client_state *csp) char *tmp; tmp = get_header_value(csp->headers, "User-Agent:"); - if (tmp && strstr(tmp, "MSIE")) + if (tmp && strstr(tmp, "MSIE") && !strstr(tmp, "Mac_")) { tmp = get_header_value(csp->headers, "Accept:"); if (tmp && strstr(tmp, "image/gif")) @@ -1222,7 +1384,7 @@ int is_untrusted_url(struct client_state *csp) * * Function : pcrs_filter_response * - * Description : Ecexute all text substitutions from all applying + * Description : Execute all text substitutions from all applying * +filter actions on the text buffer that's been accumulated * in csp->iob->buf. If this changes the contents, set * csp->content_length to the modified size and raise the @@ -1238,7 +1400,7 @@ int is_untrusted_url(struct client_state *csp) char *pcrs_filter_response(struct client_state *csp) { int hits=0; - size_t size; + size_t size, prev_size; char *old = csp->iob->cur, *new = NULL; pcrs_job *job; @@ -1247,6 +1409,8 @@ char *pcrs_filter_response(struct client_state *csp) struct re_filterfile_spec *b; struct list_entry *filtername; + int i, found_filters = 0; + /* * Sanity first */ @@ -1256,10 +1420,26 @@ char *pcrs_filter_response(struct client_state *csp) } size = csp->iob->eod - csp->iob->cur; - if ( ( NULL == (fl = csp->rlist) ) || ( NULL == fl->f) ) + /* + * Need to check the set of re_filterfiles... + */ + for (i = 0; i < MAX_AF_FILES; i++) + { + fl = csp->rlist[i]; + if (NULL != fl) + { + if (NULL != fl->f) + { + found_filters = 1; + break; + } + } + } + + if (0 == found_filters) { log_error(LOG_LEVEL_ERROR, "Unable to get current state of regexp filtering."); - return(NULL); + return(NULL); } /* @@ -1277,6 +1457,11 @@ char *pcrs_filter_response(struct client_state *csp) csp->flags |= CSP_FLAG_MODIFIED; } + for (i = 0; i < MAX_AF_FILES; i++) + { + fl = csp->rlist[i]; + if ((NULL == fl) || (NULL == fl->f)) + break; /* * For all applying +filter actions, look if a filter by that * name exists and if yes, execute it's pcrs_joblist on the @@ -1294,12 +1479,10 @@ char *pcrs_filter_response(struct client_state *csp) if ( NULL == b->joblist ) { log_error(LOG_LEVEL_RE_FILTER, "Filter %s has empty joblist. Nothing to do.", b->name); - return(NULL); + continue; } - log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s%s (size %d) with filter %s...", - csp->http->hostport, csp->http->path, size, b->name); - + prev_size = size; /* Apply all jobs from the joblist */ for (job = b->joblist; NULL != job; job = job->next) { @@ -1308,11 +1491,14 @@ char *pcrs_filter_response(struct client_state *csp) old=new; } - log_error(LOG_LEVEL_RE_FILTER, " ...produced %d hits (new size %d).", current_hits, size); + log_error(LOG_LEVEL_RE_FILTER, "re_filtering %s%s (size %d) with filter %s produced %d hits (new size %d).", + csp->http->hostport, csp->http->path, prev_size, b->name, current_hits, size); + hits += current_hits; } } } + } /* * If there were no hits, destroy our copy and let @@ -1407,6 +1593,80 @@ char *gif_deanimate_response(struct client_state *csp) } +/********************************************************************* + * + * Function : jpeg_inspect_response + * + * Description : + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : a pointer to the (newly allocated) modified buffer + * or NULL in case something went wrong. + * + *********************************************************************/ +char *jpeg_inspect_response(struct client_state *csp) +{ + struct binbuffer *in = NULL, *out = NULL; + char *p = NULL; + size_t size = csp->iob->eod - csp->iob->cur; + + /* + * If the body has a "chunked" transfer-encoding, + * get rid of it first, adjusting size and iob->eod + */ + if (csp->flags & CSP_FLAG_CHUNKED) + { + log_error(LOG_LEVEL_DEANIMATE, "Need to de-chunk first"); + if (0 == (size = remove_chunked_transfer_coding(csp->iob->cur, size))) + { + return(NULL); + } + csp->iob->eod = csp->iob->cur + size; + csp->flags |= CSP_FLAG_MODIFIED; + } + + if (NULL == (in = (struct binbuffer *)zalloc(sizeof *in ))) + { + log_error(LOG_LEVEL_DEANIMATE, "failed! (jpeg no mem 1)"); + return NULL; + } + + if (NULL == (out = (struct binbuffer *)zalloc(sizeof *out))) + { + log_error(LOG_LEVEL_DEANIMATE, "failed! (jpeg no mem 2)"); + return NULL; + } + + in->buffer = csp->iob->cur; + in->size = size; + + /* + * Calling jpeg_inspect has the side-effect of creating and + * modifying the image buffer of "out" directly. + */ + if (jpeg_inspect(in, out)) + { + log_error(LOG_LEVEL_DEANIMATE, "failed! (jpeg parsing)"); + free(in); + buf_free(out); + return(NULL); + + } + else + { + csp->content_length = out->offset; + csp->flags |= CSP_FLAG_MODIFIED; + p = out->buffer; + free(in); + free(out); + return(p); + } + +} + + /********************************************************************* * * Function : remove_chunked_transfer_coding @@ -1445,7 +1705,12 @@ int remove_chunked_transfer_coding(char *buffer, const size_t size) log_error(LOG_LEVEL_ERROR, "Parse error while stripping \"chunked\" transfer coding"); return(0); } - newsize += chunksize; + + if ((newsize += chunksize) >= size) + { + log_error(LOG_LEVEL_ERROR, "Chunksize exceeds buffer in \"chunked\" transfer coding"); + return(0); + } from_p += 2; memmove(to_p, from_p, (size_t) chunksize); @@ -1484,16 +1749,21 @@ void url_actions(struct http_request *http, { struct file_list *fl; struct url_actions *b; + int i; init_current_action(csp->action); - if (((fl = csp->actions_list) == NULL) || ((b = fl->f) == NULL)) + for (i = 0; i < MAX_AF_FILES; i++) { - return; - } + if (((fl = csp->actions_list[i]) == NULL) || ((b = fl->f) == NULL)) + { + return; + } - apply_url_actions(csp->action, http, b); + apply_url_actions(csp->action, http, b); + } + return; } @@ -1568,6 +1838,63 @@ const struct forward_spec * forward_url(struct http_request *http, } +/********************************************************************* + * + * Function : direct_response + * + * Description : Check if Max-Forwards == 0 for an OPTIONS or TRACE + * request and if so, return a HTTP 501 to the client. + * + * FIXME: I have a stupid name and I should handle the + * requests properly. Still, what we do here is rfc- + * compliant, whereas ignoring or forwarding are not. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : http_response if , NULL if nonmatch or handler fail + * + *********************************************************************/ +struct http_response *direct_response(struct client_state *csp) +{ + struct http_response *rsp; + struct list_entry *p; + + if ((0 == strcmpic(csp->http->gpc, "trace")) + || (0 == strcmpic(csp->http->gpc, "options"))) + { + for (p = csp->headers->first; (p != NULL) ; p = p->next) + { + if (!strncmp("Max-Forwards:", p->str, 13) + && (*(p->str+13) != '\0') && (atoi(p->str+13) == 0)) + { + /* FIXME: We could handle at least TRACE here, + but that would require a verbatim copy of + the request which we don't have anymore */ + + log_error(LOG_LEVEL_HEADER, "Found Max-Forwards:0 in OPTIONS or TRACE request -- Returning 501"); + + /* Get mem for response or fail*/ + if (NULL == (rsp = alloc_http_response())) + { + return cgi_error_memory(); + } + + if (NULL == (rsp->status = strdup("501 Not Implemented"))) + { + free_http_response(rsp); + return cgi_error_memory(); + } + + rsp->is_static = 1; + return(finish_http_response(rsp)); + } + } + } + return NULL; +} + + /* Local Variables: tab-width: 3