]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
filestore v2: use fileinfo records as metadata
authorJason Ish <ish@unx.ca>
Mon, 8 Jan 2018 20:09:01 +0000 (14:09 -0600)
committerJason Ish <ish@unx.ca>
Thu, 18 Jan 2018 13:26:29 +0000 (07:26 -0600)
As fileinfo records are logged to the main eve log, disable
metadata by default. But when enabled, just use the fileinfo
record.

Metadata is stored in a file named:
  <sha256>.<seconds>.<file_id>.json

where the sha256 is the same as the file logged, the seconds
is the unix timestamp in seconds for the fileinfo record,
and the file_id is an atomically incremented integer per
Suricata instance.

This should allow for each occurrence of the same file to have
its own metadata file. But a collision is expected when running
Suricata repeatedly over the same pcap, as that would be the
exact same occurrence of a file.

src/output-filestore.c
src/suricata-common.h
suricata.yaml.in

index a1edf0922abb0eff57bec3175d79fcccd4b76e32..fe3ca3dd4868de2740242fb673f562c559152487 100644 (file)
@@ -15,6 +15,8 @@
  * 02110-1301, USA.
  */
 
+#include <utime.h>
+
 #include "suricata-common.h"
 
 #include "app-layer-parser.h"
@@ -23,6 +25,7 @@
 
 #include "output.h"
 #include "output-filestore.h"
+#include "output-json-file.h"
 
 #include "util-print.h"
 #include "util-misc.h"
 
 #define MODULE_NAME "OutputFilestore"
 
+/* Create a filestore specific PATH_MAX that is less than the system
+ * PATH_MAX to prevent newer gcc truncation warnings with snprint. */
 #define SHA256_STRING_LEN (SHA256_LENGTH * 2)
 #define LEAF_DIR_MAX_LEN 4
 #define FILESTORE_PREFIX_MAX (PATH_MAX - SHA256_STRING_LEN - LEAF_DIR_MAX_LEN)
 
+/* The default log directory, relative to the default log
+ * directory. */
 static const char *default_log_dir = "filestore";
 
-SC_ATOMIC_DECLARE(uint32_t, filestore_open_file_cnt);  /**< Atomic
-                                                        * counter of
-                                                        * simultaneously
-                                                        * open
-                                                        * files */
+/* Atomic counter of simultaneously open files. */
+static SC_ATOMIC_DECLARE(uint32_t, filestore_open_file_cnt);
 
 typedef struct OutputFilestoreCtx_ {
     char prefix[FILESTORE_PREFIX_MAX];
     char tmpdir[FILESTORE_PREFIX_MAX];
+    bool fileinfo;
 } OutputFilestoreCtx;
 
 typedef struct OutputFilestoreLogThread_ {
@@ -56,106 +61,7 @@ typedef struct OutputFilestoreLogThread_ {
 
 static uint64_t OutputFilestoreOpenFilesCounter(void)
 {
-    uint64_t fcopy = SC_ATOMIC_GET(filestore_open_file_cnt);
-    return fcopy;
-}
-
-static void OutputFilestoreMetaGetUri(FILE *fp, const Packet *p, const File *ff)
-{
-    HtpState *htp_state = (HtpState *)p->flow->alstate;
-    if (htp_state != NULL) {
-        htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, ff->txid);
-        if (tx != NULL) {
-            HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
-            if (tx_ud->request_uri_normalized != NULL) {
-                PrintRawUriFp(fp, bstr_ptr(tx_ud->request_uri_normalized),
-                              bstr_len(tx_ud->request_uri_normalized));
-            }
-            return;
-        }
-    }
-
-    fprintf(fp, "<unknown>");
-}
-
-static void OutputFilestoreMetaGetHost(FILE *fp, const Packet *p, const File *ff)
-{
-    HtpState *htp_state = (HtpState *)p->flow->alstate;
-    if (htp_state != NULL) {
-        htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, ff->txid);
-        if (tx != NULL && tx->request_hostname != NULL) {
-            PrintRawUriFp(fp, (uint8_t *)bstr_ptr(tx->request_hostname),
-                          bstr_len(tx->request_hostname));
-            return;
-        }
-    }
-
-    fprintf(fp, "<unknown>");
-}
-
-static void OutputFilestoreMetaGetReferer(FILE *fp, const Packet *p, const File *ff)
-{
-    HtpState *htp_state = (HtpState *)p->flow->alstate;
-    if (htp_state != NULL) {
-        htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, ff->txid);
-        if (tx != NULL) {
-            htp_header_t *h = NULL;
-            h = (htp_header_t *)htp_table_get_c(tx->request_headers,
-                                                "Referer");
-            if (h != NULL) {
-                PrintRawUriFp(fp, (uint8_t *)bstr_ptr(h->value),
-                              bstr_len(h->value));
-                return;
-            }
-        }
-    }
-
-    fprintf(fp, "<unknown>");
-}
-
-static void OutputFilestoreMetaGetUserAgent(FILE *fp, const Packet *p, const File *ff)
-{
-    HtpState *htp_state = (HtpState *)p->flow->alstate;
-    if (htp_state != NULL) {
-        htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, htp_state, ff->txid);
-        if (tx != NULL) {
-            htp_header_t *h = NULL;
-            h = (htp_header_t *)htp_table_get_c(tx->request_headers,
-                                                "User-Agent");
-            if (h != NULL) {
-                PrintRawUriFp(fp, (uint8_t *)bstr_ptr(h->value),
-                              bstr_len(h->value));
-                return;
-            }
-        }
-    }
-
-    fprintf(fp, "<unknown>");
-}
-
-static void OutputFilestoreMetaGetSmtp(FILE *fp, const Packet *p, const File *ff)
-{
-    SMTPState *state = (SMTPState *) p->flow->alstate;
-    if (state != NULL) {
-        SMTPTransaction *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_SMTP, state, ff->txid);
-        if (tx == NULL || tx->msg_tail == NULL)
-            return;
-
-        /* Message Id */
-        if (tx->msg_tail->msg_id != NULL) {
-            fprintf(fp, "MESSAGE-ID:        ");
-            PrintRawUriFp(fp, (uint8_t *) tx->msg_tail->msg_id, tx->msg_tail->msg_id_len);
-            fprintf(fp, "\n");
-        }
-
-        /* Sender */
-        MimeDecField *field = MimeDecFindField(tx->msg_tail, "from");
-        if (field != NULL) {
-            fprintf(fp, "SENDER:            ");
-            PrintRawUriFp(fp, (uint8_t *) field->value, field->value_len);
-            fprintf(fp, "\n");
-        }
-    }
+    return SC_ATOMIC_GET(filestore_open_file_cnt);
 }
 
 static uint32_t g_file_store_max_open_files = 0;
@@ -170,136 +76,6 @@ static uint32_t FileGetMaxOpenFiles(void)
     return g_file_store_max_open_files;
 }
 
-static void OutputFilestoreLogCreateMetaFile(const Packet *p, const File *ff,
-        char *base_filename, int ipver) {
-    char metafilename[PATH_MAX] = "";
-    snprintf(metafilename, sizeof(metafilename), "%s.meta", base_filename);
-    SCLogNotice("Opening %s.", metafilename);
-    FILE *fp = fopen(metafilename, "w+");
-    if (fp != NULL) {
-        char timebuf[64];
-
-        CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
-
-        fprintf(fp, "TIME:              %s\n", timebuf);
-        if (p->pcap_cnt > 0) {
-            fprintf(fp, "PCAP PKT NUM:      %"PRIu64"\n", p->pcap_cnt);
-        }
-
-        char srcip[46], dstip[46];
-        Port sp, dp;
-        switch (ipver) {
-            case AF_INET:
-                PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
-                PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
-                break;
-            case AF_INET6:
-                PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
-                PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
-                break;
-            default:
-                strlcpy(srcip, "<unknown>", sizeof(srcip));
-                strlcpy(dstip, "<unknown>", sizeof(dstip));
-                break;
-        }
-        sp = p->sp;
-        dp = p->dp;
-
-        fprintf(fp, "SRC IP:            %s\n", srcip);
-        fprintf(fp, "DST IP:            %s\n", dstip);
-        fprintf(fp, "PROTO:             %" PRIu32 "\n", p->proto);
-        if (PKT_IS_TCP(p) || PKT_IS_UDP(p)) {
-            fprintf(fp, "SRC PORT:          %" PRIu16 "\n", sp);
-            fprintf(fp, "DST PORT:          %" PRIu16 "\n", dp);
-        }
-
-        fprintf(fp, "APP PROTO:         %s\n",
-                AppProtoToString(p->flow->alproto));
-
-        /* Only applicable to HTTP traffic */
-        if (p->flow->alproto == ALPROTO_HTTP) {
-            fprintf(fp, "HTTP URI:          ");
-            OutputFilestoreMetaGetUri(fp, p, ff);
-            fprintf(fp, "\n");
-            fprintf(fp, "HTTP HOST:         ");
-            OutputFilestoreMetaGetHost(fp, p, ff);
-            fprintf(fp, "\n");
-            fprintf(fp, "HTTP REFERER:      ");
-            OutputFilestoreMetaGetReferer(fp, p, ff);
-            fprintf(fp, "\n");
-            fprintf(fp, "HTTP USER AGENT:   ");
-            OutputFilestoreMetaGetUserAgent(fp, p, ff);
-            fprintf(fp, "\n");
-        } else if (p->flow->alproto == ALPROTO_SMTP) {
-            /* Only applicable to SMTP */
-            OutputFilestoreMetaGetSmtp(fp, p, ff);
-        }
-
-        fprintf(fp, "FILENAME:          ");
-        PrintRawUriFp(fp, ff->name, ff->name_len);
-        fprintf(fp, "\n");
-
-        fclose(fp);
-    }
-}
-
-static void OutputFilestoreLogCloseMetaFile(const OutputFilestoreCtx *ctx,
-        const File *ff, const char *filename)
-{
-    FILE *fp = fopen(filename, "a");
-    if (fp == NULL) {
-        SCLogInfo("Failed to open %s: %s", filename, strerror(errno));
-        return;
-    }
-#ifdef HAVE_MAGIC
-    fprintf(fp, "MAGIC:             %s\n",
-            ff->magic ? ff->magic : "<unknown>");
-#endif
-    switch (ff->state) {
-        case FILE_STATE_CLOSED:
-            fprintf(fp, "STATE:             CLOSED\n");
-#ifdef HAVE_NSS
-            if (ff->flags & FILE_MD5) {
-                fprintf(fp, "MD5:               ");
-                size_t x;
-                for (x = 0; x < sizeof(ff->md5); x++) {
-                    fprintf(fp, "%02x", ff->md5[x]);
-                }
-                fprintf(fp, "\n");
-            }
-            if (ff->flags & FILE_SHA1) {
-                fprintf(fp, "SHA1:              ");
-                size_t x;
-                for (x = 0; x < sizeof(ff->sha1); x++) {
-                    fprintf(fp, "%02x", ff->sha1[x]);
-                }
-                fprintf(fp, "\n");
-            }
-            if (ff->flags & FILE_SHA256) {
-                fprintf(fp, "SHA256:            ");
-                size_t x;
-                for (x = 0; x < sizeof(ff->sha256); x++) {
-                    fprintf(fp, "%02x", ff->sha256[x]);
-                }
-                fprintf(fp, "\n");
-            }
-#endif
-            break;
-        case FILE_STATE_TRUNCATED:
-            fprintf(fp, "STATE:             TRUNCATED\n");
-            break;
-        case FILE_STATE_ERROR:
-            fprintf(fp, "STATE:             ERROR\n");
-            break;
-        default:
-            fprintf(fp, "STATE:             UNKNOWN\n");
-            break;
-    }
-    fprintf(fp, "SIZE:              %"PRIu64"\n", FileTrackedSize(ff));
-    
-    fclose(fp);
-}
-
 static void PrintHexString(char *str, size_t size, uint8_t *buf, size_t buf_len)
 {
     int i = 0;
@@ -309,38 +85,79 @@ static void PrintHexString(char *str, size_t size, uint8_t *buf, size_t buf_len)
     }
 }
 
+/**
+ * \brief Update the timestamps on a file to match those of another
+ *     file.
+ *
+ * \param src_filename Filename to use as timestamp source.
+ * \param filename Filename to apply timestamps to.
+ */
+static void OutputFilestoreUpdateFileTime(const char *src_filename,
+        const char *filename)
+{
+    struct stat sb;
+    if (stat(src_filename, &sb) != 0) {
+        SCLogDebug("Failed to stat %s: %s", filename, strerror(errno));
+        return;
+    }
+    struct utimbuf utimbuf = {
+        .actime = sb.st_atime,
+        .modtime = sb.st_mtime,
+    };
+    if (utime(filename, &utimbuf) != 0) {
+        SCLogDebug("Failed to update file timestamps: %s: %s", filename,
+                strerror(errno));
+    }
+}
+
 static void OutputFilestoreFinalizeFiles(const OutputFilestoreCtx *ctx,
-        File *ff) {
-    char final_filename[PATH_MAX] = "";
-    snprintf(final_filename, sizeof(final_filename), "%s/file.%u",
-            ctx->tmpdir, ff->file_store_id);
-    char working_filename[PATH_MAX] = "";
-    snprintf(working_filename, sizeof(working_filename), "%s",
-            final_filename);
+        const Packet *p, File *ff) {
+    /* Stringify the SHA256 which will be used in the final
+     * filename. */
     char sha256string[(SHA256_LENGTH * 2) + 1];
     PrintHexString(sha256string, sizeof(sha256string), ff->sha256,
             sizeof(ff->sha256));
+
+    char tmp_filename[PATH_MAX] = "";
+    snprintf(tmp_filename, sizeof(tmp_filename), "%s/file.%u", ctx->tmpdir,
+            ff->file_store_id);
+
+    char final_filename[PATH_MAX] = "";
     snprintf(final_filename, sizeof(final_filename), "%s/%c%c/%s",
             ctx->prefix, sha256string[0], sha256string[1], sha256string);
-    if (rename(working_filename, final_filename) != 0) {
-        SCLogWarning(SC_WARN_RENAMING_FILE, "renaming file %s to %s failed",
-                working_filename, final_filename);
+
+    if (SCPathExists(final_filename)) {
+        OutputFilestoreUpdateFileTime(tmp_filename, final_filename);
+        if (unlink(tmp_filename) != 0) {
+            SCLogWarning(SC_WARN_REMOVE_FILE,
+                    "Failed to remove temporary file %s: %s", tmp_filename,
+                    strerror(errno));
+        }
+    } else if (rename(tmp_filename, final_filename) != 0) {
+        SCLogWarning(SC_WARN_RENAMING_FILE, "Failed to rename %s to %s: %s",
+                tmp_filename, final_filename, strerror(errno));
+        if (unlink(tmp_filename) != 0) {
+            SCLogWarning(SC_WARN_REMOVE_FILE,
+                    "Failed to remove temporary file %s: %s", tmp_filename,
+                    strerror(errno));
+        }
         return;
     }
 
-    /* Write metadata. */
-    char final_metafilename[PATH_MAX] = "";
-    snprintf(final_metafilename, sizeof(final_metafilename),
-            "%s.meta", final_filename);
-    char working_metafilename[PATH_MAX] = "";
-    snprintf(working_metafilename, sizeof(working_metafilename),
-            "%s.meta", working_filename);
-    OutputFilestoreLogCloseMetaFile(ctx, ff, working_metafilename);
-    if (rename(working_metafilename, final_metafilename) != 0) {
-        SCLogWarning(SC_WARN_RENAMING_FILE,
-                "renaming metafile %s to %s failed", working_metafilename,
-                final_metafilename);
+#ifdef HAVE_LIBJANSSON
+    if (ctx->fileinfo) {
+        char js_metadata_filename[PATH_MAX];
+        snprintf(js_metadata_filename, sizeof(js_metadata_filename),
+                "%s.%"PRIuMAX".%d.json", final_filename, p->ts.tv_sec,
+                ff->file_store_id);
+        json_t *js_fileinfo = JsonBuildFileInfoRecord(p, ff, true);
+        
+        if (likely(js_fileinfo != NULL)) {
+            json_dump_file(js_fileinfo, js_metadata_filename, 0);
+            json_decref(js_fileinfo);
+        }
     }
+#endif
 }
 
 static int OutputFilestoreLogger(ThreadVars *tv, void *thread_data,
@@ -352,18 +169,13 @@ static int OutputFilestoreLogger(ThreadVars *tv, void *thread_data,
     OutputFilestoreCtx *ctx = aft->ctx;
     char filename[PATH_MAX] = "";
     int file_fd = -1;
-    int ipver = -1;
 
-    /* no flow, no htp state */
+    /* no flow, no files */
     if (p->flow == NULL) {
         SCReturnInt(TM_ECODE_OK);
     }
 
-    if (PKT_IS_IPV4(p)) {
-        ipver = AF_INET;
-    } else if (PKT_IS_IPV6(p)) {
-        ipver = AF_INET6;
-    } else {
+    if (!(PKT_IS_IPV4(p) || PKT_IS_IPV6(p))) {
         return 0;
     }
 
@@ -377,12 +189,8 @@ static int OutputFilestoreLogger(ThreadVars *tv, void *thread_data,
     if (flags & OUTPUT_FILEDATA_FLAG_OPEN) {
         aft->file_cnt++;
 
-        /* create a .meta file that contains time, src/dst/sp/dp/proto */
-        OutputFilestoreLogCreateMetaFile(p, ff, base_filename, ipver);
-
         if (SC_ATOMIC_GET(filestore_open_file_cnt) < FileGetMaxOpenFiles()) {
             SC_ATOMIC_ADD(filestore_open_file_cnt, 1);
-            SCLogNotice("Opening %s.", filename);
             ff->fd = open(filename, O_CREAT | O_TRUNC | O_NOFOLLOW | O_WRONLY, 0644);
             if (ff->fd == -1) {
                 SCLogNotice("failed to create file");
@@ -390,7 +198,6 @@ static int OutputFilestoreLogger(ThreadVars *tv, void *thread_data,
             }
             file_fd = ff->fd;
         } else {
-            SCLogNotice("Opening %s.", filename);
             file_fd = open(filename, O_CREAT | O_TRUNC | O_NOFOLLOW | O_WRONLY, 0644);
             if (file_fd == -1) {
                 SCLogNotice("failed to create file");
@@ -403,7 +210,6 @@ static int OutputFilestoreLogger(ThreadVars *tv, void *thread_data,
     /* we can get called with a NULL ffd when we need to close */
     } else if (data != NULL) {
         if (ff->fd == -1) {
-            SCLogNotice("Opening %s.", filename);
             file_fd = open(filename, O_APPEND | O_NOFOLLOW | O_WRONLY);
             if (file_fd == -1) {
                 SCLogNotice("failed to open file %s: %s", filename, strerror(errno));
@@ -434,7 +240,7 @@ static int OutputFilestoreLogger(ThreadVars *tv, void *thread_data,
             ff->fd = -1;
             SC_ATOMIC_SUB(filestore_open_file_cnt, 1);
         }
-        OutputFilestoreFinalizeFiles(ctx, ff);
+        OutputFilestoreFinalizeFiles(ctx, p, ff);
     }
 
     return 0;
@@ -448,8 +254,7 @@ static TmEcode OutputFilestoreLogThreadInit(ThreadVars *t, const void *initdata,
         return TM_ECODE_FAILED;
     memset(aft, 0, sizeof(OutputFilestoreLogThread));
 
-    if (initdata == NULL)
-    {
+    if (initdata == NULL) {
         SCLogDebug("Error getting context for LogFileStore. \"initdata\" argument NULL");
         SCFree(aft);
         return TM_ECODE_FAILED;
@@ -500,7 +305,7 @@ static void GetLogDirectory(const ConfNode *conf, char *out, size_t out_size)
 {
     const char *log_base_dir = ConfNodeLookupChildValue(conf, "dir");
     if (log_base_dir == NULL) {
-        SCLogNotice("Using default log directory %s", default_log_dir);
+        SCLogConfig("Filestore (v2) default log directory %s", default_log_dir);
         log_base_dir = default_log_dir;
     }
     if (PathIsAbsolute(log_base_dir)) {
@@ -516,10 +321,11 @@ static bool InitFilestoreDirectory(const char *dir)
     const uint8_t dir_count = 0xff;
 
     if (!SCPathExists(dir)) {
-        SCLogNotice("Creating directory %s", dir);
+        SCLogInfo("Filestore (v2) creating directory %s", dir);
         if (SCCreateDirectoryTree(dir, true) != 0) {
             SCLogError(SC_ERR_CREATE_DIRECTORY,
-                    "Failed to create directory %s: %s", dir, strerror(errno));
+                    "Filestore (v2) failed to create directory %s: %s", dir,
+                    strerror(errno));
             return false;
         }
     }
@@ -528,11 +334,11 @@ static bool InitFilestoreDirectory(const char *dir)
         char leaf[PATH_MAX];
         snprintf(leaf, sizeof(leaf) - 1, "%s/%02x", dir, i);
         if (!SCPathExists(leaf)) {
-            SCLogNotice("Creating directory %s", leaf);
+            SCLogInfo("Filestore (v2) creating directory %s", leaf);
             if (SCDefaultMkDir(leaf) != 0) {
                 SCLogError(SC_ERR_CREATE_DIRECTORY,
-                        "Failed to create directory %s: %s", leaf,
-                        strerror(errno));
+                        "Filestore (v2) failed to create directory %s: %s",
+                        leaf, strerror(errno));
                 return false;
             }
         }
@@ -542,10 +348,10 @@ static bool InitFilestoreDirectory(const char *dir)
     char tmpdir[PATH_MAX];
     snprintf(tmpdir, sizeof(tmpdir) - 1, "%s/tmp", dir);
     if (!SCPathExists(tmpdir)) {
-        SCLogNotice("Creating directory %s", tmpdir);
+        SCLogInfo("Filestore (v2) creating directory %s", tmpdir);
         if (SCDefaultMkDir(tmpdir) != 0) {
             SCLogError(SC_ERR_CREATE_DIRECTORY,
-                    "Failed to create directory %s: %s", tmpdir,
+                    "Filestore (v2) failed to create directory %s: %s", tmpdir,
                     strerror(errno));
             return false;
         }
@@ -558,36 +364,50 @@ static bool InitFilestoreDirectory(const char *dir)
  *  \param conf Pointer to ConfNode containing this loggers configuration.
  *  \return NULL if failure, OutputFilestoreCtx* to the file_ctx if succesful
  * */
-static OutputCtx *OutputFilestoreLogInitCtx(ConfNode *conf)
+static OutputInitResult OutputFilestoreLogInitCtx(ConfNode *conf)
 {
+    OutputInitResult result = { NULL, false };
+
     intmax_t version = 0;
-    if (!ConfGetChildValueInt(conf, "version", &version)) {
-        return NULL;
-    }
-    if (version < 2) {
-        return NULL;
+    if (!ConfGetChildValueInt(conf, "version", &version) || version < 2) {
+        result.ok = true;
+        return result;
     }
 
     char log_directory[PATH_MAX] = "";
     GetLogDirectory(conf, log_directory, sizeof(log_directory));
     if (!InitFilestoreDirectory(log_directory)) {
-        return NULL;
+        return result;
     }
 
     OutputFilestoreCtx *ctx = SCCalloc(1, sizeof(*ctx));
     if (unlikely(ctx == NULL)) {
-        return NULL;
+        return result;
     }
     strlcpy(ctx->prefix, log_directory, sizeof(ctx->prefix));
     snprintf(ctx->tmpdir, sizeof(ctx->tmpdir) - 1, "%s/tmp", log_directory);
 
     OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
-    if (unlikely(output_ctx == NULL))
-        return NULL;
+    if (unlikely(output_ctx == NULL)) {
+        SCFree(ctx);
+        return result;
+    }
 
     output_ctx->data = ctx;
     output_ctx->DeInit = OutputFilestoreLogDeInitCtx;
 
+    const char *write_fileinfo = ConfNodeLookupChildValue(conf,
+            "write-fileinfo");
+    if (write_fileinfo != NULL && ConfValIsTrue(write_fileinfo)) {
+#ifdef HAVE_LIBJANSSON
+        SCLogConfig("Filestore (v2) will output fileinfo records.");
+        ctx->fileinfo = true;
+#else
+        SCLogWarning(SC_ERR_NO_JSON_SUPPORT,
+                "Filestore (v2) requires JSON support to log fileinfo records");
+#endif
+    }
+
     const char *force_filestore = ConfNodeLookupChildValue(conf,
             "force-filestore");
     if (force_filestore != NULL && ConfValIsTrue(force_filestore)) {
@@ -598,7 +418,7 @@ static OutputCtx *OutputFilestoreLogInitCtx(ConfNode *conf)
     const char *force_magic = ConfNodeLookupChildValue(conf, "force-magic");
     if (force_magic != NULL && ConfValIsTrue(force_magic)) {
         FileForceMagicEnable();
-        SCLogInfo("forcing magic lookup for stored files");
+        SCLogConfig("Filestore (v2) forcing magic lookup for stored files");
     }
 
     FileForceHashParseCfg(conf);
@@ -606,8 +426,6 @@ static OutputCtx *OutputFilestoreLogInitCtx(ConfNode *conf)
     /* The new filestore requires SHA256. */
     FileForceSha256Enable();
 
-    SCLogInfo("storing files in %s", ctx->prefix);
-
     const char *stream_depth_str = ConfNodeLookupChildValue(conf,
             "stream-depth");
     if (stream_depth_str != NULL && strcmp(stream_depth_str, "no")) {
@@ -638,26 +456,23 @@ static OutputCtx *OutputFilestoreLogInitCtx(ConfNode *conf)
         } else {
             if (file_count != 0) {
                 FileSetMaxOpenFiles(file_count);
-                SCLogInfo("file-store will keep a max of %d simultaneously"
-                          " open files", file_count);
+                SCLogConfig("Filestore (v2) will keep a max of %d "
+                        "simultaneously open files", file_count);
             }
         }
     }
 
-    SCReturnPtr(output_ctx, "OutputCtx");
-}
-
-#endif /* HAVE_NSS */
-
-void OutputFilestoreInitConfig(void)
-{
-#ifdef HAVE_NSS
     StatsRegisterGlobalCounter("file_store.open_files",
             OutputFilestoreOpenFilesCounter);
-#endif /* HAVE_NSS */
+
+    result.ctx = output_ctx;
+    result.ok = true;
+    SCReturnCT(result, "OutputInitResult");
 }
 
-void OutputFilestoreRegister (void)
+#endif /* HAVE_NSS */
+
+void OutputFilestoreRegister(void)
 {
 #ifdef HAVE_NSS
     OutputRegisterFiledataModule(LOGGER_FILE_STORE, MODULE_NAME, "file-store",
index 410b1d500d3e773f077bdfae5744161a619d4074..fca913d63fc16457e49bdf20ac29943e4d487fb0 100644 (file)
 #include <pcap/bpf.h>
 #endif
 
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+
 #if __CYGWIN__
 #if !defined _X86_ && !defined __x86_64
 #define _X86_
index 0c8aa90135d40aa0a7b1c0f39c8776dd86ace83c..83a4f0d8c51cc6801edb0ab81c03a5127495d979 100644 (file)
@@ -421,7 +421,54 @@ outputs:
       append: yes
       #filetype: regular # 'regular', 'unix_stream' or 'unix_dgram'
 
-  # output module to store extracted files to disk
+  # Output module for storing files on disk. Files are stored in a
+  # directory names consisting of the first 2 characaters of the
+  # SHA256 of the file. Each file is given its SHA256 as a filename.
+  #
+  # When a duplicate file is found, the existing file is touched to
+  # have its timestamps updated.
+  #
+  # Unlike the older filestore, metadata is not written out by default
+  # as each file should already have a "fileinfo" record in the
+  # eve.log. If write-fileinfo is set to yes, the each file will have
+  # one more associated .json files that consists of the fileinfo
+  # record. A fileinfo file will be written for each occurrence of the
+  # file seen using a filename suffix to ensure uniqueness.
+  #
+  # To prune the filestore directory see the "suricatactl filestore
+  # prune" command which can delete files over a certain age.
+  - file-store:
+      version: 2
+      enabled: no
+
+      # Set the directory for the filestore. If the path is not
+      # absolute will be be relative to the default-log-dir.
+      #dir: filestore
+
+      # Write out a fileinfo record for each occurrence of a
+      # file. Disabled by default as each occurrence is already logged
+      # as a fileinfo record to the main eve-log.
+      #write-fileinfo: yes
+
+      # Force storing of all files. Default: no.
+      #force-filestore: yes
+
+      # Override the global stream-depth for sessions in which we want
+      # to perform file extraction. Set to 0 for unlimited.
+      #stream-depth: 0
+
+      # Uncomment the following variable to define how many files can
+      # remain open for filestore by Suricata. Default value is 0 which
+      # means files get closed after each write
+      #max-open-files: 1000
+
+      # Force logging of checksums, available hash functions are md5,
+      # sha1 and sha256. Note that SHA256 is automatically forced by
+      # the use of this output module as it uses the SHA256 as the
+      # file naming scheme.
+      #force-hash: [sha1, md5]
+
+  # output module to store extracted files to disk (old style, deprecated)
   #
   # The files are stored to the log-dir in a format "file.<id>" where <id> is
   # an incrementing number starting at 1. For each file "file.<id>" a meta
@@ -455,13 +502,6 @@ outputs:
       #max-open-files: 1000
       include-pid: no # set to yes to include pid in file names
 
-  - file-store:
-      version: 2
-      enabled: no
-
-      # Force storing of all files. Default: no.
-      #force-filestore: yes
-
   # output module to log files tracked in a easily parsable json format
   - file-log:
       enabled: no