new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / logcollector / read_win_event_channel.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 #ifdef WIN32
11 #ifdef EVENTCHANNEL_SUPPORT
12
13 /* Saying we are on Vista in order to have the API */
14 #define _WIN32_WINNT 0x0600
15
16 /* Using Secure APIs */
17 #define MINGW_HAS_SECURE_API 1
18
19 /* Bookmarks directory */
20 #define BOOKMARKS_DIR "bookmarks"
21
22 #ifndef WC_ERR_INVALID_CHARS
23 #define WC_ERR_INVALID_CHARS 0x80
24 #endif
25
26 /* Logging levels */
27 #define WINEVENT_AUDIT          0
28 #define WINEVENT_CRITICAL       1
29 #define WINEVENT_ERROR          2
30 #define WINEVENT_WARNING        3
31 #define WINEVENT_INFORMATION    4
32 #define WINEVENT_VERBOSE        5
33
34 /* Audit types */
35 #define WINEVENT_AUDIT_FAILURE 0x10000000000000LL
36 #define WINEVENT_AUDIT_SUCCESS 0x20000000000000LL
37
38 #include "shared.h"
39 #include "logcollector.h"
40
41 #include <stdint.h>
42 #include <winevt.h>
43 #include <sec_api/stdlib_s.h>
44 #include <winerror.h>
45 #include <sddl.h>
46
47 typedef struct _os_event {
48     char *name;
49     unsigned int id;
50     char *source;
51     SID *uid;
52     char *user;
53     char *domain;
54     char *computer;
55     char *message;
56     ULONGLONG time_created;
57     char *timestamp;
58     int64_t keywords;
59     int64_t level;
60     char *category;
61 } os_event;
62
63 typedef struct _os_channel {
64     char *evt_log;
65     char *bookmark_name;
66     char bookmark_enabled;
67     char bookmark_filename[OS_MAXSTR];
68 } os_channel;
69
70
71 void free_event(os_event *event)
72 {
73     free(event->name);
74     free(event->source);
75     free(event->user);
76     free(event->domain);
77     free(event->computer);
78     free(event->message);
79     free(event->timestamp);
80 }
81
82 char *convert_windows_string(LPCWSTR string)
83 {
84     char *dest = NULL;
85     size_t size = 0;
86     int result = 0;
87
88     if (string == NULL) {
89         return (NULL);
90     }
91
92     /* Determine size required */
93     size = WideCharToMultiByte(CP_UTF8,
94                                WC_ERR_INVALID_CHARS,
95                                string,
96                                -1,
97                                NULL,
98                                0,
99                                NULL,
100                                NULL);
101
102     if (size == 0) {
103         log2file(
104             "%s: ERROR: Could not WideCharToMultiByte() when determining size which returned (%lu)",
105             ARGV0,
106             GetLastError());
107         return (NULL);
108     }
109
110     if ((dest = calloc(size, sizeof(char))) == NULL) {
111         log2file(
112             "%s: ERROR: Could not calloc() memory for WideCharToMultiByte() which returned [(%d)-(%s)]",
113             ARGV0,
114             errno,
115             strerror(errno)
116         );
117         return (NULL);
118     }
119
120     result = WideCharToMultiByte(CP_UTF8,
121                                  WC_ERR_INVALID_CHARS,
122                                  string,
123                                  -1,
124                                  dest,
125                                  size,
126                                  NULL,
127                                  NULL);
128
129     if (result == 0) {
130         log2file(
131             "%s: ERROR: Could not WideCharToMultiByte() which returned (%lu)",
132             ARGV0,
133             GetLastError());
134         free(dest);
135         return (NULL);
136     }
137
138     return (dest);
139 }
140
141 wchar_t *convert_unix_string(char *string)
142 {
143     wchar_t *dest = NULL;
144     size_t size = 0;
145     int result = 0;
146
147     if (string == NULL) {
148         return (NULL);
149     }
150
151     /* Determine size required */
152     size = MultiByteToWideChar(CP_UTF8,
153                                MB_ERR_INVALID_CHARS,
154                                string,
155                                -1,
156                                NULL,
157                                0);
158
159     if (size == 0) {
160         log2file(
161             "%s: ERROR: Could not MultiByteToWideChar() when determining size which returned (%lu)",
162             ARGV0,
163             GetLastError());
164         return (NULL);
165     }
166
167     if ((dest = calloc(size, sizeof(wchar_t))) == NULL) {
168         log2file(
169             "%s: ERROR: Could not calloc() memory for MultiByteToWideChar() which returned [(%d)-(%s)]",
170             ARGV0,
171             errno,
172             strerror(errno));
173         return (NULL);
174     }
175
176     result = MultiByteToWideChar(CP_UTF8,
177                                  MB_ERR_INVALID_CHARS,
178                                  string,
179                                  -1,
180                                  dest,
181                                  size);
182
183     if (result == 0) {
184         log2file(
185             "%s: ERROR: Could not MultiByteToWideChar() which returned (%lu)",
186             ARGV0,
187             GetLastError());
188         free(dest);
189         return (NULL);
190     }
191
192     return (dest);
193 }
194
195 /* Filter escape characters */
196
197 char* filter_special_chars(const char *string) {
198     int i, j = 0;
199     int n = strlen(string);
200     char *filtered = malloc(n + 1);
201
202     if (!filtered)
203         return NULL;
204
205     for (i = 0; i <= n; i++)
206         filtered[j++] = (string[i] == '\\') ? string[++i] : string[i];
207
208     return filtered;
209 }
210
211 char *get_property_value(PEVT_VARIANT value)
212 {
213     if (value->Type == EvtVarTypeNull) {
214         return (NULL);
215     }
216
217     return (convert_windows_string(value->StringVal));
218 }
219
220 int get_username_and_domain(os_event *event)
221 {
222     int result = 0;
223     int status = 0;
224     DWORD user_length = 0;
225     DWORD domain_length = 0;
226     SID_NAME_USE account_type;
227     LPTSTR StringSid = NULL;
228
229     /* Try to convert SID to a string. This isn't necessary to make
230      * things work but it is nice to have for error and debug logging.
231      */
232     if (!ConvertSidToStringSid(event->uid, &StringSid)) {
233         debug1(
234             "%s: WARN: Could not convert SID to string which returned (%lu)",
235             ARGV0,
236             GetLastError());
237     }
238
239     debug1("%s: DEBUG: Performing a LookupAccountSid() on (%s)",
240            ARGV0,
241            StringSid ? StringSid : "unknown");
242
243     /* Make initial call to get buffer size */
244     result = LookupAccountSid(NULL,
245                               event->uid,
246                               NULL,
247                               &user_length,
248                               NULL,
249                               &domain_length,
250                               &account_type);
251
252     if (result != FALSE || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
253         /* Not having a user can be normal */
254         goto cleanup;
255     }
256
257     if ((event->user = calloc(user_length, sizeof(char))) == NULL) {
258         log2file(
259             "%s: ERROR: Could not lookup SID (%s) due to calloc() failure on user which returned [(%d)-(%s)]",
260             ARGV0,
261             StringSid ? StringSid : "unknown",
262             errno,
263             strerror(errno));
264         goto cleanup;
265     }
266
267     if ((event->domain = calloc(domain_length, sizeof(char))) == NULL) {
268         log2file(
269             "%s: ERROR: Could not lookup SID (%s) due to calloc() failure on domain which returned [(%d)-(%s)]",
270             ARGV0,
271             StringSid ? StringSid : "unknown",
272             errno,
273             strerror(errno));
274         goto cleanup;
275     }
276
277     result = LookupAccountSid(NULL,
278                               event->uid,
279                               event->user,
280                               &user_length,
281                               event->domain,
282                               &domain_length,
283                               &account_type);
284     if (result == FALSE) {
285         log2file(
286             "%s: ERROR: Could not LookupAccountSid() for (%s) which returned (%lu)",
287             ARGV0,
288             StringSid ? StringSid : "unknown",
289             GetLastError());
290         goto cleanup;
291     }
292
293     /* Success */
294     status = 1;
295
296 cleanup:
297     if (status == 0) {
298         free(event->user);
299         free(event->domain);
300
301         event->user = NULL;
302         event->domain = NULL;
303     }
304
305     if (StringSid) {
306         LocalFree(StringSid);
307     }
308
309     return (status);
310 }
311
312 char *get_message(EVT_HANDLE evt, LPCWSTR provider_name, DWORD flags)
313 {
314     char *message = NULL;
315     EVT_HANDLE publisher = NULL;
316     DWORD size = 0;
317     wchar_t *buffer = NULL;
318     int result = 0;
319
320     publisher = EvtOpenPublisherMetadata(NULL,
321                                          provider_name,
322                                          NULL,
323                                          0,
324                                          0);
325     if (publisher == NULL) {
326         log2file(
327             "%s: ERROR: Could not EvtOpenPublisherMetadata() with flags (%lu) which returned (%lu)",
328             ARGV0,
329             flags,
330             GetLastError());
331         goto cleanup;
332     }
333
334     /* Make initial call to determine buffer size */
335     result = EvtFormatMessage(publisher,
336                               evt,
337                               0,
338                               0,
339                               NULL,
340                               flags,
341                               0,
342                               NULL,
343                               &size);
344     if (result != FALSE || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
345         log2file(
346             "%s: ERROR: Could not EvtFormatMessage() to determine buffer size with flags (%lu) which returned (%lu)",
347             ARGV0,
348             flags,
349             GetLastError());
350         goto cleanup;
351     }
352
353     if ((buffer = calloc(size, sizeof(wchar_t))) == NULL) {
354         log2file(
355             "%s: ERROR: Could not calloc() memory which returned [(%d)-(%s)]",
356             ARGV0,
357             errno,
358             strerror(errno));
359         goto cleanup;
360     }
361
362     result = EvtFormatMessage(publisher,
363                               evt,
364                               0,
365                               0,
366                               NULL,
367                               flags,
368                               size,
369                               buffer,
370                               &size);
371     if (result == FALSE) {
372         log2file(
373             "%s: ERROR: Could not EvtFormatMessage() with flags (%lu) which returned (%lu)",
374             ARGV0,
375             flags,
376             GetLastError());
377         goto cleanup;
378     }
379
380     message = convert_windows_string(buffer);
381
382 cleanup:
383     free(buffer);
384
385     if (publisher != NULL) {
386         EvtClose(publisher);
387     }
388
389     return (message);
390 }
391
392 /* Read an existing bookmark (if one exists) */
393 EVT_HANDLE read_bookmark(os_channel *channel)
394 {
395     EVT_HANDLE bookmark = NULL;
396     size_t size = 0;
397     FILE *fp = NULL;
398     wchar_t bookmark_xml[OS_MAXSTR];
399
400     /* If we have a stored bookmark, start from it */
401     if ((fp = fopen(channel->bookmark_filename, "r")) == NULL) {
402         /* Check if the error was not because the
403          * file did not exist which should be logged
404          */
405         if (errno != ENOENT) {
406             log2file(
407                 "%s: ERROR: Could not fopen() existing bookmark (%s) for (%s) which returned [(%d)-(%s)]",
408                 ARGV0,
409                 channel->bookmark_filename,
410                 channel->evt_log,
411                 errno,
412                 strerror(errno));
413         }
414         return (NULL);
415     }
416
417     size = fread(bookmark_xml, sizeof(wchar_t), OS_MAXSTR, fp);
418     if (ferror(fp)) {
419         log2file(
420             "%s: ERROR: Could not fread() bookmark (%s) for (%s) which returned [(%d)-(%s)]",
421             ARGV0,
422             channel->bookmark_filename,
423             channel->evt_log,
424             errno,
425             strerror(errno));
426         fclose(fp);
427         return (NULL);
428     }
429
430     fclose(fp);
431
432     /* Make sure bookmark data was read */
433     if (size == 0) {
434         return (NULL);
435     }
436
437     /* Make sure bookmark is terminated properly */
438     bookmark_xml[size] = L'\0';
439
440     /* Create bookmark from saved XML */
441     if ((bookmark = EvtCreateBookmark(bookmark_xml)) == NULL) {
442         log2file(
443             "%s: ERROR: Could not EvtCreateBookmark() bookmark (%s) for (%s) which returned (%lu)",
444             ARGV0,
445             channel->bookmark_filename,
446             channel->evt_log,
447             GetLastError());
448         return (NULL);
449     }
450
451     return (bookmark);
452 }
453
454 /* Update the log position of a bookmark */
455 int update_bookmark(EVT_HANDLE evt, os_channel *channel)
456 {
457     DWORD size = 0;
458     DWORD count = 0;
459     wchar_t *buffer = NULL;
460     int result = 0;
461     int status = 0;
462     int clean_tmp = 0;
463     EVT_HANDLE bookmark = NULL;
464     FILE *fp = NULL;
465     char tmp_file[OS_MAXSTR];
466
467     /* Create temporary bookmark file name */
468     snprintf(tmp_file,
469              sizeof(tmp_file),
470              "%s/%s-XXXXXX",
471              TMP_DIR,
472              channel->bookmark_name);
473
474     if ((bookmark = EvtCreateBookmark(NULL)) == NULL) {
475         log2file(
476             "%s: ERROR: Could not EvtCreateBookmark() bookmark (%s) for (%s) which returned (%lu)",
477             ARGV0,
478             channel->bookmark_filename,
479             channel->evt_log,
480             GetLastError());
481         goto cleanup;
482     }
483
484     if (!EvtUpdateBookmark(bookmark, evt)) {
485         log2file(
486             "%s: ERROR: Could not EvtUpdateBookmark() bookmark (%s) for (%s) which returned (%lu)",
487             ARGV0,
488             channel->bookmark_filename,
489             channel->evt_log,
490             GetLastError());
491         goto cleanup;
492     }
493
494     /* Make initial call to determine buffer size */
495     result = EvtRender(NULL,
496                        bookmark,
497                        EvtRenderBookmark,
498                        0,
499                        NULL,
500                        &size,
501                        &count);
502     if (result != FALSE || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
503         log2file(
504             "%s: ERROR: Could not EvtRender() to get buffer size to update bookmark (%s) for (%s) which returned (%lu)",
505             ARGV0,
506             channel->bookmark_filename,
507             channel->evt_log,
508             GetLastError());
509         goto cleanup;
510     }
511
512     if ((buffer = calloc(size, sizeof(char))) == NULL) {
513         log2file(
514             "%s: ERROR: Could not calloc() memory to save bookmark (%s) for (%s) which returned [(%d)-(%s)]",
515             ARGV0,
516             channel->bookmark_filename,
517             channel->evt_log,
518             errno,
519             strerror(errno));
520         goto cleanup;
521     }
522
523     if (!EvtRender(NULL,
524                    bookmark,
525                    EvtRenderBookmark,
526                    size,
527                    buffer,
528                    &size,
529                    &count)) {
530         log2file(
531             "%s: ERROR: Could not EvtRender() bookmark (%s) for (%s) which returned (%lu)",
532             ARGV0, channel->bookmark_filename, channel->evt_log,
533             GetLastError());
534         goto cleanup;
535     }
536
537     if (mkstemp_ex(tmp_file)) {
538         log2file(
539             "%s: ERROR: Could not mkstemp_ex() temporary bookmark (%s) for (%s)",
540             ARGV0,
541             tmp_file,
542             channel->evt_log);
543         goto cleanup;
544     }
545
546     if ((fp = fopen(tmp_file, "w")) == NULL) {
547         log2file(
548             "%s: ERROR: Could not fopen() temporary bookmark (%s) for (%s) which returned [(%d)-(%s)]",
549             ARGV0,
550             tmp_file,
551             channel->evt_log,
552             errno,
553             strerror(errno));
554         goto cleanup;
555     }
556
557     /* Help to determine whether or not temporary file needs to be removed when
558      * function cleans up after itself
559      */
560     clean_tmp = 1;
561
562     if ((fwrite(buffer, 1, size, fp)) < size) {
563         log2file(
564             "%s: ERROR: Could not fwrite() to temporary bookmark (%s) for (%s) which returned [(%d)-(%s)]",
565             ARGV0,
566             tmp_file,
567             channel->evt_log,
568             errno,
569             strerror(errno));
570         goto cleanup;
571     }
572
573     fclose(fp);
574
575     if (rename_ex(tmp_file, channel->bookmark_filename)) {
576         log2file(
577             "%s: ERROR: Could not rename_ex() temporary bookmark (%s) to (%s) for (%s)",
578             ARGV0,
579             tmp_file,
580             channel->bookmark_filename,
581             channel->evt_log);
582         goto cleanup;
583     }
584
585     /* Success */
586     status = 1;
587
588 cleanup:
589     free(buffer);
590
591     if (bookmark != NULL) {
592         EvtClose(bookmark);
593     }
594
595     if (fp) {
596         fclose(fp);
597     }
598
599     if (status == 0 && clean_tmp == 1 && unlink(tmp_file)) {
600         log2file(DELETE_ERROR,
601                  ARGV0,
602                  tmp_file,
603                  errno,
604                  strerror(errno));
605     }
606
607     return (status);
608 }
609
610 /* Format Timestamp from EventLog */
611 char *WinEvtTimeToString(ULONGLONG ulongTime)
612 {
613     SYSTEMTIME sysTime;
614     FILETIME fTime, lfTime;
615     ULARGE_INTEGER ulargeTime;
616     struct tm tm_struct;
617     char *timestamp = NULL;
618     int size = 80;
619
620     if ((timestamp = malloc(size)) == NULL) {
621         log2file(
622             "%s: ERROR: Could not malloc() memory to convert timestamp which returned [(%d)-(%s)]",
623             ARGV0,
624             errno,
625             strerror(errno));
626         goto cleanup;
627     }
628
629     /* Zero out structure */
630     memset(&tm_struct, 0, sizeof(tm_struct));
631
632     /* Convert from ULONGLONG to usable FILETIME value */
633     ulargeTime.QuadPart = ulongTime;
634
635     fTime.dwLowDateTime = ulargeTime.LowPart;
636     fTime.dwHighDateTime = ulargeTime.HighPart;
637
638     /* Adjust time value to reflect current timezone then convert to a
639      * SYSTEMTIME
640      */
641     if (FileTimeToLocalFileTime(&fTime, &lfTime) == 0) {
642         log2file(
643             "%s: ERROR: Could not FileTimeToLocalFileTime() to convert timestamp which returned (%lu)",
644             ARGV0,
645             GetLastError());
646         goto cleanup;
647     }
648
649     if (FileTimeToSystemTime(&lfTime, &sysTime) == 0) {
650         log2file(
651             "%s: ERROR: Could not FileTimeToSystemTime() to convert timestamp which returned (%lu)",
652             ARGV0,
653             GetLastError());
654         goto cleanup;
655     }
656
657     /* Convert SYSTEMTIME to tm */
658     tm_struct.tm_year = sysTime.wYear - 1900;
659     tm_struct.tm_mon  = sysTime.wMonth - 1;
660     tm_struct.tm_mday = sysTime.wDay;
661     tm_struct.tm_hour = sysTime.wHour;
662     tm_struct.tm_wday = sysTime.wDayOfWeek;
663     tm_struct.tm_min  = sysTime.wMinute;
664     tm_struct.tm_sec  = sysTime.wSecond;
665
666     /* Format timestamp string */
667     strftime(timestamp, size, "%Y %b %d %H:%M:%S", &tm_struct);
668
669     return (timestamp);
670
671 cleanup:
672     free(timestamp);
673
674     return (NULL);
675 }
676
677 void send_channel_event(EVT_HANDLE evt, os_channel *channel)
678 {
679     DWORD buffer_length = 0;
680     PEVT_VARIANT properties_values = NULL;
681     DWORD count = 0;
682     EVT_HANDLE context = NULL;
683     os_event event = {0};
684     char final_msg[OS_MAXSTR];
685     int result = 0;
686
687     if ((context = EvtCreateRenderContext(count, NULL, EvtRenderContextSystem)) == NULL) {
688         log2file(
689             "%s: ERROR: Could not EvtCreateRenderContext() for (%s) which returned (%lu)",
690             ARGV0,
691             channel->evt_log,
692             GetLastError());
693         goto cleanup;
694     }
695
696     /* Make initial call to determine buffer size necessary */
697     result = EvtRender(context,
698                        evt,
699                        EvtRenderEventValues,
700                        0,
701                        NULL,
702                        &buffer_length,
703                        &count);
704     if (result != FALSE || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
705         log2file(
706             "%s: ERROR: Could not EvtRender() to determine buffer size for (%s) which returned (%lu)",
707             ARGV0,
708             channel->evt_log,
709             GetLastError());
710         goto cleanup;
711     }
712
713     if ((properties_values = malloc(buffer_length)) == NULL) {
714         log2file(
715             "%s: ERROR: Could not malloc() memory to process event (%s) which returned [(%d)-(%s)]",
716             ARGV0,
717             channel->evt_log,
718             errno,
719             strerror(errno));
720         goto cleanup;
721     }
722
723     if (!EvtRender(context,
724                    evt,
725                    EvtRenderEventValues,
726                    buffer_length,
727                    properties_values,
728                    &buffer_length,
729                    &count)) {
730         log2file(
731             "%s: ERROR: Could not EvtRender() for (%s) which returned (%lu)",
732             ARGV0,
733             channel->evt_log,
734             GetLastError());
735         goto cleanup;
736     }
737
738     event.name = get_property_value(&properties_values[EvtSystemChannel]);
739     event.id = properties_values[EvtSystemEventID].UInt16Val;
740     event.source = get_property_value(&properties_values[EvtSystemProviderName]);
741     event.uid = properties_values[EvtSystemUserID].Type == EvtVarTypeNull ? NULL : properties_values[EvtSystemUserID].SidVal;
742     event.computer = get_property_value(&properties_values[EvtSystemComputer]);
743     event.time_created = properties_values[EvtSystemTimeCreated].FileTimeVal;
744     event.keywords = properties_values[EvtSystemKeywords].Type == EvtVarTypeNull ? 0 : properties_values[EvtSystemKeywords].UInt64Val;
745     event.level = properties_values[EvtSystemLevel].Type == EvtVarTypeNull ? -1 : properties_values[EvtSystemLevel].ByteVal;
746
747     switch (event.level) {
748         case WINEVENT_CRITICAL:
749             event.category = "CRITICAL";
750             break;
751         case WINEVENT_ERROR:
752             event.category = "ERROR";
753             break;
754         case WINEVENT_WARNING:
755             event.category = "WARNING";
756             break;
757         case WINEVENT_INFORMATION:
758             event.category = "INFORMATION";
759             break;
760         case WINEVENT_VERBOSE:
761             event.category = "DEBUG";
762             break;
763         case WINEVENT_AUDIT:
764             if (event.keywords & WINEVENT_AUDIT_FAILURE) {
765                 event.category = "AUDIT_FAILURE";
766                 break;
767             } else if (event.keywords & WINEVENT_AUDIT_SUCCESS) {
768                 event.category = "AUDIT_SUCCESS";
769                 break;
770             }
771         default:
772             event.category = "Unknown";
773             break;
774     }
775
776     if ((event.timestamp = WinEvtTimeToString(event.time_created)) == NULL) {
777         log2file(
778             "%s: ERROR: Could not convert timestamp for (%s)",
779             ARGV0,
780             channel->evt_log);
781         goto cleanup;
782     }
783
784     /* Determine user and domain */
785     get_username_and_domain(&event);
786
787     /* Get event log message */
788     if ((event.message = get_message(evt, properties_values[EvtSystemProviderName].StringVal, EvtFormatMessageEvent)) == NULL) {
789         log2file(
790             "%s: ERROR: Could not get message for (%s)",
791             ARGV0,
792             channel->evt_log);
793     } else {
794         /* Format message */
795         win_format_event_string(event.message);
796     }
797
798     snprintf(
799         final_msg,
800         sizeof(final_msg),
801         "%s WinEvtLog: %s: %s(%d): %s: %s: %s: %s: %s",
802         event.timestamp,
803         event.name,
804         event.category,
805         event.id,
806         event.source && strlen(event.source) ? event.source : "no source",
807         event.user && strlen(event.user) ? event.user : "(no user)",
808         event.domain && strlen(event.domain) ? event.domain : "no domain",
809         event.computer && strlen(event.computer) ? event.computer : "no computer",
810         event.message && strlen(event.message) ? event.message : "(no message)"
811     );
812
813     if (SendMSG(logr_queue, final_msg, "WinEvtLog", LOCALFILE_MQ) < 0) {
814         merror(QUEUE_SEND, ARGV0);
815     }
816
817     if (channel->bookmark_enabled) {
818         update_bookmark(evt, channel);
819     }
820
821 cleanup:
822     free(properties_values);
823     free_event(&event);
824
825     if (context != NULL) {
826         EvtClose(context);
827     }
828
829     return;
830 }
831
832 DWORD WINAPI event_channel_callback(EVT_SUBSCRIBE_NOTIFY_ACTION action, os_channel *channel, EVT_HANDLE evt)
833 {
834     if (action == EvtSubscribeActionDeliver) {
835         send_channel_event(evt, channel);
836     }
837
838     return (0);
839 }
840
841 void win_start_event_channel(char *evt_log, char future, char *query)
842 {
843     wchar_t *wchannel = NULL;
844     wchar_t *wquery = NULL;
845     char *filtered_query = NULL;
846     os_channel *channel = NULL;
847     DWORD flags = EvtSubscribeToFutureEvents;
848     EVT_HANDLE bookmark = NULL;
849     EVT_HANDLE result = NULL;
850     int status = 0;
851
852     if ((channel = calloc(1, sizeof(os_channel))) == NULL) {
853         log2file(
854             "%s: ERROR: Could not calloc() memory for channel to start reading (%s) which returned [(%d)-(%s)]",
855             ARGV0,
856             evt_log,
857             errno,
858             strerror(errno));
859         goto cleanup;
860     }
861
862     channel->evt_log = evt_log;
863
864     /* Create copy of event log string */
865     if ((channel->bookmark_name = strdup(channel->evt_log)) == NULL) {
866         log2file(
867             "%s: ERROR: Could not strdup() event log name to start reading (%s) which returned [(%d)-(%s)]",
868             ARGV0,
869             channel->evt_log,
870             errno,
871             strerror(errno));
872         goto cleanup;
873     }
874
875     /* Replace '/' with '_' */
876     if (strchr(channel->bookmark_name, '/')) {
877         *(strrchr(channel->bookmark_name, '/')) = '_';
878     }
879
880     /* Convert evt_log to Windows string */
881     if ((wchannel = convert_unix_string(channel->evt_log)) == NULL) {
882         log2file(
883             "%s: ERROR: Could not convert_unix_string() evt_log for (%s) which returned [(%d)-(%s)]",
884             ARGV0,
885             channel->evt_log,
886             errno,
887             strerror(errno));
888         goto cleanup;
889     }
890
891     /* Convert query to Windows string */
892     if (query) {
893         if ((filtered_query = filter_special_chars(query)) == NULL) {
894             log2file(
895                 "%s: ERROR: Could not filter_special_chars() query for (%s) which returned [(%d)-(%s)]",
896                 ARGV0,
897                 channel->evt_log,
898                 errno,
899                 strerror(errno));
900             goto cleanup;
901         }
902
903         if ((wquery = convert_unix_string(filtered_query)) == NULL) {
904             log2file(
905                 "%s: ERROR: Could not convert_unix_string() query for (%s) which returned [(%d)-(%s)]",
906                 ARGV0,
907                 channel->evt_log,
908                 errno,
909                 strerror(errno));
910             goto cleanup;
911         }
912     }
913
914     channel->bookmark_enabled = !future;
915
916     if (channel->bookmark_enabled) {
917         /* Create bookmark file name */
918         snprintf(channel->bookmark_filename,
919                  sizeof(channel->bookmark_filename), "%s/%s", BOOKMARKS_DIR,
920                  channel->bookmark_name);
921
922         /* Try to read existing bookmark */
923         if ((bookmark = read_bookmark(channel)) != NULL) {
924             flags = EvtSubscribeStartAfterBookmark;
925         }
926     }
927
928     result = EvtSubscribe(NULL,
929                           NULL,
930                           wchannel,
931                           wquery,
932                           bookmark,
933                           channel,
934                           (EVT_SUBSCRIBE_CALLBACK)event_channel_callback,
935                           flags);
936
937     if (result == NULL && flags == EvtSubscribeStartAfterBookmark) {
938         result = EvtSubscribe(NULL,
939                               NULL,
940                               wchannel,
941                               wquery,
942                               NULL,
943                               channel,
944                               (EVT_SUBSCRIBE_CALLBACK)event_channel_callback,
945                               EvtSubscribeToFutureEvents);
946     }
947
948     if (result == NULL) {
949         log2file(
950             "%s: ERROR: Could not EvtSubscribe() for (%s) which returned (%lu)",
951             ARGV0,
952             channel->evt_log,
953             GetLastError());
954         goto cleanup;
955     }
956
957     /* Success */
958     status = 1;
959
960 cleanup:
961     free(wchannel);
962     free(wquery);
963     free(filtered_query);
964
965     if (status == 0) {
966         free(channel->bookmark_name);
967         free(channel);
968
969         if (result != NULL) {
970             EvtClose(result);
971         }
972     }
973
974     if (bookmark != NULL) {
975         EvtClose(bookmark);
976     }
977
978     return;
979 }
980
981 #endif /* EVENTCHANNEL_SUPPORT */
982 #endif /* WIN32 */
983