Coverage Report

Created: 2026-06-07 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/alert-fastlog.c
Line
Count
Source
1
/* Copyright (C) 2007-2021 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
 * Logs alerts in a line based text format compatible to Snort's
24
 * alert_fast format.
25
 */
26
27
#include "suricata-common.h"
28
#include "detect.h"
29
#include "flow.h"
30
#include "conf.h"
31
32
#include "threads.h"
33
#include "tm-threads.h"
34
#include "threadvars.h"
35
#include "util-debug.h"
36
37
#include "util-unittest.h"
38
#include "util-unittest-helper.h"
39
40
#include "detect-parse.h"
41
#include "detect-engine.h"
42
#include "detect-engine-build.h"
43
#include "detect-engine-mpm.h"
44
#include "detect-reference.h"
45
#include "util-classification-config.h"
46
47
#include "output.h"
48
#include "alert-fastlog.h"
49
50
#include "util-privs.h"
51
#include "util-print.h"
52
#include "util-proto-name.h"
53
#include "util-optimize.h"
54
#include "util-logopenfile.h"
55
#include "util-time.h"
56
57
#include "action-globals.h"
58
59
4
#define DEFAULT_LOG_FILENAME "fast.log"
60
61
74
#define MODULE_NAME "AlertFastLog"
62
63
/* The largest that size allowed for one alert string. */
64
2.30k
#define MAX_FASTLOG_ALERT_SIZE 2048
65
/* The largest alert buffer that will be written at one time, possibly
66
 * holding multiple alerts. */
67
#define MAX_FASTLOG_BUFFER_SIZE (2 * MAX_FASTLOG_ALERT_SIZE)
68
69
TmEcode AlertFastLogThreadInit(ThreadVars *, const void *, void **);
70
TmEcode AlertFastLogThreadDeinit(ThreadVars *, void *);
71
void AlertFastLogRegisterTests(void);
72
static void AlertFastLogDeInitCtx(OutputCtx *);
73
74
int AlertFastLogCondition(ThreadVars *tv, void *thread_data, const Packet *p);
75
int AlertFastLogger(ThreadVars *tv, void *data, const Packet *p);
76
77
void AlertFastLogRegister(void)
78
74
{
79
74
    OutputRegisterPacketModule(LOGGER_ALERT_FAST, MODULE_NAME, "fast",
80
74
        AlertFastLogInitCtx, AlertFastLogger, AlertFastLogCondition,
81
74
        AlertFastLogThreadInit, AlertFastLogThreadDeinit, NULL);
82
74
    AlertFastLogRegisterTests();
83
74
}
84
85
typedef struct AlertFastLogThread_ {
86
    /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
87
    LogFileCtx* file_ctx;
88
} AlertFastLogThread;
89
90
int AlertFastLogCondition(ThreadVars *tv, void *thread_data, const Packet *p)
91
9.55M
{
92
9.55M
    return (p->alerts.cnt ? TRUE : FALSE);
93
9.55M
}
94
95
static inline void AlertFastLogOutputAlert(AlertFastLogThread *aft, char *buffer,
96
                                           int alert_size)
97
891k
{
98
    /* Output the alert string and count alerts. Only need to lock here. */
99
891k
    aft->file_ctx->Write(buffer, alert_size, aft->file_ctx);
100
891k
}
101
102
int AlertFastLogger(ThreadVars *tv, void *data, const Packet *p)
103
408k
{
104
408k
    AlertFastLogThread *aft = (AlertFastLogThread *)data;
105
408k
    int i;
106
408k
    char timebuf[64];
107
408k
    int decoder_event = 0;
108
109
408k
    CreateTimeString(p->ts, timebuf, sizeof(timebuf));
110
111
408k
    char srcip[46], dstip[46];
112
408k
    if (PKT_IS_IPV4(p)) {
113
383k
        PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
114
383k
        PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
115
383k
    } else if (PKT_IS_IPV6(p)) {
116
22.6k
        PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
117
22.6k
        PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
118
22.6k
    } else {
119
2.29k
        decoder_event = 1;
120
2.29k
    }
121
122
    /* Buffer to store the generated alert strings. The buffer is
123
     * filled with alert strings until it doesn't have room to store
124
     * another full alert, only then is the buffer written.  This is
125
     * more efficient for multiple alerts and only slightly slower for
126
     * single alerts.
127
     */
128
408k
    char alert_buffer[MAX_FASTLOG_BUFFER_SIZE];
129
130
408k
    char proto[16] = "";
131
408k
    const char *protoptr;
132
408k
    if (SCProtoNameValid(IP_GET_IPPROTO(p))) {
133
407k
        protoptr = known_proto[IP_GET_IPPROTO(p)];
134
407k
    } else {
135
965
        snprintf(proto, sizeof(proto), "PROTO:%03" PRIu32, IP_GET_IPPROTO(p));
136
965
        protoptr = proto;
137
965
    }
138
408k
    uint16_t src_port_or_icmp = p->sp;
139
408k
    uint16_t dst_port_or_icmp = p->dp;
140
408k
    if (IP_GET_IPPROTO(p) == IPPROTO_ICMP || IP_GET_IPPROTO(p) == IPPROTO_ICMPV6) {
141
8.34k
        src_port_or_icmp = p->icmp_s.type;
142
8.34k
        dst_port_or_icmp = p->icmp_s.code;
143
8.34k
    }
144
861k
    for (i = 0; i < p->alerts.cnt; i++) {
145
452k
        const PacketAlert *pa = &p->alerts.alerts[i];
146
452k
        if (unlikely(pa->s == NULL)) {
147
0
            continue;
148
0
        }
149
150
452k
        const char *action = "";
151
452k
        if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) {
152
0
            action = "[Drop] ";
153
452k
        } else if (pa->action & ACTION_DROP) {
154
684
            action = "[wDrop] ";
155
684
        }
156
157
        /* Create the alert string without locking. */
158
452k
        int size = 0;
159
452k
        if (likely(decoder_event == 0)) {
160
450k
            PrintBufferData(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE,
161
450k
                            "%s  %s[**] [%" PRIu32 ":%" PRIu32 ":%"
162
450k
                            PRIu32 "] %s [**] [Classification: %s] [Priority: %"PRIu32"]"
163
450k
                            " {%s} %s:%" PRIu32 " -> %s:%" PRIu32 "\n", timebuf, action,
164
450k
                            pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg, pa->s->prio,
165
450k
                            protoptr, srcip, src_port_or_icmp, dstip, dst_port_or_icmp);
166
450k
        } else {
167
2.30k
            PrintBufferData(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE, 
168
2.30k
                            "%s  %s[**] [%" PRIu32 ":%" PRIu32
169
2.30k
                            ":%" PRIu32 "] %s [**] [Classification: %s] [Priority: "
170
2.30k
                            "%" PRIu32 "] [**] [Raw pkt: ", timebuf, action, pa->s->gid,
171
2.30k
                            pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg, pa->s->prio);
172
2.30k
            PrintBufferRawLineHex(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE,
173
2.30k
                                  GET_PKT_DATA(p), GET_PKT_LEN(p) < 32 ? GET_PKT_LEN(p) : 32);
174
2.30k
            if (p->pcap_cnt != 0) {
175
2.25k
                PrintBufferData(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE, 
176
2.25k
                                "] [pcap file packet: %"PRIu64"]\n", p->pcap_cnt);
177
2.25k
            } else {
178
42
                PrintBufferData(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE, "]\n");
179
42
            }
180
2.30k
        }
181
182
        /* Write the alert to output file */
183
452k
        AlertFastLogOutputAlert(aft, alert_buffer, size);
184
452k
    }
185
186
408k
    return TM_ECODE_OK;
187
408k
}
188
189
TmEcode AlertFastLogThreadInit(ThreadVars *t, const void *initdata, void **data)
190
4
{
191
4
    AlertFastLogThread *aft = SCMalloc(sizeof(AlertFastLogThread));
192
4
    if (unlikely(aft == NULL))
193
0
        return TM_ECODE_FAILED;
194
4
    memset(aft, 0, sizeof(AlertFastLogThread));
195
4
    if(initdata == NULL)
196
0
    {
197
0
        SCLogDebug("Error getting context for AlertFastLog.  \"initdata\" argument NULL");
198
0
        SCFree(aft);
199
0
        return TM_ECODE_FAILED;
200
0
    }
201
    /** Use the Output Context (file pointer and mutex) */
202
4
    aft->file_ctx = ((OutputCtx *)initdata)->data;
203
204
4
    *data = (void *)aft;
205
4
    return TM_ECODE_OK;
206
4
}
207
208
TmEcode AlertFastLogThreadDeinit(ThreadVars *t, void *data)
209
0
{
210
0
    AlertFastLogThread *aft = (AlertFastLogThread *)data;
211
0
    if (aft == NULL) {
212
0
        return TM_ECODE_OK;
213
0
    }
214
215
    /* clear memory */
216
0
    memset(aft, 0, sizeof(AlertFastLogThread));
217
218
0
    SCFree(aft);
219
0
    return TM_ECODE_OK;
220
0
}
221
222
/**
223
 * \brief Create a new LogFileCtx for "fast" output style.
224
 * \param conf The configuration node for this output.
225
 * \return A LogFileCtx pointer on success, NULL on failure.
226
 */
227
OutputInitResult AlertFastLogInitCtx(ConfNode *conf)
228
4
{
229
4
    OutputInitResult result = { NULL, false };
230
4
    LogFileCtx *logfile_ctx = LogFileNewCtx();
231
4
    if (logfile_ctx == NULL) {
232
0
        SCLogDebug("AlertFastLogInitCtx2: Could not create new LogFileCtx");
233
0
        return result;
234
0
    }
235
236
4
    if (SCConfLogOpenGeneric(conf, logfile_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
237
0
        LogFileFreeCtx(logfile_ctx);
238
0
        return result;
239
0
    }
240
241
4
    OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
242
4
    if (unlikely(output_ctx == NULL)) {
243
0
        LogFileFreeCtx(logfile_ctx);
244
0
        return result;
245
0
    }
246
247
4
    output_ctx->data = logfile_ctx;
248
4
    output_ctx->DeInit = AlertFastLogDeInitCtx;
249
250
4
    result.ctx = output_ctx;
251
4
    result.ok = true;
252
4
    return result;
253
4
}
254
255
static void AlertFastLogDeInitCtx(OutputCtx *output_ctx)
256
0
{
257
0
    LogFileCtx *logfile_ctx = (LogFileCtx *)output_ctx->data;
258
0
    LogFileFreeCtx(logfile_ctx);
259
0
    SCFree(output_ctx);
260
0
}
261
262
/*------------------------------Unittests-------------------------------------*/
263
264
#ifdef UNITTESTS
265
266
static int AlertFastLogTest01(void)
267
{
268
    uint8_t *buf = (uint8_t *) "GET /one/ HTTP/1.1\r\n"
269
        "Host: one.example.org\r\n";
270
271
    uint16_t buflen = strlen((char *)buf);
272
    Packet *p = NULL;
273
    ThreadVars th_v;
274
    DetectEngineThreadCtx *det_ctx;
275
276
    memset(&th_v, 0, sizeof(th_v));
277
    p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
278
279
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
280
    FAIL_IF(de_ctx == NULL);
281
282
    de_ctx->flags |= DE_QUIET;
283
284
    FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
285
    SCClassConfLoadClassificationConfigFile(de_ctx, fd);
286
287
    de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
288
            "(msg:\"FastLog test\"; content:\"GET\"; "
289
            "Classtype:unknown; sid:1;)");
290
291
    SigGroupBuild(de_ctx);
292
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
293
294
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
295
    FAIL_IF_NOT(p->alerts.cnt == 1);
296
    FAIL_IF_NOT(strcmp(p->alerts.alerts[0].s->class_msg, "Unknown are we") == 0);
297
298
    SigGroupCleanup(de_ctx);
299
    SigCleanSignatures(de_ctx);
300
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
301
    DetectEngineCtxFree(de_ctx);
302
303
    UTHFreePackets(&p, 1);
304
    PASS;
305
}
306
307
static int AlertFastLogTest02(void)
308
{
309
    uint8_t *buf = (uint8_t *) "GET /one/ HTTP/1.1\r\n"
310
        "Host: one.example.org\r\n";
311
    uint16_t buflen = strlen((char *)buf);
312
    Packet *p = NULL;
313
    ThreadVars th_v;
314
    DetectEngineThreadCtx *det_ctx;
315
316
    memset(&th_v, 0, sizeof(th_v));
317
318
    p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
319
320
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
321
    FAIL_IF(de_ctx == NULL);
322
323
    de_ctx->flags |= DE_QUIET;
324
325
    FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
326
    SCClassConfLoadClassificationConfigFile(de_ctx, fd);
327
328
    de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
329
            "(msg:\"FastLog test\"; content:\"GET\"; "
330
            "Classtype:unknown; sid:1;)");
331
332
    SigGroupBuild(de_ctx);
333
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
334
335
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
336
    FAIL_IF_NOT(p->alerts.cnt == 1);
337
    FAIL_IF_NOT(strcmp(p->alerts.alerts[0].s->class_msg, "Unknown are we") == 0);
338
339
    SigGroupCleanup(de_ctx);
340
    SigCleanSignatures(de_ctx);
341
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
342
    DetectEngineCtxFree(de_ctx);
343
344
    UTHFreePackets(&p, 1);
345
    PASS;
346
}
347
348
#endif /* UNITTESTS */
349
350
/**
351
 * \brief This function registers unit tests for AlertFastLog API.
352
 */
353
void AlertFastLogRegisterTests(void)
354
74
{
355
356
#ifdef UNITTESTS
357
358
    UtRegisterTest("AlertFastLogTest01", AlertFastLogTest01);
359
    UtRegisterTest("AlertFastLogTest02", AlertFastLogTest02);
360
361
#endif /* UNITTESTS */
362
363
74
}