new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / logcollector / read_win_el.c
1 /* Copyright (C) 2009 Trend Micro Inc.
2  * All right reserved.
3  *
4  * This program is a free software; you can redistribute it
5  * and/or modify it under the terms of the GNU General Public
6  * License (version 2) as published by the FSF - Free Software
7  * Foundation
8  */
9
10 #include "shared.h"
11 #include "logcollector.h"
12
13 #ifdef WIN32
14
15 #define BUFFER_SIZE 2048*256
16
17 /* Event logging local structure */
18 typedef struct _os_el {
19     int time_of_last;
20     char *name;
21
22     EVENTLOGRECORD *er;
23     HANDLE h;
24
25     DWORD record;
26
27 } os_el;
28
29 /** Global variables **/
30
31 /* Maximum of 9 event log sources */
32 os_el el[9];
33 int el_last = 0;
34 void *vista_sec_id_hash = NULL;
35 void *dll_hash = NULL;
36
37
38 /* Start the event logging for each el */
39 int startEL(char *app, os_el *el)
40 {
41     DWORD NumberOfRecords = 0;
42
43     /* Open the event log */
44     el->h = OpenEventLog(NULL, app);
45     if (!el->h) {
46         merror(EVTLOG_OPEN, ARGV0, app);
47         return (-1);
48     }
49
50     el->name = app;
51     if (GetOldestEventLogRecord(el->h, &el->record) == 0) {
52         /* Unable to read oldest event log record */
53         merror(EVTLOG_GETLAST, ARGV0, app);
54         CloseEventLog(el->h);
55         el->h = NULL;
56         return (-1);
57     }
58
59     if (GetNumberOfEventLogRecords(el->h, &NumberOfRecords) == 0) {
60         merror(EVTLOG_GETLAST, ARGV0, app);
61         CloseEventLog(el->h);
62         el->h = NULL;
63         return (-1);
64     }
65
66     if (NumberOfRecords <= 0) {
67         return (0);
68     }
69
70     return ((int)NumberOfRecords);
71 }
72
73 /* Returns a string that is a human readable datetime from an epoch int */
74 char *epoch_to_human(time_t epoch)
75 {
76     struct tm   *ts;
77     static char buf[80];
78
79     ts = localtime(&epoch);
80     strftime(buf, sizeof(buf), "%Y %b %d %H:%M:%S", ts);
81     return (buf);
82 }
83
84 /* Returns a string related to the category id of the log */
85 char *el_getCategory(int category_id)
86 {
87     char *cat;
88     switch (category_id) {
89         case EVENTLOG_ERROR_TYPE:
90             cat = "ERROR";
91             break;
92         case EVENTLOG_WARNING_TYPE:
93             cat = "WARNING";
94             break;
95         case EVENTLOG_INFORMATION_TYPE:
96             cat = "INFORMATION";
97             break;
98         case EVENTLOG_AUDIT_SUCCESS:
99             cat = "AUDIT_SUCCESS";
100             break;
101         case EVENTLOG_AUDIT_FAILURE:
102             cat = "AUDIT_FAILURE";
103             break;
104         default:
105             cat = "Unknown";
106             break;
107     }
108     return (cat);
109 }
110
111 /* Returns the event */
112 char *el_getEventDLL(char *evt_name, char *source, char *event)
113 {
114     char *ret_str;
115     HKEY key;
116     DWORD ret;
117     char keyname[512];
118
119     keyname[511] = '\0';
120
121     snprintf(keyname, 510,
122              "System\\CurrentControlSet\\Services\\EventLog\\%s\\%s",
123              evt_name,
124              source);
125
126     /* Check if we have it in memory */
127     ret_str = OSHash_Get(dll_hash, keyname + 42);
128     if (ret_str) {
129         return (ret_str);
130     }
131
132     /* Open Registry */
133     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyname, 0,
134                      KEY_ALL_ACCESS, &key) != ERROR_SUCCESS) {
135         return (NULL);
136     }
137
138     ret = MAX_PATH - 1;
139     if (RegQueryValueEx(key, "EventMessageFile", NULL,
140                         NULL, (LPBYTE)event, &ret) != ERROR_SUCCESS) {
141         event[0] = '\0';
142         RegCloseKey(key);
143         return (NULL);
144     } else {
145         /* Adding to memory */
146         char *skey;
147         char *sval;
148
149         skey = strdup(keyname + 42);
150         sval = strdup(event);
151
152         if (skey && sval) {
153             OSHash_Add(dll_hash, skey, sval);
154         } else {
155             merror(MEM_ERROR, ARGV0, errno, strerror(errno));
156         }
157     }
158
159     RegCloseKey(key);
160     return (event);
161 }
162
163 /* Returns a descriptive message of the event - Vista only */
164 char *el_vista_getMessage(int evt_id_int, LPTSTR *el_sstring)
165 {
166     DWORD fm_flags = 0;
167     LPSTR message = NULL;
168     char *desc_string;
169     char evt_id[16];
170
171     /* Flags for format event */
172     fm_flags |= FORMAT_MESSAGE_FROM_STRING;
173     fm_flags |= FORMAT_MESSAGE_ALLOCATE_BUFFER;
174     fm_flags |= FORMAT_MESSAGE_ARGUMENT_ARRAY;
175
176     /* Get descriptive message */
177     evt_id[15] = '\0';
178     snprintf(evt_id, 15, "%d", evt_id_int);
179
180     desc_string = OSHash_Get(vista_sec_id_hash, evt_id);
181     if (!desc_string) {
182         return (NULL);
183     }
184
185     if (!FormatMessage(fm_flags, desc_string, 0, 0,
186                        (LPTSTR) &message, 0, el_sstring)) {
187         return (NULL);
188     }
189
190     return (message);
191 }
192
193 /* Returns a descriptive message of the event */
194 char *el_getMessage(EVENTLOGRECORD *er,  char *name,
195                     char *source, LPTSTR *el_sstring)
196 {
197     DWORD fm_flags = 0;
198     char tmp_str[257];
199     char event[MAX_PATH + 1];
200     char *curr_str;
201     char *next_str;
202     LPSTR message = NULL;
203
204     HMODULE hevt;
205
206     /* Initialize variables */
207     event[MAX_PATH] = '\0';
208     tmp_str[256] = '\0';
209
210     /* Flags for format event */
211     fm_flags |= FORMAT_MESSAGE_FROM_HMODULE;
212     fm_flags |= FORMAT_MESSAGE_ALLOCATE_BUFFER;
213     fm_flags |= FORMAT_MESSAGE_ARGUMENT_ARRAY;
214
215     /* Get the file name from the registry (stored on event) */
216     if (!(curr_str = el_getEventDLL(name, source, event))) {
217         return (NULL);
218     }
219
220     /* If our event has multiple libraries, try each one of them */
221     while ((next_str = strchr(curr_str, ';'))) {
222         *next_str = '\0';
223
224         ExpandEnvironmentStrings(curr_str, tmp_str, 255);
225
226         /* Revert back old value */
227         *next_str = ';';
228
229         /* Load library */
230         hevt = LoadLibraryEx(tmp_str, NULL,
231                              DONT_RESOLVE_DLL_REFERENCES |
232                              LOAD_LIBRARY_AS_DATAFILE);
233         if (hevt) {
234             if (!FormatMessage(fm_flags, hevt, er->EventID, 0,
235                                (LPTSTR) &message, 0, el_sstring)) {
236                 message = NULL;
237             }
238             FreeLibrary(hevt);
239
240             /* If we have a message, we can return it */
241             if (message) {
242                 return (message);
243             }
244         }
245
246         curr_str = next_str + 1;
247     }
248
249     /* Get last value */
250     ExpandEnvironmentStrings(curr_str, tmp_str, 255);
251     hevt = LoadLibraryEx(tmp_str, NULL,
252                          DONT_RESOLVE_DLL_REFERENCES |
253                          LOAD_LIBRARY_AS_DATAFILE);
254     if (hevt) {
255         int hr;
256         if (!(hr = FormatMessage(fm_flags, hevt, er->EventID,
257                                  0,
258                                  (LPTSTR) &message, 0, el_sstring))) {
259             message = NULL;
260         }
261         FreeLibrary(hevt);
262
263         /* If we have a message, we can return it */
264         if (message) {
265             return (message);
266         }
267     }
268
269     return (NULL);
270 }
271
272 /* Reads the event log */
273 void readel(os_el *el, int printit)
274 {
275     DWORD _evtid = 65535;
276     DWORD nstr;
277     DWORD user_size;
278     DWORD domain_size;
279     DWORD read, needed;
280     int size_left;
281     int str_size;
282     int id;
283
284     char mbuffer[BUFFER_SIZE + 1];
285     LPSTR sstr = NULL;
286
287     char *tmp_str = NULL;
288     char *category;
289     char *source;
290     char *computer_name;
291     char *descriptive_msg;
292
293     char el_user[OS_FLSIZE + 1];
294     char el_domain[OS_FLSIZE + 1];
295     char el_string[OS_MAXSTR + 1];
296     char final_msg[OS_MAXSTR + 1];
297     LPSTR el_sstring[OS_FLSIZE + 1];
298
299     /* er must point to the mbuffer */
300     el->er = (EVENTLOGRECORD *) &mbuffer;
301
302     /* Zero the values */
303     el_string[OS_MAXSTR] = '\0';
304     el_user[OS_FLSIZE] = '\0';
305     el_domain[OS_FLSIZE] = '\0';
306     final_msg[OS_MAXSTR] = '\0';
307     el_sstring[0] = NULL;
308     el_sstring[OS_FLSIZE] = NULL;
309
310     /* Event log is not open */
311     if (!el->h) {
312         return;
313     }
314
315     /* Read the event log */
316     while (ReadEventLog(el->h,
317                         EVENTLOG_FORWARDS_READ | EVENTLOG_SEQUENTIAL_READ,
318                         0,
319                         el->er, BUFFER_SIZE - 1, &read, &needed)) {
320         if (!printit) {
321             /* Set er to the beginning of the buffer */
322             el->er = (EVENTLOGRECORD *)&mbuffer;
323             continue;
324         }
325
326
327         while (read > 0) {
328             /* We need to initialize every variable before the loop */
329             category = el_getCategory(el->er->EventType);
330             source = (LPSTR) ((LPBYTE) el->er + sizeof(EVENTLOGRECORD));
331             computer_name = source + strlen(source) + 1;
332             descriptive_msg = NULL;
333
334             /* Get event id */
335             id = (int)el->er->EventID & _evtid;
336
337             /* Initialize domain/user size */
338             user_size = 255;
339             domain_size = 255;
340             el_domain[0] = '\0';
341             el_user[0] = '\0';
342
343             /* We must have some description */
344             if (el->er->NumStrings) {
345                 size_left = OS_MAXSTR - OS_SIZE_1024;
346
347                 sstr = (LPSTR)((LPBYTE)el->er + el->er->StringOffset);
348                 el_string[0] = '\0';
349
350                 for (nstr = 0; nstr < el->er->NumStrings; nstr++) {
351                     str_size = strlen(sstr);
352                     if (size_left > 1) {
353                         strncat(el_string, sstr, size_left);
354                     }
355
356                     tmp_str = strchr(el_string, '\0');
357                     if (tmp_str) {
358                         *tmp_str = ' ';
359                         tmp_str++;
360                         *tmp_str = '\0';
361                     } else {
362                         merror("%s: Invalid application string (size+)",
363                                ARGV0);
364                     }
365                     size_left -= str_size + 2;
366
367                     if (nstr <= 92) {
368                         el_sstring[nstr] = (LPSTR)sstr;
369                         el_sstring[nstr + 1] = NULL;
370                     }
371
372                     sstr = strchr( (LPSTR)sstr, '\0');
373                     if (sstr) {
374                         sstr++;
375                     } else {
376                         break;
377                     }
378                 }
379
380                 /* Get a more descriptive message (if available) */
381                 if (isVista && strcmp(el->name, "Security") == 0) {
382                     descriptive_msg = el_vista_getMessage(id, el_sstring);
383                 }
384
385                 else {
386                     descriptive_msg = el_getMessage(el->er,
387                                                     el->name,
388                                                     source,
389                                                     el_sstring);
390                 }
391
392                 if (descriptive_msg != NULL) {
393                     /* format message */
394                     win_format_event_string(descriptive_msg);
395                 }
396             } else {
397                 strncpy(el_string, "(no message)", 128);
398             }
399
400             /* Get username */
401             if (el->er->UserSidLength) {
402                 SID_NAME_USE account_type;
403                 if (!LookupAccountSid(NULL,
404                                       (SID *)((LPSTR)el->er +
405                                               el->er->UserSidOffset),
406                                       el_user,
407                                       &user_size,
408                                       el_domain,
409                                       &domain_size,
410                                       &account_type)) {
411                     strncpy(el_user, "(no user)", 255);
412                     strncpy(el_domain, "no domain", 255);
413                 }
414             }
415
416             else if (isVista && strcmp(el->name, "Security") == 0) {
417                 int uid_array_id = -1;
418
419                 switch (id) {
420                     case 4624:
421                         uid_array_id = 5;
422                         break;
423                     case 4634:
424                         uid_array_id = 1;
425                         break;
426                     case 4647:
427                         uid_array_id = 1;
428                         break;
429                     case 4769:
430                         uid_array_id = 0;
431                         break;
432                 }
433
434                 if ((uid_array_id >= 0) &&
435                         el_sstring[uid_array_id] &&
436                         el_sstring[uid_array_id + 1]) {
437                     strncpy(el_user, el_sstring[uid_array_id], OS_FLSIZE);
438                     strncpy(el_domain, el_sstring[uid_array_id + 1], OS_FLSIZE);
439                 } else {
440                     strncpy(el_user, "(no user)", 255);
441                     strncpy(el_domain, "no domain", 255);
442                 }
443             }
444
445             else {
446                 strncpy(el_user, "(no user)", 255);
447                 strncpy(el_domain, "no domain", 255);
448             }
449
450             if (printit) {
451                 DWORD _evtid = 65535;
452                 int id = (int)el->er->EventID & _evtid;
453
454                 final_msg[OS_MAXSTR - OS_LOG_HEADER] = '\0';
455                 final_msg[OS_MAXSTR - OS_LOG_HEADER - 1] = '\0';
456
457                 snprintf(final_msg, OS_MAXSTR - OS_LOG_HEADER - 1,
458                          "%s WinEvtLog: %s: %s(%d): %s: %s: %s: %s: %s",
459                          epoch_to_human((int)el->er->TimeGenerated),
460                          el->name,
461                          category,
462                          id,
463                          source,
464                          el_user,
465                          el_domain,
466                          computer_name,
467                          descriptive_msg != NULL ? descriptive_msg : el_string);
468
469                 if (SendMSG(logr_queue, final_msg, "WinEvtLog",
470                             LOCALFILE_MQ) < 0) {
471                     merror(QUEUE_SEND, ARGV0);
472                 }
473             }
474
475             if (descriptive_msg != NULL) {
476                 LocalFree(descriptive_msg);
477             }
478
479             /* Change the point to the er */
480             read -= el->er->Length;
481             el->er = (EVENTLOGRECORD *)((LPBYTE) el->er + el->er->Length);
482         }
483
484         /* Set er to the beginning of the buffer */
485         el->er = (EVENTLOGRECORD *)&mbuffer;
486     }
487
488     id = GetLastError();
489     if (id == ERROR_HANDLE_EOF) {
490         return;
491     }
492
493     /* Event log was cleared */
494     else if (id == ERROR_EVENTLOG_FILE_CHANGED) {
495         char msg_alert[512 + 1];
496         msg_alert[512] = '\0';
497         merror("%s: WARN: Event log cleared: '%s'", ARGV0, el->name);
498
499         /* Send message about cleared */
500         snprintf(msg_alert, 512, "ossec: Event log cleared: '%s'", el->name);
501         SendMSG(logr_queue, msg_alert, "WinEvtLog", LOCALFILE_MQ);
502
503         /* Close the event log and reopen */
504         CloseEventLog(el->h);
505         el->h = NULL;
506
507         /* Reopen */
508         if (startEL(el->name, el) < 0) {
509             merror("%s: ERROR: Unable to reopen event log '%s'",
510                    ARGV0, el->name);
511         }
512     }
513
514     else {
515         debug1("%s: WARN: Error reading event log: %d", ARGV0, id);
516     }
517 }
518
519 /* Read Windows Vista security description */
520 void win_read_vista_sec()
521 {
522     char *p;
523     char buf[OS_MAXSTR + 1];
524     FILE *fp;
525
526     /* Vista security */
527     fp = fopen("vista_sec.txt", "r");
528     if (!fp) {
529         merror("%s: ERROR: Unable to read vista security descriptions.",
530                ARGV0);
531         exit(1);
532     }
533
534     /* Creating the hash */
535     vista_sec_id_hash = OSHash_Create();
536     if (!vista_sec_id_hash) {
537         merror("%s: ERROR: Unable to read vista security descriptions.",
538                ARGV0);
539         exit(1);
540     }
541
542     /* Read the whole file and add it to memory */
543     while (fgets(buf, OS_MAXSTR, fp) != NULL) {
544         char *key;
545         char *desc;
546
547         /* Get the last occurrence of \n */
548         if ((p = strrchr(buf, '\n')) != NULL) {
549             *p = '\0';
550         }
551
552         p = strchr(buf, ',');
553         if (!p) {
554             merror("%s: ERROR: Invalid entry on the Vista security "
555                    "description.", ARGV0);
556             continue;
557         }
558
559         *p = '\0';
560         p++;
561
562         /* Remove whitespace */
563         while (*p == ' ') {
564             p++;
565         }
566
567         /* Allocate memory */
568         desc = strdup(p);
569         key = strdup(buf);
570         if (!key || !desc) {
571             merror("%s: ERROR: Invalid entry on the Vista security "
572                    "description.", ARGV0);
573             continue;
574         }
575
576         /* Insert on hash */
577         OSHash_Add(vista_sec_id_hash, key, desc);
578     }
579
580     fclose(fp);
581 }
582
583 /* Start the event logging for windows */
584 void win_startel(char *evt_log)
585 {
586     int entries_count = 0;
587
588     /* Maximum size */
589     if (el_last == 9) {
590         merror(EVTLOG_DUP, ARGV0, evt_log);
591         return;
592     }
593
594     /* Create the DLL hash */
595     if (!dll_hash) {
596         dll_hash = OSHash_Create();
597         if (!dll_hash) {
598             merror("%s: ERROR: Unable to create DLL hash.",
599                    ARGV0);
600         }
601     }
602
603     /* Start event log -- going to last available record */
604     if ((entries_count = startEL(evt_log, &el[el_last])) < 0) {
605         merror(INV_EVTLOG, ARGV0, evt_log);
606         return;
607     } else {
608         readel(&el[el_last], 0);
609     }
610     el_last++;
611 }
612
613 /* Read the event logging for windows */
614 void win_readel()
615 {
616     int i = 0;
617
618     /* Sleep plus 2 seconds before reading again */
619     Sleep(2000);
620
621     for (; i < el_last; i++) {
622         readel(&el[i], 1);
623     }
624 }
625
626 #endif
627