Coverage Report

Created: 2026-05-16 07:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata/src/alert-syslog.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 Gurvinder Singh <gurvindersinghdahiya@gmail.com>
22
 *
23
 * Logs alerts in a line based text format into syslog.
24
 *
25
 */
26
27
#include "suricata-common.h"
28
#include "flow.h"
29
#include "conf.h"
30
31
#include "threads.h"
32
#include "tm-threads.h"
33
#include "threadvars.h"
34
35
#include "detect.h"
36
#include "detect-parse.h"
37
#include "detect-engine.h"
38
#include "detect-engine-mpm.h"
39
#include "detect-reference.h"
40
41
#include "output.h"
42
#include "alert-syslog.h"
43
44
#include "util-classification-config.h"
45
#include "util-debug.h"
46
#include "util-print.h"
47
#include "util-proto-name.h"
48
#include "util-syslog.h"
49
#include "util-optimize.h"
50
#include "util-logopenfile.h"
51
#include "action-globals.h"
52
53
#ifndef OS_WIN32
54
55
71
#define MODULE_NAME                             "AlertSyslog"
56
57
static int alert_syslog_level = DEFAULT_ALERT_SYSLOG_LEVEL;
58
59
typedef struct AlertSyslogThread_ {
60
    /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
61
    LogFileCtx* file_ctx;
62
} AlertSyslogThread;
63
64
/**
65
 * \brief Function to clear the memory of the output context and closes the
66
 *        syslog interface
67
 *
68
 * \param output_ctx pointer to the output context to be cleared
69
 */
70
static void AlertSyslogDeInitCtx(OutputCtx *output_ctx)
71
0
{
72
0
    if (output_ctx != NULL) {
73
0
        LogFileCtx *logfile_ctx = (LogFileCtx *)output_ctx->data;
74
0
        if (logfile_ctx != NULL) {
75
0
            LogFileFreeCtx(logfile_ctx);
76
0
        }
77
0
        SCFree(output_ctx);
78
0
    }
79
0
    closelog();
80
0
}
81
82
/**
83
 * \brief Create a new LogFileCtx for "syslog" output style.
84
 *
85
 * \param conf The configuration node for this output.
86
 * \return A OutputCtx pointer on success, NULL on failure.
87
 */
88
static OutputInitResult AlertSyslogInitCtx(SCConfNode *conf)
89
0
{
90
0
    SCLogWarning("The syslog output has been deprecated and will be removed in Suricata 9.0.");
91
92
0
    OutputInitResult result = { NULL, false };
93
0
    const char *facility_s = SCConfNodeLookupChildValue(conf, "facility");
94
0
    if (facility_s == NULL) {
95
0
        facility_s = DEFAULT_ALERT_SYSLOG_FACILITY_STR;
96
0
    }
97
98
0
    LogFileCtx *logfile_ctx = LogFileNewCtx();
99
0
    if (logfile_ctx == NULL) {
100
0
        SCLogDebug("AlertSyslogInitCtx: Could not create new LogFileCtx");
101
0
        return result;
102
0
    }
103
104
0
    int facility = SCMapEnumNameToValue(facility_s, SCSyslogGetFacilityMap());
105
0
    if (facility == -1) {
106
0
        SCLogWarning("Invalid syslog facility: \"%s\","
107
0
                     " now using \"%s\" as syslog facility",
108
0
                facility_s, DEFAULT_ALERT_SYSLOG_FACILITY_STR);
109
0
        facility = DEFAULT_ALERT_SYSLOG_FACILITY;
110
0
    }
111
112
0
    const char *level_s = SCConfNodeLookupChildValue(conf, "level");
113
0
    if (level_s != NULL) {
114
0
        int level = SCMapEnumNameToValue(level_s, SCSyslogGetLogLevelMap());
115
0
        if (level != -1) {
116
0
            alert_syslog_level = level;
117
0
        }
118
0
    }
119
120
0
    const char *ident = SCConfNodeLookupChildValue(conf, "identity");
121
    /* if null we just pass that to openlog, which will then
122
     * figure it out by itself. */
123
124
0
    openlog(ident, LOG_PID|LOG_NDELAY, facility);
125
126
0
    OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
127
0
    if (unlikely(output_ctx == NULL)) {
128
0
        SCLogDebug("could not create new OutputCtx");
129
0
        LogFileFreeCtx(logfile_ctx);
130
0
        return result;
131
0
    }
132
133
0
    output_ctx->data = logfile_ctx;
134
0
    output_ctx->DeInit = AlertSyslogDeInitCtx;
135
136
0
    SCLogInfo("Syslog output initialized");
137
138
0
    result.ctx = output_ctx;
139
0
    result.ok = true;
140
0
    return result;
141
0
}
142
143
/**
144
 * \brief Function to initialize the AlertSyslogThread and sets the output
145
 *        context pointer
146
 *
147
 * \param tv            Pointer to the threadvars
148
 * \param initdata      Pointer to the output context
149
 * \param data          pointer to pointer to point to the AlertSyslogThread
150
 */
151
static TmEcode AlertSyslogThreadInit(ThreadVars *t, const void *initdata, void **data)
152
0
{
153
0
    if(initdata == NULL) {
154
0
        SCLogDebug("Error getting context for AlertSyslog. \"initdata\" "
155
0
                "argument NULL");
156
0
        return TM_ECODE_FAILED;
157
0
    }
158
159
0
    AlertSyslogThread *ast = SCCalloc(1, sizeof(AlertSyslogThread));
160
0
    if (unlikely(ast == NULL))
161
0
        return TM_ECODE_FAILED;
162
163
    /** Use the Output Context (file pointer and mutex) */
164
0
    ast->file_ctx = ((OutputCtx *)initdata)->data;
165
166
0
    *data = (void *)ast;
167
0
    return TM_ECODE_OK;
168
0
}
169
170
/**
171
 * \brief Function to deinitialize the AlertSyslogThread
172
 *
173
 * \param tv            Pointer to the threadvars
174
 * \param data          pointer to the AlertSyslogThread to be cleared
175
 */
176
static TmEcode AlertSyslogThreadDeinit(ThreadVars *t, void *data)
177
0
{
178
0
    AlertSyslogThread *ast = (AlertSyslogThread *)data;
179
0
    if (ast == NULL) {
180
0
        return TM_ECODE_OK;
181
0
    }
182
183
    /* clear memory */
184
0
    memset(ast, 0, sizeof(AlertSyslogThread));
185
186
0
    SCFree(ast);
187
0
    return TM_ECODE_OK;
188
0
}
189
190
/**
191
 * \brief   Function which is called to print the IPv4 alerts to the syslog
192
 *
193
 * \param tv    Pointer to the threadvars
194
 * \param p     Pointer to the packet
195
 * \param data  pointer to the AlertSyslogThread
196
 *
197
 * \return On succes return TM_ECODE_OK
198
 */
199
static TmEcode AlertSyslogIPv4(ThreadVars *tv, const Packet *p, void *data)
200
0
{
201
0
    AlertSyslogThread *ast = (AlertSyslogThread *)data;
202
0
    const char *action = "";
203
204
0
    if (p->alerts.cnt == 0)
205
0
        return TM_ECODE_OK;
206
207
0
    char proto[16] = "";
208
0
    const char *protoptr;
209
0
    const IPV4Hdr *ipv4h = PacketGetIPv4(p);
210
0
    const uint8_t ipproto = IPV4_GET_RAW_IPPROTO(ipv4h);
211
0
    if (SCProtoNameValid(ipproto)) {
212
0
        protoptr = known_proto[ipproto];
213
0
    } else {
214
0
        snprintf(proto, sizeof(proto), "PROTO:%03" PRIu8, ipproto);
215
0
        protoptr = proto;
216
0
    }
217
218
0
    char srcip[16], dstip[16];
219
0
    PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
220
0
    PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
221
222
0
    for (int i = 0; i < p->alerts.cnt; i++) {
223
0
        const PacketAlert *pa = &p->alerts.alerts[i];
224
0
        if (unlikely(pa->s == NULL || (pa->action & ACTION_ALERT) == 0)) {
225
0
            continue;
226
0
        }
227
228
0
        if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) {
229
0
            action = "[Drop] ";
230
0
        } else if (pa->action & ACTION_DROP) {
231
0
            action = "[wDrop] ";
232
0
        }
233
234
        /* Not sure if this mutex is needed around calls to syslog. */
235
0
        SCMutexLock(&ast->file_ctx->fp_mutex);
236
0
        syslog(alert_syslog_level, "%s[%" PRIu32 ":%" PRIu32 ":%"
237
0
                PRIu32 "] %s [Classification: %s] [Priority: %"PRIu32"]"
238
0
                " {%s} %s:%" PRIu32 " -> %s:%" PRIu32 "", action, pa->s->gid,
239
0
                pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg, pa->s->prio,
240
0
                protoptr,  srcip, p->sp, dstip, p->dp);
241
0
        SCMutexUnlock(&ast->file_ctx->fp_mutex);
242
0
    }
243
244
0
    return TM_ECODE_OK;
245
0
}
246
247
/**
248
 * \brief   Function which is called to print the IPv6 alerts to the syslog
249
 *
250
 * \param tv    Pointer to the threadvars
251
 * \param p     Pointer to the packet
252
 * \param data  pointer to the AlertSyslogThread
253
 *
254
 * \return On succes return TM_ECODE_OK
255
 */
256
static TmEcode AlertSyslogIPv6(ThreadVars *tv, const Packet *p, void *data)
257
0
{
258
0
    AlertSyslogThread *ast = (AlertSyslogThread *)data;
259
0
    const char *action = "";
260
261
0
    if (p->alerts.cnt == 0)
262
0
        return TM_ECODE_OK;
263
264
0
    char proto[16] = "";
265
0
    const char *protoptr;
266
0
    const uint8_t ipproto = IPV6_GET_L4PROTO(p);
267
0
    if (SCProtoNameValid(ipproto)) {
268
0
        protoptr = known_proto[ipproto];
269
0
    } else {
270
0
        snprintf(proto, sizeof(proto), "PROTO:03%" PRIu8, ipproto);
271
0
        protoptr = proto;
272
0
    }
273
274
0
    char srcip[46], dstip[46];
275
0
    PrintInetIPv6(
276
0
            (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip), ast->file_ctx->compress_ipv6);
277
0
    PrintInetIPv6(
278
0
            (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip), ast->file_ctx->compress_ipv6);
279
280
0
    for (int i = 0; i < p->alerts.cnt; i++) {
281
0
        const PacketAlert *pa = &p->alerts.alerts[i];
282
0
        if (unlikely(pa->s == NULL || (pa->action & ACTION_ALERT) == 0)) {
283
0
            continue;
284
0
        }
285
286
0
        if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) {
287
0
            action = "[Drop] ";
288
0
        } else if (pa->action & ACTION_DROP) {
289
0
            action = "[wDrop] ";
290
0
        }
291
292
0
        SCMutexLock(&ast->file_ctx->fp_mutex);
293
0
        syslog(alert_syslog_level, "%s[%" PRIu32 ":%" PRIu32 ":%"
294
0
                "" PRIu32 "] %s [Classification: %s] [Priority: %"
295
0
                "" PRIu32 "] {%s} %s:%" PRIu32 " -> %s:%" PRIu32 "",
296
0
                action, pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg,
297
0
                pa->s->prio, protoptr, srcip, p->sp,
298
0
                dstip, p->dp);
299
0
        SCMutexUnlock(&ast->file_ctx->fp_mutex);
300
0
    }
301
302
0
    return TM_ECODE_OK;
303
0
}
304
305
/**
306
 * \brief   Function which is called to print the decode alerts to the syslog
307
 *
308
 * \param tv    Pointer to the threadvars
309
 * \param p     Pointer to the packet
310
 * \param data  pointer to the AlertSyslogThread
311
 *
312
 * \return On succes return TM_ECODE_OK
313
 */
314
static TmEcode AlertSyslogDecoderEvent(ThreadVars *tv, const Packet *p, void *data)
315
0
{
316
0
    AlertSyslogThread *ast = (AlertSyslogThread *)data;
317
0
    const char *action = "";
318
319
0
    if (p->alerts.cnt == 0)
320
0
        return TM_ECODE_OK;
321
322
0
    char temp_buf_hdr[512];
323
0
    char temp_buf_pkt[65] = "";
324
0
    char temp_buf_tail[64];
325
0
    char alert[2048] = "";
326
327
0
    for (int i = 0; i < p->alerts.cnt; i++) {
328
0
        const PacketAlert *pa = &p->alerts.alerts[i];
329
0
        if (unlikely(pa->s == NULL || (pa->action & ACTION_ALERT) == 0)) {
330
0
            continue;
331
0
        }
332
333
0
        if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) {
334
0
            action = "[Drop] ";
335
0
        } else if (pa->action & ACTION_DROP) {
336
0
            action = "[wDrop] ";
337
0
        }
338
339
0
        snprintf(temp_buf_hdr, sizeof(temp_buf_hdr), "%s[%" PRIu32 ":%" PRIu32
340
0
                ":%" PRIu32 "] %s [Classification: %s] [Priority: %" PRIu32
341
0
                "] [**] [Raw pkt: ", action, pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg,
342
0
                pa->s->class_msg, pa->s->prio);
343
0
        strlcpy(alert, temp_buf_hdr, sizeof(alert));
344
345
0
        PrintRawLineHexBuf(temp_buf_pkt, sizeof(temp_buf_pkt), GET_PKT_DATA(p), GET_PKT_LEN(p) < 32 ? GET_PKT_LEN(p) : 32);
346
0
        strlcat(alert, temp_buf_pkt, sizeof(alert));
347
348
0
        uint64_t pcap_cnt = PcapPacketCntGet(p);
349
0
        if (pcap_cnt != 0) {
350
0
            snprintf(temp_buf_tail, sizeof(temp_buf_tail), "] [pcap file packet: %" PRIu64 "]",
351
0
                    pcap_cnt);
352
0
        } else {
353
0
            temp_buf_tail[0] = ']';
354
0
            temp_buf_tail[1] = '\0';
355
0
        }
356
0
        strlcat(alert, temp_buf_tail, sizeof(alert));
357
358
0
        SCMutexLock(&ast->file_ctx->fp_mutex);
359
0
        syslog(alert_syslog_level, "%s", alert);
360
0
        SCMutexUnlock(&ast->file_ctx->fp_mutex);
361
0
    }
362
363
0
    return TM_ECODE_OK;
364
0
}
365
366
static bool AlertSyslogCondition(ThreadVars *tv, void *thread_data, const Packet *p)
367
0
{
368
0
    return (p->alerts.cnt > 0);
369
0
}
370
371
static int AlertSyslogLogger(ThreadVars *tv, void *thread_data, const Packet *p)
372
0
{
373
0
    if (PacketIsIPv4(p)) {
374
0
        return AlertSyslogIPv4(tv, p, thread_data);
375
0
    } else if (PacketIsIPv6(p)) {
376
0
        return AlertSyslogIPv6(tv, p, thread_data);
377
0
    } else if (p->events.cnt > 0) {
378
0
        return AlertSyslogDecoderEvent(tv, p, thread_data);
379
0
    }
380
381
0
    return TM_ECODE_OK;
382
0
}
383
384
#endif /* !OS_WIN32 */
385
386
/** \brief   Function to register the AlertSyslog module */
387
void AlertSyslogRegister (void)
388
71
{
389
71
#ifndef OS_WIN32
390
71
    OutputPacketLoggerFunctions output_logger_functions = {
391
71
        .LogFunc = AlertSyslogLogger,
392
71
        .ConditionFunc = AlertSyslogCondition,
393
71
        .ThreadInitFunc = AlertSyslogThreadInit,
394
71
        .ThreadDeinitFunc = AlertSyslogThreadDeinit,
395
71
        .ThreadExitPrintStatsFunc = NULL,
396
71
    };
397
71
    OutputRegisterPacketModule(LOGGER_ALERT_SYSLOG, MODULE_NAME, "syslog", AlertSyslogInitCtx,
398
71
            &output_logger_functions);
399
71
#endif /* !OS_WIN32 */
400
71
}