new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / logcollector / logcollector.c
1 /* Copyright (C) 2009 Trend Micro Inc.
2  * All right 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 "shared.h"
11 #include "logcollector.h"
12
13 /* Prototypes */
14 int update_fname(int i);
15
16 /* Global variables */
17 int loop_timeout;
18 int logr_queue;
19 int open_file_attempts;
20 logreader *logff;
21 static int _cday = 0;
22
23
24 static char *rand_keepalive_str(char *dst, int size)
25 {
26     static const char text[] = "abcdefghijklmnopqrstuvwxyz"
27                                "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
28                                "0123456789"
29                                "!@#$%^&*()_+-=;'[],./?";
30     int i, len = rand() % (size - 10);
31
32     strncpy(dst, "--MARK--: ", 12);
33     for ( i = 10; i < len; ++i ) {
34         dst[i] = text[(unsigned int)rand() % (sizeof text - 1)];
35     }
36     dst[i] = '\0';
37     return dst;
38 }
39
40 /* Handle file management */
41 void LogCollectorStart()
42 {
43     int i = 0, r = 0;
44     int max_file = 0;
45     int f_check = 0;
46     time_t curr_time = 0;
47     char keepalive[1024];
48
49
50 #ifndef WIN32
51     int int_error = 0;
52     struct timeval fp_timeout;
53     /* To check for inode changes */
54     struct stat tmp_stat;
55 #else
56     BY_HANDLE_FILE_INFORMATION lpFileInformation;
57
58     /* Check if we are on Windows Vista */
59     checkVista();
60
61     /* Read vista descriptions */
62     if (isVista) {
63         win_read_vista_sec();
64     }
65 #endif
66
67     debug1("%s: DEBUG: Entering LogCollectorStart().", ARGV0);
68
69     /* Initialize each file and structure */
70     for (i = 0;; i++) {
71         if (logff[i].file == NULL) {
72             break;
73         }
74
75         /* Remove duplicate entries */
76         for (r = 0; r < i; r++) {
77             if (logff[r].file && strcmp(logff[i].file, logff[r].file) == 0) {
78                 merror("%s: WARN: Duplicated log file given: '%s'.",
79                        ARGV0, logff[i].file);
80                 logff[i].file = NULL;
81                 logff[i].command = NULL;
82                 logff[i].fp = NULL;
83
84                 break;
85             }
86         }
87
88         if (logff[i].file == NULL) {
89             /* Do nothing, duplicated entry */
90         }
91
92         else if (strcmp(logff[i].logformat, "eventlog") == 0) {
93 #ifdef WIN32
94
95             verbose(READING_EVTLOG, ARGV0, logff[i].file);
96             win_startel(logff[i].file);
97
98 #endif
99             logff[i].file = NULL;
100             logff[i].command = NULL;
101             logff[i].fp = NULL;
102         }
103
104         else if (strcmp(logff[i].logformat, "eventchannel") == 0) {
105 #ifdef WIN32
106
107 #ifdef EVENTCHANNEL_SUPPORT
108             verbose(READING_EVTLOG, ARGV0, logff[i].file);
109             win_start_event_channel(logff[i].file, logff[i].future, logff[i].query);
110 #else
111             merror("%s: WARN: eventchannel not available on this version of OSSEC", ARGV0);
112 #endif
113
114 #endif
115
116             logff[i].file = NULL;
117             logff[i].command = NULL;
118             logff[i].fp = NULL;
119         }
120
121         else if (strcmp(logff[i].logformat, "command") == 0) {
122             logff[i].file = NULL;
123             logff[i].fp = NULL;
124             logff[i].size = 0;
125
126             if (logff[i].command) {
127                 logff[i].read = read_command;
128
129                 verbose("%s: INFO: Monitoring output of command(%d): %s", ARGV0, logff[i].ign, logff[i].command);
130
131                 if (!logff[i].alias) {
132                     os_strdup(logff[i].command, logff[i].alias);
133                 }
134             } else {
135                 merror("%s: ERROR: Missing command argument. Ignoring it.",
136                        ARGV0);
137             }
138         } else if (strcmp(logff[i].logformat, "full_command") == 0) {
139             logff[i].file = NULL;
140             logff[i].fp = NULL;
141             logff[i].size = 0;
142             if (logff[i].command) {
143                 logff[i].read = read_fullcommand;
144
145                 verbose("%s: INFO: Monitoring full output of command(%d): %s", ARGV0, logff[i].ign, logff[i].command);
146
147                 if (!logff[i].alias) {
148                     os_strdup(logff[i].command, logff[i].alias);
149                 }
150             } else {
151                 merror("%s: ERROR: Missing command argument. Ignoring it.",
152                        ARGV0);
153             }
154         }
155
156         else {
157             logff[i].command = NULL;
158
159             /* Initialize the files */
160             if (logff[i].ffile) {
161                 /* Day must be zero for all files to be initialized */
162                 _cday = 0;
163                 if (update_fname(i)) {
164                     handle_file(i, 1, 1);
165                 } else {
166                     ErrorExit(PARSE_ERROR, ARGV0, logff[i].ffile);
167                 }
168
169             } else {
170                 handle_file(i, 1, 1);
171             }
172
173             verbose(READING_FILE, ARGV0, logff[i].file);
174
175             /* Get the log type */
176             if (strcmp("snort-full", logff[i].logformat) == 0) {
177                 logff[i].read = read_snortfull;
178             }
179 #ifndef WIN32
180             if (strcmp("ossecalert", logff[i].logformat) == 0) {
181                 logff[i].read = read_ossecalert;
182             }
183 #endif
184             else if (strcmp("nmapg", logff[i].logformat) == 0) {
185                 logff[i].read = read_nmapg;
186             } else if (strcmp("mysql_log", logff[i].logformat) == 0) {
187                 logff[i].read = read_mysql_log;
188             } else if (strcmp("mssql_log", logff[i].logformat) == 0) {
189                 logff[i].read = read_mssql_log;
190             } else if (strcmp("postgresql_log", logff[i].logformat) == 0) {
191                 logff[i].read = read_postgresql_log;
192             } else if (strcmp("djb-multilog", logff[i].logformat) == 0) {
193                 if (!init_djbmultilog(i)) {
194                     merror(INV_MULTILOG, ARGV0, logff[i].file);
195                     if (logff[i].fp) {
196                         fclose(logff[i].fp);
197                         logff[i].fp = NULL;
198                     }
199                     logff[i].file = NULL;
200                 }
201                 logff[i].read = read_djbmultilog;
202             } else if (logff[i].logformat[0] >= '0' && logff[i].logformat[0] <= '9') {
203                 logff[i].read = read_multiline;
204             } else if (strcmp("audit", logff[i].logformat) == 0) {
205                 logff[i].read = read_audit;
206             } else {
207                 logff[i].read = read_syslog;
208             }
209
210             /* More tweaks for Windows. For some reason IIS places
211              * some weird characters at the end of the files and getc
212              * always returns 0 (even after clearerr).
213              */
214 #ifdef WIN32
215             if (logff[i].fp) {
216                 logff[i].read(i, &r, 1);
217             }
218 #endif
219         }
220
221         if (logff[i].alias) {
222             int ii = 0;
223             while (logff[i].alias[ii] != '\0') {
224                 if (logff[i].alias[ii] == ':') {
225                     logff[i].alias[ii] = '\\';
226                 }
227                 ii++;
228             }
229         }
230     }
231
232     /* Start up message */
233     verbose(STARTUP_MSG, ARGV0, (int)getpid());
234
235     max_file = i - 1;
236
237     /* Cannot be zero */
238     if (max_file < 0) {
239         max_file = 0;
240     }
241
242     /* Daemon loop */
243     while (1) {
244 #ifndef WIN32
245         fp_timeout.tv_sec = loop_timeout;
246         fp_timeout.tv_usec = 0;
247
248         /* Wait for the select timeout */
249         if ((r = select(0, NULL, NULL, NULL, &fp_timeout)) < 0) {
250             merror(SELECT_ERROR, ARGV0, errno, strerror(errno));
251             int_error++;
252
253             if (int_error >= 5) {
254                 ErrorExit(SYSTEM_ERROR, ARGV0);
255             }
256             continue;
257         }
258 #else
259
260         /* Windows doesn't like select that way */
261         sleep(loop_timeout + 2);
262
263         /* Check for messages in the event viewer */
264         win_readel();
265 #endif
266
267         f_check++;
268
269         /* Check which file is available */
270         for (i = 0; i <= max_file; i++) {
271             if (!logff[i].fp) {
272                 /* Run the command */
273                 if (logff[i].command && (f_check % 2)) {
274                     curr_time = time(0);
275                     if ((curr_time - logff[i].size) >= logff[i].ign) {
276                         logff[i].size = curr_time;
277                         logff[i].read(i, &r, 0);
278                     }
279                 }
280                 continue;
281             }
282
283             /* Windows with IIS logs is very strange.
284              * For some reason it always returns 0 (not EOF)
285              * the fgetc. To solve this problem, we always
286              * pass it to the function pointer directly.
287              */
288 #ifndef WIN32
289             /* We check for the end of file. If is returns EOF,
290              * we don't attempt to read it.
291              */
292             if ((r = fgetc(logff[i].fp)) == EOF) {
293                 clearerr(logff[i].fp);
294                 continue;
295             }
296
297             /* If it is not EOF, we need to return the read character */
298             ungetc(r, logff[i].fp);
299 #endif
300
301             /* Finally, send to the function pointer to read it */
302             logff[i].read(i, &r, 0);
303
304             /* Check for error */
305             if (!ferror(logff[i].fp)) {
306                 /* Clear EOF */
307                 clearerr(logff[i].fp);
308
309                 /* Parsing error */
310                 if (r != 0) {
311                     logff[i].ign++;
312                 }
313             }
314             /* If ferror is set */
315             else {
316                 merror(FREAD_ERROR, ARGV0, logff[i].file, errno, strerror(errno));
317 #ifndef WIN32
318                 if (fseek(logff[i].fp, 0, SEEK_END) < 0)
319 #else
320                 if (1)
321 #endif
322                 {
323
324 #ifndef WIN32
325                     merror(FSEEK_ERROR, ARGV0, logff[i].file, errno, strerror(errno));
326 #endif
327
328                     /* Close the file */
329                     if (logff[i].fp) {
330                         fclose(logff[i].fp);
331 #ifdef WIN32
332                         CloseHandle(logff[i].h);
333 #endif
334                     }
335                     logff[i].fp = NULL;
336
337
338                     /* Try to open it again */
339                     if (handle_file(i, 1, 1) != 0) {
340                         logff[i].ign++;
341                         continue;
342                     }
343 #ifdef WIN32
344                     logff[i].read(i, &r, 1);
345 #endif
346                 }
347                 /* Increase the error count  */
348                 logff[i].ign++;
349                 clearerr(logff[i].fp);
350             }
351         }
352
353         /* Only check below if check > VCHECK_FILES */
354         if (f_check <= VCHECK_FILES) {
355             continue;
356         }
357
358         /* Send keep alive message */
359         rand_keepalive_str(keepalive, 700);
360         SendMSG(logr_queue, keepalive, "ossec-keepalive", LOCALFILE_MQ);
361
362         /* Zero f_check */
363         f_check = 0;
364
365         /* Check if any file has been renamed/removed */
366         for (i = 0; i <= max_file; i++) {
367             /* These are the windows logs or ignored files */
368             if (!logff[i].file) {
369                 continue;
370             }
371
372             /* Files with date -- check for day change */
373             if (logff[i].ffile) {
374                 if (update_fname(i)) {
375                     if (logff[i].fp) {
376                         fclose(logff[i].fp);
377 #ifdef WIN32
378                         CloseHandle(logff[i].h);
379 #endif
380                     }
381                     logff[i].fp = NULL;
382                     handle_file(i, 0, 1);
383                     continue;
384                 }
385
386                 /* Variable file name */
387                 else if (!logff[i].fp) {
388                     handle_file(i, 0, 0);
389                     continue;
390                 }
391             }
392
393             /* Check for file change -- if the file is open already */
394             if (logff[i].fp) {
395 #ifndef WIN32
396
397                 /* To help detect a file rollover, temporarily open the file a second time.
398                  * Previously the fstat would work on "cached" file data, but this should 
399                  * ensure it's fresh when hardlinks are used (like alerts.log).
400                  */
401                 FILE *tf;
402                 tf = fopen(logff[i].file, "r");
403                 if(tf == NULL) {
404                         merror(FOPEN_ERROR, ARGV0, logff[i].file, errno, strerror(errno));
405                 }
406
407                 else if ((fstat(fileno(tf), &tmp_stat)) == -1) {
408                     fclose(logff[i].fp);
409                     fclose(tf);
410                     logff[i].fp = NULL;
411
412                     merror(FSTAT_ERROR, ARGV0, logff[i].file, errno, strerror(errno));
413                 }
414                 else if(fclose(tf) == EOF) {
415                         merror("Closing the temporary file %s did not work (%d): %s", logff[i].file, errno, strerror(errno));
416                 }
417 #else
418                 HANDLE h1;
419
420                 h1 = CreateFile(logff[i].file, GENERIC_READ,
421                                 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
422                                 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
423                 if (h1 == INVALID_HANDLE_VALUE) {
424                     fclose(logff[i].fp);
425                     CloseHandle(logff[i].h);
426                     logff[i].fp = NULL;
427                     merror(FILE_ERROR, ARGV0, logff[i].file);
428                 } else if (GetFileInformationByHandle(h1, &lpFileInformation) == 0) {
429                     fclose(logff[i].fp);
430                     CloseHandle(logff[i].h);
431                     CloseHandle(h1);
432                     logff[i].fp = NULL;
433                     merror(FILE_ERROR, ARGV0, logff[i].file);;
434                 }
435 #endif
436
437 #ifdef WIN32
438                 else if (logff[i].fd != (lpFileInformation.nFileIndexLow + lpFileInformation.nFileIndexHigh))
439 #else
440                 else if (logff[i].fd != tmp_stat.st_ino)
441 #endif
442                 {
443                     char msg_alert[512 + 1];
444
445                     snprintf(msg_alert, 512, "ossec: File rotated (inode "
446                              "changed): '%s'.",
447                              logff[i].file);
448
449                     /* Send message about log rotated */
450                     SendMSG(logr_queue, msg_alert,
451                             "ossec-logcollector", LOCALFILE_MQ);
452
453                     debug1("%s: DEBUG: File inode changed. %s",
454                            ARGV0, logff[i].file);
455
456                     fclose(logff[i].fp);
457
458 #ifdef WIN32
459                     CloseHandle(logff[i].h);
460                     CloseHandle(h1);
461 #endif
462
463                     logff[i].fp = NULL;
464                     handle_file(i, 0, 1);
465                     continue;
466                 }
467 #ifdef WIN32
468                 else if (logff[i].size > (lpFileInformation.nFileSizeHigh + lpFileInformation.nFileSizeLow))
469 #else
470                 else if (logff[i].size > tmp_stat.st_size)
471 #endif
472                 {
473                     char msg_alert[512 + 1];
474
475                     snprintf(msg_alert, 512, "ossec: File size reduced "
476                              "(inode remained): '%s'.",
477                              logff[i].file);
478
479                     /* Send message about log rotated */
480                     SendMSG(logr_queue, msg_alert,
481                             "ossec-logcollector", LOCALFILE_MQ);
482
483                     debug1("%s: DEBUG: File size reduced. %s",
484                            ARGV0, logff[i].file);
485
486                     /* Get new file */
487                     fclose(logff[i].fp);
488
489 #ifdef WIN32
490                     CloseHandle(logff[i].h);
491                     CloseHandle(h1);
492 #endif
493
494                     logff[i].fp = NULL;
495                     handle_file(i, 1, 1);
496                 }
497 #ifdef WIN32
498                 else {
499                     CloseHandle(h1);
500                 }
501 #endif
502             }
503
504
505             /* Too many errors for the file */
506             if (logff[i].ign > open_file_attempts) {
507                 /* 999 Maximum ignore */
508                 if (logff[i].ign == 999) {
509                     continue;
510                 }
511
512                 merror(LOGC_FILE_ERROR, ARGV0, logff[i].file);
513                 if (logff[i].fp) {
514                     fclose(logff[i].fp);
515 #ifdef WIN32
516                     CloseHandle(logff[i].h);
517 #endif
518                 }
519
520                 logff[i].fp = NULL;
521
522                 /* If the file has a variable date, ignore it for today only */
523                 if (!logff[i].ffile) {
524                     /* Variable log files should always be attempted
525                      * to be open...
526                      */
527                     //logff[i].file = NULL;
528                 }
529                 logff[i].ign = 999;
530                 continue;
531             }
532
533             /* File not open */
534             if (!logff[i].fp) {
535                 if (logff[i].ign >= 999) {
536                     continue;
537                 } else {
538                     /* Try for a few times to open the file */
539                     if (handle_file(i, 1, 1) < 0) {
540                         logff[i].ign++;
541                     }
542                     continue;
543                 }
544             }
545
546             /* Update file size */
547 #ifdef WIN32
548             logff[i].size = lpFileInformation.nFileSizeHigh + lpFileInformation.nFileSizeLow;
549 #else
550             logff[i].size = tmp_stat.st_size;
551 #endif
552         }
553     }
554 }
555
556 int update_fname(int i)
557 {
558     struct tm *p;
559     time_t __ctime = time(0);
560     char lfile[OS_FLSIZE + 1];
561     size_t ret;
562
563     p = localtime(&__ctime);
564
565     /* Handle file */
566     if (p->tm_mday == _cday) {
567         return (0);
568     }
569
570     lfile[OS_FLSIZE] = '\0';
571     ret = strftime(lfile, OS_FLSIZE, logff[i].ffile, p);
572     if (ret == 0) {
573         ErrorExit(PARSE_ERROR, ARGV0, logff[i].ffile);
574     }
575
576     /* Update the filename */
577     if (strcmp(lfile, logff[i].file) != 0) {
578         os_free(logff[i].file);
579
580         os_strdup(lfile, logff[i].file);
581
582         verbose(VAR_LOG_MON, ARGV0, logff[i].file);
583
584         /* Setting cday to zero because other files may need
585          * to be changed.
586          */
587         _cday = 0;
588         return (1);
589     }
590
591     _cday = p->tm_mday;
592     return (0);
593 }
594
595 /* Open, get the fileno, seek to the end and update mtime */
596 int handle_file(int i, int do_fseek, int do_log)
597 {
598     int fd;
599     struct stat stat_fd;
600
601     /* We must be able to open the file, fseek and get the
602      * time of change from it.
603      */
604 #ifndef WIN32
605     logff[i].fp = fopen(logff[i].file, "r");
606     if (!logff[i].fp) {
607         if (do_log == 1) {
608             merror(FOPEN_ERROR, ARGV0, logff[i].file, errno, strerror(errno));
609         }
610         return (-1);
611     }
612     /* Get inode number for fp */
613     fd = fileno(logff[i].fp);
614     if (fstat(fd, &stat_fd) == -1) {
615         merror(FSTAT_ERROR, ARGV0, logff[i].file, errno, strerror(errno));
616         fclose(logff[i].fp);
617         logff[i].fp = NULL;
618         return (-1);
619     }
620
621     logff[i].fd = stat_fd.st_ino;
622     logff[i].size =  stat_fd.st_size;
623
624
625 #else
626     BY_HANDLE_FILE_INFORMATION lpFileInformation;
627
628     logff[i].fp = NULL;
629     logff[i].h = CreateFile(logff[i].file, GENERIC_READ,
630                             FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
631                             NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
632     if (logff[i].h == INVALID_HANDLE_VALUE) {
633         if (do_log == 1) {
634             merror(FOPEN_ERROR, ARGV0, logff[i].file, errno, strerror(errno));
635         }
636         return (-1);
637     }
638     fd = _open_osfhandle((long)logff[i].h, 0);
639     if (fd == -1) {
640         merror(FOPEN_ERROR, ARGV0, logff[i].file, errno, strerror(errno));
641         CloseHandle(logff[i].h);
642         return (-1);
643     }
644     logff[i].fp = _fdopen(fd, "r");
645     if (logff[i].fp == NULL) {
646         merror(FOPEN_ERROR, ARGV0, logff[i].file, errno, strerror(errno));
647         CloseHandle(logff[i].h);
648         return (-1);
649     }
650
651
652     /* On windows, we also need the real inode, which is the combination
653      * of the index low + index high numbers.
654      */
655     if (GetFileInformationByHandle(logff[i].h, &lpFileInformation) == 0) {
656         merror("%s: Unable to get file information by handle.", ARGV0);
657         fclose(logff[i].fp);
658         CloseHandle(logff[i].h);
659         logff[i].fp = NULL;
660         return (-1);
661     }
662
663     logff[i].fd = (lpFileInformation.nFileIndexLow + lpFileInformation.nFileIndexHigh);
664     logff[i].size = (lpFileInformation.nFileSizeHigh + lpFileInformation.nFileSizeLow);
665
666 #endif
667
668     /* Only seek the end of the file if set to */
669     if (do_fseek == 1 && S_ISREG(stat_fd.st_mode)) {
670         /* Windows and fseek causes some weird issues */
671 #ifndef WIN32
672         if (fseek(logff[i].fp, 0, SEEK_END) < 0) {
673             merror(FSEEK_ERROR, ARGV0, logff[i].file, errno, strerror(errno));
674             fclose(logff[i].fp);
675             logff[i].fp = NULL;
676             return (-1);
677         }
678 #endif
679     }
680
681     /* Set ignore to zero */
682     logff[i].ign = 0;
683     return (0);
684 }
685
686 #ifdef WIN32
687
688 /* Remove newlines and replace tabs in the argument fields with spaces */
689 void win_format_event_string(char *string)
690 {
691     if (string == NULL) {
692         return;
693     }
694
695     while (*string != '\0') {
696         if (*string == '\n' || *string == '\r' || *string == ':') {
697             if (*string == '\n' || *string == '\r') {
698                 *string = ' ';
699             }
700
701             string++;
702
703             while (*string == '\t') {
704                 *string = ' ';
705                 string++;
706             }
707
708             continue;
709         }
710
711         string++;
712     }
713 }
714
715 #endif /* WIN32 */
716