7eeda3d4d6f8ca7b3896be920b86ef5fe6c2792e
[ossec-hids.git] / src / logcollector / read_win_el.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 #include "shared.h"
14 #include "logcollector.h"       
15
16
17 /* This is only for windows */
18 #ifdef WIN32
19
20 #define BUFFER_SIZE 2048*256
21
22
23
24 /* Event logging local structure */
25 typedef struct _os_el
26 {
27     int time_of_last;   
28     char *name;
29
30     EVENTLOGRECORD *er;
31     HANDLE h;
32
33     DWORD record;
34
35 }os_el;
36
37
38 /** Global variables **/
39
40 /* Maximum of 9 event log sources. */
41 os_el el[9];
42 int el_last = 0;
43 void *vista_sec_id_hash = NULL;
44 void *dll_hash = NULL;
45
46
47
48 /** int startEL(char *app, os_el *el)
49  * Starts the event logging for each el 
50  */
51 int startEL(char *app, os_el *el)
52 {
53     DWORD NumberOfRecords = 0;
54     
55     /* Opening the event log */
56     el->h = OpenEventLog(NULL, app);
57     if(!el->h)
58     {
59         merror(EVTLOG_OPEN, ARGV0, app);
60         return(-1);         
61     }
62
63     el->name = app;
64     if(GetOldestEventLogRecord(el->h, &el->record) == 0)
65     {
66         /* Unable to read oldest event log record */
67         merror(EVTLOG_GETLAST, ARGV0, app);
68         CloseEventLog(el->h);
69         el->h = NULL;
70         return(-1);
71     }
72
73     if(GetNumberOfEventLogRecords(el->h, &NumberOfRecords) == 0)
74     {
75         merror(EVTLOG_GETLAST, ARGV0, app);
76         CloseEventLog(el->h);
77         el->h = NULL;
78         return(-1);
79     }
80     
81     if(NumberOfRecords <= 0)
82     {
83         return(0);
84     }
85     
86     return((int)NumberOfRecords);
87 }
88
89
90
91 /** char *el_getCategory(int category_id) 
92  * Returns a string related to the category id of the log.
93  */
94 char *el_getCategory(int category_id)
95 {
96     char *cat;
97     switch(category_id)
98     {
99         case EVENTLOG_ERROR_TYPE:
100             cat = "ERROR";
101             break;
102         case EVENTLOG_WARNING_TYPE:
103             cat = "WARNING";
104             break;
105         case EVENTLOG_INFORMATION_TYPE:
106             cat = "INFORMATION";
107             break;
108         case EVENTLOG_AUDIT_SUCCESS:
109             cat = "AUDIT_SUCCESS";
110             break;
111         case EVENTLOG_AUDIT_FAILURE:
112             cat = "AUDIT_FAILURE";
113             break;
114         default:
115             cat = "Unknown";
116             break;
117     }
118     return(cat);
119 }
120
121
122
123 /** char *el_getEventDLL(char *evt_name, char *source, char *event)
124  * Returns the event.
125  */
126 char *el_getEventDLL(char *evt_name, char *source, char *event) 
127 {
128     char *ret_str;
129     HKEY key;
130     DWORD ret;
131     char keyname[512];
132
133
134     keyname[511] = '\0';
135
136     snprintf(keyname, 510, 
137             "System\\CurrentControlSet\\Services\\EventLog\\%s\\%s", 
138             evt_name, 
139             source);
140
141
142     /* Checking if we have it in memory. */
143     ret_str = OSHash_Get(dll_hash, keyname + 42);
144     if(ret_str)
145     {
146         return(ret_str);
147     }
148
149
150     /* Opening registry */          
151     if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyname, 0, 
152                     KEY_ALL_ACCESS, &key) != ERROR_SUCCESS)
153     {
154         return(NULL);    
155     }
156
157
158     ret = MAX_PATH -1;  
159     if (RegQueryValueEx(key, "EventMessageFile", NULL, 
160                 NULL, (LPBYTE)event, &ret) != ERROR_SUCCESS)
161     {
162         event[0] = '\0';        
163         RegCloseKey(key);
164         return(NULL);
165     }
166     else
167     {
168         /* Adding to memory. */
169         char *skey;
170         char *sval;
171
172         skey = strdup(keyname + 42);
173         sval = strdup(event);
174         
175         if(skey && sval)
176         {
177             OSHash_Add(dll_hash, skey, sval); 
178         }
179         else
180         {
181             merror(MEM_ERROR, ARGV0);
182         }
183     }
184     
185     RegCloseKey(key);
186     return(event);
187 }
188
189
190
191 /** char *el_vista_getmessage() 
192  * Returns a descriptive message of the event - Vista only.
193  */
194 char *el_vista_getMessage(int evt_id_int, LPTSTR *el_sstring)
195 {
196     DWORD fm_flags = 0;
197     LPSTR message = NULL;
198     char *desc_string;
199     char evt_id[16];
200
201
202     /* Flags for format event */
203     fm_flags |= FORMAT_MESSAGE_FROM_STRING;
204     fm_flags |= FORMAT_MESSAGE_ALLOCATE_BUFFER;
205     fm_flags |= FORMAT_MESSAGE_ARGUMENT_ARRAY;
206
207
208     /* Getting descriptive message. */
209     evt_id[15] = '\0';
210     snprintf(evt_id, 15, "%d", evt_id_int);
211     
212     desc_string = OSHash_Get(vista_sec_id_hash, evt_id);
213     if(!desc_string)
214     {
215         return(NULL);
216     }
217     
218
219     if(!FormatMessage(fm_flags, desc_string, 0, 0, 
220                       (LPTSTR) &message, 0, el_sstring))
221     {
222         return(NULL);
223     }
224
225     return(message);
226 }
227
228
229
230 /** char *el_getmessage() 
231  * Returns a descriptive message of the event.
232  */
233 char *el_getMessage(EVENTLOGRECORD *er,  char *name, 
234                     char * source, LPTSTR *el_sstring) 
235 {
236     DWORD fm_flags = 0;
237     char tmp_str[257];
238     char event[MAX_PATH +1];
239     char *curr_str;
240     char *next_str;
241     LPSTR message = NULL;
242
243     HMODULE hevt;
244
245     /* Initializing variables */
246     event[MAX_PATH] = '\0';
247     tmp_str[256] = '\0';
248
249
250     /* Flags for format event */
251     fm_flags |= FORMAT_MESSAGE_FROM_HMODULE;
252     fm_flags |= FORMAT_MESSAGE_ALLOCATE_BUFFER;
253     fm_flags |= FORMAT_MESSAGE_ARGUMENT_ARRAY;
254
255
256
257     /* Get the file name from the registry (stored on event) */
258     if(!(curr_str = el_getEventDLL(name, source, event)))
259     {
260         return(NULL);       
261     }       
262
263
264
265     /* If our event has multiple libraries, try each one of them */ 
266     while((next_str = strchr(curr_str, ';')))
267     {
268         *next_str = '\0';
269
270         ExpandEnvironmentStrings(curr_str, tmp_str, 255);
271
272         /* Reverting back old value. */
273         *next_str = ';';
274         
275
276         /* Loading library. */
277         hevt = LoadLibraryEx(tmp_str, NULL, 
278                              DONT_RESOLVE_DLL_REFERENCES |
279                              LOAD_LIBRARY_AS_DATAFILE);
280         if(hevt)
281         {
282             if(!FormatMessage(fm_flags, hevt, er->EventID, 0,
283                               (LPTSTR) &message, 0, el_sstring))
284             {
285                 message = NULL;           
286             }
287             FreeLibrary(hevt);
288
289             /* If we have a message, we can return it */
290             if(message)
291                 return(message);
292         }
293
294
295         curr_str = next_str +1;
296     }
297
298     
299     /* Getting last value. */
300     ExpandEnvironmentStrings(curr_str, tmp_str, 255);
301     hevt = LoadLibraryEx(tmp_str, NULL, 
302                          DONT_RESOLVE_DLL_REFERENCES |
303                          LOAD_LIBRARY_AS_DATAFILE);
304     if(hevt)
305     {
306         int hr;    
307         if(!(hr = FormatMessage(fm_flags, hevt, er->EventID, 
308                         0,
309                         (LPTSTR) &message, 0, el_sstring)))
310         {
311             message = NULL;               
312         }
313         FreeLibrary(hevt);
314
315         /* If we have a message, we can return it */
316         if(message)
317             return(message);
318     }
319
320     return(NULL);
321 }
322
323
324
325 /** void readel(os_el *el)
326  * Reads the event log.
327  */ 
328 void readel(os_el *el, int printit)
329 {
330     DWORD _evtid = 65535;
331     DWORD nstr;
332     DWORD user_size;
333     DWORD domain_size;
334     DWORD read, needed;
335     int size_left;
336     int str_size;
337     int id;
338
339     char mbuffer[BUFFER_SIZE +1];
340     LPSTR sstr = NULL;
341
342     char *tmp_str = NULL;
343     char *category;
344     char *source;
345     char *computer_name;
346     char *descriptive_msg;
347
348     char el_user[OS_FLSIZE +1];
349     char el_domain[OS_FLSIZE +1];
350     char el_string[OS_MAXSTR +1];
351     char final_msg[OS_MAXSTR +1];
352     LPSTR el_sstring[OS_FLSIZE +1];
353
354     /* Er must point to the mbuffer */
355     el->er = (EVENTLOGRECORD *) &mbuffer; 
356
357     /* Zeroing the values */
358     el_string[OS_MAXSTR] = '\0';
359     el_user[OS_FLSIZE] = '\0';
360     el_domain[OS_FLSIZE] = '\0';
361     final_msg[OS_MAXSTR] = '\0';
362     el_sstring[0] = NULL;
363     el_sstring[OS_FLSIZE] = NULL;
364
365
366     /* Event log is not open */
367     if(!el->h)
368     {
369         return;
370     }
371
372     /* Reading the event log */     
373     while(ReadEventLog(el->h, 
374                 EVENTLOG_FORWARDS_READ | EVENTLOG_SEQUENTIAL_READ,
375                 0,
376                 el->er, BUFFER_SIZE -1, &read, &needed))
377     {
378         if(!printit)
379         {
380             /* Setting er to the beginning of the buffer */
381             el->er = (EVENTLOGRECORD *)&mbuffer;
382             continue;
383         }
384
385         
386         while(read > 0)
387         {
388
389             /* We need to initialize every variable before the loop */
390             category = el_getCategory(el->er->EventType);
391             source = (LPSTR) ((LPBYTE) el->er + sizeof(EVENTLOGRECORD));
392             computer_name = source + strlen(source) + 1;
393             descriptive_msg = NULL;
394
395
396             /* Getting event id. */
397             id = (int)el->er->EventID & _evtid;
398                             
399
400
401             /* Initialing domain/user size */
402             user_size = 255; domain_size = 255;
403             el_domain[0] = '\0';
404             el_user[0] = '\0';
405
406
407
408             /* We must have some description */
409             if(el->er->NumStrings)
410             {   
411                 size_left = OS_MAXSTR - OS_SIZE_1024;   
412
413                 sstr = (LPSTR)((LPBYTE)el->er + el->er->StringOffset);
414                 el_string[0] = '\0';
415
416                 for (nstr = 0;nstr < el->er->NumStrings;nstr++)
417                 {
418                     str_size = strlen(sstr);
419                     if(size_left > 1)
420                     {
421                         strncat(el_string, sstr, size_left);
422                     }
423
424                     tmp_str = strchr(el_string, '\0');
425                     if(tmp_str)
426                     {
427                         *tmp_str = ' ';         
428                         tmp_str++; *tmp_str = '\0';
429                     }
430                     else
431                     {
432                         merror("%s: Invalid application string (size+)",
433                                ARGV0); 
434                     }
435                     size_left-=str_size + 2;
436
437                     if(nstr <= 92)
438                     {
439                         el_sstring[nstr] = (LPSTR)sstr;
440                         el_sstring[nstr +1] = NULL;
441                     }
442
443                     sstr = strchr( (LPSTR)sstr, '\0');
444                     if(sstr)
445                         sstr++;
446                     else
447                         break;     
448                 }
449
450                 /* Get a more descriptive message (if available) */
451                 if(isVista && strcmp(el->name, "Security") == 0)
452                 {
453                     descriptive_msg = el_vista_getMessage(id, el_sstring);
454                 }
455
456                 else
457                 {
458                     descriptive_msg = el_getMessage(el->er, 
459                                                     el->name, 
460                                                     source, 
461                                                     el_sstring);
462                 }
463                 
464                 if(descriptive_msg != NULL)
465                 {
466                     /* Remove any \n or \r */
467                     /* Replace tabs from the argument field to spaces.
468                      * So whenever we have option:\tvalue\t, it will
469                      * become option: value\t
470                      */
471                     tmp_str = descriptive_msg;    
472                     while(*tmp_str != '\0')
473                     {
474                         if(*tmp_str == '\n')
475                             *tmp_str = ' ';
476                         else if(*tmp_str == '\r')
477                             *tmp_str = ' ';
478                         else if((*tmp_str == ':') && (tmp_str[1] == '\t'))
479                         {
480                             tmp_str[1] = ' ';
481                             tmp_str++;
482                         }
483                                     
484                         tmp_str++;
485                     }
486                 }
487             }
488             else
489             {
490                 strncpy(el_string, "(no message)", 128);        
491             }
492
493
494             /* Getting username */
495             if(el->er->UserSidLength)
496             {
497                 SID_NAME_USE account_type;
498                 if(!LookupAccountSid(NULL, 
499                                     (SID *)((LPSTR)el->er + 
500                                     el->er->UserSidOffset),
501                                     el_user, 
502                                     &user_size, 
503                                     el_domain, 
504                                     &domain_size, 
505                                     &account_type))             
506                 {
507                     strncpy(el_user, "(no user)", 255);
508                     strncpy(el_domain, "no domain", 255);
509                 }
510
511             }
512
513             else if(isVista && strcmp(el->name, "Security") == 0)
514             {
515                 int uid_array_id = -1;
516
517                 switch(id)
518                 {
519                     case 4624:
520                         uid_array_id = 5;
521                         break;
522                     case 4634:
523                         uid_array_id = 1;
524                         break;    
525                     case 4647:
526                         uid_array_id = 1;
527                         break;    
528                     case 4769:
529                         uid_array_id = 0;
530                         break;
531                 }
532
533                 if((uid_array_id >= 0) && 
534                    el_sstring[uid_array_id] &&
535                    el_sstring[uid_array_id +1])
536                 {
537                     strncpy(el_user, el_sstring[uid_array_id], OS_FLSIZE);
538                     strncpy(el_domain, el_sstring[uid_array_id +1], OS_FLSIZE);
539                 }
540                 else
541                 {
542                     strncpy(el_user, "(no user)", 255);
543                     strncpy(el_domain, "no domain", 255);
544                 }
545             }
546             
547             else
548             {
549                 strncpy(el_user, "(no user)", 255);     
550                 strncpy(el_domain, "no domain", 255);   
551             }
552
553
554             if(printit)
555             {
556                 DWORD _evtid = 65535;
557                 int id = (int)el->er->EventID & _evtid; 
558                
559                 final_msg[OS_MAXSTR - OS_LOG_HEADER] = '\0'; 
560                 final_msg[OS_MAXSTR - OS_LOG_HEADER -1] = '\0'; 
561                 
562                 snprintf(final_msg, OS_MAXSTR - OS_LOG_HEADER -1, 
563                         "WinEvtLog: %s: %s(%d): %s: %s: %s: %s: %s", 
564                         el->name,
565                         category, 
566                         id,
567                         source,
568                         el_user,
569                         el_domain,
570                         computer_name,
571                         descriptive_msg != NULL?descriptive_msg:el_string);     
572                 
573                 if(SendMSG(logr_queue, final_msg, "WinEvtLog",
574                             LOCALFILE_MQ) < 0)
575                 {
576                     merror(QUEUE_SEND, ARGV0);
577                 }
578             }
579
580             if(descriptive_msg != NULL)
581             {
582                 LocalFree(descriptive_msg);
583             }
584
585             /* Changing the point to the er */
586             read -= el->er->Length;
587             el->er = (EVENTLOGRECORD *)((LPBYTE) el->er + el->er->Length);
588         }               
589
590         /* Setting er to the beginning of the buffer */ 
591         el->er = (EVENTLOGRECORD *)&mbuffer;
592     }
593
594
595     id = GetLastError();
596     if(id == ERROR_HANDLE_EOF)
597     {
598         return;
599     }
600
601
602     /* Event log was cleared. */
603     else if(id == ERROR_EVENTLOG_FILE_CHANGED)
604     {
605         char msg_alert[512 +1];
606         msg_alert[512] = '\0';
607         merror("%s: WARN: Event log cleared: '%s'", ARGV0, el->name);
608         
609
610         /* Send message about cleared */
611         snprintf(msg_alert, 512, "ossec: Event log cleared: '%s'", el->name);
612         SendMSG(logr_queue, msg_alert, "WinEvtLog", LOCALFILE_MQ);
613
614
615         /* Closing the event log and reopenning. */
616         CloseEventLog(el->h);
617         el->h = NULL;
618
619         /* Reopening. */
620         if(startEL(el->name, el) < 0)
621         {
622             merror("%s: ERROR: Unable to reopen event log '%s'", 
623                    ARGV0, el->name);
624         }
625     }
626
627     else
628     {
629         debug1("%s: WARN: Error reading event log: %d", ARGV0, id);
630     }
631 }
632
633
634 /** void win_read_vista_sec()
635  * Reads vista security description.
636  */
637 void win_read_vista_sec()
638 {
639     char *p;
640     char buf[OS_MAXSTR +1];
641     FILE *fp;
642
643
644     /* Vista security csv. */
645     fp = fopen("vista_sec.csv", "r");
646     if(!fp)
647     {
648         merror("%s: ERROR: Unable to read vista security descriptions.",
649                ARGV0);
650         exit(1);
651     }
652
653
654     /* Creating the hash. */
655     vista_sec_id_hash = OSHash_Create();
656     if(!vista_sec_id_hash)
657     {
658         merror("%s: ERROR: Unable to read vista security descriptions.",
659                ARGV0);
660         exit(1);
661     }
662
663     
664     /* Reading the whole file and adding to memory. */
665     while(fgets(buf, OS_MAXSTR, fp) != NULL)
666     {
667         char *key;
668         char *desc;
669         
670         /* Getting the last occurence of \n */
671         if ((p = strrchr(buf, '\n')) != NULL)
672         {
673             *p = '\0';
674         }
675
676         p = strchr(buf, ',');
677         if(!p)
678         {
679             merror("%s: ERROR: Invalid entry on the Vista security "
680                    "description.", ARGV0);
681             continue;
682         }
683
684         *p = '\0';
685         p++;
686
687         /* Removing white spaces. */
688         while(*p == ' ')
689             p++;
690
691         
692         /* Allocating memory. */
693         desc = strdup(p);
694         key = strdup(buf);
695         if(!key || !desc)
696         {
697             merror("%s: ERROR: Invalid entry on the Vista security "
698                    "description.", ARGV0);
699             continue;
700         }
701         
702         
703         /* Inserting on hash. */    
704         OSHash_Add(vista_sec_id_hash, key, desc);
705     }
706
707     fclose(fp);
708 }
709
710
711 /** void win_startel()
712  * Starts the event logging for windows
713  */
714 void win_startel(char *evt_log)
715 {
716     int entries_count = 0;
717     
718     /* Maximum size */
719     if(el_last == 9)
720     {
721         merror(EVTLOG_DUP, ARGV0, evt_log);
722         return;
723     }
724
725
726     /* Creating the dll hash. */
727     if(!dll_hash)
728     {
729         dll_hash = OSHash_Create();
730         if(!dll_hash)
731         {
732             merror("%s: ERROR: Unable to create DLL hash.",
733                     ARGV0);
734         }
735     }
736
737     
738     /* Starting event log -- going to last available record */
739     if((entries_count = startEL(evt_log, &el[el_last])) < 0)
740     {
741         merror(INV_EVTLOG, ARGV0, evt_log);
742         return;
743     }
744     else
745     {
746         readel(&el[el_last], 0);
747     }
748     el_last++;
749 }
750
751
752 /** void win_readel() 
753  * Reads the event logging for windows
754  */
755 void win_readel()
756 {
757     int i = 0;
758     
759     /* Sleep plus 2 seconds before reading again */
760     Sleep(2000);
761     
762     for(;i<el_last;i++)
763         readel(&el[i],1);
764 }
765
766
767 #endif
768
769 /* EOF */