/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 | } |