9dbaa2b60f87e133a565b169272031e22bafbb6d
[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, "eventchannel") == 0)
120         {
121                         #ifdef WIN32
122                         
123                         #ifdef EVENTCHANNEL_SUPPORT
124                         verbose(READING_EVTLOG, ARGV0, logff[i].file);
125                         win_start_event_channel(logff[i].file, logff[i].future, logff[i].query);
126                         #else
127                         merror("%s: WARN: eventchannel not available on this version of OSSEC", ARGV0);
128                         #endif
129                         
130                         #endif
131                         
132                         logff[i].file = NULL;
133                         logff[i].command = NULL;
134             logff[i].fp = NULL;
135         }
136
137         else if(strcmp(logff[i].logformat, "command") == 0)
138         {
139             logff[i].file = NULL;
140             logff[i].fp = NULL;
141             logff[i].size = 0;
142
143             if(logff[i].command)
144             {
145                 logff[i].read = (void *)read_command;
146
147                 verbose("%s: INFO: Monitoring output of command(%d): %s", ARGV0, logff[i].ign, logff[i].command);
148
149                 if(!logff[i].alias)
150                 {
151                     os_strdup(logff[i].command, logff[i].alias);
152                 }
153             }
154             else
155             {
156                 merror("%s: ERROR: Missing command argument. Ignoring it.",
157                        ARGV0);
158             }
159         }
160         else if(strcmp(logff[i].logformat, "full_command") == 0)
161         {
162             logff[i].file = NULL;
163             logff[i].fp = NULL;
164             logff[i].size = 0;
165             if(logff[i].command)
166             {
167                 logff[i].read = (void *)read_fullcommand;
168
169                 verbose("%s: INFO: Monitoring full output of command(%d): %s", ARGV0, logff[i].ign, logff[i].command);
170
171                 if(!logff[i].alias)
172                     os_strdup(logff[i].command, logff[i].alias);
173             }
174             else
175             {
176                 merror("%s: ERROR: Missing command argument. Ignoring it.",
177                        ARGV0);
178             }
179         }
180
181         else
182         {
183             logff[i].command = NULL;
184
185
186             /* Initializing the files */
187             if(logff[i].ffile)
188             {
189                 /* Day must be zero for all files to be initialized */
190                 _cday = 0;
191                 if(update_fname(i))
192                 {
193                     handle_file(i, 1, 1);
194                 }
195                 else
196                 {
197                     ErrorExit(PARSE_ERROR, ARGV0, logff[i].ffile);
198                 }
199
200             }
201             else
202             {
203                 handle_file(i, 1, 1);
204             }
205
206             verbose(READING_FILE, ARGV0, logff[i].file);
207
208             /* Getting the log type */
209             if(strcmp("snort-full", logff[i].logformat) == 0)
210             {
211                 logff[i].read = (void *)read_snortfull;
212             }
213             #ifndef WIN32
214             if(strcmp("ossecalert", logff[i].logformat) == 0)
215             {
216                 logff[i].read = (void *)read_ossecalert;
217             }
218             #endif
219             else if(strcmp("nmapg", logff[i].logformat) == 0)
220             {
221                 logff[i].read = (void *)read_nmapg;
222             }
223             else if(strcmp("mysql_log", logff[i].logformat) == 0)
224             {
225                 logff[i].read = (void *)read_mysql_log;
226             }
227             else if(strcmp("mssql_log", logff[i].logformat) == 0)
228             {
229                 logff[i].read = (void *)read_mssql_log;
230             }
231             else if(strcmp("postgresql_log", logff[i].logformat) == 0)
232             {
233                 logff[i].read = (void *)read_postgresql_log;
234             }
235             else if(strcmp("djb-multilog", logff[i].logformat) == 0)
236             {
237                 if(!init_djbmultilog(i))
238                 {
239                     merror(INV_MULTILOG, ARGV0, logff[i].file);
240                     if(logff[i].fp)
241                     {
242                         fclose(logff[i].fp);
243                         logff[i].fp = NULL;
244                     }
245                     logff[i].file = NULL;
246                 }
247                 logff[i].read = (void *)read_djbmultilog;
248             }
249             else if(logff[i].logformat[0] >= '0' && logff[i].logformat[0] <= '9')
250             {
251                 logff[i].read = (void *)read_multiline;
252             }
253             else
254             {
255                 logff[i].read = (void *)read_syslog;
256             }
257
258             /* More tweaks for Windows. For some reason IIS places
259              * some wierd characters at the end of the files and getc
260              * always returns 0 (even after clearerr).
261              */
262             #ifdef WIN32
263             if(logff[i].fp)
264             {
265                 logff[i].read(i, &r, 1);
266             }
267             #endif
268         }
269
270         if(logff[i].alias)
271         {
272             int ii = 0;
273             while(logff[i].alias[ii] != '\0')
274             {
275                 if(logff[i].alias[ii] == ':')
276                 {
277                     logff[i].alias[ii] = '\\';
278                 }
279                 ii++;
280             }
281         }
282     }
283
284
285     /* Start up message */
286     verbose(STARTUP_MSG, ARGV0, (int)getpid());
287
288     max_file = i -1;
289
290
291     /* Cannot be zero */
292     if(max_file < 0)
293     {
294         max_file = 0;
295     }
296
297
298     /* Daemon loop */
299     while(1)
300     {
301         #ifndef WIN32
302         fp_timeout.tv_sec = loop_timeout;
303         fp_timeout.tv_usec = 0;
304
305         /* Waiting for the select timeout */
306         if ((r = select(0, NULL, NULL, NULL, &fp_timeout)) < 0)
307         {
308             merror(SELECT_ERROR, ARGV0);
309             int_error++;
310
311             if(int_error >= 5)
312             {
313                 ErrorExit(SYSTEM_ERROR, ARGV0);
314             }
315             continue;
316         }
317         #else
318
319         /* Windows don't like select that way */
320         sleep(loop_timeout + 2);
321
322
323         /* Check for messages in the event viewer */
324         win_readel();
325         #endif
326
327         f_check++;
328
329
330         /* Checking which file is available */
331         for(i = 0; i <= max_file; i++)
332         {
333             if(!logff[i].fp)
334             {
335                 /* Run the command. */
336                 if(logff[i].command && (f_check %2))
337                 {
338                     curr_time = time(0);
339                     if((curr_time - logff[i].size) >= logff[i].ign)
340                     {
341                         logff[i].size = curr_time;
342                         logff[i].read(i, &r, 0);
343                     }
344                 }
345                 continue;
346             }
347
348             /* Windows with IIS logs is very strange.
349              * For some reason it always returns 0 (not EOF)
350              * the fgetc. To solve this problem, we always
351              * pass it to the function pointer directly.
352              */
353             #ifndef WIN32
354             /* We check for the end of file. If is returns EOF,
355              * we don't attempt to read it.
356              */
357             if((r = fgetc(logff[i].fp)) == EOF)
358             {
359                 clearerr(logff[i].fp);
360                 continue;
361             }
362
363
364             /* If it is not EOF, we need to return the read character */
365             ungetc(r, logff[i].fp);
366             #endif
367
368
369             /* Finally, send to the function pointer to read it */
370             logff[i].read(i, &r, 0);
371
372
373             /* Checking for error */
374             if(!ferror(logff[i].fp))
375             {
376                 /* Clearing EOF */
377                 clearerr(logff[i].fp);
378
379                 /* Parsing error */
380                 if(r != 0)
381                 {
382                     logff[i].ign++;
383                 }
384             }
385             /* If ferror is set */
386             else
387             {
388                 merror(FREAD_ERROR, ARGV0, logff[i].file);
389                 #ifndef WIN32
390                 if(fseek(logff[i].fp, 0, SEEK_END) < 0)
391                 #else
392                 if(1)
393                 #endif
394                 {
395
396                     #ifndef WIN32
397                     merror(FSEEK_ERROR, ARGV0, logff[i].file);
398                     #endif
399
400                     /* Closing the file */
401                     if(logff[i].fp)
402                     {
403                         fclose(logff[i].fp);
404                         #ifdef WIN32
405                         CloseHandle(logff[i].h);
406                         #endif
407                     }
408                     logff[i].fp = NULL;
409
410
411                     /* Trying to open it again */
412                     if(handle_file(i, 1, 1) != 0)
413                     {
414                         logff[i].ign++;
415                         continue;
416                     }
417
418                     #ifdef WIN32
419                     logff[i].read(i, &r, 1);
420                     #endif
421                 }
422
423                 /* Increase the error count  */
424                 logff[i].ign++;
425                 clearerr(logff[i].fp);
426             }
427         }
428
429
430         /* Only check bellow if check > VCHECK_FILES */
431         if(f_check <= VCHECK_FILES)
432             continue;
433
434
435         /* Send keep alive message */
436
437         rand_keepalive_str(keepalive, 700);
438         SendMSG(logr_queue, keepalive, "ossec-keepalive", LOCALFILE_MQ);
439
440
441         /* Zeroing f_check */
442         f_check = 0;
443
444
445         /* Checking if any file has been renamed/removed */
446         for(i = 0; i <= max_file; i++)
447         {
448             /* These are the windows logs or ignored files */
449             if(!logff[i].file)
450                 continue;
451
452
453             /* Files with date -- check for day change */
454             if(logff[i].ffile)
455             {
456                 if(update_fname(i))
457                 {
458                     if(logff[i].fp)
459                     {
460                         fclose(logff[i].fp);
461                         #ifdef WIN32
462                         CloseHandle(logff[i].h);
463                         #endif
464                     }
465                     logff[i].fp = NULL;
466                     handle_file(i, 0, 1);
467                     continue;
468                 }
469
470                 /* Variable file name */
471                 else if(!logff[i].fp)
472                 {
473                     handle_file(i, 0, 0);
474                     continue;
475                 }
476             }
477
478
479             /* Check for file change -- if the file is open already */
480             if(logff[i].fp)
481             {
482                 #ifndef WIN32
483                 if(stat(logff[i].file, &tmp_stat) == -1)
484                 {
485                     fclose(logff[i].fp);
486                     logff[i].fp = NULL;
487
488                     merror(FILE_ERROR, ARGV0, logff[i].file);
489                 }
490
491                 #else
492                 BY_HANDLE_FILE_INFORMATION lpFileInformation;
493                 HANDLE h1;
494
495                 h1 = CreateFile(logff[i].file, GENERIC_READ,
496                             FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
497                             NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
498                 if(h1 == INVALID_HANDLE_VALUE)
499                 {
500                     fclose(logff[i].fp);
501                     CloseHandle(logff[i].h);
502                     logff[i].fp = NULL;
503                     merror(FILE_ERROR, ARGV0, logff[i].file);
504                 }
505                 else if(GetFileInformationByHandle(h1, &lpFileInformation) == 0)
506                 {
507                     fclose(logff[i].fp);
508                     CloseHandle(logff[i].h);
509                     CloseHandle(h1);
510                     logff[i].fp = NULL;
511                     merror(FILE_ERROR, ARGV0, logff[i].file);;
512                 }
513                 #endif
514
515
516                 #ifdef WIN32
517                 else if(logff[i].fd != (lpFileInformation.nFileIndexLow + lpFileInformation.nFileIndexHigh))
518                 #else
519                 else if(logff[i].fd != tmp_stat.st_ino)
520                 #endif
521                 {
522                     char msg_alert[512 +1];
523
524                     snprintf(msg_alert, 512, "ossec: File rotated (inode "
525                                              "changed): '%s'.",
526                                              logff[i].file);
527
528                     /* Send message about log rotated  */
529                     SendMSG(logr_queue, msg_alert,
530                             "ossec-logcollector", LOCALFILE_MQ);
531
532                     debug1("%s: DEBUG: File inode changed. %s",
533                             ARGV0, logff[i].file);
534
535                     fclose(logff[i].fp);
536
537                     #ifdef WIN32
538                     CloseHandle(logff[i].h);
539                     CloseHandle(h1);
540                     #endif
541
542                     logff[i].fp = NULL;
543                     handle_file(i, 0, 1);
544                     continue;
545                 }
546                 #ifdef WIN32
547                 else if(logff[i].size > (lpFileInformation.nFileSizeHigh + lpFileInformation.nFileSizeLow))
548                 #else
549                 else if(logff[i].size > tmp_stat.st_size)
550                 #endif
551                 {
552                     char msg_alert[512 +1];
553
554                     snprintf(msg_alert, 512, "ossec: File size reduced "
555                                              "(inode remained): '%s'.",
556                                              logff[i].file);
557
558                     /* Send message about log rotated  */
559                     SendMSG(logr_queue, msg_alert,
560                             "ossec-logcollector", LOCALFILE_MQ);
561
562                     debug1("%s: DEBUG: File size reduced. %s",
563                             ARGV0, logff[i].file);
564
565
566                     /* Fixing size so we don't alert more than once */
567                     logff[i].size = tmp_stat.st_size;
568
569
570                     /* Getting new file. */
571                     fclose(logff[i].fp);
572
573                     #ifdef WIN32
574                     CloseHandle(logff[i].h);
575                     CloseHandle(h1);
576                     #endif
577
578                     logff[i].fp = NULL;
579                     handle_file(i, 1, 1);
580                 }
581                 #ifdef WIN32
582                 else
583                 {
584                     CloseHandle(h1);
585                 }
586                 #endif
587             }
588
589
590             /* Too many errors for the file */
591             if(logff[i].ign > open_file_attempts)
592             {
593                 /* 999 Maximum ignore */
594                 if(logff[i].ign == 999)
595                 {
596                     continue;
597                 }
598
599                 merror(LOGC_FILE_ERROR, ARGV0, logff[i].file);
600                 if(logff[i].fp)
601                 {
602                     fclose(logff[i].fp);
603                     #ifdef WIN32
604                     CloseHandle(logff[i].h);
605                     #endif
606                 }
607
608                 logff[i].fp = NULL;
609
610
611                 /* If the file has a variable date, ignore it for
612                  * today only.
613                  */
614                 if(!logff[i].ffile)
615                 {
616                     /* Variable log files should always be attempted
617                      * to be open...
618                      */
619                     //logff[i].file = NULL;
620                 }
621                 logff[i].ign = 999;
622                 continue;
623             }
624
625
626             /* File not opened */
627             if(!logff[i].fp)
628             {
629                 if(logff[i].ign >= 999)
630                     continue;
631                 else
632                 {
633                     /* Try for a few times to open the file */
634                     if(handle_file(i, 1, 1) < 0)
635                     {
636                         logff[i].ign++;
637                     }
638                     continue;
639                 }
640             }
641         }
642     }
643 }
644
645
646
647 /**int update_fname(int i): updates file name */
648 int update_fname(int i)
649 {
650     struct tm *p;
651     time_t __ctime = time(0);
652
653     char lfile[OS_FLSIZE + 1];
654     size_t ret;
655
656
657     p = localtime(&__ctime);
658
659
660     /* Handle file */
661     if(p->tm_mday == _cday)
662     {
663         return(0);
664     }
665
666
667     lfile[OS_FLSIZE] = '\0';
668     ret = strftime(lfile, OS_FLSIZE, logff[i].ffile, p);
669     if(ret == 0)
670     {
671         ErrorExit(PARSE_ERROR, ARGV0, logff[i].ffile);
672     }
673
674
675     /* Update the file name */
676     if(strcmp(lfile, logff[i].file) != 0)
677     {
678         os_free(logff[i].file);
679
680         os_strdup(lfile, logff[i].file);
681
682         verbose(VAR_LOG_MON, ARGV0, logff[i].file);
683
684         /* Setting cday to zero because other files may need
685          * to be changed.
686          */
687         _cday = 0;
688         return(1);
689     }
690
691     _cday = p->tm_mday;
692     return(0);
693 }
694
695
696 /* handle_file: Open, get the fileno, seek to the end and update mtime */
697 int handle_file(int i, int do_fseek, int do_log)
698 {
699     int fd;
700     struct stat stat_fd;
701
702     /* We must be able to open the file, fseek and get the
703      * time of change from it.
704      */
705     #ifndef WIN32
706     logff[i].fp = fopen(logff[i].file, "r");
707     if(!logff[i].fp)
708     {
709         if(do_log == 1)
710         {
711             merror(FOPEN_ERROR, ARGV0, logff[i].file);
712         }
713         return(-1);
714     }
715     /* Getting inode number for fp */
716     fd = fileno(logff[i].fp);
717     if(fstat(fd, &stat_fd) == -1)
718     {
719         merror(FILE_ERROR,ARGV0,logff[i].file);
720         fclose(logff[i].fp);
721         logff[i].fp = NULL;
722         return(-1);
723     }
724
725     logff[i].fd = stat_fd.st_ino;
726     logff[i].size =  stat_fd.st_size;
727
728
729     #else
730     BY_HANDLE_FILE_INFORMATION lpFileInformation;
731
732     logff[i].fp = NULL;
733     logff[i].h = CreateFile(logff[i].file, GENERIC_READ,
734                             FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
735                             NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
736     if(logff[i].h == INVALID_HANDLE_VALUE)
737     {
738         if(do_log == 1)
739         {
740             merror(FOPEN_ERROR, ARGV0, logff[i].file);
741         }
742         return(-1);
743     }
744     fd = _open_osfhandle((long)logff[i].h, 0);
745     if(fd == -1)
746     {
747         merror(FOPEN_ERROR, ARGV0, logff[i].file);
748         CloseHandle(logff[i].h);
749         return(-1);
750     }
751     logff[i].fp = _fdopen(fd, "r");
752     if(logff[i].fp == NULL)
753     {
754         merror(FOPEN_ERROR, ARGV0, logff[i].file);
755         CloseHandle(logff[i].h);
756         return(-1);
757     }
758
759
760     /* On windows, we also need the real inode, which is the combination
761      * of the index low + index high numbers.
762      */
763     if(GetFileInformationByHandle(logff[i].h, &lpFileInformation) == 0)
764     {
765         merror("%s: Unable to get file information by handle.", ARGV0);
766         fclose(logff[i].fp);
767         CloseHandle(logff[i].h);
768         logff[i].fp = NULL;
769         return(-1);
770     }
771
772     logff[i].fd = (lpFileInformation.nFileIndexLow + lpFileInformation.nFileIndexHigh);
773     logff[i].size = (lpFileInformation.nFileSizeHigh + lpFileInformation.nFileSizeLow);
774
775     #endif
776
777
778     /* Only seek the end of the file if set to. */
779     if(do_fseek == 1 && S_ISREG(stat_fd.st_mode))
780     {
781         /* Windows and fseek causes some weird issues.. */
782         #ifndef WIN32
783         if(fseek(logff[i].fp, 0, SEEK_END) < 0)
784         {
785             merror(FSEEK_ERROR, ARGV0,logff[i].file);
786             fclose(logff[i].fp);
787             logff[i].fp = NULL;
788             return(-1);
789         }
790         #endif
791     }
792
793
794     /* Setting ignore to zero */
795     logff[i].ign = 0;
796     return(0);
797 }
798
799
800 #ifdef WIN32
801
802 /* Remove newlines and replace tabs in the argument fields with spaces */
803 void win_format_event_string(char *string)
804 {
805     if (string == NULL) {
806         return;
807     }
808
809     while (*string != '\0') {
810         if (*string == '\n' || *string == '\r' || *string == ':') {
811             if (*string == '\n' || *string == '\r') {
812                 *string = ' ';
813             }
814
815             string++;
816
817             while (*string == '\t') {
818                 *string = ' ';
819                 string++;
820             }
821
822             continue;
823         }
824
825         string++;
826     }
827 }
828
829 #endif /* WIN32 */
830
831
832
833 /* EOF */