+Pending - build 139
+
+-- sync 297 http xff, swf, and pdf updates
+
15/02/20 - build 138
-- sync ftp with 297; replace stream event callbacks with FlowData virtuals
hi_client.h
hi_client_norm.cc
hi_client_norm.h
- hi_client_stateful.h
hi_cmd_lookup.cc
hi_cmd_lookup.h
+ hi_file_decomp.cc
+ hi_file_decomp.h
+ hi_file_decomp_pdf.cc
+ hi_file_decomp_pdf.h
+ hi_file_decomp_swf.cc
+ hi_file_decomp_swf.h
hi_include.h
hi_mi.cc
hi_mi.h
hi_ad.cc hi_ad.h \
hi_client.cc hi_client.h \
hi_client_norm.cc hi_client_norm.h \
-hi_client_stateful.h \
hi_cmd_lookup.cc hi_cmd_lookup.h \
hi_events.cc hi_events.h \
+hi_file_decomp.cc hi_file_decomp.h \
+hi_file_decomp_pdf.cc hi_file_decomp_pdf.h \
+hi_file_decomp_swf.cc hi_file_decomp_swf.h \
hi_include.h \
hi_mi.cc hi_mi.h \
hi_module.cc hi_module.h \
#define HEADER_LENGTH__COOKIE 6
#define HEADER_NAME__CONTENT_LENGTH "Content-length"
#define HEADER_LENGTH__CONTENT_LENGTH 14
-#define HEADER_NAME__XFF "X-Forwarded-For"
-#define HEADER_LENGTH__XFF 15
-#define HEADER_NAME__TRUE_IP "True-Client-IP"
-#define HEADER_LENGTH__TRUE_IP 14
+#define HEADER_NAME__XFF HI_UI_CONFIG_XFF_FIELD_NAME
+#define HEADER_LENGTH__XFF (sizeof(HEADER_NAME__XFF)-1)
+#define HEADER_NAME__TRUE_IP HI_UI_CONFIG_TCI_FIELD_NAME
+#define HEADER_LENGTH__TRUE_IP (sizeof(HEADER_NAME__TRUE_IP)-1)
#define HEADER_NAME__HOSTNAME "Host"
#define HEADER_LENGTH__HOSTNAME 4
#define HEADER_NAME__TRANSFER_ENCODING "Transfer-encoding"
const u_char *proxy_start = NULL;
const u_char *proxy_end = NULL;
+// FIXIT-L for 2.9.7 code not yet ported in
+/*static const char *g_field_names[] =
+{
+ HEADER_NAME__COOKIE,
+ HEADER_NAME__CONTENT_LENGTH,
+ HEADER_NAME__XFF,
+ HEADER_NAME__TRUE_IP,
+ HEADER_NAME__HOSTNAME,
+ HEADER_NAME__TRANSFER_ENCODING,
+ HEADER_NAME__CONTENT_TYPE,
+ NULL
+};*/
+
/** This makes passing function arguments much more readable and easier
** to follow.
*/
*/
int CheckChunkEncoding(HI_SESSION *session, const u_char *start, const u_char *end,
const u_char **post_end, u_char *iChunkBuf, uint32_t max_size,
- uint32_t chunk_remainder, uint32_t *updated_chunk_remainder, uint32_t *chunkRead, HttpsessionData *hsd,
+ uint32_t chunk_remainder, uint32_t *updated_chunk_remainder, uint32_t *chunkRead, HttpSessionData *hsd,
int iInspectMode)
{
uint32_t iChunkLen = 0;
printf("** second_end = %c\n", *uri_ptr->second_sp_end);
if(uri_ptr->delimiter)
printf("** delimiter = %c\n", *uri_ptr->delimiter);
-
if(uri_ptr->uri)
printf("** uri = %c\n", *uri_ptr->uri);
if(uri_ptr->norm)
** This is one of the last checks we do to make sure that we didn't
** mess up or anything.
*/
- if(Client->request.uri_size < 1 || Client->request.uri_size > dsize)
+ if(Client->request.uri_size > dsize)
{
- /*
- ** Bad stuff, let's just bail.
- */
return HI_NONFATAL_ERR;
}
static inline int hi_client_extract_post(
HI_SESSION *session, HTTPINSPECT_CONF *ServerConf,
const u_char *ptr, const u_char *end, URI_PTR *result,
- int content_length, bool is_chunked, HttpsessionData *hsd)
+ int content_length, bool is_chunked, HttpSessionData *hsd)
{
const u_char *start = ptr;
const u_char *post_end = end;
static inline void HTTP_CopyUri(
HTTPINSPECT_CONF* /*ServerConf*/, const u_char *start, const u_char *end,
- HttpsessionData *hsd, int stream_ins)
+ HttpSessionData *hsd, int stream_ins)
{
int iRet = 0;
const u_char *cur_ptr;
}
-static inline int unfold_http_uri(HTTPINSPECT_CONF *ServerConf, const u_char *end, URI_PTR *uri_ptr, HttpsessionData *hsd, int stream_ins)
+static inline int unfold_http_uri(HTTPINSPECT_CONF *ServerConf, const u_char *end, URI_PTR *uri_ptr, HttpSessionData *hsd, int stream_ins)
{
uint8_t unfold_buf[DECODE_BLEN];
uint32_t unfold_size =0;
static inline int hi_client_extract_uri(
HI_SESSION *session, HTTPINSPECT_CONF *ServerConf,
HI_CLIENT * Client, const u_char *start, const u_char *end,
- const u_char *ptr, URI_PTR *uri_ptr, HttpsessionData *hsd, int stream_ins)
+ const u_char *ptr, URI_PTR *uri_ptr, HttpSessionData *hsd, int stream_ins)
{
int iRet = HI_SUCCESS;
const u_char *tmp;
session->norm_flags &= ~HI_BODY;
-
/*
** This loop compares each char to an array of functions
** (one for each char) and calling that function if there is one.
ptr++;
}
+ /* No uri in this request. We shouldn't process this request */
+ if(uri_ptr->uri == uri_ptr->uri_end)
+ return HI_NONFATAL_ERR;
return iRet;
}
if(!true_ip)
return p;
- if( (hdrs_args->true_clnt_xff & HDRS_BOTH) == HDRS_BOTH)
+ if( (hdrs_args->true_clnt_xff & (HDRS_BOTH | XFF_HEADERS)) == HDRS_BOTH)
{
hi_set_event(GID_HTTP_CLIENT, HI_CLIENT_BOTH_TRUEIP_XFF_HDRS);
}
return p;
}
}
+ /* At this point we have a new/valid IP from the header being processed.
+ If we are using custom xff headers, check the precedence ranking. */
+ if( (hdrs_args->true_clnt_xff & XFF_HEADERS) != 0 )
+ {
+ /* Have we located any others? */
+ if( (hdrs_args->top_precedence > 0) &&
+ (hdrs_args->new_precedence >= hdrs_args->top_precedence) )
+ {
+ sfip_free( tmp );
+ free( ipAddr );
+ return( p );
+ }
+
+ hdrs_args->top_precedence = hdrs_args->new_precedence;
+
+ /* if we find the top precedence, no need to continue
+ looking so clear the XFF_HEADERS_ACTIVE flag. */
+ if( hdrs_args->top_precedence == XFF_TOP_PRECEDENCE )
+ hdrs_args->true_clnt_xff &= (~XFF_HEADERS_ACTIVE);
+ }
+
+ /* If we have already set a 'true_ip' for the session, look to see if the
+ new IP differs from the current IP. If so, replace it and post an alert. */
if(*true_ip)
{
if(!sfip_equals(*true_ip, tmp))
sfip_free(*true_ip);
*true_ip = tmp;
- hi_set_event(GID_HTTP_CLIENT, HI_CLIENT_MULTIPLE_TRUEIP_IN_SESSION);
+ if ((hdrs_args->true_clnt_xff & XFF_HEADERS) == 0)
+ hi_set_event(GID_HTTP_CLIENT, HI_CLIENT_MULTIPLE_TRUEIP_IN_SESSION);
}
else
sfip_free(tmp);
const u_char *extract_http_hostname(HI_SESSION *session, const u_char *p, const u_char *start,
- const u_char *end, HEADER_PTR *header_ptr, HttpsessionData *hsd)
+ const u_char *end, HEADER_PTR *header_ptr, HttpSessionData *hsd)
{
int num_spaces = 0;
uint8_t unfold_buf[DECODE_BLEN];
return p;
}
+static inline bool IsXFFFieldName( HI_CLIENT_HDR_ARGS *hdrs_args,
+ u_char **pp, const u_char *end,
+ uint8_t **Field_Names, uint8_t *Field_Length )
+{
+ int i;
+ int len;
+ uint8_t *header_ptr;
+ uint8_t *field_ptr;
+
+ i = 0; // index into the list of XFF field names
+ field_ptr = NULL; // pointer into the active Field_Name entry
+ header_ptr = *pp; // pointer into the header, will not step past 'end'
+ len = 0; // len of the matched name entry
+
+ while( true )
+ {
+ /* If we run off the end of the active table, or table is truncated then
+ we can stop. We didn't locate a match. */
+ if( (i >= (HI_UI_CONFIG_MAX_XFF_FIELD_NAMES)) || (Field_Names[i] == NULL) )
+ break;
+
+ if( field_ptr == NULL ) // didn't start to match any entry
+ {
+ /* If the length doesn't permit a match, move on. */
+ if( (end - *pp) < Field_Length[i] )
+ {
+ i += 1;
+ continue;
+ }
+
+ if( toupper(*header_ptr) == *Field_Names[i] ) // does the first char match?
+ {
+ /* set our working pointer to the field name */
+ field_ptr = (Field_Names[i] + 1);
+ header_ptr += 1;
+ len = 1; // We matched one character
+ continue;
+ }
+ i += 1;
+ }
+ else
+ {
+ /* If we are still matching and we get to the end
+ of the field name, then we've located a name match */
+ if( *field_ptr == 0 ) // End of the field name
+ {
+ *pp += len; // Step input pointer over what we found
+ hdrs_args->new_precedence = (i+1); // Precedence started with one
+ return( true );
+ }
+ else
+ {
+ /* check for another matching character */
+ if( toupper(*header_ptr) == *field_ptr )
+ {
+ header_ptr += 1;
+ field_ptr += 1;
+ len += 1;
+ }
+ else
+ {
+ header_ptr = *pp; // Back to the start for the name
+ field_ptr = NULL; // No longer a match
+ len = 0;
+ i += 1;
+ }
+ }
+ }
+ }
+
+ return( false );
+}
+
static inline const u_char *extractHeaderFieldValues(HI_SESSION *session,
HTTPINSPECT_CONF *ServerConf, const u_char *p, const u_char *offset,
const u_char *start, const u_char *end, HI_CLIENT_HDR_ARGS *hdrs_args)
{
- HttpsessionData *hsd;
+ HttpSessionData *hsd;
hsd = hdrs_args->sd;
- if (((p - offset) == 0) && ((*p == 'C') || (*p == 'c')))
+ if (((p - offset) == 0) && (ServerConf->enable_xff != 0) &&
+ ((hdrs_args->true_clnt_xff & XFF_HEADERS_ACTIVE) != 0) && (hsd) &&
+ IsXFFFieldName(hdrs_args, (u_char **)&p, (const u_char *)end,
+ ServerConf->xff_headers, ServerConf->xff_header_lengths))
+ {
+ p = extract_http_xff(session, p, start, end, hdrs_args);
+ }
+ else if (((p - offset) == 0) && ((*p == 'C') || (*p == 'c')))
{
/* Search for 'Cookie' at beginning, starting from current *p */
if ( ServerConf->enable_cookie &&
}
else if (((p - offset) == 0) && ((*p == 'x') || (*p == 'X') || (*p == 't') || (*p == 'T')))
{
- if ( (ServerConf->enable_xff) && hsd )
+ //* The default/legacy behavior with two builtin XFF field names */
+ if ( (ServerConf->enable_xff) && hsd && ((hdrs_args->true_clnt_xff & XFF_HEADERS) == 0) )
{
if(IsHeaderFieldName(p, end, HEADER_NAME__XFF, HEADER_LENGTH__XFF))
{
static inline const u_char *hi_client_extract_header(
HI_SESSION *session, HTTPINSPECT_CONF *ServerConf,
HEADER_PTR *header_ptr, const u_char *start,
- const u_char *end, HttpsessionData *hsd, int stream_ins)
+ const u_char *end, HttpSessionData *hsd, int stream_ins)
{
int iRet = HI_SUCCESS;
const u_char *p;
hdrs_args.sd = hsd;
hdrs_args.strm_ins = stream_ins;
hdrs_args.hst_name_hdr = 0;
- hdrs_args.true_clnt_xff = 0;
+ hdrs_args.true_clnt_xff = (ServerConf->xff_headers[0] != NULL) ? XFF_INIT : 0;
SkipBlankSpace(start,end,&p);
Client->request.header_raw = NULL;\
Client->request.header_raw_size = 0;\
Client->request.header_norm = NULL; \
+ Client->request.header_norm_size = 0 ;\
Client->request.cookie.cookie = NULL;\
Client->request.cookie.cookie_end = NULL;\
+ if(Client->request.cookie.next) { \
+ COOKIE_PTR *cookie = Client->request.cookie.next; \
+ do { \
+ Client->request.cookie.next = Client->request.cookie.next->next; \
+ free(cookie); \
+ cookie = Client->request.cookie.next; \
+ } while(cookie); \
+ }\
Client->request.cookie.next = NULL;\
Client->request.cookie_norm = NULL;\
+ Client->request.cookie_norm_size = 0;\
} while(0);
#define CLR_METHOD(Client) \
** @retval HI_SUCCESS URI detected and session pointers updated
*/
-int StatelessInspection(Packet *p, HI_SESSION *session, HttpsessionData *hsd, int stream_ins)
+int StatelessInspection(Packet *p, HI_SESSION *session, HttpSessionData *hsd, int stream_ins)
{
HTTPINSPECT_CONF *ServerConf;
HTTPINSPECT_CONF *ClientConf;
const u_char *method_end = NULL;
int method_len;
int iRet=0;
- char sans_uri = 0;
+ bool sans_uri = false;
const unsigned char *data = p->data;
int dsize = p->dsize;
if ( !stream_ins )
hi_set_event(GID_HTTP_CLIENT, HI_CLIENT_UNKNOWN_METHOD);
Client->request.method = HI_UNKNOWN_METHOD;
- sans_uri = 1;
+ sans_uri = true;
}
}
if (!sans_uri )
{
- uri_ptr.uri = ptr;
+ uri_ptr.uri = method_ptr.uri_end;
uri_ptr.uri_end = end;
/* This will set up the URI pointers - effectively extracting
* the URI. */
iRet = hi_client_extract_uri(
- session, ServerConf, Client, start, end, ptr, &uri_ptr, hsd, stream_ins);
+ session, ServerConf, Client, start, end, uri_ptr.uri, &uri_ptr, hsd, stream_ins);
}
/* Check if the URI exceeds the max header field length */
iRet = SetClientVars(Client, &uri_ptr, dsize);
if (iRet)
{
+ CLR_HEADER(Client);
+ CLR_POST(Client);
+ CLR_METHOD(Client);
return iRet;
}
/*
return HI_SUCCESS;
}
-int hi_client_inspection(Packet *p, void *S, HttpsessionData *hsd, int stream_ins)
+int hi_client_inspection(Packet *p, void *S, HttpSessionData *hsd, int stream_ins)
{
- HI_SESSION *session;
-
- int iRet;
-
if(!S || !(p->data) || (p->dsize < 1))
{
return HI_INVALID_ARG;
}
- session = (HI_SESSION *)S;
+ HI_SESSION* session = (HI_SESSION*) S;
if(!session->global_conf)
{
return HI_INVALID_ARG;
}
- {
- /*
- ** Otherwise we assume stateless inspection
- */
- iRet = StatelessInspection(p, session, hsd, stream_ins);
- if (iRet)
- {
- return iRet;
- }
- }
-
- return HI_SUCCESS;
+ return StatelessInspection(p, session, hsd, stream_ins);
}
/*
** Initializes arrays and search algorithms depending on the type of
** inspection that we are doing.
**
-** @param GlobalConf pointer to the global configuration
-**
-** @return integer
-**
** @retval HI_SUCCESS function successful.
*/
-int hi_client_init(HTTPINSPECT_GLOBAL_CONF*)
+int hi_client_init()
{
int iCtr;
+ memset(lookup_table, 0x00, sizeof(lookup_table));
+
+ // Set up the non-ASCII register for processing.
+ for(iCtr = 0x80; iCtr <= 0xff; iCtr++)
{
- memset(lookup_table, 0x00, sizeof(lookup_table));
+ lookup_table[iCtr] = SetBinaryNorm;
+ }
+ lookup_table[0x00] = SetBinaryNorm;
- /*
- ** Set up the non-ASCII register for processing.
- */
- for(iCtr = 0x80; iCtr <= 0xff; iCtr++)
- {
- lookup_table[iCtr] = SetBinaryNorm;
- }
- lookup_table[0x00] = SetBinaryNorm;
+ lookup_table[(uint8_t)' '] = NextNonWhiteSpace;
+ lookup_table[(uint8_t)'\r'] = find_rfc_delimiter;
+ lookup_table[(uint8_t)'\n'] = find_non_rfc_delimiter;
- lookup_table[(uint8_t)' '] = NextNonWhiteSpace;
- lookup_table[(uint8_t)'\r'] = find_rfc_delimiter;
- lookup_table[(uint8_t)'\n'] = find_non_rfc_delimiter;
+ // ASCII encoding
+ lookup_table[(uint8_t)'%'] = SetPercentNorm;
- /*
- ** ASCII encoding
- */
- lookup_table[(uint8_t)'%'] = SetPercentNorm;
+ // Looking for multiple slashes
+ lookup_table[(uint8_t)'/'] = SetSlashNorm;
- /*
- ** Looking for multiple slashes
- */
- lookup_table[(uint8_t)'/'] = SetSlashNorm;
+ // Looking for backslashs
+ lookup_table[(uint8_t)'\\'] = SetBackSlashNorm;
- /*
- ** Looking for backslashs
- */
- lookup_table[(uint8_t)'\\'] = SetBackSlashNorm;
-
- lookup_table[(uint8_t)'+'] = SetPlusNorm;
-
-
- /*
- ** Look up parameter field, so we don't alert on long directory
- ** strings, when the next slash in the parameter field.
- */
- lookup_table[(uint8_t)'?'] = SetParamField;
+ lookup_table[(uint8_t)'+'] = SetPlusNorm;
- /*
- ** Look for absolute URI and proxy communication.
- */
- lookup_table[(uint8_t)':'] = SetProxy;
+ // Look up parameter field, so we don't alert on long directory
+ // strings, when the next slash in the parameter field.
+ lookup_table[(uint8_t)'?'] = SetParamField;
- }
+ // Look for absolute URI and proxy communication.
+ lookup_table[(uint8_t)':'] = SetProxy;
return HI_SUCCESS;
}
-
/**
** This was just an initial testing program for these functions.
*/
hi_ui_config_print_config(&GlobalConf);
- if((iRet = hi_client_init(&GlobalConf)))
+ if((iRet = hi_client_init()))
{
printf("** error client init\n");
return iRet;
#define URI_END 99
#define POST_END 100
#define NO_URI 101
-typedef enum {
- TRUE_CLIENT_IP_HDR = 0x01,
- XFF_HDR = 0x02,
- HDRS_BOTH = 0x03
-} ActionSFCC;
+#define XFF_MODE_MASK (0x000f)
+#define XFF_EXFF_MASK (0x000c)
+#define TRUE_CLIENT_IP_HDR (0x01)
+#define XFF_HDR (0x02)
+#define HDRS_BOTH (0x03)
+#define XFF_HEADERS (0x04) // Using xff_headers list
+#define XFF_HEADERS_ACTIVE (0x08) // Looking for highest precedence xff header
+#define XFF_INIT (XFF_HEADERS | XFF_HEADERS_ACTIVE)
+
+#define XFF_TOP_PRECEDENCE (1)
+#define XFF_BOT_PRECEDENCE (255)
+
typedef struct s_COOKIE_PTR
{
const u_char *cookie;
typedef struct s_HI_CLIENT_REQ
{
- /*
- u_char *method;
- int method_size;
- */
-
const u_char *uri;
const u_char *uri_norm;
const u_char *post_raw;
u_int cookie_norm_size;
u_int method_size;
- /*
- u_char *param;
- u_int param_size;
- u_int param_norm;
- */
-
- /*
- u_char *ver;
- u_int ver_size;
-
- u_char *hdr;
- u_int hdr_size;
-
- u_char *payload;
- u_int payload_size;
- */
-
const u_char *pipeline_req;
u_char method;
uint16_t uri_encode_type;
{
HEADER_PTR *hdr_ptr;
HEADER_FIELD_PTR *hdr_field_ptr;
- HttpsessionData *sd;
+ HttpSessionData *sd;
int strm_ins;
int hst_name_hdr;
- int true_clnt_xff;
+ uint8_t true_clnt_xff;
+ uint8_t top_precedence;
+ uint8_t new_precedence;
} HI_CLIENT_HDR_ARGS;
-int hi_client_inspection(Packet *p, void *session, HttpsessionData *hsd, int stream_ins);
-int hi_client_init(HTTPINSPECT_GLOBAL_CONF *GlobalConf);
+int hi_client_inspection(Packet *p, void *session, HttpSessionData *hsd, int stream_ins);
+int hi_client_init();
+
+char **hi_client_get_field_names();
extern const u_char *proxy_start;
extern const u_char *proxy_end;
+++ /dev/null
-//--------------------------------------------------------------------------
-// Copyright (C) 2014-2015 Cisco and/or its affiliates. All rights reserved.
-// Copyright (C) 2005-2013 Sourcefire, Inc.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License Version 2 as published
-// by the Free Software Foundation. You may not use, modify or distribute
-// this program under any other version of the GNU General Public License.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License along
-// with this program; if not, write to the Free Software Foundation, Inc.,
-// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-//--------------------------------------------------------------------------
-
-/*
-* hi_client_stateful.h: Functions prototypes for client
- * stateful processing.
-*
-* Author: Chris Sherwin <csherwin@sourcefire.com>
-*
-*/
-#ifndef HI_CLIENT_STATEFUL_H
-#define HI_CLIENT_STATEFUL_H
-
-#include "hi_include.h"
-#include "hi_si.h"
-
-int StatefulInspection( HI_SESSION* sessionp, unsigned char* datap, int dsize );
-
-#endif /* HI_CLIENT_STATEFUL_H */
HI_SERVER_JS_OBFUSCATION_EXCD,
HI_SERVER_JS_EXCESS_WS,
HI_SERVER_MIXED_ENCODINGS,
+ HI_SERVER_SWF_ZLIB_FAILURE,
+ HI_SERVER_SWF_LZMA_FAILURE,
+ HI_SERVER_PDF_DEFL_FAILURE,
+ HI_SERVER_PDF_UNSUP_COMP_TYPE,
+ HI_SERVER_PDF_CASC_COMP,
+ HI_SERVER_PDF_PARSE_FAILURE,
HI_SERVER_EVENT_NUM
-}HI_EVENTS;
+} HI_EVENTS;
/*
** These defines are the alert names for each event
"javascript whitespaces exceeds max allowed"
#define HI_SERVER_MIXED_ENCODINGS_STR \
"multiple encodings within javascript obfuscated data"
+#define HI_SERVER_SWF_ZLIB_FAILURE_STR \
+ "HTTP response SWF file zlib decompression failure"
+#define HI_SERVER_SWF_LZMA_FAILURE_STR \
+ "HTTP response SWF file LZMA decompression failure"
+#define HI_SERVER_PDF_DEFL_FAILURE_STR \
+ "HTTP response PDF file deflate decompression failure"
+#define HI_SERVER_PDF_UNSUP_COMP_TYPE_STR \
+ "HTTP response PDF file unsupported compression type"
+#define HI_SERVER_PDF_CASC_COMP_STR \
+ "HTTP response PDF file cascaded compression"
+#define HI_SERVER_PDF_PARSE_FAILURE_STR \
+ "HTTP response PDF file parse failure"
void hi_set_event(unsigned gid, unsigned sid);
void hi_clear_events();
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2015 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "util.h"
+#include "hi_file_decomp.h"
+#include "snort_types.h"
+#include "detection_util.h"
+#include "hi_file_decomp_pdf.h"
+#include "hi_file_decomp_swf.h"
+
+static const char PDF_Sig[5] = { '%', 'P', 'D', 'F', '-' };
+static const char SWF_ZLIB_Sig[3] = { 'C', 'W', 'S' };
+#ifdef LZMA
+static const char SWF_LZMA_Sig[3] = { 'Z', 'W', 'S' };
+#endif
+static const char SWF_Uncomp_Sig[3] = { 'F', 'W', 'S' };
+
+/* Please assure that the following value correlates with the set of sig's */
+#define MAX_SIG_LENGTH (5)
+
+static struct sig_map_s
+{
+ const char* Sig;
+ size_t Sig_Length;
+ bool Enabled;
+ file_type_t File_Type;
+ file_compression_type_t File_Compression_Type;
+} Signature_Map[] =
+{
+
+ { PDF_Sig, sizeof(PDF_Sig), false, FILE_TYPE_PDF, FILE_COMPRESSION_TYPE_NONE }, // Compression type is embedded in PDF dictionaries
+ { SWF_ZLIB_Sig, sizeof(SWF_ZLIB_Sig), false, FILE_TYPE_SWF, FILE_COMPRESSION_TYPE_ZLIB },
+#ifdef LZMA
+ { SWF_LZMA_Sig, sizeof(SWF_LZMA_Sig), false, FILE_TYPE_SWF, FILE_COMPRESSION_TYPE_LZMA },
+#endif
+ { NULL, 0, false, FILE_TYPE_NONE, FILE_COMPRESSION_TYPE_NONE }
+};
+
+/* Define the elements of the Sig_State value (packed for storage efficiency */
+#define SIG_MATCH_ACTIVE (0x80)
+#define SIG_SIG_INDEX_MASK (0x70)
+#define SIG_SIG_INDEX_SHIFT (4)
+#define SIG_CHR_INDEX_MASK (0x07)
+#define SIG_CHR_INDEX_SHIFT (0)
+
+static uint8_t File_Decomp_Buffer[DECODE_BLEN];
+
+/* Look for possible sig at the current payload location.
+ Do NOT beyond the current location (initial Next_In). */
+static fd_status_t Locate_Sig_Here( fd_session_p_t SessionPtr )
+{
+ uint64_t Sig_Index, Char_Index;
+
+ /* If there's no new input, we don't change state */
+ if( (SessionPtr->Avail_In == 0) ||
+ (SessionPtr->Next_In == NULL) || (SessionPtr->Next_Out == NULL) )
+ return( File_Decomp_Error );
+
+ if( SessionPtr->Avail_Out < MAX_SIG_LENGTH )
+ return( File_Decomp_BlockOut );
+
+ /* Given that we are here, there is at least one input byte to process.
+ And at least enough room in the output stream for the signature. */
+
+ /* Have we started down a sig string? */
+ if( (SessionPtr->Sig_State & SIG_MATCH_ACTIVE) != 0 )
+ {
+ /* Get the current index into the sig map table (indicating which sig) and
+ the index into the sig itself. */
+ Sig_Index = (SessionPtr->Sig_State & SIG_SIG_INDEX_MASK) >> SIG_SIG_INDEX_SHIFT;
+ /* Char_Index indicates the sig char that we are looking for now. */
+ Char_Index = (SessionPtr->Sig_State & SIG_CHR_INDEX_MASK) >> SIG_CHR_INDEX_SHIFT;
+ }
+ else
+ {
+ Sig_Index = 0;
+ Char_Index = 0;
+ }
+
+ /* There must be more in the input stream for us to look at, else
+ we indicate that we didn't find the sig yet. */
+ if( SessionPtr->Avail_In <= Char_Index )
+ return( File_Decomp_BlockIn );
+
+ /* NOTE: The following code block makes the assumption that there are
+ at least MAX_SIG_LENGTH bytes in the output buffer. This assumption
+ is valid for the current implementation where the signature only
+ occurs at the beginning of the file. For the generic case of the sig
+ begin embedded with the file, the seach will need to modified.*/
+ while( 1 )
+ {
+ /* if we get to the end of the sig table (or the table is empty),
+ indicate that we didn't match a sig */
+ if( Signature_Map[Sig_Index].Sig == NULL )
+ return( File_Decomp_NoSig );
+
+ /* Get next char and see if it matches next char in sig */
+ if( (Signature_Map[Sig_Index].Enabled) &&
+ (*(SessionPtr->Next_In+Char_Index) == *(Signature_Map[Sig_Index].Sig+Char_Index)) )
+ {
+ /* Check to see if we are at the end of the sig string. */
+ if( Char_Index == (Signature_Map[Sig_Index].Sig_Length-1) )
+ {
+ uint8_t *Sig = (uint8_t *)Signature_Map[Sig_Index].Sig;
+ uint16_t Len = (uint16_t)Signature_Map[Sig_Index].Sig_Length;
+
+ SessionPtr->File_Type = Signature_Map[Sig_Index].File_Type;
+ SessionPtr->Decomp_Type = Signature_Map[Sig_Index].File_Compression_Type;
+
+ if( (SessionPtr->File_Type == FILE_TYPE_SWF) && ((SessionPtr->Modes & FILE_REVERT_BIT) != 0) )
+ {
+ Sig = (uint8_t *)SWF_Uncomp_Sig;
+ Len = (uint16_t)sizeof( SWF_Uncomp_Sig );
+ }
+ /* The following is safe as we can only be here is there are
+ are least MAX_SIG_LENGTH bytes in the output buffer */
+ (void)Put_N(SessionPtr, Sig, Len);
+ /* Skip the Sig bytes in the input stream */
+ SessionPtr->Next_In += Len;
+ SessionPtr->Avail_In -= Len;
+ SessionPtr->Total_In += Len;
+ return( File_Decomp_OK );
+ }
+
+ /* check for more available input bytes */
+ if( Char_Index < SessionPtr->Avail_In )
+ {
+ /* Set to the next char and keep checking this matching sig */
+ Char_Index += 1;
+ continue; /* goto top of while() loop */
+ }
+ else
+ {
+ /* Indicate that we are actively finding a sig, save the char index
+ and save the sig index. We'll pickup where we left off when more
+ input is available. */
+ SessionPtr->Sig_State = SIG_MATCH_ACTIVE |
+ ((Sig_Index & SIG_SIG_INDEX_MASK) << SIG_SIG_INDEX_SHIFT) |
+ ((Char_Index & SIG_CHR_INDEX_MASK) << SIG_CHR_INDEX_SHIFT);
+ return( File_Decomp_BlockIn );
+ }
+ }
+ else
+ {
+ /* Failed somewhere matching this sig, goto next sig and reset the
+ Char_Index to the beginning */
+ Sig_Index += 1;
+ Char_Index = 0;
+ }
+ }
+}
+
+static fd_status_t Initialize_Decompression( fd_session_p_t SessionPtr )
+{
+ fd_status_t Ret_Code = File_Decomp_OK;
+
+ switch( SessionPtr->File_Type )
+ {
+ case( FILE_TYPE_SWF ):
+ {
+ Ret_Code = File_Decomp_Init_SWF( SessionPtr );
+ break;
+ }
+ case( FILE_TYPE_PDF ):
+ {
+ Ret_Code = File_Decomp_Init_PDF( SessionPtr );
+ break;
+ }
+ default:
+ return( File_Decomp_Error );
+ }
+
+ if( Ret_Code == File_Decomp_OK )
+ SessionPtr->State = STATE_ACTIVE;
+
+ return( Ret_Code );
+}
+
+static fd_status_t Process_Decompression( fd_session_p_t SessionPtr )
+{
+ fd_status_t Ret_Code = File_Decomp_OK;
+
+ switch( SessionPtr->File_Type )
+ {
+ case( FILE_TYPE_SWF ):
+ {
+ Ret_Code = File_Decomp_SWF( SessionPtr );
+ break;
+ }
+ case( FILE_TYPE_PDF ):
+ {
+ Ret_Code = File_Decomp_PDF( SessionPtr );
+ break;
+ }
+ default:
+ return( File_Decomp_Error );
+ }
+
+ if( Ret_Code == File_Decomp_Complete )
+ SessionPtr->State = STATE_COMPLETE;
+
+ return( Ret_Code );
+}
+
+/* The caller provides Compr_Depth, Decompr_Depth and Modes in the session object.
+ Based on the requested Modes, gear=up to initialize the potential decompressors. */
+fd_status_t File_Decomp_Init( fd_session_p_t SessionPtr )
+{
+ int Sig;
+
+ if( SessionPtr == NULL )
+ return( File_Decomp_Error );
+
+ SessionPtr->State = STATE_READY;
+ SessionPtr->File_Type = FILE_TYPE_NONE;
+ SessionPtr->Decomp_Type = FILE_COMPRESSION_TYPE_NONE;
+
+ for( Sig=0; Signature_Map[Sig].Sig != NULL; Sig++ )
+ {
+ if( (Signature_Map[Sig].File_Type == FILE_TYPE_PDF ) &&
+ ((SessionPtr->Modes & FILE_PDF_ANY) != 0) )
+ Signature_Map[Sig].Enabled = true;
+
+ if( (Signature_Map[Sig].File_Type == FILE_TYPE_SWF ) &&
+ (Signature_Map[Sig].File_Compression_Type == FILE_COMPRESSION_TYPE_ZLIB) &&
+ ((SessionPtr->Modes & FILE_SWF_ZLIB_BIT) != 0) )
+ Signature_Map[Sig].Enabled = true;
+
+#ifdef LZMA
+ if( (Signature_Map[Sig].File_Type == FILE_TYPE_SWF ) &&
+ (Signature_Map[Sig].File_Compression_Type == FILE_COMPRESSION_TYPE_LZMA) &&
+ ((SessionPtr->Modes & FILE_SWF_LZMA_BIT) != 0) )
+ Signature_Map[Sig].Enabled = true;
+#endif
+ }
+
+ return( File_Decomp_OK );
+}
+
+/* Setup session to use internal decompression buffer. Set compr/decompr limits */
+fd_status_t File_Decomp_SetBuf( fd_session_p_t SessionPtr )
+{
+ if( SessionPtr == NULL )
+ return( File_Decomp_Error );
+
+ SessionPtr->Buffer = File_Decomp_Buffer;
+ SessionPtr->Buffer_Len = sizeof(File_Decomp_Buffer);
+
+ SessionPtr->Next_Out = File_Decomp_Buffer;
+ SessionPtr->Avail_Out = sizeof(File_Decomp_Buffer);
+
+ /* If Compr/Decompr limits are set, then enforce then. */
+ if( SessionPtr->Decompr_Depth > 0 )
+ {
+ uint32_t remainder;
+
+ if( SessionPtr->Total_Out > SessionPtr->Decompr_Depth )
+ return( File_Decomp_Error );
+
+ /* Calc whats left in allowance */
+ remainder = (SessionPtr->Total_Out - SessionPtr->Decompr_Depth);
+
+ /* Use smaller of remainder or value provided */
+ SessionPtr->Avail_Out = (remainder < SessionPtr->Avail_Out) ?
+ remainder : SessionPtr->Avail_Out;
+ }
+
+ if( SessionPtr->Compr_Depth > 0 )
+ {
+ uint32_t remainder;
+
+ if( SessionPtr->Total_In > SessionPtr->Compr_Depth )
+ return( File_Decomp_Error );
+
+ remainder = (SessionPtr->Total_In - SessionPtr->Compr_Depth);
+
+ SessionPtr->Avail_In = (remainder < SessionPtr->Avail_In) ?
+ remainder : SessionPtr->Avail_In;
+ }
+
+ /* SessionPtr->Next_In is set by the caller to File_Decomp() */
+
+ return( File_Decomp_OK );
+}
+
+/* Returns a new session object from the MemPool */
+fd_session_p_t File_Decomp_New()
+{
+ fd_session_p_t New_Session = new fd_session_t;
+
+ New_Session->State = STATE_NEW;
+ New_Session->Sig_State = 0;
+ New_Session->Total_In = 0;
+ New_Session->Total_Out = 0;
+ New_Session->Avail_In = 0;
+ New_Session->Next_In = NULL;
+ New_Session->Avail_Out = 0;
+ New_Session->Next_Out = NULL;
+
+ return New_Session;
+}
+
+/* Process Decompression. The session Next_In, Avail_In, Next_Out, Avail_Out MUST have been
+ set by caller.
+*/
+fd_status_t File_Decomp( fd_session_p_t SessionPtr )
+{
+ fd_status_t Return_Code;
+
+ if( (SessionPtr->State == STATE_NEW) ||
+ (SessionPtr->Next_In == NULL) || (SessionPtr->Next_Out == NULL) )
+ return( File_Decomp_Error );
+
+ /* STATE_NEW: Look for one of the configured file signatures. */
+ if( SessionPtr->State == STATE_READY )
+ {
+ /* Look for the signature at the beginning of the payload stream. */
+ if( (Return_Code = Locate_Sig_Here( SessionPtr )) == File_Decomp_OK )
+ {
+ /* We now know the file type and decompression type. Setup appropriate state. */
+ if( (Return_Code = Initialize_Decompression( SessionPtr )) == File_Decomp_OK )
+ {
+ return( Process_Decompression( SessionPtr ) );
+ }
+ else
+ return( Return_Code );
+ }
+ else
+ /* Locate_Sig_Here() might return BlockIn, BlockOut, Error, or NoSig */
+ return( Return_Code );
+ }
+ else if( SessionPtr->State == STATE_ACTIVE )
+ {
+ return( Process_Decompression( SessionPtr ) );
+ }
+ else
+ return( File_Decomp_Error );
+}
+
+
+fd_status_t File_Decomp_End( fd_session_p_t SessionPtr )
+{
+ if( SessionPtr == NULL )
+ return( File_Decomp_Error );
+
+ switch( SessionPtr->File_Type )
+ {
+ case( FILE_TYPE_SWF ):
+ {
+ return( File_Decomp_End_SWF( SessionPtr ) );
+ }
+ case( FILE_TYPE_PDF ):
+ {
+ return( File_Decomp_End_PDF( SessionPtr ) );
+ }
+ default:
+ return( File_Decomp_Error );
+ }
+
+ return( File_Decomp_OK );
+}
+
+fd_status_t File_Decomp_Reset( fd_session_p_t SessionPtr )
+{
+ fd_status_t Ret_Code;
+
+ if( SessionPtr == NULL )
+ return( File_Decomp_Error );
+
+ Ret_Code = File_Decomp_End( SessionPtr );
+
+ SessionPtr->State = STATE_READY;
+
+ return( Ret_Code );
+}
+
+fd_status_t File_Decomp_StopFree( fd_session_p_t SessionPtr )
+{
+ if( SessionPtr == NULL )
+ return( File_Decomp_Error );
+
+ File_Decomp_End( SessionPtr );
+ File_Decomp_Free( SessionPtr );
+
+ return( File_Decomp_OK );
+}
+
+
+void File_Decomp_Free( fd_session_p_t SessionPtr )
+{
+ delete SessionPtr;
+}
+
+void File_Decomp_Alert( fd_session_p_t SessionPtr, int Event )
+{
+ if( (SessionPtr != NULL) && (SessionPtr->Alert_Callback != NULL) && (SessionPtr->Alert_Context) )
+ (SessionPtr->Alert_Callback)(SessionPtr->Alert_Context, Event);
+}
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2015 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2003-2013 Sourcefire, Inc.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+#ifndef HI_FILE_DECOMP_H
+#define HI_FILE_DECOMP_H
+
+// FIXIT-H temporary for development convenience
+//#define LZMA 1
+
+#include <stdint.h>
+#include <string.h>
+
+/* File_Decomp global typedefs (used in child objects) */
+typedef enum fd_status
+{
+ File_Decomp_DecompError = -2, /* Error from decompression */
+ File_Decomp_Error = -1, /* Error from decompression */
+ File_Decomp_OK = 0,
+ File_Decomp_NoSig = 1, /* No file signature located */
+ File_Decomp_Complete = 2, /* Completed */
+ File_Decomp_BlockOut = 3, /* Blocked due to lack of output space */
+ File_Decomp_BlockIn = 4, /* Blocked due to lack in input data */
+ File_Decomp_Eof = 5 /* End of file located */
+} fd_status_t;
+
+typedef enum file_compression_type
+{
+ FILE_COMPRESSION_TYPE_NONE,
+ FILE_COMPRESSION_TYPE_DEFLATE,
+ FILE_COMPRESSION_TYPE_ZLIB,
+ FILE_COMPRESSION_TYPE_LZMA,
+ FILE_COMPRESSION_TYPE_MAX
+} file_compression_type_t;
+
+typedef struct fd_session_s *fd_session_p_t, fd_session_t;
+
+#include "hi_file_decomp_pdf.h"
+#include "hi_file_decomp_swf.h"
+#include <zlib.h>
+
+#ifdef LZMA
+#include <lzma.h>
+#endif
+
+/* Potential decompression modes */
+#define FILE_SWF_LZMA_BIT (0x00000001)
+#define FILE_SWF_ZLIB_BIT (0x00000002)
+#define FILE_PDF_DEFL_BIT (0x00000004)
+#define FILE_FILT_NORM_BIT (0x40000000) /* Normalize the PDF /Filter value string */
+#define FILE_REVERT_BIT (0x80000000) /* Revert to 'uncompressed' state */
+
+#define FILE_PDF_ANY (FILE_PDF_DEFL_BIT)
+#define FILE_SWF_ANY (FILE_SWF_LZMA_BIT | FILE_SWF_ZLIB_BIT)
+
+/* Private Types */
+typedef enum file_type
+{
+ FILE_TYPE_NONE,
+ FILE_TYPE_SWF,
+ FILE_TYPE_PDF,
+ FILE_TYPE_MAX
+} file_type_t;
+
+typedef enum states
+{
+ STATE_NEW, /* Session created */
+ STATE_READY, /* Session created and ready for content, no file/decomp selected */
+ STATE_ACTIVE, /* Decompressor inited and ready for content */
+ STATE_COMPLETE /* Decompression completed */
+} fd_states_t;
+
+struct fd_session_s
+{
+ uint8_t *Next_In; /* next input byte */
+ uint32_t Avail_In; /* number of bytes available at next_in */
+ uint32_t Total_In; /* total number of input bytes read so far */
+
+ uint8_t *Next_Out; /* next output byte should be put there */
+ uint32_t Avail_Out; /* remaining free space at next_out */
+ uint32_t Total_Out; /* total number of bytes output so far */
+
+ /* Internal buffer setup by _Init(). App can overide. */
+ uint8_t *Buffer; /* pointer to decompresiion buffer */
+ uint32_t Buffer_Len;/* length of decompression buffer */
+
+ /* Configuration settings */
+ uint32_t Compr_Depth;
+ uint32_t Decompr_Depth;
+ uint32_t Modes; /* Bit mapped set of potential file/algo modes */
+
+ /* Alerting callback */
+ void (*Alert_Callback)(void *Context, int Event);
+ void *Alert_Context;
+
+ /* Internal State */
+ uint8_t File_Type; /* Active file type */
+ uint8_t Decomp_Type; /* Active decompression type */
+ uint8_t Sig_State; /* Sig search state machine */
+ uint8_t State; /* main state machine */
+
+ union
+ {
+ fd_PDF_t PDF;
+ fd_SWF_t SWF;
+ } Decomp_State;
+
+ /* Specific event indicated by DecomprError return */
+ int Error_Event;
+};
+
+/* Macros */
+
+#ifndef SYNC_IN
+#define SYNC_IN(dest) \
+ dest->next_in = SessionPtr->Next_In; \
+ dest->avail_in = SessionPtr->Avail_In; \
+ dest->total_in = SessionPtr->Total_In; \
+ dest->next_out = SessionPtr->Next_Out; \
+ dest->avail_out = SessionPtr->Avail_Out; \
+ dest->total_out = SessionPtr->Total_Out;
+#endif
+
+#ifndef SYNC_OUT
+#define SYNC_OUT(src) \
+ SessionPtr->Next_In = (uint8_t *)src->next_in; \
+ SessionPtr->Avail_In = src->avail_in; \
+ SessionPtr->Total_In = src->total_in; \
+ SessionPtr->Next_Out = (uint8_t *)src->next_out; \
+ SessionPtr->Avail_Out = src->avail_out; \
+ SessionPtr->Total_Out = src->total_out;
+#endif
+
+
+/* Inline Functions */
+
+static inline bool Peek_1( fd_session_p_t SessionPtr, uint8_t *c)
+{
+ if( (SessionPtr->Next_In != NULL) && (SessionPtr->Avail_In > 0) )
+ {
+ *c = *(SessionPtr->Next_In);
+ return( true );
+ }
+ else
+ return( false );
+}
+
+static inline bool Get_1( fd_session_p_t SessionPtr, uint8_t *c )
+{
+ if( (SessionPtr->Next_In != NULL) && (SessionPtr->Avail_In > 0) )
+ {
+ *c = *(SessionPtr->Next_In)++;
+ SessionPtr->Avail_In -= 1;
+ SessionPtr->Total_In += 1;
+ return( true );
+ }
+ else
+ return( false );
+}
+
+static inline bool Get_N( fd_session_p_t SessionPtr, uint8_t **c, uint16_t N )
+{
+ if( (SessionPtr->Next_In != NULL) && (SessionPtr->Avail_In >= N) )
+ {
+ *c = SessionPtr->Next_In;
+ SessionPtr->Next_In += N;
+ SessionPtr->Avail_In -= N;
+ SessionPtr->Total_In += N;
+ return( true );
+ }
+ else
+ return( false );
+}
+
+static inline bool Put_1( fd_session_p_t SessionPtr, uint8_t c )
+{
+ if( (SessionPtr->Next_Out != NULL) && (SessionPtr->Avail_Out > 0) )
+ {
+ *(SessionPtr->Next_Out)++ = c;
+ SessionPtr->Avail_Out -= 1;
+ SessionPtr->Total_Out += 1;
+ return( true );
+ }
+ else
+ return( false );
+}
+
+static inline bool Put_N( fd_session_p_t SessionPtr, uint8_t *c, uint16_t N )
+{
+ if( (SessionPtr->Next_Out != NULL) && (SessionPtr->Avail_Out >= N) )
+ {
+ strncpy( (char *)SessionPtr->Next_Out, (const char *)c, N);
+ SessionPtr->Next_Out += N;
+ SessionPtr->Avail_Out -= N;
+ SessionPtr->Total_Out += N;
+ return( true );
+ }
+ else
+ return( false );
+}
+
+static inline bool Move_1( fd_session_p_t SessionPtr )
+{
+ if( (SessionPtr->Next_Out != NULL) && (SessionPtr->Avail_Out > 0) &&
+ (SessionPtr->Next_In != NULL) && (SessionPtr->Avail_In > 0) )
+ {
+ *(SessionPtr->Next_Out) = *(SessionPtr->Next_In);
+ SessionPtr->Next_Out += 1;
+ SessionPtr->Next_In += 1;
+ SessionPtr->Avail_In -= 1;
+ SessionPtr->Avail_Out -= 1;
+ SessionPtr->Total_In += 1;
+ SessionPtr->Total_Out += 1;
+ return( true );
+ }
+ else
+ return( false );
+}
+
+static inline bool Move_N( fd_session_p_t SessionPtr, uint16_t N )
+{
+ if( (SessionPtr->Next_Out != NULL) && (SessionPtr->Avail_Out >= N) &&
+ (SessionPtr->Next_In != NULL) && (SessionPtr->Avail_In >= N) )
+ {
+ strncpy( (char *)SessionPtr->Next_Out, (const char *)SessionPtr->Next_In, N);
+ SessionPtr->Next_Out += N;
+ SessionPtr->Next_In += N;
+ SessionPtr->Avail_In -= N;
+ SessionPtr->Avail_Out -= N;
+ SessionPtr->Avail_Out -= N;
+ SessionPtr->Total_Out += N;
+ return( true );
+ }
+ else
+ return( false );
+}
+
+/* API Functions */
+
+fd_session_p_t File_Decomp_New();
+
+fd_status_t File_Decomp_Init( fd_session_p_t SessionPtr );
+
+fd_status_t File_Decomp_SetBuf( fd_session_p_t SessionPtr );
+
+fd_status_t File_Decomp( fd_session_p_t SessionPtr );
+
+fd_status_t File_Decomp_End( fd_session_p_t SessionPtr );
+
+fd_status_t File_Decomp_Reset( fd_session_p_t SessionPtr );
+
+fd_status_t File_Decomp_StopFree( fd_session_p_t SessionPtr );
+
+void File_Decomp_Free( fd_session_p_t SessionPtr );
+
+void File_Decomp_Alert( fd_session_p_t SessionPtr, int Event );
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2015 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <zlib.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hi_file_decomp.h"
+#include "hi_file_decomp_pdf.h"
+#include "hi_events.h"
+#include "mstring.h"
+
+/* Define characters and tokens in PDF grammar */
+#define TOK_STRM_OPEN "stream"
+#define TOK_STRM_CLOSE "endstream"
+
+#define TOK_OBJ_OPEN "obj"
+#define TOK_OBJ_CLOSE "endobj"
+
+#define TOK_DICT_OPEN "<<"
+#define TOK_DICT_CLOSE ">>"
+#define TOK_DICT_FILT "Filter"
+#define TOK_DICT_FLATE "FlateDecode"
+#define TOK_DICT_FLATE_ALT "Fl"
+#define TOK_DICT_PARMS "DecodeParms"
+#define TOK_DICT_PARMS_ALT "DP"
+#define TOK_DICT_LENGTH "Length"
+#define TOK_DICT_NULL "null"
+#define TOK_DICT_NULL_FILT " null " // Enclose the null object in spaces
+#define TOK_XRF_XREF "xref"
+#define TOK_XRF_TRAILER "trailer"
+#define TOK_XRF_STARTXREF "startxref"
+#define TOK_XRF_END "%%EOF"
+
+#define WHITESPACE_STRING "\011\012\014\015\040" // plus \000
+
+#define TOK_EOL_CR "\r"
+#define TOK_EOL_LF "\n"
+#define TOK_EOL_CRLF "\r\n"
+
+#define CHR_CR '\r'
+#define CHR_LF '\n'
+
+#define CHR_COMMENT '%'
+
+#define CHR_ARRAY_OPEN '['
+#define CHR_ARRAY_CLOSE ']'
+
+#define CHR_ANGLE_OPEN '<'
+#define CHR_ANGLE_CLOSE '>'
+
+#define CHR_SPACE ' '
+#define CHR_NAME_SEP '/'
+
+#define IS_WHITESPACE(c) ((strchr((char *)WHITESPACE_STRING, (int)c) != NULL) || (c == 0))
+#define IS_EOL(c) ((c == CHR_CR) || (c == CHR_LF))
+
+/* Define the parser states */
+typedef enum p_states
+{
+ P_START = 1, // Ground state, nothing 'open'
+ P_COMMENT, // inside a comment (initial state of parser)
+ P_IND_OBJ, // Indirect Object - Sub_State usage
+ P_XREF, // The combined xref, trailer, startxref top level items
+ P_DICT_OBJECT, // A dictionary object
+ P_STREAM // A pseudo state used to process a stream object
+} p_state_t;
+
+typedef enum p_xref_substates
+{
+ P_XREF_TOKEN = 1,
+ P_XREF_END_TOKEN
+} p_xref_t;
+
+typedef enum p_dict_substates
+{
+ P_DICT_OPEN = 1,
+ P_DICT_OPEN_TOK,
+ P_DICT_CLOSE_TOK,
+ P_DICT_FILTER,
+ P_DICT_SKIP,
+ P_DICT_ACTIVE
+} p_dict_t;
+
+typedef enum p_indirect_object_substates
+{
+ P_OBJ_NUMBER = 1,
+ P_GEN_NUMBER,
+ P_OBJ_TOKEN,
+ P_OBJ_EOL,
+ P_STREAM_TOKEN,
+ P_STREAM_EOL,
+ P_STREAM_LF,
+ P_ENDSTREAM_TOKEN,
+ P_ENDOBJ_TOKEN
+} p_indirect_object_substate_t;
+
+static struct filters_s
+{
+ const char *Token;
+ uint8_t Length;
+ uint8_t Type;
+} Filter_Map[] =
+{
+ { TOK_DICT_FLATE, (sizeof(TOK_DICT_FLATE)-1), FILE_COMPRESSION_TYPE_DEFLATE },
+ { TOK_DICT_FLATE_ALT, (sizeof(TOK_DICT_FLATE_ALT)-1), FILE_COMPRESSION_TYPE_DEFLATE },
+ { TOK_DICT_NULL, (sizeof(TOK_DICT_NULL)-1), FILE_COMPRESSION_TYPE_NONE },
+ { NULL, 0, FILE_COMPRESSION_TYPE_NONE }
+};
+
+/* Given a pointer to a /Filter value token, return the
+ associated compression type from the Filter_Map. */
+static inline uint8_t Get_Decomp_Type( uint8_t *Token, uint8_t Length )
+{
+ int Index;
+
+ Index=0;
+
+ while( Filter_Map[Index].Token != NULL )
+ {
+ if( (Filter_Map[Index].Length == Length) &&
+ (strncmp( (const char *)Token, Filter_Map[Index].Token, Length ) == 0 ) )
+ return( Filter_Map[Index].Type );
+ else
+ Index += 1;
+ }
+ return( FILE_COMPRESSION_TYPE_NONE );
+}
+
+static inline void Process_One_Filter( fd_session_p_t SessionPtr, uint8_t *Token, uint8_t Length )
+{
+ uint8_t Comp_Type;
+
+ /* Lookup the token and see if it matches a known filter */
+ Comp_Type = Get_Decomp_Type( Token, Length );
+
+ if( Comp_Type != FILE_COMPRESSION_TYPE_NONE )
+ {
+ /* Check if we've found one already. Indicate cascading if we did. */
+ if( SessionPtr->Decomp_Type != FILE_COMPRESSION_TYPE_NONE )
+ {
+ File_Decomp_Alert( SessionPtr, HI_SERVER_PDF_CASC_COMP );
+ SessionPtr->Decomp_Type = FILE_COMPRESSION_TYPE_NONE;
+ }
+ else
+ {
+ /* Found our first matching, supported filter type */
+ SessionPtr->Decomp_Type = Comp_Type;
+ SessionPtr->Decomp_State.PDF.Decomp_Type = Comp_Type;
+ }
+ }
+ else
+ {
+ File_Decomp_Alert( SessionPtr, HI_SERVER_PDF_UNSUP_COMP_TYPE );
+ SessionPtr->Decomp_Type = FILE_COMPRESSION_TYPE_NONE;
+ }
+}
+
+/* Parse the buffered Filter_Spec and create a stream decompression
+ mode and/or event alerts. Return File_Decomp_OK if successfui.
+ Return File_Decomp_Error for a parsing error. */
+static fd_status_t Process_Filter_Spec( fd_session_p_t SessionPtr )
+{
+ /* The following string contains CHR_ARRAY_OPEN, CHR_ARRAY_CLOSE,
+ and CHR_NAME_SEP. */
+ const uint8_t Delim_Str[] = { "\011\012\014\015\040/[]" };
+ bool Found_Array = false;
+ bool Found_Token = false;
+ uint8_t *Filter;
+ uint8_t Length;
+ uint8_t c;
+ int Index;
+
+ fd_status_t Ret_Code = File_Decomp_OK;
+ fd_PDF_Parse_p_t p = &(SessionPtr->Decomp_State.PDF.Parse);
+
+ /* Assume the 'no compression' result */
+ SessionPtr->Decomp_Type = FILE_COMPRESSION_TYPE_NONE;
+ Filter = NULL;
+ Length = 0;
+
+ for( Index=0; Index<p->Filter_Spec_Index; Index++ )
+ {
+ c = p->Filter_Spec_Buf[Index];
+
+ if( (c == 0) || (strchr( (char *)Delim_Str, (int)c ) != 0) )
+ {
+ if( c == CHR_ARRAY_OPEN )
+ {
+ /* Looks like an array starting, but we are already
+ in an array, or have seen a filter spec already. */
+ if( Found_Array || Found_Token || (Filter != NULL) )
+ {
+ Ret_Code = File_Decomp_Error;
+ break;
+ }
+ else
+ {
+ Found_Array = true;
+ Filter = NULL;
+ Length = 0;
+ continue; // Nothing else to do, goto next char
+ }
+ }
+ else if( c == CHR_ARRAY_CLOSE )
+ {
+ /* We MUST have an array open at this point. */
+ if( !Found_Array )
+ {
+ Ret_Code = File_Decomp_Error;
+ break;
+ }
+ Found_Array = false;
+ }
+
+ /* The white-space or other separator terminates the
+ current filter name we are parsing. */
+ if( (Filter != NULL) && (Length > 0) )
+ {
+ Process_One_Filter( SessionPtr, Filter, Length );
+ Filter = NULL;
+ Length = 0;
+ }
+ }
+ else // non-separator character
+ {
+ /* Start a token if we haven't already. */
+ if( Filter == NULL )
+ {
+ Found_Token = true; // Used in the array syntax checking
+ Filter = &(p->Filter_Spec_Buf[Index]);
+ Length = 1; // We've found one character so far
+ }
+ else
+ {
+ Length += 1;
+ }
+ }
+ }
+
+ /* Indicate an error is we exit the parsing with the array open */
+ if( Found_Array )
+ Ret_Code = File_Decomp_Error;
+
+ /* Any error code implies no compression type */
+ if( Ret_Code == File_Decomp_Error )
+ SessionPtr->Decomp_Type = FILE_COMPRESSION_TYPE_NONE;
+ /* Look for case where the filter name ends at the
+ last character of the filter_spec. */
+ else if( (Filter != NULL) && (Length > 0) )
+ Process_One_Filter( SessionPtr, Filter, Length );
+
+ return( Ret_Code );
+}
+
+
+static inline void Init_Parser( fd_session_p_t SessionPtr )
+{
+ fd_PDF_Parse_p_t p = &(SessionPtr->Decomp_State.PDF.Parse);
+ /* The parser starts in the P_COMMENT state we start
+ parsing the file just after the signature is located
+ and the signature is syntactially a comment. */
+ p->State = P_COMMENT;
+ p->Parse_Stack_Index = 0; // Stack is empty
+}
+
+static inline fd_status_t Push_State( fd_PDF_Parse_p_t p )
+{
+ fd_PDF_Parse_Stack_p_t StckPtr;
+
+ if( p->Parse_Stack_Index >= (PARSE_STACK_LEN-1) )
+ return( File_Decomp_Error );
+
+ StckPtr = &(p->Parse_Stack[(p->Parse_Stack_Index)++]);
+
+ StckPtr->State = p->State;
+ StckPtr->Sub_State = p->Sub_State;
+
+ return( File_Decomp_OK );
+}
+
+static inline fd_status_t Pop_State( fd_PDF_Parse_p_t p )
+{
+ fd_PDF_Parse_Stack_p_t StckPtr;
+
+ if( p->Parse_Stack_Index == 0 )
+ return( File_Decomp_Error );
+
+ StckPtr = &(p->Parse_Stack[--(p->Parse_Stack_Index)]);
+
+ p->Elem_Index = 0; // Reset to beginning of token as can't push/pop in mid-token
+ p->State = StckPtr->State;
+ p->Sub_State = StckPtr->Sub_State;
+
+ return( File_Decomp_OK );
+}
+
+/* If there's a previous state on the stack, return a pointer to it, else return NULL */
+static inline fd_PDF_Parse_Stack_p_t Get_Previous_State( fd_PDF_Parse_p_t p )
+{
+ if( p->Parse_Stack_Index == 0 )
+ return( (fd_PDF_Parse_Stack_p_t)NULL );
+
+ return( &(p->Parse_Stack[(p->Parse_Stack_Index)-1]) );
+}
+
+/* Objects are the heart and soul of the PDF. In particular, we need to concentrate on Dictionary
+ objects and objects that map to the Filter element in Dictionaries. 'null' is a valid object'.
+ Objects can be recursively composed of arrays of objects. In our limited parsing paradigm, we
+ will only process the contents of top level Dictionaries and ignore deeper levels. We will
+ only explore Dictionary objects within Indirect Objects. */
+static inline fd_status_t Handle_State_DICT_OBJECT( fd_session_p_t SessionPtr, uint8_t c )
+{
+ char Filter_Tok[] = TOK_DICT_FILT;
+ fd_PDF_Parse_p_t p = &(SessionPtr->Decomp_State.PDF.Parse);
+
+ /* enter with c being an EOL from the ind obj state */
+ if( p->State != P_DICT_OBJECT )
+ {
+ p->Sub_State = P_DICT_OPEN; // Looking to open a Dict`
+ p->Dict_Nesting_Cnt = 0; // No Dicts are 'active'
+ p->State = P_DICT_OBJECT;
+ p->Filter_Spec_Index = 0;
+ SessionPtr->Decomp_Type = FILE_COMPRESSION_TYPE_NONE;
+ return( File_Decomp_OK );
+ }
+
+ switch( p->Sub_State )
+ {
+ /* look for the first angle bracket */
+ case( P_DICT_OPEN ):
+ {
+ if( c == CHR_ANGLE_OPEN )
+ {
+ p->Sub_State = P_DICT_OPEN_TOK;
+ }
+ else if( !IS_WHITESPACE(c) )
+ {
+ /* for other objects, just skip and wait for the close of the
+ indirect object as we don't parse objects other than Dict's. */
+ if( Pop_State( p ) == File_Decomp_Error )
+ return( File_Decomp_Error );
+ }
+ break;
+ }
+ /* now look for the second angle bracket */
+ case( P_DICT_OPEN_TOK ):
+ {
+ if( c == CHR_ANGLE_OPEN )
+ {
+ /* Only ACTIVE if this is the opening of the
+ 'base level' Dict, NOT a nested one. */
+ if( p->Dict_Nesting_Cnt++ == 0 )
+ {
+ p->Sub_State = P_DICT_ACTIVE;
+ }
+ else
+ {
+ p->Sub_State = P_DICT_SKIP;
+ }
+ }
+ else
+ {
+ /* for other objects, just skip and wait for the close of the
+ indirect object as we don't parse objects other than Dict's. */
+ if( Pop_State( p ) == File_Decomp_Error )
+ return( File_Decomp_Error );
+ }
+ break;
+ }
+
+ case( P_DICT_SKIP ):
+ case( P_DICT_ACTIVE ):
+ {
+ /* Main purpose is to search for the value portion of the
+ /Filter entry. Main loop looks for the /Filter token
+ and handles other diversion such as nested Dict objects.
+ If the /Filter token doesn't exist then we don't fill the
+ Filter_Spec_Buf[]. If in skip mode, no need to look for token. */
+ if( (p->Sub_State == P_DICT_ACTIVE) && c == Filter_Tok[p->Elem_Index++] )
+ {
+ if( Filter_Tok[p->Elem_Index] == '\0' )
+ {
+ p->Sub_State = P_DICT_FILTER;
+ }
+ }
+ else
+ {
+ /* On a mis-match, reset back to the start of the token */
+ p->Elem_Index = 0;
+
+ /* we might find a Sub-Dict while we're looking */
+ if( c == CHR_ANGLE_OPEN )
+ {
+ /* Save where we are, and process the Dict */
+ if( Push_State( p ) != File_Decomp_OK )
+ return( File_Decomp_Error );
+ p->Sub_State = P_DICT_OPEN_TOK;
+ }
+ else if( c == CHR_ANGLE_CLOSE )
+ {
+ if( Push_State( p ) != File_Decomp_OK )
+ return( File_Decomp_Error );
+ p->Sub_State = P_DICT_CLOSE_TOK;
+ }
+ }
+ break;
+ }
+
+ case( P_DICT_FILTER ):
+ {
+ if( (c == CHR_ANGLE_CLOSE) ||
+ ((c == CHR_NAME_SEP) && (p->Dict_Nesting_Cnt==3) )) // See the large comment below
+ {
+ if( c == CHR_ANGLE_CLOSE )
+ {
+ if( Push_State( p ) != File_Decomp_OK )
+ return( File_Decomp_Error );
+ p->Sub_State = P_DICT_CLOSE_TOK;
+ }
+ else
+ {
+ p->Sub_State = P_DICT_SKIP;
+ }
+ if( (Process_Filter_Spec( SessionPtr ) == File_Decomp_Error) )
+ return( File_Decomp_Error );
+ }
+ else
+ {
+ /* Since we don't have a full object parse, we need to assure
+ that we capture the entire filter spec string. The '>' is always
+ a terminator, but we also want to terminate on the next /Name entry
+ after a possible array of /Names. The Dict_Nesting_Cnt is used to
+ step through the transition options. The '/' character is only a valid
+ filter spec terminator if we've seen a valid array or one /Name entry. */
+ if( (c == CHR_NAME_SEP) && (p->Dict_Nesting_Cnt==1) )
+ p->Dict_Nesting_Cnt = 3;
+ else if( (c == CHR_ARRAY_OPEN) && (p->Dict_Nesting_Cnt==1) )
+ p->Dict_Nesting_Cnt = 2;
+ else if( (c == CHR_ARRAY_CLOSE) && (p->Dict_Nesting_Cnt==2) )
+ p->Dict_Nesting_Cnt = 3;
+
+ if( p->Filter_Spec_Index < (FILTER_SPEC_BUF_LEN-1) )
+ {
+ p->Filter_Spec_Buf[p->Filter_Spec_Index++] = c;
+ }
+ else
+ return( File_Decomp_Error );
+ }
+ break;
+ }
+
+ case( P_DICT_CLOSE_TOK ):
+ {
+ if( c == CHR_ANGLE_CLOSE )
+ {
+ /* Pop the temp state just prior to the first > */
+ if( Pop_State( p ) == File_Decomp_Error )
+ return( File_Decomp_Error );
+
+ /* Pop back to the state before the <<. */
+ /* But not so fast... Look at what state/sub-state we are popping
+ back to. If it's IND_OBJ, AND we have an active filter type,
+ we don't want to scan to the end of the stream but rather the beginning
+ of the stream. */
+ if( SessionPtr->Decomp_Type != FILE_COMPRESSION_TYPE_NONE )
+ {
+ fd_PDF_Parse_Stack_p_t StckPtr;
+
+ if( (StckPtr = Get_Previous_State( p )) == NULL )
+ {
+ /* There MUST be a previous state that got us here. */
+ return( File_Decomp_Error );
+ }
+ else
+ {
+ if( (StckPtr->State == P_IND_OBJ) &&
+ (StckPtr->Sub_State == P_ENDOBJ_TOKEN) )
+ {
+ StckPtr->Sub_State = P_STREAM_TOKEN;
+ }
+ }
+ }
+ if( Pop_State( p ) == File_Decomp_Error )
+ return( File_Decomp_Error );
+ }
+ else
+ /* Return to where we looking (didn't get >>) */
+ if( Pop_State( p ) == File_Decomp_Error )
+ return( File_Decomp_Error );
+ break;
+ }
+
+ default:
+ return( File_Decomp_Error );
+ }
+
+ return( File_Decomp_OK );
+}
+
+static inline fd_status_t Process_Stream( fd_PDF_Parse_p_t p )
+{
+ p->Sub_State = P_ENDSTREAM_TOKEN;
+ p->State = P_IND_OBJ;
+
+ if( Push_State( p ) == File_Decomp_Error )
+ return( File_Decomp_Error );
+ else
+ {
+ p->State = P_STREAM;
+ p->Sub_State = 0;
+ }
+ return( File_Decomp_OK );
+}
+
+/* Indirect Objects occur only at the top level of the file and comprise the
+ bulk of the file content. */
+static inline fd_status_t Handle_State_IND_OBJ( fd_session_p_t SessionPtr, uint8_t c )
+{
+ static uint8_t Ind_Obj_Token[] = { TOK_OBJ_OPEN };
+ static uint8_t Ind_Obj_End_Token[] = { TOK_OBJ_CLOSE };
+ static uint8_t Stream_Token[] = { TOK_STRM_OPEN };
+ static uint8_t Stream_End_Token[] = { TOK_STRM_CLOSE };
+ fd_PDF_Parse_p_t p = &(SessionPtr->Decomp_State.PDF.Parse);
+
+ /* Upon initial entry, setup state context */
+ if( p->State != P_IND_OBJ )
+ {
+ p->State = P_IND_OBJ;
+ p->Sub_State = P_OBJ_NUMBER;
+ p->Elem_Index = 1;
+ p->Elem_Buf[0] = c;
+ return( File_Decomp_OK );
+ }
+
+ switch( p->Sub_State )
+ {
+ case( P_OBJ_NUMBER ):
+ case( P_GEN_NUMBER ):
+ {
+ if( isdigit( c ) )
+ {
+ if( p->Elem_Index < (sizeof(p->Elem_Buf)-1))
+ {
+ p->Elem_Buf[p->Elem_Index++] = c;
+ }
+ else
+ return( File_Decomp_Error );
+ }
+ else if( c == CHR_SPACE )
+ {
+ uint32_t Value;
+ p->Elem_Buf[p->Elem_Index] = '\0';
+ Value = (uint32_t)strtoul( (const char *)p->Elem_Buf, NULL, 10 );
+ if( p->Sub_State == P_OBJ_NUMBER )
+ {
+ p->Obj_Number = Value;
+ p->Sub_State = P_GEN_NUMBER;
+ p->Elem_Index = 0;
+ }
+ else
+ {
+ p->Gen_Number = Value;
+ p->Sub_State = P_OBJ_TOKEN;
+ p->Elem_Index = 0;
+ }
+ }
+ break;
+ }
+
+ case( P_OBJ_TOKEN ):
+ {
+ if( c == Ind_Obj_Token[p->Elem_Index++] )
+ {
+ if( Ind_Obj_Token[p->Elem_Index] == '\0' )
+ {
+ p->Sub_State = P_OBJ_EOL;
+ break;
+ }
+ }
+ else
+ {
+ return( File_Decomp_Error );
+ }
+ }
+
+ case( P_OBJ_EOL ):
+ {
+ if( IS_EOL(c) )
+ {
+ DEBUG_WRAP(DebugMessage(DEBUG_HTTPINSPECT, "Indirect Object: objnum: %u\n", p->Obj_Number););
+ p->Sub_State = P_ENDOBJ_TOKEN;
+ /* Save our place in the IND_OBJ and go process an OBJECT */
+ if( Push_State( p ) != File_Decomp_OK )
+ return( File_Decomp_Error );
+ return( Handle_State_DICT_OBJECT( SessionPtr, c ) );
+ }
+
+ break;
+ }
+
+
+ case( P_STREAM_TOKEN ):
+ {
+ if( c == Stream_Token[p->Elem_Index++] )
+ {
+ if( Stream_Token[p->Elem_Index] == '\0' )
+ {
+ /* Look for the limited EOL sequence */
+ p->Sub_State = P_STREAM_EOL;
+ }
+ break;
+ }
+ else if( IS_WHITESPACE(c) )
+ {
+ p->Elem_Index = 0; // reset and keep looking
+ }
+ else
+ return( File_Decomp_Error );
+
+ break;
+ }
+
+ case( P_STREAM_EOL ):
+ {
+ if( c == CHR_CR )
+ {
+ /* The next char MUST be a LF or error */
+ p->Sub_State = P_STREAM_LF;
+ }
+ else if( c == CHR_LF )
+ {
+ if( Process_Stream( p ) != File_Decomp_OK )
+ return( File_Decomp_Error );
+ }
+ else
+ return( File_Decomp_Error );
+
+ break;
+ }
+
+ case( P_STREAM_LF ):
+ {
+ if( c == CHR_LF )
+ {
+ if( Process_Stream( p ) != File_Decomp_OK )
+ return( File_Decomp_Error );
+ }
+ else
+ return( File_Decomp_Error );
+ break;
+ }
+
+ case( P_ENDSTREAM_TOKEN ):
+ {
+ if( c == Stream_End_Token[p->Elem_Index++] )
+ {
+ if( Stream_End_Token[p->Elem_Index] == '\0' )
+ {
+ p->Sub_State = P_ENDOBJ_TOKEN;
+ }
+ }
+ else
+ {
+ p->Elem_Index = 0; // reset and keep looking
+ }
+
+ break;
+ }
+
+ case( P_ENDOBJ_TOKEN ):
+ {
+ if( c == Ind_Obj_End_Token[p->Elem_Index++] )
+ {
+ if( Ind_Obj_End_Token[p->Elem_Index] == '\0' )
+ {
+ /* we found the end of the indirect object, return
+ back to the parent state (always START in this case) */
+ return( Pop_State( p ) );
+ }
+ }
+ else
+ {
+ /* Since we don't necessarily handle all object types correctly,
+ we will spin here searching for the end token. Not the best,
+ but should work if we don't have a full object parser. */
+ p->Elem_Index = 0; // reset and keep looking
+
+ }
+
+ break;
+ }
+
+ default:
+ return( File_Decomp_Error );
+ }
+
+ return( File_Decomp_OK );
+}
+
+/* A simple state machine to process the xref/trailer/startxref file segments. No
+ semantic processing and only rough syntactical processing to allow us to skip through
+ this segment. */
+static inline fd_status_t Handle_State_XREF( fd_session_p_t SessionPtr, uint8_t c )
+{
+ static uint8_t *Xref_Tok;
+ uint8_t Xref_End_Tok[] = { TOK_XRF_END };
+ fd_PDF_Parse_p_t p = &(SessionPtr->Decomp_State.PDF.Parse);
+
+ if( p->State != P_XREF )
+ {
+ p->Sub_State = P_XREF_TOKEN;
+ p->Elem_Index = 1; // Aready matched the first char in START state
+ p->State = P_XREF;
+ Xref_Tok = (uint8_t *)((c == TOK_XRF_XREF[0]) ? TOK_XRF_XREF : TOK_XRF_STARTXREF);
+ return( File_Decomp_OK );
+ }
+
+ switch( p->Sub_State )
+ {
+ case( P_XREF_TOKEN ):
+ {
+ if( c == Xref_Tok[p->Elem_Index++] )
+ {
+ if( Xref_Tok[p->Elem_Index] == '\0' )
+ {
+ p->Elem_Index = 0;
+ p->Sub_State = P_XREF_END_TOKEN;
+ }
+ }
+ else
+ {
+ return( File_Decomp_Error );
+ }
+ break;
+ }
+
+ case( P_XREF_END_TOKEN ):
+ {
+ if( c == Xref_End_Tok[p->Elem_Index++] )
+ {
+ if( Xref_End_Tok[p->Elem_Index] == '\0' )
+ {
+ p->State = P_START;
+ }
+ }
+ else
+ {
+ /* Since we don't necessarily handle all xref content correctly,
+ we will spin here searching for the end token. Not the best,
+ but should work if we don't have a full object parser. */
+ p->Elem_Index = 0; // reset and keep looking
+
+ }
+
+ break;
+ }
+
+ default:
+ return( File_Decomp_Error );
+ }
+
+ return( File_Decomp_OK );
+}
+
+static inline fd_status_t Handle_State_START( fd_session_p_t SessionPtr, uint8_t c )
+{
+ fd_PDF_Parse_p_t p = &(SessionPtr->Decomp_State.PDF.Parse);
+ /* Skip any whitespace. This will include
+ the LF as part of a <CRLF> EOL token. */
+ if( IS_WHITESPACE(c) )
+ {
+ return( File_Decomp_OK );
+ }
+ if( c == CHR_COMMENT )
+ {
+ p->State = P_COMMENT;
+ }
+ else if( isdigit( c ) )
+ {
+ /* Save state and process an indirect object */
+ if( Push_State( p ) != File_Decomp_OK )
+ return( File_Decomp_Error );
+ return( Handle_State_IND_OBJ( SessionPtr, c ) );
+ }
+ else if( (c == TOK_XRF_XREF[0]) || (c == TOK_XRF_STARTXREF[0]) )
+ {
+ /* Save state and process the xref block */
+ if( Push_State( p ) != File_Decomp_OK )
+ return( File_Decomp_Error );
+ return( Handle_State_XREF( SessionPtr, c ) );
+ }
+ else if( !(IS_WHITESPACE(c)) )
+ {
+ /* If is not an ind_obj started, or a comment starting, then
+ we don't know what it is, so return an error. */
+ return( File_Decomp_Error );
+ }
+
+ return( File_Decomp_OK );
+}
+
+/* Incrementally search the incoming data for a PDF compressed stream
+ (of the type that we can decompress). Move bytes to outgoing data
+ up to the beginning of the compressed segment. If the FILE_REVERT_BIT
+ is set in the Session, remove the /Filter spec that was located by
+ replacing the name with null. */
+
+/* Parse file until input blocked or stream located. */
+static fd_status_t Locate_Stream_Beginning( fd_session_p_t SessionPtr )
+{
+ fd_PDF_Parse_p_t p = &(SessionPtr->Decomp_State.PDF.Parse);
+ fd_status_t Ret_Code = File_Decomp_OK;
+ uint8_t c;
+
+ while( 1 )
+ {
+ /* No reason to parse if there's no input or
+ room for output. */
+ if( SessionPtr->Avail_In == 0 )
+ return( File_Decomp_BlockIn );
+ if( SessionPtr->Avail_Out == 0 )
+ return( File_Decomp_BlockOut );
+
+ /* Get next byte in input queue */
+ c = *SessionPtr->Next_In;
+
+ switch( p->State )
+ {
+ /* The 'ground' state of the parser. All indirect objects
+ should be located at this level. */
+ case( P_START ):
+ {
+ if( (Ret_Code = Handle_State_START( SessionPtr, c )) != File_Decomp_OK )
+ return( Ret_Code );
+ break;
+ }
+
+ case( P_COMMENT ):
+ {
+ /* CR or LF closes the comment. The optional LF
+ after a CR will be considered whitespace and
+ removed in the P_START state. */
+ if( IS_EOL(c) )
+ p->State = P_START;
+ break;
+ }
+
+ case( P_IND_OBJ ):
+ {
+ if( (Ret_Code = Handle_State_IND_OBJ( SessionPtr, c )) != File_Decomp_OK )
+ return( Ret_Code );
+ break;
+ }
+
+ case( P_DICT_OBJECT ):
+ {
+ if( (Ret_Code = Handle_State_DICT_OBJECT( SessionPtr, c )) != File_Decomp_OK )
+ return( Ret_Code );
+ break;
+ }
+
+ case( P_XREF ):
+ {
+ if( (Ret_Code = Handle_State_XREF( SessionPtr, c )) != File_Decomp_OK )
+ return( Ret_Code );
+ break;
+ }
+
+ case( P_STREAM ):
+ {
+ return( File_Decomp_Complete );
+ }
+
+ default:
+ return( File_Decomp_Error );
+ }
+ /* After parsing, move the byte from the input to the
+ output stream. We can only be here if there's input
+ available and output space. */
+ (void)Move_1(SessionPtr);
+ }
+}
+
+static fd_status_t Init_Stream( fd_session_p_t SessionPtr )
+{
+ fd_PDF_p_t StPtr = &(SessionPtr->Decomp_State.PDF);
+
+ switch( StPtr->Decomp_Type )
+ {
+ case FILE_COMPRESSION_TYPE_DEFLATE:
+ {
+ int z_ret;
+
+ z_stream *z_s = &(StPtr->PDF_Decomp_State.Deflate.StreamDeflate);
+
+ memset( (char *)z_s, 0, sizeof(z_stream));
+
+ z_s->zalloc = (alloc_func)NULL;
+ z_s->zfree = (free_func)NULL;
+ SYNC_IN(z_s)
+
+ z_ret = inflateInit2(z_s, 47);
+
+ if( z_ret != Z_OK )
+ {
+ File_Decomp_Alert( SessionPtr, HI_SERVER_PDF_DEFL_FAILURE );
+ return( File_Decomp_Error );
+ }
+
+ break;
+ }
+ default:
+ return( File_Decomp_Error );
+ }
+
+ return( File_Decomp_OK );
+}
+
+static fd_status_t Decomp_Stream( fd_session_p_t SessionPtr )
+{
+ fd_PDF_p_t StPtr = &(SessionPtr->Decomp_State.PDF);
+
+ /* No reason to decompress if there's no input or
+ room for output. */
+ if( SessionPtr->Avail_In == 0 )
+ return( File_Decomp_BlockIn );
+ if( SessionPtr->Avail_Out == 0 )
+ return( File_Decomp_BlockOut );
+
+ switch( StPtr->Decomp_Type )
+ {
+ case FILE_COMPRESSION_TYPE_DEFLATE:
+ {
+ int z_ret;
+ z_stream *z_s = &(StPtr->PDF_Decomp_State.Deflate.StreamDeflate);
+
+ SYNC_IN(z_s)
+
+ z_ret = inflate(z_s, Z_SYNC_FLUSH);
+
+ SYNC_OUT(z_s)
+
+ if( z_ret == Z_STREAM_END )
+ {
+ return( File_Decomp_Complete );
+ }
+
+ if( z_ret != Z_OK )
+ {
+ DEBUG_WRAP(DebugMessage(DEBUG_HTTPINSPECT, "Decompression Error: objnum: %u\n", StPtr->Parse.Obj_Number););
+ File_Decomp_Alert( SessionPtr, HI_SERVER_PDF_DEFL_FAILURE );
+ return( File_Decomp_Error );
+ }
+
+ break;
+ }
+ default:
+ return( File_Decomp_Error );
+ }
+
+ return( File_Decomp_OK );
+}
+
+/* After processing a stream, close the decompession engine
+ and return the state of the parser. */
+static fd_status_t Close_Stream( fd_session_p_t SessionPtr )
+{
+ /* Put the parser state back where it was interrupted */
+ if( Pop_State( &(SessionPtr->Decomp_State.PDF.Parse) ) == File_Decomp_Error )
+ return( File_Decomp_Error );
+
+ SessionPtr->Decomp_State.PDF.State = PDF_STATE_LOCATE_STREAM;
+
+ return( File_Decomp_OK );
+}
+
+/* Abort the decompression session upon command from caller. */
+fd_status_t File_Decomp_End_PDF( fd_session_p_t SessionPtr )
+{
+ fd_PDF_p_t StPtr;
+
+ if( SessionPtr == NULL )
+ return( File_Decomp_Error );
+
+ StPtr = &(SessionPtr->Decomp_State.PDF);
+
+ if( (StPtr->State != PDF_STATE_INIT_STREAM) &&
+ (StPtr->State != PDF_STATE_PROCESS_STREAM) )
+ return( File_Decomp_OK );
+
+ switch( StPtr->Decomp_Type )
+ {
+ case FILE_COMPRESSION_TYPE_DEFLATE:
+ {
+ int z_ret;
+ z_stream *z_s = &(StPtr->PDF_Decomp_State.Deflate.StreamDeflate);
+
+ z_ret = inflateEnd(z_s);
+
+ if( z_ret != Z_OK )
+ {
+ File_Decomp_Alert( SessionPtr, HI_SERVER_PDF_DEFL_FAILURE );
+ return( File_Decomp_Error );
+ }
+
+ break;
+ }
+ default:
+ return( File_Decomp_Error );
+ }
+
+ return( File_Decomp_OK );
+}
+
+
+/* From caller, initialize PDF state machine. */
+fd_status_t File_Decomp_Init_PDF( fd_session_p_t SessionPtr )
+{
+ fd_PDF_p_t StPtr;
+
+ if( SessionPtr == NULL )
+ return( File_Decomp_Error );
+
+ StPtr = &(SessionPtr->Decomp_State.PDF);
+
+ Init_Parser( SessionPtr );
+
+ StPtr->Decomp_Type = FILE_COMPRESSION_TYPE_NONE;
+
+ /* Search for Dictionary/Stream object. */
+ StPtr->State = PDF_STATE_LOCATE_STREAM;
+
+ return( File_Decomp_OK );
+}
+
+/* Run the PDF state machine */
+fd_status_t File_Decomp_PDF( fd_session_p_t SessionPtr )
+{
+ fd_status_t Ret_Code;
+
+ if( (SessionPtr == NULL) || (SessionPtr->File_Type != FILE_TYPE_PDF) )
+ return( File_Decomp_Error );
+
+ /* Process all data until blocked */
+ while( 1 )
+ {
+ switch( SessionPtr->Decomp_State.PDF.State )
+ {
+ case( PDF_STATE_LOCATE_STREAM ):
+ {
+ /* Will return File_Decomp_Complete if/when the start of a valid compressed
+ stream is located. Decomp_Type will be set. The parsing will be suspended. */
+ if( (Ret_Code = Locate_Stream_Beginning( SessionPtr ) ) == File_Decomp_Error)
+ {
+ SessionPtr->Error_Event = HI_SERVER_PDF_PARSE_FAILURE;
+ return( File_Decomp_DecompError );
+ }
+
+ /* If we didn't succeed then get more input */
+ if( Ret_Code != File_Decomp_Complete )
+ return( Ret_Code );
+
+ /* The Parsing state remains, we break out to perform the stream
+ decompression. */
+ if( SessionPtr->Decomp_Type == FILE_COMPRESSION_TYPE_NONE )
+ {
+ break;
+ }
+ else
+ {
+ DEBUG_WRAP(DebugMessage(DEBUG_HTTPINSPECT, "Compressed stream of type: %u\n", SessionPtr->Decomp_Type););
+ SessionPtr->Decomp_State.PDF.State = PDF_STATE_INIT_STREAM;
+ /* If we've located the beginning of stream, set new state
+ and fall into next state */
+ }
+ }
+
+ case( PDF_STATE_INIT_STREAM ):
+ {
+ /* Initialize the selected decompression engine. */
+ Ret_Code = Init_Stream( SessionPtr );
+ if( Ret_Code != File_Decomp_OK )
+ {
+ Ret_Code = File_Decomp_End_PDF( SessionPtr );
+ if( Close_Stream( SessionPtr ) != File_Decomp_OK )
+ return( File_Decomp_Error );
+ File_Decomp_Alert( SessionPtr, HI_SERVER_PDF_DEFL_FAILURE );
+ break;
+ }
+
+ SessionPtr->Decomp_State.PDF.State = PDF_STATE_PROCESS_STREAM;
+ /* INTENTIONAL FALL-THROUGH INTO PDF_STATE_PROCESS_STREAM CASE. */
+ }
+
+ case( PDF_STATE_PROCESS_STREAM ):
+ {
+ Ret_Code = Decomp_Stream( SessionPtr );
+ /* Has the decompressor indicated the end of the data */
+ if( Ret_Code == File_Decomp_Error )
+ {
+ Ret_Code = File_Decomp_End_PDF( SessionPtr );
+ if( Close_Stream( SessionPtr ) != File_Decomp_OK )
+ return( File_Decomp_Error );
+ File_Decomp_Alert( SessionPtr, HI_SERVER_PDF_DEFL_FAILURE );
+ break;
+ }
+ /* OK -> circle back for more input */
+ else if( Ret_Code == File_Decomp_OK )
+ break;
+ else if( Ret_Code != File_Decomp_Complete )
+ return( Ret_Code );
+
+ /* Close the decompression engine */
+ if( (Ret_Code = File_Decomp_End_PDF( SessionPtr ) ) == File_Decomp_Error)
+ return( File_Decomp_Error);
+
+ /* Put the parser state back where it was interrupted */
+ if( (Close_Stream( SessionPtr) ) == File_Decomp_Error )
+ return( File_Decomp_Error );
+
+ break;
+ }
+
+ default:
+ return( File_Decomp_Error );
+ } // switch()
+ } // while()
+
+ return( File_Decomp_OK );
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2015 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2003-2013 Sourcefire, Inc.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+#ifndef HI_FILE_DECOMP_PDF_H
+#define HI_FILE_DECOMP_PDF_H
+
+#include <zlib.h>
+
+#define ELEM_BUF_LEN (12)
+#define FILTER_SPEC_BUF_LEN (40)
+#define PARSE_STACK_LEN (12)
+
+typedef enum pdf_states
+{
+ PDF_STATE_NEW,
+ PDF_STATE_LOCATE_STREAM, /* Found sig bytes, looking for dictionary & stream*/
+ PDF_STATE_INIT_STREAM, /* Init stream */
+ PDF_STATE_PROCESS_STREAM /* Processing stream */
+} fd_PDF_States;
+
+typedef struct fd_PDF_Parse_Stack_s
+{
+ uint8_t State;
+ uint8_t Sub_State;
+} fd_PDF_Parse_Stack_t, *fd_PDF_Parse_Stack_p_t;
+
+typedef struct fd_PDF_Parse_s
+{
+ uint8_t Dict_Nesting_Cnt;
+ uint8_t Elem_Buf[ELEM_BUF_LEN];
+ uint8_t Elem_Index;
+ uint8_t Filter_Spec_Buf[FILTER_SPEC_BUF_LEN+1];
+ uint8_t Filter_Spec_Index;
+ fd_PDF_Parse_Stack_t Parse_Stack[PARSE_STACK_LEN];
+ uint8_t Parse_Stack_Index;
+ uint32_t Obj_Number;
+ uint32_t Gen_Number;
+ uint8_t Sub_State;
+ uint8_t State;
+} fd_PDF_Parse_t, *fd_PDF_Parse_p_t;
+
+typedef struct fd_PDF_Deflate_s
+{
+ z_stream StreamDeflate;
+} fd_PDF_Deflate_t;
+
+typedef struct fd_PDF_s
+{
+ union
+ {
+ fd_PDF_Deflate_t Deflate;
+ } PDF_Decomp_State;
+ fd_PDF_Parse_t Parse;
+ uint8_t Decomp_Type;
+ uint8_t State;
+} fd_PDF_t, *fd_PDF_p_t;
+
+/* API Functions */
+
+fd_status_t File_Decomp_Init_PDF( fd_session_p_t SessionPtr );
+
+fd_status_t File_Decomp_End_PDF( fd_session_p_t SessionPtr );
+
+fd_status_t File_Decomp_PDF(fd_session_p_t SessionPtr);
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2015 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <zlib.h>
+#include <string.h>
+
+#ifdef LZMA
+#include <lzma.h>
+#endif
+
+#include "hi_file_decomp.h"
+#include "hi_file_decomp_swf.h"
+#include "hi_events.h"
+
+#ifdef LZMA
+#define LZMA_HEADER_LEN (13)
+#define LZMA_PRP_OFFSET (0)
+#define SWF_PRP_OFFSET (9)
+#define LZMA_UCL_OFFSET (5)
+#define SWF_UCL_OFFSET (1)
+#define SWF_HDR_LEN (SWF_SIG_LEN + SWF_VER_LEN + SWF_UCL_LEN)
+
+static fd_status_t File_Decomp_Process_LZMA_Header( fd_session_p_t SessionPtr )
+{
+ uint8_t LZMA_Header[LZMA_HEADER_LEN];
+ uint8_t *SWF_Header = SessionPtr->Decomp_State.SWF.Header_Bytes;
+ uint64_t LZMA_Uncomp_Len;
+ uint32_t SWF_Uncomp_Len;
+ int idx;
+
+ lzma_ret l_ret;
+ lzma_stream *l_s = &(SessionPtr->Decomp_State.SWF.StreamLZMA);
+
+ SWF_Uncomp_Len = 0;
+ /* Read little-endian into value */
+ for( idx=0; idx<4; idx++ )
+ SWF_Uncomp_Len +=
+ (uint32_t)((uint8_t)(*(SWF_Header + SWF_UCL_OFFSET + idx)) << (8*idx) );
+
+ if( SWF_Uncomp_Len < SWF_HDR_LEN )
+ {
+ SessionPtr->Error_Event = HI_SERVER_SWF_LZMA_FAILURE;
+ return( File_Decomp_DecompError );
+ }
+
+ LZMA_Uncomp_Len = (uint64_t)(SWF_Uncomp_Len - (SWF_HDR_LEN));
+
+ /* Write little-endian from value */
+ for( idx=0; idx<8; idx++ )
+ *(LZMA_Header + LZMA_UCL_OFFSET + idx) =
+ (uint8_t)(LZMA_Uncomp_Len & (0xff << idx));
+
+ LZMA_Uncomp_Len = (uint64_t)(SWF_Uncomp_Len - (SWF_HDR_LEN));
+
+ /* Move the LZMA Properties */
+ for( idx=0; idx<SWF_LZMA_PRP_LEN; idx++ )
+ LZMA_Header[LZMA_PRP_OFFSET + idx] = *(SWF_Header + SWF_PRP_OFFSET + idx);
+
+ l_s->next_out = SessionPtr->Next_Out;
+ l_s->avail_out = SessionPtr->Avail_Out;
+ l_s->total_out = SessionPtr->Total_Out;
+
+ l_s->next_in = LZMA_Header;
+ l_s->avail_in = sizeof(LZMA_Header);
+
+ l_ret = lzma_code(l_s, LZMA_RUN);
+
+ SessionPtr->Next_Out = l_s->next_out;
+ SessionPtr->Avail_Out = l_s->avail_out;
+ SessionPtr->Total_Out = l_s->total_out;
+
+ if( l_ret != LZMA_OK )
+ {
+ SessionPtr->Error_Event = HI_SERVER_SWF_LZMA_FAILURE;
+ return( File_Decomp_DecompError );
+ }
+
+ return( File_Decomp_OK );
+}
+#endif
+
+static fd_status_t Decomp( fd_session_p_t SessionPtr )
+{
+ switch( SessionPtr->Decomp_Type )
+ {
+ case FILE_COMPRESSION_TYPE_ZLIB:
+ {
+ int z_ret;
+ z_stream *z_s = &(SessionPtr->Decomp_State.SWF.StreamZLIB);
+
+ SYNC_IN(z_s)
+
+ z_ret = inflate(z_s, Z_SYNC_FLUSH);
+
+ SYNC_OUT(z_s)
+
+ if( z_ret == Z_STREAM_END )
+ {
+ return( File_Decomp_Complete );
+ }
+
+ if( z_ret != Z_OK )
+ {
+ SessionPtr->Error_Event = HI_SERVER_SWF_ZLIB_FAILURE;
+ return( File_Decomp_DecompError );
+ }
+
+
+ break;
+ }
+#ifdef LZMA
+ case FILE_COMPRESSION_TYPE_LZMA:
+ {
+ lzma_ret l_ret;
+ lzma_stream *l_s = &(SessionPtr->Decomp_State.SWF.StreamLZMA);
+
+ SYNC_IN(l_s)
+
+ l_ret = lzma_code(l_s, LZMA_RUN);
+
+ SYNC_OUT(l_s)
+
+ if( l_ret == LZMA_STREAM_END )
+ {
+ return( File_Decomp_Complete );
+ }
+
+ if( l_ret != LZMA_OK )
+ {
+ SessionPtr->Error_Event = HI_SERVER_SWF_LZMA_FAILURE;
+ return( File_Decomp_DecompError );
+ }
+
+ break;
+ }
+#endif
+ default:
+ return( File_Decomp_Error );
+ }
+
+ return( File_Decomp_OK );
+}
+
+fd_status_t File_Decomp_End_SWF( fd_session_p_t SessionPtr )
+{
+ if( SessionPtr == NULL )
+ return( File_Decomp_Error );
+
+ switch( SessionPtr->Decomp_Type )
+ {
+ case FILE_COMPRESSION_TYPE_ZLIB:
+ {
+ int z_ret;
+ z_stream *z_s = &(SessionPtr->Decomp_State.SWF.StreamZLIB);
+
+ z_ret = inflateEnd(z_s);
+
+ if( z_ret != Z_OK )
+ {
+ SessionPtr->Error_Event = HI_SERVER_SWF_ZLIB_FAILURE;
+ return( File_Decomp_DecompError );
+ }
+
+ break;
+ }
+#ifdef LZMA
+ case FILE_COMPRESSION_TYPE_LZMA:
+ {
+ lzma_stream *l_s = &(SessionPtr->Decomp_State.SWF.StreamLZMA);
+
+ lzma_end(l_s);
+
+ break;
+ }
+#endif
+ default:
+ return( File_Decomp_Error );
+ }
+
+ return( File_Decomp_OK );
+}
+
+
+fd_status_t File_Decomp_Init_SWF( fd_session_p_t SessionPtr )
+{
+ if( SessionPtr == NULL )
+ return( File_Decomp_Error );
+
+ /* Indicate the we need to look for the remainder of the
+ uncompressed header. */
+ SessionPtr->Decomp_State.SWF.State = SWF_STATE_GET_HEADER;
+ SessionPtr->Decomp_State.SWF.Header_Cnt = 0;
+
+ switch( SessionPtr->Decomp_Type )
+ {
+ case FILE_COMPRESSION_TYPE_ZLIB:
+ {
+ int z_ret;
+ z_stream *z_s;
+
+ SessionPtr->Decomp_State.SWF.Header_Len =
+ SWF_VER_LEN + SWF_UCL_LEN;
+
+ z_s = &(SessionPtr->Decomp_State.SWF.StreamZLIB);
+
+ memset( (char *)z_s, 0, sizeof(z_stream));
+
+ z_s->zalloc = (alloc_func)NULL;
+ z_s->zfree = (free_func)NULL;
+ SYNC_IN(z_s)
+
+ z_ret = inflateInit(z_s);
+
+ if( z_ret != Z_OK )
+ {
+ SessionPtr->Error_Event = HI_SERVER_SWF_ZLIB_FAILURE;
+ return( File_Decomp_DecompError );
+ }
+
+ break;
+ }
+#ifdef LZMA
+ case FILE_COMPRESSION_TYPE_LZMA:
+ {
+ lzma_ret l_ret;
+ lzma_stream *l_s;
+
+ SessionPtr->Decomp_State.SWF.Header_Len =
+ SWF_VER_LEN + SWF_UCL_LEN + SWF_LZMA_CML_LEN + SWF_LZMA_PRP_LEN;
+
+ l_s = &(SessionPtr->Decomp_State.SWF.StreamLZMA);
+
+ memset( (char *)l_s, 0, sizeof(lzma_stream));
+
+ SYNC_IN(l_s)
+
+ l_ret = lzma_alone_decoder(l_s, UINT64_MAX);
+
+ if( l_ret != LZMA_OK )
+ {
+ SessionPtr->Error_Event = HI_SERVER_SWF_LZMA_FAILURE;
+ return( File_Decomp_DecompError );
+ }
+
+
+ break;
+ }
+#endif
+ default:
+ return( File_Decomp_Error );
+ }
+
+ return( File_Decomp_OK );
+}
+
+fd_status_t File_Decomp_SWF( fd_session_p_t SessionPtr )
+{
+ fd_status_t Ret_Code;
+
+ if( (SessionPtr == NULL) || (SessionPtr->File_Type != FILE_TYPE_SWF) )
+ return( File_Decomp_Error );
+
+ /* Are we still looking for the balance of the uncompressed header? */
+ switch( SessionPtr->Decomp_State.SWF.State )
+ {
+ case( SWF_STATE_GET_HEADER ):
+ {
+ uint8_t *Cnt_Ptr = &(SessionPtr->Decomp_State.SWF.Header_Cnt); // For convenience
+ uint8_t *Len_Ptr = &(SessionPtr->Decomp_State.SWF.Header_Len); // For convenience
+
+ while( *Len_Ptr > *Cnt_Ptr )
+ {
+ if( SessionPtr->Avail_In == 0 )
+ return( File_Decomp_BlockIn );
+
+ if( SessionPtr->Avail_Out == 0 )
+ return( File_Decomp_BlockOut );
+
+ SessionPtr->Decomp_State.SWF.Header_Bytes[*Cnt_Ptr] =
+ *(SessionPtr->Next_In);
+
+ (void)Move_1(SessionPtr);
+ *Cnt_Ptr += 1;
+ }
+
+ SessionPtr->Decomp_State.SWF.State = SWF_STATE_PROC_HEADER;
+ /* INTENTIONAL FALL-THROUGH INTO SWF_STATE_PROC_HEADER CASE. */
+ }
+ case( SWF_STATE_PROC_HEADER ):
+ {
+#ifdef LZMA
+ if( SessionPtr->Decomp_Type == FILE_COMPRESSION_TYPE_LZMA )
+ {
+ Ret_Code = File_Decomp_Process_LZMA_Header( SessionPtr );
+ if( Ret_Code != File_Decomp_OK )
+ return( Ret_Code );
+ }
+#endif
+
+ SessionPtr->Decomp_State.SWF.State = SWF_STATE_DATA;
+ /* INTENTIONAL FALL-THROUGH INTO SWF_STATE_DATA CASE. */
+ }
+ case( SWF_STATE_DATA ):
+ {
+ Ret_Code = Decomp( SessionPtr );
+ if( Ret_Code != File_Decomp_Complete )
+ return( Ret_Code );
+
+ Ret_Code = File_Decomp_End_SWF( SessionPtr );
+ if( Ret_Code != File_Decomp_OK )
+ return( Ret_Code );
+
+ return( File_Decomp_Complete );
+ }
+ default:
+ return( File_Decomp_Error );
+ }
+
+ return( File_Decomp_OK );
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2015 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2003-2013 Sourcefire, Inc.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+#ifndef HI_FILE_DECOMP_SWF_H
+#define HI_FILE_DECOMP_SWF_H
+
+#include <zlib.h>
+#ifdef LZMA
+#include <lzma.h>
+#endif
+
+/* Both ZLIB & LZMA files have an uncompressed eight byte header. The signature is
+ three bytes. The header consists of a three byte sig, a one byte version,
+ and a four byte uncompressed length (little-endian). */
+
+#define SWF_SIG_LEN (3)
+#define SWF_VER_LEN (1)
+#define SWF_UCL_LEN (4)
+
+/* LZMA Files have an additional nine bytes of header prior to the compressed data.
+ This includes a four byte compressed length (little-endian) and five bytes
+ of LZMA properties. */
+#define SWF_LZMA_CML_LEN (4)
+#define SWF_LZMA_PRP_LEN (5)
+
+/* AFTER the sig, the max number of header bytes to fetch.
+ VER+UCL+CML+LZMA_PRP -> 14 bytes. */
+#define SWF_MAX_HEADER (14)
+
+/* Types */
+
+typedef enum swf_states
+{
+ SWF_STATE_NEW,
+ SWF_STATE_GET_HEADER, /* Found sig bytes, looking for end of uncomp header */
+ SWF_STATE_PROC_HEADER, /* Found header bytes, now process the header */
+ SWF_STATE_DATA /* Done with header, looking for start of data */
+} fd_SWF_States;
+
+typedef struct fd_SWF_s
+{
+ z_stream StreamZLIB;
+#ifdef LZMA
+ lzma_stream StreamLZMA;
+#endif
+ uint8_t Header_Bytes[SWF_MAX_HEADER];
+ uint8_t State;
+ uint8_t Header_Len;
+ uint8_t Header_Cnt;
+} fd_SWF_t;
+
+/* API Functions */
+
+fd_status_t File_Decomp_Init_SWF( fd_session_p_t SessionPtr );
+
+fd_status_t File_Decomp_SWF( fd_session_p_t SessionPtr );
+
+fd_status_t File_Decomp_End_SWF( fd_session_p_t SessionPtr );
+
+#endif
HttpFlowData::~HttpFlowData()
{
- FreeHttpsessionData(&session);
+ FreeHttpSessionData(&session);
}
-HttpsessionData* SetNewHttpsessionData(Packet *p, void*)
+HttpSessionData* SetNewHttpSessionData(Packet *p, void*)
{
HttpFlowData* fd = new HttpFlowData;
p->flow->set_application_data(fd);
return &fd->session;
}
-static HttpsessionData* get_session_data(Flow* flow)
+static HttpSessionData* get_session_data(Flow* flow)
{
HttpFlowData* fd = (HttpFlowData*)flow->get_application_data(
HttpFlowData::flow_id);
xtra_jsnorm_id = stream.reg_xtra_data_cb(GetHttpJSNormData);
}
+static void PrintFileDecompOpt(HTTPINSPECT_CONF *ServerConf)
+{
+ LogMessage(" Decompress response files: %s %s %s\n",
+ ((ServerConf->file_decomp_modes & FILE_SWF_ZLIB_BIT) != 0) ? "SWF-ZLIB" : "",
+ ((ServerConf->file_decomp_modes & FILE_SWF_LZMA_BIT) != 0) ? "SWF-LZMA" : "",
+ ((ServerConf->file_decomp_modes & FILE_PDF_DEFL_BIT) != 0) ? "PDF-DEFL" : "");
+}
+
static int PrintConfOpt(HTTPINSPECT_CONF_OPT *ConfOpt, const char *Option)
{
if(!ConfOpt || !Option)
ServerConf->log_hostname ? "YES" : "NO");
LogMessage(" Extract Gzip from responses: %s\n",
ServerConf->extract_gzip ? "YES" : "NO");
+ PrintFileDecompOpt(ServerConf);
PrintConfOpt(&ServerConf->ascii, "Ascii");
PrintConfOpt(&ServerConf->double_decoding, "Double Decoding");
// eg just once when captured; this function is called on every packet and
// repeatedly sets the flags on session
static inline void HttpLogFuncs(
- HttpsessionData *hsd, Packet *p, int iCallDetect )
+ HttpSessionData *hsd, Packet *p, int iCallDetect )
{
if(!hsd)
return;
int iInspectMode = 0;
int iRet;
int iCallDetect = 1;
- HttpsessionData *hsd = NULL;
+ HttpSessionData *hsd = NULL;
PROFILE_VARS;
}
if (hsd == NULL)
- hsd = SetNewHttpsessionData(p, (void *)session);
+ hsd = SetNewHttpSessionData(p, (void *)session);
else
{
/* Gzip data should not be logged with all the packets of the session.*/
detect_data_size = 0;
}
- set_file_data((uint8_t *)session->server.response.body, detect_data_size);
+ /* Do we have a file decompression object? */
+ if( hsd->fd_state != 0 )
+ {
+ fd_status_t Ret_Code;
+
+ uint16_t Data_Len;
+ const uint8_t *Data;
+
+ hsd->fd_state->Next_In = (uint8_t*)(Data = session->server.response.body);
+ hsd->fd_state->Avail_In = (Data_Len = (uint16_t)detect_data_size);
+
+ (void)File_Decomp_SetBuf( hsd->fd_state );
+
+ Ret_Code = File_Decomp( hsd->fd_state );
+
+ if( Ret_Code == File_Decomp_DecompError )
+ {
+ session->server.response.body = Data;
+ session->server.response.body_size = Data_Len;
+
+ hi_set_event(GID_HTTP_SERVER, hsd->fd_state->Error_Event);
+ File_Decomp_StopFree( hsd->fd_state );
+ hsd->fd_state = NULL;
+ }
+ /* If we didn't find a Sig, then clear the File_Decomp state
+ and don't keep looking. */
+ else if( Ret_Code == File_Decomp_NoSig )
+ {
+ File_Decomp_StopFree( hsd->fd_state );
+ hsd->fd_state = NULL;
+ }
+ else
+ {
+ session->server.response.body = hsd->fd_state->Buffer;
+ session->server.response.body_size = hsd->fd_state->Total_Out;
+ }
+
+ set_file_data((uint8_t *)session->server.response.body, (uint16_t)session->server.response.body_size);
+ }
+
+ else
+ {
+ set_file_data((uint8_t *)session->server.response.body, detect_data_size);
+ }
if (PacketHasPAFPayload(p)
&& file_api->file_process(p,(uint8_t *)session->server.response.body, (uint16_t)session->server.response.body_size,
if (iRet)
return iRet;
- iRet = hi_client_init(config);
+ iRet = hi_client_init();
if (iRet)
return iRet;
return 0;
}
-void FreeHttpsessionData(void *data)
+void FreeHttpSessionData(void *data)
{
- HttpsessionData *hsd = (HttpsessionData *)data;
+ HttpSessionData *hsd = (HttpSessionData *)data;
if (hsd->decomp_state != NULL)
{
sfip_free(hsd->true_ip);
file_api->free_mime_session(hsd->mime_ssn);
+
+ if( hsd->fd_state != 0 )
+ {
+ File_Decomp_StopFree(hsd->fd_state); // Stop & Stop & Free fd session object
+ hsd->fd_state = NULL; // ...just for good measure
+ }
}
int GetHttpTrueIP(Flow* flow, uint8_t **buf, uint32_t *len, uint32_t *type)
{
- HttpsessionData* hsd = get_session_data(flow);
+ HttpSessionData* hsd = get_session_data(flow);
if(!hsd->true_ip)
return 0;
int IsGzipData(Flow* flow)
{
- HttpsessionData *hsd = NULL;
+ HttpSessionData *hsd = NULL;
if (flow == NULL)
return -1;
int IsJSNormData(Flow* flow)
{
- HttpsessionData *hsd = NULL;
+ HttpSessionData *hsd = NULL;
if (flow == NULL)
return -1;
int GetHttpUriData(Flow* flow, uint8_t **buf, uint32_t *len, uint32_t *type)
{
- HttpsessionData *hsd = NULL;
+ HttpSessionData *hsd = NULL;
if (flow == NULL)
return 0;
int GetHttpHostnameData(Flow* flow, uint8_t **buf, uint32_t *len, uint32_t *type)
{
- HttpsessionData *hsd = NULL;
+ HttpSessionData *hsd = NULL;
if (flow == NULL)
return 0;
uint8_t hostname_extracted[MAX_HOSTNAME];
}HTTP_LOG_STATE;
-typedef struct _HttpsessionData
+typedef struct _HttpSessionData
{
- uint32_t event_flags;
HTTP_RESP_STATE resp_state;
DECOMPRESS_STATE *decomp_state;
HTTP_LOG_STATE *log_state;
uint8_t cli_small_chunk_count;
uint8_t srv_small_chunk_count;
MimeState *mime_ssn;
-} HttpsessionData;
+ fd_session_p_t fd_state;
+} HttpSessionData;
class HttpFlowData : public FlowData
{
public:
static unsigned flow_id;
- HttpsessionData session;
+ HttpSessionData session;
};
typedef struct _HISearch
extern THREAD_LOCAL HISearch *hi_current_search;
extern THREAD_LOCAL HISearchInfo hi_search_info;
-void ApplyFlowDepth(HTTPINSPECT_CONF *, Packet *, HttpsessionData *, int, int, uint32_t);
+void ApplyFlowDepth(HTTPINSPECT_CONF *, Packet *, HttpSessionData *, int, int, uint32_t);
void HttpInspectRegisterXtraDataFuncs();
int HttpInspectMain(HTTPINSPECT_CONF *GlobalConf, Packet *p);
int PrintGlobalConf(HTTPINSPECT_GLOBAL_CONF *);
int PrintServerConf(HTTPINSPECT_CONF*);
int HttpInspectInitializeGlobalConfig(HTTPINSPECT_GLOBAL_CONF*);
-HttpsessionData * SetNewHttpsessionData(Packet *, void *);
-void FreeHttpsessionData(void *data);
+HttpSessionData * SetNewHttpSessionData(Packet *, void *);
+void FreeHttpSessionData(void *data);
int GetHttpTrueIP(Flow*, uint8_t **buf, uint32_t *len, uint32_t *type);
int GetHttpGzipData(Flow*, uint8_t **buf, uint32_t *len, uint32_t *type);
int GetHttpJSNormData(Flow*, uint8_t **buf, uint32_t *len, uint32_t *type);
ds->max_seq = 0;
}
-static inline int SetLogBuffers(HttpsessionData *hsd)
+static inline int SetLogBuffers(HttpSessionData *hsd)
{
int iRet = 0;
*/
int hi_mi_mode_inspection(HI_SESSION *session, int iInspectMode,
- Packet *p, HttpsessionData *hsd)
+ Packet *p, HttpSessionData *hsd)
{
int iRet;
if (!session || !p->data || (p->dsize == 0))
#include "hi_include.h"
#include "hi_main.h"
-int hi_mi_mode_inspection(HI_SESSION *session, int iInspectMode, Packet *p, HttpsessionData *);
+int hi_mi_mode_inspection(HI_SESSION *session, int iInspectMode, Packet *p, HttpSessionData *);
#endif
{ nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
};
-
static const Parameter hi_server_params[] =
{
{ "allow_proxy_use", Parameter::PT_BOOL, nullptr, "false",
"don't alert on proxy use for this server" },
+ { "decompress_pdf", Parameter::PT_BOOL, nullptr, "false",
+ "????" }, // FIXIT-M need to figure out this parameter format and implement it
+
+ { "decompress_swf", Parameter::PT_BOOL, nullptr, "false",
+ "????" }, // FIXIT-M need to figure out this parameter format and implement it with deflate and lzma
+
{ "enable_cookies", Parameter::PT_BOOL, nullptr, "true",
"extract cookies" },
{ "unlimited_decompress", Parameter::PT_BOOL, nullptr, "true",
"decompress across multiple packets" },
+ { "xff_headers", Parameter::PT_BOOL, nullptr, "false",
+ "????" }, // FIXIT-M need to figure out this parameter format and implement it
+
{ nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
};
{ HI_SERVER_JS_OBFUSCATION_EXCD, HI_SERVER_JS_OBFUSCATION_EXCD_STR },
{ HI_SERVER_JS_EXCESS_WS, HI_SERVER_JS_EXCESS_WS_STR },
{ HI_SERVER_MIXED_ENCODINGS, HI_SERVER_MIXED_ENCODINGS_STR },
+ { HI_SERVER_SWF_ZLIB_FAILURE, HI_SERVER_SWF_ZLIB_FAILURE_STR },
+ { HI_SERVER_SWF_LZMA_FAILURE, HI_SERVER_SWF_LZMA_FAILURE_STR },
+ { HI_SERVER_PDF_DEFL_FAILURE, HI_SERVER_PDF_DEFL_FAILURE_STR },
+ { HI_SERVER_PDF_UNSUP_COMP_TYPE, HI_SERVER_PDF_UNSUP_COMP_TYPE_STR },
+ { HI_SERVER_PDF_CASC_COMP, HI_SERVER_PDF_CASC_COMP_STR },
+ { HI_SERVER_PDF_PARSE_FAILURE, HI_SERVER_PDF_PARSE_FAILURE_STR },
{ 0, nullptr }
};
else if ( v.is("code_page") )
server->iis_unicode_codepage = v.get_long();
+ else if ( v.is("decompress_pdf") ); // FIXIT-M need to figure this out and implement it
+
+ else if ( v.is("decompress_swf") ); // FIXIT-M need to figure this out and implement it
+ // including #define DECOMPRESS_DEFLATE "deflate" and
+ // including #define DECOMPRESS_LZMA "lzma"
+
else if ( v.is("directory") )
server->directory.on = v.get_bool();
else if ( v.is("whitespace_chars") )
v.get_bits(server->whitespace);
+ else if ( v.is("xff_headers") ); // FIXIT-M need to figure this out and implement it
+
else
return false;
typedef int (*DECODE_FUNC)(HI_SESSION *, const u_char *,
const u_char *, const u_char **, URI_NORM_STATE *, uint16_t *);
+static THREAD_LOCAL bool byte_decoded=false;
-bool byte_decoded=false;
/*
** NAME
** GetPtr::
return HI_SUCCESS;
}
+/*
+** NAME
+** EndPathField::
+*/
+/* This function determines whether a character marks the end of the URI path field.
+** It recognizes the query field '?' and fragment field '#' delimiters.
+** It will not accept a percent-encoded character as a valid delimiter.
+*/
+static inline bool EndPathField (bool percent_encoded, u_char uri_character) {
+ return ((uri_character == '?') || (uri_character == '#')) && !percent_encoded;
+}
+
/*
** NAME
** InspectUriChar::
}
}
+ // This block is necessary to detect '?' and '#' delimiters that immediately follow a '/'.
+ if(EndPathField(byte_decoded, (u_char)iDir))
+ {
+ // This is the end of the path field. Check for a long directory following.
+ CheckLongDir(session, norm_state, *ub_ptr);
+ norm_state->param = *ub_ptr;
+ }
+
**ub_ptr = (u_char)iDir;
(*ub_ptr)++;
}
return HI_SUCCESS;
}
- if((!byte_decoded && (u_char)iChar == '?'))
+ if(EndPathField(byte_decoded, (u_char)iChar))
{
- /*
- ** We assume that this is the beginning of the parameter field,
- ** and check for a long directory following. Event though seeing
- ** a question mark does not guarantee the parameter field, thanks
- ** IIS.
- */
+ // This is the end of the path field. Check for a long directory following.
CheckLongDir(session, norm_state, *ub_ptr);
norm_state->param = *ub_ptr;
}
** @retval HI_SUCCESS function successful
** @retval HI_INVALID_ARG invalid argument
*/
-int hi_normalization(HI_SESSION *session, int iInspectMode, HttpsessionData *hsd)
+int hi_normalization(HI_SESSION *session, int iInspectMode, HttpSessionData *hsd)
{
int iRet;
#define MAX_URI 8192
-int hi_normalization(HI_SESSION *session, int iInspectMode, HttpsessionData *hsd);
+int hi_normalization(HI_SESSION *session, int iInspectMode, HttpSessionData *hsd);
int hi_norm_uri(HI_SESSION *session, u_char *uribuf,int *uribuf_size,
const u_char *uri, int uri_size, uint16_t *encodeType);
Server->response.header_norm_size = 0 ;\
Server->response.cookie.cookie = NULL;\
Server->response.cookie.cookie_end = NULL;\
+ if(Server->response.cookie.next) {\
+ COOKIE_PTR *cookie = Server->response.cookie.next; \
+ do { \
+ Server->response.cookie.next = Server->response.cookie.next->next; \
+ free(cookie); \
+ cookie = Server->response.cookie.next; \
+ }while(cookie);\
+ }\
Server->response.cookie.next = NULL;\
Server->response.cookie_norm = NULL;\
Server->response.cookie_norm_size = 0;\
** @retval HI_INVALID_ARG invalid argument
** @retval HI_SUCCESS function success
*/
-static int IsHttpServerData(HI_SESSION *session, Packet *p, HttpsessionData *sd)
+static int IsHttpServerData(HI_SESSION *session, Packet *p, HttpSessionData *sd)
{
const u_char *start;
const u_char *end;
/* Grab the argument of "charset=foo" from a Content-Type header */
static inline const u_char *extract_http_content_type_charset(
- HI_SESSION*, HttpsessionData *hsd,
+ HI_SESSION*, HttpSessionData *hsd,
const u_char *p, const u_char*, const u_char *end )
{
size_t cmplen;
}
const u_char *extract_http_transfer_encoding(
- HI_SESSION*, HttpsessionData *hsd,
+ HI_SESSION*, HttpSessionData *hsd,
const u_char *p, const u_char *start, const u_char *end,
HEADER_PTR *header_ptr, int iInspectMode)
{
static inline const u_char *extractHttpRespHeaderFieldValues(HTTPINSPECT_CONF *ServerConf,
const u_char *p, const u_char *offset, const u_char *start,
const u_char *end, HEADER_PTR *header_ptr,
- HEADER_FIELD_PTR *header_field_ptr, int parse_cont_encoding, HttpsessionData *hsd,
+ HEADER_FIELD_PTR *header_field_ptr, int parse_cont_encoding, HttpSessionData *hsd,
HI_SESSION *session)
{
if (((p - offset) == 0) && ((*p == 'S') || (*p == 's')))
HI_SESSION *session, HTTPINSPECT_CONF *ServerConf,
HEADER_PTR *header_ptr, const u_char *start,
const u_char *end, int parse_cont_encoding,
- HttpsessionData *hsd)
+ HttpSessionData *hsd)
{
const u_char *p;
const u_char *offset;
}
static inline int hi_server_extract_body(
- HI_SESSION *session, HttpsessionData *sd,
+ HI_SESSION *session, HttpSessionData *sd,
const u_char *ptr, const u_char *end, URI_PTR *result)
{
HTTPINSPECT_CONF *ServerConf;
return STAT_END;
}
-static void SetGzipBuffers(HttpsessionData *hsd, HI_SESSION *session)
+static void LogFileDecomp(void*, int event)
+{
+ // FIXIT-H first argument is supposed to be pointer to session which may be needed to
+ // generate the event correctly.
+ hi_set_event(GID_HTTP_SERVER, event);
+}
+
+static void InitFileDecomp(HttpSessionData *hsd, HI_SESSION *session)
+{
+ fd_session_p_t fd_session;
+
+ if((hsd == NULL) || (session == NULL) || (session->server_conf == NULL) ||
+ (session->global_conf == NULL))
+ return;
+
+ if( (fd_session = File_Decomp_New()) == (fd_session_p_t)NULL )
+ return;
+
+ hsd->fd_state = fd_session;
+ fd_session->Modes = session->server_conf->file_decomp_modes;
+
+ fd_session->Alert_Callback = LogFileDecomp;
+ fd_session->Alert_Context = session;
+
+ if( (session->server_conf->unlimited_decompress) != 0 )
+ {
+ fd_session->Compr_Depth = 0;
+ fd_session->Decompr_Depth = 0;
+ }
+ else
+ {
+ fd_session->Compr_Depth = session->global_conf->compr_depth;
+ fd_session->Decompr_Depth = session->global_conf->decompr_depth;
+ }
+
+ (void)File_Decomp_Init( fd_session );
+}
+
+static void SetGzipBuffers(HttpSessionData *hsd, HI_SESSION *session)
{
if ((hsd != NULL) && (hsd->decomp_state == NULL)
&& (session != NULL) && (session->server_conf != NULL)
}
int uncompress_gzip ( u_char *dest, int destLen, const u_char *source,
- int sourceLen, HttpsessionData *sd, int *total_bytes_read, int compr_fmt)
+ int sourceLen, HttpSessionData *sd, int *total_bytes_read, int compr_fmt)
{
z_stream stream;
int err;
{
/* If some of the compressed data is decompressed we need to provide that for detection */
- if( stream.total_out > 0)
+ if (( stream.total_out > 0) && (err != Z_DATA_ERROR))
{
*total_bytes_read = stream.total_out;
iRet = HI_NONFATAL_ERR;
return HI_SUCCESS;
}
-static inline int hi_server_decompress(HI_SESSION *session, HttpsessionData *sd, const u_char *ptr,
+static inline int hi_server_decompress(HI_SESSION *session, HttpSessionData *sd, const u_char *ptr,
const u_char *end, URI_PTR *result)
{
const u_char *start = ptr;
if ((compr_avail <= 0) || (decompr_avail <= 0))
{
+ (void)File_Decomp_Reset(sd->fd_state);
ResetGzipState(sd->decomp_state);
ResetRespState(&(sd->resp_state));
return iRet;
}
else
ResetRespState(&(sd->resp_state));
+ (void)File_Decomp_Reset(sd->fd_state);
ResetGzipState(sd->decomp_state);
}
}
-static inline int hi_server_inspect_body(HI_SESSION *session, HttpsessionData *sd, const u_char *ptr,
+static inline int hi_server_inspect_body(HI_SESSION *session, HttpSessionData *sd, const u_char *ptr,
const u_char *end, URI_PTR *result)
{
int iRet = HI_SUCCESS;
{
if ((sd != NULL))
{
+ (void)File_Decomp_Reset(sd->fd_state);
ResetGzipState(sd->decomp_state);
ResetRespState(&(sd->resp_state));
}
}
void ApplyFlowDepth(
HTTPINSPECT_CONF *ServerConf, Packet *p,
- HttpsessionData *sd, int resp_header_size, int, uint32_t seq_num)
+ HttpSessionData *sd, int resp_header_size, int, uint32_t seq_num)
{
if(!ServerConf->server_flow_depth)
{
}
}
-static inline void ResetState (HttpsessionData* sd)
+static inline void ResetState (HttpSessionData* sd)
{
+ (void)File_Decomp_Reset(sd->fd_state);
ResetGzipState(sd->decomp_state);
ResetRespState(&(sd->resp_state));
}
static int HttpResponseInspection(HI_SESSION *session, Packet *p, const unsigned char *data,
- int dsize, HttpsessionData *sd)
+ int dsize, HttpSessionData *sd)
{
HTTPINSPECT_CONF *ServerConf;
URI_PTR stat_code_ptr;
}
else
{
+ (void)File_Decomp_Reset(sd->fd_state);
ResetGzipState(sd->decomp_state);
ResetRespState(&(sd->resp_state));
}
}
else
{
+ (void)File_Decomp_Reset(sd->fd_state);
ResetGzipState(sd->decomp_state);
ResetRespState(&(sd->resp_state));
}
}
else
{
+ (void)File_Decomp_Reset(sd->fd_state);
ResetGzipState(sd->decomp_state);
ResetRespState(&(sd->resp_state));
}
ApplyFlowDepth(ServerConf, p, sd, resp_header_size, 0, seq_num);
if ( not_stream_insert && (sd != NULL))
{
+ (void)File_Decomp_Reset(sd->fd_state);
ResetGzipState(sd->decomp_state);
ResetRespState(&(sd->resp_state));
}
expected_pkt = 0;
if(sd != NULL)
{
+ (void)File_Decomp_Reset(sd->fd_state);
ResetGzipState(sd->decomp_state);
ResetRespState(&(sd->resp_state));
sd->resp_state.flow_depth_excd = false;
sd->resp_state.inspect_body = 1;
}
+ if( ServerConf->file_decomp_modes != 0 )
+ {
+ InitFileDecomp(sd, session);
+ }
+
sd->resp_state.last_pkt_contlen = (header_ptr.content_len.len != 0);
if(ServerConf->server_flow_depth == -1)
sd->resp_state.flow_depth_excd = true;
{
status = SafeMemcpy(HttpDecodeBuf.data, Server->response.body,
alt_dsize, HttpDecodeBuf.data, HttpDecodeBuf.data + sizeof(HttpDecodeBuf.data));
- if( status != SAFEMEM_SUCCESS )
+ if (status != SAFEMEM_SUCCESS)
+ {
+ CLR_SERVER_HEADER(Server);
+ CLR_SERVER_STAT_MSG(Server);
+ CLR_SERVER_STAT(Server);
return HI_MEM_ALLOC_FAIL;
+ }
SetHttpDecode((uint16_t)alt_dsize);
Server->response.body = HttpDecodeBuf.data;
}
}
- {
- /* There is no body to the HTTP response.
- * In this case we need to inspect the entire HTTP response header.
- */
- ApplyFlowDepth(ServerConf, p, sd, resp_header_size, 1, seq_num);
- }
-
+ ApplyFlowDepth(ServerConf, p, sd, resp_header_size, 1, seq_num);
return HI_SUCCESS;
}
-int ServerInspection(HI_SESSION *session, Packet *p, HttpsessionData *hsd)
+int ServerInspection(HI_SESSION *session, Packet *p, HttpSessionData *hsd)
{
int iRet;
return HI_SUCCESS;
}
-int hi_server_inspection(void *S, Packet *p, HttpsessionData *hsd)
+int hi_server_inspection(void *S, Packet *p, HttpSessionData *hsd)
{
HI_SESSION *session;
HI_SERVER_RESP response;
} HI_SERVER;
-int hi_server_inspection(void *, Packet *, HttpsessionData *);
+int hi_server_inspection(void *, Packet *, HttpSessionData *);
#endif
#include "detection_util.h"
-int hi_server_norm(HI_SESSION *session, HttpsessionData *hsd)
+int hi_server_norm(HI_SESSION *session, HttpSessionData *hsd)
{
static THREAD_LOCAL u_char HeaderBuf[MAX_URI];
static THREAD_LOCAL u_char CookieBuf[MAX_URI];
#include "hi_include.h"
#include "hi_si.h"
-int hi_server_norm(HI_SESSION *session, HttpsessionData *hsd);
+int hi_server_norm(HI_SESSION *session, HttpSessionData *hsd);
#endif
Packet *p);
extern int CheckChunkEncoding(HI_SESSION *, const u_char *, const u_char *, const u_char **, u_char *,
- uint32_t , uint32_t, uint32_t *, uint32_t *, HttpsessionData *, int);
+ uint32_t , uint32_t, uint32_t *, uint32_t *, HttpSessionData *, int);
extern int IsHttpVersion(const u_char **, const u_char *);
extern const u_char *extract_http_cookie(const u_char *p, const u_char *end, HEADER_PTR *, HEADER_FIELD_PTR *);
extern const u_char *extract_http_content_length(HI_SESSION *, HTTPINSPECT_CONF *, const u_char *, const u_char *, const u_char *, HEADER_PTR *, HEADER_FIELD_PTR *) ;
-extern const u_char *extract_http_transfer_encoding(HI_SESSION *, HttpsessionData *,
+extern const u_char *extract_http_transfer_encoding(HI_SESSION *, HttpSessionData *,
const u_char *, const u_char *, const u_char *, HEADER_PTR *, int);
#endif
#include "sf_ip.h"
#include "hi_util_kmap.h"
#include "file_api/file_api.h"
+#include "hi_file_decomp.h"
#include "framework/bits.h"
/*
#define HI_UI_CONFIG_MAX_HDR_DEFAULT 0
#define HI_UI_CONFIG_MAX_HEADERS_DEFAULT 0
#define HI_UI_CONFIG_MAX_SPACES_DEFAULT 200
+#define HI_UI_CONFIG_MAX_XFF_FIELD_NAMES 8
/*
** Special characters treated as whitespace before or after URI
char uri_only;
char enable_cookie;
char inspect_response;
+ uint8_t *xff_headers[HI_UI_CONFIG_MAX_XFF_FIELD_NAMES];
+ uint8_t xff_header_lengths[HI_UI_CONFIG_MAX_XFF_FIELD_NAMES];
char enable_xff;
char log_uri;
char log_hostname;
bool unlimited_decompress;
char extract_gzip;
+ unsigned long file_decomp_modes;
+
+/* NOTE: The XFF_BUILTING_NAMES value must match the code in snort_httpinspect.c that
+ adds the builtin names to the list. */
+#define HI_UI_CONFIG_XFF_FIELD_NAME "X-Forwarded-For"
+#define HI_UI_CONFIG_TCI_FIELD_NAME "True-Client-IP"
+#define XFF_BUILTIN_NAMES (2)
/* Support Extended ascii codes in the URI */
char extended_ascii_uri;
#include "snort_debug.h"
#include "util.h"
#include "parser.h"
+#include "hi_file_decomp.h"
#include "hi_client.h"
#include "hi_ui_config.h"
#include "framework/inspector.h"
#include "managers/data_manager.h"
-#define ERRSTRLEN 1000
-
int hex_lookup[256];
int valid_lookup[256];
pPolicyConfig->decompr_depth = DEFAULT_DECOMP_DEPTH;
pPolicyConfig->max_gzip_sessions =
- pPolicyConfig->max_gzip_mem / sizeof(DECOMPRESS_STATE);
+ pPolicyConfig->max_gzip_mem / (sizeof(DECOMPRESS_STATE) + sizeof(fd_session_t));
}
static void CheckMemcap(HTTPINSPECT_GLOBAL_CONF *pPolicyConfig)