source: trunk/src/sh_log_repeat.c@ 548

Last change on this file since 548 was 516, checked in by katerina, 8 years ago

Fix for ticket #409 and #410 (unprivileged suidcheck and gcc 6.2 compiler warnings).

File size: 13.6 KB
Line 
1#include "config_xor.h"
2
3#ifdef USE_LOGFILE_MONITOR
4
5#include <string.h>
6#include <ctype.h>
7
8#undef FIL__
9#define FIL__ _("sh_log_repeat.c")
10
11#include "samhain.h"
12#include "sh_pthread.h"
13#include "sh_utils.h"
14#include "sh_string.h"
15#include "sh_log_check.h"
16#include "sh_log_evalrule.h"
17
18#define SH_NHIST 12
19#define SH_NFINT 5
20#define SH_NFIELDS 5*sizeof(SINT32) /* 16 */
21#define SH_NBLOCK 63
22
23typedef enum
24{
25 SH_GFLAG_EMAIL = 1 << 0,
26 SH_GFLAG_PATH = 1 << 1,
27 SH_GFLAG_IP = 1 << 2,
28 SH_GFLAG_FQDN = 1 << 3,
29 SH_GFLAG_NUM = 1 << 4,
30 SH_GFLAG_ELSE = 1 << 5,
31
32 SH_GFLAG_XNUM = 1 << 6,
33 SH_GFLAG_CHAR = 1 << 7,
34 SH_GFLAG_USC = 1 << 8
35} SH_GFlags;
36
37
38/* 64 bytes
39 */
40struct gestalt {
41 unsigned char hist[SH_NHIST]; /* time histogram 12 minutes */
42 union {
43 unsigned char flags[SH_NFIELDS]; /* flags indicating field type */
44 SINT32 flint[SH_NFINT];
45 } f;
46 UINT16 sum[SH_NFIELDS]; /* checksum of field */
47 UINT16 ltime; /* last time, in minutes */
48 UINT16 total; /* seen how often? */
49};
50
51static unsigned int nrec = 0; /* size of array */
52static unsigned int urec = 0; /* in use thereof */
53static struct gestalt * arec = NULL; /* array */
54
55static int repeat_count = 24; /* triggers report */
56static int clean_counter = 0; /* cleanup after N inserts */
57static int free_slots = 0; /* free slots available */
58
59#define SH_CLEANUP 256
60
61void * sh_dummy_g_array = NULL;
62
63static struct gestalt * add_entry (unsigned char * flags, UINT16 * sum,
64 time_t ltime)
65{
66 struct gestalt * array = NULL;
67
68 sh_dummy_g_array = (void*) &array;
69
70 start:
71 if (urec < nrec)
72 {
73 if (free_slots)
74 {
75 unsigned int i;
76 for (i = 0; i < urec; ++i)
77 {
78 if (arec[i].total == 0)
79 {
80 array = &arec[i];
81 --free_slots;
82 break;
83 }
84 }
85 }
86
87 if (!array)
88 {
89 array = &arec[urec];
90 ++urec;
91 }
92
93 memcpy(array->sum, sum, sizeof(UINT16) * SH_NFIELDS);
94 memcpy(array->f.flags, flags, sizeof(unsigned char) * SH_NFIELDS);
95 memset(array->hist, 0, sizeof(unsigned char) * SH_NHIST);
96
97 array->ltime = (UINT16)(ltime % 60);
98 array->hist[SH_NHIST-1] = 1;
99 array->total = 1;
100
101 ++clean_counter;
102 return array;
103 }
104
105 array = SH_ALLOC(sizeof(struct gestalt) * (nrec + SH_NBLOCK + 1));
106 memset(array, 0, sizeof(struct gestalt) * (nrec + SH_NBLOCK + 1));
107 memcpy(array, arec, sizeof(struct gestalt) * (nrec));
108
109 nrec += (SH_NBLOCK + 1);
110 goto start;
111}
112
113static UINT16 shift_history(unsigned char * hist, unsigned int shift,
114 UINT16 total)
115{
116 unsigned int i, j = 0;
117
118 if (shift >= SH_NHIST)
119 {
120 memset(hist, 0, sizeof(unsigned char) * SH_NHIST);
121 return 0;
122 }
123
124 for (i = shift; i < SH_NHIST; ++i)
125 {
126 if (j < shift)
127 total -= hist[j];
128 hist[j] = hist[i];
129 ++j;
130 }
131 for (i = (SH_NHIST-shift); i < SH_NHIST; ++i)
132 {
133 hist[i] = 0;
134 }
135 return total;
136}
137
138static void update_entry (struct gestalt * array, time_t ltime)
139{
140 UINT16 ntime = (UINT16)(ltime % 60);
141
142 if (array->ltime == ntime)
143 {
144 if (array->hist[SH_NHIST-1] < 255)
145 {
146 ++(array->hist[SH_NHIST-1]);
147 ++(array->total);
148 }
149 }
150 else if (array->ltime < ntime)
151 {
152 unsigned int shift = ntime - array->ltime;
153 array->total = shift_history(array->hist, shift, array->total);
154 array->hist[SH_NHIST-1] = 1;
155 array->ltime = ntime;
156 ++(array->total);
157 }
158}
159
160static struct gestalt * update_or_add (unsigned char * flags, UINT16 * sum,
161 time_t ltime)
162{
163 SINT32 flint[SH_NFINT];
164 start:
165
166 if (arec)
167 {
168 unsigned int i;
169 struct gestalt * array = arec;
170
171 sh_dummy_g_array = (void*) &array;
172
173 memcpy(flint, flags, SH_NFIELDS);
174
175 for (i = 0; i < urec; ++i)
176 {
177 /* Check whether field types match. Integer
178 * comparison is much faster than memcmp() [tested].
179 */
180 if (flint[0] == array->f.flint[0] &&
181 flint[1] == array->f.flint[1] &&
182 flint[2] == array->f.flint[2] &&
183 flint[3] == array->f.flint[3] &&
184 flint[4] == array->f.flint[4])
185 {
186 unsigned int j;
187 int c1 = 0, c2 = 0;
188 UINT16 * asum = array->sum;
189
190 for (j = 0; j < SH_NFIELDS; ++j)
191 {
192 if (flags[j] == SH_GFLAG_ELSE)
193 {
194 ++c1;
195 if (asum[j] == sum[j])
196 ++c2;
197 }
198 }
199
200 if (c1 == c2)
201 {
202 /* Found a matching entry, update time histogram
203 */
204 update_entry (array, ltime);
205 return array;
206 }
207 }
208 ++array;
209 }
210
211 /* No match found, create a new entry
212 */
213 array = add_entry (flags, sum, ltime);
214 return array;
215 }
216
217 arec = SH_ALLOC(sizeof(struct gestalt) * SH_NBLOCK);
218 nrec = SH_NBLOCK;
219 urec = 0;
220
221 goto start;
222}
223
224/* --------------------------------------------------------------------
225 *
226 * crc16 checksum from the linux kernel.
227 * This source code is licensed under the GNU General Public License,
228 * Version 2.
229 */
230
231/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */
232UINT16 const crc16_table[256] = {
233 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
234 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
235 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
236 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
237 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
238 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
239 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
240 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
241 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
242 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
243 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
244 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
245 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
246 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
247 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
248 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
249 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
250 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
251 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
252 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
253 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
254 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
255 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
256 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
257 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
258 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
259 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
260 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
261 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
262 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
263 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
264 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
265};
266
267static inline UINT16 crc16_byte(UINT16 crc, const unsigned char data)
268{
269 return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff];
270}
271
272/**
273 * crc16 - compute the CRC-16 for the data buffer
274 * @crc: previous CRC value
275 * @buffer: data pointer
276 * @len: number of bytes in the buffer
277 *
278 * Returns the updated CRC value.
279 */
280static inline UINT16 crc16(UINT16 crc, const unsigned char * buffer,
281 size_t len)
282{
283 while (len--)
284 crc = crc16_byte(crc, *buffer++);
285 return crc;
286}
287
288
289/* end crc16 code
290 *
291 * -------------------------------------------------------------------- */
292
293static void classify(char ** splits, size_t * lengths, unsigned int nfields,
294 unsigned char * flags, UINT16 * sums)
295{
296 unsigned int i;
297 unsigned int flag;
298
299 /* flags we don't want to see in XYZ
300 */
301 static int m_ip = SH_GFLAG_PATH|SH_GFLAG_EMAIL|SH_GFLAG_USC|SH_GFLAG_ELSE|SH_GFLAG_CHAR|SH_GFLAG_XNUM;
302 static int m_num = SH_GFLAG_PATH|SH_GFLAG_EMAIL|SH_GFLAG_USC|SH_GFLAG_ELSE|SH_GFLAG_CHAR;
303 static int m_fqdn = SH_GFLAG_PATH|SH_GFLAG_EMAIL|SH_GFLAG_USC|SH_GFLAG_ELSE;
304 static int m_email = SH_GFLAG_PATH;
305
306 nfields = (nfields > SH_NFIELDS) ? SH_NFIELDS : nfields;
307
308 for (i = 0; i < nfields; ++i)
309 {
310 char *p = splits[i];
311 unsigned int np = 0;
312 unsigned int fqdn = 0;
313
314 flag = 0;
315
316 while (*p)
317 {
318 if (isxdigit((unsigned int)*p))
319 {
320 if (isdigit((unsigned int)*p))
321 {
322 flag |= SH_GFLAG_NUM;
323 }
324 else
325 {
326 flag |= SH_GFLAG_XNUM;
327 }
328 }
329 else if (*p == '.')
330 {
331 flag |= SH_GFLAG_IP;
332 ++np;
333 }
334 else if (*p == '/')
335 {
336 flag |= SH_GFLAG_PATH;
337 }
338 else if (*p == '@')
339 {
340 flag |= SH_GFLAG_EMAIL;
341 }
342 else if (*p == '-')
343 {
344 flag |= SH_GFLAG_FQDN;
345 }
346 else if (*p == '_')
347 {
348 flag |= SH_GFLAG_USC;
349 }
350 else if (isalpha((unsigned int)*p))
351 {
352 if (flag & SH_GFLAG_IP)
353 ++fqdn;
354 flag |= SH_GFLAG_CHAR;
355 }
356 else if (!isascii((unsigned int)*p))
357 {
358 flags[i] = SH_GFLAG_ELSE;
359 break;
360 }
361 else
362 {
363 flag |= SH_GFLAG_ELSE;
364 }
365 ++p;
366 }
367
368 if (flags[i] == 0)
369 {
370 if (0 == (flag & m_ip) &&
371 0 != (flag & SH_GFLAG_IP) &&
372 0 != (flag & SH_GFLAG_NUM) &&
373 np > 2)
374 {
375 flags[i] = SH_GFLAG_IP;
376 }
377 else if (0 == (flag & m_num) &&
378 (0 != (flag & SH_GFLAG_NUM) || 0 != (flag & SH_GFLAG_XNUM)))
379 {
380 flags[i] = SH_GFLAG_NUM;
381 }
382 else if (0 == (flag & m_fqdn) &&
383 0 != (flag & SH_GFLAG_IP) &&
384 0 != (flag & SH_GFLAG_CHAR) &&
385 fqdn)
386 {
387 flags[i] = SH_GFLAG_FQDN;
388 }
389 else if ('/' == splits[i][0])
390 {
391 flags[i] = SH_GFLAG_PATH;
392 }
393 else if (0 == (flag & m_email) &&
394 0 != (flag & SH_GFLAG_EMAIL) &&
395 0 != (flag & SH_GFLAG_CHAR))
396 {
397 flags[i] = SH_GFLAG_EMAIL;
398 }
399 else
400 {
401 flags[i] = SH_GFLAG_ELSE;
402 }
403 }
404
405 /* CRC-16 checksum
406 */
407 sums[i] = crc16(0, (unsigned char *) splits[i], lengths[i]);
408 }
409
410 return;
411}
412
413static void cleanup_array (time_t ltime)
414{
415 UINT16 ntime = (UINT16)(ltime % 60);
416
417 if (ntime > 12) ntime -= 12;
418
419 if (arec && urec > 0)
420 {
421 struct gestalt * array;
422 unsigned int i, last, urec_orig = urec;
423
424 last = urec-1;
425 array = &arec[0];
426
427 for (i = 0; i < urec_orig; ++i)
428 {
429 if (array->ltime < ntime)
430 {
431 memset(array, 0, sizeof(struct gestalt));
432 if (i != last)
433 ++free_slots;
434 else
435 --urec;
436 }
437 }
438 ++array;
439 }
440 clean_counter = 0;
441 return;
442}
443
444/* ----------------------------------------------------------------------
445 *
446 * Public functions
447 */
448
449int sh_repeat_set_trigger (const char * str)
450{
451 unsigned long value;
452 char * foo;
453
454 value = (size_t) strtoul(str, &foo, 0);
455
456 if (*foo == '\0' && value < 65535) {
457 repeat_count = value;
458 return 0;
459 }
460 return -1;
461}
462
463static char * sh_repeat_queue = NULL;
464
465int sh_repeat_set_queue (const char * str)
466{
467 if (!str)
468 return -1;
469 if (sh_repeat_queue)
470 SH_FREE(sh_repeat_queue);
471 sh_repeat_queue = sh_util_strdup(str);
472 return 0;
473}
474
475static int sh_repeat_cron = S_FALSE;
476
477int sh_repeat_set_cron (const char * str)
478{
479 return sh_util_flagval(str, &sh_repeat_cron);
480}
481
482int sh_repeat_message_check (const sh_string * host,
483 const sh_string * msg,
484 time_t ltime)
485{
486 struct gestalt * array;
487
488 UINT16 sums[SH_NFIELDS] = { 0 };
489 unsigned char flags[SH_NFIELDS] = { 0 };
490
491 /* split message into SH_NFIELDS+1, discard last */
492
493 unsigned int nfields = SH_NFIELDS+1;
494 size_t lengths[SH_NFIELDS+1];
495 char * new;
496 char ** splits;
497
498 if (repeat_count == 0)
499 return 0;
500
501 if (sh_repeat_cron == S_FALSE)
502 {
503 char * s = sh_string_str(msg);
504
505 if (0 == strcmp(s, _("cron")) || 0 == strcmp(s, _("CRON")))
506 return 0;
507 }
508
509 new = sh_util_strdup_l(sh_string_str(msg), sh_string_len(msg));
510
511 splits = split_array_token (new, &nfields, lengths,
512 " :,()='[]<>\t\n");
513
514 /* classify fields */
515
516 classify (splits, lengths, nfields, flags, sums);
517
518 /* compare */
519
520 array = update_or_add (flags, sums, ltime);
521
522 /* report */
523
524 if (array->total > repeat_count)
525 {
526 volatile int repeat = array->total;
527 char * tmpmsg;
528 char * tmphost;
529 sh_string * alias;
530
531 /* issue report */
532
533 SH_MUTEX_LOCK(mutex_thread_nolog);
534 tmphost = sh_util_safe_name (sh_string_str(host));
535 tmpmsg = sh_util_safe_name_keepspace (sh_string_str(msg));
536 sh_error_handle (sh_log_lookup_severity(sh_repeat_queue),
537 FIL__, __LINE__, 0, MSG_LOGMON_BURST,
538 repeat, tmpmsg, tmphost);
539 alias = sh_log_lookup_alias(sh_repeat_queue);
540 if (alias)
541 {
542 sh_error_mail (sh_string_str(alias),
543 sh_log_lookup_severity(sh_repeat_queue),
544 FIL__, __LINE__, 0, MSG_LOGMON_BURST,
545 repeat, tmpmsg, tmphost);
546 }
547 SH_FREE(tmpmsg);
548 SH_FREE(tmphost);
549 SH_MUTEX_UNLOCK(mutex_thread_nolog);
550
551 /* mark slot as free */
552
553 memset(array, 0, sizeof(struct gestalt));
554 if (array != &arec[urec-1])
555 ++free_slots;
556 else
557 urec -= 1;
558 }
559
560 SH_FREE(new);
561
562 /* run cleanup routine */
563
564 if (clean_counter >= SH_CLEANUP)
565 {
566 cleanup_array(ltime);
567 }
568
569 return 0;
570}
571
572#endif
Note: See TracBrowser for help on using the repository browser.