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