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