]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
bhagya - pop and imap inspectors ported
authorRuss Combs <rucombs@cisco.com>
Wed, 29 Apr 2015 17:36:48 +0000 (13:36 -0400)
committerRuss Combs <rucombs@cisco.com>
Wed, 29 Apr 2015 17:36:48 +0000 (13:36 -0400)
42 files changed:
ChangeLog
configure.ac
lua/snort.lua
src/CMakeLists.txt
src/Makefile.am
src/file_api/file_api.h
src/file_api/file_mime_config.cc
src/file_api/file_mime_config.h
src/file_api/file_mime_process.cc
src/file_api/file_mime_process.h
src/file_api/file_service.cc
src/protocols/CMakeLists.txt
src/protocols/Makefile.am
src/protocols/ssl.cc [new file with mode: 0644]
src/protocols/ssl.h [new file with mode: 0644]
src/service_inspectors/CMakeLists.txt
src/service_inspectors/Makefile.am
src/service_inspectors/http_inspect/hi_main.cc
src/service_inspectors/imap/CMakeLists.txt [new file with mode: 0644]
src/service_inspectors/imap/Makefile.am [new file with mode: 0644]
src/service_inspectors/imap/imap.cc [new file with mode: 0644]
src/service_inspectors/imap/imap.h [new file with mode: 0644]
src/service_inspectors/imap/imap_config.h [new file with mode: 0644]
src/service_inspectors/imap/imap_module.cc [new file with mode: 0644]
src/service_inspectors/imap/imap_module.h [new file with mode: 0644]
src/service_inspectors/imap/imap_paf.cc [new file with mode: 0644]
src/service_inspectors/imap/imap_paf.h [new file with mode: 0644]
src/service_inspectors/pop/CMakeLists.txt [new file with mode: 0644]
src/service_inspectors/pop/Makefile.am [new file with mode: 0644]
src/service_inspectors/pop/pop.cc [new file with mode: 0644]
src/service_inspectors/pop/pop.h [new file with mode: 0644]
src/service_inspectors/pop/pop_config.h [new file with mode: 0644]
src/service_inspectors/pop/pop_module.cc [new file with mode: 0644]
src/service_inspectors/pop/pop_module.h [new file with mode: 0644]
src/service_inspectors/pop/pop_paf.cc [new file with mode: 0644]
src/service_inspectors/pop/pop_paf.h [new file with mode: 0644]
src/service_inspectors/service_inspectors.cc
tools/snort2lua/preprocessor_states/CMakeLists.txt
tools/snort2lua/preprocessor_states/Makefile.am
tools/snort2lua/preprocessor_states/pps_imap.cc [new file with mode: 0644]
tools/snort2lua/preprocessor_states/pps_pop.cc [new file with mode: 0644]
tools/snort2lua/preprocessor_states/preprocessor_api.cc

index 5bcb6b78c9fc8e749463831dd85c87a6df657f73..89b3559cd3ad988ff89697d08f1187a911dadd7d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+Pending - build 150
+
+-- pop and imap inspectors ported
+
 15/04/28 - build 149
 
 -- fixed build issue with extras
index 5aa0c8d01403445a3ca0b47cd407f2bd18280c01..95c17c529e666719aa452a753b10d8e18ec3474b 100644 (file)
@@ -971,7 +971,9 @@ src/service_inspectors/back_orifice/Makefile \
 src/service_inspectors/dns/Makefile \
 src/service_inspectors/ftp_telnet/Makefile \
 src/service_inspectors/http_inspect/Makefile \
+src/service_inspectors/imap/Makefile \
 src/service_inspectors/nhttp_inspect/Makefile \
+src/service_inspectors/pop/Makefile \
 src/service_inspectors/rpc_decode/Makefile \
 src/service_inspectors/ssh/Makefile \
 src/service_inspectors/wizard/Makefile \
index 62d9e743b56d9f02110d645f685aca7f46ae9530..c37fabe3325b978ee8792a82adebc0ad1c81c845 100644 (file)
@@ -65,7 +65,9 @@ stream_udp = { }
 arp_spoof = { }
 back_orifice = { }
 dns = { }
+imap = { }
 perf_monitor = { }
+pop = { }
 port_scan = { }
 rpc_decode = { }
 ssh = { }
index 85ffc2d5791e8c7359f2855fdfc75f8942c51802..424ef8a1cdb0cccccff1f530172a0679c4a3f591 100644 (file)
@@ -57,7 +57,9 @@ if (STATIC_INSPECTORS)
         back_orifice
         dns
         ftp_telnet
+       imap
         nhttp_inspect
+       pop
         rpc_decode
         ssh
         wizard
index 432892c4f02efc44451101de273b490354fb44f3..f63195ed68a840514621f088e5a1a7dce6dac0c1 100644 (file)
@@ -12,7 +12,9 @@ network_inspectors/arp_spoof/libarp_spoof.a \
 service_inspectors/back_orifice/libback_orifice.a \
 service_inspectors/dns/libdns.a \
 service_inspectors/ftp_telnet/libftp_telnet.a \
+service_inspectors/imap/libimap.a \
 service_inspectors/nhttp_inspect/libnhttp_inspect.a \
+service_inspectors/pop/libpop.a \
 service_inspectors/rpc_decode/librpc_decode.a \
 service_inspectors/ssh/libssh.a \
 service_inspectors/wizard/libwizard.a
index 6aaeab924d6821d57c164c5cdde1962ffcbf4f16..9bd928dd81121d9ae629c5e5195fb08e5b249846 100644 (file)
@@ -77,17 +77,63 @@ struct MAIL_LogConfig
     uint32_t email_hdrs_log_depth;
 };
 
+/* State tracker for data */
+enum MimeDataState
+{
+    MIME_PAF_FINDING_BOUNDARY_STATE,
+    MIME_PAF_FOUND_BOUNDARY_STATE
+};
+
+/* State tracker for Boundary Signature */
+enum MimeBoundaryState
+{
+    MIME_PAF_BOUNDARY_UNKNOWN = 0,      /* UNKNOWN */
+    MIME_PAF_BOUNDARY_LF,               /* '\n' */
+    MIME_PAF_BOUNDARY_HYPEN_FIRST,      /* First '-' */
+    MIME_PAF_BOUNDARY_HYPEN_SECOND      /* Second '-' */
+};
+
+/* State tracker for end of pop/smtp command */
+enum DataEndState
+{
+    PAF_DATA_END_UNKNOWN,         /* Start or UNKNOWN */
+    PAF_DATA_END_FIRST_CR,        /* First '\r' */
+    PAF_DATA_END_FIRST_LF,        /* First '\n' */
+    PAF_DATA_END_DOT,             /* '.' */
+    PAF_DATA_END_SECOND_CR,       /* Second '\r' */
+    PAF_DATA_END_SECOND_LF        /* Second '\n' */
+};
+
 #define MAX_MIME_BOUNDARY_LEN  70  /* Max length of boundary string, defined in RFC 2046 */
 
-struct MimeBoundary
+struct MimeDataPafInfo
 {
-    char boundary[2 + MAX_MIME_BOUNDARY_LEN + 1];    /* '--' + MIME boundary string + '\0' */
+    MimeDataState data_state;
+    char boundary[ MAX_MIME_BOUNDARY_LEN + 1];            /* MIME boundary string + '\0' */
     int boundary_len;
-    class SearchTool* boundary_search;
+    char* boundary_search;
+    MimeBoundaryState boundary_state;
+};
+
+typedef int (* Handle_header_line_func)(void* pkt, const uint8_t* ptr, const uint8_t* eol, int
+    max_header_len, void* mime_ssn);
+typedef int (* Normalize_data_func)(void* pkt, const uint8_t* ptr, const uint8_t* data_end);
+typedef void (* Decode_alert_func)(void* decode_state);
+typedef void (* Reset_state_func)(void *ssn);
+typedef bool (* Is_end_of_data_func)(void* ssn);
+
+struct MimeMethods
+{
+    Handle_header_line_func handle_header_line;
+    Normalize_data_func normalize_data;
+    Decode_alert_func decode_alert;
+    Reset_state_func reset_state;
+    Is_end_of_data_func is_end_of_data;
 };
 
 struct DecodeConfig
 {
+    bool ignore_data;
     int max_mime_mem;
     int max_depth;
     int b64_depth;
@@ -103,12 +149,13 @@ struct MimeState
     int state_flags;
     int log_flags;
     void* decode_state;
-    MimeBoundary mime_boundary;
+    MimeDataPafInfo mime_boundary;
     DecodeConfig* decode_conf;
     MAIL_LogConfig* log_config;
     MAIL_LogState* log_state;
     void* decode_bkt;
     void* log_mempool;
+    MimeMethods* methods;
 };
 
 #define FILE_API_VERSION5 2
@@ -151,9 +198,10 @@ typedef void (* Set_mime_decode_config_defaults_func)(DecodeConfig* decode_conf)
 typedef void (* Set_mime_log_config_defaults_func)(MAIL_LogConfig* log_config);
 typedef int (* Parse_mime_decode_args_func)(DecodeConfig* decode_conf, char* arg, const
     char* preproc_name);
+typedef void (* Check_decode_config_func)(DecodeConfig* decode_conf);
 typedef const uint8_t* (* Process_mime_data_func)(void* packet, const uint8_t* start, const
     uint8_t* end,
-    const uint8_t* data_end_marker, uint8_t* data_end, MimeState* mime_ssn, bool upload);
+    MimeState* mime_ssn, bool upload, bool paf_enabled);
 typedef void (* Free_mime_session_func)(MimeState* mime_ssn);
 typedef bool (* Is_decoding_enabled_func)(DecodeConfig* decode_conf);
 typedef bool (* Is_decoding_conf_changed_func)(DecodeConfig* configNext, DecodeConfig* config,
@@ -163,6 +211,13 @@ typedef void (* Finalize_mime_position_func)(Flow* flow, void* decode_state,
     FilePosition* position);
 typedef File_Verdict (* Get_file_verdict_func)(Flow* flow);
 typedef void (* Render_block_verdict_func)(void* ctx, void* p);
+typedef bool (*Check_paf_abort_func)(void* ssn);
+typedef FilePosition (*GetFilePosition)(void *pkt);
+typedef void (*Reset_mime_paf_state_func)(MimeDataPafInfo *data_info);
+/*  Process data boundary and flush each file based on boundary*/
+typedef bool (*Process_mime_paf_data_func)(MimeDataPafInfo *data_info,  uint8_t data);
+typedef bool (*Check_data_end_func)(void *end_state,  uint8_t data);
+
 typedef struct _file_api
 {
     int version;
@@ -197,12 +252,18 @@ typedef struct _file_api
     Set_mime_decode_config_defaults_func set_mime_decode_config_defauts;
     Set_mime_log_config_defaults_func set_mime_log_config_defauts;
     Parse_mime_decode_args_func parse_mime_decode_args;
+    Check_decode_config_func check_decode_config;
     Process_mime_data_func process_mime_data;
     Free_mime_session_func free_mime_session;
     Is_decoding_enabled_func is_decoding_enabled;
     Is_decoding_conf_changed_func is_decoding_conf_changed;
     Is_mime_log_enabled_func is_mime_log_enabled;
     Finalize_mime_position_func finalize_mime_position;
+    Reset_mime_paf_state_func reset_mime_paf_state;
+    Process_mime_paf_data_func process_mime_paf_data;
+    Check_data_end_func check_data_end;
+    Check_paf_abort_func check_paf_abort;
+    GetFilePosition get_file_position;
 
     Get_file_verdict_func get_file_verdict;
     Render_block_verdict_func render_block_verdict;
@@ -247,5 +308,19 @@ static inline bool isFileEnd(FilePosition position)
     return ((position == SNORT_FILE_END) || (position == SNORT_FILE_FULL));
 }
 
+static inline bool scanning_boundary(MimeDataPafInfo* mime_info, uint32_t boundary_start,
+    uint32_t* fp)
+{
+    if (boundary_start &&
+        mime_info->data_state == MIME_PAF_FOUND_BOUNDARY_STATE &&
+        mime_info->boundary_state != MIME_PAF_BOUNDARY_UNKNOWN)
+    {
+        *fp = boundary_start;
+        return true;
+    }
+
+    return false;
+}
+
 #endif /* FILE_API_H */
 
index f2f3af86a9b88fcee6d8abd4cd3d902053e7c876..7beb7842cb35b2fa66826aeb7ba0dd35b899b75d 100644 (file)
@@ -286,3 +286,34 @@ int parse_mime_decode_args(DecodeConfig* decode_conf, char* arg, const char* pre
     return ret;
 }
 
+void check_decode_config(DecodeConfig *currentConfig)
+{
+    int max = -1;
+
+    if (!currentConfig->max_mime_mem)
+        currentConfig->max_mime_mem = DEFAULT_MAX_MIME_MEM;
+
+    if(!currentConfig->b64_depth || !currentConfig->qp_depth
+        || !currentConfig->uu_depth || !currentConfig->bitenc_depth)
+    {
+        currentConfig->max_depth = MAX_DEPTH;
+    }
+    else
+    {
+        if(max < currentConfig->b64_depth)
+            max = currentConfig->b64_depth;
+
+        if(max < currentConfig->qp_depth)
+            max = currentConfig->qp_depth;
+
+        if(max < currentConfig->bitenc_depth)
+            max = currentConfig->bitenc_depth;
+
+        if(max < currentConfig->uu_depth)
+            max = currentConfig->uu_depth;
+
+        currentConfig->max_depth = max;
+    }
+    return;
+}
+
index d8d9a750db235d4329e41cd73083426a34214ca4..6ae30fff8330b080be4481f93112ab12534e718c 100644 (file)
@@ -36,5 +36,6 @@ bool is_decoding_enabled(DecodeConfig* decode_conf);
 bool is_mime_log_enabled(MAIL_LogConfig* log_config);
 bool is_decoding_conf_changed(DecodeConfig* configNext, DecodeConfig* config, const
     char* preproc_name);
+void check_decode_config(DecodeConfig *currentConfig);
 #endif
 
index 88b2de3869ac0980c1015425fa234f5bfef08377..d3225a575b5ea4cfb1f919ec7c0649be3f3989ee 100644 (file)
@@ -81,6 +81,7 @@ MIMESearchInfo mime_search_info;
 SearchTool* mime_hdr_search_mpse = nullptr;
 MIMESearch mime_hdr_search[HDR_LAST];
 MIMESearch* mime_current_search = NULL;
+static const char* boundary_str = "boundary=";
 
 /* Extract the filename from the header */
 static inline int extract_file_name(const char** start, int length, bool* disp_cont)
@@ -255,79 +256,6 @@ static void set_mime_buffers(MimeState* ssn)
     }
 }
 
-/*
- * Initialize run-time boundary search, this should be called for every transaction
- */
-static int init_boundary_search(MimeBoundary* mime_boundary)
-{
-    if (mime_boundary->boundary_search != NULL)
-        delete mime_boundary->boundary_search;
-
-    mime_boundary->boundary_search = new SearchTool;
-
-    if (mime_boundary->boundary_search == NULL)
-        return -1;
-
-    mime_boundary->boundary_search->add(
-        mime_boundary->boundary, mime_boundary->boundary_len, BOUNDARY);
-
-    mime_boundary->boundary_search->prep();
-
-    return 0;
-}
-
-/*
- * Update boundary search string when found
- */
-static int get_boundary(const char* data, int data_len, MimeBoundary* mime_boundary)
-{
-    int result;
-    int ovector[9];
-    int ovecsize = 9;
-    const char* boundary;
-    int boundary_len;
-    int ret;
-    char* mime_boundary_str;
-    int* mime_boundary_len;
-
-    mime_boundary_str = &mime_boundary->boundary[0];
-    mime_boundary_len = &mime_boundary->boundary_len;
-
-    /* result will be the number of matches (including submatches) */
-    result = pcre_exec(mime_boundary_pcre.re, mime_boundary_pcre.pe,
-        data, data_len, 0, 0, ovector, ovecsize);
-    if (result < 0)
-        return -1;
-
-    result = pcre_get_substring(data, ovector, result, 1, &boundary);
-    if (result < 0)
-        return -1;
-
-    boundary_len = strlen(boundary);
-    if (boundary_len > MAX_MIME_BOUNDARY_LEN)
-    {
-        /* XXX should we alert? breaking the law of RFC */
-        boundary_len = MAX_MIME_BOUNDARY_LEN;
-    }
-
-    mime_boundary_str[0] = '-';
-    mime_boundary_str[1] = '-';
-    ret = SafeMemcpy(mime_boundary_str + 2, boundary, boundary_len,
-        mime_boundary_str + 2, mime_boundary_str + 2 + MAX_MIME_BOUNDARY_LEN);
-
-    pcre_free_substring(boundary);
-
-    if (ret != SAFEMEM_SUCCESS)
-    {
-        return -1;
-    }
-
-    *mime_boundary_len = 2 + boundary_len;
-    mime_boundary_str[*mime_boundary_len] = '\0';
-
-    return 0;
-}
-
 void get_mime_eol(const uint8_t* ptr, const uint8_t* end,
     const uint8_t** eol, const uint8_t** eolm)
 {
@@ -391,27 +319,6 @@ static int search_str_found(void* id, void*, int index, void*, void*)
     return 1;
 }
 
-/*
- * Callback function for boundary search
- *
- * @param   id      id in array of search strings
- * @param   index   index in array of search strings
- * @param   data    buffer passed in to search function
- *
- * @return response
- * @retval 1        commands caller to stop searching
- */
-static int boundary_str_found(void* id, void*, int index, void*, void*)
-{
-    int boundary_id = (int)(uintptr_t)id;
-
-    mime_search_info.id = boundary_id;
-    mime_search_info.index = index;
-    //mime_search_info.length = mime_ssn->mime_boundary.boundary_len;
-
-    return 1;
-}
-
 static inline int is_decoding_enabled(DecodeConfig* pPolicyConfig)
 {
     if ( (pPolicyConfig->b64_depth > -1) || (pPolicyConfig->qp_depth > -1)
@@ -498,7 +405,7 @@ static inline void setup_decode(const char* data, int size, bool cnt_xf, MimeSta
  * @return  i       index into p->payload where we stopped looking at data
  */
 static const uint8_t* process_mime_header(
-    Packet*, const uint8_t* ptr,
+    Packet* p, const uint8_t* ptr,
     const uint8_t* data_end_marker, MimeState* mime_ssn)
 {
     const uint8_t* eol = data_end_marker;
@@ -508,7 +415,6 @@ static const uint8_t* process_mime_header(
     const uint8_t* cont_trans_enc = NULL;
     const uint8_t* cont_disp = NULL;
     int header_found;
-    int ret;
     const uint8_t* start_hdr;
 
     start_hdr = ptr;
@@ -526,6 +432,8 @@ static const uint8_t* process_mime_header(
 
     while (ptr < data_end_marker)
     {
+        int header_name_len;
+        int max_header_name_len = 0;
         get_mime_eol(ptr, data_end_marker, &eol, &eolm);
 
         /* got a line with only end of line marker should signify end of header */
@@ -571,6 +479,14 @@ static const uint8_t* process_mime_header(
                 colon++;
             }
 
+            /* Check for Exim 4.32 exploit where number of chars before colon is greater than 64 */
+            header_name_len = colon - ptr;
+            if ((mime_ssn->data_state != STATE_DATA_UNKNOWN) &&
+                (colon < eolm) && (header_name_len > MAX_HEADER_NAME_LEN))
+            {
+                max_header_name_len = header_name_len;
+            }
+
             /* If the end on line marker and end of line are the same, assume
              * header was truncated, so stay in data header state */
             if ((eolm != eol) &&
@@ -632,6 +548,23 @@ static const uint8_t* process_mime_header(
             mime_ssn->state_flags &= ~MIME_FLAG_DATA_HEADER_CONT;
         }
 
+        if (mime_ssn->methods && mime_ssn->methods->handle_header_line)
+        {
+            int ret = mime_ssn->methods->handle_header_line(p, ptr, eol, max_header_name_len,
+                mime_ssn);
+            if (ret < 0)
+                return NULL;
+            else if (ret > 0)
+            {
+                /* assume we guessed wrong and are in the body */
+                mime_ssn->data_state = STATE_DATA_BODY;
+                mime_ssn->state_flags &=
+                    ~(MIME_FLAG_FOLDING | MIME_FLAG_IN_CONTENT_TYPE | MIME_FLAG_DATA_HEADER_CONT
+                    | MIME_FLAG_IN_CONT_TRANS_ENC | MIME_FLAG_IN_CONT_DISP);
+                return ptr;
+            }
+        }
+
         /* check for folding
          * if char on next line is a space and not \n or \r\n, we are folding */
         if ((eol < data_end_marker) && isspace((int)eol[0]) && (eol[0] != '\n'))
@@ -658,24 +591,8 @@ static const uint8_t* process_mime_header(
         if ((mime_ssn->state_flags &
             (MIME_FLAG_IN_CONTENT_TYPE | MIME_FLAG_FOLDING)) == MIME_FLAG_IN_CONTENT_TYPE)
         {
-            if (mime_ssn->data_state != STATE_MIME_HEADER)
-            {
-                /* we got the full content-type header - look for boundary string */
-                ret = get_boundary((const char*)content_type_ptr, eolm - content_type_ptr,
-                    &(mime_ssn->mime_boundary));
-                if (ret != -1)
-                {
-                    ret = init_boundary_search(&(mime_ssn->mime_boundary));
-                    if (ret != -1)
-                    {
-                        DEBUG_WRAP(DebugMessage(DEBUG_FILE, "Got mime boundary: %s\n",
-                            mime_ssn->mime_boundary.boundary); );
-
-                        mime_ssn->state_flags |= MIME_FLAG_GOT_BOUNDARY;
-                    }
-                }
-            }
-            else if (!(mime_ssn->state_flags & MIME_FLAG_EMAIL_ATTACH))
+            if ((mime_ssn->data_state == STATE_MIME_HEADER) && !(mime_ssn->state_flags &
+                MIME_FLAG_EMAIL_ATTACH))
             {
                 setup_decode((const char*)content_type_ptr, (eolm - content_type_ptr), false,
                     mime_ssn);
@@ -729,100 +646,81 @@ static const uint8_t* process_mime_header(
     return ptr;
 }
 
+/* Get the end of data body (excluding boundary)*/
+static const uint8_t* GetDataEnd(const uint8_t* data_start,
+    const uint8_t* data_end_marker)
+{
+    /* '\r\n' + '--' + MIME boundary string */
+    const int Max_Search = 4 + MAX_MIME_BOUNDARY_LEN;
+    uint8_t* start;
+    /*Exclude 2 bytes because either \r\n or '--'  at the end */
+    uint8_t* end = (uint8_t*)data_end_marker - 2;
+
+    /*Search for the start of boundary, should be less than boundary length*/
+    if (end > data_start + Max_Search)
+        start = end - Max_Search;
+    else
+        start = (uint8_t*)data_start;
+
+    while (end > start)
+    {
+        if (*(--end) != '\n')
+            continue;
+
+        if ((*(end+1) == '-') && (*(end+2) == '-'))
+        {
+            if ((end > start) && (*(end-1) == '\r'))
+                return (end - 1);
+            else
+                return end;
+        }
+        break;
+    }
+    return data_end_marker;
+}
+
 /*
  * Handle DATA_BODY state
  * @param   packet standard Packet structure
  * @param   i index into p->payload buffer to start looking at data
  * @return  i index into p->payload where we stopped looking at data
  */
-static const uint8_t* process_mime_body(
-    Packet*, const uint8_t* ptr,
-    const uint8_t* data_end_marker, MimeState* mime_ssn)
+static const uint8_t* process_mime_body(Packet*, const uint8_t* ptr,
+    const uint8_t* data_end, MimeState* mime_ssn, bool is_data_end)
 {
-    int boundary_found = 0;
-    const uint8_t* boundary_ptr = NULL;
-    const uint8_t* attach_start = NULL;
-    const uint8_t* attach_end = NULL;
     Email_DecodeState* decode_state = (Email_DecodeState*)(mime_ssn->decode_state);
 
-    if ( mime_ssn->state_flags & MIME_FLAG_EMAIL_ATTACH )
-        attach_start = ptr;
-    /* look for boundary */
-    if (mime_ssn->state_flags & MIME_FLAG_GOT_BOUNDARY)
+    if (mime_ssn->state_flags & MIME_FLAG_EMAIL_ATTACH)
     {
-        boundary_found = mime_ssn->mime_boundary.boundary_search->find(
-            (const char*)ptr, data_end_marker - ptr, boundary_str_found);
+        const uint8_t* attach_start = ptr;
+        const uint8_t* attach_end;
 
-        mime_search_info.length = mime_ssn->mime_boundary.boundary_len;
-
-        if (boundary_found > 0)
+        if (is_data_end )
         {
-            boundary_ptr = ptr + mime_search_info.index;
-
-            /* should start at beginning of line */
-            if ((boundary_ptr == ptr) || (*(boundary_ptr - 1) == '\n'))
-            {
-                const uint8_t* eol;
-                const uint8_t* eolm;
-                const uint8_t* tmp;
-
-                if (mime_ssn->state_flags & MIME_FLAG_EMAIL_ATTACH )
-                {
-                    attach_end = boundary_ptr-1;
-                    mime_ssn->state_flags &= ~MIME_FLAG_EMAIL_ATTACH;
-                    if (attach_start < attach_end)
-                    {
-                        if (EmailDecode(attach_start, attach_end, decode_state) < DECODE_SUCCESS )
-                        {
-                            // MIME_DecodeAlert();
-                        }
-                    }
-                }
-
-                /* Check for end boundary */
-                tmp = boundary_ptr + mime_search_info.length;
-                if (((tmp + 1) < data_end_marker) && (tmp[0] == '-') && (tmp[1] == '-'))
-                {
-                    DEBUG_WRAP(DebugMessage(DEBUG_FILE, "Mime boundary end found: %s--\n",
-                        (char*)mime_ssn->mime_boundary.boundary); );
-
-                    /* no more MIME */
-                    mime_ssn->state_flags &= ~MIME_FLAG_GOT_BOUNDARY;
-                    mime_ssn->state_flags |= MIME_FLAG_MIME_END;
-
-                    /* free boundary search */
-                    delete mime_ssn->mime_boundary.boundary_search;
-                    mime_ssn->mime_boundary.boundary_search = NULL;
-                }
-                else
-                {
-                    DEBUG_WRAP(DebugMessage(DEBUG_FILE, "Mime boundary found: %s\n",
-                        (char*)mime_ssn->mime_boundary.boundary); );
-
-                    mime_ssn->data_state = STATE_MIME_HEADER;
-                }
-
-                /* get end of line - there could be spaces after boundary before eol */
-                get_mime_eol(boundary_ptr + mime_search_info.length, data_end_marker, &eol, &eolm);
-
-                return eol;
-            }
+            attach_end = GetDataEnd(ptr, data_end);
+        }
+        else
+        {
+            attach_end = data_end;
         }
-    }
 
-    if ( mime_ssn->state_flags & MIME_FLAG_EMAIL_ATTACH )
-    {
-        attach_end = data_end_marker;
-        if (attach_start < attach_end)
+        if ( attach_start < attach_end )
         {
             if (EmailDecode(attach_start, attach_end, decode_state) < DECODE_SUCCESS )
             {
-                //  MIME_DecodeAlert();
+                if (mime_ssn->methods && mime_ssn->methods->decode_alert)
+                    mime_ssn->methods->decode_alert(mime_ssn->decode_state);
             }
         }
     }
 
-    return data_end_marker;
+    if (is_data_end)
+    {
+        mime_ssn->data_state = STATE_MIME_HEADER;
+        mime_ssn->state_flags &= ~MIME_FLAG_EMAIL_ATTACH;
+    }
+
+    return data_end;
 }
 
 /*
@@ -832,47 +730,26 @@ static void reset_mime_state(MimeState* mime_ssn)
 {
     Email_DecodeState* decode_state = (Email_DecodeState*)(mime_ssn->decode_state);
 
-    if (mime_ssn->mime_boundary.boundary_search != NULL)
-    {
-        delete mime_ssn->mime_boundary.boundary_search;
-        mime_ssn->mime_boundary.boundary_search = NULL;
-    }
-
     mime_ssn->data_state = STATE_DATA_INIT;
     mime_ssn->state_flags = 0;
     ClearEmailDecodeState(decode_state);
-    memset(&mime_ssn->mime_boundary, 0, sizeof(MimeBoundary));
-}
-
-#if 0
-static inline FilePosition getFilePoistion(Packet* p)
-{
-    FilePosition position = SNORT_FILE_POSITION_UNKNOWN;
-
-    if (PacketHasFullPDU(p))
-        position = SNORT_FILE_FULL;
-    else if (PacketHasStartOfPDU(p))
-        position = SNORT_FILE_START;
-    else if (p->packet_flags & PKT_PDU_TAIL)
-        position = SNORT_FILE_END;
-    else if (file_api->get_file_processed_size(p->flow))
-        position = SNORT_FILE_MIDDLE;
-
-    return position;
 }
 
-#endif
-
 /*
  * Main function for mime processing
  *
  * This should be called when mime data is available
  */
-const uint8_t* process_mime_data(void* packet, const uint8_t* start, const uint8_t* end,
-    const uint8_t* data_end_marker, uint8_t* data_end, MimeState* mime_ssn, bool upload)
+const uint8_t* process_mime_data_paf(void* packet, const uint8_t* start, const uint8_t* end,
+    MimeState* mime_ssn, bool upload, FilePosition position)
 {
     Packet* p = (Packet*)packet;
-    FilePosition position = SNORT_FILE_START;
+    bool done_data = false;
+
+    if (mime_ssn->methods && mime_ssn->methods->is_end_of_data)
+    {
+        done_data = mime_ssn->methods->is_end_of_data(p->flow);
+    }
 
     /* if we've just entered the data state, check for a dot + end of line
      * if found, no data */
@@ -892,6 +769,11 @@ const uint8_t* process_mime_data(void* packet, const uint8_t* start, const uint8
             {
                 /* if we're normalizing and not ignoring data copy data end marker
                  * and dot to alt buffer */
+                if (mime_ssn->methods && mime_ssn->methods->normalize_data)
+                {
+                    if (mime_ssn->methods->normalize_data(p, start, end) < 0)
+                        return NULL;
+                }
 
                 reset_mime_state(mime_ssn);
 
@@ -914,25 +796,8 @@ const uint8_t* process_mime_data(void* packet, const uint8_t* start, const uint8
          * in the body which seems more reasonable. */
     }
 
-    /* get end of data body
-     * TODO check last bytes of previous packet to see if we had a partial
-     * end of data */
-    /* mime_current_search = &mime_data_end_search[0];
-    data_end_found = mime_data_search_mpse->find(
-        (const char *)start, end - start, search_str_found);
-
-    if (data_end_found > 0)
-    {
-        data_end_marker = start + mime_search_info.index;
-        data_end = data_end_marker + mime_search_info.length;
-    }
-    else
-    {
-        data_end_marker = data_end = end;
-    }
-     */
-
-    set_file_data((uint8_t*)start, (data_end - start));
+    if ( mime_ssn->decode_conf && !mime_ssn->decode_conf->ignore_data)
+        set_file_data((uint8_t*)start, (end - start));
 
     if ((mime_ssn->data_state == STATE_DATA_HEADER) ||
         (mime_ssn->data_state == STATE_DATA_UNKNOWN))
@@ -948,54 +813,30 @@ const uint8_t* process_mime_data(void* packet, const uint8_t* start, const uint8
         }
 #endif
 
-        start = process_mime_header(p, start, data_end_marker, mime_ssn);
+        start = process_mime_header(p, start, end, mime_ssn);
         if (start == NULL)
             return NULL;
     }
 
+    if (mime_ssn->methods && mime_ssn->methods->normalize_data)
+    {
+        if (mime_ssn->methods->normalize_data(p, start, end) < 0)
+            return NULL;
+    }
     /* now we shouldn't have to worry about copying any data to the alt buffer
-     * only mime headers if we find them and only if we're ignoring data */
-    initFilePosition(&position, file_api->get_file_processed_size(p->flow));
+     *      * only mime headers if we find them and only if we're ignoring data */
 
-    while ((start != NULL) && (start < data_end_marker))
+    while ((start != NULL) && (start < end))
     {
-        /* multiple MIME attachments in one single packet.
-         * Pipeline the MIME decoded data.*/
-        if ( mime_ssn->state_flags & MIME_FLAG_MULTIPLE_EMAIL_ATTACH)
-        {
-            DecodeConfig* conf= mime_ssn->decode_conf;
-            int detection_size = getDetectionSize(conf->b64_depth, conf->qp_depth,
-                conf->uu_depth, conf->bitenc_depth, (Email_DecodeState*)(mime_ssn->decode_state) );
-
-            set_file_data(((Email_DecodeState*)(mime_ssn->decode_state))->decodePtr,
-                detection_size);
-            /*Process file type/file signature*/
-            if (file_api->file_process(p,
-                (uint8_t*)((Email_DecodeState*)(mime_ssn->decode_state))->decodePtr,
-                (uint16_t)((Email_DecodeState*)(mime_ssn->decode_state))->decoded_bytes, position,
-                upload, false)
-                && (isFileStart(position)) && mime_ssn->log_state)
-            {
-                file_api->set_file_name_from_log(&(mime_ssn->log_state->file_log), p->flow);
-            }
-            updateFilePosition(&position, file_api->get_file_processed_size(p->flow));
-            get_data_bus().publish(PACKET_EVENT, p);
-            mime_ssn->state_flags &= ~MIME_FLAG_MULTIPLE_EMAIL_ATTACH;
-            ResetEmailDecodeState((Email_DecodeState*)(mime_ssn->decode_state));
-            p->packet_flags |= PKT_ALLOW_MULTIPLE_DETECT;
-            /* Reset the log count when a packet goes through detection multiple times */
-            DetectReset();
-        }
         switch (mime_ssn->data_state)
         {
         case STATE_MIME_HEADER:
             DEBUG_WRAP(DebugMessage(DEBUG_FILE, "MIME HEADER STATE ~~~~~~~~~~~~~~~~~~~~~~\n"); );
-            start = process_mime_header(p, start, data_end_marker, mime_ssn);
-            file_api->finalize_mime_position(p->flow, mime_ssn->decode_state, &position);
+            start = process_mime_header(p, start, end, mime_ssn);
             break;
         case STATE_DATA_BODY:
             DEBUG_WRAP(DebugMessage(DEBUG_FILE, "DATA BODY STATE ~~~~~~~~~~~~~~~~~~~~~~~~\n"); );
-            start = process_mime_body(p, start, data_end_marker, mime_ssn);
+            start = process_mime_body(p, start, end, mime_ssn, isFileEnd(position) );
             break;
         }
     }
@@ -1004,27 +845,18 @@ const uint8_t* process_mime_data(void* packet, const uint8_t* start, const uint8
 
     if ((mime_ssn->decode_state) != NULL)
     {
-        if ((position == SNORT_FILE_START) || (position == SNORT_FILE_FULL))
+        DecodeConfig* conf= mime_ssn->decode_conf;
+        Email_DecodeState* ds = (Email_DecodeState*)(mime_ssn->decode_state);
+        if (conf)
         {
-            DecodeConfig* conf= mime_ssn->decode_conf;
             int detection_size = getDetectionSize(conf->b64_depth, conf->qp_depth,
-                conf->uu_depth, conf->bitenc_depth, (Email_DecodeState*)(mime_ssn->decode_state) );
-            set_file_data(((Email_DecodeState*)(mime_ssn->decode_state))->decodePtr,
-                detection_size);
-        }
-        else
-        {
-            set_file_data(((Email_DecodeState*)(mime_ssn->decode_state))->decodePtr, 0);
-        }
-        if ((data_end_marker != end)||(mime_ssn->state_flags & MIME_FLAG_MIME_END))
-        {
-            finalFilePosition(&position);
+                conf->uu_depth, conf->bitenc_depth, ds);
+            set_file_data(ds->decodePtr, (uint16_t)detection_size);
         }
+
         /*Process file type/file signature*/
-        if (file_api->file_process(p,
-            (uint8_t*)((Email_DecodeState*)(mime_ssn->decode_state))->decodePtr,
-            (uint16_t)((Email_DecodeState*)(mime_ssn->decode_state))->decoded_bytes, position,
-            upload, false)
+        if (file_api->file_process(p, (uint8_t*)ds->decodePtr,
+            (uint16_t)ds->decoded_bytes, position, upload, false)
             && (isFileStart(position))&& mime_ssn->log_state)
         {
             file_api->set_file_name_from_log(&(mime_ssn->log_state->file_log), p->flow);
@@ -1033,13 +865,64 @@ const uint8_t* process_mime_data(void* packet, const uint8_t* start, const uint8
     }
 
     /* if we got the data end reset state, otherwise we're probably still in the data
-     * to expect more data in next packet */
-    if (data_end_marker != end)
+     *      * to expect more data in next packet */
+    if (done_data)
     {
         reset_mime_state(mime_ssn);
+        if (mime_ssn->methods && mime_ssn->methods->reset_state)
+            mime_ssn->methods->reset_state(p->flow);
     }
 
-    return data_end;
+    return end;
+}
+
+/*
+ *  * Main function for mime processing
+ *   *
+ *    * This should be called when mime data is available
+ *     */
+const uint8_t* process_mime_data(void* packet, const uint8_t* start,
+    const uint8_t* data_end_marker, MimeState* mime_ssn, bool upload, bool paf_enabled)
+{
+    const uint8_t* attach_start = start;
+    const uint8_t* attach_end;
+    Packet* p = (Packet*)packet;
+    FilePosition position = SNORT_FILE_START;
+
+    if (paf_enabled)
+    {
+        position = file_api->get_file_position(p);
+        process_mime_data_paf(packet, attach_start, data_end_marker,
+            mime_ssn, upload, position);
+        return data_end_marker;
+    }
+
+    initFilePosition(&position, file_api->get_file_processed_size(p->flow));
+    /* look for boundary */
+    while (start < data_end_marker)
+    {
+        /*Found the boundary, start processing data*/
+        if (process_mime_paf_data(&(mime_ssn->mime_boundary),  *start))
+        {
+            attach_end = start;
+            finalFilePosition(&position);
+            process_mime_data_paf(packet, attach_start, attach_end,
+                mime_ssn, upload, position);
+            position = SNORT_FILE_START;
+            attach_start = start + 1;
+        }
+
+        start++;
+    }
+
+    if ((start == data_end_marker) && (attach_start < data_end_marker))
+    {
+        updateFilePosition(&position, file_api->get_file_processed_size(p->flow));
+        process_mime_data_paf(packet, attach_start, data_end_marker,
+            mime_ssn, upload, position);
+    }
+
+    return data_end_marker;
 }
 
 /*
@@ -1048,8 +931,6 @@ const uint8_t* process_mime_data(void* packet, const uint8_t* start, const uint8
  */
 void init_mime(void)
 {
-    const char* error;
-    int erroffset;
     const MimeToken* tmp;
 
     /* Header search */
@@ -1070,26 +951,6 @@ void init_mime(void)
     }
 
     mime_hdr_search_mpse->prep();
-
-    /* create regex for finding boundary string - since it can be cut across multiple
-     * lines, a straight search won't do. Shouldn't be too slow since it will most
-     * likely only be acting on a small portion of data */
-    mime_boundary_pcre.re = pcre_compile("boundary\\s*=\\s*\"?([^\\s\"]+)\"?",
-        PCRE_CASELESS | PCRE_DOTALL,
-        &error, &erroffset, NULL);
-    if (mime_boundary_pcre.re == NULL)
-    {
-        FatalError("Failed to compile pcre regex for getting boundary "
-            "in a multipart message: %s\n", error);
-    }
-
-    mime_boundary_pcre.pe = pcre_study(mime_boundary_pcre.re, 0, &error);
-
-    if (error != NULL)
-    {
-        FatalError("Failed to study pcre regex for getting boundary "
-            "in a multipart message: %s\n", error);
-    }
 }
 
 /*
@@ -1103,12 +964,6 @@ void free_mime(void)
 {
     if (mime_hdr_search_mpse != NULL)
         delete mime_hdr_search_mpse;
-
-    if (mime_boundary_pcre.re )
-        pcre_free(mime_boundary_pcre.re);
-
-    if (mime_boundary_pcre.pe )
-        pcre_free(mime_boundary_pcre.pe);
 }
 
 void free_mime_session(MimeState* mime_ssn)
@@ -1116,12 +971,6 @@ void free_mime_session(MimeState* mime_ssn)
     if (!mime_ssn)
         return;
 
-    if (mime_ssn->mime_boundary.boundary_search != NULL)
-    {
-        delete mime_ssn->mime_boundary.boundary_search;
-        mime_ssn->mime_boundary.boundary_search = NULL;
-    }
-
     if (mime_ssn->decode_state != NULL)
     {
         free(mime_ssn->decode_state);
@@ -1146,3 +995,203 @@ void finalize_mime_position(Flow* flow, void* decode_state, FilePosition* positi
         finalFilePosition(position);
 }
 
+/* Save the bounday string into paf state*/
+static inline bool store_boundary(MimeDataPafInfo* data_info,  uint8_t val)
+{
+    if (!data_info->boundary_search)
+    {
+        if ((val == '.') || isspace (val))
+            data_info->boundary_search = (char*)&boundary_str[0];
+        return 0;
+    }
+
+    if ((*(data_info->boundary_search) == '='))
+    {
+        /*Skip spaces for the end of boundary*/
+        if (val == '=')
+            data_info->boundary_search++;
+        else if (!isspace(val))
+            data_info->boundary_search = NULL;
+    }
+    else if (*(data_info->boundary_search) == '\0')
+    {
+        /*get boundary string*/
+        if (isspace(val) || (val == '"'))
+        {
+            if (!data_info->boundary_len)
+                return 0;
+            else
+            {
+                /*Found boundary string*/
+                data_info->boundary[data_info->boundary_len] = '\0';
+                return 1;
+            }
+        }
+
+        if (data_info->boundary_len < (int)sizeof(data_info->boundary))
+        {
+            data_info->boundary[data_info->boundary_len++] = val;
+        }
+        else
+        {
+            /*Found boundary string*/
+            data_info->boundary[data_info->boundary_len -1] = '\0';
+            return 1;
+        }
+    }
+    else if ((val == *(data_info->boundary_search))
+        || (val == *(data_info->boundary_search) - 'a' + 'A'))
+    {
+        data_info->boundary_search++;
+    }
+    else
+    {
+        if ((val == '.') || isspace (val))
+            data_info->boundary_search = (char*)&boundary_str[0];
+        else
+            data_info->boundary_search = NULL;
+    }
+
+    return 0;
+}
+
+/* check the bounday string in the mail body*/
+static inline bool check_boundary(MimeDataPafInfo* data_info,  uint8_t data)
+{
+    /* Search for boundary signature "--"*/
+    switch (data_info->boundary_state)
+    {
+    case MIME_PAF_BOUNDARY_UNKNOWN:
+        if (data == '\n')
+            data_info->boundary_state = MIME_PAF_BOUNDARY_LF;
+        break;
+
+    case MIME_PAF_BOUNDARY_LF:
+        if (data == '-')
+            data_info->boundary_state = MIME_PAF_BOUNDARY_HYPEN_FIRST;
+        else if (data != '\n')
+            data_info->boundary_state = MIME_PAF_BOUNDARY_UNKNOWN;
+        break;
+
+    case MIME_PAF_BOUNDARY_HYPEN_FIRST:
+        if (data == '-')
+        {
+            data_info->boundary_state = MIME_PAF_BOUNDARY_HYPEN_SECOND;
+            data_info->boundary_search = data_info->boundary;
+        }
+        else if (data == '\n')
+            data_info->boundary_state = MIME_PAF_BOUNDARY_LF;
+        else
+            data_info->boundary_state = MIME_PAF_BOUNDARY_UNKNOWN;
+        break;
+
+    case MIME_PAF_BOUNDARY_HYPEN_SECOND:
+        /* Compare with boundary string stored */
+        if (*(data_info->boundary_search) == '\0')
+        {
+            if (data == '\n')
+            {
+                /*reset boundary search etc.*/
+                data_info->boundary_state = MIME_PAF_BOUNDARY_UNKNOWN;
+                return 1;
+            }
+            else if ((data != '\r') && ((data != '-')))
+                data_info->boundary_state = MIME_PAF_BOUNDARY_UNKNOWN;
+        }
+        else if (*(data_info->boundary_search) == data)
+            data_info->boundary_search++;
+        else
+            data_info->boundary_state = MIME_PAF_BOUNDARY_UNKNOWN;
+
+        break;
+    }
+
+    return 0;
+}
+
+void reset_mime_paf_state(MimeDataPafInfo* data_info)
+{
+    data_info->boundary_search = NULL;
+    data_info->boundary_len = 0;
+    data_info->boundary[0] = '\0';
+    data_info->boundary_state = MIME_PAF_BOUNDARY_UNKNOWN;
+    data_info->data_state = MIME_PAF_FINDING_BOUNDARY_STATE;
+}
+
+/*  Process data boundary and flush each file based on boundary*/
+bool process_mime_paf_data(MimeDataPafInfo* data_info,  uint8_t data)
+{
+    switch (data_info->data_state)
+    {
+    case MIME_PAF_FINDING_BOUNDARY_STATE:
+        /* Search for boundary
+           Store bounday string in PAF state*/
+        if (store_boundary(data_info, data))
+        {
+            /* End of boundary, move to MIME_PAF_FOUND_BOUNDARY_STATE*/
+            DEBUG_WRAP(DebugMessage(DEBUG_FILE, "Create boudary string: %s\n",
+                data_info->boundary); );
+            data_info->data_state = MIME_PAF_FOUND_BOUNDARY_STATE;
+        }
+
+        break;
+    case MIME_PAF_FOUND_BOUNDARY_STATE:
+        if (check_boundary(data_info,  data))
+        {
+            /* End of boundary, move to MIME_PAF_FOUND_BOUNDARY_STATE*/
+            DEBUG_WRAP(DebugMessage(DEBUG_FILE, "Found Boudary string: %s\n",
+                data_info->boundary); );
+            return 1;
+        }
+        break;
+    default:
+        break;
+    }
+
+    return 0;
+}
+
+bool check_data_end(void* data_end_state,  uint8_t val)
+{
+    DataEndState state =  *((DataEndState*)data_end_state);
+
+    switch (state)
+    {
+    case PAF_DATA_END_UNKNOWN:
+        if (val == '\n')
+        {
+            state = PAF_DATA_END_FIRST_LF;
+        }
+        break;
+
+    case PAF_DATA_END_FIRST_LF:
+        if (val == '.')
+        {
+            state = PAF_DATA_END_DOT;
+        }
+        else if ((val != '\r') && (val != '\n'))
+        {
+            state = PAF_DATA_END_UNKNOWN;
+        }
+        break;
+    case PAF_DATA_END_DOT:
+        if (val == '\n')
+        {
+            *((DataEndState*)data_end_state) = PAF_DATA_END_UNKNOWN;
+            return 1;
+        }
+        else if (val != '\r')
+        {
+            state = PAF_DATA_END_UNKNOWN;
+        }
+        break;
+
+    default:
+        state = PAF_DATA_END_UNKNOWN;
+        break;
+    }
+
+    *((DataEndState*)data_end_state) = state;
+    return 0;
+}
+
index 695e99a235302ed50298e995f6436b8fc9616a4d..3211a96c1298c3ea1ee270ac8a0bc80c92cea8f0 100644 (file)
@@ -50,6 +50,9 @@
 #define STATE_MIME_HEADER  3    /* MIME header section within data section */
 #define STATE_DATA_UNKNOWN 4
 
+/* Maximum length of header chars before colon, based on Exim 4.32 exploit */
+#define MAX_HEADER_NAME_LEN 64
+
 /* log flags */
 #define MIME_FLAG_FILENAME_PRESENT           0x00000004
 
@@ -63,9 +66,14 @@ int log_file_name(const uint8_t* start, int length, FILE_LogState* log_state, bo
 int set_log_buffers(MAIL_LogState** log_state, MAIL_LogConfig* conf);
 void init_mime(void);
 void free_mime(void);
-const uint8_t* process_mime_data(void* packet, const uint8_t* start, const uint8_t* end,
-    const uint8_t* data_end_marker, uint8_t* data_end, MimeState* mime_ssn, bool upload);
+const uint8_t* process_mime_data(void *packet, const uint8_t *start, const uint8_t *end,
+                MimeState *mime_ssn, bool upload, bool paf_enabled);
 void free_mime_session(MimeState* mime_ssn);
 void finalize_mime_position(Flow* flow, void* decode_state, FilePosition* position);
+void reset_mime_paf_state(MimeDataPafInfo *data_info);
+/*  Process data boundary and flush each file based on boundary*/
+bool process_mime_paf_data(MimeDataPafInfo *data_info,  uint8_t val);
+bool check_data_end(void *end_state,  uint8_t val);
+
 #endif
 
index c9c9943fff7b434e2fe0778a85bc4df2bc63c051..ac9852f9b7c1673f534d1ddcefc1b86f4207a880 100644 (file)
@@ -92,6 +92,8 @@ static void file_signature_lookup(void* p, bool is_retransmit);
 static inline void finish_signature_lookup(FileContext* context, Flow* flow);
 static File_Verdict get_file_verdict(Flow* flow);
 static void render_block_verdict(void* ctx, void* p);
+static FilePosition get_file_position(void* pkt);
+static bool check_paf_abort(void* ssn);
 
 FileAPI fileAPI;
 FileAPI* file_api = NULL;
@@ -166,6 +168,7 @@ void FileAPIInit(void)
     fileAPI.set_mime_decode_config_defauts = &set_mime_decode_config_defauts;
     fileAPI.set_mime_log_config_defauts = &set_mime_log_config_defauts;
     fileAPI.parse_mime_decode_args = &parse_mime_decode_args;
+    fileAPI.check_decode_config = &check_decode_config;
     fileAPI.process_mime_data = &process_mime_data;
     fileAPI.free_mime_session = &free_mime_session;
     fileAPI.is_decoding_enabled = &is_decoding_enabled;
@@ -174,6 +177,12 @@ void FileAPIInit(void)
     fileAPI.finalize_mime_position = &finalize_mime_position;
     fileAPI.get_file_verdict = &get_file_verdict;
     fileAPI.render_block_verdict = &render_block_verdict;
+    fileAPI.get_file_position = &get_file_position;
+    fileAPI.reset_mime_paf_state = &reset_mime_paf_state;
+    fileAPI.process_mime_paf_data = &process_mime_paf_data;
+    fileAPI.check_data_end = check_data_end;
+    fileAPI.check_paf_abort = &check_paf_abort;
+
     file_api = &fileAPI;
     init_mime();
     FileFlowData::init();
@@ -838,6 +847,52 @@ static void set_file_name_from_log(FILE_LogState* log_state, void* pv)
     }
 }
 
+static FilePosition get_file_position(void* pkt)
+{
+    FilePosition position = SNORT_FILE_POSITION_UNKNOWN;
+    Packet* p = (Packet*)pkt;
+
+    if (PacketHasFullPDU(p))
+        position = SNORT_FILE_FULL;
+    else if (PacketHasStartOfPDU(p))
+        position = SNORT_FILE_START;
+    else if (p->packet_flags & PKT_PDU_TAIL)
+        position = SNORT_FILE_END;
+    else if (get_file_processed_size(p->flow))
+        position = SNORT_FILE_MIDDLE;
+
+    return position;
+}
+
+/*
+*  This function determines whether we shold abort PAF.  Will return
+*  true if the current packet is midstream, or unestablisted session
+*
+*  PARAMS:
+*      uint32_t - session flags passed in to callback.
+*
+*  RETURNS:
+*      true - if we should abort paf
+*      false - if we should continue using paf
+*/
+static bool check_paf_abort(void* ssn)
+{
+    uint32_t flags = stream.get_session_flags((Flow*)ssn);
+    if (flags & SSNFLAG_MIDSTREAM)
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_FILE,
+            "Aborting PAF because of midstream pickup.\n"));
+        return true;
+    }
+    else if (!(flags & SSNFLAG_ESTABLISHED))
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_FILE,
+            "Aborting PAF because of unestablished session.\n"));
+        return true;
+    }
+    return false;
+}
+
 static uint32_t str_to_hash(uint8_t* str, int length)
 {
     uint32_t a,b,c,tmp;
@@ -875,7 +930,7 @@ static uint32_t str_to_hash(uint8_t* str, int length)
             j = 0;
         }
     }
-    final(a,b,c);
+    final (a,b,c);
     return c;
 }
 
index 79fdbb0f5c7890c6ed74466fc4c1cfe390faac0e..046a173fe83fa133b7f5701e5980cd6c51fd434a 100644 (file)
@@ -16,6 +16,7 @@ set (PROTOCOL_HEADERS
     packet.h
     packet_manager.h
     protocol_ids.h
+    ssl.h
     tcp.h
     tcp_options.h
     teredo.h
@@ -31,6 +32,7 @@ add_library (protocols STATIC
     packet.cc
     ip.cc
     ipv4_options.cc
+    ssl.cc
     tcp_options.cc
     packet_manager.cc
 )
index 0eda8325a564ef9e73f5ca27f256bfccf8f07b8f..c7007577403386252718ef6fd4eb122b8975b57b 100644 (file)
@@ -21,6 +21,7 @@ mpls.h \
 packet.h \
 packet_manager.h \
 protocol_ids.h \
+ssl.h \
 tcp.h \
 tcp_options.h \
 teredo.h \
@@ -35,7 +36,8 @@ packet_manager.cc \
 packet.cc \
 ip.cc \
 ipv4_options.cc \
-tcp_options.cc
+tcp_options.cc \
+ssl.cc
 
 
 AM_CXXFLAGS = @AM_CXXFLAGS@
diff --git a/src/protocols/ssl.cc b/src/protocols/ssl.cc
new file mode 100644 (file)
index 0000000..8426e42
--- /dev/null
@@ -0,0 +1,582 @@
+/*
+ * Copyright (C) 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.
+ *
+*/
+
+/*
+ * Adam Keeton
+ * ssl.c
+ * 10/09/07
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef WIN32
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include "ssl.h"
+#include "snort_types.h"
+#include "packet.h"
+
+#define THREE_BYTE_LEN(x) (x[2] | x[1] << 8 | x[0] << 16)
+
+#define SSL_ERROR_FLAGS \
+    (SSL_BOGUS_HS_DIR_FLAG | \
+    SSL_BAD_VER_FLAG | \
+    SSL_BAD_TYPE_FLAG | \
+    SSL_UNKNOWN_FLAG)
+
+#define SSL3_FIRST_BYTE 0x16
+#define SSL3_SECOND_BYTE 0x03
+#define SSL2_CHELLO_BYTE 0x01
+#define SSL2_SHELLO_BYTE 0x04
+
+/* very simplistic - just enough to say this is binary data - the rules will make a final
+* judgement.  Should maybe add an option to the imap configuration to enable the
+* continuing of command inspection like ftptelnet. */
+bool IsTlsClientHello(const uint8_t* ptr, const uint8_t* end)
+{
+    /* at least 3 bytes of data - see below */
+    if ((end - ptr) < 3)
+        return false;
+
+    if ((ptr[0] == SSL3_FIRST_BYTE) && (ptr[1] == SSL3_SECOND_BYTE))
+    {
+        /* TLS v1 or SSLv3 */
+        return true;
+    }
+    else if ((ptr[2] == SSL2_CHELLO_BYTE) || (ptr[3] == SSL2_CHELLO_BYTE))
+    {
+        /* SSLv2 */
+        return true;
+    }
+
+    return false;
+}
+
+/* this may at least tell us whether the server accepted the client hello by the presence
+ * of binary data */
+
+bool IsTlsServerHello(const uint8_t* ptr, const uint8_t* end)
+{
+    /* at least 3 bytes of data - see below */
+    if ((end - ptr) < 3)
+        return false;
+
+    if ((ptr[0] == SSL3_FIRST_BYTE) && (ptr[1] == SSL3_SECOND_BYTE))
+    {
+        /* TLS v1 or SSLv3 */
+        return true;
+    }
+    else if (ptr[2] == SSL2_SHELLO_BYTE)
+    {
+        /* SSLv2 */
+        return true;
+    }
+
+    return false;
+}
+
+bool IsSSL(const uint8_t* ptr, int len, int pkt_flags)
+{
+    uint32_t ssl_flags = SSL_decode(ptr, len, pkt_flags, 0, NULL, NULL, 0);
+
+    if ((ssl_flags != SSL_ARG_ERROR_FLAG) &&
+        !(ssl_flags & SSL_ERROR_FLAGS))
+    {
+        return true;
+    }
+
+    return false;
+}
+
+static uint32_t SSL_decode_version_v3(uint8_t major, uint8_t minor)
+{
+    /* Should only be called internally and by functions which have previously
+     * validated their arguments */
+
+    if (major == 3)
+    {
+        /* Minor version */
+        switch (minor)
+        {
+        case 0:
+            return SSL_VER_SSLV3_FLAG;
+            break;
+        case 1:
+            return SSL_VER_TLS10_FLAG;
+            break;
+        case 2:
+            return SSL_VER_TLS11_FLAG;
+            break;
+        case 3:
+            return SSL_VER_TLS12_FLAG;
+            break;
+        default:
+            return SSL_BAD_VER_FLAG;
+        }
+    }
+    /* This is a special case. Technically, major == 0, minor == 2 is SSLv2.
+     * But if this traffic was SSLv2, this code path would not have been
+     * exercised. */
+    else if (minor == 2)
+    {
+        return SSL_BAD_VER_FLAG;
+    }
+
+    return SSL_BAD_VER_FLAG;
+}
+
+static uint32_t SSL_decode_handshake_v3(const uint8_t* pkt, int size,
+    uint32_t cur_flags, uint32_t pkt_flags)
+{
+    SSL_handshake_t* handshake;
+    SSL_handshake_hello_t* hello;
+    uint32_t hs_len;
+    uint32_t retval = 0;
+
+    while (size > 0)
+    {
+        if (size < (int)SSL_HS_PAYLOAD_OFFSET)
+        {
+            retval |= SSL_TRUNCATED_FLAG;
+            break;
+        }
+
+        /* Note, handhshake version field is optional depending on type
+           Will recast to different type as necessary. */
+        handshake = (SSL_handshake_t*)pkt;
+        pkt += SSL_HS_PAYLOAD_OFFSET;
+        size -= SSL_HS_PAYLOAD_OFFSET;
+
+        /* The code below effectively implements the following:
+         *      hs_len = 0;
+         *      memcpy(&hs_len, handshake->length, 3);
+         *      hs_len = ntohl(hs_len);
+         * It was written this way for performance */
+        hs_len = THREE_BYTE_LEN(handshake->length);
+
+        switch (handshake->type)
+        {
+        case SSL_HS_CHELLO:
+            if (pkt_flags & PKT_FROM_SERVER)
+                retval |= SSL_BOGUS_HS_DIR_FLAG;
+            else
+                retval |= SSL_CLIENT_HELLO_FLAG | SSL_CUR_CLIENT_HELLO_FLAG;
+
+            /* This type of record contains a version string.
+               Make sure there is room for a version. */
+            if (size < (int)sizeof(uint16_t))
+            {
+                retval |= SSL_TRUNCATED_FLAG;
+                break;
+            }
+
+            hello = (SSL_handshake_hello_t*)handshake;
+            retval |= SSL_decode_version_v3(hello->major, hello->minor);
+
+            /* Compare version of record with version of handshake */
+            if ((cur_flags & SSL_VERFLAGS) != (retval & SSL_VERFLAGS))
+                retval |= SSL_BAD_VER_FLAG;
+
+            break;
+
+        case SSL_HS_SHELLO:
+            if (pkt_flags & PKT_FROM_SERVER)
+                retval |= SSL_SERVER_HELLO_FLAG | SSL_CUR_SERVER_HELLO_FLAG;
+            else
+                retval |= SSL_BOGUS_HS_DIR_FLAG;
+
+            /* This type of record contains a version string. */
+            if (size < (int)sizeof(uint16_t))
+            {
+                retval |= SSL_TRUNCATED_FLAG;
+                break;
+            }
+
+            hello = (SSL_handshake_hello_t*)handshake;
+            retval |= SSL_decode_version_v3(hello->major, hello->minor);
+
+            /* Compare version of record with version of handshake */
+            if ((cur_flags & SSL_VERFLAGS) != (retval & SSL_VERFLAGS))
+                retval |= SSL_BAD_VER_FLAG;
+
+            break;
+
+        case SSL_HS_SHELLO_DONE:
+            if (pkt_flags & PKT_FROM_SERVER)
+                retval |= SSL_HS_SDONE_FLAG;
+            else
+                retval |= SSL_BOGUS_HS_DIR_FLAG;
+            break;
+
+        case SSL_HS_SKEYX:
+            if (pkt_flags & PKT_FROM_SERVER)
+                retval |= SSL_SERVER_KEYX_FLAG | SSL_CUR_SERVER_KEYX_FLAG;
+            else
+                retval |= SSL_BOGUS_HS_DIR_FLAG;
+            break;
+
+        case SSL_HS_CKEYX:
+            if (pkt_flags & PKT_FROM_SERVER)
+                retval |= SSL_BOGUS_HS_DIR_FLAG;
+            else
+                retval |= SSL_CLIENT_KEYX_FLAG | SSL_CUR_CLIENT_KEYX_FLAG;
+            break;
+
+        case SSL_HS_CERT:
+            retval |= SSL_CERTIFICATE_FLAG;
+            break;
+
+        /* The following types are not presently of interest */
+        case SSL_HS_HELLO_REQ:
+        case SSL_HS_CERT_VERIFY:
+        case SSL_HS_CERT_REQ:
+        case SSL_CERT_URL:      /* RFC 3546 */
+        case SSL_CERT_STATUS:     /* RFC 3546 */
+            break;
+
+        /* Will never see this since it's always encrypted */
+        case SSL_HS_FINISHED:
+        default:
+            /* Could be either a bad type or an encrypted handshake record
+               If the record is encrypted, the type will likely appear bogus. */
+            return SSL_POSSIBLE_HS_FLAG | SSL_POSSIBLY_ENC_FLAG;
+        }
+
+        size -= hs_len;
+        pkt += hs_len;
+    }
+
+    if (size < 0)
+        retval |= SSL_TRUNCATED_FLAG;
+
+    return retval;
+}
+
+static uint32_t SSL_decode_v3(const uint8_t* pkt, int size, uint32_t pkt_flags,
+    uint8_t* alert_flags, uint16_t* partial_rec_len, int max_hb_len)
+{
+    SSL_record_t* record;
+    uint32_t retval = 0;
+    uint16_t reclen;
+    uint16_t hblen;
+    int ccs = 0;   /* Set if we see a Change Cipher Spec and reset after the next record */
+    SSL_heartbeat* heartbeat;
+    uint16_t psize = 0;
+
+    if ( size && partial_rec_len && *partial_rec_len > 0)
+    {
+        if (size < (int)(*partial_rec_len))
+        {
+            *partial_rec_len = *partial_rec_len - size;
+            retval |= SSL_TRUNCATED_FLAG;
+            return retval;
+        }
+        else
+        {
+            pkt += *partial_rec_len;
+            size -= *partial_rec_len;
+        }
+        *partial_rec_len = 0;
+    }
+
+    while (size > 0)
+    {
+        if (size < (int)SSL_REC_PAYLOAD_OFFSET)
+        {
+            retval |= SSL_TRUNCATED_FLAG;
+            break;
+        }
+
+        record = (SSL_record_t*)pkt;
+        pkt += SSL_REC_PAYLOAD_OFFSET;
+        size -= SSL_REC_PAYLOAD_OFFSET;
+
+        retval |= SSL_decode_version_v3(record->major, record->minor);
+
+        reclen = ntohs(record->length);
+
+        psize = (size < reclen) ? (reclen - size) : 0;
+
+        switch (record->type)
+        {
+        case SSL_CHANGE_CIPHER_REC:
+            retval |= SSL_CHANGE_CIPHER_FLAG;
+
+            /* If there is another record, mark it as possibly encrypted */
+            if ((size - (int)reclen) > 0)
+                retval |= SSL_POSSIBLY_ENC_FLAG;
+
+            ccs = 1;
+            break;
+
+        case SSL_ALERT_REC:
+            retval |= SSL_ALERT_FLAG;
+            ccs = 0;
+            break;
+        case SSL_HEARTBEAT_REC:
+            retval |= SSL_HEARTBEAT_SEEN;
+            ccs = 0;
+            if ((size < (int)sizeof(SSL_heartbeat)) || !max_hb_len || !alert_flags)
+                break;
+            heartbeat = (SSL_heartbeat*)pkt;
+            if ((heartbeat->type) == SSL_HEARTBEAT_REQUEST)
+            {
+                hblen = ntohs(heartbeat->length);
+                if (hblen > max_hb_len)
+                    *alert_flags = SSL_HEARTBLEED_REQUEST;
+            }
+            else if ((heartbeat->type) == SSL_HEARTBEAT_RESPONSE)
+            {
+                if (reclen > max_hb_len )
+                    *alert_flags = SSL_HEARTBLEED_RESPONSE;
+            }
+            else if (!(retval & SSL_BAD_VER_FLAG))
+            {
+                if (reclen > max_hb_len )
+                    *alert_flags = SSL_HEARTBLEED_UNKNOWN;
+            }
+            break;
+
+        case SSL_HANDSHAKE_REC:
+            /* If the CHANGE_CIPHER_FLAG is set, the following handshake
+             * record should be encrypted */
+            if (!(retval & SSL_CHANGE_CIPHER_FLAG))
+            {
+                int hsize = size < (int)reclen ? size : (int)reclen;
+                retval |= SSL_decode_handshake_v3(pkt, hsize, retval, pkt_flags);
+            }
+            else if (ccs)
+            {
+                /* If we just got a change cipher spec, the next record must
+                 * be a finished encrypted, which has no type, so it will fall
+                 * into this default case, but it's good and we still need to
+                 * see client and server app data */
+                retval |= SSL_HS_SDONE_FLAG;
+            }
+
+            ccs = 0;
+            break;
+
+        case SSL_APPLICATION_REC:
+            if (pkt_flags & PKT_FROM_SERVER)
+                retval |= SSL_SAPP_FLAG;
+            else
+                retval |= SSL_CAPP_FLAG;
+            ccs = 0;
+            break;
+
+        default:
+            retval |= SSL_BAD_TYPE_FLAG;
+            ccs = 0;
+            break;
+        }
+
+        size -= reclen;
+        pkt += reclen;
+    }
+
+    if (size < 0)
+        retval |= SSL_TRUNCATED_FLAG;
+
+    if (!(retval & SSL_VERFLAGS) || (retval & SSL_BAD_VER_FLAG))
+    {
+        psize = 0;
+        retval = retval | SSL_UNKNOWN_FLAG;
+    }
+
+    if (partial_rec_len)
+        *partial_rec_len = psize;
+
+    return retval;
+}
+
+// See RFCs 6101, 2246, 4346 and 5246 for SSL 3.0, TLS 1.0, 1.1 and 1.2 respectively
+// Appendix E. Backward Compatibility With SSL
+static inline bool SSL_v3_back_compat_v2(SSLv2_chello_t* chello)
+{
+    if ((chello->major == 3) && (chello->minor <= 3))
+        return true;
+    return false;
+}
+
+static uint32_t SSL_decode_v2(const uint8_t* pkt, int size, uint32_t pkt_flags)
+{
+    uint16_t reclen;
+    SSLv2_chello_t* chello;
+    SSLv2_shello_t* shello;
+    uint32_t retval = 0;
+    SSLv2_record_t* record = (SSLv2_record_t*)pkt;
+
+    while (size > 0)
+    {
+        if (size < SSL_V2_MIN_LEN)
+        {
+            retval |= SSL_TRUNCATED_FLAG | SSL_UNKNOWN_FLAG;
+            break;
+        }
+
+        /* Note: top bit has special meaning and is not included
+         * with the length */
+        reclen = ntohs(record->length) & 0x7fff;
+
+        switch (record->type)
+        {
+        case SSL_V2_CHELLO:
+            if (pkt_flags & PKT_FROM_SERVER)
+                retval |= SSL_BOGUS_HS_DIR_FLAG;
+            else
+                retval |= SSL_CLIENT_HELLO_FLAG | SSL_CUR_CLIENT_HELLO_FLAG;
+
+            if (size < (int)sizeof(SSLv2_chello_t))
+            {
+                retval |= SSL_TRUNCATED_FLAG | SSL_UNKNOWN_FLAG;
+                break;
+            }
+
+            chello = (SSLv2_chello_t*)pkt;
+
+            // Check for SSLv3/TLS backward compatibility
+            if (SSL_v3_back_compat_v2(chello))
+                retval |= SSL_V3_BACK_COMPAT_V2;
+            else if (chello->minor != 2)
+                retval |= SSL_BAD_VER_FLAG | SSL_UNKNOWN_FLAG;
+
+            break;
+
+        case SSL_V2_SHELLO:
+            if (pkt_flags & PKT_FROM_CLIENT)
+                retval |= SSL_BOGUS_HS_DIR_FLAG;
+            else
+                retval |= SSL_SERVER_HELLO_FLAG | SSL_CUR_SERVER_HELLO_FLAG;
+
+            if (size < (int)sizeof(SSLv2_shello_t))
+            {
+                retval |= SSL_TRUNCATED_FLAG | SSL_UNKNOWN_FLAG;
+                break;
+            }
+
+            shello = (SSLv2_shello_t*)pkt;
+
+            if (shello->minor != 2)
+            {
+                retval |= SSL_BAD_VER_FLAG | SSL_UNKNOWN_FLAG;
+                break;
+            }
+
+            break;
+
+        case SSL_V2_CKEY:
+            retval |= SSL_CLIENT_KEYX_FLAG |  SSL_CUR_CLIENT_KEYX_FLAG;
+            break;
+
+        default:
+            return retval | SSL_BAD_TYPE_FLAG | SSL_UNKNOWN_FLAG;
+        }
+
+        size -= (reclen + 2);
+        pkt += (reclen + 2);
+    }
+
+    if (size < 0)
+        retval |= SSL_TRUNCATED_FLAG;
+
+    return retval | SSL_VER_SSLV2_FLAG;
+}
+
+uint32_t SSL_decode(const uint8_t* pkt, int size, uint32_t pkt_flags, uint32_t prev_flags,
+    uint8_t* alert_flags, uint16_t* partial_rec_len, int max_hb_len)
+{
+    SSL_record_t* record;
+    uint16_t reclen;
+    uint32_t datalen;
+
+    if (!pkt || !size)
+        return SSL_ARG_ERROR_FLAG;
+
+    if (size < (int)SSL_REC_PAYLOAD_OFFSET)
+        return SSL_TRUNCATED_FLAG | SSL_UNKNOWN_FLAG;
+
+    if (!( prev_flags & SSL_HS_SDONE_FLAG ))
+    {
+        /* Determine the protocol type. */
+
+        /* Only SSL v2 will have these bits set */
+        if (((pkt[0] & 0x80) || (pkt[0] & 0x40)) && !(partial_rec_len && *partial_rec_len))
+            return SSL_decode_v2(pkt, size, pkt_flags);
+
+        /* If this packet is only 5 bytes, it inconclusive whether its SSLv2 or TLS.
+         * If it is v2, it's definitely truncated anyway.  By decoding a 5 byte
+         * SSLv2 as TLS,the decoder will either catch a bad type, bad version, or
+         * indicate that it is truncated. */
+        if (size == 5)
+            return SSL_decode_v3(pkt, size, pkt_flags, alert_flags, partial_rec_len, max_hb_len);
+
+        /* At this point, 'size' has to be > 5 */
+
+        /* If the field below contains a 2, it's either an SSLv2 client hello or
+         * it is TLS and is containing a server hello. */
+        if (pkt[4] == 2)
+        {
+            /* This could be a TLS server hello.  Check for a TLS version string */
+            if (size >= 10)
+            {
+                if (pkt[9] == 3)
+                {
+                    /* Saw a TLS version, but this could also be an SSHv2 length.
+                      * If it is, check if a hypothetical TLS record-data length agress
+                      * with its record length */
+                    datalen = THREE_BYTE_LEN( (pkt+6) );
+
+                    record = (SSL_record_t*)pkt;
+                    reclen = ntohs(record->length);
+
+                    /* If these lengths match, it's v3
+                       Otherwise, it's v2 */
+                    if (reclen - SSL_HS_PAYLOAD_OFFSET != datalen)
+                        return SSL_decode_v2(pkt, size, pkt_flags);
+                }
+            }
+        }
+        /* Check if it's possibly a SSLv2 server-hello, in which case the version
+         * is at byte 7 */
+        else if (size >= 8 && pkt[7] == 2)
+        {
+            /* A version of '2' at byte 7 overlaps with TLS record-data length.
+             * Check if a hypothetical TLS record-data length agress with its
+             * record length */
+            datalen = THREE_BYTE_LEN( (pkt+6) );
+
+            record = (SSL_record_t*)pkt;
+            reclen = ntohs(record->length);
+
+            /* If these lengths match, it's v3
+               Otherwise, it's v2 */
+            if (reclen - SSL_HS_PAYLOAD_OFFSET != datalen)
+                return SSL_decode_v2(pkt, size, pkt_flags);
+        }
+    }
+
+    return SSL_decode_v3(pkt, size, pkt_flags, alert_flags, partial_rec_len, max_hb_len);
+}
+
diff --git a/src/protocols/ssl.h b/src/protocols/ssl.h
new file mode 100644 (file)
index 0000000..643a41a
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 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.
+ *
+*/
+
+/*
+ * Adam Keeton
+ * ssl.h
+ * 10/09/07
+*/
+
+#ifndef SSL_H
+#define SSL_H
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#define SSL_NO_FLAG             0x00000000
+
+/* SSL record type flags */
+#define SSL_CHANGE_CIPHER_FLAG  0x00000001
+#define SSL_ALERT_FLAG          0x00000002
+#define SSL_POSSIBLE_HS_FLAG    0x00000004 /* For handshakes in TLSv3 that are encrypted */
+#define SSL_CLIENT_HELLO_FLAG   0x00000008
+#define SSL_SERVER_HELLO_FLAG   0x00000010
+#define SSL_CERTIFICATE_FLAG    0x00000020
+#define SSL_SERVER_KEYX_FLAG    0x00000040
+#define SSL_CLIENT_KEYX_FLAG    0x00000080
+#define SSL_CIPHER_SPEC_FLAG    0x00000100
+#define SSL_SFINISHED_FLAG      0x00000200
+#define SSL_SAPP_FLAG           0x00000400
+#define SSL_CAPP_FLAG           0x00000800
+#define SSL_HS_SDONE_FLAG       0x00001000
+#define SSL_HEARTBEAT_SEEN      0x00002000
+
+/* Misc state flag */
+#define SSL_POSSIBLY_ENC_FLAG   0x00004000
+
+/* Version flags */
+#define SSL_VER_SSLV2_FLAG      0x00008000
+#define SSL_VER_SSLV3_FLAG      0x00010000
+#define SSL_VER_TLS10_FLAG      0x00020000
+#define SSL_VER_TLS11_FLAG      0x00040000
+#define SSL_VER_TLS12_FLAG      0x00080000
+
+#define SSL_VERFLAGS \
+    (SSL_VER_SSLV2_FLAG | SSL_VER_SSLV3_FLAG | \
+    SSL_VER_TLS10_FLAG | SSL_VER_TLS11_FLAG | \
+    SSL_VER_TLS12_FLAG)
+
+#define SSL_V3_SERVER_HELLO(x) \
+    (((x) & SSL_CUR_SERVER_HELLO_FLAG) \
+    && ((x) & SSL_VERFLAGS) && (((x) & SSL_VERFLAGS) != SSL_VER_SSLV2_FLAG))
+
+/* For rule state matching. These are only set when presently valid,
+ * and do not stay set across packets. */
+#define SSL_CUR_CLIENT_HELLO_FLAG   0x00100000
+#define SSL_CUR_SERVER_HELLO_FLAG   0x00200000
+#define SSL_CUR_SERVER_KEYX_FLAG    0x00400000
+#define SSL_CUR_CLIENT_KEYX_FLAG    0x00800000
+#define SSL_ENCRYPTED_FLAG          0x01000000 /* Provided for external use */
+#define SSL_UNKNOWN_FLAG            0x02000000 /* Set when we decoded mostly garbage */
+
+#define SSL_STATEFLAGS \
+    (SSL_CUR_CLIENT_HELLO_FLAG | SSL_CUR_SERVER_HELLO_FLAG | \
+    SSL_CUR_SERVER_KEYX_FLAG | SSL_CUR_CLIENT_KEYX_FLAG | \
+    SSL_UNKNOWN_FLAG)
+
+// Flag set when a client uses SSLv3/TLS backward compatibility and sends a
+// SSLv2 Hello specifying an SSLv3/TLS version.
+#define SSL_V3_BACK_COMPAT_V2   0x04000000
+
+/* Error flags */
+#define SSL_BOGUS_HS_DIR_FLAG   0x08000000 /* Record type disagrees with direction */
+#define SSL_TRAILING_GARB_FLAG  0x10000000
+#define SSL_BAD_TYPE_FLAG       0x20000000
+#define SSL_BAD_VER_FLAG        0x40000000
+#define SSL_TRUNCATED_FLAG      0x80000000
+#define SSL_ARG_ERROR_FLAG      0x00000000 /* Note: overloaded with SSL_NO_FLAG */
+
+/* The following flags are not presently of interest:
+* #define SSL_CERT_URL_FLAG       (RFC 3546)
+* #define SSL_CERT_STATUS_FLAG    (RFC 3546)
+* #define SSL_CFINISHED_FLAG      This is contained in encrypted data
+* #define SSL_HS_FINISHED_FLAG    Ignored for our purposes
+*/
+
+/* The constants used below are from RFC 2246 */
+
+/* SSLv3 & TLS Record types */
+#define SSL_CHANGE_CIPHER_REC 20
+#define SSL_ALERT_REC 21
+#define SSL_HANDSHAKE_REC 22
+#define SSL_APPLICATION_REC 23
+#define SSL_HEARTBEAT_REC 24
+
+/* SSLv3 heartbeat types */
+#define SSL_HEARTBEAT_REQUEST 1
+#define SSL_HEARTBEAT_RESPONSE 2
+
+/* SSLv3 & TLS handshake types */
+#define SSL_HS_HELLO_REQ 0
+#define SSL_HS_CHELLO    1
+#define SSL_HS_SHELLO    2
+#define SSL_HS_CERT      11
+#define SSL_HS_SKEYX     12
+#define SSL_HS_CERT_REQ  13
+#define SSL_HS_SHELLO_DONE 14
+#define SSL_HS_CERT_VERIFY 15
+#define SSL_HS_CKEYX     16
+#define SSL_HS_FINISHED  20
+#define SSL_CERT_URL     21
+#define SSL_CERT_STATUS  22
+
+/* SSLv2 handshake types */
+#define SSL_V2_CHELLO  1
+#define SSL_V2_CKEY    2
+#define SSL_V2_SHELLO  4
+
+#ifdef WIN32
+#pragma pack(push,ssl_hdrs,1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct _SSL_record
+{
+    uint8_t type;
+    uint8_t major;
+    uint8_t minor;
+    uint16_t length;
+} SSL_record_t;
+
+#define SSL_REC_PAYLOAD_OFFSET (sizeof(uint8_t) * 5)
+
+typedef struct _SSL_heartbeat
+{
+    uint8_t type;
+    uint16_t length;
+} SSL_heartbeat;
+
+typedef struct _SSL_handshake
+{
+    uint8_t type;
+    uint8_t length[3];
+} SSL_handshake_t;
+
+typedef struct _SSL_handshake_hello
+{
+    uint8_t type;
+    uint8_t length[3];
+    uint8_t major;
+    uint8_t minor;
+} SSL_handshake_hello_t;
+
+// http://www.mozilla.org/projects/security/pki/nss/ssl/draft02.html
+typedef struct _SSLv2_record
+{
+    uint16_t length;
+    uint8_t type;
+} SSLv2_record_t;
+
+typedef struct _SSLv2_chello
+{
+    uint16_t length;
+    uint8_t type;
+    uint8_t major;
+    uint8_t minor;
+} SSLv2_chello_t;
+
+typedef struct _SSLv2_shello
+{
+    uint16_t length;
+    uint8_t type;
+    uint8_t ssnid;
+    uint8_t certtype;
+    uint8_t major;
+    uint8_t minor;
+} SSLv2_shello_t;
+
+#define SSL_V2_MIN_LEN 5
+
+#ifdef WIN32
+#pragma pack(pop,ssl_hdrs)
+#else
+#pragma pack()
+#endif
+
+#define SSL_HS_PAYLOAD_OFFSET (sizeof(uint8_t) * 4) /* Type and length fields */
+
+#define SSL_BAD_HS(x) (x & SSL_BOGUS_HS_DIR_FLAG)
+#define SSL_IS_HANDSHAKE(x) \
+    (x & (SSL_CLIENT_HELLO_FLAG | SSL_SERVER_HELLO_FLAG | \
+    SSL_CERTIFICATE_FLAG | SSL_SERVER_KEYX_FLAG | \
+    SSL_CLIENT_KEYX_FLAG | SSL_CIPHER_SPEC_FLAG))
+#define SSL_IS_CHELLO(x) (x & SSL_CLIENT_HELLO_FLAG)
+#define SSL_IS_SHELLO(x) (x & SSL_SERVER_HELLO_FLAG)
+#define SSL_IS_CKEYX(x) (x & SSL_CLIENT_KEYX_FLAG)
+#define SSL_IS_APP(x) ((x & SSL_SAPP_FLAG) || (x & SSL_CAPP_FLAG))
+#define SSL_IS_ALERT(x) (x & SSL_ALERT_FLAG)
+#define SSL_CLEAR_TEMPORARY_FLAGS(x) x &= ~SSL_STATEFLAGS;
+/* Verifies that the error flags haven't been triggered */
+#define SSL_IS_CLEAN(x) \
+    !(x & (SSL_BOGUS_HS_DIR_FLAG | SSL_TRUNCATED_FLAG | \
+    SSL_BAD_VER_FLAG | SSL_BAD_TYPE_FLAG | \
+    SSL_TRAILING_GARB_FLAG | SSL_UNKNOWN_FLAG))
+
+#define SSL_HEARTBLEED_REQUEST 0x01
+#define SSL_HEARTBLEED_RESPONSE 0x02
+#define SSL_HEARTBLEED_UNKNOWN 0x03
+
+uint32_t SSL_decode(const uint8_t* pkt, int size, uint32_t pktflags, uint32_t prevflags,
+    uint8_t* alert_flags, uint16_t* partial_rec_len, int hblen);
+bool IsTlsClientHello(const uint8_t* ptr, const uint8_t* end);
+bool IsTlsServerHello(const uint8_t* ptr, const uint8_t* end);
+bool IsSSL(const uint8_t* ptr, int len, int pkt_flags);
+
+#endif
+
index df448bbc12e88fca99ddf02f3e8defec3fef17bd..c071cdcf89226cd2e73f94b3ca4084de1efe5d94 100644 (file)
@@ -3,7 +3,9 @@ add_subdirectory(back_orifice)
 add_subdirectory(ftp_telnet)
 add_subdirectory(dns)
 add_subdirectory(http_inspect)
+add_subdirectory(imap)
 add_subdirectory(nhttp_inspect)
+add_subdirectory(pop)
 add_subdirectory(rpc_decode)
 add_subdirectory(ssh)
 add_subdirectory(wizard)
@@ -13,7 +15,9 @@ if (STATIC_INSPECTORS)
         back_orifice
         ftp_telnet
         dns
+       imap
         nhttp_inspect
+       pop
         rpc_decode
         ssh
         wizard
index 067ee9aab5d8b440c0bee97aa9f5eb9412a35fcc..f744eb7a0e9122116c2c4cd6078887eba346dc37 100644 (file)
@@ -21,7 +21,9 @@ back_orifice \
 dns \
 ftp_telnet \
 http_inspect \
+imap \
 nhttp_inspect \
+pop \
 rpc_decode \
 ssh \
 wizard
index 50339a0a875f6a1eb08a0ac2187495f4452257d7..a017c66828fbc575a66318ae3a9bdb0b0dc3aa0f 100644 (file)
@@ -653,7 +653,7 @@ int HttpInspectMain(HTTPINSPECT_CONF* conf, Packet* p)
                 if (hsd->mime_ssn)
                 {
                     uint8_t* end = ( uint8_t*)(p->data) + p->dsize;
-                    file_api->process_mime_data(p, p->data, end, end, end, hsd->mime_ssn, 1);
+                    file_api->process_mime_data(p, p->data, end, hsd->mime_ssn, 1, false);
                 }
                 else if (file_api->get_file_processed_size(p->flow) >0)
                 {
@@ -785,7 +785,7 @@ int HttpInspectMain(HTTPINSPECT_CONF* conf, Packet* p)
 
                         end = (uint8_t*)(session->client.request.post_raw +
                             session->client.request.post_raw_size);
-                        file_api->process_mime_data(p, start, end, end, end, hsd->mime_ssn, 1);
+                        file_api->process_mime_data(p, start, end, hsd->mime_ssn, 1, false);
                     }
                     else
                     {
@@ -821,7 +821,7 @@ int HttpInspectMain(HTTPINSPECT_CONF* conf, Packet* p)
                 if (hsd->mime_ssn)
                 {
                     uint8_t* end = ( uint8_t*)(p->data) + p->dsize;
-                    file_api->process_mime_data(p, p->data, end, end, end, hsd->mime_ssn, 1);
+                    file_api->process_mime_data(p, p->data, end, hsd->mime_ssn, 1, false);
                 }
                 else if (file_api->get_file_processed_size(p->flow) >0)
                 {
diff --git a/src/service_inspectors/imap/CMakeLists.txt b/src/service_inspectors/imap/CMakeLists.txt
new file mode 100644 (file)
index 0000000..2aaf1cc
--- /dev/null
@@ -0,0 +1,18 @@
+
+set( FILE_LIST
+    imap.cc
+    imap.h
+    imap_paf.cc
+    imap_paf.h
+    imap_config.h
+    imap_module.cc
+    imap_module.h
+)
+
+if (STATIC_INSPECTORS)
+    add_library( imap STATIC ${FILE_LIST})
+
+else (STATIC_INSPECTORS)
+    add_shared_library(imap inspectors ${FILE_LIST})
+
+endif (STATIC_INSPECTORS)
diff --git a/src/service_inspectors/imap/Makefile.am b/src/service_inspectors/imap/Makefile.am
new file mode 100644 (file)
index 0000000..53a7bf3
--- /dev/null
@@ -0,0 +1,24 @@
+AUTOMAKE_OPTIONS=foreign no-dependencies
+
+file_list = \
+imap_config.h \
+imap.cc \
+imap.h \
+imap_paf.cc \
+imap_paf.h \
+imap_module.cc \
+imap_module.h
+
+if STATIC_INSPECTORS
+noinst_LIBRARIES = libimap.a
+libimap_a_SOURCES = $(file_list)
+else
+shlibdir = $(pkglibdir)/inspectors
+shlib_LTLIBRARIES = libimap.la
+libimap_la_CXXFLAGS = $(AM_CXXFLAGS) -DBUILDING_SO
+libimap_la_LDFLAGS = -export-dynamic -shared
+libimap_la_SOURCES = $(file_list)
+endif
+
+AM_CXXFLAGS = @AM_CXXFLAGS@
+
diff --git a/src/service_inspectors/imap/imap.cc b/src/service_inspectors/imap/imap.cc
new file mode 100644 (file)
index 0000000..e0e7266
--- /dev/null
@@ -0,0 +1,911 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 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.
+//--------------------------------------------------------------------------
+//
+
+/*
+ * IMAP preprocessor
+ * Author: Bhagyashree Bantwal <bbantwal@cisco.com>
+ *
+ *
+ */
+#include "imap.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "snort_types.h"
+#include "snort_debug.h"
+
+#include "imap_module.h"
+#include "profiler.h"
+#include "stream/stream_api.h"
+#include "file_api/file_api.h"
+#include "parser.h"
+#include "framework/inspector.h"
+#include "utils/sfsnprintfappend.h"
+#include "target_based/sftarget_protocol_reference.h"
+#include "imap_paf.h"
+#include "search_engines/search_tool.h"
+#include "sf_email_attach_decode.h"
+#include "protocols/ssl.h"
+
+THREAD_LOCAL ProfileStats imapPerfStats;
+THREAD_LOCAL SimpleStats imapstats;
+
+IMAPToken imap_known_cmds[] =
+{
+    { "APPEND",          6, CMD_APPEND },
+    { "AUTHENTICATE",    12, CMD_AUTHENTICATE },
+    { "CAPABILITY",      10, CMD_CAPABILITY },
+    { "CHECK",           5, CMD_CHECK },
+    { "CLOSE",           5, CMD_CLOSE },
+    { "COMPARATOR",      10, CMD_COMPARATOR },
+    { "COMPRESS",        8, CMD_COMPRESS },
+    { "CONVERSIONS",     11, CMD_CONVERSIONS },
+    { "COPY",            4, CMD_COPY },
+    { "CREATE",          6, CMD_CREATE },
+    { "DELETE",          6, CMD_DELETE },
+    { "DELETEACL",       9, CMD_DELETEACL },
+    { "DONE",            4, CMD_DONE },
+    { "EXAMINE",         7, CMD_EXAMINE },
+    { "EXPUNGE",         7, CMD_EXPUNGE },
+    { "FETCH",           5, CMD_FETCH },
+    { "GETACL",          6, CMD_GETACL },
+    { "GETMETADATA",     11, CMD_GETMETADATA },
+    { "GETQUOTA",        8, CMD_GETQUOTA },
+    { "GETQUOTAROOT",    12, CMD_GETQUOTAROOT },
+    { "IDLE",            4, CMD_IDLE },
+    { "LIST",            4, CMD_LIST },
+    { "LISTRIGHTS",      10, CMD_LISTRIGHTS },
+    { "LOGIN",           5, CMD_LOGIN },
+    { "LOGOUT",          6, CMD_LOGOUT },
+    { "LSUB",            4, CMD_LSUB },
+    { "MYRIGHTS",        8, CMD_MYRIGHTS },
+    { "NOOP",            4, CMD_NOOP },
+    { "NOTIFY",          6, CMD_NOTIFY },
+    { "RENAME",          6, CMD_RENAME },
+    { "SEARCH",          6, CMD_SEARCH },
+    { "SELECT",          6, CMD_SELECT },
+    { "SETACL",          6, CMD_SETACL },
+    { "SETMETADATA",     11, CMD_SETMETADATA },
+    { "SETQUOTA",        8, CMD_SETQUOTA },
+    { "SORT",            4, CMD_SORT },
+    { "STARTTLS",        8, CMD_STARTTLS },
+    { "STATUS",          6, CMD_STATUS },
+    { "STORE",           5, CMD_STORE },
+    { "SUBSCRIBE",       9, CMD_SUBSCRIBE },
+    { "THREAD",          6, CMD_THREAD },
+    { "UID",             3, CMD_UID },
+    { "UNSELECT",        8, CMD_UNSELECT },
+    { "UNSUBSCRIBE",     11, CMD_UNSUBSCRIBE },
+    { "X",               1, CMD_X },
+    { NULL,              0, 0 }
+};
+
+IMAPToken imap_resps[] =
+{
+    { "CAPABILITY",      10, RESP_CAPABILITY },
+    { "LIST",            4, RESP_LIST },
+    { "LSUB",            4, RESP_LSUB },
+    { "STATUS",          6, RESP_STATUS },
+    { "SEARCH",          6, RESP_SEARCH },
+    { "FLAGS",           5, RESP_FLAGS },
+    { "EXISTS",          6, RESP_EXISTS },
+    { "RECENT",          6, RESP_RECENT },
+    { "EXPUNGE",         7, RESP_EXPUNGE },
+    { "FETCH",           5, RESP_FETCH },
+    { "BAD",             3, RESP_BAD },
+    { "BYE",             3, RESP_BYE },
+    { "NO",              2, RESP_NO },
+    { "OK",              2, RESP_OK },
+    { "PREAUTH",         7, RESP_PREAUTH },
+    { "ENVELOPE",        8, RESP_ENVELOPE },
+    { "UID",             3, RESP_UID },
+    { NULL,   0,  0 }
+};
+
+SearchTool* imap_resp_search_mpse = nullptr;
+SearchTool* imap_cmd_search_mpse = nullptr;
+
+IMAPSearch imap_resp_search[RESP_LAST];
+IMAPSearch imap_cmd_search[CMD_LAST];
+THREAD_LOCAL const IMAPSearch* imap_current_search = NULL;
+THREAD_LOCAL IMAPSearchInfo imap_search_info;
+
+static void snort_imap(IMAP_PROTO_CONF* GlobalConf, Packet* p);
+static void IMAP_ResetState(void*);
+void IMAP_DecodeAlert(void* ds);
+
+MimeMethods imap_mime_methods = { NULL, NULL, IMAP_DecodeAlert, IMAP_ResetState, imap_is_data_end };
+
+unsigned ImapFlowData::flow_id = 0;
+static IMAPData* get_session_data(Flow* flow)
+{
+    ImapFlowData* fd = (ImapFlowData*)flow->get_application_data(
+        ImapFlowData::flow_id);
+
+    return fd ? &fd->session : NULL;
+}
+
+IMAPData* SetNewIMAPData(IMAP_PROTO_CONF* config, Packet* p)
+{
+    IMAPData* imap_ssn;
+    ImapFlowData* fd = new ImapFlowData;
+
+    p->flow->set_application_data(fd);
+    imap_ssn = &fd->session;
+
+    imap_ssn->mime_ssn.log_config = &(config->log_config);
+    imap_ssn->mime_ssn.decode_conf = &(config->decode_conf);
+    imap_ssn->mime_ssn.methods = &(imap_mime_methods);
+    if (file_api->set_log_buffers(&(imap_ssn->mime_ssn.log_state), &(config->log_config)) < 0)
+    {
+        return NULL;
+    }
+
+    if (p->packet_flags & SSNFLAG_MIDSTREAM)
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Got midstream packet - "
+            "setting state to unknown\n"); );
+        imap_ssn->state = STATE_UNKNOWN;
+    }
+
+    imap_ssn->body_read = imap_ssn->body_len = 0;
+
+    return imap_ssn;
+}
+
+void IMAP_DecodeAlert(void* ds)
+{
+    Email_DecodeState* decode_state = (Email_DecodeState*)ds;
+    switch ( decode_state->decode_type )
+    {
+    case DECODE_B64:
+        SnortEventqAdd(GID_IMAP, IMAP_B64_DECODING_FAILED);
+        break;
+    case DECODE_QP:
+        SnortEventqAdd(GID_IMAP, IMAP_QP_DECODING_FAILED);
+        break;
+    case DECODE_UU:
+        SnortEventqAdd(GID_IMAP, IMAP_UU_DECODING_FAILED);
+        break;
+
+    default:
+        break;
+    }
+}
+
+void IMAP_SearchInit(void)
+{
+    const IMAPToken* tmp;
+    imap_cmd_search_mpse = new SearchTool();
+    if (imap_cmd_search_mpse == NULL)
+    {
+        FatalError("Could not allocate memory for IMAP Command search.\n");
+    }
+    for (tmp = &imap_known_cmds[0]; tmp->name != NULL; tmp++)
+    {
+        imap_cmd_search[tmp->search_id].name = tmp->name;
+        imap_cmd_search[tmp->search_id].name_len = tmp->name_len;
+        imap_cmd_search_mpse->add(tmp->name, tmp->name_len, tmp->search_id);
+    }
+    imap_cmd_search_mpse->prep();
+
+    imap_resp_search_mpse = new SearchTool();
+    if (imap_resp_search_mpse == NULL)
+    {
+        FatalError("Could not allocate memory for IMAP Response search.\n");
+    }
+    for (tmp = &imap_resps[0]; tmp->name != NULL; tmp++)
+    {
+        imap_resp_search[tmp->search_id].name = tmp->name;
+        imap_resp_search[tmp->search_id].name_len = tmp->name_len;
+        imap_resp_search_mpse->add(tmp->name, tmp->name_len, tmp->search_id);
+    }
+    imap_resp_search_mpse->prep();
+}
+
+void IMAP_SearchFree(void)
+{
+    if (imap_cmd_search_mpse != NULL)
+        delete imap_cmd_search_mpse;
+
+    if (imap_resp_search_mpse != NULL)
+        delete imap_resp_search_mpse;
+}
+
+/*
+* Reset IMAP session state
+*
+* @param  none
+*
+* @return none
+*/
+static void IMAP_ResetState(void* ssn)
+{
+    IMAPData* imap_ssn = get_session_data((Flow*)ssn);
+    imap_ssn->state = STATE_COMMAND;
+    imap_ssn->state_flags = 0;
+    imap_ssn->body_read = imap_ssn->body_len = 0;
+}
+
+void IMAP_GetEOL(const uint8_t* ptr, const uint8_t* end,
+    const uint8_t** eol, const uint8_t** eolm)
+{
+    const uint8_t* tmp_eol;
+    const uint8_t* tmp_eolm;
+
+    /* XXX maybe should fatal error here since none of these
+     *      * pointers should be NULL */
+    if (ptr == NULL || end == NULL || eol == NULL || eolm == NULL)
+        return;
+
+    tmp_eol = (uint8_t*)memchr(ptr, '\n', end - ptr);
+    if (tmp_eol == NULL)
+    {
+        tmp_eol = end;
+        tmp_eolm = end;
+    }
+    else
+    {
+        /* end of line marker (eolm) should point to marker and
+         *          * end of line (eol) should point to end of marker */
+        if ((tmp_eol > ptr) && (*(tmp_eol - 1) == '\r'))
+        {
+            tmp_eolm = tmp_eol - 1;
+        }
+        else
+        {
+            tmp_eolm = tmp_eol;
+        }
+
+        /* move past newline */
+        tmp_eol++;
+    }
+
+    *eol = tmp_eol;
+    *eolm = tmp_eolm;
+}
+
+static void PrintImapConf(IMAP_PROTO_CONF* config)
+{
+    if (config == NULL)
+        return;
+
+    LogMessage("IMAP config: \n");
+
+    if (config->decode_conf.b64_depth > -1)
+    {
+        switch (config->decode_conf.b64_depth)
+        {
+        case 0:
+            LogMessage("    Base64 Decoding Depth: %s\n", "Unlimited");
+            break;
+        default:
+            LogMessage("    Base64 Decoding Depth: %d\n", config->decode_conf.b64_depth);
+            break;
+        }
+    }
+    else
+        LogMessage("    Base64 Decoding: %s\n", "Disabled");
+
+    if (config->decode_conf.qp_depth > -1)
+    {
+        switch (config->decode_conf.qp_depth)
+        {
+        case 0:
+            LogMessage("    Quoted-Printable Decoding Depth: %s\n", "Unlimited");
+            break;
+        default:
+            LogMessage("    Quoted-Printable Decoding Depth: %d\n", config->decode_conf.qp_depth);
+            break;
+        }
+    }
+    else
+        LogMessage("    Quoted-Printable Decoding: %s\n", "Disabled");
+    if (config->decode_conf.uu_depth > -1)
+    {
+        switch (config->decode_conf.uu_depth)
+        {
+        case 0:
+            LogMessage("    Unix-to-Unix Decoding Depth: %s\n", "Unlimited");
+            break;
+        default:
+            LogMessage("    Unix-to-Unix Decoding Depth: %d\n", config->decode_conf.uu_depth);
+            break;
+        }
+    }
+    else
+        LogMessage("    Unix-to-Unix Decoding: %s\n", "Disabled");
+
+    if (config->decode_conf.bitenc_depth > -1)
+    {
+        switch (config->decode_conf.bitenc_depth)
+        {
+        case 0:
+            LogMessage("    Non-Encoded MIME attachment Extraction Depth: %s\n", "Unlimited");
+            break;
+        default:
+            LogMessage("    Non-Encoded MIME attachment Extraction Depth: %d\n",
+                config->decode_conf.bitenc_depth);
+            break;
+        }
+    }
+    else
+        LogMessage("    Non-Encoded MIME attachment Extraction: %s\n", "Disabled");
+
+    LogMessage("\n");
+}
+
+static inline int InspectPacket(Packet* p)
+{
+    return PacketHasPAFPayload(p);
+}
+
+static int IMAP_Setup(Packet* p, IMAPData* ssn)
+{
+    int pkt_dir;
+
+    /* Get the direction of the packet. */
+    if ( p->packet_flags & PKT_FROM_SERVER )
+        pkt_dir = IMAP_PKT_FROM_SERVER;
+    else
+        pkt_dir = IMAP_PKT_FROM_CLIENT;
+
+    if (!(ssn->session_flags & IMAP_FLAG_CHECK_SSL))
+        ssn->session_flags |= IMAP_FLAG_CHECK_SSL;
+    /* Check to see if there is a reassembly gap.  If so, we won't know
+     *      * what state we're in when we get the _next_ reassembled packet */
+    if ((pkt_dir != IMAP_PKT_FROM_SERVER) &&
+        (p->packet_flags & PKT_REBUILT_STREAM))
+    {
+        int missing_in_rebuilt =
+            stream.missing_in_reassembled(p->flow, SSN_DIR_FROM_CLIENT);
+
+        if (ssn->session_flags & IMAP_FLAG_NEXT_STATE_UNKNOWN)
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Found gap in previous reassembly buffer - "
+                "set state to unknown\n"); );
+            ssn->state = STATE_UNKNOWN;
+            ssn->session_flags &= ~IMAP_FLAG_NEXT_STATE_UNKNOWN;
+        }
+
+        if (missing_in_rebuilt == SSN_MISSING_BEFORE)
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Found missing packets before "
+                "in reassembly buffer - set state to unknown\n"); );
+            ssn->state = STATE_UNKNOWN;
+        }
+    }
+
+    return pkt_dir;
+}
+
+static int IMAP_SearchStrFound(void* id, void* , int index, void* , void* )
+{
+    int search_id = (int)(uintptr_t)id;
+
+    imap_search_info.id = search_id;
+    imap_search_info.index = index;
+    imap_search_info.length = imap_current_search[search_id].name_len;
+
+    /* Returning non-zero stops search, which is okay since we only look for one at a time */
+    return 1;
+}
+
+/*
+ * Handle COMMAND state
+ *
+ * @param   p       standard Packet structure
+ * @param   ptr     pointer into p->data buffer to start looking at data
+ * @param   end     points to end of p->data buffer
+ *
+ * @return          pointer into p->data where we stopped looking at data
+ *                  will be end of line or end of packet
+ */
+static const uint8_t* IMAP_HandleCommand(Packet* p, IMAPData* imap_ssn, const uint8_t* ptr, const
+    uint8_t* end)
+{
+    const uint8_t* eol;   /* end of line */
+    const uint8_t* eolm;  /* end of line marker */
+    int cmd_found;
+
+    /* get end of line and end of line marker */
+    IMAP_GetEOL(ptr, end, &eol, &eolm);
+
+    /* TODO If the end of line marker coincides with the end of data we can't be
+     * sure that we got a command and not a substring which we could tell through
+     * inspection of the next packet. Maybe a command pending state where the first
+     * char in the next packet is checked for a space and end of line marker */
+
+    /* do not confine since there could be space chars before command */
+    imap_current_search = &imap_cmd_search[0];
+    cmd_found = imap_cmd_search_mpse->find(
+        (const char*)ptr, eolm - ptr, IMAP_SearchStrFound);
+
+    /* if command not found, alert and move on */
+    if (!cmd_found)
+    {
+        if (imap_ssn->state == STATE_UNKNOWN)
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Command not found, but state is "
+                "unknown - checking for SSL\n"); );
+
+            /* check for encrypted */
+
+            if ((imap_ssn->session_flags & IMAP_FLAG_CHECK_SSL) &&
+                (IsSSL(ptr, end - ptr, p->packet_flags)))
+            {
+                DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Packet is SSL encrypted\n"); );
+
+                imap_ssn->state = STATE_TLS_DATA;
+
+                /* Ignore data */
+                return end;
+            }
+            else
+            {
+                DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Not SSL - try data state\n"); );
+                /* don't check for ssl again in this packet */
+                if (imap_ssn->session_flags & IMAP_FLAG_CHECK_SSL)
+                    imap_ssn->session_flags &= ~IMAP_FLAG_CHECK_SSL;
+
+                imap_ssn->state = STATE_DATA;
+                //imap_ssn->data_state = STATE_DATA_UNKNOWN;
+
+                return ptr;
+            }
+        }
+        else
+        {
+            SnortEventqAdd(GID_IMAP, IMAP_UNKNOWN_CMD);
+            DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "No known command found\n"); );
+            return eol;
+        }
+    }
+    else
+    {
+        if (imap_ssn->state == STATE_UNKNOWN)
+            imap_ssn->state = STATE_COMMAND;
+    }
+
+    if (imap_search_info.id == CMD_STARTTLS)
+    {
+        if (eol == end)
+            imap_ssn->state = STATE_TLS_CLIENT_PEND;
+    }
+
+    return eol;
+}
+
+/*
+ * Process client packet
+ *
+ * @param   packet  standard Packet structure
+ *
+ * @return  none
+ */
+static void IMAP_ProcessClientPacket(Packet* p, IMAPData* imap_ssn)
+{
+    const uint8_t* ptr = p->data;
+    const uint8_t* end = p->data + p->dsize;
+
+    ptr = IMAP_HandleCommand(p, imap_ssn, ptr, end);
+}
+
+/*
+ * Process server packet
+ *
+ * @param   packet  standard Packet structure
+ *
+ */
+static void IMAP_ProcessServerPacket(Packet* p, IMAPData* imap_ssn)
+{
+    int resp_found;
+    const uint8_t *ptr;
+    const uint8_t *end;
+    const uint8_t *data_end;
+    const uint8_t *eolm;
+    const uint8_t *eol;
+    int resp_line_len;
+    const char *tmp = NULL;
+    uint8_t *body_start = NULL;
+    char *eptr;
+    uint32_t len = 0;
+
+    ptr = p->data;
+    end = p->data + p->dsize;
+
+    while (ptr < end)
+    {
+        if (imap_ssn->state == STATE_DATA)
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "DATA STATE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); );
+            if ( imap_ssn->body_len > imap_ssn->body_read)
+            {
+                len = imap_ssn->body_len - imap_ssn->body_read;
+                if ( (uint32_t)(end - ptr) < len )
+                {
+                    data_end = end;
+                    len = data_end - ptr;
+                }
+                else
+                    data_end = ptr + len;
+                ptr = file_api->process_mime_data(p, ptr, end, &(imap_ssn->mime_ssn), 0, true);
+                if ( ptr < data_end)
+                    len = len - (data_end - ptr);
+
+                imap_ssn->body_read += len;
+
+                continue;
+            }
+            else
+            {
+                imap_ssn->body_len = imap_ssn->body_read = 0;
+                IMAP_ResetState(p->flow);
+            }
+        }
+        IMAP_GetEOL(ptr, end, &eol, &eolm);
+
+        resp_line_len = eol - ptr;
+
+        /* Check for response code */
+        imap_current_search = &imap_resp_search[0];
+        resp_found = imap_resp_search_mpse->find(
+            (const char*)ptr, resp_line_len, IMAP_SearchStrFound);
+
+        if (resp_found > 0)
+        {
+            const uint8_t* cmd_start = ptr + imap_search_info.index;
+            switch (imap_search_info.id)
+            {
+            case RESP_FETCH:
+                imap_ssn->body_len = imap_ssn->body_read = 0;
+                imap_ssn->state = STATE_DATA;
+                tmp = SnortStrcasestr((const char*)cmd_start, (eol - cmd_start), "BODY");
+                if (tmp != NULL)
+                    imap_ssn->state = STATE_DATA;
+                else
+                {
+                    tmp = SnortStrcasestr((const char*)cmd_start, (eol - cmd_start), "RFC822");
+                    if (tmp != NULL)
+                        imap_ssn->state = STATE_DATA;
+                    else
+                        imap_ssn->state = STATE_UNKNOWN;
+                }
+                break;
+            default:
+                break;
+            }
+            if (imap_ssn->state == STATE_DATA)
+            {
+                body_start = (uint8_t*)memchr((char*)ptr, '{', (eol - ptr));
+                if ( body_start == NULL )
+                {
+                    imap_ssn->state = STATE_UNKNOWN;
+                }
+                else
+                {
+                    if ( (body_start + 1) < (uint8_t*)eol )
+                    {
+                        len = (uint32_t)SnortStrtoul((const char*)(body_start + 1), &eptr, 10);
+                        if (*eptr != '}')
+                        {
+                            imap_ssn->state = STATE_UNKNOWN;
+                        }
+                        else
+                            imap_ssn->body_len = len;
+
+                        len = 0;
+                    }
+                    else
+                        imap_ssn->state = STATE_UNKNOWN;
+                }
+            }
+        }
+        else
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_IMAP,
+                "Server response not found - see if it's SSL data\n"); );
+
+            if ((imap_ssn->session_flags & IMAP_FLAG_CHECK_SSL) &&
+                (IsSSL(ptr, end - ptr, p->packet_flags)))
+            {
+                DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Server response is an SSL packet\n"); );
+
+                imap_ssn->state = STATE_TLS_DATA;
+
+                return;
+            }
+            else if (imap_ssn->session_flags & IMAP_FLAG_CHECK_SSL)
+            {
+                imap_ssn->session_flags &= ~IMAP_FLAG_CHECK_SSL;
+            }
+            if ( (*ptr != '*') && (*ptr !='+') && (*ptr != '\r') && (*ptr != '\n') )
+            {
+                SnortEventqAdd(GID_IMAP, IMAP_UNKNOWN_RESP);
+                DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Server response not found\n"); );
+            }
+        }
+
+        ptr = eol;
+    }
+}
+
+/* Main runtime entry point for IMAP preprocessor.
+ * Analyzes IMAP packets for anomalies/exploits.
+ *
+ * PARAMETERS:
+ *
+ * p:    Pointer to current packet to process.
+ * contextp:    Pointer to context block, not used.
+ *
+ * RETURNS:     Nothing.
+ */
+static void snort_imap(IMAP_PROTO_CONF* config, Packet* p)
+{
+    IMAPData* imap_ssn = NULL;
+    int pkt_dir;
+
+    /* Attempt to get a previously allocated IMAP block. */
+    imap_ssn = get_session_data(p->flow);
+
+    if (imap_ssn == NULL)
+    {
+        /* Check the stream session. If it does not currently
+         * have our IMAP data-block attached, create one.
+         */
+        imap_ssn = SetNewIMAPData(config, p);
+
+        if ( !imap_ssn )
+        {
+            /* Could not get/create the session data for this packet. */
+            return;
+        }
+    }
+
+    pkt_dir = IMAP_Setup(p, imap_ssn);
+
+    if (pkt_dir == IMAP_PKT_FROM_CLIENT)
+    {
+        /* This packet should be a tls client hello */
+        if (imap_ssn->state == STATE_TLS_CLIENT_PEND)
+        {
+            if (IsTlsClientHello(p->data, p->data + p->dsize))
+            {
+                DEBUG_WRAP(DebugMessage(DEBUG_IMAP,
+                    "TLS DATA STATE ~~~~~~~~~~~~~~~~~~~~~~~~~\n"); );
+
+                imap_ssn->state = STATE_TLS_SERVER_PEND;
+                return;
+            }
+            else
+            {
+                /* reset state - server may have rejected STARTTLS command */
+                imap_ssn->state = STATE_UNKNOWN;
+            }
+        }
+        if ((imap_ssn->state == STATE_TLS_DATA)
+            || (imap_ssn->state == STATE_TLS_SERVER_PEND))
+        {
+            return;
+        }
+        IMAP_ProcessClientPacket(p, imap_ssn);
+        DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP client packet\n"); );
+    }
+    else
+    {
+        if (imap_ssn->state == STATE_TLS_SERVER_PEND)
+        {
+            if (IsTlsServerHello(p->data, p->data + p->dsize))
+            {
+                imap_ssn->state = STATE_TLS_DATA;
+            }
+            else if (!(stream.get_session_flags(p->flow) & SSNFLAG_MIDSTREAM)
+                && !stream.missed_packets(p->flow, SSN_DIR_BOTH))
+            {
+                /* revert back to command state - assume server didn't accept STARTTLS */
+                imap_ssn->state = STATE_UNKNOWN;
+            }
+            else
+                return;
+        }
+
+        if (imap_ssn->state == STATE_TLS_DATA)
+        {
+            return;
+        }
+        if ( !InspectPacket(p))
+        {
+            /* Packet will be rebuilt, so wait for it */
+            DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Client packet will be reassembled\n"));
+            return;
+        }
+        else if (!(p->packet_flags & PKT_REBUILT_STREAM))
+        {
+            /* If this isn't a reassembled packet and didn't get
+             * inserted into reassembly buffer, there could be a
+             * problem.  If we miss syn or syn-ack that had window
+             * scaling this packet might not have gotten inserted
+             * into reassembly buffer because it fell outside of
+             * window, because we aren't scaling it */
+            imap_ssn->session_flags |= IMAP_FLAG_GOT_NON_REBUILT;
+            imap_ssn->state = STATE_UNKNOWN;
+        }
+        else if (imap_ssn->session_flags & IMAP_FLAG_GOT_NON_REBUILT)
+        {
+            /* This is a rebuilt packet.  If we got previous packets
+             * that were not rebuilt, state is going to be messed up
+             * so set state to unknown. It's likely this was the
+             * beginning of the conversation so reset state */
+            DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Got non-rebuilt packets before "
+                "this rebuilt packet\n"); );
+
+            imap_ssn->state = STATE_UNKNOWN;
+            imap_ssn->session_flags &= ~IMAP_FLAG_GOT_NON_REBUILT;
+        }
+        /* Process as a server packet */
+        IMAP_ProcessServerPacket(p, imap_ssn);
+    }
+}
+
+//-------------------------------------------------------------------------
+// class stuff
+//-------------------------------------------------------------------------
+
+class Imap : public Inspector
+{
+public:
+    Imap(IMAP_PROTO_CONF*);
+    ~Imap();
+
+    bool configure(SnortConfig*) override;
+    void show(SnortConfig*) override;
+    void eval(Packet*) override;
+
+    StreamSplitter* get_splitter(bool c2s) override
+    { return new ImapSplitter(c2s); }
+
+private:
+    IMAP_PROTO_CONF* config;
+};
+
+Imap::Imap(IMAP_PROTO_CONF* pc)
+{
+    config = pc;
+}
+
+Imap::~Imap()
+{
+    if ( config )
+        delete config;
+}
+
+bool Imap::configure(SnortConfig*)
+{
+    config->decode_conf.file_depth = file_api->get_max_file_depth();
+
+    if (config->decode_conf.file_depth > 0)
+        config->log_config.log_filename = 1;
+
+    if (file_api->is_decoding_enabled(&config->decode_conf) )
+    {
+        updateMaxDepth(config->decode_conf.file_depth,
+            &config->decode_conf.max_depth);
+    }
+    file_api->check_decode_config(&config->decode_conf);
+
+    return true;
+}
+
+void Imap::show(SnortConfig*)
+{
+    PrintImapConf(config);
+}
+
+void Imap::eval(Packet* p)
+{
+    PROFILE_VARS;
+    // precondition - what we registered for
+    assert(p->is_tcp() && p->dsize && p->data);
+
+    ++imapstats.total_packets;
+
+    MODULE_PROFILE_START(imapPerfStats);
+
+    snort_imap(config, p);
+
+    MODULE_PROFILE_END(imapPerfStats);
+}
+
+//-------------------------------------------------------------------------
+// api stuff
+//-------------------------------------------------------------------------
+
+static Module* mod_ctor()
+{ return new ImapModule; }
+
+static void mod_dtor(Module* m)
+{ delete m; }
+
+static void imap_init()
+{
+    ImapFlowData::init();
+    IMAP_SearchInit();
+}
+
+static void imap_term()
+{
+    IMAP_SearchFree();
+}
+
+static Inspector* imap_ctor(Module* m)
+{
+    ImapModule* mod = (ImapModule*)m;
+    return new Imap(mod->get_data());
+}
+
+static void imap_dtor(Inspector* p)
+{
+    delete p;
+}
+
+const InspectApi imap_api =
+{
+    {
+        PT_INSPECTOR,
+        sizeof(InspectApi),
+        INSAPI_VERSION,
+        0,
+        API_RESERVED,
+        API_OPTIONS,
+        IMAP_NAME,
+        IMAP_HELP,
+        mod_ctor,
+        mod_dtor
+    },
+    IT_SERVICE,
+    (uint16_t)PktType::TCP,
+    nullptr, // buffers
+    "imap",
+    imap_init,
+    imap_term, // pterm
+    nullptr, // tinit
+    nullptr, // tterm
+    imap_ctor,
+    imap_dtor,
+    nullptr, // ssn
+    nullptr  // reset
+};
+
+#ifdef BUILDING_SO
+SO_PUBLIC const BaseApi* snort_plugins[] =
+{
+    &imap_api.base,
+    nullptr
+};
+#else
+const BaseApi* sin_imap = &imap_api.base;
+#endif
+
diff --git a/src/service_inspectors/imap/imap.h b/src/service_inspectors/imap/imap.h
new file mode 100644 (file)
index 0000000..5ee11eb
--- /dev/null
@@ -0,0 +1,198 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 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.
+//--------------------------------------------------------------------------
+//
+
+/*
+ * imap.h: Definitions, structs, function prototype(s) for
+ * Author: Bhagyashree Bantwal <bbantwal@cisco.com>
+ */
+
+#ifndef IMAP_H
+#define IMAP_H
+
+#include "protocols/packet.h"
+#include "stream/stream_api.h"
+#include "profiler.h"
+#include "imap_config.h"
+/* Direction packet is coming from, if we can figure it out */
+#define IMAP_PKT_FROM_UNKNOWN  0
+#define IMAP_PKT_FROM_CLIENT   1
+#define IMAP_PKT_FROM_SERVER   2
+
+#define SEARCH_CMD       0
+#define SEARCH_RESP      1
+#define SEARCH_HDR       2
+#define SEARCH_DATA_END  3
+#define NUM_SEARCHES  4
+
+#define BOUNDARY     0
+
+#define STATE_DATA             0    /* Data state */
+#define STATE_TLS_CLIENT_PEND  1    /* Got STARTTLS */
+#define STATE_TLS_SERVER_PEND  2    /* Got STARTTLS */
+#define STATE_TLS_DATA         3    /* Successful handshake, TLS encrypted data */
+#define STATE_COMMAND          4
+#define STATE_UNKNOWN          5
+
+#define STATE_DATA_INIT    0
+#define STATE_DATA_HEADER  1    /* Data header section of data state */
+#define STATE_DATA_BODY    2    /* Data body section of data state */
+#define STATE_MIME_HEADER  3    /* MIME header section within data section */
+#define STATE_DATA_UNKNOWN 4
+
+/* session flags */
+#define IMAP_FLAG_NEXT_STATE_UNKNOWN         0x00000004
+#define IMAP_FLAG_GOT_NON_REBUILT            0x00000008
+#define IMAP_FLAG_CHECK_SSL                  0x00000010
+
+/* Maximum length of header chars before colon, based on Exim 4.32 exploit */
+#define MAX_HEADER_NAME_LEN 64
+typedef enum _IMAPCmdEnum
+{
+    CMD_APPEND = 0,
+    CMD_AUTHENTICATE,
+    CMD_CAPABILITY,
+    CMD_CHECK,
+    CMD_CLOSE,
+    CMD_COMPARATOR,
+    CMD_COMPRESS,
+    CMD_CONVERSIONS,
+    CMD_COPY,
+    CMD_CREATE,
+    CMD_DELETE,
+    CMD_DELETEACL,
+    CMD_DONE,
+    CMD_EXAMINE,
+    CMD_EXPUNGE,
+    CMD_FETCH,
+    CMD_GETACL,
+    CMD_GETMETADATA,
+    CMD_GETQUOTA,
+    CMD_GETQUOTAROOT,
+    CMD_IDLE,
+    CMD_LIST,
+    CMD_LISTRIGHTS,
+    CMD_LOGIN,
+    CMD_LOGOUT,
+    CMD_LSUB,
+    CMD_MYRIGHTS,
+    CMD_NOOP,
+    CMD_NOTIFY,
+    CMD_RENAME,
+    CMD_SEARCH,
+    CMD_SELECT,
+    CMD_SETACL,
+    CMD_SETMETADATA,
+    CMD_SETQUOTA,
+    CMD_SORT,
+    CMD_STARTTLS,
+    CMD_STATUS,
+    CMD_STORE,
+    CMD_SUBSCRIBE,
+    CMD_THREAD,
+    CMD_UID,
+    CMD_UNSELECT,
+    CMD_UNSUBSCRIBE,
+    CMD_X,
+    CMD_LAST
+} IMAPCmdEnum;
+
+typedef enum _IMAPRespEnum
+{
+    RESP_CAPABILITY = 0,
+    RESP_LIST,
+    RESP_LSUB,
+    RESP_STATUS,
+    RESP_SEARCH,
+    RESP_FLAGS,
+    RESP_EXISTS,
+    RESP_RECENT,
+    RESP_EXPUNGE,
+    RESP_FETCH,
+    RESP_BAD,
+    RESP_BYE,
+    RESP_NO,
+    RESP_OK,
+    RESP_PREAUTH,
+    RESP_ENVELOPE,
+    RESP_UID,
+    RESP_LAST
+} IMAPRespEnum;
+
+typedef enum _IMAPHdrEnum
+{
+    HDR_CONTENT_TYPE = 0,
+    HDR_CONT_TRANS_ENC,
+    HDR_CONT_DISP,
+    HDR_LAST
+} IMAPHdrEnum;
+struct IMAPSearch
+{
+    const char* name;
+    int name_len;
+};
+
+struct IMAPToken
+{
+    const char* name;
+    int name_len;
+    int search_id;
+};
+
+struct IMAPCmdConfig
+{
+    char alert;                  /*  1 if alert when seen                          */
+    char normalize;              /*  1 if we should normalize this command         */
+    int max_line_len;            /*  Max length of this particular command         */
+};
+
+struct IMAPSearchInfo
+{
+    int id;
+    int index;
+    int length;
+};
+
+struct IMAPData
+{
+    int state;
+    int state_flags;
+    int session_flags;
+    uint32_t body_len;
+    uint32_t body_read;
+    MimeState mime_ssn;
+};
+
+class ImapFlowData : public FlowData
+{
+public:
+    ImapFlowData() : FlowData(flow_id)
+    { memset(&session, 0, sizeof(session)); }
+
+    ~ImapFlowData() { }
+
+    static void init()
+    { flow_id = FlowData::get_flow_id(); }
+
+public:
+    static unsigned flow_id;
+    IMAPData session;
+};
+
+#endif /* IMAP_H */
+
diff --git a/src/service_inspectors/imap/imap_config.h b/src/service_inspectors/imap/imap_config.h
new file mode 100644 (file)
index 0000000..40b1bc5
--- /dev/null
@@ -0,0 +1,33 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 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.
+//--------------------------------------------------------------------------
+//
+
+#ifndef IMAP_CONFIG_H
+#define IMAP_CONFIG_H
+
+#include "file_api/file_api.h"
+
+struct IMAP_PROTO_CONF
+{
+    uint32_t memcap;
+    DecodeConfig decode_conf;
+    MAIL_LogConfig log_config;
+};
+
+#endif
+
diff --git a/src/service_inspectors/imap/imap_module.cc b/src/service_inspectors/imap/imap_module.cc
new file mode 100644 (file)
index 0000000..2aad991
--- /dev/null
@@ -0,0 +1,143 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-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.
+//--------------------------------------------------------------------------
+
+// imap_module.cc author Bhagyashree Bantwal <bbantwal@cisco.com>
+
+#include "imap_module.h"
+#include <assert.h>
+#include <sstream>
+#include "main/snort_config.h"
+
+using namespace std;
+
+#define IMAP_UNKNOWN_CMD_STR                 "Unknown IMAP3 command"
+#define IMAP_UNKNOWN_RESP_STR                "Unknown IMAP3 response"
+#define IMAP_B64_DECODING_FAILED_STR         "Base64 Decoding failed."
+#define IMAP_QP_DECODING_FAILED_STR          "Quoted-Printable Decoding failed."
+#define IMAP_UU_DECODING_FAILED_STR          "Unix-to-Unix Decoding failed."
+
+static const Parameter s_params[] =
+{
+    { "b64_decode_depth", Parameter::PT_INT, "-1:65535", "1460",
+      " base64 decoding depth" },
+
+    { "bitenc_decode_depth", Parameter::PT_INT, "-1:65535", "1460",
+      " Non-Encoded MIME attachment extraction depth" },
+
+    { "qp_decode_depth", Parameter::PT_INT, "-1:65535", "1460",
+      " Quoted Printable decoding depth" },
+
+    { "uu_decode_depth", Parameter::PT_INT, "-1:65535", "1460",
+      " Unix-to-Unix decoding depth" },
+
+    { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+static const RuleMap imap_rules[] =
+{
+    { IMAP_UNKNOWN_CMD, IMAP_UNKNOWN_CMD_STR },
+    { IMAP_UNKNOWN_RESP, IMAP_UNKNOWN_RESP_STR },
+    { IMAP_B64_DECODING_FAILED, IMAP_B64_DECODING_FAILED_STR },
+    { IMAP_QP_DECODING_FAILED, IMAP_QP_DECODING_FAILED_STR },
+    { IMAP_UU_DECODING_FAILED, IMAP_UU_DECODING_FAILED_STR },
+
+    { 0, nullptr }
+};
+
+//-------------------------------------------------------------------------
+// imap module
+//-------------------------------------------------------------------------
+
+ImapModule::ImapModule() : Module(IMAP_NAME, IMAP_HELP, s_params)
+{
+    config = nullptr;
+}
+
+ImapModule::~ImapModule()
+{
+    if ( config )
+        delete config;
+}
+
+const RuleMap* ImapModule::get_rules() const
+{ return imap_rules; }
+
+const PegInfo* ImapModule::get_pegs() const
+{ return simple_pegs; }
+
+PegCount* ImapModule::get_counts() const
+{ return (PegCount*)&imapstats; }
+
+ProfileStats* ImapModule::get_profile() const
+{ return &imapPerfStats; }
+
+bool ImapModule::set(const char*, Value& v, SnortConfig*)
+{
+    if ( v.is("b64_decode_depth") )
+    {
+        int decode_depth = v.get_long();
+
+        if ((decode_depth > 0) && (decode_depth & 3))
+        {
+            decode_depth += 4 - (decode_depth & 3);
+            if (decode_depth > 65535 )
+            {
+                decode_depth = decode_depth - 4;
+            }
+            LogMessage("WARNING: IMAP: 'b64_decode_depth' is not a multiple of 4. "
+                "Rounding up to the next multiple of 4. The new 'b64_decode_depth' is %d.\n",
+                decode_depth);
+        }
+        config->decode_conf.b64_depth = decode_depth;
+    }
+    else if ( v.is("bitenc_decode_depth") )
+        config->decode_conf.bitenc_depth = v.get_long();
+
+    else if ( v.is("qp_decode_depth") )
+        config->decode_conf.qp_depth = v.get_long();
+
+    else if ( v.is("uu_decode_depth") )
+        config->decode_conf.uu_depth = v.get_long();
+
+    else
+        return false;
+
+    return true;
+}
+
+IMAP_PROTO_CONF* ImapModule::get_data()
+{
+    IMAP_PROTO_CONF* tmp = config;
+    config = nullptr;
+    return tmp;
+}
+
+bool ImapModule::begin(const char*, int, SnortConfig*)
+{
+    config = new IMAP_PROTO_CONF;
+    file_api->set_mime_decode_config_defauts(&(config->decode_conf));
+    file_api->set_mime_log_config_defauts(&(config->log_config));
+
+    return true;
+}
+
+bool ImapModule::end(const char*, int, SnortConfig*)
+{
+    return true;
+}
+
diff --git a/src/service_inspectors/imap/imap_module.h b/src/service_inspectors/imap/imap_module.h
new file mode 100644 (file)
index 0000000..ad250b0
--- /dev/null
@@ -0,0 +1,70 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-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.
+//--------------------------------------------------------------------------
+
+// imap_module.h author Bhagyashree Bantwal <bbantwal@cisco.com>
+
+#ifndef IMAP_MODULE_H
+#define IMAP_MODULE_H
+
+#include "framework/module.h"
+#include "framework/bits.h"
+#include "main/thread.h"
+#include "imap_config.h"
+
+#define GID_IMAP 141
+
+#define IMAP_UNKNOWN_CMD            1
+#define IMAP_UNKNOWN_RESP           2
+#define IMAP_B64_DECODING_FAILED    4
+#define IMAP_QP_DECODING_FAILED     5
+#define IMAP_UU_DECODING_FAILED     7
+
+#define IMAP_NAME "imap"
+#define IMAP_HELP "imap inspection"
+
+struct SnortConfig;
+
+extern THREAD_LOCAL SimpleStats imapstats;
+extern THREAD_LOCAL ProfileStats imapPerfStats;
+
+class ImapModule : public Module
+{
+public:
+    ImapModule();
+    ~ImapModule();
+
+    bool set(const char*, Value&, SnortConfig*) override;
+    bool begin(const char*, int, SnortConfig*) override;
+    bool end(const char*, int, SnortConfig*) override;
+
+    unsigned get_gid() const override
+    { return GID_IMAP; }
+
+    const RuleMap* get_rules() const override;
+    const PegInfo* get_pegs() const override;
+    PegCount* get_counts() const override;
+    ProfileStats* get_profile() const override;
+
+    IMAP_PROTO_CONF* get_data();
+
+private:
+    IMAP_PROTO_CONF* config;
+};
+
+#endif
+
diff --git a/src/service_inspectors/imap/imap_paf.cc b/src/service_inspectors/imap/imap_paf.cc
new file mode 100644 (file)
index 0000000..f583a6b
--- /dev/null
@@ -0,0 +1,529 @@
+/****************************************************************************
+ * Copyright (C) 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.
+ *
+ ****************************************************************************/
+
+#include <sys/types.h>
+#include "snort_types.h"
+#include "snort_debug.h"
+
+#include "imap_paf.h"
+#include "imap.h"
+
+extern IMAPToken imap_resps[];
+
+static inline ImapPafData* get_state(Flow* flow, bool c2s)
+{
+    if ( !flow )
+        return nullptr;
+
+    ImapSplitter* s = (ImapSplitter*)stream.get_splitter(flow, c2s);
+    return s ? &s->state : nullptr;
+}
+
+static inline void reset_data_states(ImapPafData* pfdata)
+{
+    // reset MIME info
+    file_api->reset_mime_paf_state(&(pfdata->mime_info));
+
+    // reset server info
+    pfdata->imap_state = IMAP_PAF_CMD_IDENTIFIER;
+
+    // reset fetch data information information
+    pfdata->imap_data_info.paren_cnt = 0;
+    pfdata->imap_data_info.next_letter = 0;
+    pfdata->imap_data_info.length = 0;
+}
+
+static inline bool is_untagged(const uint8_t ch)
+{
+    return (ch == '*' || ch == '+');
+}
+
+static bool parse_literal_length(const uint8_t ch, uint32_t* len)
+{
+    uint32_t length = *len;
+
+    if (isdigit(ch))
+    {
+        uint64_t tmp_len = (10 * length)  + (ch - '0');
+        if (tmp_len < UINT32_MAX)
+        {
+            *len = (uint32_t)tmp_len;
+            return false;
+        }
+        else
+        {
+            *len = 0;
+        }
+    }
+    else if (ch != '}')
+        *len = 0;                      //  ALERT!!  charachter should be a digit or ''}''
+
+    return true;
+}
+
+static void parse_fetch_header(const uint8_t ch, ImapPafData* pfdata)
+{
+    if (pfdata->imap_data_info.esc_nxt_char)
+    {
+        pfdata->imap_data_info.esc_nxt_char = false;
+    }
+    else
+    {
+        switch (ch)
+        {
+        case '{':
+            pfdata->imap_state = IMAP_PAF_DATA_LEN_STATE;
+            break;
+        case '(':
+            pfdata->imap_data_info.paren_cnt++;
+            break;
+
+        case ')':
+            if (pfdata->imap_data_info.paren_cnt > 0)
+                pfdata->imap_data_info.paren_cnt--;
+            break;
+
+        case '\n':
+            if (pfdata->imap_data_info.paren_cnt)
+            {
+                pfdata->imap_state = IMAP_PAF_DATA_STATE;
+            }
+            else
+            {
+                reset_data_states(pfdata);
+            }
+            break;
+
+        case '\\':
+            pfdata->imap_data_info.esc_nxt_char = true;
+            break;
+
+        default:
+            break;
+        }
+    }
+}
+
+/*
+ * Statefully search for the single line termination sequence LF ("\n").
+ *
+ * PARAMS:
+ *        const uint8_t ch - the next character to analyze.
+ *        ImapPafData *pfdata - the struct containing all imap paf information
+ *
+ * RETURNS:
+ *        false - if termination sequence not found
+ *        true - if termination sequence found
+ */
+static bool find_data_end_single_line(const uint8_t ch, ImapPafData* pfdata)
+{
+    if (ch == '\n')
+    {
+        reset_data_states(pfdata);
+        return true;
+    }
+    return false;
+}
+
+/* Flush based on data length*/
+static inline bool literal_complete(ImapPafData* pfdata)
+{
+    if (pfdata->imap_data_info.length)
+    {
+        pfdata->imap_data_info.length--;
+        if (pfdata->imap_data_info.length)
+            return false;
+    }
+
+    return true;
+}
+
+static bool check_imap_data_end(ImapDataEnd* data_end_state,  uint8_t val)
+{
+    switch (*data_end_state)
+    {
+    case IMAP_PAF_DATA_END_UNKNOWN:
+        if (val == ')')
+            *data_end_state = IMAP_PAF_DATA_END_PAREN;
+        break;
+
+    case IMAP_PAF_DATA_END_PAREN:
+        if (val == '\n')
+        {
+            *data_end_state = IMAP_PAF_DATA_END_UNKNOWN;
+            return true;
+        }
+        else if (val != '\r')
+        {
+            *data_end_state = IMAP_PAF_DATA_END_UNKNOWN;
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    return false;
+}
+
+/*
+ * Statefully search for the data termination sequence or a MIME boundary.
+ *
+ * PARAMS:
+ *        const uint8_t ch - the next character to analyze.
+ *        ImapPafData *pfdata - the struct containing all imap paf information
+ *
+ * RETURNS:
+ *        false - if termination sequence not found
+ *        true - if termination sequence found
+ */
+static bool find_data_end_mime_data(const uint8_t ch, ImapPafData* pfdata)
+{
+    if (literal_complete(pfdata)
+        && check_imap_data_end(&(pfdata->data_end_state), ch))
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP PAF: End of Data!\n"); );
+        reset_data_states(pfdata);
+        return true;
+    }
+
+    // check for mime flush point
+    if (file_api->process_mime_paf_data(&(pfdata->mime_info), ch))
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP PAF: Mime Boundary found."
+            " Flushing data!\n"); );
+        return true;
+    }
+
+    return false;
+}
+
+/*
+ * Initial command processing function.  Determine if this command
+ * may be analyzed irregularly ( which currently means if emails
+ * and email attachments need to be analyzed).
+ *
+ * PARAMS:
+ *        const uint8_t ch - the next character to analyze.
+ *        ImapPafData *pfdata - the struct containing all imap paf information
+ */
+static inline void init_command_search(const uint8_t ch, ImapPafData* pfdata)
+{
+    switch (ch)
+    {
+    case 'F':
+    case 'f':
+        // may be a FETCH response
+        pfdata->imap_data_info.next_letter = &(imap_resps[RESP_FETCH].name[1]);
+        break;
+
+    default:
+        // this is not a data command. Search for regular end of line.
+        pfdata->imap_state = IMAP_PAF_REG_STATE;
+    }
+}
+
+/*
+ * Confirms every character in the current sequence is part of the expected
+ * command. After confirmation is complete, IMAP PAF will begin searching
+ * for data. If any character is unexpected, searches for the default
+ * termination sequence.
+ *
+ * PARAMS:
+ *        const uint8_t ch - the next character to analyze.
+ *        ImapPafData *pfdata - the struct containing all imap paf information
+ */
+static inline void parse_command(const uint8_t ch, ImapPafData* pfdata)
+{
+    char val = *(pfdata->imap_data_info.next_letter);
+
+    if (val == '\0' && isblank(ch))
+        pfdata->imap_state = IMAP_PAF_DATA_HEAD_STATE;
+
+    else if (toupper(ch) == toupper(val))
+        pfdata->imap_data_info.next_letter++;
+
+    else
+        pfdata->imap_state = IMAP_PAF_REG_STATE;
+}
+
+/*
+ * Wrapper function for the command parser.  Determines whether this is the
+ * first letter being processed and calls the appropriate processing
+ * function.
+ *
+ * PARAMS:
+ *        const uint8_t ch - the next character to analyze.
+ *        ImapPafData *pfdata - the struct containing all imap paf information
+ */
+static inline void process_command(const uint8_t ch, ImapPafData* pfdata)
+{
+    if (pfdata->imap_data_info.next_letter)
+        parse_command(ch, pfdata);
+    else
+        init_command_search(ch, pfdata);
+}
+
+/*
+ * This function only does something when the character is a blank or a CR/LF.
+ * In those specific cases, this function will set the appropriate next
+ * state information
+ *
+ * PARAMS:
+ *        const uint8_t ch - the next character to analyze.
+ *        ImapPafData *pfdata - the struct containing all imap paf information
+ *        ImapPafData base_state - if a space is not found, revert to this state
+ *        ImapPafData next_state - if a space is found, go to this state
+ * RETURNS:
+ *        true - if the status has been eaten
+ *        false - if a CR or LF has been found
+ */
+static inline void eat_character(const uint8_t ch, ImapPafData* pfdata,
+    ImapPafState base_state, ImapPafState next_state)
+{
+    switch (ch)
+    {
+    case ' ':
+    case '\t':
+        pfdata->imap_state = next_state;
+        break;
+
+    case '\r':
+    case '\n':
+        pfdata->imap_state = base_state;
+        break;
+    }
+}
+
+/*
+ *  defined above in the eat_character function
+ *
+ * Keeping the next two functions to ease any future development
+ * where these cases will no longer be simple or identical
+ */
+static inline void eat_second_argument(const uint8_t ch, ImapPafData* pfdata)
+{
+    eat_character(ch, pfdata, IMAP_PAF_REG_STATE, IMAP_PAF_CMD_SEARCH);
+}
+
+/* explanation in 'eat_second_argument' above */
+static inline void eat_response_identifier(const uint8_t ch, ImapPafData* pfdata)
+{
+    eat_character(ch, pfdata, IMAP_PAF_REG_STATE, IMAP_PAF_CMD_STATUS);
+}
+
+/*
+ * Analyzes the current data for a correct flush point.  Flushes when
+ * a command is complete or a MIME boundary is found.
+ *
+ * PARAMS:
+ *    ImapPafData *pfdata - ImapPaf state tracking structure
+ *    const uint8_t *data - payload data to inspect
+ *    uint32_t len - length of payload data
+ *    uint32_t * fp- pointer to set flush point
+ *
+ * RETURNS:
+ *    StreamSplitter::Status - StreamSplitter::FLUSH if flush point found,
+ *    StreamSplitter::SEARCH otherwise
+ */
+static StreamSplitter::Status imap_paf_server(ImapPafData* pfdata,
+    const uint8_t* data, uint32_t len, uint32_t* fp)
+{
+    uint32_t i;
+    uint32_t flush_len = 0;
+    uint32_t boundary_start = 0;
+
+    pfdata->end_of_data = false;
+
+    for (i = 0; i < len; i++)
+    {
+        uint8_t ch = data[i];
+        switch (pfdata->imap_state)
+        {
+        case IMAP_PAF_CMD_IDENTIFIER:
+            // can be '+', '*', or a tag
+            if (is_untagged(ch))
+            {
+                // continue checking for fetch command
+                pfdata->imap_state = IMAP_PAF_CMD_TAG;
+            }
+            else
+            {
+                // end of a command.  flush at end of line.
+                pfdata->imap_state = IMAP_PAF_FLUSH_STATE;
+            }
+            break;
+
+        case IMAP_PAF_CMD_TAG:
+            eat_response_identifier(ch, pfdata);
+            break;
+
+        case IMAP_PAF_CMD_STATUS:
+            // can be a command name, msg sequence number, msg count, etc...
+            // since we are only interested in fetch, eat this argument
+            eat_second_argument(ch, pfdata);
+            break;
+
+        case IMAP_PAF_CMD_SEARCH:
+            process_command(ch, pfdata);
+            find_data_end_single_line(ch, pfdata);
+            break;
+
+        case IMAP_PAF_REG_STATE:
+            find_data_end_single_line(ch, pfdata); // data reset when end of line hit
+            break;
+
+        case IMAP_PAF_DATA_HEAD_STATE:
+            parse_fetch_header(ch, pfdata); // function will change state
+            break;
+
+        case IMAP_PAF_DATA_LEN_STATE:
+            if (parse_literal_length(ch, &(pfdata->imap_data_info.length)))
+            {
+                pfdata->imap_state = IMAP_PAF_DATA_HEAD_STATE;
+            }
+            break;
+
+        case IMAP_PAF_DATA_STATE:
+            if (find_data_end_mime_data(ch, pfdata))
+            {
+                // if not a boundary, wait for end of
+                // the server's response before flushing
+                if (pfdata->imap_state == IMAP_PAF_DATA_STATE)
+                {
+                    *fp = i + 1;
+                    return StreamSplitter::FLUSH;
+                }
+            }
+            if (pfdata->mime_info.boundary_state == MIME_PAF_BOUNDARY_UNKNOWN)
+                boundary_start = i;
+            break;
+
+        case IMAP_PAF_FLUSH_STATE:
+            if (find_data_end_single_line(ch, pfdata))
+            {
+                flush_len = i +1;
+            }
+            break;
+        }
+    }
+
+    if (flush_len)
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP PAF: flushing data!\n"); );
+
+        // flush at the final termination sequence
+        *fp = flush_len;
+        return StreamSplitter::FLUSH;
+    }
+
+    if ( scanning_boundary(&pfdata->mime_info, boundary_start, fp) )
+        return StreamSplitter::LIMIT;
+
+    return StreamSplitter::SEARCH;
+}
+
+/*
+ * Searches through the current data for a LF.  All client
+ * commands end with this termination sequence
+ *
+ * PARAMS:
+ *    ImapPafData *pfdata - ImapPaf state tracking structure
+ *    const uint8_t *data - payload data to inspect
+ *    uint32_t len - length of payload data
+ *    uint32_t * fp- pointer to set flush point
+ *
+ * RETURNS:
+ *    StreamSplitter::Status - StreamSplitter::FLUSH if flush point found,
+ *    StreamSplitter::SEARCH otherwise
+ */
+static StreamSplitter::Status imap_paf_client(const uint8_t* data, uint32_t len, uint32_t* fp)
+{
+    const char* pch;
+
+    pch = (char *)memchr (data, '\n', len);
+
+    if (pch != NULL)
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP PAF: Flushing client"
+            " data!\n"); );
+        *fp = (uint32_t)(pch - (const char*)data) + 1;
+        return StreamSplitter::FLUSH;
+    }
+    return StreamSplitter::SEARCH;
+}
+
+//--------------------------------------------------------------------
+// callback for stateful scanning of in-order raw payload
+//--------------------------------------------------------------------
+
+ImapSplitter::ImapSplitter(bool c2s) : StreamSplitter(c2s)
+{
+    memset(&state, 0, sizeof(state));
+    reset_data_states(&state);
+}
+
+ImapSplitter::~ImapSplitter() { }
+
+/* Function: imap_paf()
+
+   Purpose: IMAP PAF callback.
+            Inspects imap traffic.  Checks client traffic for the current command
+            and sets correct server termination sequence. Client side data will
+            flush after receiving CRLF ("\r\n").  Server data flushes after
+            finding set termination sequence.
+
+   Arguments:
+     void * - stream5 session pointer
+     void ** - IMAP state tracking structure
+     const uint8_t * - payload data to inspect
+     uint32_t - length of payload data
+     uint32_t - flags to check whether client or server
+     uint32_t * - pointer to set flush point
+
+   Returns:
+    StreamSplitter::Status - StreamSplitter::FLUSH if flush point found, StreamSplitter::SEARCH otherwise
+*/
+
+StreamSplitter::Status ImapSplitter::scan(
+    Flow* , const uint8_t* data, uint32_t len,
+    uint32_t flags, uint32_t* fp)
+{
+    ImapPafData* pfdata = &state;
+
+    if (flags & PKT_FROM_SERVER)
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "PAF: From server.\n"); );
+        return imap_paf_server(pfdata, data, len, fp);
+    }
+    else
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "PAF: From client.\n"); );
+        return imap_paf_client(data, len, fp);
+    }
+}
+
+bool imap_is_data_end(void* session)
+{
+    Flow* ssn = (Flow*)session;
+    ImapPafData* s = get_state(ssn, true);
+    return s->end_of_data;
+}
+
diff --git a/src/service_inspectors/imap/imap_paf.h b/src/service_inspectors/imap/imap_paf.h
new file mode 100644 (file)
index 0000000..b381747
--- /dev/null
@@ -0,0 +1,86 @@
+/****************************************************************************
+ * Copyright (C) 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.
+ *
+ ****************************************************************************/
+
+#ifndef IMAP_PAF_H
+#define IMAP_PAF_H
+
+#include "snort_types.h"
+#include "stream/stream_api.h"
+#include "stream/stream_splitter.h"
+#include "file_api/file_api.h"
+
+struct ImapDataInfo
+{
+    int paren_cnt;            /* The open parentheses count in fetch */
+    const char* next_letter;        /* The current command in fetch */
+    bool found_len;
+    uint32_t length;
+    bool esc_nxt_char;        /* true if the next charachter has been escaped */
+};
+
+/* State tracker for SMTP PAF */
+typedef enum _ImapPafState
+{
+    IMAP_PAF_REG_STATE,           /* default state. eat until LF */
+    IMAP_PAF_DATA_HEAD_STATE,     /* parses the fetch header */
+    IMAP_PAF_DATA_LEN_STATE,      /* parse the literal length */
+    IMAP_PAF_DATA_STATE,          /* search for and flush on MIME boundaries */
+    IMAP_PAF_FLUSH_STATE,         /* flush if a termination sequence is found */
+    IMAP_PAF_CMD_IDENTIFIER,      /* determine the line identifier ('+', '*', tag) */
+    IMAP_PAF_CMD_TAG,             /* currently analyzing tag . identifier*/
+    IMAP_PAF_CMD_STATUS,          /* currently parsing second argument */
+    IMAP_PAF_CMD_SEARCH           /* currently searching data for a command */
+} ImapPafState;
+
+typedef enum _ImapDataEnd
+{
+    IMAP_PAF_DATA_END_UNKNOWN,
+    IMAP_PAF_DATA_END_PAREN
+} ImapDataEnd;
+
+/* State tracker for IMAP PAF */
+struct ImapPafData
+{
+    MimeDataPafInfo mime_info;    /* Mime response information */
+    ImapPafState imap_state;      /* The current IMAP paf stat */
+    ImapDataInfo imap_data_info;  /* Used for parsing data */
+    ImapDataEnd data_end_state;
+    bool end_of_data;
+};
+
+class ImapSplitter : public StreamSplitter
+{
+public:
+    ImapSplitter(bool c2s);
+    ~ImapSplitter();
+
+    Status scan(Flow*, const uint8_t* data, uint32_t len,
+        uint32_t flags, uint32_t* fp) override;
+
+    virtual bool is_paf() override { return true; }
+
+public:
+    ImapPafData state;
+};
+
+bool imap_is_data_end(void* ssn);
+
+#endif
+
diff --git a/src/service_inspectors/pop/CMakeLists.txt b/src/service_inspectors/pop/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c1e2ba3
--- /dev/null
@@ -0,0 +1,18 @@
+
+set( FILE_LIST
+    pop.cc
+    pop.h
+    pop_paf.cc
+    pop_paf.h
+    pop_config.h
+    pop_module.cc
+    pop_module.h
+)
+
+if (STATIC_INSPECTORS)
+    add_library( pop STATIC ${FILE_LIST})
+
+else (STATIC_INSPECTORS)
+    add_shared_library(pop inspectors ${FILE_LIST})
+
+endif (STATIC_INSPECTORS)
diff --git a/src/service_inspectors/pop/Makefile.am b/src/service_inspectors/pop/Makefile.am
new file mode 100644 (file)
index 0000000..a044e49
--- /dev/null
@@ -0,0 +1,24 @@
+AUTOMAKE_OPTIONS=foreign no-dependencies
+
+file_list = \
+pop_config.h \
+pop.cc \
+pop.h \
+pop_paf.cc \
+pop_paf.h \
+pop_module.cc \
+pop_module.h
+
+if STATIC_INSPECTORS
+noinst_LIBRARIES = libpop.a
+libpop_a_SOURCES = $(file_list)
+else
+shlibdir = $(pkglibdir)/inspectors
+shlib_LTLIBRARIES = libpop.la
+libpop_la_CXXFLAGS = $(AM_CXXFLAGS) -DBUILDING_SO
+libpop_la_LDFLAGS = -export-dynamic -shared
+libpop_la_SOURCES = $(file_list)
+endif
+
+AM_CXXFLAGS = @AM_CXXFLAGS@
+
diff --git a/src/service_inspectors/pop/pop.cc b/src/service_inspectors/pop/pop.cc
new file mode 100644 (file)
index 0000000..d0797e5
--- /dev/null
@@ -0,0 +1,844 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 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.
+//--------------------------------------------------------------------------
+//
+
+/*
+ * POP preprocessor
+ * Author: Bhagyashree Bantwal < bbantwal@cisco.com>
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "snort_types.h"
+#include "snort_debug.h"
+
+#include "pop.h"
+#include "pop_module.h"
+#include "profiler.h"
+#include "stream/stream_api.h"
+#include "file_api/file_api.h"
+#include "parser.h"
+#include "framework/inspector.h"
+#include "utils/sfsnprintfappend.h"
+#include "target_based/sftarget_protocol_reference.h"
+#include "pop_paf.h"
+#include "search_engines/search_tool.h"
+#include "sf_email_attach_decode.h"
+#include "protocols/ssl.h"
+
+THREAD_LOCAL ProfileStats popPerfStats;
+THREAD_LOCAL SimpleStats popstats;
+
+POPToken pop_known_cmds[] =
+{
+    { "APOP",          4, CMD_APOP },
+    { "AUTH",          4, CMD_AUTH },
+    { "CAPA",          4, CMD_CAPA },
+    { "DELE",          4, CMD_DELE },
+    { "LIST",          4, CMD_LIST },
+    { "NOOP",          4, CMD_NOOP },
+    { "PASS",          4, CMD_PASS },
+    { "QUIT",          4, CMD_QUIT },
+    { "RETR",          4, CMD_RETR },
+    { "RSET",          4, CMD_RSET },
+    { "STAT",          4, CMD_STAT },
+    { "STLS",          4, CMD_STLS },
+    { "TOP",           3, CMD_TOP },
+    { "UIDL",          4, CMD_UIDL },
+    { "USER",          4, CMD_USER },
+    { NULL,            0, 0 }
+};
+
+POPToken pop_resps[] =
+{
+    { "+OK",   3,  RESP_OK },   /* SUCCESS */
+    { "-ERR",  4,  RESP_ERR },  /* FAILURE */
+    { NULL,   0,  0 }
+};
+
+SearchTool* pop_resp_search_mpse = nullptr;
+SearchTool* pop_cmd_search_mpse = nullptr;
+
+POPSearch pop_resp_search[RESP_LAST];
+POPSearch pop_cmd_search[CMD_LAST];
+THREAD_LOCAL const POPSearch* pop_current_search = NULL;
+THREAD_LOCAL POPSearchInfo pop_search_info;
+
+static void snort_pop(POP_PROTO_CONF* GlobalConf, Packet* p);
+static void POP_ResetState(void*);
+void POP_DecodeAlert(void* ds);
+
+MimeMethods pop_mime_methods = { NULL, NULL, POP_DecodeAlert, POP_ResetState, pop_is_data_end };
+
+unsigned PopFlowData::flow_id = 0;
+static POPData* get_session_data(Flow* flow)
+{
+    PopFlowData* fd = (PopFlowData*)flow->get_application_data(
+        PopFlowData::flow_id);
+
+    return fd ? &fd->session : NULL;
+}
+
+POPData* SetNewPOPData(POP_PROTO_CONF* config, Packet* p)
+{
+    POPData* pop_ssn;
+    PopFlowData* fd = new PopFlowData;
+
+    p->flow->set_application_data(fd);
+    pop_ssn = &fd->session;
+
+    pop_ssn->mime_ssn.log_config = &(config->log_config);
+    pop_ssn->mime_ssn.decode_conf = &(config->decode_conf);
+    pop_ssn->mime_ssn.methods = &(pop_mime_methods);
+    if (file_api->set_log_buffers(&(pop_ssn->mime_ssn.log_state), &(config->log_config)) < 0)
+    {
+        return NULL;
+    }
+
+    if (p->packet_flags & SSNFLAG_MIDSTREAM)
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_POP, "Got midstream packet - "
+            "setting state to unknown\n"); );
+        pop_ssn->state = STATE_UNKNOWN;
+    }
+
+    return pop_ssn;
+}
+
+void POP_DecodeAlert(void* ds)
+{
+    Email_DecodeState* decode_state = (Email_DecodeState*)ds;
+    switch ( decode_state->decode_type )
+    {
+    case DECODE_B64:
+        SnortEventqAdd(GID_POP, POP_B64_DECODING_FAILED);
+        break;
+    case DECODE_QP:
+        SnortEventqAdd(GID_POP, POP_QP_DECODING_FAILED);
+        break;
+    case DECODE_UU:
+        SnortEventqAdd(GID_POP, POP_UU_DECODING_FAILED);
+        break;
+
+    default:
+        break;
+    }
+}
+
+void POP_SearchInit(void)
+{
+    const POPToken* tmp;
+    pop_cmd_search_mpse = new SearchTool();
+    if (pop_cmd_search_mpse == NULL)
+    {
+        FatalError("Could not allocate memory for POP Command search.\n");
+    }
+    for (tmp = &pop_known_cmds[0]; tmp->name != NULL; tmp++)
+    {
+        pop_cmd_search[tmp->search_id].name = tmp->name;
+        pop_cmd_search[tmp->search_id].name_len = tmp->name_len;
+        pop_cmd_search_mpse->add(tmp->name, tmp->name_len, tmp->search_id);
+    }
+    pop_cmd_search_mpse->prep();
+
+    pop_resp_search_mpse = new SearchTool();
+    if (pop_resp_search_mpse == NULL)
+    {
+        FatalError("Could not allocate memory for POP Response search.\n");
+    }
+    for (tmp = &pop_resps[0]; tmp->name != NULL; tmp++)
+    {
+        pop_resp_search[tmp->search_id].name = tmp->name;
+        pop_resp_search[tmp->search_id].name_len = tmp->name_len;
+        pop_resp_search_mpse->add(tmp->name, tmp->name_len, tmp->search_id);
+    }
+    pop_resp_search_mpse->prep();
+}
+
+void POP_SearchFree(void)
+{
+    if (pop_cmd_search_mpse != NULL)
+        delete pop_cmd_search_mpse;
+
+    if (pop_resp_search_mpse != NULL)
+        delete pop_resp_search_mpse;
+}
+
+/*
+* Reset POP session state
+*
+* @param  none
+*
+* @return none
+*/
+static void POP_ResetState(void* ssn)
+{
+    POPData* pop_ssn = get_session_data((Flow*)ssn);
+    pop_ssn->state = STATE_COMMAND;
+    pop_ssn->prev_response = 0;
+    pop_ssn->state_flags = 0;
+}
+
+void POP_GetEOL(const uint8_t* ptr, const uint8_t* end,
+    const uint8_t** eol, const uint8_t** eolm)
+{
+    const uint8_t* tmp_eol;
+    const uint8_t* tmp_eolm;
+
+    /* XXX maybe should fatal error here since none of these
+     *      * pointers should be NULL */
+    if (ptr == NULL || end == NULL || eol == NULL || eolm == NULL)
+        return;
+
+    tmp_eol = (uint8_t*)memchr(ptr, '\n', end - ptr);
+    if (tmp_eol == NULL)
+    {
+        tmp_eol = end;
+        tmp_eolm = end;
+    }
+    else
+    {
+        /* end of line marker (eolm) should point to marker and
+         *          * end of line (eol) should point to end of marker */
+        if ((tmp_eol > ptr) && (*(tmp_eol - 1) == '\r'))
+        {
+            tmp_eolm = tmp_eol - 1;
+        }
+        else
+        {
+            tmp_eolm = tmp_eol;
+        }
+
+        /* move past newline */
+        tmp_eol++;
+    }
+
+    *eol = tmp_eol;
+    *eolm = tmp_eolm;
+}
+
+static void PrintPopConf(POP_PROTO_CONF* config)
+{
+    if (config == NULL)
+        return;
+
+    LogMessage("POP config: \n");
+
+    if (config->decode_conf.b64_depth > -1)
+    {
+        switch (config->decode_conf.b64_depth)
+        {
+        case 0:
+            LogMessage("    Base64 Decoding Depth: %s\n", "Unlimited");
+            break;
+        default:
+            LogMessage("    Base64 Decoding Depth: %d\n", config->decode_conf.b64_depth);
+            break;
+        }
+    }
+    else
+        LogMessage("    Base64 Decoding: %s\n", "Disabled");
+
+    if (config->decode_conf.qp_depth > -1)
+    {
+        switch (config->decode_conf.qp_depth)
+        {
+        case 0:
+            LogMessage("    Quoted-Printable Decoding Depth: %s\n", "Unlimited");
+            break;
+        default:
+            LogMessage("    Quoted-Printable Decoding Depth: %d\n", config->decode_conf.qp_depth);
+            break;
+        }
+    }
+    else
+        LogMessage("    Quoted-Printable Decoding: %s\n", "Disabled");
+    if (config->decode_conf.uu_depth > -1)
+    {
+        switch (config->decode_conf.uu_depth)
+        {
+        case 0:
+            LogMessage("    Unix-to-Unix Decoding Depth: %s\n", "Unlimited");
+            break;
+        default:
+            LogMessage("    Unix-to-Unix Decoding Depth: %d\n", config->decode_conf.uu_depth);
+            break;
+        }
+    }
+    else
+        LogMessage("    Unix-to-Unix Decoding: %s\n", "Disabled");
+
+    if (config->decode_conf.bitenc_depth > -1)
+    {
+        switch (config->decode_conf.bitenc_depth)
+        {
+        case 0:
+            LogMessage("    Non-Encoded MIME attachment Extraction Depth: %s\n", "Unlimited");
+            break;
+        default:
+            LogMessage("    Non-Encoded MIME attachment Extraction Depth: %d\n",
+                config->decode_conf.bitenc_depth);
+            break;
+        }
+    }
+    else
+        LogMessage("    Non-Encoded MIME attachment Extraction: %s\n", "Disabled");
+
+    LogMessage("\n");
+}
+
+static inline int InspectPacket(Packet* p)
+{
+    return PacketHasPAFPayload(p);
+}
+
+static int POP_Setup(Packet* p, POPData* ssn)
+{
+    int pkt_dir;
+
+    /* Get the direction of the packet. */
+    if ( p->packet_flags & PKT_FROM_SERVER )
+        pkt_dir = POP_PKT_FROM_SERVER;
+    else
+        pkt_dir = POP_PKT_FROM_CLIENT;
+
+    if (!(ssn->session_flags & POP_FLAG_CHECK_SSL))
+        ssn->session_flags |= POP_FLAG_CHECK_SSL;
+    /* Check to see if there is a reassembly gap.  If so, we won't know
+     *      * what state we're in when we get the _next_ reassembled packet */
+    if ((pkt_dir != POP_PKT_FROM_SERVER) &&
+        (p->packet_flags & PKT_REBUILT_STREAM))
+    {
+        int missing_in_rebuilt =
+            stream.missing_in_reassembled(p->flow, SSN_DIR_FROM_CLIENT);
+
+        if (ssn->session_flags & POP_FLAG_NEXT_STATE_UNKNOWN)
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_POP, "Found gap in previous reassembly buffer - "
+                "set state to unknown\n"); );
+            ssn->state = STATE_UNKNOWN;
+            ssn->session_flags &= ~POP_FLAG_NEXT_STATE_UNKNOWN;
+        }
+
+        if (missing_in_rebuilt == SSN_MISSING_BEFORE)
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_POP, "Found missing packets before "
+                "in reassembly buffer - set state to unknown\n"); );
+            ssn->state = STATE_UNKNOWN;
+        }
+    }
+
+    return pkt_dir;
+}
+
+static int POP_SearchStrFound(void* id, void* , int index, void* , void* )
+{
+    int search_id = (int)(uintptr_t)id;
+
+    pop_search_info.id = search_id;
+    pop_search_info.index = index;
+    pop_search_info.length = pop_current_search[search_id].name_len;
+
+    /* Returning non-zero stops search, which is okay since we only look for one at a time */
+    return 1;
+}
+
+/*
+ * Handle COMMAND state
+ *
+ * @param   p       standard Packet structure
+ * @param   ptr     pointer into p->data buffer to start looking at data
+ * @param   end     points to end of p->data buffer
+ *
+ * @return          pointer into p->data where we stopped looking at data
+ *                  will be end of line or end of packet
+ */
+static const uint8_t* POP_HandleCommand(Packet* p, POPData* pop_ssn, const uint8_t* ptr, const
+    uint8_t* end)
+{
+    const uint8_t* eol;   /* end of line */
+    const uint8_t* eolm;  /* end of line marker */
+    int cmd_found;
+
+    /* get end of line and end of line marker */
+    POP_GetEOL(ptr, end, &eol, &eolm);
+
+    /* TODO If the end of line marker coincides with the end of data we can't be
+     * sure that we got a command and not a substring which we could tell through
+     * inspection of the next packet. Maybe a command pending state where the first
+     * char in the next packet is checked for a space and end of line marker */
+
+    /* do not confine since there could be space chars before command */
+    pop_current_search = &pop_cmd_search[0];
+    cmd_found = pop_cmd_search_mpse->find(
+        (const char*)ptr, eolm - ptr, POP_SearchStrFound);
+    /* see if we actually found a command and not a substring */
+    if (cmd_found > 0)
+    {
+        const uint8_t* tmp = ptr;
+        const uint8_t* cmd_start = ptr + pop_search_info.index;
+        const uint8_t* cmd_end = cmd_start + pop_search_info.length;
+
+        /* move past spaces up until start of command */
+        while ((tmp < cmd_start) && isspace((int)*tmp))
+            tmp++;
+
+        /* if not all spaces before command, we found a
+         * substring */
+        if (tmp != cmd_start)
+            cmd_found = 0;
+
+        /* if we're before the end of line marker and the next
+         * character is not whitespace, we found a substring */
+        if ((cmd_end < eolm) && !isspace((int)*cmd_end))
+            cmd_found = 0;
+
+        /* there is a chance that end of command coincides with the end of data
+         * in which case, it could be a substring, but for now, we will treat it as found */
+    }
+
+    /* if command not found, alert and move on */
+    if (!cmd_found)
+    {
+        if (pop_ssn->state == STATE_UNKNOWN)
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_POP, "Command not found, but state is "
+                "unknown - checking for SSL\n"); );
+
+            /* check for encrypted */
+
+            if ((pop_ssn->session_flags & POP_FLAG_CHECK_SSL) &&
+                (IsSSL(ptr, end - ptr, p->packet_flags)))
+            {
+                DEBUG_WRAP(DebugMessage(DEBUG_POP, "Packet is SSL encrypted\n"); );
+
+                pop_ssn->state = STATE_TLS_DATA;
+
+                /* Ignore data */
+                return end;
+            }
+            else
+            {
+                DEBUG_WRAP(DebugMessage(DEBUG_POP, "Not SSL - try data state\n"); );
+                /* don't check for ssl again in this packet */
+                if (pop_ssn->session_flags & POP_FLAG_CHECK_SSL)
+                    pop_ssn->session_flags &= ~POP_FLAG_CHECK_SSL;
+
+                pop_ssn->state = STATE_DATA;
+                //pop_ssn->data_state = STATE_DATA_UNKNOWN;
+
+                return ptr;
+            }
+        }
+        else
+        {
+            SnortEventqAdd(GID_POP, POP_UNKNOWN_CMD);
+            DEBUG_WRAP(DebugMessage(DEBUG_POP, "No known command found\n"); );
+            return eol;
+        }
+    }
+    else if (pop_search_info.id == CMD_TOP)
+    {
+        pop_ssn->state = STATE_DATA;
+    }
+    else
+    {
+        if (pop_ssn->state == STATE_UNKNOWN)
+            pop_ssn->state = STATE_COMMAND;
+    }
+
+    if (pop_search_info.id == CMD_STLS)
+    {
+        if (eol == end)
+            pop_ssn->state = STATE_TLS_CLIENT_PEND;
+    }
+
+    return eol;
+}
+
+/*
+ * Process client packet
+ *
+ * @param   packet  standard Packet structure
+ *
+ * @return  none
+ */
+static void POP_ProcessClientPacket(Packet* p, POPData* pop_ssn)
+{
+    const uint8_t* ptr = p->data;
+    const uint8_t* end = p->data + p->dsize;
+
+    ptr = POP_HandleCommand(p, pop_ssn, ptr, end);
+}
+
+/*
+ * Process server packet
+ *
+ * @param   packet  standard Packet structure
+ *
+ */
+static void POP_ProcessServerPacket(Packet* p, POPData* pop_ssn)
+{
+    int resp_found;
+    const uint8_t* ptr;
+    const uint8_t* end;
+    const uint8_t* eolm;
+    const uint8_t* eol;
+    int resp_line_len;
+    const char* tmp = NULL;
+
+    ptr = p->data;
+    end = p->data + p->dsize;
+
+    while (ptr < end)
+    {
+        if (pop_ssn->state == STATE_DATA)
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_POP, "DATA STATE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); );
+            //ptr = POP_HandleData(p, ptr, end);
+            ptr = file_api->process_mime_data(p, ptr, end, &(pop_ssn->mime_ssn), 0, true);
+            continue;
+        }
+        POP_GetEOL(ptr, end, &eol, &eolm);
+
+        resp_line_len = eol - ptr;
+
+        /* Check for response code */
+        pop_current_search = &pop_resp_search[0];
+        resp_found = pop_resp_search_mpse->find(
+            (const char*)ptr, resp_line_len, POP_SearchStrFound);
+
+        if (resp_found > 0)
+        {
+            const uint8_t* cmd_start = ptr + pop_search_info.index;
+            switch (pop_search_info.id)
+            {
+            case RESP_OK:
+                tmp = SnortStrcasestr((const char*)cmd_start, (eol - cmd_start), "octets");
+                if (tmp != NULL)
+                    pop_ssn->state = STATE_DATA;
+                else
+                {
+                    pop_ssn->prev_response = RESP_OK;
+                    pop_ssn->state = STATE_UNKNOWN;
+                }
+                break;
+
+            default:
+                break;
+            }
+        }
+        else
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_POP,
+                "Server response not found - see if it's SSL data\n"); );
+
+            if ((pop_ssn->session_flags & POP_FLAG_CHECK_SSL) &&
+                (IsSSL(ptr, end - ptr, p->packet_flags)))
+            {
+                DEBUG_WRAP(DebugMessage(DEBUG_POP, "Server response is an SSL packet\n"); );
+
+                pop_ssn->state = STATE_TLS_DATA;
+
+                return;
+            }
+            else if (pop_ssn->session_flags & POP_FLAG_CHECK_SSL)
+            {
+                pop_ssn->session_flags &= ~POP_FLAG_CHECK_SSL;
+            }
+            if (pop_ssn->prev_response == RESP_OK)
+            {
+                {
+                    pop_ssn->state = STATE_DATA;
+                    pop_ssn->prev_response = 0;
+                    continue;
+                }
+            }
+            else if (*ptr == '+')
+            {
+                SnortEventqAdd(GID_POP, POP_UNKNOWN_RESP);
+                DEBUG_WRAP(DebugMessage(DEBUG_POP, "Server response not found\n"); );
+            }
+        }
+
+        ptr = eol;
+    }
+}
+
+/* Main runtime entry point for POP preprocessor.
+ * Analyzes POP packets for anomalies/exploits.
+ *
+ * PARAMETERS:
+ *
+ * p:    Pointer to current packet to process.
+ * contextp:    Pointer to context block, not used.
+ *
+ * RETURNS:     Nothing.
+ */
+static void snort_pop(POP_PROTO_CONF* config, Packet* p)
+{
+    POPData* pop_ssn = NULL;
+    int pkt_dir;
+
+    /* Attempt to get a previously allocated POP block. */
+    pop_ssn = get_session_data(p->flow);
+
+    if (pop_ssn == NULL)
+    {
+        /* Check the stream session. If it does not currently
+         * have our POP data-block attached, create one.
+         */
+        pop_ssn = SetNewPOPData(config, p);
+
+        if ( !pop_ssn )
+        {
+            /* Could not get/create the session data for this packet. */
+            return;
+        }
+    }
+
+    pkt_dir = POP_Setup(p, pop_ssn);
+
+    if (pkt_dir == POP_PKT_FROM_CLIENT)
+    {
+        /* This packet should be a tls client hello */
+        if (pop_ssn->state == STATE_TLS_CLIENT_PEND)
+        {
+            if (IsTlsClientHello(p->data, p->data + p->dsize))
+            {
+                DEBUG_WRAP(DebugMessage(DEBUG_POP,
+                    "TLS DATA STATE ~~~~~~~~~~~~~~~~~~~~~~~~~\n"); );
+
+                pop_ssn->state = STATE_TLS_SERVER_PEND;
+                return;
+            }
+            else
+            {
+                /* reset state - server may have rejected STARTTLS command */
+                pop_ssn->state = STATE_UNKNOWN;
+            }
+        }
+        if ((pop_ssn->state == STATE_TLS_DATA)
+            || (pop_ssn->state == STATE_TLS_SERVER_PEND))
+        {
+            return;
+        }
+        POP_ProcessClientPacket(p, pop_ssn);
+        DEBUG_WRAP(DebugMessage(DEBUG_POP, "POP client packet\n"); );
+    }
+    else
+    {
+        if (pop_ssn->state == STATE_TLS_SERVER_PEND)
+        {
+            if (IsTlsServerHello(p->data, p->data + p->dsize))
+            {
+                pop_ssn->state = STATE_TLS_DATA;
+            }
+            else if (!(stream.get_session_flags(p->flow) & SSNFLAG_MIDSTREAM)
+                && !stream.missed_packets(p->flow, SSN_DIR_BOTH))
+            {
+                /* revert back to command state - assume server didn't accept STARTTLS */
+                pop_ssn->state = STATE_UNKNOWN;
+            }
+            else
+                return;
+        }
+
+        if (pop_ssn->state == STATE_TLS_DATA)
+        {
+            return;
+        }
+        if ( !InspectPacket(p))
+        {
+            /* Packet will be rebuilt, so wait for it */
+            DEBUG_WRAP(DebugMessage(DEBUG_POP, "Client packet will be reassembled\n"));
+            return;
+        }
+        else if (!(p->packet_flags & PKT_REBUILT_STREAM))
+        {
+            /* If this isn't a reassembled packet and didn't get
+             * inserted into reassembly buffer, there could be a
+             * problem.  If we miss syn or syn-ack that had window
+             * scaling this packet might not have gotten inserted
+             * into reassembly buffer because it fell outside of
+             * window, because we aren't scaling it */
+            pop_ssn->session_flags |= POP_FLAG_GOT_NON_REBUILT;
+            pop_ssn->state = STATE_UNKNOWN;
+        }
+        else if (pop_ssn->session_flags & POP_FLAG_GOT_NON_REBUILT)
+        {
+            /* This is a rebuilt packet.  If we got previous packets
+             * that were not rebuilt, state is going to be messed up
+             * so set state to unknown. It's likely this was the
+             * beginning of the conversation so reset state */
+            DEBUG_WRAP(DebugMessage(DEBUG_POP, "Got non-rebuilt packets before "
+                "this rebuilt packet\n"); );
+
+            pop_ssn->state = STATE_UNKNOWN;
+            pop_ssn->session_flags &= ~POP_FLAG_GOT_NON_REBUILT;
+        }
+        /* Process as a server packet */
+        POP_ProcessServerPacket(p, pop_ssn);
+    }
+}
+
+//-------------------------------------------------------------------------
+// class stuff
+//-------------------------------------------------------------------------
+
+class Pop : public Inspector
+{
+public:
+    Pop(POP_PROTO_CONF*);
+    ~Pop();
+
+    bool configure(SnortConfig*) override;
+    void show(SnortConfig*) override;
+    void eval(Packet*) override;
+
+    StreamSplitter* get_splitter(bool c2s) override
+    { return new PopSplitter(c2s); }
+
+private:
+    POP_PROTO_CONF* config;
+};
+
+Pop::Pop(POP_PROTO_CONF* pc)
+{
+    config = pc;
+}
+
+Pop::~Pop()
+{
+    if ( config )
+        delete config;
+}
+
+bool Pop::configure(SnortConfig* )
+{
+    config->decode_conf.file_depth = file_api->get_max_file_depth();
+
+    if (config->decode_conf.file_depth > 0)
+        config->log_config.log_filename = 1;
+
+    if (file_api->is_decoding_enabled(&config->decode_conf) )
+    {
+        updateMaxDepth(config->decode_conf.file_depth,
+            &config->decode_conf.max_depth);
+    }
+    file_api->check_decode_config(&config->decode_conf);
+    return true;
+}
+
+void Pop::show(SnortConfig*)
+{
+    PrintPopConf(config);
+}
+
+void Pop::eval(Packet* p)
+{
+    PROFILE_VARS;
+    // precondition - what we registered for
+    assert(p->is_tcp() && p->dsize && p->data);
+
+    ++popstats.total_packets;
+
+    MODULE_PROFILE_START(popPerfStats);
+
+    snort_pop(config, p);
+
+    MODULE_PROFILE_END(popPerfStats);
+}
+
+//-------------------------------------------------------------------------
+// api stuff
+//-------------------------------------------------------------------------
+
+static Module* mod_ctor()
+{ return new PopModule; }
+
+static void mod_dtor(Module* m)
+{ delete m; }
+
+static void pop_init()
+{
+    PopFlowData::init();
+    POP_SearchInit();
+}
+
+static void pop_term()
+{
+    POP_SearchFree();
+}
+
+static Inspector* pop_ctor(Module* m)
+{
+    PopModule* mod = (PopModule*)m;
+    return new Pop(mod->get_data());
+}
+
+static void pop_dtor(Inspector* p)
+{
+    delete p;
+}
+
+const InspectApi pop_api =
+{
+    {
+        PT_INSPECTOR,
+        sizeof(InspectApi),
+        INSAPI_VERSION,
+        0,
+        API_RESERVED,
+        API_OPTIONS,
+        POP_NAME,
+        POP_HELP,
+        mod_ctor,
+        mod_dtor
+    },
+    IT_SERVICE,
+    (uint16_t)PktType::TCP,
+    nullptr, // buffers
+    "pop",
+    pop_init,
+    pop_term, // pterm
+    nullptr, // tinit
+    nullptr, // tterm
+    pop_ctor,
+    pop_dtor,
+    nullptr, // ssn
+    nullptr  // reset
+};
+
+#ifdef BUILDING_SO
+SO_PUBLIC const BaseApi* snort_plugins[] =
+{
+    &pop_api.base,
+    nullptr
+};
+#else
+const BaseApi* sin_pop = &pop_api.base;
+#endif
+
diff --git a/src/service_inspectors/pop/pop.h b/src/service_inspectors/pop/pop.h
new file mode 100644 (file)
index 0000000..61aa049
--- /dev/null
@@ -0,0 +1,154 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 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.
+//--------------------------------------------------------------------------
+//
+
+/*
+ * pop.h: Definitions, structs, function prototype(s) for
+ *             the POP service inspectors.
+ * Author: Bhagyashree Bantwal <bbantwal@cisco.com>
+ */
+
+#ifndef POP_H
+#define POP_H
+
+#include "protocols/packet.h"
+#include "stream/stream_api.h"
+#include "profiler.h"
+#include "pop_config.h"
+/* Direction packet is coming from, if we can figure it out */
+#define POP_PKT_FROM_UNKNOWN  0
+#define POP_PKT_FROM_CLIENT   1
+#define POP_PKT_FROM_SERVER   2
+
+#define SEARCH_CMD       0
+#define SEARCH_RESP      1
+#define SEARCH_HDR       2
+#define SEARCH_DATA_END  3
+#define NUM_SEARCHES  4
+
+#define BOUNDARY     0
+
+#define STATE_DATA             0    /* Data state */
+#define STATE_TLS_CLIENT_PEND  1    /* Got STARTTLS */
+#define STATE_TLS_SERVER_PEND  2    /* Got STARTTLS */
+#define STATE_TLS_DATA         3    /* Successful handshake, TLS encrypted data */
+#define STATE_COMMAND          4
+#define STATE_UNKNOWN          5
+
+#define STATE_DATA_INIT    0
+#define STATE_DATA_HEADER  1    /* Data header section of data state */
+#define STATE_DATA_BODY    2    /* Data body section of data state */
+#define STATE_MIME_HEADER  3    /* MIME header section within data section */
+#define STATE_DATA_UNKNOWN 4
+
+/* session flags */
+#define POP_FLAG_NEXT_STATE_UNKNOWN         0x00000004
+#define POP_FLAG_GOT_NON_REBUILT            0x00000008
+#define POP_FLAG_CHECK_SSL                  0x00000010
+
+/* Maximum length of header chars before colon, based on Exim 4.32 exploit */
+#define MAX_HEADER_NAME_LEN 64
+typedef enum _POPCmdEnum
+{
+    CMD_APOP = 0,
+    CMD_AUTH,
+    CMD_CAPA,
+    CMD_DELE,
+    CMD_LIST,
+    CMD_NOOP,
+    CMD_PASS,
+    CMD_QUIT,
+    CMD_RETR,
+    CMD_RSET,
+    CMD_STAT,
+    CMD_STLS,
+    CMD_TOP,
+    CMD_UIDL,
+    CMD_USER,
+    CMD_LAST
+} POPCmdEnum;
+
+typedef enum _POPRespEnum
+{
+    RESP_OK = 1,
+    RESP_ERR,
+    RESP_LAST
+} POPRespEnum;
+
+typedef enum _POPHdrEnum
+{
+    HDR_CONTENT_TYPE = 0,
+    HDR_CONT_TRANS_ENC,
+    HDR_CONT_DISP,
+    HDR_LAST
+} POPHdrEnum;
+
+struct POPSearch
+{
+    const char* name;
+    int name_len;
+};
+
+struct POPToken
+{
+    const char* name;
+    int name_len;
+    int search_id;
+};
+
+struct POPCmdConfig
+{
+    char alert;                  /*  1 if alert when seen                          */
+    char normalize;              /*  1 if we should normalize this command         */
+    int max_line_len;            /*  Max length of this particular command         */
+};
+
+struct POPSearchInfo
+{
+    int id;
+    int index;
+    int length;
+};
+
+struct POPData
+{
+    int state;
+    int prev_response;
+    int state_flags;
+    int session_flags;
+    MimeState mime_ssn;
+};
+
+class PopFlowData : public FlowData
+{
+public:
+    PopFlowData() : FlowData(flow_id)
+    { memset(&session, 0, sizeof(session)); }
+
+    ~PopFlowData() { }
+
+    static void init()
+    { flow_id = FlowData::get_flow_id(); }
+
+public:
+    static unsigned flow_id;
+    POPData session;
+};
+
+#endif /* POP_H */
+
diff --git a/src/service_inspectors/pop/pop_config.h b/src/service_inspectors/pop/pop_config.h
new file mode 100644 (file)
index 0000000..f7e65fd
--- /dev/null
@@ -0,0 +1,33 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 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.
+//--------------------------------------------------------------------------
+//
+
+#ifndef POP_CONFIG_H
+#define POP_CONFIG_H
+
+#include "file_api/file_api.h"
+
+struct POP_PROTO_CONF
+{
+    uint32_t memcap;
+    DecodeConfig decode_conf;
+    MAIL_LogConfig log_config;
+};
+
+#endif
+
diff --git a/src/service_inspectors/pop/pop_module.cc b/src/service_inspectors/pop/pop_module.cc
new file mode 100644 (file)
index 0000000..95735a7
--- /dev/null
@@ -0,0 +1,143 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-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.
+//--------------------------------------------------------------------------
+
+// pop_module.cc author Bhagyashree Bantwal <bbantwal@cisco.com>
+
+#include "pop_module.h"
+#include <assert.h>
+#include <sstream>
+#include "main/snort_config.h"
+
+using namespace std;
+
+#define POP_UNKNOWN_CMD_STR                 "Unknown POP3 command"
+#define POP_UNKNOWN_RESP_STR                "Unknown POP3 response"
+#define POP_B64_DECODING_FAILED_STR         "Base64 Decoding failed."
+#define POP_QP_DECODING_FAILED_STR          "Quoted-Printable Decoding failed."
+#define POP_UU_DECODING_FAILED_STR          "Unix-to-Unix Decoding failed."
+
+static const Parameter s_params[] =
+{
+    { "b64_decode_depth", Parameter::PT_INT, "-1:65535", "1460",
+      " base64 decoding depth" },
+
+    { "bitenc_decode_depth", Parameter::PT_INT, "-1:65535", "1460",
+      " Non-Encoded MIME attachment extraction depth" },
+
+    { "qp_decode_depth", Parameter::PT_INT, "-1:65535", "1460",
+      " Quoted Printable decoding depth" },
+
+    { "uu_decode_depth", Parameter::PT_INT, "-1:65535", "1460",
+      " Unix-to-Unix decoding depth" },
+
+    { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+static const RuleMap pop_rules[] =
+{
+    { POP_UNKNOWN_CMD, POP_UNKNOWN_CMD_STR },
+    { POP_UNKNOWN_RESP, POP_UNKNOWN_RESP_STR },
+    { POP_B64_DECODING_FAILED, POP_B64_DECODING_FAILED_STR },
+    { POP_QP_DECODING_FAILED, POP_QP_DECODING_FAILED_STR },
+    { POP_UU_DECODING_FAILED, POP_UU_DECODING_FAILED_STR },
+
+    { 0, nullptr }
+};
+
+//-------------------------------------------------------------------------
+// pop module
+//-------------------------------------------------------------------------
+
+PopModule::PopModule() : Module(POP_NAME, POP_HELP, s_params)
+{
+    config = nullptr;
+}
+
+PopModule::~PopModule()
+{
+    if ( config )
+        delete config;
+}
+
+const RuleMap* PopModule::get_rules() const
+{ return pop_rules; }
+
+const PegInfo* PopModule::get_pegs() const
+{ return simple_pegs; }
+
+PegCount* PopModule::get_counts() const
+{ return (PegCount*)&popstats; }
+
+ProfileStats* PopModule::get_profile() const
+{ return &popPerfStats; }
+
+bool PopModule::set(const char*, Value& v, SnortConfig*)
+{
+    if ( v.is("b64_decode_depth") )
+    {
+        int decode_depth = v.get_long();
+
+        if ((decode_depth > 0) && (decode_depth & 3))
+        {
+            decode_depth += 4 - (decode_depth & 3);
+            if (decode_depth > 65535 )
+            {
+                decode_depth = decode_depth - 4;
+            }
+            LogMessage("WARNING: POP: 'b64_decode_depth' is not a multiple of 4. "
+                "Rounding up to the next multiple of 4. The new 'b64_decode_depth' is %d.\n",
+                decode_depth);
+        }
+        config->decode_conf.b64_depth = decode_depth;
+    }
+    else if ( v.is("bitenc_decode_depth") )
+        config->decode_conf.bitenc_depth = v.get_long();
+
+    else if ( v.is("qp_decode_depth") )
+        config->decode_conf.qp_depth = v.get_long();
+
+    else if ( v.is("uu_decode_depth") )
+        config->decode_conf.uu_depth = v.get_long();
+
+    else
+        return false;
+
+    return true;
+}
+
+POP_PROTO_CONF* PopModule::get_data()
+{
+    POP_PROTO_CONF* tmp = config;
+    config = nullptr;
+    return tmp;
+}
+
+bool PopModule::begin(const char*, int, SnortConfig*)
+{
+    config = new POP_PROTO_CONF;
+    file_api->set_mime_decode_config_defauts(&(config->decode_conf));
+    file_api->set_mime_log_config_defauts(&(config->log_config));
+
+    return true;
+}
+
+bool PopModule::end(const char*, int, SnortConfig*)
+{
+    return true;
+}
+
diff --git a/src/service_inspectors/pop/pop_module.h b/src/service_inspectors/pop/pop_module.h
new file mode 100644 (file)
index 0000000..20b95fd
--- /dev/null
@@ -0,0 +1,70 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-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.
+//--------------------------------------------------------------------------
+
+// pop_module.h author Bhagyashree Bantwal <bbantwal@cisco.com>
+
+#ifndef POP_MODULE_H
+#define POP_MODULE_H
+
+#include "framework/module.h"
+#include "framework/bits.h"
+#include "main/thread.h"
+#include "pop_config.h"
+
+#define GID_POP 142
+
+#define POP_UNKNOWN_CMD            1
+#define POP_UNKNOWN_RESP           2
+#define POP_B64_DECODING_FAILED    4
+#define POP_QP_DECODING_FAILED     5
+#define POP_UU_DECODING_FAILED     7
+
+#define POP_NAME "pop"
+#define POP_HELP "pop inspection"
+
+struct SnortConfig;
+
+extern THREAD_LOCAL SimpleStats popstats;
+extern THREAD_LOCAL ProfileStats popPerfStats;
+
+class PopModule : public Module
+{
+public:
+    PopModule();
+    ~PopModule();
+
+    bool set(const char*, Value&, SnortConfig*) override;
+    bool begin(const char*, int, SnortConfig*) override;
+    bool end(const char*, int, SnortConfig*) override;
+
+    unsigned get_gid() const override
+    { return GID_POP; }
+
+    const RuleMap* get_rules() const override;
+    const PegInfo* get_pegs() const override;
+    PegCount* get_counts() const override;
+    ProfileStats* get_profile() const override;
+
+    POP_PROTO_CONF* get_data();
+
+private:
+    POP_PROTO_CONF* config;
+};
+
+#endif
+
diff --git a/src/service_inspectors/pop/pop_paf.cc b/src/service_inspectors/pop/pop_paf.cc
new file mode 100644 (file)
index 0000000..83b0305
--- /dev/null
@@ -0,0 +1,417 @@
+/****************************************************************************
+ * Copyright (C) 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.
+ *
+ ****************************************************************************/
+
+#include <sys/types.h>
+#include "snort_types.h"
+#include "snort_debug.h"
+
+#include "pop_paf.h"
+#include "pop.h"
+
+extern POPToken pop_known_cmds[];
+
+static inline PopPafData* get_state(Flow* flow, bool c2s)
+{
+    if ( !flow )
+        return nullptr;
+
+    PopSplitter* s = (PopSplitter*)stream.get_splitter(flow, c2s);
+    return s ? &s->state : nullptr;
+}
+
+/*
+ *  read process_command() description below
+ */
+static bool search_for_command(PopPafData* pfdata, const uint8_t ch)
+{
+    char val = *(pfdata->cmd_state.next_letter);
+
+    // if end of command && data contains a space or newline
+    if (val == '\0'  && (isblank(ch) || ch == '\r' || ch == '\n'))
+    {
+        if (pfdata->cmd_state.exp_resp == POP_PAF_HAS_ARG)
+        {
+            pfdata->cmd_state.status = POP_CMD_ARG;
+        }
+        else
+        {
+            pfdata->cmd_state.status = POP_CMD_FIN;
+            pfdata->pop_state = pfdata->cmd_state.exp_resp;
+            return true;
+        }
+    }
+    else if (toupper(ch) == toupper(val) )
+    {
+        pfdata->cmd_state.next_letter++;
+    }
+    else
+    {
+        pfdata->cmd_state.status = POP_CMD_FIN;
+    }
+
+    return false;
+}
+
+/*
+ *  read process_command() description below
+ */
+static bool init_command_search(PopPafData* pfdata, const uint8_t ch)
+{
+    pfdata->pop_state = POP_PAF_SINGLE_LINE_STATE;
+
+    switch (ch)
+    {
+    case 'c':
+    case 'C':
+        pfdata->cmd_state.exp_resp = POP_PAF_MULTI_LINE_STATE;
+        pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_CAPA].name[1]);
+        break;
+    case 'l':
+    case 'L':
+        pfdata->cmd_state.exp_resp = POP_PAF_HAS_ARG;
+        pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_LIST].name[1]);
+        break;
+    case 'r':
+    case 'R':
+        pfdata->cmd_state.exp_resp = POP_PAF_DATA_STATE;
+        pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_RETR].name[1]);
+        break;
+    case 't':
+    case 'T':
+        pfdata->cmd_state.exp_resp = POP_PAF_DATA_STATE;
+        pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_TOP].name[1]);
+        break;
+    case 'u':
+    case 'U':
+        pfdata->cmd_state.exp_resp = POP_PAF_HAS_ARG;
+        pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_UIDL].name[1]);
+        break;
+    default:
+        pfdata->cmd_state.status = POP_CMD_FIN;
+    }
+
+    return false;
+}
+
+/*
+ * Attempts to determine the current command based upon the given character
+ * If another character is required to determine the current command,
+ * sets the function pointer to the correct next state
+ *
+ * PARAMS:
+ *         pop_cmd - a pointer to the struct containing all of the
+ *                     relevant parsing info
+ *         ch      - the first character from the clients command
+ * RETURNS
+ *         true  - if the expected response is NOT a single line
+ *         false - otherwise
+ */
+static inline bool process_command(PopPafData* pfdata, const uint8_t ch)
+{
+    if (pfdata->cmd_state.next_letter)
+        return search_for_command(pfdata, ch);
+    else
+        return init_command_search(pfdata, ch);
+}
+
+static inline void reset_data_states(PopPafData* pfdata)
+{
+    // reset MIME info
+    file_api->reset_mime_paf_state(&(pfdata->data_info));
+
+    // reset general pop fields
+    pfdata->cmd_continued = false;
+    pfdata->end_state = PAF_DATA_END_UNKNOWN;
+    pfdata->pop_state = POP_PAF_SINGLE_LINE_STATE;
+}
+
+/*
+ *  Checks if the current data is a valid response.
+ *  According to RFC 1939, every response begins with either
+ *     +OK
+ *     -ERR.
+ *
+ *  RETURNS:
+ *           true - if the character is a +
+ *           false - if the character is anything else
+ */
+static inline int valid_response(const uint8_t data)
+{
+    return (data == '+');
+}
+
+/*
+ * Client PAF calls this command to set the server's state.  This is the
+ * function which ensure's the server know the correct expected
+ * DATA
+ */
+static inline void set_server_state(Flow* ssn, PopExpectedResp state)
+{
+    PopPafData* server_data = get_state(ssn, false);
+
+    // ERROR IF SERVER DATA DOES NOT EXIST!! SHOULD NOT BE POSSIBLE!!
+    if (server_data)
+    {
+        reset_data_states(server_data);
+        server_data->end_of_data = false;
+        server_data->pop_state = state;
+    }
+}
+
+/*
+ * A helper function to reset the client's command parsing
+ * information
+ */
+static inline void reset_client_cmd_info(PopPafData* pfdata)
+{
+    pfdata->cmd_state.next_letter = '\0';
+    pfdata->cmd_state.status = POP_CMD_SEARCH;
+}
+
+/*
+ * Statefully search for the termination sequence CRCL.CRLF ("\r\n.\r\n").
+ *
+ * PARAMS:
+ *        mime_data : true if this is mime_data.
+ *
+ * RETURNS:
+ *         0 - if termination sequence not found
+ *         1 - if termination sequence found
+ */
+static bool find_data_end_multi_line(PopPafData* pfdata, const uint8_t ch, bool mime_data)
+{
+    // TODO:  This will currently flush on MIME boundary, and one line later at end of PDU
+
+    if (file_api->check_data_end(&(pfdata->end_state), ch))
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_POP, "End of Multi-line response found\n"); );
+        pfdata->end_of_data = true;
+        pfdata->pop_state = POP_PAF_SINGLE_LINE_STATE;
+        reset_data_states(pfdata);
+        return true;
+    }
+
+    // if this is a data command, search for MIME ending
+    if (mime_data)
+    {
+        if (file_api->process_mime_paf_data(&(pfdata->data_info), ch))
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_POP, "Mime Boundary found.  Flushing data!\n"); );
+            pfdata->cmd_continued = true;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+/*
+ * Statefully search for the termination sequence LF ("\n").  Will also
+ * set the correct response state.
+ *
+ * PARAMS:
+ *
+ * RETURNS:
+ *         0 - if terminatino sequence not found
+ *         1 - if termination sequence found
+ */
+static inline bool find_data_end_single_line(PopPafData* pfdata, const uint8_t ch, bool client)
+{
+    if (ch == '\n')
+    {
+        // reset the correct information
+        if (client)
+            reset_client_cmd_info(pfdata);
+        else
+            reset_data_states(pfdata);
+
+        DEBUG_WRAP(DebugMessage(DEBUG_POP, "End of single-line response "
+            "found.  Flushing data!\n"); );
+        return true;
+    }
+
+    return false;
+}
+
+static StreamSplitter::Status pop_paf_server(PopPafData* pfdata,
+    const uint8_t* data, uint32_t len, uint32_t* fp)
+{
+    uint32_t i;
+    uint32_t boundary_start = 0;
+
+    // if a negative response was received, it will be a one line response.
+    if (!pfdata->cmd_continued && !valid_response(*data))
+        pfdata->pop_state = POP_PAF_SINGLE_LINE_STATE;
+
+    for (i = 0; i < len; i++)
+    {
+        uint8_t ch = data[i];
+
+        // find the termination sequence based upon the current state
+        switch (pfdata->pop_state)
+        {
+        case POP_PAF_MULTI_LINE_STATE:
+            if ( find_data_end_multi_line(pfdata, ch, false) )
+            {
+                *fp = i + 1;
+                return StreamSplitter::FLUSH;
+            }
+            break;
+
+        case POP_PAF_DATA_STATE:
+            // TODO --> statefully get length
+            if ( find_data_end_multi_line(pfdata, ch, true) )
+            {
+                *fp = i + 1;
+                return StreamSplitter::FLUSH;
+            }
+
+            if (pfdata->data_info.boundary_state == MIME_PAF_BOUNDARY_UNKNOWN)
+                boundary_start = i;
+
+            break;
+
+        case POP_PAF_SINGLE_LINE_STATE:
+        default:
+            if ( find_data_end_single_line(pfdata, ch, false) )
+            {
+                *fp = i + 1;
+                return StreamSplitter::FLUSH;
+            }
+            break;
+        }
+    }
+
+    pfdata->cmd_continued = true;
+
+    if ( scanning_boundary(&pfdata->data_info, boundary_start, fp) )
+        return StreamSplitter::LIMIT;
+
+    return StreamSplitter::SEARCH;
+}
+
+/*
+ * Determine the Client's command and set the response state.
+ * Flush data when "\r\n" is received
+ */
+static StreamSplitter::Status pop_paf_client(Flow* ssn, PopPafData* pfdata,
+    const uint8_t* data, uint32_t len, uint32_t* fp)
+{
+    uint32_t i;
+
+    // TODO ... ensure current command is smaller than max command length
+
+    for (i = 0; i < len; i++)
+    {
+        uint8_t ch = data[i];
+
+        switch (pfdata->cmd_state.status)
+        {
+        case POP_CMD_SEARCH:
+            if (process_command(pfdata, ch) )
+            {
+                set_server_state(ssn, pfdata->pop_state);
+            }
+
+        //break;  DO NOT UNCOMMENT!!  both cases should check for a LF.
+
+        case POP_CMD_FIN:
+            if (find_data_end_single_line(pfdata, ch, true) )
+            {
+                // reset command parsing data
+                *fp = i + 1;
+                return StreamSplitter::FLUSH;
+            }
+            break;
+
+        case POP_CMD_ARG:
+            if (find_data_end_single_line(pfdata, ch, true))
+            {
+                set_server_state(ssn, POP_PAF_MULTI_LINE_STATE);
+                *fp = i + 1;
+                return StreamSplitter::FLUSH;
+            }
+            else if (isdigit(ch))
+            {
+                pfdata->cmd_state.status = POP_CMD_FIN;
+            }
+        }
+    }
+
+    return StreamSplitter::SEARCH;
+}
+
+//--------------------------------------------------------------------
+// callback for stateful scanning of in-order raw payload
+//--------------------------------------------------------------------
+
+PopSplitter::PopSplitter(bool c2s) : StreamSplitter(c2s)
+{
+    memset(&state, 0, sizeof(state));
+    reset_data_states(&state);
+}
+
+PopSplitter::~PopSplitter() { }
+
+/* Function: pop_paf()
+
+   Purpose: POP PAF callback.
+            Inspects pop traffic.  Checks client traffic for the current command
+            and sets correct server termination sequence. Client side data will
+            flush after receiving CRLF ("\r\n").  Server data flushes after
+            finding set termination sequence.
+
+   Arguments:
+     void * - stream5 session pointer
+     void ** - DNP3 state tracking structure
+     const uint8_t * - payload data to inspect
+     uint32_t - length of payload data
+     uint32_t - flags to check whether client or server
+     uint32_t * - pointer to set flush point
+
+   Returns:
+    StreamSplitter::Status - StreamSplitter::FLUSH if flush point found, StreamSplitter::SEARCH otherwise
+*/
+
+StreamSplitter::Status PopSplitter::scan(
+    Flow* ssn, const uint8_t* data, uint32_t len,
+    uint32_t flags, uint32_t* fp)
+{
+    PopPafData* pfdata = &state;
+
+    if (flags & PKT_FROM_SERVER)
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_POP, "PAF: From server.\n"); );
+        return pop_paf_server(pfdata, data, len, fp);
+    }
+    else
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_POP, "PAF: From client.\n"); );
+        return pop_paf_client(ssn, pfdata, data, len, fp);
+    }
+}
+
+bool pop_is_data_end(void* session)
+{
+    Flow* ssn = (Flow*)session;
+    PopPafData* s = get_state(ssn, false);
+    return s->end_of_data;
+}
+
diff --git a/src/service_inspectors/pop/pop_paf.h b/src/service_inspectors/pop/pop_paf.h
new file mode 100644 (file)
index 0000000..21d1686
--- /dev/null
@@ -0,0 +1,82 @@
+/****************************************************************************
+ * Copyright (C) 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.
+ *
+ ****************************************************************************/
+
+#ifndef POP_PAF_H
+#define POP_PAF_H
+
+#include "snort_types.h"
+#include "stream/stream_api.h"
+#include "stream/stream_splitter.h"
+#include "file_api/file_api.h"
+
+/* Structure used to record expected server termination sequence */
+enum PopExpectedResp
+{
+    POP_PAF_SINGLE_LINE_STATE,      /* server response will end with \r\n */
+    POP_PAF_MULTI_LINE_STATE,       /* server response will end with \r\n.\r\n */
+    POP_PAF_DATA_STATE,             /* Indicated MIME will be contained in response */
+    POP_PAF_HAS_ARG                 /* Intermediate state when parsing LIST */
+};
+
+enum PopParseCmdState
+{
+    POP_CMD_SEARCH,         /* Search for Command */
+    POP_CMD_FIN,            /* Found space. Finished parsing Command */
+    POP_CMD_ARG             /* Parsing command with multi-line response iff arg given */
+};
+
+/* saves data when parsing client commands */
+struct PopPafParseCmd
+{
+    const char* next_letter;        /* a pointer to the current commands data */
+    PopExpectedResp exp_resp;       /* the expected termination sequence for this command */
+    PopParseCmdState status;        /* whether the current has already been found */
+};
+
+/* State tracker for POP PAF */
+struct PopPafData
+{
+    PopExpectedResp pop_state;       /* The current POP PAF state. */
+    PopPafParseCmd cmd_state;        /* all of the command parsing data */
+    DataEndState end_state;          /* Current termination sequence state */
+    MimeDataPafInfo data_info;       /* Mime Information */
+    bool cmd_continued;              /* data continued from previous packet? */
+    bool end_of_data;
+};
+
+class PopSplitter : public StreamSplitter
+{
+public:
+    PopSplitter(bool c2s);
+    ~PopSplitter();
+
+    Status scan(Flow*, const uint8_t* data, uint32_t len,
+        uint32_t flags, uint32_t* fp) override;
+
+    virtual bool is_paf() override { return true; }
+
+public:
+    PopPafData state;
+};
+
+bool pop_is_data_end(void* ssn);
+
+#endif
+
index ae4ae7461e4302e44f25f396d803046955c15d36..b0d8879b50d5dc119c8a33513c1199eae1e7475e 100644 (file)
@@ -33,7 +33,9 @@ extern const BaseApi* sin_dns;
 extern const BaseApi* sin_ftp_client;
 extern const BaseApi* sin_ftp_server;
 extern const BaseApi* sin_ftp_data;
+extern const BaseApi* sin_imap;
 extern const BaseApi* sin_nhttp;
+extern const BaseApi* sin_pop;
 extern const BaseApi* sin_rpc_decode;
 extern const BaseApi* sin_ssh;
 extern const BaseApi* sin_telnet;
@@ -51,7 +53,9 @@ const BaseApi* service_inspectors[] =
     sin_ftp_client,
     sin_ftp_server,
     sin_ftp_data,
+    sin_imap,
     sin_nhttp,
+    sin_pop,
     sin_rpc_decode,
     sin_ssh,
     sin_telnet,
index 67f5a04f533846a4e833c7a220f2f89d29c5aa45..1dcc22ad09e996e80f40ca841b25f2d7b6411f29 100644 (file)
@@ -14,6 +14,8 @@ add_library(preprocessor_states
     pps_rpc_decode.cc
     pps_ssh.cc
     pps_dns.cc
+    pps_pop.cc
+    pps_imap.cc
     pps_sfportscan.cc
     pps_stream5_ip.cc
     pps_stream5_global.cc
index d88d0f06e802a4a4798cb428766dfd61bece59ad..48a4ca0a5f4eced71208e22c10887caf9d111688 100644 (file)
@@ -18,6 +18,8 @@ pps_perfmonitor.cc \
 pps_rpc_decode.cc \
 pps_ssh.cc \
 pps_dns.cc \
+pps_pop.cc \
+pps_imap.cc \
 pps_sfportscan.cc \
 pps_stream5_ip.cc \
 pps_stream5_global.cc \
diff --git a/tools/snort2lua/preprocessor_states/pps_imap.cc b/tools/snort2lua/preprocessor_states/pps_imap.cc
new file mode 100644 (file)
index 0000000..58661ef
--- /dev/null
@@ -0,0 +1,149 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-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.
+//--------------------------------------------------------------------------
+// pps_imap.cc author Bhagya Bantwal <bbantwal@cisco.com>
+
+#include <sstream>
+#include <vector>
+
+#include "conversion_state.h"
+#include "helpers/s2l_util.h"
+#include "helpers/util_binder.h"
+
+namespace preprocessors
+{
+namespace
+{
+class Imap : public ConversionState
+{
+public:
+    Imap(Converter& c) : ConversionState(c) { }
+    virtual ~Imap() { }
+    virtual bool convert(std::istringstream& data_stream);
+
+};
+} // namespace
+
+bool Imap::convert(std::istringstream& data_stream)
+{
+    std::string keyword;
+    bool retval = true;
+    bool ports_set = false;
+    Binder bind(table_api);
+
+    bind.set_when_proto("tcp");
+    bind.set_use_type("imap");
+
+    table_api.open_table("imap");
+
+
+    // parse the file configuration
+    while (data_stream >> keyword)
+    {
+        bool tmpval = true;
+
+        if (!keyword.compare("disabled"))
+        {
+            table_api.add_deleted_comment("disabled");
+        }
+
+        else if (!keyword.compare("memcap"))
+        {
+            table_api.add_deleted_comment("memcap");
+        }
+
+        else if (!keyword.compare("max_mime_mem"))
+        {
+            table_api.add_deleted_comment("max_mime_mem");
+        }
+
+        else if (!keyword.compare("b64_decode_depth"))
+        {
+            tmpval = parse_int_option("b64_decode_depth", data_stream, false);
+        }
+
+        else if (!keyword.compare("qp_decode_depth"))
+        {
+            tmpval = parse_int_option("qp_decode_depth", data_stream, false);
+        }
+
+        else if (!keyword.compare("bitenc_decode_depth"))
+        {
+            tmpval = parse_int_option("bitenc_decode_depth", data_stream, false);
+        }
+
+        else if (!keyword.compare("uu_decode_depth"))
+        {
+            tmpval = parse_int_option("uu_decode_depth", data_stream, false);
+        }
+
+        else if (!keyword.compare("ports"))
+        {
+            std::string tmp = "";
+            table_api.add_diff_option_comment("ports", "bindings");
+
+            if ((data_stream >> keyword) && !keyword.compare("{"))
+            {
+                while (data_stream >> keyword && keyword.compare("}"))
+                {
+                    ports_set = true;
+                    bind.add_when_port(keyword);
+                }
+            }
+            else
+            {
+                data_api.failed_conversion(data_stream, "ports <bracketed_port_list>");
+                retval = false;
+            }
+        }
+
+        else
+        {
+            tmpval = false;
+        }
+
+        if (!tmpval)
+        {
+            data_api.failed_conversion(data_stream, keyword);
+            retval = false;
+        }
+    }
+
+    if (!ports_set)
+        bind.add_when_port("143");
+
+    return retval;
+}
+
+/**************************
+ *******  A P I ***********
+ **************************/
+
+static ConversionState* ctor(Converter& c)
+{
+    return new Imap(c);
+}
+
+static const ConvertMap preprocessor_imap =
+{
+    "imap",
+    ctor,
+};
+
+const ConvertMap* imap_map = &preprocessor_imap;
+}
+
diff --git a/tools/snort2lua/preprocessor_states/pps_pop.cc b/tools/snort2lua/preprocessor_states/pps_pop.cc
new file mode 100644 (file)
index 0000000..14be29e
--- /dev/null
@@ -0,0 +1,149 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-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.
+//--------------------------------------------------------------------------
+// pps_pop.cc author Bhagya Bantwal <bbantwal@cisco.com>
+
+#include <sstream>
+#include <vector>
+
+#include "conversion_state.h"
+#include "helpers/s2l_util.h"
+#include "helpers/util_binder.h"
+
+namespace preprocessors
+{
+namespace
+{
+class Pop : public ConversionState
+{
+public:
+    Pop(Converter& c) : ConversionState(c) { }
+    virtual ~Pop() { }
+    virtual bool convert(std::istringstream& data_stream);
+
+};
+} // namespace
+
+bool Pop::convert(std::istringstream& data_stream)
+{
+    std::string keyword;
+    bool retval = true;
+    bool ports_set = false;
+    Binder bind(table_api);
+
+    bind.set_when_proto("tcp");
+    bind.set_use_type("pop");
+
+    table_api.open_table("pop");
+
+
+    // parse the file configuration
+    while (data_stream >> keyword)
+    {
+        bool tmpval = true;
+
+        if (!keyword.compare("disabled"))
+        {
+            table_api.add_deleted_comment("disabled");
+        }
+
+        else if (!keyword.compare("memcap"))
+        {
+            table_api.add_deleted_comment("memcap");
+        }
+
+        else if (!keyword.compare("max_mime_mem"))
+        {
+            table_api.add_deleted_comment("max_mime_mem");
+        }
+
+        else if (!keyword.compare("b64_decode_depth"))
+        {
+            tmpval = parse_int_option("b64_decode_depth", data_stream, false);
+        }
+
+        else if (!keyword.compare("qp_decode_depth"))
+        {
+            tmpval = parse_int_option("qp_decode_depth", data_stream, false);
+        }
+
+        else if (!keyword.compare("bitenc_decode_depth"))
+        {
+            tmpval = parse_int_option("bitenc_decode_depth", data_stream, false);
+        }
+
+        else if (!keyword.compare("uu_decode_depth"))
+        {
+            tmpval = parse_int_option("uu_decode_depth", data_stream, false);
+        }
+
+        else if (!keyword.compare("ports"))
+        {
+            std::string tmp = "";
+            table_api.add_diff_option_comment("ports", "bindings");
+
+            if ((data_stream >> keyword) && !keyword.compare("{"))
+            {
+                while (data_stream >> keyword && keyword.compare("}"))
+                {
+                    ports_set = true;
+                    bind.add_when_port(keyword);
+                }
+            }
+            else
+            {
+                data_api.failed_conversion(data_stream, "ports <bracketed_port_list>");
+                retval = false;
+            }
+        }
+
+        else
+        {
+            tmpval = false;
+        }
+
+        if (!tmpval)
+        {
+            data_api.failed_conversion(data_stream, keyword);
+            retval = false;
+        }
+    }
+
+    if (!ports_set)
+        bind.add_when_port("110");
+
+    return retval;
+}
+
+/**************************
+ *******  A P I ***********
+ **************************/
+
+static ConversionState* ctor(Converter& c)
+{
+    return new Pop(c);
+}
+
+static const ConvertMap preprocessor_pop =
+{
+    "pop",
+    ctor,
+};
+
+const ConvertMap* pop_map = &preprocessor_pop;
+}
+
index c8e77a4c9838057cd8b914e7242fb7fb83b0599d..68a7e675cea59ae6dd5af3d49c8657eb0ef37868 100644 (file)
@@ -40,6 +40,8 @@ extern const ConvertMap* perfmonitor_map;
 extern const ConvertMap* rpc_decode_map;
 extern const ConvertMap* ssh_map;
 extern const ConvertMap* dns_map;
+extern const ConvertMap* pop_map;
+extern const ConvertMap* imap_map;
 extern const ConvertMap* sfportscan_map;
 extern const ConvertMap* stream_ip_map;
 extern const ConvertMap* stream_global_map;
@@ -67,6 +69,8 @@ const std::vector<const ConvertMap*> preprocessor_api =
     rpc_decode_map,
     ssh_map,
     dns_map,
+    pop_map,
+    imap_map,
     sfportscan_map,
     stream_ip_map,
     stream_global_map,