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
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"
44 #include <sec_api/stdlib_s.h>
48 typedef struct _os_event {
57 ULONGLONG time_created;
64 typedef struct _os_channel {
67 char bookmark_enabled;
68 char bookmark_filename[OS_MAXSTR];
72 void free_event(os_event *event)
78 free(event->computer);
80 free(event->timestamp);
83 char *convert_windows_string(LPCWSTR string)
93 /* Determine size required */
94 size = WideCharToMultiByte(CP_UTF8,
105 "%s: ERROR: Could not WideCharToMultiByte() when determining size which returned (%lu)",
111 if ((dest = calloc(size, sizeof(char))) == NULL) {
113 "%s: ERROR: Could not calloc() memory for WideCharToMultiByte() which returned [(%d)-(%s)]",
121 result = WideCharToMultiByte(CP_UTF8,
122 WC_ERR_INVALID_CHARS,
132 "%s: ERROR: Could not WideCharToMultiByte() which returned (%lu)",
142 wchar_t *convert_unix_string(char *string)
144 wchar_t *dest = NULL;
148 if (string == NULL) {
152 /* Determine size required */
153 size = MultiByteToWideChar(CP_UTF8,
154 MB_ERR_INVALID_CHARS,
162 "%s: ERROR: Could not MultiByteToWideChar() when determining size which returned (%lu)",
168 if ((dest = calloc(size, sizeof(wchar_t))) == NULL) {
170 "%s: ERROR: Could not calloc() memory for MultiByteToWideChar() which returned [(%d)-(%s)]",
177 result = MultiByteToWideChar(CP_UTF8,
178 MB_ERR_INVALID_CHARS,
186 "%s: ERROR: Could not MultiByteToWideChar() which returned (%lu)",
196 char *get_property_value(PEVT_VARIANT value)
198 if (value->Type == EvtVarTypeNull) {
202 return (convert_windows_string(value->StringVal));
205 int get_username_and_domain(os_event *event)
209 DWORD user_length = 0;
210 DWORD domain_length = 0;
211 SID_NAME_USE account_type;
212 LPTSTR StringSid = NULL;
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.
217 if (!ConvertSidToStringSid(event->uid, &StringSid)) {
219 "%s: WARN: Could not convert SID to string which returned (%lu)",
224 debug1("%s: DEBUG: Performing a LookupAccountSid() on (%s)",
226 StringSid ? StringSid : "unknown");
228 /* Make initial call to get buffer size */
229 result = LookupAccountSid(NULL,
237 if (result != FALSE || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
238 /* Not having a user can be normal */
242 if ((event->user = calloc(user_length, sizeof(char))) == NULL) {
244 "%s: ERROR: Could not lookup SID (%s) due to calloc() failure on user which returned [(%d)-(%s)]",
246 StringSid ? StringSid : "unknown",
252 if ((event->domain = calloc(domain_length, sizeof(char))) == NULL) {
254 "%s: ERROR: Could not lookup SID (%s) due to calloc() failure on domain which returned [(%d)-(%s)]",
256 StringSid ? StringSid : "unknown",
262 result = LookupAccountSid(NULL,
269 if (result == FALSE) {
271 "%s: ERROR: Could not LookupAccountSid() for (%s) which returned (%lu)",
273 StringSid ? StringSid : "unknown",
287 event->domain = NULL;
291 LocalFree(StringSid);
297 char *get_message(EVT_HANDLE evt, LPCWSTR provider_name, DWORD flags)
299 char *message = NULL;
300 EVT_HANDLE publisher = NULL;
302 wchar_t *buffer = NULL;
305 publisher = EvtOpenPublisherMetadata(NULL,
310 if (publisher == NULL) {
312 "%s: ERROR: Could not EvtOpenPublisherMetadata() with flags (%lu) which returned (%lu)",
319 /* Make initial call to determine buffer size */
320 result = EvtFormatMessage(publisher,
329 if (result != FALSE || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
331 "%s: ERROR: Could not EvtFormatMessage() to determine buffer size with flags (%lu) which returned (%lu)",
338 if ((buffer = calloc(size, sizeof(wchar_t))) == NULL) {
340 "%s: ERROR: Could not calloc() memory which returned [(%d)-(%s)]",
347 result = EvtFormatMessage(publisher,
356 if (result == FALSE) {
358 "%s: ERROR: Could not EvtFormatMessage() with flags (%lu) which returned (%lu)",
365 message = convert_windows_string(buffer);
370 if (publisher != NULL) {
377 /* Read an existing bookmark (if one exists) */
378 EVT_HANDLE read_bookmark(os_channel *channel)
380 EVT_HANDLE bookmark = NULL;
383 wchar_t bookmark_xml[OS_MAXSTR];
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
390 if (errno != ENOENT) {
392 "%s: ERROR: Could not fopen() existing bookmark (%s) for (%s) which returned [(%d)-(%s)]",
394 channel->bookmark_filename,
402 size = fread(bookmark_xml, sizeof(wchar_t), OS_MAXSTR, fp);
405 "%s: ERROR: Could not fread() bookmark (%s) for (%s) which returned [(%d)-(%s)]",
407 channel->bookmark_filename,
417 /* Make sure bookmark data was read */
422 /* Make sure bookmark is terminated properly */
423 bookmark_xml[size] = L'\0';
425 /* Create bookmark from saved XML */
426 if ((bookmark = EvtCreateBookmark(bookmark_xml)) == NULL) {
428 "%s: ERROR: Could not EvtCreateBookmark() bookmark (%s) for (%s) which returned (%lu)",
430 channel->bookmark_filename,
439 /* Update the log position of a bookmark */
440 int update_bookmark(EVT_HANDLE evt, os_channel *channel)
444 wchar_t *buffer = NULL;
448 EVT_HANDLE bookmark = NULL;
450 char tmp_file[OS_MAXSTR];
452 /* Create temporary bookmark file name */
457 channel->bookmark_name);
459 if ((bookmark = EvtCreateBookmark(NULL)) == NULL) {
461 "%s: ERROR: Could not EvtCreateBookmark() bookmark (%s) for (%s) which returned (%lu)",
463 channel->bookmark_filename,
469 if (!EvtUpdateBookmark(bookmark, evt)) {
471 "%s: ERROR: Could not EvtUpdateBookmark() bookmark (%s) for (%s) which returned (%lu)",
473 channel->bookmark_filename,
479 /* Make initial call to determine buffer size */
480 result = EvtRender(NULL,
487 if (result != FALSE || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
489 "%s: ERROR: Could not EvtRender() to get buffer size to update bookmark (%s) for (%s) which returned (%lu)",
491 channel->bookmark_filename,
497 if ((buffer = calloc(size, sizeof(char))) == NULL) {
499 "%s: ERROR: Could not calloc() memory to save bookmark (%s) for (%s) which returned [(%d)-(%s)]",
501 channel->bookmark_filename,
516 "%s: ERROR: Could not EvtRender() bookmark (%s) for (%s) which returned (%lu)",
517 ARGV0, channel->bookmark_filename, channel->evt_log,
522 if (mkstemp_ex(tmp_file)) {
524 "%s: ERROR: Could not mkstemp_ex() temporary bookmark (%s) for (%s)",
531 if ((fp = fopen(tmp_file, "w")) == NULL) {
533 "%s: ERROR: Could not fopen() temporary bookmark (%s) for (%s) which returned [(%d)-(%s)]",
542 /* Help to determine whether or not temporary file needs to be removed when
543 * function cleans up after itself
547 if ((fwrite(buffer, 1, size, fp)) < size) {
549 "%s: ERROR: Could not fwrite() to temporary bookmark (%s) for (%s) which returned [(%d)-(%s)]",
560 if (rename_ex(tmp_file, channel->bookmark_filename)) {
562 "%s: ERROR: Could not rename_ex() temporary bookmark (%s) to (%s) for (%s)",
565 channel->bookmark_filename,
576 if (bookmark != NULL) {
584 if (status == 0 && clean_tmp == 1 && unlink(tmp_file)) {
585 log2file(DELETE_ERROR,
595 /* Format Timestamp from EventLog */
596 char *WinEvtTimeToString(ULONGLONG ulongTime)
599 FILETIME fTime, lfTime;
600 ULARGE_INTEGER ulargeTime;
602 char *timestamp = NULL;
605 if ((timestamp = malloc(size)) == NULL) {
607 "%s: ERROR: Could not malloc() memory to convert timestamp which returned [(%d)-(%s)]",
614 /* Zero out structure */
615 memset(&tm_struct, 0, sizeof(tm_struct));
617 /* Convert from ULONGLONG to usable FILETIME value */
618 ulargeTime.QuadPart = ulongTime;
620 fTime.dwLowDateTime = ulargeTime.LowPart;
621 fTime.dwHighDateTime = ulargeTime.HighPart;
623 /* Adjust time value to reflect current timezone then convert to a
626 if (FileTimeToLocalFileTime(&fTime, &lfTime) == 0) {
628 "%s: ERROR: Could not FileTimeToLocalFileTime() to convert timestamp which returned (%lu)",
634 if (FileTimeToSystemTime(&lfTime, &sysTime) == 0) {
636 "%s: ERROR: Could not FileTimeToSystemTime() to convert timestamp which returned (%lu)",
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;
651 /* Format timestamp string */
652 strftime(timestamp, size, "%Y %b %d %H:%M:%S", &tm_struct);
662 void send_channel_event(EVT_HANDLE evt, os_channel *channel)
664 DWORD buffer_length = 0;
665 PEVT_VARIANT properties_values = NULL;
667 EVT_HANDLE context = NULL;
668 os_event event = {0};
669 char final_msg[OS_MAXSTR];
672 if ((context = EvtCreateRenderContext(count, NULL, EvtRenderContextSystem)) == NULL) {
674 "%s: ERROR: Could not EvtCreateRenderContext() for (%s) which returned (%lu)",
681 /* Make initial call to determine buffer size necessary */
682 result = EvtRender(context,
684 EvtRenderEventValues,
689 if (result != FALSE || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
691 "%s: ERROR: Could not EvtRender() to determine buffer size for (%s) which returned (%lu)",
698 if ((properties_values = malloc(buffer_length)) == NULL) {
700 "%s: ERROR: Could not malloc() memory to process event (%s) which returned [(%d)-(%s)]",
708 if (!EvtRender(context,
710 EvtRenderEventValues,
716 "%s: ERROR: Could not EvtRender() for (%s) which returned (%lu)",
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;
732 switch (event.level) {
733 case WINEVENT_CRITICAL:
734 event.category = "CRITICAL";
737 event.category = "ERROR";
739 case WINEVENT_WARNING:
740 event.category = "WARNING";
742 case WINEVENT_INFORMATION:
743 event.category = "INFORMATION";
745 case WINEVENT_VERBOSE:
746 event.category = "DEBUG";
749 if (event.keywords & WINEVENT_AUDIT_FAILURE) {
750 event.category = "AUDIT_FAILURE";
752 } else if (event.keywords & WINEVENT_AUDIT_SUCCESS) {
753 event.category = "AUDIT_SUCCESS";
757 event.category = "Unknown";
761 if ((event.timestamp = WinEvtTimeToString(event.time_created)) == NULL) {
763 "%s: ERROR: Could not convert timestamp for (%s)",
769 /* Determine user and domain */
770 get_username_and_domain(&event);
772 /* Get event log message */
773 if ((event.message = get_message(evt, properties_values[EvtSystemProviderName].StringVal, EvtFormatMessageEvent)) == NULL) {
775 "%s: ERROR: Could not get message for (%s)",
780 win_format_event_string(event.message);
786 "%s WinEvtLog: %s: %s(%d): %s: %s: %s: %s: %s",
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)"
798 if (SendMSG(logr_queue, final_msg, "WinEvtLog", LOCALFILE_MQ) < 0) {
799 merror(QUEUE_SEND, ARGV0);
802 if (channel->bookmark_enabled) {
803 update_bookmark(evt, channel);
807 free(properties_values);
810 if (context != NULL) {
817 DWORD WINAPI event_channel_callback(EVT_SUBSCRIBE_NOTIFY_ACTION action, os_channel *channel, EVT_HANDLE evt)
819 if (action == EvtSubscribeActionDeliver) {
820 send_channel_event(evt, channel);
826 void win_start_event_channel(char *evt_log, char future, char *query)
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;
836 if ((channel = calloc(1, sizeof(os_channel))) == NULL) {
838 "%s: ERROR: Could not calloc() memory for channel to start reading (%s) which returned [(%d)-(%s)]",
846 channel->evt_log = evt_log;
848 /* Create copy of event log string */
849 if ((channel->bookmark_name = strdup(channel->evt_log)) == NULL) {
851 "%s: ERROR: Could not strdup() event log name to start reading (%s) which returned [(%d)-(%s)]",
859 /* Replace '/' with '_' */
860 if (strchr(channel->bookmark_name, '/')) {
861 *(strrchr(channel->bookmark_name, '/')) = '_';
864 /* Convert evt_log to Windows string */
865 if ((wchannel = convert_unix_string(channel->evt_log)) == NULL) {
867 "%s: ERROR: Could not convert_unix_string() evt_log for (%s) which returned [(%d)-(%s)]",
875 /* Convert query to Windows string */
877 if ((wquery = convert_unix_string(query)) == NULL) {
879 "%s: ERROR: Could not convert_unix_string() query for (%s) which returned [(%d)-(%s)]",
888 channel->bookmark_enabled = !future;
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);
896 /* Try to read existing bookmark */
897 if ((bookmark = read_bookmark(channel)) != NULL) {
898 flags = EvtSubscribeStartAfterBookmark;
902 result = EvtSubscribe(NULL,
908 (EVT_SUBSCRIBE_CALLBACK)event_channel_callback,
911 if (result == NULL && flags == EvtSubscribeStartAfterBookmark) {
912 result = EvtSubscribe(NULL,
918 (EVT_SUBSCRIBE_CALLBACK)event_channel_callback,
919 EvtSubscribeToFutureEvents);
922 if (result == NULL) {
924 "%s: ERROR: Could not EvtSubscribe() for (%s) which returned (%lu)",
939 free(channel->bookmark_name);
942 if (result != NULL) {
947 if (bookmark != NULL) {
954 #endif /* EVENTCHANNEL_SUPPORT */