Coverage Report

Created: 2026-01-16 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/output-filedata.c
Line
Count
Source
1
/* Copyright (C) 2007-2022 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 * \author Victor Julien <victor@inliniac.net>
22
 *
23
 * AppLayer Filedata Logger Output registration functions
24
 */
25
26
#include "suricata-common.h"
27
#include "output.h"
28
#include "output-filedata.h"
29
#include "app-layer-parser.h"
30
#include "detect-filemagic.h"
31
#include "conf.h"
32
#include "util-profiling.h"
33
#include "util-validate.h"
34
#include "util-magic.h"
35
#include "util-path.h"
36
37
bool g_filedata_logger_enabled = false;
38
39
/* logger instance, a module + a output ctx,
40
 * it's perfectly valid that have multiple instances of the same
41
 * log module (e.g. http.log) with different output ctx'. */
42
typedef struct OutputFiledataLogger_ {
43
    FiledataLogger LogFunc;
44
    OutputCtx *output_ctx;
45
    struct OutputFiledataLogger_ *next;
46
    const char *name;
47
    LoggerId logger_id;
48
    ThreadInitFunc ThreadInit;
49
    ThreadDeinitFunc ThreadDeinit;
50
    ThreadExitPrintStatsFunc ThreadExitPrintStats;
51
} OutputFiledataLogger;
52
53
static OutputFiledataLogger *list = NULL;
54
55
int OutputRegisterFiledataLogger(LoggerId id, const char *name,
56
    FiledataLogger LogFunc, OutputCtx *output_ctx, ThreadInitFunc ThreadInit,
57
    ThreadDeinitFunc ThreadDeinit,
58
    ThreadExitPrintStatsFunc ThreadExitPrintStats)
59
2
{
60
2
    OutputFiledataLogger *op = SCMalloc(sizeof(*op));
61
2
    if (op == NULL)
62
0
        return -1;
63
2
    memset(op, 0x00, sizeof(*op));
64
65
2
    op->LogFunc = LogFunc;
66
2
    op->output_ctx = output_ctx;
67
2
    op->name = name;
68
2
    op->logger_id = id;
69
2
    op->ThreadInit = ThreadInit;
70
2
    op->ThreadDeinit = ThreadDeinit;
71
2
    op->ThreadExitPrintStats = ThreadExitPrintStats;
72
73
2
    if (list == NULL)
74
2
        list = op;
75
0
    else {
76
0
        OutputFiledataLogger *t = list;
77
0
        while (t->next)
78
0
            t = t->next;
79
0
        t->next = op;
80
0
    }
81
82
2
    SCLogDebug("OutputRegisterFiledataLogger happy");
83
2
    g_filedata_logger_enabled = true;
84
2
    return 0;
85
2
}
86
87
SC_ATOMIC_DECLARE(unsigned int, g_file_store_id);
88
89
static int CallLoggers(ThreadVars *tv, OutputLoggerThreadStore *store_list, Packet *p, File *ff,
90
        void *tx, const uint64_t tx_id, const uint8_t *data, uint32_t data_len, uint8_t flags,
91
        uint8_t dir)
92
2.24M
{
93
2.24M
    OutputFiledataLogger *logger = list;
94
2.24M
    OutputLoggerThreadStore *store = store_list;
95
2.24M
    int file_logged = 0;
96
97
4.48M
    while (logger && store) {
98
2.24M
        DEBUG_VALIDATE_BUG_ON(logger->LogFunc == NULL);
99
100
2.24M
        SCLogDebug("logger %p", logger);
101
2.24M
        PACKET_PROFILING_LOGGER_START(p, logger->logger_id);
102
2.24M
        logger->LogFunc(tv, store->thread_data, (const Packet *)p, ff, tx, tx_id, data, data_len,
103
2.24M
                flags, dir);
104
2.24M
        PACKET_PROFILING_LOGGER_END(p, logger->logger_id);
105
106
2.24M
        file_logged = 1;
107
108
2.24M
        logger = logger->next;
109
2.24M
        store = store->next;
110
111
2.24M
        DEBUG_VALIDATE_BUG_ON(logger == NULL && store != NULL);
112
2.24M
        DEBUG_VALIDATE_BUG_ON(logger != NULL && store == NULL);
113
2.24M
    }
114
2.24M
    return file_logged;
115
2.24M
}
116
117
static void CloseFile(const Packet *p, Flow *f, File *file, void *txv)
118
52.2k
{
119
52.2k
    DEBUG_VALIDATE_BUG_ON((file->flags & FILE_STORED) != 0);
120
121
52.2k
    AppLayerTxData *txd = AppLayerParserGetTxData(f->proto, f->alproto, txv);
122
52.2k
    if (txd) {
123
52.2k
        BUG_ON(f->alproto == ALPROTO_SMB && txd->files_logged != 0);
124
52.2k
        txd->files_stored++;
125
52.2k
    }
126
52.2k
    file->flags |= FILE_STORED;
127
52.2k
}
128
129
void OutputFiledataLogFfc(ThreadVars *tv, OutputFiledataLoggerThreadData *td, Packet *p,
130
        AppLayerGetFileState files, void *txv, const uint64_t tx_id, AppLayerTxData *txd,
131
        const uint8_t call_flags, const bool file_close, const bool file_trunc, const uint8_t dir)
132
3.96M
{
133
3.96M
    SCLogDebug("ffc %p", files.fc);
134
135
3.96M
    OutputLoggerThreadStore *store = td->store;
136
6.20M
    for (File *ff = files.fc->head; ff != NULL; ff = ff->next) {
137
2.24M
        FileApplyTxFlags(txd, dir, ff);
138
2.24M
        FilePrintFlags(ff);
139
140
2.24M
        uint8_t file_flags = call_flags;
141
#ifdef HAVE_MAGIC
142
        if (FileForceMagic() && ff->magic == NULL) {
143
            FilemagicThreadLookup(&td->magic_ctx, ff);
144
        }
145
#endif
146
2.24M
        if (ff->flags & FILE_STORED) {
147
6
            continue;
148
6
        }
149
150
2.24M
        if (!(ff->flags & FILE_STORE)) {
151
0
            continue;
152
0
        }
153
154
        /* if file_store_id == 0, this is the first store of this file */
155
2.24M
        if (ff->file_store_id == 0) {
156
            /* new file */
157
169k
            ff->file_store_id = SC_ATOMIC_ADD(g_file_store_id, 1);
158
169k
            file_flags |= OUTPUT_FILEDATA_FLAG_OPEN;
159
169k
        }
160
161
        /* if we have no data chunks left to log, we should still
162
         * close the logger(s) */
163
2.24M
        if (FileDataSize(ff) == ff->content_stored && (file_trunc || file_close)) {
164
1.73k
            if (ff->state < FILE_STATE_CLOSED) {
165
1.17k
                FileCloseFilePtr(ff, files.cfg, NULL, 0, FILE_TRUNCATED);
166
1.17k
            }
167
1.73k
            file_flags |= OUTPUT_FILEDATA_FLAG_CLOSE;
168
1.73k
            CallLoggers(tv, store, p, ff, txv, tx_id, NULL, 0, file_flags, dir);
169
1.73k
            CloseFile(p, p->flow, ff, txv);
170
1.73k
            continue;
171
1.73k
        }
172
173
        /* if file needs to be closed or truncated, inform
174
         * loggers */
175
2.23M
        if ((file_close || file_trunc) && ff->state < FILE_STATE_CLOSED) {
176
1.27k
            FileCloseFilePtr(ff, files.cfg, NULL, 0, FILE_TRUNCATED);
177
1.27k
        }
178
179
        /* tell the logger we're closing up */
180
2.23M
        if (ff->state >= FILE_STATE_CLOSED)
181
157k
            file_flags |= OUTPUT_FILEDATA_FLAG_CLOSE;
182
183
        /* do the actual logging */
184
2.23M
        const uint8_t *data = NULL;
185
2.23M
        uint32_t data_len = 0;
186
187
2.23M
        StreamingBufferGetDataAtOffset(ff->sb, &data, &data_len, ff->content_stored);
188
189
2.23M
        const int file_logged =
190
2.23M
                CallLoggers(tv, store, p, ff, txv, tx_id, data, data_len, file_flags, dir);
191
2.23M
        if (file_logged) {
192
2.23M
            ff->content_stored += data_len;
193
194
            /* all done */
195
2.23M
            if (file_flags & OUTPUT_FILEDATA_FLAG_CLOSE) {
196
157k
                CloseFile(p, p->flow, ff, txv);
197
157k
            }
198
2.23M
        }
199
2.23M
    }
200
3.96M
}
201
202
/** \brief thread init for the filedata logger
203
 *  This will run the thread init functions for the individual registered
204
 *  loggers */
205
TmEcode OutputFiledataLogThreadInit(ThreadVars *tv, OutputFiledataLoggerThreadData **data)
206
4
{
207
4
    OutputFiledataLoggerThreadData *td = SCMalloc(sizeof(*td));
208
4
    if (td == NULL)
209
0
        return TM_ECODE_FAILED;
210
4
    memset(td, 0x00, sizeof(*td));
211
4
    *data = td;
212
213
#ifdef HAVE_MAGIC
214
    td->magic_ctx = MagicInitContext();
215
    if (td->magic_ctx == NULL) {
216
        SCFree(td);
217
        return TM_ECODE_FAILED;
218
    }
219
#endif
220
221
4
    SCLogDebug("OutputFiledataLogThreadInit happy (*data %p)", *data);
222
223
4
    OutputFiledataLogger *logger = list;
224
8
    while (logger) {
225
4
        if (logger->ThreadInit) {
226
4
            void *retptr = NULL;
227
4
            if (logger->ThreadInit(tv, (void *)logger->output_ctx, &retptr) == TM_ECODE_OK) {
228
4
                OutputLoggerThreadStore *ts = SCMalloc(sizeof(*ts));
229
4
                /* todo */ BUG_ON(ts == NULL);
230
4
                memset(ts, 0x00, sizeof(*ts));
231
232
                /* store thread handle */
233
4
                ts->thread_data = retptr;
234
235
4
                if (td->store == NULL) {
236
4
                    td->store = ts;
237
4
                } else {
238
0
                    OutputLoggerThreadStore *tmp = td->store;
239
0
                    while (tmp->next != NULL)
240
0
                        tmp = tmp->next;
241
0
                    tmp->next = ts;
242
0
                }
243
244
4
                SCLogDebug("%s is now set up", logger->name);
245
4
            }
246
4
        }
247
248
4
        logger = logger->next;
249
4
    }
250
4
    return TM_ECODE_OK;
251
4
}
252
253
TmEcode OutputFiledataLogThreadDeinit(
254
        ThreadVars *tv, OutputFiledataLoggerThreadData *op_thread_data)
255
0
{
256
0
    OutputLoggerThreadStore *store = op_thread_data->store;
257
0
    OutputFiledataLogger *logger = list;
258
259
0
    while (logger && store) {
260
0
        if (logger->ThreadDeinit) {
261
0
            logger->ThreadDeinit(tv, store->thread_data);
262
0
        }
263
264
0
        OutputLoggerThreadStore *next_store = store->next;
265
0
        SCFree(store);
266
0
        store = next_store;
267
0
        logger = logger->next;
268
0
    }
269
270
#ifdef HAVE_MAGIC
271
    MagicDeinitContext(op_thread_data->magic_ctx);
272
#endif
273
274
0
    SCFree(op_thread_data);
275
0
    return TM_ECODE_OK;
276
0
}
277
278
void OutputFiledataLoggerRegister(void)
279
71
{
280
71
    SC_ATOMIC_INIT(g_file_store_id);
281
71
    SC_ATOMIC_SET(g_file_store_id, 1);
282
71
}
283
284
void OutputFiledataShutdown(void)
285
0
{
286
0
    OutputFiledataLogger *logger = list;
287
0
    while (logger) {
288
0
        OutputFiledataLogger *next_logger = logger->next;
289
0
        SCFree(logger);
290
0
        logger = next_logger;
291
0
    }
292
293
    list = NULL;
294
0
}