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