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