From 74738b29b0fb51a954ef188f230f1fdb5a224dec Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 14 Jun 2007 15:26:08 +0000 Subject: [PATCH] Added new directive (SecPdfProtectMethod) to enable the user to choose between using token redirection (falling back on forced download in some cases) and forced download (in all cases). --- apache2/apache2_config.c | 33 ++++++++++++++++++++++++++++++++- apache2/modsecurity.h | 1 + apache2/pdf_protect.c | 38 +++++++++++++++++++++++++++++++++++--- apache2/pdf_protect.h | 3 +++ 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 8c975d45..16606402 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -14,7 +14,7 @@ #include "modsecurity.h" #include "msc_logging.h" - +#include "pdf_protect.h" #include "http_log.h" /* #define DEBUG_CONF 1 */ @@ -90,6 +90,7 @@ void *create_directory_config(apr_pool_t *mp, char *path) { dcfg->pdfp_timeout = NOT_SET; dcfg->pdfp_token_name = NOT_SET_P; dcfg->pdfp_only_get = NOT_SET; + dcfg->pdfp_method = NOT_SET; /* Geo Lookups */ dcfg->geo = NOT_SET_P; @@ -384,6 +385,8 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { ? parent->pdfp_token_name : child->pdfp_token_name); merged->pdfp_only_get = (child->pdfp_only_get == NOT_SET ? parent->pdfp_only_get : child->pdfp_only_get); + merged->pdfp_method = (child->pdfp_method == NOT_SET + ? parent->pdfp_method : child->pdfp_method); /* Geo Lookup */ merged->geo = (child->geo == NOT_SET_P @@ -457,6 +460,7 @@ void init_directory_config(directory_config *dcfg) { if (dcfg->pdfp_timeout == NOT_SET) dcfg->pdfp_timeout = 10; if (dcfg->pdfp_token_name == NOT_SET_P) dcfg->pdfp_token_name = "PDFPTOKEN"; if (dcfg->pdfp_only_get == NOT_SET) dcfg->pdfp_only_get = 1; + if (dcfg->pdfp_method == NOT_SET) dcfg->pdfp_method = PDF_PROTECT_METHOD_TOKEN_REDIRECTION; /* Geo Lookup */ if (dcfg->geo == NOT_SET_P) dcfg->geo = NULL; @@ -1195,6 +1199,25 @@ static const char *cmd_pdf_protect_intercept_get_only(cmd_parms *cmd, void *_dcf return NULL; } +static const char *cmd_pdf_protect_method(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "TokenRedirection") == 0) { + dcfg->pdfp_method = PDF_PROTECT_METHOD_TOKEN_REDIRECTION; + } else + if (strcasecmp(p1, "ForcedDownload") == 0) { + dcfg->pdfp_method = PDF_PROTECT_METHOD_FORCED_DOWNLOAD; + } else { + return (const char *)apr_psprintf(cmd->pool, + "ModSecurity: Unrecognised parameter value for SecPdfProtectMethod: %s", p1); + } + + return NULL; +} + /* -- Geo Lookup configuration -- */ static const char *cmd_geo_lookups_db(cmd_parms *cmd, void *_dcfg, @@ -1550,6 +1573,14 @@ const command_rec module_directives[] = { "whether or not to intercept only GET and HEAD requess. Defaults to true." ), + AP_INIT_TAKE1 ( + "SecPdfProtectMethod", + cmd_pdf_protect_method, + NULL, + RSRC_CONF, + "protection method to use. Can be 'TokenRedirection' (default) or 'ForcedDownload'" + ), + AP_INIT_TAKE1 ( "SecGeoLookupsDb", cmd_geo_lookups_db, diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index fb106284..6a34ca5b 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -412,6 +412,7 @@ struct directory_config { int pdfp_timeout; const char *pdfp_token_name; int pdfp_only_get; + int pdfp_method; /* Geo Lookup */ geo_db *geo; diff --git a/apache2/pdf_protect.c b/apache2/pdf_protect.c index e77f1933..87e2df2b 100644 --- a/apache2/pdf_protect.c +++ b/apache2/pdf_protect.c @@ -225,11 +225,14 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { if (msr == NULL) { ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, f->r->server, "ModSecurity: Internal Error: Unable to retrieve context in PDF output filter."); + ap_remove_output_filter(f); + return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); } if (msr->txcfg->pdfp_enabled == 1) { + // TODO Should we look at err_headers_out too? const char *h_content_type = apr_table_get(f->r->headers_out, "Content-Type"); if (msr->txcfg->debuglog_level >= 9) { @@ -258,7 +261,7 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { // TODO application/x-pdf, application/vnd.fdf, application/vnd.adobe.xfdf, // application/vnd.adobe.xdp+xml, application/vnd.adobe.xfd+xml, application/vnd.pdf - // application/acrobat, text/pdf, text/x-pdf + // application/acrobat, text/pdf, text/x-pdf ??? if (((f->r->content_type != NULL)&&(strcasecmp(f->r->content_type, "application/pdf") == 0)) || ((h_content_type != NULL)&&(strcasecmp(h_content_type, "application/pdf") == 0))) { @@ -270,13 +273,32 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { log_escape_nq(msr->mp, r->uri)); } + /* If we are configured with ForcedDownload protection method then we + * can do our thing here and finish early. + */ + if (msr->txcfg->pdfp_method == PDF_PROTECT_METHOD_FORCED_DOWNLOAD) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "PdfProtect: Forcing download of a dynamically " + "generated PDF file."); + } + + apr_table_set(f->r->headers_out, "Content-Disposition", DISPOSITION_VALUE); + f->r->content_type = ATTACHMENT_MIME_TYPE; + + ap_remove_output_filter(f); + + return ap_pass_brigade(f->next, bb_in); + } + + /* If we are here that means TokenRedirection is the desired protection method. */ + /* Is this a non-GET request? */ if ((f->r->method_number != M_GET)&& ((msr->txcfg->pdfp_only_get == 1)||(msr->txcfg->pdfp_only_get == -1)) ) { /* This is a non-GET request and we have been configured - * not to intercept it. We are not going to do that but - * we are going to tweak the headers to force download. + * not to intercept it. So we are going to tweak the headers + * to force download. */ if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "PdfProtect: Forcing download of a dynamically " @@ -287,6 +309,7 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { f->r->content_type = ATTACHMENT_MIME_TYPE; ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb_in); } @@ -362,6 +385,15 @@ int pdfp_check(modsec_rec *msr) { return 0; } + if (msr->txcfg->pdfp_method != PDF_PROTECT_METHOD_TOKEN_REDIRECTION) { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "PdfProtect: Configured with ForcedDownload as protection method, " + "skipping detection on the inbound."); + } + + return 0; + } + /* Then determine whether we need to act at * all. If the request is not for a PDF file * return straight away. diff --git a/apache2/pdf_protect.h b/apache2/pdf_protect.h index 3502a6e1..086bc77f 100644 --- a/apache2/pdf_protect.h +++ b/apache2/pdf_protect.h @@ -14,6 +14,9 @@ #ifndef _PDF_PROTECT_H_ #define _PDF_PROTECT_H_ +#define PDF_PROTECT_METHOD_TOKEN_REDIRECTION 1 +#define PDF_PROTECT_METHOD_FORCED_DOWNLOAD 2 + apr_status_t DSOLOCAL pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in); int DSOLOCAL pdfp_check(modsec_rec *msr);