suricata
alert-fastlog.c
Go to the documentation of this file.
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"
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"
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#define DEFAULT_LOG_FILENAME "fast.log"
60
61#define MODULE_NAME "AlertFastLog"
62
63/* The largest that size allowed for one alert string. */
64#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
69TmEcode AlertFastLogThreadInit(ThreadVars *, const void *, void **);
72static void AlertFastLogDeInitCtx(OutputCtx *);
73
74static bool AlertFastLogCondition(ThreadVars *tv, void *thread_data, const Packet *p);
75int AlertFastLogger(ThreadVars *tv, void *data, const Packet *p);
76
78{
79 OutputPacketLoggerFunctions output_logger_functions = {
81 .FlushFunc = NULL,
82 .ConditionFunc = AlertFastLogCondition,
83 .ThreadInitFunc = AlertFastLogThreadInit,
84 .ThreadDeinitFunc = AlertFastLogThreadDeinit,
85 .ThreadExitPrintStatsFunc = NULL,
86 };
87
89 LOGGER_ALERT_FAST, MODULE_NAME, "fast", AlertFastLogInitCtx, &output_logger_functions);
91}
92
93typedef struct AlertFastLogThread_ {
94 /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
97
98static bool AlertFastLogCondition(ThreadVars *tv, void *thread_data, const Packet *p)
99{
100 return (p->alerts.cnt > 0);
101}
102
103static inline void AlertFastLogOutputAlert(AlertFastLogThread *aft, char *buffer,
104 int alert_size)
105{
106 /* Output the alert string and count alerts. Only need to lock here. */
107 aft->file_ctx->Write(buffer, alert_size, aft->file_ctx);
108}
109
110int AlertFastLogger(ThreadVars *tv, void *data, const Packet *p)
111{
113 int i;
114 char timebuf[64];
115 int decoder_event = 0;
116
117 CreateTimeString(p->ts, timebuf, sizeof(timebuf));
118
119 char srcip[46], dstip[46];
120 if (PacketIsIPv4(p)) {
121 PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
122 PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
123 } else if (PacketIsIPv6(p)) {
124 PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
125 PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
126 } else {
127 decoder_event = 1;
128 }
129
130 /* Buffer to store the generated alert strings. The buffer is
131 * filled with alert strings until it doesn't have room to store
132 * another full alert, only then is the buffer written. This is
133 * more efficient for multiple alerts and only slightly slower for
134 * single alerts.
135 */
136 char alert_buffer[MAX_FASTLOG_BUFFER_SIZE];
137
138 char proto[16] = "";
139 const char *protoptr;
140 if (SCProtoNameValid(PacketGetIPProto(p))) {
141 protoptr = known_proto[PacketGetIPProto(p)];
142 } else {
143 snprintf(proto, sizeof(proto), "PROTO:%03" PRIu32, PacketGetIPProto(p));
144 protoptr = proto;
145 }
146 uint16_t src_port_or_icmp = p->sp;
147 uint16_t dst_port_or_icmp = p->dp;
148 if (PacketGetIPProto(p) == IPPROTO_ICMP || PacketGetIPProto(p) == IPPROTO_ICMPV6) {
149 src_port_or_icmp = p->icmp_s.type;
150 dst_port_or_icmp = p->icmp_s.code;
151 }
152 for (i = 0; i < p->alerts.cnt; i++) {
153 const PacketAlert *pa = &p->alerts.alerts[i];
154 if (unlikely(pa->s == NULL)) {
155 continue;
156 }
157
158 const char *action = "";
159 if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) {
160 action = "[Drop] ";
161 } else if (pa->action & ACTION_DROP) {
162 action = "[wDrop] ";
163 }
164
165 /* Create the alert string without locking. */
166 int size = 0;
167 if (likely(decoder_event == 0)) {
168 PrintBufferData(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE,
169 "%s %s[**] [%" PRIu32 ":%" PRIu32 ":%"
170 PRIu32 "] %s [**] [Classification: %s] [Priority: %"PRIu32"]"
171 " {%s} %s:%" PRIu32 " -> %s:%" PRIu32 "\n", timebuf, action,
172 pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg, pa->s->prio,
173 protoptr, srcip, src_port_or_icmp, dstip, dst_port_or_icmp);
174 } else {
175 PrintBufferData(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE,
176 "%s %s[**] [%" PRIu32 ":%" PRIu32
177 ":%" PRIu32 "] %s [**] [Classification: %s] [Priority: "
178 "%" PRIu32 "] [**] [Raw pkt: ", timebuf, action, pa->s->gid,
179 pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg, pa->s->prio);
181 GET_PKT_DATA(p), GET_PKT_LEN(p) < 32 ? GET_PKT_LEN(p) : 32);
182 if (p->pcap_cnt != 0) {
183 PrintBufferData(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE,
184 "] [pcap file packet: %"PRIu64"]\n", p->pcap_cnt);
185 } else {
186 PrintBufferData(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE, "]\n");
187 }
188 }
189
190 /* Write the alert to output file */
191 AlertFastLogOutputAlert(aft, alert_buffer, size);
192 }
193
194 return TM_ECODE_OK;
195}
196
197TmEcode AlertFastLogThreadInit(ThreadVars *t, const void *initdata, void **data)
198{
200 if (unlikely(aft == NULL))
201 return TM_ECODE_FAILED;
202 if(initdata == NULL)
203 {
204 SCLogDebug("Error getting context for AlertFastLog. \"initdata\" argument NULL");
205 SCFree(aft);
206 return TM_ECODE_FAILED;
207 }
208 /** Use the Output Context (file pointer and mutex) */
209 aft->file_ctx = ((OutputCtx *)initdata)->data;
210
211 *data = (void *)aft;
212 return TM_ECODE_OK;
213}
214
216{
218 if (aft == NULL) {
219 return TM_ECODE_OK;
220 }
221
222 /* clear memory */
223 memset(aft, 0, sizeof(AlertFastLogThread));
224
225 SCFree(aft);
226 return TM_ECODE_OK;
227}
228
229/**
230 * \brief Create a new LogFileCtx for "fast" output style.
231 * \param conf The configuration node for this output.
232 * \return A LogFileCtx pointer on success, NULL on failure.
233 */
235{
236 OutputInitResult result = { NULL, false };
237 LogFileCtx *logfile_ctx = LogFileNewCtx();
238 if (logfile_ctx == NULL) {
239 SCLogDebug("AlertFastLogInitCtx2: Could not create new LogFileCtx");
240 return result;
241 }
242
243 if (SCConfLogOpenGeneric(conf, logfile_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
244 LogFileFreeCtx(logfile_ctx);
245 return result;
246 }
247
248 OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
249 if (unlikely(output_ctx == NULL)) {
250 LogFileFreeCtx(logfile_ctx);
251 return result;
252 }
253
254 output_ctx->data = logfile_ctx;
255 output_ctx->DeInit = AlertFastLogDeInitCtx;
256
257 result.ctx = output_ctx;
258 result.ok = true;
259 return result;
260}
261
262static void AlertFastLogDeInitCtx(OutputCtx *output_ctx)
263{
264 LogFileCtx *logfile_ctx = (LogFileCtx *)output_ctx->data;
265 LogFileFreeCtx(logfile_ctx);
266 SCFree(output_ctx);
267}
268
269/*------------------------------Unittests-------------------------------------*/
270
271#ifdef UNITTESTS
272
273static int AlertFastLogTest01(void)
274{
275 uint8_t *buf = (uint8_t *) "GET /one/ HTTP/1.1\r\n"
276 "Host: one.example.org\r\n";
277
278 uint16_t buflen = strlen((char *)buf);
279 Packet *p = NULL;
280 ThreadVars th_v;
281 DetectEngineThreadCtx *det_ctx;
282
283 memset(&th_v, 0, sizeof(th_v));
284 p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
285
287 FAIL_IF(de_ctx == NULL);
288
290
293
294 de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
295 "(msg:\"FastLog test\"; content:\"GET\"; "
296 "Classtype:unknown; sid:1;)");
297
299 DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
300
301 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
302 FAIL_IF_NOT(p->alerts.cnt == 1);
303 FAIL_IF_NOT(strcmp(p->alerts.alerts[0].s->class_msg, "Unknown are we") == 0);
304
307 DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
309
310 UTHFreePackets(&p, 1);
311 PASS;
312}
313
314static int AlertFastLogTest02(void)
315{
316 uint8_t *buf = (uint8_t *) "GET /one/ HTTP/1.1\r\n"
317 "Host: one.example.org\r\n";
318 uint16_t buflen = strlen((char *)buf);
319 Packet *p = NULL;
320 ThreadVars th_v;
321 DetectEngineThreadCtx *det_ctx;
322
323 memset(&th_v, 0, sizeof(th_v));
324
325 p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
326
328 FAIL_IF(de_ctx == NULL);
329
331
334
335 de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
336 "(msg:\"FastLog test\"; content:\"GET\"; "
337 "Classtype:unknown; sid:1;)");
338
340 DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
341
342 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
343 FAIL_IF_NOT(p->alerts.cnt == 1);
344 FAIL_IF_NOT(strcmp(p->alerts.alerts[0].s->class_msg, "Unknown are we") == 0);
345
348 DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
350
351 UTHFreePackets(&p, 1);
352 PASS;
353}
354
355#endif /* UNITTESTS */
356
357/**
358 * \brief This function registers unit tests for AlertFastLog API.
359 */
361{
362
363#ifdef UNITTESTS
364
365 UtRegisterTest("AlertFastLogTest01", AlertFastLogTest01);
366 UtRegisterTest("AlertFastLogTest02", AlertFastLogTest02);
367
368#endif /* UNITTESTS */
369
370}
#define ACTION_DROP
struct AlertFastLogThread_ AlertFastLogThread
#define MODULE_NAME
#define MAX_FASTLOG_ALERT_SIZE
void AlertFastLogRegister(void)
#define MAX_FASTLOG_BUFFER_SIZE
TmEcode AlertFastLogThreadInit(ThreadVars *, const void *, void **)
void AlertFastLogRegisterTests(void)
This function registers unit tests for AlertFastLog API.
#define DEFAULT_LOG_FILENAME
int AlertFastLogger(ThreadVars *tv, void *data, const Packet *p)
OutputInitResult AlertFastLogInitCtx(SCConfNode *conf)
Create a new LogFileCtx for "fast" output style.
TmEcode AlertFastLogThreadDeinit(ThreadVars *, void *)
uint8_t proto
#define GET_IPV6_DST_ADDR(p)
Definition decode.h:204
#define GET_IPV4_SRC_ADDR_PTR(p)
Definition decode.h:198
#define GET_PKT_DATA(p)
Definition decode.h:209
#define GET_IPV6_SRC_ADDR(p)
Definition decode.h:203
#define GET_PKT_LEN(p)
Definition decode.h:208
#define GET_IPV4_DST_ADDR_PTR(p)
Definition decode.h:199
int SigGroupBuild(DetectEngineCtx *de_ctx)
Convert the signature list into the runtime match structure.
void SigCleanSignatures(DetectEngineCtx *de_ctx)
int SigGroupCleanup(DetectEngineCtx *de_ctx)
DetectEngineCtx * DetectEngineCtxInit(void)
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
TmEcode DetectEngineThreadCtxInit(ThreadVars *tv, void *initdata, void **data)
initialize thread specific detection engine context
TmEcode DetectEngineThreadCtxDeinit(ThreadVars *tv, void *data)
Signature * SigInit(DetectEngineCtx *de_ctx, const char *sigstr)
Parses a signature and adds it to the Detection Engine Context.
void SigMatchSignatures(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
wrapper for old tests
Definition detect.c:2420
#define DE_QUIET
Definition detect.h:330
ThreadVars * tv
DetectEngineCtx * de_ctx
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
#define PASS
Pass the test.
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
void OutputRegisterPacketModule(LoggerId id, const char *name, const char *conf_name, OutputInitFunc InitFunc, OutputPacketLoggerFunctions *output_module_functions)
Register a packet output module.
Definition output.c:196
LogFileCtx * file_ctx
main detection engine ctx
Definition detect.h:932
uint8_t flags
Definition detect.h:934
Signature * sig_list
Definition detect.h:941
int(* Write)(const char *buffer, int buffer_len, struct LogFileCtx_ *fp)
void * data
Definition tm-modules.h:91
void(* DeInit)(struct OutputCtx_ *)
Definition tm-modules.h:94
OutputCtx * ctx
Definition output.h:47
const struct Signature_ * s
Definition decode.h:252
uint8_t action
Definition decode.h:250
uint16_t cnt
Definition decode.h:287
PacketAlert * alerts
Definition decode.h:290
struct Packet_::@33::@40 icmp_s
uint64_t pcap_cnt
Definition decode.h:626
SCTime_t ts
Definition decode.h:555
Port sp
Definition decode.h:508
uint8_t code
Definition decode.h:512
uint8_t type
Definition decode.h:511
PacketAlerts alerts
Definition decode.h:620
Port dp
Definition decode.h:516
uint32_t rev
Definition detect.h:715
int prio
Definition detect.h:716
char * class_msg
Definition detect.h:739
uint32_t id
Definition detect.h:713
char * msg
Definition detect.h:736
uint32_t gid
Definition detect.h:714
Per thread variable structure.
Definition threadvars.h:58
@ LOGGER_ALERT_FAST
int EngineModeIsIPS(void)
Definition suricata.c:242
@ TM_ECODE_FAILED
@ TM_ECODE_OK
FILE * SCClassConfGenerateValidDummyClassConfigFD01(void)
Creates a dummy classification file, with all valid Classtypes, for testing purposes.
bool SCClassConfLoadClassificationConfigFile(DetectEngineCtx *de_ctx, FILE *fd)
Loads the Classtype info from the classification.config file.
#define SCLogDebug(...)
Definition util-debug.h:275
int LogFileFreeCtx(LogFileCtx *lf_ctx)
LogFileFreeCtx() Destroy a LogFileCtx (Close the file and free memory)
LogFileCtx * LogFileNewCtx(void)
LogFileNewCtx() Get a new LogFileCtx.
int SCConfLogOpenGeneric(SCConfNode *conf, LogFileCtx *log_ctx, const char *default_filename, int rotate)
open a generic output "log file", which may be a regular file or a socket
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define likely(expr)
#define unlikely(expr)
void PrintBufferRawLineHex(char *nbuf, int *offset, int max_size, const uint8_t *buf, uint32_t buflen)
print a buffer as hex on a single line
Definition util-print.c:44
const char * PrintInet(int af, const void *src, char *dst, socklen_t size)
Definition util-print.c:231
#define PrintBufferData(buf, buf_offset_ptr, buf_size,...)
Definition util-print.h:27
bool SCProtoNameValid(uint16_t proto)
Function to check if the received protocol number is valid and do we have corresponding name entry fo...
const char * known_proto[256]
void CreateTimeString(const SCTime_t ts, char *str, size_t size)
Definition util-time.c:272
void UTHFreePackets(Packet **p, int numpkts)
UTHFreePackets: function to release the allocated data from UTHBuildPacket and the packet itself.
Packet * UTHBuildPacket(uint8_t *payload, uint16_t payload_len, uint8_t ipproto)
UTHBuildPacket is a wrapper that build packets with default ip and port fields.