You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/util-file.c

1309 lines
31 KiB
C

/* Copyright (C) 2007-2012 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* 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
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Victor Julien <victor@inliniac.net>
* \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
*
*/
#include "suricata-common.h"
#include "suricata.h"
#include "debug.h"
#include "flow.h"
#include "stream.h"
#include "stream-tcp.h"
#include "runmodes.h"
#include "util-hash.h"
#include "util-debug.h"
#include "util-memcmp.h"
#include "util-print.h"
#include "app-layer-parser.h"
#include "util-validate.h"
/** \brief switch to force filestore on all files
* regardless of the rules.
*/
static int g_file_force_filestore = 0;
/** \brief switch to force magic checks on all files
* regardless of the rules.
*/
static int g_file_force_magic = 0;
/** \brief switch to force md5 calculation on all files
* regardless of the rules.
*/
static int g_file_force_md5 = 0;
/** \brief switch to force sha1 calculation on all files
* regardless of the rules.
*/
static int g_file_force_sha1 = 0;
/** \brief switch to force sha256 calculation on all files
* regardless of the rules.
*/
static int g_file_force_sha256 = 0;
/** \brief switch to force tracking off all files
* regardless of the rules.
*/
static int g_file_force_tracking = 0;
/** \brief switch to use g_file_store_reassembly_depth
* to reassembly files
*/
static int g_file_store_enable = 0;
/** \brief stream_config.reassembly_depth equivalent
* for files
*/
static uint32_t g_file_store_reassembly_depth = 0;
/* prototypes */
static void FileFree(File *);
#ifdef HAVE_NSS
static void FileEndSha256(File *ff);
#endif
void FileForceFilestoreEnable(void)
{
g_file_force_filestore = 1;
}
void FileForceMagicEnable(void)
{
g_file_force_magic = 1;
}
void FileForceMd5Enable(void)
{
g_file_force_md5 = 1;
}
void FileForceSha1Enable(void)
{
g_file_force_sha1 = 1;
}
void FileForceSha256Enable(void)
{
g_file_force_sha256 = 1;
}
int FileForceFilestore(void)
{
return g_file_force_filestore;
}
void FileReassemblyDepthEnable(uint32_t size)
{
g_file_store_enable = 1;
g_file_store_reassembly_depth = size;
}
uint32_t FileReassemblyDepth(void)
{
if (g_file_store_enable == 1)
return g_file_store_reassembly_depth;
else
return stream_config.reassembly_depth;
}
int FileForceMagic(void)
{
return g_file_force_magic;
}
int FileForceMd5(void)
{
return g_file_force_md5;
}
int FileForceSha1(void)
{
return g_file_force_sha1;
}
int FileForceSha256(void)
{
return g_file_force_sha256;
}
void FileForceTrackingEnable(void)
{
g_file_force_tracking = 1;
}
/**
* \brief Function to parse forced file hashing configuration.
*/
void FileForceHashParseCfg(ConfNode *conf)
{
BUG_ON(conf == NULL);
ConfNode *forcehash_node = NULL;
/* legacy option */
const char *force_md5 = ConfNodeLookupChildValue(conf, "force-md5");
if (force_md5 != NULL) {
SCLogWarning(SC_ERR_DEPRECATED_CONF, "deprecated 'force-md5' option "
"found. Please use 'force-hash: [md5]' instead");
if (ConfValIsTrue(force_md5)) {
#ifdef HAVE_NSS
FileForceMd5Enable();
SCLogInfo("forcing md5 calculation for logged files");
#else
SCLogInfo("md5 calculation requires linking against libnss");
#endif
}
}
if (conf != NULL)
forcehash_node = ConfNodeLookupChild(conf, "force-hash");
if (forcehash_node != NULL) {
ConfNode *field = NULL;
TAILQ_FOREACH(field, &forcehash_node->head, next) {
if (strcasecmp("md5", field->val) == 0) {
#ifdef HAVE_NSS
FileForceMd5Enable();
SCLogConfig("forcing md5 calculation for logged or stored files");
#else
SCLogInfo("md5 calculation requires linking against libnss");
#endif
}
if (strcasecmp("sha1", field->val) == 0) {
#ifdef HAVE_NSS
FileForceSha1Enable();
SCLogConfig("forcing sha1 calculation for logged or stored files");
#else
SCLogInfo("sha1 calculation requires linking against libnss");
#endif
}
if (strcasecmp("sha256", field->val) == 0) {
#ifdef HAVE_NSS
FileForceSha256Enable();
SCLogConfig("forcing sha256 calculation for logged or stored files");
#else
SCLogInfo("sha256 calculation requires linking against libnss");
#endif
}
}
}
}
uint16_t FileFlowToFlags(const Flow *flow, uint8_t direction)
{
uint16_t flags = 0;
if (direction == STREAM_TOSERVER) {
if (flow->file_flags & FLOWFILE_NO_STORE_TS) {
flags |= FILE_NOSTORE;
}
if (flow->file_flags & FLOWFILE_NO_MAGIC_TS) {
flags |= FILE_NOMAGIC;
}
if (flow->file_flags & FLOWFILE_NO_MD5_TS) {
flags |= FILE_NOMD5;
}
if (flow->file_flags & FLOWFILE_NO_SHA1_TS) {
flags |= FILE_NOSHA1;
}
if (flow->file_flags & FLOWFILE_NO_SHA256_TS) {
flags |= FILE_NOSHA256;
}
} else {
if (flow->file_flags & FLOWFILE_NO_STORE_TC) {
flags |= FILE_NOSTORE;
}
if (flow->file_flags & FLOWFILE_NO_MAGIC_TC) {
flags |= FILE_NOMAGIC;
}
if (flow->file_flags & FLOWFILE_NO_MD5_TC) {
flags |= FILE_NOMD5;
}
if (flow->file_flags & FLOWFILE_NO_SHA1_TC) {
flags |= FILE_NOSHA1;
}
if (flow->file_flags & FLOWFILE_NO_SHA256_TC) {
flags |= FILE_NOSHA256;
}
}
return flags;
}
static int FileMagicSize(void)
{
/** \todo make this size configurable */
return 512;
}
/**
* \brief get the size of the file data
*
* This doesn't reflect how much of the file we have in memory, just the
* total size of filedata so far.
*/
uint64_t FileDataSize(const File *file)
{
if (file != NULL && file->sb != NULL) {
SCLogDebug("returning %"PRIu64,
file->sb->stream_offset + file->sb->buf_offset);
return file->sb->stream_offset + file->sb->buf_offset;
}
SCLogDebug("returning 0 (default)");
return 0;
}
/**
* \brief get the size of the file
*
* This doesn't reflect how much of the file we have in memory, just the
* total size of file so far.
*/
uint64_t FileTrackedSize(const File *file)
{
if (file != NULL) {
return file->size;
}
return 0;
}
static int FilePruneFile(File *file)
{
SCEnter();
#ifdef HAVE_MAGIC
if (!(file->flags & FILE_NOMAGIC)) {
/* need magic but haven't set it yet, bail out */
if (file->magic == NULL)
SCReturnInt(0);
else
SCLogDebug("file->magic %s", file->magic);
} else {
SCLogDebug("file->flags & FILE_NOMAGIC == true");
}
#endif
uint64_t left_edge = file->content_stored;
if (file->flags & FILE_NOSTORE) {
left_edge = FileDataSize(file);
}
if (file->flags & FILE_USE_DETECT) {
left_edge = MIN(left_edge, file->content_inspected);
}
if (left_edge) {
StreamingBufferSlideToOffset(file->sb, left_edge);
}
if (left_edge != FileDataSize(file)) {
SCReturnInt(0);
}
SCLogDebug("file->state %d. Is >= FILE_STATE_CLOSED: %s", file->state, (file->state >= FILE_STATE_CLOSED) ? "yes" : "no");
/* file is done when state is closed+, logging/storing is done (if any) */
if (file->state >= FILE_STATE_CLOSED &&
(!RunModeOutputFileEnabled() || (file->flags & FILE_LOGGED)) &&
(!RunModeOutputFiledataEnabled() || (file->flags & FILE_STORED)))
{
SCReturnInt(1);
} else {
SCReturnInt(0);
}
}
void FilePrune(FileContainer *ffc)
{
File *file = ffc->head;
File *prev = NULL;
while (file) {
if (FilePruneFile(file) == 0) {
prev = file;
file = file->next;
continue;
}
SCLogDebug("removing file %p", file);
File *file_next = file->next;
if (prev)
prev->next = file_next;
/* update head and tail */
if (file == ffc->head)
ffc->head = file_next;
if (file == ffc->tail)
ffc->tail = prev;
FileFree(file);
file = file_next;
}
}
/**
* \brief allocate a FileContainer
*
* \retval new newly allocated FileContainer
* \retval NULL error
*/
FileContainer *FileContainerAlloc(void)
{
FileContainer *new = SCMalloc(sizeof(FileContainer));
if (unlikely(new == NULL)) {
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating mem");
return NULL;
}
memset(new, 0, sizeof(FileContainer));
new->head = new->tail = NULL;
return new;
}
/**
* \brief Recycle a FileContainer
*
* \param ffc FileContainer
*/
void FileContainerRecycle(FileContainer *ffc)
{
if (ffc == NULL)
return;
File *cur = ffc->head;
File *next = NULL;
for (;cur != NULL; cur = next) {
next = cur->next;
FileFree(cur);
}
ffc->head = ffc->tail = NULL;
}
/**
* \brief Free a FileContainer
*
* \param ffc FileContainer
*/
void FileContainerFree(FileContainer *ffc)
{
if (ffc == NULL)
return;
File *ptr = ffc->head;
File *next = NULL;
for (;ptr != NULL; ptr = next) {
next = ptr->next;
FileFree(ptr);
}
ffc->head = ffc->tail = NULL;
SCFree(ffc);
}
/**
* \brief Alloc a new File
*
* \param name character array containing the name (not a string)
* \param name_len length in bytes of the name
*
* \retval new File object or NULL on error
*/
static File *FileAlloc(const uint8_t *name, uint16_t name_len)
{
File *new = SCMalloc(sizeof(File));
if (unlikely(new == NULL)) {
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating mem");
return NULL;
}
memset(new, 0, sizeof(File));
new->name = SCMalloc(name_len);
if (new->name == NULL) {
SCFree(new);
return NULL;
}
new->name_len = name_len;
memcpy(new->name, name, name_len);
return new;
}
static void FileFree(File *ff)
{
if (ff == NULL)
return;
if (ff->name != NULL)
SCFree(ff->name);
#ifdef HAVE_MAGIC
/* magic returned by libmagic is strdup'd by MagicLookup. */
if (ff->magic != NULL)
SCFree(ff->magic);
#endif
if (ff->sb != NULL) {
StreamingBufferFree(ff->sb);
}
#ifdef HAVE_NSS
if (ff->md5_ctx)
HASH_Destroy(ff->md5_ctx);
if (ff->sha1_ctx)
HASH_Destroy(ff->sha1_ctx);
if (ff->sha256_ctx)
HASH_Destroy(ff->sha256_ctx);
#endif
SCFree(ff);
}
void FileContainerAdd(FileContainer *ffc, File *ff)
{
if (ffc->head == NULL || ffc->tail == NULL) {
ffc->head = ffc->tail = ff;
} else {
ffc->tail->next = ff;
ffc->tail = ff;
}
}
/**
* \brief Tag a file for storing
*
* \param ff The file to store
*/
int FileStore(File *ff)
{
ff->flags |= FILE_STORE;
SCReturnInt(0);
}
/**
* \brief Set the TX id for a file
*
* \param ff The file to store
* \param txid the tx id
*/
int FileSetTx(File *ff, uint64_t txid)
{
SCLogDebug("ff %p txid %"PRIu64, ff, txid);
if (ff != NULL)
ff->txid = txid;
SCReturnInt(0);
}
void FileContainerSetTx(FileContainer *ffc, uint64_t tx_id)
{
if (ffc && ffc->tail) {
(void)FileSetTx(ffc->tail, tx_id);
}
}
/**
* \brief check if we have stored enough
*
* \param ff file
*
* \retval 0 limit not reached yet
* \retval 1 limit reached
*/
static int FileStoreNoStoreCheck(File *ff)
{
SCEnter();
if (ff == NULL) {
SCReturnInt(0);
}
if (ff->flags & FILE_NOSTORE) {
if (ff->state == FILE_STATE_OPENED &&
FileDataSize(ff) >= (uint64_t)FileMagicSize())
{
SCReturnInt(1);
}
}
SCReturnInt(0);
}
static int AppendData(File *file, const uint8_t *data, uint32_t data_len)
{
if (StreamingBufferAppendNoTrack(file->sb, data, data_len) != 0) {
SCReturnInt(-1);
}
#ifdef HAVE_NSS
if (file->md5_ctx) {
HASH_Update(file->md5_ctx, data, data_len);
}
if (file->sha1_ctx) {
HASH_Update(file->sha1_ctx, data, data_len);
}
if (file->sha256_ctx) {
HASH_Update(file->sha256_ctx, data, data_len);
}
#endif
SCReturnInt(0);
}
/** \internal
* \brief Store/handle a chunk of file data in the File structure
*
* \param ff the file
* \param data data chunk
* \param data_len data chunk len
*
* \retval 0 ok
* \retval -1 error
* \retval -2 no store for this file
*/
static int FileAppendDataDo(File *ff, const uint8_t *data, uint32_t data_len)
{
SCEnter();
#ifdef DEBUG_VALIDATION
BUG_ON(ff == NULL);
#endif
ff->size += data_len;
if (ff->state != FILE_STATE_OPENED) {
if (ff->flags & FILE_NOSTORE) {
SCReturnInt(-2);
}
SCReturnInt(-1);
}
if (FileStoreNoStoreCheck(ff) == 1) {
#ifdef HAVE_NSS
int hash_done = 0;
/* no storage but forced hashing */
if (ff->md5_ctx) {
HASH_Update(ff->md5_ctx, data, data_len);
hash_done = 1;
}
if (ff->sha1_ctx) {
HASH_Update(ff->sha1_ctx, data, data_len);
hash_done = 1;
}
if (ff->sha256_ctx) {
HASH_Update(ff->sha256_ctx, data, data_len);
hash_done = 1;
}
if (hash_done)
SCReturnInt(0);
#endif
if (g_file_force_tracking || (!(ff->flags & FILE_NOTRACK)))
SCReturnInt(0);
ff->state = FILE_STATE_TRUNCATED;
SCLogDebug("flowfile state transitioned to FILE_STATE_TRUNCATED");
SCReturnInt(-2);
}
SCLogDebug("appending %"PRIu32" bytes", data_len);
if (AppendData(ff, data, data_len) != 0) {
ff->state = FILE_STATE_ERROR;
SCReturnInt(-1);
}
SCReturnInt(0);
}
/**
* \brief Store/handle a chunk of file data in the File structure
* The last file in the FileContainer will be used.
*
* \param ffc FileContainer used to append to
* \param data data chunk
* \param data_len data chunk len
*
* \retval 0 ok
* \retval -1 error
* \retval -2 no store for this file
*/
int FileAppendData(FileContainer *ffc, const uint8_t *data, uint32_t data_len)
{
SCEnter();
if (ffc == NULL || ffc->tail == NULL || data == NULL || data_len == 0) {
SCReturnInt(-1);
}
int r = FileAppendDataDo(ffc->tail, data, data_len);
SCReturnInt(r);
}
/**
* \brief Store/handle a chunk of file data in the File structure
* The file with 'track_id' in the FileContainer will be used.
*
* \param ffc FileContainer used to append to
* \param track_id id to lookup the file
* \param data data chunk
* \param data_len data chunk len
*
* \retval 0 ok
* \retval -1 error
* \retval -2 no store for this file
*/
int FileAppendDataById(FileContainer *ffc, uint32_t track_id,
const uint8_t *data, uint32_t data_len)
{
SCEnter();
if (ffc == NULL || ffc->tail == NULL || data == NULL || data_len == 0) {
SCReturnInt(-1);
}
File *ff = ffc->head;
for ( ; ff != NULL; ff = ff->next) {
if (track_id == ff->file_track_id) {
int r = FileAppendDataDo(ff, data, data_len);
SCReturnInt(r);
}
}
SCReturnInt(-1);
}
/**
* \brief Store/handle a chunk of file data in the File structure
* The file with 'track_id' in the FileContainer will be used.
*
* \param ffc FileContainer used to append to
* \param track_id id to lookup the file
* \param data data chunk
* \param data_len data chunk len
*
* \retval 0 ok
* \retval -1 error
* \retval -2 no store for this file
*/
int FileAppendGAPById(FileContainer *ffc, uint32_t track_id,
const uint8_t *data, uint32_t data_len)
{
SCEnter();
if (ffc == NULL || ffc->tail == NULL || data == NULL || data_len == 0) {
SCReturnInt(-1);
}
File *ff = ffc->head;
for ( ; ff != NULL; ff = ff->next) {
if (track_id == ff->file_track_id) {
ff->flags |= FILE_HAS_GAPS;
ff->flags |= (FILE_NOMD5|FILE_NOSHA1|FILE_NOSHA256);
ff->flags &= ~(FILE_MD5|FILE_SHA1|FILE_SHA256);
SCLogDebug("FILE_HAS_GAPS set");
int r = FileAppendDataDo(ff, data, data_len);
SCReturnInt(r);
}
}
SCReturnInt(-1);
}
/**
* \brief Open a new File
*
* \param ffc flow container
* \param sbcfg buffer config
* \param name filename character array
* \param name_len filename len
* \param data initial data
* \param data_len initial data len
* \param flags open flags
*
* \retval ff flowfile object
*
* \note filename is not a string, so it's not nul terminated.
*/
File *FileOpenFile(FileContainer *ffc, const StreamingBufferConfig *sbcfg,
const uint8_t *name, uint16_t name_len,
const uint8_t *data, uint32_t data_len, uint16_t flags)
{
SCEnter();
//PrintRawDataFp(stdout, name, name_len);
File *ff = FileAlloc(name, name_len);
if (ff == NULL) {
SCReturnPtr(NULL, "File");
}
ff->sb = StreamingBufferInit(sbcfg);
if (ff->sb == NULL) {
FileFree(ff);
SCReturnPtr(NULL, "File");
}
SCLogDebug("ff->sb %p", ff->sb);
if (flags & FILE_STORE || g_file_force_filestore) {
FileStore(ff);
} else if (flags & FILE_NOSTORE) {
SCLogDebug("not storing this file");
ff->flags |= FILE_NOSTORE;
}
if (flags & FILE_NOMAGIC) {
SCLogDebug("not doing magic for this file");
ff->flags |= FILE_NOMAGIC;
}
if (flags & FILE_NOMD5) {
SCLogDebug("not doing md5 for this file");
ff->flags |= FILE_NOMD5;
}
if (flags & FILE_NOSHA1) {
SCLogDebug("not doing sha1 for this file");
ff->flags |= FILE_NOSHA1;
}
if (flags & FILE_NOSHA256) {
SCLogDebug("not doing sha256 for this file");
ff->flags |= FILE_NOSHA256;
}
if (flags & FILE_USE_DETECT) {
SCLogDebug("considering content_inspect tracker when pruning");
ff->flags |= FILE_USE_DETECT;
}
#ifdef HAVE_NSS
if (!(ff->flags & FILE_NOMD5) || g_file_force_md5) {
ff->md5_ctx = HASH_Create(HASH_AlgMD5);
if (ff->md5_ctx != NULL) {
HASH_Begin(ff->md5_ctx);
}
}
if (!(ff->flags & FILE_NOSHA1) || g_file_force_sha1) {
ff->sha1_ctx = HASH_Create(HASH_AlgSHA1);
if (ff->sha1_ctx != NULL) {
HASH_Begin(ff->sha1_ctx);
}
}
if (!(ff->flags & FILE_NOSHA256) || g_file_force_sha256) {
ff->sha256_ctx = HASH_Create(HASH_AlgSHA256);
if (ff->sha256_ctx != NULL) {
HASH_Begin(ff->sha256_ctx);
}
}
#endif
ff->state = FILE_STATE_OPENED;
SCLogDebug("flowfile state transitioned to FILE_STATE_OPENED");
ff->fd = -1;
FileContainerAdd(ffc, ff);
if (data != NULL) {
ff->size += data_len;
if (AppendData(ff, data, data_len) != 0) {
ff->state = FILE_STATE_ERROR;
SCReturnPtr(NULL, "File");
}
SCLogDebug("file size is now %"PRIu64, FileTrackedSize(ff));
}
SCReturnPtr(ff, "File");
}
File *FileOpenFileWithId(FileContainer *ffc, const StreamingBufferConfig *sbcfg,
uint32_t track_id, const uint8_t *name, uint16_t name_len,
const uint8_t *data, uint32_t data_len, uint16_t flags)
{
File *ff = FileOpenFile(ffc, sbcfg, name, name_len, data, data_len, flags);
if (ff == NULL)
return NULL;
ff->file_track_id = track_id;
ff->flags |= FILE_USE_TRACKID;
return ff;
}
static int FileCloseFilePtr(File *ff, const uint8_t *data,
uint32_t data_len, uint16_t flags)
{
SCEnter();
if (ff == NULL) {
SCReturnInt(-1);
}
if (ff->state != FILE_STATE_OPENED) {
SCReturnInt(-1);
}
if (data != NULL) {
ff->size += data_len;
if (ff->flags & FILE_NOSTORE) {
#ifdef HAVE_NSS
/* no storage but hashing */
if (ff->md5_ctx)
HASH_Update(ff->md5_ctx, data, data_len);
if (ff->sha1_ctx)
HASH_Update(ff->sha1_ctx, data, data_len);
if (ff->sha256_ctx)
HASH_Update(ff->sha256_ctx, data, data_len);
#endif
} else {
if (AppendData(ff, data, data_len) != 0) {
ff->state = FILE_STATE_ERROR;
SCReturnInt(-1);
}
}
}
if ((flags & FILE_TRUNCATED) || (ff->flags & FILE_HAS_GAPS)) {
ff->state = FILE_STATE_TRUNCATED;
SCLogDebug("flowfile state transitioned to FILE_STATE_TRUNCATED");
if (flags & FILE_NOSTORE) {
SCLogDebug("not storing this file");
ff->flags |= FILE_NOSTORE;
} else {
#ifdef HAVE_NSS
if (g_file_force_sha256 && ff->sha256_ctx) {
FileEndSha256(ff);
}
#endif
}
} else {
ff->state = FILE_STATE_CLOSED;
SCLogDebug("flowfile state transitioned to FILE_STATE_CLOSED");
#ifdef HAVE_NSS
if (ff->md5_ctx) {
unsigned int len = 0;
HASH_End(ff->md5_ctx, ff->md5, &len, sizeof(ff->md5));
ff->flags |= FILE_MD5;
}
if (ff->sha1_ctx) {
unsigned int len = 0;
HASH_End(ff->sha1_ctx, ff->sha1, &len, sizeof(ff->sha1));
ff->flags |= FILE_SHA1;
}
if (ff->sha256_ctx) {
FileEndSha256(ff);
}
#endif
}
SCReturnInt(0);
}
/**
* \brief Close a File
*
* \param ffc the container
* \param data final data if any
* \param data_len data len if any
* \param flags flags
*
* \retval 0 ok
* \retval -1 error
*/
int FileCloseFile(FileContainer *ffc, const uint8_t *data,
uint32_t data_len, uint16_t flags)
{
SCEnter();
if (ffc == NULL || ffc->tail == NULL) {
SCReturnInt(-1);
}
if (FileCloseFilePtr(ffc->tail, data, data_len, flags) == -1) {
SCReturnInt(-1);
}
SCReturnInt(0);
}
int FileCloseFileById(FileContainer *ffc, uint32_t track_id,
const uint8_t *data, uint32_t data_len, uint16_t flags)
{
SCEnter();
if (ffc == NULL || ffc->tail == NULL) {
SCReturnInt(-1);
}
File *ff = ffc->head;
for ( ; ff != NULL; ff = ff->next) {
if (track_id == ff->file_track_id) {
if (FileCloseFilePtr(ff, data, data_len, flags) == -1) {
SCReturnInt(-1);
}
SCReturnInt(0);
}
}
SCReturnInt(-1);
}
/**
* \brief disable file storage for a flow
*
* \param f *LOCKED* flow
* \param direction flow direction
*/
void FileDisableStoring(Flow *f, uint8_t direction)
{
File *ptr = NULL;
SCEnter();
DEBUG_ASSERT_FLOW_LOCKED(f);
if (direction == STREAM_TOSERVER)
f->file_flags |= FLOWFILE_NO_STORE_TS;
else
f->file_flags |= FLOWFILE_NO_STORE_TC;
App layer API rewritten. The main files in question are: app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch]. Things addressed in this commit: - Brings out a proper separation between protocol detection phase and the parser phase. - The dns app layer now is registered such that we don't use "dnstcp" and "dnsudp" in the rules. A user who previously wrote a rule like this - "alert dnstcp....." or "alert dnsudp....." would now have to use, alert dns (ipproto:tcp;) or alert udp (app-layer-protocol:dns;) or alert ip (ipproto:udp; app-layer-protocol:dns;) The same rules extend to other another such protocol, dcerpc. - The app layer parser api now takes in the ipproto while registering callbacks. - The app inspection/detection engine also takes an ipproto. - All app layer parser functions now take direction as STREAM_TOSERVER or STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the functions. - FlowInitialize() and FlowRecycle() now resets proto to 0. This is needed by unittests, which would try to clean the flow, and that would call the api, AppLayerParserCleanupParserState(), which would try to clean the app state, but the app layer now needs an ipproto to figure out which api to internally call to clean the state, and if the ipproto is 0, it would return without trying to clean the state. - A lot of unittests are now updated where if they are using a flow and they need to use the app layer, we would set a flow ipproto. - The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
if (ffc != NULL) {
for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
/* if we're already storing, we'll continue */
if (!(ptr->flags & FILE_STORE)) {
SCLogDebug("not storing this file");
ptr->flags |= FILE_NOSTORE;
}
}
}
SCReturn;
}
/**
* \brief disable file magic lookups for this flow
*
* \param f *LOCKED* flow
* \param direction flow direction
*/
void FileDisableMagic(Flow *f, uint8_t direction)
{
File *ptr = NULL;
SCEnter();
DEBUG_ASSERT_FLOW_LOCKED(f);
if (direction == STREAM_TOSERVER)
f->file_flags |= FLOWFILE_NO_MAGIC_TS;
else
f->file_flags |= FLOWFILE_NO_MAGIC_TC;
App layer API rewritten. The main files in question are: app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch]. Things addressed in this commit: - Brings out a proper separation between protocol detection phase and the parser phase. - The dns app layer now is registered such that we don't use "dnstcp" and "dnsudp" in the rules. A user who previously wrote a rule like this - "alert dnstcp....." or "alert dnsudp....." would now have to use, alert dns (ipproto:tcp;) or alert udp (app-layer-protocol:dns;) or alert ip (ipproto:udp; app-layer-protocol:dns;) The same rules extend to other another such protocol, dcerpc. - The app layer parser api now takes in the ipproto while registering callbacks. - The app inspection/detection engine also takes an ipproto. - All app layer parser functions now take direction as STREAM_TOSERVER or STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the functions. - FlowInitialize() and FlowRecycle() now resets proto to 0. This is needed by unittests, which would try to clean the flow, and that would call the api, AppLayerParserCleanupParserState(), which would try to clean the app state, but the app layer now needs an ipproto to figure out which api to internally call to clean the state, and if the ipproto is 0, it would return without trying to clean the state. - A lot of unittests are now updated where if they are using a flow and they need to use the app layer, we would set a flow ipproto. - The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
if (ffc != NULL) {
for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
SCLogDebug("disabling magic for file %p from direction %s",
ptr, direction == STREAM_TOSERVER ? "toserver":"toclient");
ptr->flags |= FILE_NOMAGIC;
}
}
SCReturn;
}
/**
* \brief disable file md5 calc for this flow
*
* \param f *LOCKED* flow
* \param direction flow direction
*/
void FileDisableMd5(Flow *f, uint8_t direction)
{
File *ptr = NULL;
SCEnter();
DEBUG_ASSERT_FLOW_LOCKED(f);
if (direction == STREAM_TOSERVER)
f->file_flags |= FLOWFILE_NO_MD5_TS;
else
f->file_flags |= FLOWFILE_NO_MD5_TC;
App layer API rewritten. The main files in question are: app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch]. Things addressed in this commit: - Brings out a proper separation between protocol detection phase and the parser phase. - The dns app layer now is registered such that we don't use "dnstcp" and "dnsudp" in the rules. A user who previously wrote a rule like this - "alert dnstcp....." or "alert dnsudp....." would now have to use, alert dns (ipproto:tcp;) or alert udp (app-layer-protocol:dns;) or alert ip (ipproto:udp; app-layer-protocol:dns;) The same rules extend to other another such protocol, dcerpc. - The app layer parser api now takes in the ipproto while registering callbacks. - The app inspection/detection engine also takes an ipproto. - All app layer parser functions now take direction as STREAM_TOSERVER or STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the functions. - FlowInitialize() and FlowRecycle() now resets proto to 0. This is needed by unittests, which would try to clean the flow, and that would call the api, AppLayerParserCleanupParserState(), which would try to clean the app state, but the app layer now needs an ipproto to figure out which api to internally call to clean the state, and if the ipproto is 0, it would return without trying to clean the state. - A lot of unittests are now updated where if they are using a flow and they need to use the app layer, we would set a flow ipproto. - The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
if (ffc != NULL) {
for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
SCLogDebug("disabling md5 for file %p from direction %s",
ptr, direction == STREAM_TOSERVER ? "toserver":"toclient");
ptr->flags |= FILE_NOMD5;
#ifdef HAVE_NSS
/* destroy any ctx we may have so far */
if (ptr->md5_ctx != NULL) {
HASH_Destroy(ptr->md5_ctx);
ptr->md5_ctx = NULL;
}
#endif
}
}
SCReturn;
}
/**
* \brief disable file sha1 calc for this flow
*
* \param f *LOCKED* flow
* \param direction flow direction
*/
void FileDisableSha1(Flow *f, uint8_t direction)
{
File *ptr = NULL;
SCEnter();
DEBUG_ASSERT_FLOW_LOCKED(f);
if (direction == STREAM_TOSERVER)
f->file_flags |= FLOWFILE_NO_SHA1_TS;
else
f->file_flags |= FLOWFILE_NO_SHA1_TC;
FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
if (ffc != NULL) {
for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
SCLogDebug("disabling sha1 for file %p from direction %s",
ptr, direction == STREAM_TOSERVER ? "toserver":"toclient");
ptr->flags |= FILE_NOSHA1;
#ifdef HAVE_NSS
/* destroy any ctx we may have so far */
if (ptr->sha1_ctx != NULL) {
HASH_Destroy(ptr->sha1_ctx);
ptr->sha1_ctx = NULL;
}
#endif
}
}
SCReturn;
}
/**
* \brief disable file sha256 calc for this flow
*
* \param f *LOCKED* flow
* \param direction flow direction
*/
void FileDisableSha256(Flow *f, uint8_t direction)
{
File *ptr = NULL;
SCEnter();
DEBUG_ASSERT_FLOW_LOCKED(f);
if (direction == STREAM_TOSERVER)
f->file_flags |= FLOWFILE_NO_SHA256_TS;
else
f->file_flags |= FLOWFILE_NO_SHA256_TC;
FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
if (ffc != NULL) {
for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
SCLogDebug("disabling sha256 for file %p from direction %s",
ptr, direction == STREAM_TOSERVER ? "toserver":"toclient");
ptr->flags |= FILE_NOSHA256;
#ifdef HAVE_NSS
/* destroy any ctx we may have so far */
if (ptr->sha256_ctx != NULL) {
HASH_Destroy(ptr->sha256_ctx);
ptr->sha256_ctx = NULL;
}
#endif
}
}
SCReturn;
}
/**
* \brief disable file size tracking for this flow
*
* \param f *LOCKED* flow
* \param direction flow direction
*/
void FileDisableFilesize(Flow *f, uint8_t direction)
{
File *ptr = NULL;
SCEnter();
DEBUG_ASSERT_FLOW_LOCKED(f);
if (direction == STREAM_TOSERVER)
f->file_flags |= FLOWFILE_NO_SIZE_TS;
else
f->file_flags |= FLOWFILE_NO_SIZE_TC;
App layer API rewritten. The main files in question are: app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch]. Things addressed in this commit: - Brings out a proper separation between protocol detection phase and the parser phase. - The dns app layer now is registered such that we don't use "dnstcp" and "dnsudp" in the rules. A user who previously wrote a rule like this - "alert dnstcp....." or "alert dnsudp....." would now have to use, alert dns (ipproto:tcp;) or alert udp (app-layer-protocol:dns;) or alert ip (ipproto:udp; app-layer-protocol:dns;) The same rules extend to other another such protocol, dcerpc. - The app layer parser api now takes in the ipproto while registering callbacks. - The app inspection/detection engine also takes an ipproto. - All app layer parser functions now take direction as STREAM_TOSERVER or STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the functions. - FlowInitialize() and FlowRecycle() now resets proto to 0. This is needed by unittests, which would try to clean the flow, and that would call the api, AppLayerParserCleanupParserState(), which would try to clean the app state, but the app layer now needs an ipproto to figure out which api to internally call to clean the state, and if the ipproto is 0, it would return without trying to clean the state. - A lot of unittests are now updated where if they are using a flow and they need to use the app layer, we would set a flow ipproto. - The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
if (ffc != NULL) {
for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
SCLogDebug("disabling size tracking for file %p from direction %s",
ptr, direction == STREAM_TOSERVER ? "toserver":"toclient");
ptr->flags |= FILE_NOTRACK;
}
}
SCReturn;
}
/**
* \brief set no store flag, close file if needed
*
* \param ff file
*/
static void FileDisableStoringForFile(File *ff)
{
SCEnter();
if (ff == NULL) {
SCReturn;
}
SCLogDebug("not storing this file");
ff->flags |= FILE_NOSTORE;
if (ff->state == FILE_STATE_OPENED && FileDataSize(ff) >= (uint64_t)FileMagicSize()) {
if (g_file_force_md5 == 0 && g_file_force_sha1 == 0 && g_file_force_sha256 == 0
&& g_file_force_tracking == 0) {
(void)FileCloseFilePtr(ff, NULL, 0,
(FILE_TRUNCATED|FILE_NOSTORE));
}
}
}
/**
* \brief disable file storing for files in a transaction
*
* \param f *LOCKED* flow
* \param direction flow direction
* \param tx_id transaction id
*/
void FileDisableStoringForTransaction(Flow *f, uint8_t direction, uint64_t tx_id)
{
File *ptr = NULL;
DEBUG_ASSERT_FLOW_LOCKED(f);
SCEnter();
App layer API rewritten. The main files in question are: app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch]. Things addressed in this commit: - Brings out a proper separation between protocol detection phase and the parser phase. - The dns app layer now is registered such that we don't use "dnstcp" and "dnsudp" in the rules. A user who previously wrote a rule like this - "alert dnstcp....." or "alert dnsudp....." would now have to use, alert dns (ipproto:tcp;) or alert udp (app-layer-protocol:dns;) or alert ip (ipproto:udp; app-layer-protocol:dns;) The same rules extend to other another such protocol, dcerpc. - The app layer parser api now takes in the ipproto while registering callbacks. - The app inspection/detection engine also takes an ipproto. - All app layer parser functions now take direction as STREAM_TOSERVER or STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the functions. - FlowInitialize() and FlowRecycle() now resets proto to 0. This is needed by unittests, which would try to clean the flow, and that would call the api, AppLayerParserCleanupParserState(), which would try to clean the app state, but the app layer now needs an ipproto to figure out which api to internally call to clean the state, and if the ipproto is 0, it would return without trying to clean the state. - A lot of unittests are now updated where if they are using a flow and they need to use the app layer, we would set a flow ipproto. - The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
FileContainer *ffc = AppLayerParserGetFiles(f->proto, f->alproto, f->alstate, direction);
if (ffc != NULL) {
for (ptr = ffc->head; ptr != NULL; ptr = ptr->next) {
if (ptr->txid == tx_id) {
if (ptr->flags & FILE_STORE) {
/* weird, already storing -- let it continue*/
SCLogDebug("file is already being stored");
} else {
FileDisableStoringForFile(ptr);
}
}
}
}
SCReturn;
}
/**
* \brief flag a file with id "file_id" to be stored.
*
* \param fc file store
* \param file_id the file's id
*/
void FileStoreFileById(FileContainer *fc, uint32_t file_id)
{
File *ptr = NULL;
SCEnter();
if (fc != NULL) {
for (ptr = fc->head; ptr != NULL; ptr = ptr->next) {
if (ptr->file_store_id == file_id) {
FileStore(ptr);
}
}
}
}
void FileStoreAllFilesForTx(FileContainer *fc, uint64_t tx_id)
{
File *ptr = NULL;
SCEnter();
if (fc != NULL) {
for (ptr = fc->head; ptr != NULL; ptr = ptr->next) {
if (ptr->txid == tx_id) {
FileStore(ptr);
}
}
}
}
void FileStoreAllFiles(FileContainer *fc)
{
File *ptr = NULL;
SCEnter();
if (fc != NULL) {
for (ptr = fc->head; ptr != NULL; ptr = ptr->next) {
FileStore(ptr);
}
}
}
void FileTruncateAllOpenFiles(FileContainer *fc)
{
File *ptr = NULL;
SCEnter();
if (fc != NULL) {
for (ptr = fc->head; ptr != NULL; ptr = ptr->next) {
if (ptr->state == FILE_STATE_OPENED) {
FileCloseFilePtr(ptr, NULL, 0, FILE_TRUNCATED);
}
}
}
}
/**
* \brief Finish the SHA256 calculation.
*/
#ifdef HAVE_NSS
static void FileEndSha256(File *ff)
{
if (!(ff->flags & FILE_SHA256) && ff->sha256_ctx) {
unsigned int len = 0;
HASH_End(ff->sha256_ctx, ff->sha256, &len, sizeof(ff->sha256));
ff->flags |= FILE_SHA256;
}
}
#endif