]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
app-layer-ftp: add memcap for ftp
authorEric Leblond <eric@regit.org>
Fri, 24 Nov 2017 15:59:34 +0000 (16:59 +0100)
committerVictor Julien <victor@inliniac.net>
Tue, 19 Dec 2017 20:00:15 +0000 (21:00 +0100)
Add a memory cap for the FTP protocol.

src/app-layer-ftp.c
src/app-layer-ftp.h
src/app-layer.c
suricata.yaml.in

index b080eb77bd1740d05ef85b01aa0177d6d21d4528..d13cb914f6e8b66832e8519d5ad65799366ae7a1 100644 (file)
 #include "util-memcmp.h"
 #include "util-memrchr.h"
 #include "util-byte.h"
+#include "util-mem.h"
+#include "util-misc.h"
 
 #ifdef HAVE_RUST
 #include "rust-ftp-mod-gen.h"
 #endif
 
+uint64_t ftp_config_memcap = 0;
+
+SC_ATOMIC_DECLARE(uint64_t, ftp_memuse);
+SC_ATOMIC_DECLARE(uint64_t, ftp_memcap);
+
+static void FTPParseMemcap(void)
+{
+    const char *conf_val;
+
+    /** set config values for memcap, prealloc and hash_size */
+    if ((ConfGet("app-layer.protocols.ftp.memcap", &conf_val)) == 1)
+    {
+        if (ParseSizeStringU64(conf_val, &ftp_config_memcap) < 0) {
+            SCLogError(SC_ERR_SIZE_PARSE, "Error parsing ftp.memcap "
+                       "from conf file - %s.  Killing engine",
+                       conf_val);
+            exit(EXIT_FAILURE);
+        }
+        SCLogInfo("FTP memcap: %"PRIu64, ftp_config_memcap);
+    } else {
+        /* default to unlimited */
+        ftp_config_memcap = 0;
+    }
+
+    SC_ATOMIC_INIT(ftp_memuse);
+    SC_ATOMIC_INIT(ftp_memcap);
+}
+
+static void FTPIncrMemuse(uint64_t size)
+{
+    (void) SC_ATOMIC_ADD(ftp_memuse, size);
+    return;
+}
+
+static void FTPDecrMemuse(uint64_t size)
+{
+    (void) SC_ATOMIC_SUB(ftp_memuse, size);
+    return;
+}
+
+uint64_t FTPMemuseGlobalCounter(void)
+{
+    uint64_t tmpval = SC_ATOMIC_GET(ftp_memuse);
+    return tmpval;
+}
+
+uint64_t FTPMemcapGlobalCounter(void)
+{
+    uint64_t tmpval = SC_ATOMIC_GET(ftp_memcap);
+    return tmpval;
+}
+
+/**
+ *  \brief Check if alloc'ing "size" would mean we're over memcap
+ *
+ *  \retval 1 if in bounds
+ *  \retval 0 if not in bounds
+ */
+static int FTPCheckMemcap(uint64_t size)
+{
+    if (ftp_config_memcap == 0 || size + SC_ATOMIC_GET(ftp_memuse) <= ftp_config_memcap)
+        return 1;
+    (void) SC_ATOMIC_ADD(ftp_memcap, 1);
+    return 0;
+}
+
+static void *FTPMalloc(size_t size)
+{
+    void *ptr = NULL;
+
+    if (FTPCheckMemcap((uint32_t)size) == 0)
+        return NULL;
+
+    ptr = SCMalloc(size);
+
+    if (unlikely(ptr == NULL))
+        return NULL;
+
+    FTPIncrMemuse((uint64_t)size);
+
+    return ptr;
+}
+
+static void *FTPCalloc(size_t n, size_t size)
+{
+    void *ptr = NULL;
+
+    if (FTPCheckMemcap((uint32_t)(n * size)) == 0)
+        return NULL;
+
+    ptr = SCCalloc(n, size);
+
+    if (unlikely(ptr == NULL))
+        return NULL;
+
+    FTPIncrMemuse((uint64_t)(n * size));
+
+    return ptr;
+}
+
+static void *FTPRealloc(void *ptr, size_t orig_size, size_t size)
+{
+    void *rptr = NULL;
+
+    if (FTPCheckMemcap((uint32_t)(size - orig_size)) == 0)
+        return NULL;
+
+    rptr = SCRealloc(ptr, size);
+    if (rptr == NULL)
+        return NULL;
+
+    if (size > orig_size) {
+        FTPIncrMemuse(size - orig_size);
+    } else {
+        FTPDecrMemuse(orig_size - size);
+    }
+
+    return rptr;
+}
+
+static void FTPFree(void *ptr, size_t size)
+{
+    SCFree(ptr);
+
+    FTPDecrMemuse((uint64_t)size);
+}
+
 static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state)
 {
     void *ptmp;
@@ -68,7 +197,7 @@ static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state)
         line_state->current_line_lf_seen = 0;
         if (line_state->current_line_db == 1) {
             line_state->current_line_db = 0;
-            SCFree(line_state->db);
+            FTPFree(line_state->db, line_state->db_len);
             line_state->db = NULL;
             line_state->db_len = 0;
             state->current_line = NULL;
@@ -86,7 +215,7 @@ static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state)
          * 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);
+            line_state->db = FTPMalloc(state->input_len);
             if (line_state->db == NULL) {
                 return -1;
             }
@@ -94,10 +223,10 @@ static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state)
             memcpy(line_state->db, state->input, state->input_len);
             line_state->db_len = state->input_len;
         } else {
-            ptmp = SCRealloc(line_state->db,
+            ptmp = FTPRealloc(line_state->db, line_state->db_len,
                              (line_state->db_len + state->input_len));
             if (ptmp == NULL) {
-                SCFree(line_state->db);
+                FTPFree(line_state->db, line_state->db_len);
                 line_state->db = NULL;
                 line_state->db_len = 0;
                 return -1;
@@ -117,10 +246,10 @@ static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state)
         line_state->current_line_lf_seen = 1;
 
         if (line_state->current_line_db == 1) {
-            ptmp = SCRealloc(line_state->db,
+            ptmp = FTPRealloc(line_state->db, line_state->db_len,
                              (line_state->db_len + (lf_idx + 1 - state->input)));
             if (ptmp == NULL) {
-                SCFree(line_state->db);
+                FTPFree(line_state->db, line_state->db_len);
                 line_state->db = NULL;
                 line_state->db_len = 0;
                 return -1;
@@ -240,7 +369,7 @@ static void FtpTransferCmdFree(void *data)
     if (cmd->file_name) {
         SCFree(cmd->file_name);
     }
-    SCFree(cmd);
+    FTPFree(cmd, sizeof(struct FtpTransferCmd));
 }
 
 /**
@@ -281,9 +410,10 @@ static int FTPParseRequest(Flow *f, void *ftp_state,
         switch (state->command) {
             case FTP_COMMAND_PORT:
                 if (state->current_line_len > state->port_line_size) {
-                    ptmp = SCRealloc(state->port_line, state->current_line_len);
+                    ptmp = FTPRealloc(state->port_line, state->port_line_size,
+                                      state->current_line_len);
                     if (ptmp == NULL) {
-                        SCFree(state->port_line);
+                        FTPFree(state->port_line, state->port_line_size);
                         state->port_line = NULL;
                         state->port_line_size = 0;
                         return 0;
@@ -308,14 +438,14 @@ static int FTPParseRequest(Flow *f, void *ftp_state,
                     if (state->dyn_port == 0) {
                         SCReturnInt(-1);
                     }
-                    struct FtpTransferCmd *data = SCCalloc(1, sizeof(struct FtpTransferCmd));
+                    struct FtpTransferCmd *data = FTPCalloc(1, sizeof(struct FtpTransferCmd));
                     if (data == NULL)
                         SCReturnInt(-1);
                     data->DFree = FtpTransferCmdFree;
                     /* Min size has been checked in FTPParseRequestCommand */
-                    data->file_name = SCCalloc(state->current_line_len - 4, sizeof(char));
+                    data->file_name = FTPCalloc(state->current_line_len - 4, sizeof(char));
                     if (data->file_name == NULL) {
-                        SCFree(data);
+                        FTPFree(data, sizeof(struct FtpTransferCmd));
                         SCReturnInt(-1);
                     }
                     data->file_name[state->current_line_len - 5] = 0;
@@ -327,7 +457,7 @@ static int FTPParseRequest(Flow *f, void *ftp_state,
                                                          state->dyn_port,
                                                          ALPROTO_FTPDATA, data);
                     if (ret == -1) {
-                        SCFree(data);
+                        FTPFree(data, sizeof(struct FtpTransferCmd));
                         SCLogDebug("No expectation created.");
                         SCReturnInt(-1);
                     } else {
@@ -443,7 +573,7 @@ static uint64_t ftp_state_memcnt = 0;
 
 static void *FTPStateAlloc(void)
 {
-    void *s = SCMalloc(sizeof(FtpState));
+    void *s = FTPMalloc(sizeof(FtpState));
     if (unlikely(s == NULL))
         return NULL;
 
@@ -462,11 +592,11 @@ static void FTPStateFree(void *s)
 {
     FtpState *fstate = (FtpState *) s;
     if (fstate->port_line != NULL)
-        SCFree(fstate->port_line);
+        FTPFree(fstate->port_line, fstate->port_line_size);
     if (fstate->line_state[0].db)
-        SCFree(fstate->line_state[0].db);
+        FTPFree(fstate->line_state[0].db, fstate->line_state[0].db_len);
     if (fstate->line_state[1].db)
-        SCFree(fstate->line_state[1].db);
+        FTPFree(fstate->line_state[1].db, fstate->line_state[1].db_len);
 
     //AppLayerDecoderEventsFreeEvents(&s->decoder_events);
 
@@ -474,7 +604,7 @@ static void FTPStateFree(void *s)
         DetectEngineStateFree(fstate->de_state);
     }
 
-    SCFree(s);
+    FTPFree(s, sizeof(FtpState));
 #ifdef DEBUG
     SCMutexLock(&ftp_state_mem_lock);
     ftp_state_memcnt--;
@@ -678,7 +808,7 @@ static uint64_t ftpdata_state_memcnt = 0;
 
 static void *FTPDataStateAlloc(void)
 {
-    void *s = SCMalloc(sizeof(FtpDataState));
+    void *s = FTPMalloc(sizeof(FtpDataState));
     if (unlikely(s == NULL))
         return NULL;
 
@@ -702,7 +832,7 @@ static void FTPDataStateFree(void *s)
         DetectEngineStateFree(fstate->de_state);
     }
     if (fstate->file_name != NULL) {
-        SCFree(fstate->file_name);
+        FTPFree(fstate->file_name, fstate->file_len);
     }
 
     FileContainerFree(fstate->files);
@@ -831,7 +961,12 @@ void RegisterFTPParsers(void)
                 FTPDataGetAlstateProgressCompletionStatus);
 
         sbcfg.buf_size = 4096;
+        sbcfg.Malloc = FTPMalloc;
+        sbcfg.Calloc = FTPCalloc;
+        sbcfg.Realloc = FTPRealloc;
+        sbcfg.Free = FTPFree;
 
+        FTPParseMemcap();
     } else {
         SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
                   "still on.", proto_name);
index b74f1417632d71366a4fb46d7e8419db9e1aec0f..84373a8662b108879f0cb24506c91f2834a4a034 100644 (file)
@@ -160,6 +160,8 @@ typedef struct FtpDataState_ {
 void RegisterFTPParsers(void);
 void FTPParserRegisterTests(void);
 void FTPAtExitPrintStats(void);
+uint64_t FTPMemuseGlobalCounter(void);
+uint64_t FTPMemcapGlobalCounter(void);
 
 #ifdef HAVE_LIBJANSSON
 json_t *JsonFTPDataAddMetadata(const Flow *f);
index 66cd13a12d384211496cdf86480b12c57147aef8..5366db762e5a6acc0e0dbf90a4f82f6cc88f6280 100644 (file)
@@ -30,6 +30,7 @@
 #include "app-layer-parser.h"
 #include "app-layer-protos.h"
 #include "app-layer-expectation.h"
+#include "app-layer-ftp.h"
 #include "app-layer-detect-proto.h"
 #include "stream-tcp-reassemble.h"
 #include "stream-tcp-private.h"
@@ -833,6 +834,8 @@ void AppLayerRegisterGlobalCounters(void)
     StatsRegisterGlobalCounter("dns.memcap_global", DNSMemcapGetMemcapGlobalCounter);
     StatsRegisterGlobalCounter("http.memuse", HTPMemuseGlobalCounter);
     StatsRegisterGlobalCounter("http.memcap", HTPMemcapGlobalCounter);
+    StatsRegisterGlobalCounter("ftp.memuse", FTPMemuseGlobalCounter);
+    StatsRegisterGlobalCounter("ftp.memcap", FTPMemcapGlobalCounter);
     StatsRegisterGlobalCounter("app_layer.expectations", ExpectationGetCounter);
 }
 
index e0669e67554873bf6949765597bf9239e5f130ab..eb89c8c428af4ba9ae1ae2e73665e4e5d1927ead 100644 (file)
@@ -705,6 +705,7 @@ app-layer:
       enabled: yes
     ftp:
       enabled: yes
+      # memcap: 64mb
     ssh:
       enabled: yes
     smtp: