]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
FTP parser updated to not use the archaic App layer feature of AppLayerParserResultElmt.
authorAnoop Saldanha <anoopsaldanha@gmail.com>
Mon, 21 Oct 2013 15:03:09 +0000 (20:33 +0530)
committerVictor Julien <victor@inliniac.net>
Wed, 4 Dec 2013 08:49:00 +0000 (09:49 +0100)
The parser otherwise remains pretty much the same.

src/app-layer-ftp.c
src/app-layer-ftp.h

index bf1ccaf8fe280fa8c8ec8939ce1b92de92b73966..7aac7273707bd1f21e471d60d99ea2fa4905d0f1 100644 (file)
 #include "util-debug.h"
 #include "util-memcmp.h"
 
+static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state)
+{
+    if (line_state->current_line_lf_seen == 1) {
+        /* we have seen the lf for the previous line.  Clear the parser
+         * details to parse new line */
+        line_state->current_line_lf_seen = 0;
+        if (line_state->current_line_db == 1) {
+            line_state->current_line_db = 0;
+            SCFree(line_state->db);
+            line_state->db = NULL;
+            line_state->db_len = 0;
+            state->current_line = NULL;
+            state->current_line_len = 0;
+        }
+    }
+
+    uint8_t *lf_idx = memchr(state->input, 0x0a, state->input_len);
+
+    if (lf_idx == NULL) {
+        /* fragmented lines.  Decoder event for special cases.  Not all
+         * fragmented lines should be treated as a possible evasion
+         * attempt.  With multi payload ftp chunks we can have valid
+         * cases of fragmentation.  But within the same segment chunk
+         * if we see fragmentation then it's definitely something you
+         * should alert about */
+        if (line_state->current_line_db == 0) {
+            line_state->db = SCMalloc(state->input_len);
+            if (line_state->db == NULL) {
+                return -1;
+            }
+            line_state->current_line_db = 1;
+            memcpy(line_state->db, state->input, state->input_len);
+            line_state->db_len = state->input_len;
+        } else {
+            line_state->db = SCRealloc(line_state->db,
+                                                (line_state->db_len +
+                                                 state->input_len));
+            if (line_state->db == NULL) {
+                return -1;
+            }
+            memcpy(line_state->db + line_state->db_len,
+                   state->input, state->input_len);
+            line_state->db_len += state->input_len;
+        }
+        state->input += state->input_len;
+        state->input_len = 0;
+
+        return -1;
+
+    } else {
+        line_state->current_line_lf_seen = 1;
+
+        if (line_state->current_line_db == 1) {
+            line_state->db = SCRealloc(line_state->db,
+                                                (line_state->db_len +
+                                                 (lf_idx + 1 - state->input)));
+            if (line_state->db == NULL) {
+                return -1;
+            }
+            memcpy(line_state->db + line_state->db_len,
+                   state->input, (lf_idx + 1 - state->input));
+            line_state->db_len += (lf_idx + 1 - state->input);
+
+            if (line_state->db_len > 1 &&
+                line_state->db[line_state->db_len - 2] == 0x0D) {
+                line_state->db_len -= 2;
+                state->current_line_delimiter_len = 2;
+            } else {
+                line_state->db_len -= 1;
+                state->current_line_delimiter_len = 1;
+            }
+
+            state->current_line = line_state->db;
+            state->current_line_len = line_state->db_len;
+
+        } else {
+            state->current_line = state->input;
+            state->current_line_len = lf_idx - state->input;
+
+            if (state->input != lf_idx &&
+                *(lf_idx - 1) == 0x0D) {
+                state->current_line_len--;
+                state->current_line_delimiter_len = 2;
+            } else {
+                state->current_line_delimiter_len = 1;
+            }
+        }
+
+        state->input_len -= (lf_idx - state->input) + 1;
+        state->input = (lf_idx + 1);
+
+        return 0;
+    }
+
+}
+
+static int FTPGetLine(FtpState *state)
+{
+    SCEnter();
+
+    /* we have run out of input */
+    if (state->input_len <= 0)
+        return -1;
+
+    /* toserver */
+    if (state->direction == 0)
+        return FTPGetLineForDirection(state, &state->line_state[0]);
+    else
+        return FTPGetLineForDirection(state, &state->line_state[1]);
+}
+
 /**
  * \brief This function is called to determine and set which command is being
  * transfered to the ftp server
@@ -62,6 +173,7 @@ static int FTPParseRequestCommand(void *ftp_state, uint8_t *input,
                                   uint32_t input_len) {
     SCEnter();
     FtpState *fstate = (FtpState *)ftp_state;
+    fstate->command = FTP_COMMAND_UNKNOWN;
 
     if (input_len >= 4) {
         if (SCMemcmpLowercase("port", input, 4) == 0) {
@@ -76,76 +188,6 @@ static int FTPParseRequestCommand(void *ftp_state, uint8_t *input,
     return 1;
 }
 
-/**
- * \brief This function is called to retrieve the request line and parse it
- * \param ftp_state the ftp state structure for the parser
- * \param input input line of the command
- * \param input_len length of the request
- * \param output the resulting output
- *
- * \retval 1 when the command is parsed, 0 otherwise
- */
-static int FTPParseRequestCommandLine(Flow *f, void *ftp_state, AppLayerParserState
-                                      *pstate, uint8_t *input,uint32_t input_len,
-                                      void *local_data, AppLayerParserResult *output) {
-    SCEnter();
-    //PrintRawDataFp(stdout, input,input_len);
-
-    FtpState *fstate = (FtpState *)ftp_state;
-    uint16_t max_fields = 2;
-    uint16_t u = 0;
-    uint32_t offset = 0;
-
-    if (pstate == NULL)
-        return -1;
-
-    for (u = pstate->parse_field; u < max_fields; u++) {
-
-        switch(u) {
-            case 0: /* REQUEST COMMAND */
-            {
-                const uint8_t delim[] = { 0x20, };
-                int r = AlpParseFieldByDelimiter(output, pstate,
-                                FTP_FIELD_REQUEST_COMMAND, delim, sizeof(delim),
-                                input, input_len, &offset);
-
-                if (r == 0) {
-                    pstate->parse_field = 0;
-                    return 0;
-                }
-                fstate->arg_offset = offset;
-                FTPParseRequestCommand(ftp_state, input, input_len);
-                break;
-            }
-            case 1: /* REQUEST COMMAND ARG */
-            {
-                switch (fstate->command) {
-                    case FTP_COMMAND_PORT:
-                        /* We don't need to parse args, we are going to check
-                        * the ftpbounce condition directly from detect-ftpbounce
-                        */
-                        if (fstate->port_line != NULL)
-                            SCFree(fstate->port_line);
-                        fstate->port_line = SCMalloc(input_len);
-                        if (fstate->port_line == NULL)
-                            return 0;
-                        fstate->port_line = memcpy(fstate->port_line, input,
-                                                   input_len);
-                        fstate->port_line_len = input_len;
-                        break;
-                    default:
-                        break;
-                } /* end switch command specified args */
-
-                break;
-            }
-        }
-    }
-
-    pstate->parse_field = 0;
-    return 1;
-}
-
 /**
  * \brief This function is called to retrieve a ftp request
  * \param ftp_state the ftp state structure for the parser
@@ -163,26 +205,32 @@ static int FTPParseRequest(Flow *f, void *ftp_state,
     SCEnter();
     /* PrintRawDataFp(stdout, input,input_len); */
 
-    uint32_t offset = 0;
-
-    if (pstate == NULL)
-        return -1;
-
-
-    //PrintRawDataFp(stdout, pstate->store, pstate->store_len);
-
-    const uint8_t delim[] = { 0x0D, 0x0A };
-    int r = AlpParseFieldByDelimiter(output, pstate, FTP_FIELD_REQUEST_LINE,
-                                     delim, sizeof(delim), input, input_len,
-                                     &offset);
-    if (r == 0) {
-        pstate->parse_field = 0;
-        return 0;
+    FtpState *state = (FtpState *)ftp_state;
+
+    state->input = input;
+    state->input_len = input_len;
+    /* toserver stream */
+    state->direction = 0;
+
+    while (FTPGetLine(state) >= 0) {
+        FTPParseRequestCommand(state,
+                               state->current_line, state->current_line_len);
+        if (state->command == FTP_COMMAND_PORT) {
+            if (state->current_line_len > state->port_line_size) {
+                state->port_line = SCRealloc(state->port_line,
+                                             state->current_line_len);
+                if (state->port_line == NULL) {
+                    state->port_line_size = 0;
+                    return 0;
+                }
+                state->port_line_size = state->current_line_len;
+            }
+            memcpy(state->port_line, state->current_line,
+                   state->current_line_len);
+            state->port_line_len = state->current_line_len;
+        }
     }
-    if (pstate->store_len)
-        PrintRawDataFp(stdout, pstate->store, pstate->store_len);
 
-    pstate->parse_field = 0;
     return 1;
 }
 
@@ -253,9 +301,6 @@ void RegisterFTPParsers(void) {
                               FTPParseRequest);
         AppLayerRegisterProto(proto_name, ALPROTO_FTP, STREAM_TOCLIENT,
                               FTPParseResponse);
-        AppLayerRegisterParser("ftp.request_command_line", ALPROTO_FTP,
-                               FTP_FIELD_REQUEST_LINE, FTPParseRequestCommandLine,
-                               "ftp");
         AppLayerRegisterStateFuncs(ALPROTO_FTP, FTPStateAlloc, FTPStateFree);
     } else {
         SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
@@ -479,8 +524,9 @@ int FTPParserTest07(void) {
         goto end;
     }
 
-    if (ftp_state->command != FTP_COMMAND_UNKNOWN) {
-        SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_UNKNOWN, ftp_state->command);
+    if (ftp_state->command != FTP_COMMAND_PORT) {
+        SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ",
+                   FTP_COMMAND_PORT, ftp_state->command);
         result = 0;
         goto end;
     }
index c102addc9005fae55ab48f5f888aa6e6219371a6..4a001290d554487c22675db2e87939c8b0c1887e 100644 (file)
@@ -91,11 +91,37 @@ enum {
     FTP_FIELD_MAX,
 };
 
+/** used to hold the line state when we have fragmentation. */
+typedef struct FtpLineState_ {
+    /** used to indicate if the current_line buffer is a malloced buffer.  We
+     * use a malloced buffer, if a line is fragmented */
+    uint8_t *db;
+    uint32_t db_len;
+    uint8_t current_line_db;
+    /** we have see LF for the currently parsed line */
+    uint8_t current_line_lf_seen;
+} FtpLineState;
+
 /** FTP State for app layer parser */
 typedef struct FtpState_ {
+    uint8_t *input;
+    int32_t input_len;
+    uint8_t direction;
+
+    /* --parser details-- */
+    /** current line extracted by the parser from the call to FTPGetline() */
+    uint8_t *current_line;
+    /** length of the line in current_line.  Doesn't include the delimiter */
+    uint32_t current_line_len;
+    uint8_t current_line_delimiter_len;
+
+    /* 0 for toserver, 1 for toclient */
+    FtpLineState line_state[2];
+
     FtpRequestCommand command;
     FtpRequestCommandArgOfs arg_offset;
     uint32_t port_line_len;
+    uint32_t port_line_size;
     uint8_t *port_line;
 } FtpState;