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 
23 typedef 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  */
40 struct 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 
51 static unsigned int     nrec = 0;    /* size of array               */
52 static unsigned int     urec = 0;    /* in use thereof              */
53 static struct gestalt * arec = NULL; /* array                       */
54 
55 static int      repeat_count = 24;   /* triggers report             */
56 static int     clean_counter = 0;    /* cleanup after N inserts     */
57 static int        free_slots = 0;    /* free slots available        */
58 
59 #define SH_CLEANUP 256
60 
61 void * sh_dummy_g_array     = NULL;
62 
add_entry(unsigned char * flags,UINT16 * sum,time_t ltime)63 static 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 
shift_history(unsigned char * hist,unsigned int shift,UINT16 total)113 static 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 
update_entry(struct gestalt * array,time_t ltime)138 static 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 
update_or_add(unsigned char * flags,UINT16 * sum,time_t ltime)160 static 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) */
232 UINT16 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 
crc16_byte(UINT16 crc,const unsigned char data)267 static 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  */
crc16(UINT16 crc,const unsigned char * buffer,size_t len)280 static 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 
classify(char ** splits,size_t * lengths,unsigned int nfields,unsigned char * flags,UINT16 * sums)293 static 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 
cleanup_array(time_t ltime)413 static 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 
sh_repeat_set_trigger(const char * str)449 int 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 
463 static char * sh_repeat_queue = NULL;
464 
sh_repeat_set_queue(const char * str)465 int 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 
475 static int sh_repeat_cron = S_FALSE;
476 
sh_repeat_set_cron(const char * str)477 int sh_repeat_set_cron (const char * str)
478 {
479   return sh_util_flagval(str, &sh_repeat_cron);
480 }
481 
sh_repeat_message_check(const sh_string * host,const sh_string * msg,time_t ltime)482 int 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
573