hashKey.file_id = file_id;
FileContext* file = find(hashKey, timeout);
if (to_create and !file)
- file = add(hashKey, timeout);
+ file = add(hashKey, timeout);
return file;
}
FileVerdict verdict = policy->type_lookup(p, file);
if ( file->get_file_sig_sha256() and
- ((verdict == FILE_VERDICT_UNKNOWN) ||
- (verdict == FILE_VERDICT_STOP_CAPTURE)))
+ ((verdict == FILE_VERDICT_UNKNOWN) or (verdict == FILE_VERDICT_STOP_CAPTURE)))
{
verdict = policy->signature_lookup(p, file);
}
- if ((verdict == FILE_VERDICT_UNKNOWN) ||
- (verdict == FILE_VERDICT_STOP_CAPTURE))
+ if ((verdict == FILE_VERDICT_UNKNOWN) or (verdict == FILE_VERDICT_STOP_CAPTURE))
{
verdict = file->verdict;
}
// can't reset session inside a session
act->set_delayed_action(Active::ACT_RESET, true);
break;
+ case FILE_VERDICT_STOP_CAPTURE:
+ file_ctx->stop_file_capture();
+ return false;
case FILE_VERDICT_PENDING:
packet_gettimeofday(&now);
#define DEFAULT_FILE_CAPTURE_MIN_SIZE 0 // 0
#define DEFAULT_FILE_CAPTURE_BLOCK_SIZE 32768 // 32 KiB
#define DEFAULT_MAX_FILES_CACHED 65536
+#define DEFAULT_MAX_FILES_PER_FLOW 32
#define FILE_ID_NAME "file_id"
#define FILE_ID_HELP "configure file identification"
int64_t capture_block_size = DEFAULT_FILE_CAPTURE_BLOCK_SIZE;
int64_t file_depth = 0;
int64_t max_files_cached = DEFAULT_MAX_FILES_CACHED;
+ uint64_t max_files_per_flow = DEFAULT_MAX_FILES_PER_FLOW;
int64_t show_data_depth = DEFAULT_FILE_SHOW_DATA_DEPTH;
bool trace_type = false;
#include "file_flows.h"
+#include "detection/detection_engine.h"
#include "main/snort_config.h"
#include "managers/inspector_manager.h"
#include "protocols/packet.h"
#include "file_cache.h"
#include "file_config.h"
#include "file_lib.h"
+#include "file_module.h"
#include "file_service.h"
+#include "file_stats.h"
using namespace snort;
FileFlows::~FileFlows()
{
delete(main_context);
+
+ // Delete any remaining FileContexts stored on the flow
+ for (auto const& elem : flow_file_contexts)
+ {
+ delete elem.second;
+ }
}
FileContext* FileFlows::find_main_file_context(FilePosition pos, FileDirection dir, size_t index)
FileContext* FileFlows::get_file_context(uint64_t file_id, bool to_create)
{
- // search for file based on id to support multiple files
- FileCache* file_cache = FileService::get_file_cache();
- assert(file_cache);
+ FileContext *context = nullptr;
+
+ // First check if this file is currently being processed and is stored on the file flows object
+ auto elem = flow_file_contexts.find(file_id);
+ if (elem != flow_file_contexts.end())
+ context = elem->second;
+ // Otherwise check if it has been fully processed and is in the file cache. If the file is not
+ // in the cache, don't add it.
+ else
+ {
+ FileCache* file_cache = FileService::get_file_cache();
+ assert(file_cache);
+ context = file_cache->get_file(flow, file_id, false);
+ }
+
+ // If we haven't found the context, create it and store it on the file flows object
+ if (!context and to_create)
+ {
+ // If we have reached the max file per flow limit, alert and increment the peg count
+ FileConfig* fc = get_file_config(SnortConfig::get_conf());
+ if (flow_file_contexts.size() == fc->max_files_per_flow)
+ {
+ file_counts.files_over_flow_limit_not_processed++;
+ events.create_event(EVENT_FILE_DROPPED_OVER_LIMIT);
+ }
+ else
+ {
+ context = new FileContext;
+ flow_file_contexts[file_id] = context;
+ if (flow_file_contexts.size() > file_counts.max_concurrent_files_per_flow)
+ file_counts.max_concurrent_files_per_flow = flow_file_contexts.size();
+ }
+ }
- FileContext* context = file_cache->get_file(flow, file_id, to_create);
current_file_id = file_id;
return context;
}
+void FileFlows::remove_file_context(uint64_t file_id)
+{
+ auto elem = flow_file_contexts.find(file_id);
+ if (elem == flow_file_contexts.end())
+ return;
+ delete elem->second;
+ flow_file_contexts.erase(file_id);
+}
+
/* This function is used to process file that is sent in pieces
*
* Return:
int data_size, uint64_t offset, FileDirection dir)
{
int64_t file_depth = FileService::get_max_file_depth();
+ bool continue_processing;
- if ((file_depth < 0)or (offset > (uint64_t)file_depth))
+ if ((file_depth < 0) or (offset > (uint64_t)file_depth))
{
return false;
}
context->set_file_id(file_id);
}
- if (context->verdict != FILE_VERDICT_UNKNOWN)
+ if (context->processing_complete and context->verdict != FILE_VERDICT_UNKNOWN)
{
/*A new file session, but policy might be different*/
context->check_policy(flow, dir, file_policy);
- if ((context->get_file_sig_sha256())
- || !context->is_file_signature_enabled())
+ if ((context->get_file_sig_sha256()) || !context->is_file_signature_enabled())
{
/* Just check file type and signature */
FilePosition position = SNORT_FILE_FULL;
- return context->process(p, file_data, data_size, position, file_policy);
+ continue_processing = context->process(p, file_data, data_size, position,
+ file_policy);
+ if (context->processing_complete)
+ remove_file_context(file_id);
+ return continue_processing;
}
}
- return context->process(p, file_data, data_size, offset, file_policy);
+ continue_processing = context->process(p, file_data, data_size, offset, file_policy);
+ if (context->processing_complete)
+ remove_file_context(file_id);
+ return continue_processing;
}
/*
#include "flow/flow.h"
#include "main/snort_types.h"
+#include "utils/event_gen.h"
#include "file_api.h"
#include "file_module.h"
#include "file_policy.h"
+#include <map>
+
+using FileEventGen = EventGen<EVENT__MAX_VALUE, EVENT__NONE, FILE_ID_GID>;
namespace snort
{
size_t size_of() override
{ return sizeof(*this); }
+ void remove_file_context(uint64_t file_id);
+
private:
void init_file_context(FileDirection, FileContext*);
FileContext* find_main_file_context(FilePosition, FileDirection, size_t id = 0);
bool gen_signature = false;
Flow* flow = nullptr;
FilePolicyBase* file_policy = nullptr;
+
+ std::unordered_map<uint64_t, FileContext*> flow_file_contexts;
+ FileEventGen events;
};
}
#endif
config_file_type(false);
config_file_signature(false);
update_file_size(data_size, position);
+ processing_complete = true;
stop_file_capture();
return false;
}
{
file_size = processed_bytes;
processed_bytes = 0;
+ processing_complete = true;
}
}
FileState get_file_state() { return file_state; }
FileVerdict verdict = FILE_VERDICT_UNKNOWN;
+ bool processing_complete = false;
struct timeval pending_expire_time = {0, 0};
protected:
{ "max_files_cached", Parameter::PT_INT, "8:max53", "65536",
"maximal number of files cached in memory" },
+ { "max_files_per_flow", Parameter::PT_INT, "1:max53", "32",
+ "maximal number of files able to be concurrently processed per flow" },
+
{ "enable_type", Parameter::PT_BOOL, nullptr, "true",
"enable type ID" },
{ CountType::SUM, "total_files", "number of files processed" },
{ CountType::SUM, "total_file_data", "number of file data bytes processed" },
{ CountType::SUM, "cache_failures", "number of file cache add failures" },
+ { CountType::SUM, "files_not_processed", "number of files not processed due to per-flow limit" },
+ { CountType::MAX, "max_concurrent_files", "maximum files processed concurrently on a flow" },
{ CountType::END, nullptr, nullptr }
};
PegCount* FileIdModule::get_counts() const
{ return (PegCount*)&file_counts; }
+static const RuleMap file_id_rules[] =
+{
+ { EVENT_FILE_DROPPED_OVER_LIMIT, "file not processed due to per flow limit" },
+ { 0, nullptr }
+};
+
+const RuleMap* FileIdModule::get_rules() const
+{
+ return file_id_rules;
+}
+
void FileIdModule::sum_stats(bool accumulate_now_stats)
{
file_stats_sum();
else if ( v.is("max_files_cached") )
fc->max_files_cached = v.get_int64();
+ else if ( v.is("max_files_per_flow") )
+ fc->max_files_per_flow = v.get_uint64();
+
else if ( v.is("enable_type") )
{
if ( v.get_bool() )
// file_id module
//-------------------------------------------------------------------------
+static const uint32_t FILE_ID_GID = 150;
+
class FileIdModule : public snort::Module
{
public:
void show_dynamic_stats() override;
- // FIXIT-L delete file_id gid when bogus rules are eliminated
- // (this ensures those rules don't fire on every packet)
unsigned get_gid() const override
- { return 146; }
+ { return FILE_ID_GID; }
+
+ const snort::RuleMap* get_rules() const override;
private:
FileMagicRule rule;
FileConfig *fc = nullptr;
};
+enum FileSid
+{
+ EVENT__NONE = -1,
+ EVENT_FILE_DROPPED_OVER_LIMIT = 1,
+ EVENT__MAX_VALUE
+};
+
#endif
else if ((current_offset < context->get_file_size()) && (current_offset < offset))
{
add(file_data, data_size, offset);
+ return 1;
}
return ret;
PegCount files_total;
PegCount file_data_total;
PegCount cache_add_fails;
+ PegCount files_over_flow_limit_not_processed;
+ PegCount max_concurrent_files_per_flow;
PegCount files_buffered_total;
PegCount files_released_total;
PegCount files_freed_total;
DCE2_Smb2ProcessFileData(ssd, nullptr, 0, dir);
}
+ // FIXIT-L Close should probably remove file contexts from FileFlows
}
/********************************************************************