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