1 /* Copyright (C) 2009 Trend Micro Inc.
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
11 #ifdef EVENTCHANNEL_SUPPORT
13 /* Saying we are on Vista in order to have the API */
14 #define _WIN32_WINNT 0x0600
16 /* Using Secure APIs */
17 #define MINGW_HAS_SECURE_API 1
19 /* Bookmarks directory */
20 #define BOOKMARKS_DIR "bookmarks"
22 #ifndef WC_ERR_INVALID_CHARS
23 #define WC_ERR_INVALID_CHARS 0x80
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
35 #define WINEVENT_AUDIT_FAILURE 0x10000000000000LL
36 #define WINEVENT_AUDIT_SUCCESS 0x20000000000000LL
39 #include "logcollector.h"
43 #include <sec_api/stdlib_s.h>
47 typedef struct _os_event {
56 ULONGLONG time_created;
63 typedef struct _os_channel {
66 char bookmark_enabled;
67 char bookmark_filename[OS_MAXSTR];
71 void free_event(os_event *event)
77 free(event->computer);
79 free(event->timestamp);
82 char *convert_windows_string(LPCWSTR string)
92 /* Determine size required */
93 size = WideCharToMultiByte(CP_UTF8,
104 "%s: ERROR: Could not WideCharToMultiByte() when determining size which returned (%lu)",
110 if ((dest = calloc(size, sizeof(char))) == NULL) {
112 "%s: ERROR: Could not calloc() memory for WideCharToMultiByte() which returned [(%d)-(%s)]",
120 result = WideCharToMultiByte(CP_UTF8,
121 WC_ERR_INVALID_CHARS,
131 "%s: ERROR: Could not WideCharToMultiByte() which returned (%lu)",
141 wchar_t *convert_unix_string(char *string)
143 wchar_t *dest = NULL;
147 if (string == NULL) {
151 /* Determine size required */
152 size = MultiByteToWideChar(CP_UTF8,
153 MB_ERR_INVALID_CHARS,
161 "%s: ERROR: Could not MultiByteToWideChar() when determining size which returned (%lu)",
167 if ((dest = calloc(size, sizeof(wchar_t))) == NULL) {
169 "%s: ERROR: Could not calloc() memory for MultiByteToWideChar() which returned [(%d)-(%s)]",
176 result = MultiByteToWideChar(CP_UTF8,
177 MB_ERR_INVALID_CHARS,
185 "%s: ERROR: Could not MultiByteToWideChar() which returned (%lu)",
195 /* Filter escape characters */
197 char* filter_special_chars(const char *string) {
199 int n = strlen(string);
200 char *filtered = malloc(n + 1);
205 for (i = 0; i <= n; i++)
206 filtered[j++] = (string[i] == '\\') ? string[++i] : string[i];
211 char *get_property_value(PEVT_VARIANT value)
213 if (value->Type == EvtVarTypeNull) {
217 return (convert_windows_string(value->StringVal));
220 int get_username_and_domain(os_event *event)
224 DWORD user_length = 0;
225 DWORD domain_length = 0;
226 SID_NAME_USE account_type;
227 LPTSTR StringSid = NULL;
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.
232 if (!ConvertSidToStringSid(event->uid, &StringSid)) {
234 "%s: WARN: Could not convert SID to string which returned (%lu)",
239 debug1("%s: DEBUG: Performing a LookupAccountSid() on (%s)",
241 StringSid ? StringSid : "unknown");
243 /* Make initial call to get buffer size */
244 result = LookupAccountSid(NULL,
252 if (result != FALSE || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
253 /* Not having a user can be normal */
257 if ((event->user = calloc(user_length, sizeof(char))) == NULL) {
259 "%s: ERROR: Could not lookup SID (%s) due to calloc() failure on user which returned [(%d)-(%s)]",
261 StringSid ? StringSid : "unknown",
267 if ((event->domain = calloc(domain_length, sizeof(char))) == NULL) {
269 "%s: ERROR: Could not lookup SID (%s) due to calloc() failure on domain which returned [(%d)-(%s)]",
271 StringSid ? StringSid : "unknown",
277 result = LookupAccountSid(NULL,
284 if (result == FALSE) {
286 "%s: ERROR: Could not LookupAccountSid() for (%s) which returned (%lu)",
288 StringSid ? StringSid : "unknown",
302 event->domain = NULL;
306 LocalFree(StringSid);
312 char *get_message(EVT_HANDLE evt, LPCWSTR provider_name, DWORD flags)
314 char *message = NULL;
315 EVT_HANDLE publisher = NULL;
317 wchar_t *buffer = NULL;
320 publisher = EvtOpenPublisherMetadata(NULL,
325 if (publisher == NULL) {
327 "%s: ERROR: Could not EvtOpenPublisherMetadata() with flags (%lu) which returned (%lu)",
334 /* Make initial call to determine buffer size */
335 result = EvtFormatMessage(publisher,
344 if (result != FALSE || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
346 "%s: ERROR: Could not EvtFormatMessage() to determine buffer size with flags (%lu) which returned (%lu)",
353 if ((buffer = calloc(size, sizeof(wchar_t))) == NULL) {
355 "%s: ERROR: Could not calloc() memory which returned [(%d)-(%s)]",
362 result = EvtFormatMessage(publisher,
371 if (result == FALSE) {
373 "%s: ERROR: Could not EvtFormatMessage() with flags (%lu) which returned (%lu)",
380 message = convert_windows_string(buffer);
385 if (publisher != NULL) {
392 /* Read an existing bookmark (if one exists) */
393 EVT_HANDLE read_bookmark(os_channel *channel)
395 EVT_HANDLE bookmark = NULL;
398 wchar_t bookmark_xml[OS_MAXSTR];
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
405 if (errno != ENOENT) {
407 "%s: ERROR: Could not fopen() existing bookmark (%s) for (%s) which returned [(%d)-(%s)]",
409 channel->bookmark_filename,
417 size = fread(bookmark_xml, sizeof(wchar_t), OS_MAXSTR, fp);
420 "%s: ERROR: Could not fread() bookmark (%s) for (%s) which returned [(%d)-(%s)]",
422 channel->bookmark_filename,
432 /* Make sure bookmark data was read */
437 /* Make sure bookmark is terminated properly */
438 bookmark_xml[size] = L'\0';
440 /* Create bookmark from saved XML */
441 if ((bookmark = EvtCreateBookmark(bookmark_xml)) == NULL) {
443 "%s: ERROR: Could not EvtCreateBookmark() bookmark (%s) for (%s) which returned (%lu)",
445 channel->bookmark_filename,
454 /* Update the log position of a bookmark */
455 int update_bookmark(EVT_HANDLE evt, os_channel *channel)
459 wchar_t *buffer = NULL;
463 EVT_HANDLE bookmark = NULL;
465 char tmp_file[OS_MAXSTR];
467 /* Create temporary bookmark file name */
472 channel->bookmark_name);
474 if ((bookmark = EvtCreateBookmark(NULL)) == NULL) {
476 "%s: ERROR: Could not EvtCreateBookmark() bookmark (%s) for (%s) which returned (%lu)",
478 channel->bookmark_filename,
484 if (!EvtUpdateBookmark(bookmark, evt)) {
486 "%s: ERROR: Could not EvtUpdateBookmark() bookmark (%s) for (%s) which returned (%lu)",
488 channel->bookmark_filename,
494 /* Make initial call to determine buffer size */
495 result = EvtRender(NULL,
502 if (result != FALSE || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
504 "%s: ERROR: Could not EvtRender() to get buffer size to update bookmark (%s) for (%s) which returned (%lu)",
506 channel->bookmark_filename,
512 if ((buffer = calloc(size, sizeof(char))) == NULL) {
514 "%s: ERROR: Could not calloc() memory to save bookmark (%s) for (%s) which returned [(%d)-(%s)]",
516 channel->bookmark_filename,
531 "%s: ERROR: Could not EvtRender() bookmark (%s) for (%s) which returned (%lu)",
532 ARGV0, channel->bookmark_filename, channel->evt_log,
537 if (mkstemp_ex(tmp_file)) {
539 "%s: ERROR: Could not mkstemp_ex() temporary bookmark (%s) for (%s)",
546 if ((fp = fopen(tmp_file, "w")) == NULL) {
548 "%s: ERROR: Could not fopen() temporary bookmark (%s) for (%s) which returned [(%d)-(%s)]",
557 /* Help to determine whether or not temporary file needs to be removed when
558 * function cleans up after itself
562 if ((fwrite(buffer, 1, size, fp)) < size) {
564 "%s: ERROR: Could not fwrite() to temporary bookmark (%s) for (%s) which returned [(%d)-(%s)]",
575 if (rename_ex(tmp_file, channel->bookmark_filename)) {
577 "%s: ERROR: Could not rename_ex() temporary bookmark (%s) to (%s) for (%s)",
580 channel->bookmark_filename,
591 if (bookmark != NULL) {
599 if (status == 0 && clean_tmp == 1 && unlink(tmp_file)) {
600 log2file(DELETE_ERROR,
610 /* Format Timestamp from EventLog */
611 char *WinEvtTimeToString(ULONGLONG ulongTime)
614 FILETIME fTime, lfTime;
615 ULARGE_INTEGER ulargeTime;
617 char *timestamp = NULL;
620 if ((timestamp = malloc(size)) == NULL) {
622 "%s: ERROR: Could not malloc() memory to convert timestamp which returned [(%d)-(%s)]",
629 /* Zero out structure */
630 memset(&tm_struct, 0, sizeof(tm_struct));
632 /* Convert from ULONGLONG to usable FILETIME value */
633 ulargeTime.QuadPart = ulongTime;
635 fTime.dwLowDateTime = ulargeTime.LowPart;
636 fTime.dwHighDateTime = ulargeTime.HighPart;
638 /* Adjust time value to reflect current timezone then convert to a
641 if (FileTimeToLocalFileTime(&fTime, &lfTime) == 0) {
643 "%s: ERROR: Could not FileTimeToLocalFileTime() to convert timestamp which returned (%lu)",
649 if (FileTimeToSystemTime(&lfTime, &sysTime) == 0) {
651 "%s: ERROR: Could not FileTimeToSystemTime() to convert timestamp which returned (%lu)",
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;
666 /* Format timestamp string */
667 strftime(timestamp, size, "%Y %b %d %H:%M:%S", &tm_struct);
677 void send_channel_event(EVT_HANDLE evt, os_channel *channel)
679 DWORD buffer_length = 0;
680 PEVT_VARIANT properties_values = NULL;
682 EVT_HANDLE context = NULL;
683 os_event event = {0};
684 char final_msg[OS_MAXSTR];
687 if ((context = EvtCreateRenderContext(count, NULL, EvtRenderContextSystem)) == NULL) {
689 "%s: ERROR: Could not EvtCreateRenderContext() for (%s) which returned (%lu)",
696 /* Make initial call to determine buffer size necessary */
697 result = EvtRender(context,
699 EvtRenderEventValues,
704 if (result != FALSE || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
706 "%s: ERROR: Could not EvtRender() to determine buffer size for (%s) which returned (%lu)",
713 if ((properties_values = malloc(buffer_length)) == NULL) {
715 "%s: ERROR: Could not malloc() memory to process event (%s) which returned [(%d)-(%s)]",
723 if (!EvtRender(context,
725 EvtRenderEventValues,
731 "%s: ERROR: Could not EvtRender() for (%s) which returned (%lu)",
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;
747 switch (event.level) {
748 case WINEVENT_CRITICAL:
749 event.category = "CRITICAL";
752 event.category = "ERROR";
754 case WINEVENT_WARNING:
755 event.category = "WARNING";
757 case WINEVENT_INFORMATION:
758 event.category = "INFORMATION";
760 case WINEVENT_VERBOSE:
761 event.category = "DEBUG";
764 if (event.keywords & WINEVENT_AUDIT_FAILURE) {
765 event.category = "AUDIT_FAILURE";
767 } else if (event.keywords & WINEVENT_AUDIT_SUCCESS) {
768 event.category = "AUDIT_SUCCESS";
772 event.category = "Unknown";
776 if ((event.timestamp = WinEvtTimeToString(event.time_created)) == NULL) {
778 "%s: ERROR: Could not convert timestamp for (%s)",
784 /* Determine user and domain */
785 get_username_and_domain(&event);
787 /* Get event log message */
788 if ((event.message = get_message(evt, properties_values[EvtSystemProviderName].StringVal, EvtFormatMessageEvent)) == NULL) {
790 "%s: ERROR: Could not get message for (%s)",
795 win_format_event_string(event.message);
801 "%s WinEvtLog: %s: %s(%d): %s: %s: %s: %s: %s",
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)"
813 if (SendMSG(logr_queue, final_msg, "WinEvtLog", LOCALFILE_MQ) < 0) {
814 merror(QUEUE_SEND, ARGV0);
817 if (channel->bookmark_enabled) {
818 update_bookmark(evt, channel);
822 free(properties_values);
825 if (context != NULL) {
832 DWORD WINAPI event_channel_callback(EVT_SUBSCRIBE_NOTIFY_ACTION action, os_channel *channel, EVT_HANDLE evt)
834 if (action == EvtSubscribeActionDeliver) {
835 send_channel_event(evt, channel);
841 void win_start_event_channel(char *evt_log, char future, char *query)
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;
852 if ((channel = calloc(1, sizeof(os_channel))) == NULL) {
854 "%s: ERROR: Could not calloc() memory for channel to start reading (%s) which returned [(%d)-(%s)]",
862 channel->evt_log = evt_log;
864 /* Create copy of event log string */
865 if ((channel->bookmark_name = strdup(channel->evt_log)) == NULL) {
867 "%s: ERROR: Could not strdup() event log name to start reading (%s) which returned [(%d)-(%s)]",
875 /* Replace '/' with '_' */
876 if (strchr(channel->bookmark_name, '/')) {
877 *(strrchr(channel->bookmark_name, '/')) = '_';
880 /* Convert evt_log to Windows string */
881 if ((wchannel = convert_unix_string(channel->evt_log)) == NULL) {
883 "%s: ERROR: Could not convert_unix_string() evt_log for (%s) which returned [(%d)-(%s)]",
891 /* Convert query to Windows string */
893 if ((filtered_query = filter_special_chars(query)) == NULL) {
895 "%s: ERROR: Could not filter_special_chars() query for (%s) which returned [(%d)-(%s)]",
903 if ((wquery = convert_unix_string(filtered_query)) == NULL) {
905 "%s: ERROR: Could not convert_unix_string() query for (%s) which returned [(%d)-(%s)]",
914 channel->bookmark_enabled = !future;
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);
922 /* Try to read existing bookmark */
923 if ((bookmark = read_bookmark(channel)) != NULL) {
924 flags = EvtSubscribeStartAfterBookmark;
928 result = EvtSubscribe(NULL,
934 (EVT_SUBSCRIBE_CALLBACK)event_channel_callback,
937 if (result == NULL && flags == EvtSubscribeStartAfterBookmark) {
938 result = EvtSubscribe(NULL,
944 (EVT_SUBSCRIBE_CALLBACK)event_channel_callback,
945 EvtSubscribeToFutureEvents);
948 if (result == NULL) {
950 "%s: ERROR: Could not EvtSubscribe() for (%s) which returned (%lu)",
963 free(filtered_query);
966 free(channel->bookmark_name);
969 if (result != NULL) {
974 if (bookmark != NULL) {
981 #endif /* EVENTCHANNEL_SUPPORT */