/src/suricata7/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 | 74 | #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(ConfNode *conf) |
89 | 0 | { |
90 | 0 | OutputInitResult result = { NULL, false }; |
91 | 0 | const char *facility_s = ConfNodeLookupChildValue(conf, "facility"); |
92 | 0 | if (facility_s == NULL) { |
93 | 0 | facility_s = DEFAULT_ALERT_SYSLOG_FACILITY_STR; |
94 | 0 | } |
95 | |
|
96 | 0 | LogFileCtx *logfile_ctx = LogFileNewCtx(); |
97 | 0 | if (logfile_ctx == NULL) { |
98 | 0 | SCLogDebug("AlertSyslogInitCtx: Could not create new LogFileCtx"); |
99 | 0 | return result; |
100 | 0 | } |
101 | | |
102 | 0 | int facility = SCMapEnumNameToValue(facility_s, SCSyslogGetFacilityMap()); |
103 | 0 | if (facility == -1) { |
104 | 0 | SCLogWarning("Invalid syslog facility: \"%s\"," |
105 | 0 | " now using \"%s\" as syslog facility", |
106 | 0 | facility_s, DEFAULT_ALERT_SYSLOG_FACILITY_STR); |
107 | 0 | facility = DEFAULT_ALERT_SYSLOG_FACILITY; |
108 | 0 | } |
109 | |
|
110 | 0 | const char *level_s = ConfNodeLookupChildValue(conf, "level"); |
111 | 0 | if (level_s != NULL) { |
112 | 0 | int level = SCMapEnumNameToValue(level_s, SCSyslogGetLogLevelMap()); |
113 | 0 | if (level != -1) { |
114 | 0 | alert_syslog_level = level; |
115 | 0 | } |
116 | 0 | } |
117 | |
|
118 | 0 | const char *ident = ConfNodeLookupChildValue(conf, "identity"); |
119 | | /* if null we just pass that to openlog, which will then |
120 | | * figure it out by itself. */ |
121 | |
|
122 | 0 | openlog(ident, LOG_PID|LOG_NDELAY, facility); |
123 | |
|
124 | 0 | OutputCtx *output_ctx = SCMalloc(sizeof(OutputCtx)); |
125 | 0 | if (unlikely(output_ctx == NULL)) { |
126 | 0 | SCLogDebug("could not create new OutputCtx"); |
127 | 0 | LogFileFreeCtx(logfile_ctx); |
128 | 0 | return result; |
129 | 0 | } |
130 | 0 | memset(output_ctx, 0x00, sizeof(OutputCtx)); |
131 | |
|
132 | 0 | output_ctx->data = logfile_ctx; |
133 | 0 | output_ctx->DeInit = AlertSyslogDeInitCtx; |
134 | |
|
135 | 0 | SCLogInfo("Syslog output initialized"); |
136 | |
|
137 | 0 | result.ctx = output_ctx; |
138 | 0 | result.ok = true; |
139 | 0 | return result; |
140 | 0 | } |
141 | | |
142 | | /** |
143 | | * \brief Function to initialize the AlertSyslogThread and sets the output |
144 | | * context pointer |
145 | | * |
146 | | * \param tv Pointer to the threadvars |
147 | | * \param initdata Pointer to the output context |
148 | | * \param data pointer to pointer to point to the AlertSyslogThread |
149 | | */ |
150 | | static TmEcode AlertSyslogThreadInit(ThreadVars *t, const void *initdata, void **data) |
151 | 0 | { |
152 | 0 | if(initdata == NULL) { |
153 | 0 | SCLogDebug("Error getting context for AlertSyslog. \"initdata\" " |
154 | 0 | "argument NULL"); |
155 | 0 | return TM_ECODE_FAILED; |
156 | 0 | } |
157 | | |
158 | 0 | AlertSyslogThread *ast = SCMalloc(sizeof(AlertSyslogThread)); |
159 | 0 | if (unlikely(ast == NULL)) |
160 | 0 | return TM_ECODE_FAILED; |
161 | | |
162 | 0 | memset(ast, 0, sizeof(AlertSyslogThread)); |
163 | | |
164 | | /** Use the Output Context (file pointer and mutex) */ |
165 | 0 | ast->file_ctx = ((OutputCtx *)initdata)->data; |
166 | |
|
167 | 0 | *data = (void *)ast; |
168 | 0 | return TM_ECODE_OK; |
169 | 0 | } |
170 | | |
171 | | /** |
172 | | * \brief Function to deinitialize the AlertSyslogThread |
173 | | * |
174 | | * \param tv Pointer to the threadvars |
175 | | * \param data pointer to the AlertSyslogThread to be cleared |
176 | | */ |
177 | | static TmEcode AlertSyslogThreadDeinit(ThreadVars *t, void *data) |
178 | 0 | { |
179 | 0 | AlertSyslogThread *ast = (AlertSyslogThread *)data; |
180 | 0 | if (ast == NULL) { |
181 | 0 | return TM_ECODE_OK; |
182 | 0 | } |
183 | | |
184 | | /* clear memory */ |
185 | 0 | memset(ast, 0, sizeof(AlertSyslogThread)); |
186 | |
|
187 | 0 | SCFree(ast); |
188 | 0 | return TM_ECODE_OK; |
189 | 0 | } |
190 | | |
191 | | /** |
192 | | * \brief Function which is called to print the IPv4 alerts to the syslog |
193 | | * |
194 | | * \param tv Pointer to the threadvars |
195 | | * \param p Pointer to the packet |
196 | | * \param data pointer to the AlertSyslogThread |
197 | | * |
198 | | * \return On succes return TM_ECODE_OK |
199 | | */ |
200 | | static TmEcode AlertSyslogIPv4(ThreadVars *tv, const Packet *p, void *data) |
201 | 0 | { |
202 | 0 | AlertSyslogThread *ast = (AlertSyslogThread *)data; |
203 | 0 | int i; |
204 | 0 | const char *action = ""; |
205 | |
|
206 | 0 | if (p->alerts.cnt == 0) |
207 | 0 | return TM_ECODE_OK; |
208 | | |
209 | 0 | char proto[16] = ""; |
210 | 0 | const char *protoptr; |
211 | 0 | if (SCProtoNameValid(IPV4_GET_IPPROTO(p))) { |
212 | 0 | protoptr = known_proto[IPV4_GET_IPPROTO(p)]; |
213 | 0 | } else { |
214 | 0 | snprintf(proto, sizeof(proto), "PROTO:%03" PRIu32, IPV4_GET_IPPROTO(p)); |
215 | 0 | protoptr = proto; |
216 | 0 | } |
217 | | |
218 | | /* Not sure if this mutex is needed around calls to syslog. */ |
219 | 0 | SCMutexLock(&ast->file_ctx->fp_mutex); |
220 | |
|
221 | 0 | for (i = 0; i < p->alerts.cnt; i++) { |
222 | 0 | const PacketAlert *pa = &p->alerts.alerts[i]; |
223 | 0 | if (unlikely(pa->s == NULL)) { |
224 | 0 | continue; |
225 | 0 | } |
226 | | |
227 | 0 | char srcip[16], dstip[16]; |
228 | |
|
229 | 0 | PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip)); |
230 | 0 | PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip)); |
231 | |
|
232 | 0 | if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) { |
233 | 0 | action = "[Drop] "; |
234 | 0 | } else if (pa->action & ACTION_DROP) { |
235 | 0 | action = "[wDrop] "; |
236 | 0 | } |
237 | |
|
238 | 0 | syslog(alert_syslog_level, "%s[%" PRIu32 ":%" PRIu32 ":%" |
239 | 0 | PRIu32 "] %s [Classification: %s] [Priority: %"PRIu32"]" |
240 | 0 | " {%s} %s:%" PRIu32 " -> %s:%" PRIu32 "", action, pa->s->gid, |
241 | 0 | pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg, pa->s->prio, |
242 | 0 | protoptr, srcip, p->sp, dstip, p->dp); |
243 | 0 | } |
244 | 0 | SCMutexUnlock(&ast->file_ctx->fp_mutex); |
245 | |
|
246 | 0 | return TM_ECODE_OK; |
247 | 0 | } |
248 | | |
249 | | /** |
250 | | * \brief Function which is called to print the IPv6 alerts to the syslog |
251 | | * |
252 | | * \param tv Pointer to the threadvars |
253 | | * \param p Pointer to the packet |
254 | | * \param data pointer to the AlertSyslogThread |
255 | | * |
256 | | * \return On succes return TM_ECODE_OK |
257 | | */ |
258 | | static TmEcode AlertSyslogIPv6(ThreadVars *tv, const Packet *p, void *data) |
259 | 0 | { |
260 | 0 | AlertSyslogThread *ast = (AlertSyslogThread *)data; |
261 | 0 | int i; |
262 | 0 | const char *action = ""; |
263 | |
|
264 | 0 | if (p->alerts.cnt == 0) |
265 | 0 | return TM_ECODE_OK; |
266 | | |
267 | 0 | char proto[16] = ""; |
268 | 0 | const char *protoptr; |
269 | 0 | if (SCProtoNameValid(IPV6_GET_L4PROTO(p))) { |
270 | 0 | protoptr = known_proto[IPV6_GET_L4PROTO(p)]; |
271 | 0 | } else { |
272 | 0 | snprintf(proto, sizeof(proto), "PROTO:03%" PRIu32, IPV6_GET_L4PROTO(p)); |
273 | 0 | protoptr = proto; |
274 | 0 | } |
275 | |
|
276 | 0 | SCMutexLock(&ast->file_ctx->fp_mutex); |
277 | |
|
278 | 0 | for (i = 0; i < p->alerts.cnt; i++) { |
279 | 0 | const PacketAlert *pa = &p->alerts.alerts[i]; |
280 | 0 | if (unlikely(pa->s == NULL)) { |
281 | 0 | continue; |
282 | 0 | } |
283 | | |
284 | 0 | char srcip[46], dstip[46]; |
285 | |
|
286 | 0 | PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip)); |
287 | 0 | PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip)); |
288 | |
|
289 | 0 | if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) { |
290 | 0 | action = "[Drop] "; |
291 | 0 | } else if (pa->action & ACTION_DROP) { |
292 | 0 | action = "[wDrop] "; |
293 | 0 | } |
294 | |
|
295 | 0 | syslog(alert_syslog_level, "%s[%" PRIu32 ":%" PRIu32 ":%" |
296 | 0 | "" PRIu32 "] %s [Classification: %s] [Priority: %" |
297 | 0 | "" PRIu32 "] {%s} %s:%" PRIu32 " -> %s:%" PRIu32 "", |
298 | 0 | action, pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg, |
299 | 0 | pa->s->prio, protoptr, srcip, p->sp, |
300 | 0 | dstip, p->dp); |
301 | |
|
302 | 0 | } |
303 | 0 | SCMutexUnlock(&ast->file_ctx->fp_mutex); |
304 | |
|
305 | 0 | return TM_ECODE_OK; |
306 | 0 | } |
307 | | |
308 | | /** |
309 | | * \brief Function which is called to print the decode alerts to the syslog |
310 | | * |
311 | | * \param tv Pointer to the threadvars |
312 | | * \param p Pointer to the packet |
313 | | * \param data pointer to the AlertSyslogThread |
314 | | * |
315 | | * \return On succes return TM_ECODE_OK |
316 | | */ |
317 | | static TmEcode AlertSyslogDecoderEvent(ThreadVars *tv, const Packet *p, void *data) |
318 | 0 | { |
319 | 0 | AlertSyslogThread *ast = (AlertSyslogThread *)data; |
320 | 0 | int i; |
321 | 0 | const char *action = ""; |
322 | |
|
323 | 0 | if (p->alerts.cnt == 0) |
324 | 0 | return TM_ECODE_OK; |
325 | | |
326 | 0 | SCMutexLock(&ast->file_ctx->fp_mutex); |
327 | |
|
328 | 0 | char temp_buf_hdr[512]; |
329 | 0 | char temp_buf_pkt[65] = ""; |
330 | 0 | char temp_buf_tail[64]; |
331 | 0 | char alert[2048] = ""; |
332 | |
|
333 | 0 | for (i = 0; i < p->alerts.cnt; i++) { |
334 | 0 | const PacketAlert *pa = &p->alerts.alerts[i]; |
335 | 0 | if (unlikely(pa->s == NULL)) { |
336 | 0 | continue; |
337 | 0 | } |
338 | | |
339 | 0 | if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) { |
340 | 0 | action = "[Drop] "; |
341 | 0 | } else if (pa->action & ACTION_DROP) { |
342 | 0 | action = "[wDrop] "; |
343 | 0 | } |
344 | |
|
345 | 0 | snprintf(temp_buf_hdr, sizeof(temp_buf_hdr), "%s[%" PRIu32 ":%" PRIu32 |
346 | 0 | ":%" PRIu32 "] %s [Classification: %s] [Priority: %" PRIu32 |
347 | 0 | "] [**] [Raw pkt: ", action, pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg, |
348 | 0 | pa->s->class_msg, pa->s->prio); |
349 | 0 | strlcpy(alert, temp_buf_hdr, sizeof(alert)); |
350 | |
|
351 | 0 | PrintRawLineHexBuf(temp_buf_pkt, sizeof(temp_buf_pkt), GET_PKT_DATA(p), GET_PKT_LEN(p) < 32 ? GET_PKT_LEN(p) : 32); |
352 | 0 | strlcat(alert, temp_buf_pkt, sizeof(alert)); |
353 | |
|
354 | 0 | if (p->pcap_cnt != 0) { |
355 | 0 | snprintf(temp_buf_tail, sizeof(temp_buf_tail), "] [pcap file packet: %"PRIu64"]", |
356 | 0 | p->pcap_cnt); |
357 | 0 | } else { |
358 | 0 | temp_buf_tail[0] = ']'; |
359 | 0 | temp_buf_tail[1] = '\0'; |
360 | 0 | } |
361 | 0 | strlcat(alert, temp_buf_tail, sizeof(alert)); |
362 | |
|
363 | 0 | syslog(alert_syslog_level, "%s", alert); |
364 | 0 | } |
365 | 0 | SCMutexUnlock(&ast->file_ctx->fp_mutex); |
366 | |
|
367 | 0 | return TM_ECODE_OK; |
368 | 0 | } |
369 | | |
370 | | static int AlertSyslogCondition(ThreadVars *tv, void *thread_data, const Packet *p) |
371 | 0 | { |
372 | 0 | return (p->alerts.cnt > 0 ? TRUE : FALSE); |
373 | 0 | } |
374 | | |
375 | | static int AlertSyslogLogger(ThreadVars *tv, void *thread_data, const Packet *p) |
376 | 0 | { |
377 | 0 | if (PKT_IS_IPV4(p)) { |
378 | 0 | return AlertSyslogIPv4(tv, p, thread_data); |
379 | 0 | } else if (PKT_IS_IPV6(p)) { |
380 | 0 | return AlertSyslogIPv6(tv, p, thread_data); |
381 | 0 | } else if (p->events.cnt > 0) { |
382 | 0 | return AlertSyslogDecoderEvent(tv, p, thread_data); |
383 | 0 | } |
384 | | |
385 | 0 | return TM_ECODE_OK; |
386 | 0 | } |
387 | | |
388 | | #endif /* !OS_WIN32 */ |
389 | | |
390 | | /** \brief Function to register the AlertSyslog module */ |
391 | | void AlertSyslogRegister (void) |
392 | 74 | { |
393 | 74 | #ifndef OS_WIN32 |
394 | 74 | OutputRegisterPacketModule(LOGGER_ALERT_SYSLOG, MODULE_NAME, "syslog", |
395 | 74 | AlertSyslogInitCtx, AlertSyslogLogger, AlertSyslogCondition, |
396 | | AlertSyslogThreadInit, AlertSyslogThreadDeinit, NULL); |
397 | 74 | #endif /* !OS_WIN32 */ |
398 | 74 | } |