new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / analysisd / cleanevent.c
1 /* Copyright (C) 2009 Trend Micro Inc.
2  * All rights reserved.
3  *
4  * This program is a free software; you can redistribute it
5  * and/or modify it under the terms of the GNU General Public
6  * License (version 2) as published by the FSF - Free Software
7  * Foundation.
8  */
9
10 #include "cleanevent.h"
11
12 #include "shared.h"
13 #include "os_regex/os_regex.h"
14 #include "analysisd.h"
15 #include "fts.h"
16 #include "config.h"
17
18 /* To translate between month (int) to month (char) */
19 static const char *(month[]) = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
20                    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
21                   };
22
23
24 /* Format a received message in the Eventinfo structure */
25 int OS_CleanMSG(char *msg, Eventinfo *lf)
26 {
27     size_t loglen;
28     char *pieces;
29     struct tm *p;
30
31     /* The message is formatted in the following way:
32      * id:location:message.
33      */
34
35     /* Ignore the id of the message in here */
36     msg += 2;
37
38     /* Set pieces as the message */
39     pieces = strchr(msg, ':');
40     if (!pieces) {
41         merror(FORMAT_ERROR, ARGV0);
42         return (-1);
43     }
44
45     /* Is this from an agent? */
46     if ( *msg == '(' )
47     {   /* look past '->' for the first ':' */
48         pieces = strchr(strstr(msg, "->"), ':');
49         if(!pieces)
50         {
51             merror(FORMAT_ERROR, ARGV0);
52             return(-1);
53         }
54     }
55
56     *pieces = '\0';
57     pieces++;
58
59     os_strdup(msg, lf->location);
60
61     /* Get the log length */
62     loglen = strlen(pieces) + 1;
63
64     /* Assign the values in the structure (lf->full_log) */
65     os_malloc((2 * loglen) + 1, lf->full_log);
66
67     /* Set the whole message at full_log */
68     strncpy(lf->full_log, pieces, loglen);
69
70     /* Log is the one used for parsing in the decoders and rules */
71     lf->log = lf->full_log + loglen;
72     strncpy(lf->log, pieces, loglen);
73
74     /* check if month contains an umlaut and repair
75      * umlauts are non-ASCII and use 2 slots in the char array
76      * repair to only one slot so we can detect the correct date format in the next step
77      * ex: Mär 02 17:30:52
78      */
79     if (pieces[1] == (char) 195) {
80         if (pieces[2] == (char) 164) {
81             pieces[0] = '\0';
82             pieces[1] = 'M';
83             pieces[2] = 'a';
84             pieces++;
85         }
86     }
87
88     /* Check for the syslog date format
89      * ( ex: Dec 29 10:00:01
90      *   or  2015-04-16 21:51:02,805 for proftpd 1.3.5
91      *   or  2007-06-14T15:48:55-04:00 for syslog-ng isodate
92      *   or  2007-06-14T15:48:55.3352-04:00 for syslog-ng isodate with up to 6 optional fraction of a second
93      *   or  2009-05-22T09:36:46.214994-07:00 for rsyslog
94      *   or  2015 Dec 29 10:00:01 )
95      */
96     if (
97         (   /* ex: Dec 29 10:00:01 */
98             (loglen > 17) &&
99             (pieces[3] == ' ') &&
100             (pieces[6] == ' ') &&
101             (pieces[9] == ':') &&
102             (pieces[12] == ':') &&
103             (pieces[15] == ' ') && (lf->log += 16)
104         )
105         ||
106         (   /* ex: 2015-04-16 21:51:02,805 */
107             (loglen > 24) &&
108             (pieces[4] == '-') &&
109             (pieces[7] == '-') &&
110             (pieces[10] == ' ') &&
111             (pieces[13] == ':') &&
112             (pieces[16] == ':') &&
113             (pieces[19] == ',') &&
114             (lf->log += 23)
115         )
116         ||
117         (
118             (loglen > 33) &&
119             (pieces[4] == '-') &&
120             (pieces[7] == '-') &&
121             (pieces[10] == 'T') &&
122             (pieces[13] == ':') &&
123             (pieces[16] == ':') &&
124             (   /* ex: 2007-06-14T15:48:55-04:00 */
125                 (
126                     (pieces[22] == ':') &&
127                     (pieces[25] == ' ') && (lf->log += 26)
128                 )
129                 ||
130                 /* ex: 2007-06-14T15:48:55.3-04:00 or 2009-05-22T09:36:46,214994-07:00 */
131                 (
132                     (
133                         (pieces[19] == '.') || (pieces[19] == ',')
134                     )
135                     &&
136                     (
137                         ( (pieces[24] == ':') && (lf->log += 27) ) ||
138                         ( (pieces[25] == ':') && (lf->log += 28) ) ||
139                         ( (pieces[26] == ':') && (lf->log += 29) ) ||
140                         ( (pieces[27] == ':') && (lf->log += 30) ) ||
141                         ( (pieces[28] == ':') && (lf->log += 31) ) ||
142                         ( (pieces[29] == ':') && (lf->log += 32) )
143                     )
144                 )
145             )
146         )
147         ||
148         (   /* ex: 2015 Dec 29 10:00:01 */
149             (loglen > 21) &&
150             (isdigit(pieces[0])) &&
151             (pieces[4] == ' ') &&
152             (pieces[8] == ' ') &&
153             (pieces[11] == ' ') &&
154             (pieces[14] == ':') &&
155             (pieces[17] == ':') &&
156             (pieces[20] == ' ') && (lf->log += 21)
157         )
158     ) {
159         /* Check for an extra space in here */
160         if (*lf->log == ' ') {
161             lf->log++;
162         }
163
164
165         /* Hostname */
166         pieces = lf->hostname = lf->log;
167
168
169         /* Check for a valid hostname */
170         while (isValidChar(*pieces) == 1) {
171             pieces++;
172         }
173
174         /* Check if it is a syslog without hostname (common on Solaris) */
175         if (*pieces == ':' && pieces[1] == ' ') {
176             /* Getting solaris 8/9 messages without hostname.
177              * In these cases, the process_name should be there.
178              * http://www.ossec.net/wiki/index.php/Log_Samples_Solaris
179              */
180             lf->program_name = lf->hostname;
181             lf->hostname = NULL;
182
183             /* End the program name string */
184             *pieces = '\0';
185
186             pieces += 2;
187             lf->log = pieces;
188         }
189
190         /* Extract the hostname */
191         else if (*pieces != ' ') {
192             /* Invalid hostname */
193             lf->hostname = NULL;
194             pieces = NULL;
195         } else {
196             /* End the hostname string */
197             *pieces = '\0';
198
199             /* Move pieces to the beginning of the log message */
200             pieces++;
201             lf->log = pieces;
202
203             /* Get program_name */
204             lf->program_name = pieces;
205
206             /* Extract program_name */
207             /* Valid names:
208              * p_name:
209              * p_name[pid]:
210              * p_name[pid]: [ID xx facility.severity]
211              * auth|security:info p_name:
212              */
213             while (isValidChar(*pieces) == 1) {
214                 pieces++;
215             }
216
217             /* Check for the first format: p_name: */
218             if ((*pieces == ':') && (pieces[1] == ' ')) {
219                 *pieces = '\0';
220                 pieces += 2;
221             }
222
223             /* Check for the second format: p_name[pid]: */
224             else if ((*pieces == '[') && (isdigit((int)pieces[1]))) {
225                 *pieces = '\0';
226                 pieces += 2;
227                 while (isdigit((int)*pieces)) {
228                     pieces++;
229                 }
230
231                 if ((*pieces == ']') && (pieces[1] == ':') && (pieces[2] == ' ')) {
232                     pieces += 3;
233                 }
234                 /* Some systems are not terminating the program name with
235                  * a ':'. Working around this in here...
236                  */
237                 else if ((*pieces == ']') && (pieces[1] == ' ')) {
238                     pieces += 2;
239                 } else {
240                     /* Fix for some weird log formats */
241                     pieces--;
242                     while (isdigit((int)*pieces)) {
243                         pieces--;
244                     }
245
246                     if (*pieces == '\0') {
247                         *pieces = '[';
248                     }
249                     pieces = NULL;
250                     lf->program_name = NULL;
251                 }
252             }
253             /* AIX syslog */
254             else if ((*pieces == '|') && islower((int)pieces[1])) {
255                 pieces += 2;
256
257                 /* Remove facility */
258                 while (isalnum((int)*pieces)) {
259                     pieces++;
260                 }
261
262                 if (*pieces == ':') {
263                     /* Remove severity */
264                     pieces++;
265                     while (isalnum((int)*pieces)) {
266                         pieces++;
267                     }
268
269                     if (*pieces == ' ') {
270                         pieces++;
271                         lf->program_name = pieces;
272
273
274                         /* Get program name again */
275                         while (isValidChar(*pieces) == 1) {
276                             pieces++;
277                         }
278
279                         /* Check for the first format: p_name: */
280                         if ((*pieces == ':') && (pieces[1] == ' ')) {
281                             *pieces = '\0';
282                             pieces += 2;
283                         }
284
285                         /* Check for the second format: p_name[pid]: */
286                         else if ((*pieces == '[') && (isdigit((int)pieces[1]))) {
287                             *pieces = '\0';
288                             pieces += 2;
289                             while (isdigit((int)*pieces)) {
290                                 pieces++;
291                             }
292
293                             if ((*pieces == ']') && (pieces[1] == ':') &&
294                                     (pieces[2] == ' ')) {
295                                 pieces += 3;
296                             } else {
297                                 pieces = NULL;
298                             }
299                         }
300                     } else {
301                         pieces = NULL;
302                         lf->program_name = NULL;
303                     }
304                 }
305                 /* Invalid AIX */
306                 else {
307                     pieces = NULL;
308                     lf->program_name = NULL;
309                 }
310             } else {
311                 pieces = NULL;
312                 lf->program_name = NULL;
313             }
314         }
315
316         /* Remove [ID xx facility.severity] */
317         if (pieces) {
318             /* Set log after program name */
319             lf->log = pieces;
320
321             if ((pieces[0] == '[') &&
322                     (pieces[1] == 'I') &&
323                     (pieces[2] == 'D') &&
324                     (pieces[3] == ' ')) {
325                 pieces += 4;
326
327                 /* Going after the ] */
328                 pieces = strchr(pieces, ']');
329                 if (pieces) {
330                     pieces += 2;
331                     lf->log = pieces;
332                 }
333             }
334         }
335
336         /* Get program name size */
337         if (lf->program_name) {
338             lf->p_name_size = strlen(lf->program_name);
339         }
340     }
341
342     /* xferlog date format
343      * Mon Apr 17 18:27:14 2006 1 64.160.42.130
344      */
345     else if ((loglen > 28) &&
346              (pieces[3] == ' ') &&
347              (pieces[7] == ' ') &&
348              (pieces[10] == ' ') &&
349              (pieces[13] == ':') &&
350              (pieces[16] == ':') &&
351              (pieces[19] == ' ') &&
352              (pieces[24] == ' ') &&
353              (pieces[26] == ' ')) {
354         /* Move log to the beginning of the message */
355         lf->log += 24;
356     }
357
358     /* Check for snort date format
359      * ex: 01/28-09:13:16.240702  [**]
360      */
361     else if ( (loglen > 24) &&
362               (pieces[2] == '/') &&
363               (pieces[5] == '-') &&
364               (pieces[8] == ':') &&
365               (pieces[11] == ':') &&
366               (pieces[14] == '.') &&
367               (pieces[21] == ' ') ) {
368         lf->log += 23;
369     }
370
371     /* Check for suricata (new) date format
372      * ex: 01/28/1979-09:13:16.240702  [**]
373      */
374     else if ( (loglen > 26) &&
375               (pieces[2] == '/') &&
376               (pieces[5] == '/') &&
377               (pieces[10] == '-') &&
378               (pieces[13] == ':') &&
379               (pieces[16] == ':') &&
380               (pieces[19] == '.') &&
381               (pieces[26] == ' ') ) {
382         lf->log += 28;
383     }
384
385
386     /* Check for apache log format */
387     /* [Fri Feb 11 18:06:35 2004] [warn] */
388     else if ( (loglen > 27) &&
389               (pieces[0] == '[') &&
390               (pieces[4] == ' ') &&
391               (pieces[8] == ' ') &&
392               (pieces[11] == ' ') &&
393               (pieces[14] == ':') &&
394               (pieces[17] == ':') &&
395               (pieces[20] == ' ') &&
396               (pieces[25] == ']') ) {
397         lf->log += 27;
398     }
399
400     /* Check for the osx asl log format.
401      * Examples:
402      * [Time 2006.12.28 15:53:55 UTC] [Facility auth] [Sender sshd] [PID 483] [Message error: PAM: Authentication failure for username from 192.168.0.2] [Level 3] [UID -2] [GID -2] [Host Hostname]
403      * [Time 2006.11.02 14:02:11 UTC] [Facility auth] [Sender sshd] [PID 856]
404      [Message refused connect from 59.124.44.34] [Level 4] [UID -2] [GID -2]
405      [Host robert-wyatts-emac]
406      */
407     else if ((loglen > 26) &&
408              (pieces[0] == '[')  &&
409              (pieces[1] == 'T')  &&
410              (pieces[5] == ' ')  &&
411              (pieces[10] == '.') &&
412              (pieces[13] == '.') &&
413              (pieces[16] == ' ') &&
414              (pieces[19] == ':')) {
415         /* Do not read more than 1 message entry -> log tampering */
416         short unsigned int done_message = 0;
417
418         /* Remove the date */
419         lf->log += 25;
420
421         /* Get the desired values */
422         pieces = strchr(lf->log, '[');
423         while (pieces) {
424             pieces++;
425
426             /* Get the sender (set to program name) */
427             if ((strncmp(pieces, "Sender ", 7) == 0) &&
428                     (lf->program_name == NULL)) {
429                 pieces += 7;
430                 lf->program_name = pieces;
431
432                 /* Get the closing brackets */
433                 pieces = strchr(pieces, ']');
434                 if (pieces) {
435                     *pieces = '\0';
436
437                     /* Set program_name size */
438                     lf->p_name_size = strlen(lf->program_name);
439
440                     pieces++;
441                 }
442                 /* Invalid program name */
443                 else {
444                     lf->program_name = NULL;
445                     break;
446                 }
447             }
448
449             /* Get message */
450             else if ((strncmp(pieces, "Message ", 8) == 0) &&
451                      (done_message == 0)) {
452                 pieces += 8;
453                 done_message = 1;
454
455                 lf->log = pieces;
456
457                 /* Get the closing brackets */
458                 pieces = strchr(pieces, ']');
459                 if (pieces) {
460                     *pieces = '\0';
461                     pieces++;
462                 }
463                 /* Invalid log closure */
464                 else {
465                     break;
466                 }
467             }
468
469             /* Get hostname */
470             else if (strncmp(pieces, "Host ", 5) == 0) {
471                 pieces += 5;
472                 lf->hostname = pieces;
473
474                 /* Get the closing brackets */
475                 pieces = strchr(pieces, ']');
476                 if (pieces) {
477                     *pieces = '\0';
478                     pieces++;
479                 }
480
481                 /* Invalid hostname */
482                 else {
483                     lf->hostname = NULL;
484                 }
485                 break;
486             }
487
488             /* Get next entry */
489             pieces = strchr(pieces, '[');
490         }
491     }
492
493     /* Check for squid date format
494      * 1140804070.368  11623
495      * seconds from 00:00:00 1970-01-01 UTC
496      */
497     else if ((loglen > 32) &&
498              (pieces[0] == '1') &&
499              (isdigit((int)pieces[1])) &&
500              (isdigit((int)pieces[2])) &&
501              (isdigit((int)pieces[3])) &&
502              (pieces[10] == '.') &&
503              (isdigit((int)pieces[13])) &&
504              (pieces[14] == ' ') &&
505              ((pieces[21] == ' ') || (pieces[22] == ' '))) {
506         lf->log += 14;
507
508         /* We need to start at the size of the event */
509         while (*lf->log == ' ') {
510             lf->log++;
511         }
512     }
513
514     /* Every message must be in the format
515      * hostname->location or
516      * (agent) ip->location.
517      */
518
519     /* Set hostname for local messages */
520     if (lf->location[0] == '(') {
521         /* Messages from an agent */
522         lf->hostname = lf->location;
523     } else if (lf->hostname == NULL) {
524         lf->hostname = __shost;
525     }
526
527     /* Set up the event data */
528     lf->time = c_time;
529     p = localtime(&c_time);
530
531     /* Assign hour, day, year and month values */
532     lf->day = p->tm_mday;
533     lf->year = p->tm_year + 1900;
534     strncpy(lf->mon, month[p->tm_mon], 3);
535     snprintf(lf->hour, 9, "%02d:%02d:%02d",
536              p->tm_hour,
537              p->tm_min,
538              p->tm_sec);
539
540     /* Set the global hour/weekday */
541     __crt_hour = p->tm_hour;
542     __crt_wday = p->tm_wday;
543
544 #ifdef TESTRULE
545     if (!alert_only) {
546         print_out("**Phase 1: Completed pre-decoding.");
547         print_out("       full event: '%s'", lf->full_log);
548         print_out("       hostname: '%s'", lf->hostname);
549         print_out("       program_name: '%s'", lf->program_name);
550         print_out("       log: '%s'", lf->log);
551     }
552 #endif
553     return (0);
554 }
555