1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "common.h"
21 #include "log.h"
22 #include "logfiles/logfiles.h"
23 #include "sysinfo.h"
24 #include "zbxregexp.h"
25 #include "winmeta.h"
26 #include <strsafe.h>
27 #include "eventlog.h"
28 #include <delayimp.h>
29 #include <sddl.h>
30 
31 #define MAX_NAME			256
32 
33 static const wchar_t	*RENDER_ITEMS[] = {
34 	L"/Event/System/Provider/@Name",
35 	L"/Event/System/Provider/@EventSourceName",
36 	L"/Event/System/EventRecordID",
37 	L"/Event/System/EventID",
38 	L"/Event/System/Level",
39 	L"/Event/System/Keywords",
40 	L"/Event/System/TimeCreated/@SystemTime",
41 	L"/Event/EventData/Data"
42 };
43 
44 #define	RENDER_ITEMS_COUNT (sizeof(RENDER_ITEMS) / sizeof(const wchar_t *))
45 
46 #define	VAR_PROVIDER_NAME(p)			(p[0].StringVal)
47 #define	VAR_SOURCE_NAME(p)			(p[1].StringVal)
48 #define	VAR_RECORD_NUMBER(p)			(p[2].UInt64Val)
49 #define	VAR_EVENT_ID(p)				(p[3].UInt16Val)
50 #define	VAR_LEVEL(p)				(p[4].ByteVal)
51 #define	VAR_KEYWORDS(p)				(p[5].UInt64Val)
52 #define	VAR_TIME_CREATED(p)			(p[6].FileTimeVal)
53 #define	VAR_EVENT_DATA_STRING(p)		(p[7].StringVal)
54 #define	VAR_EVENT_DATA_STRING_ARRAY(p, i)	(p[7].StringArr[i])
55 #define	VAR_EVENT_DATA_TYPE(p)			(p[7].Type)
56 #define	VAR_EVENT_DATA_COUNT(p)			(p[7].Count)
57 
58 #define	EVENTLOG_REG_PATH TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\")
59 
60 #ifndef INFORMATION_TYPE
61 #	define INFORMATION_TYPE	"Information"
62 #endif
63 #ifndef WARNING_TYPE
64 #	define WARNING_TYPE	"Warning"
65 #endif
66 #ifndef ERROR_TYPE
67 #	define ERROR_TYPE	"Error"
68 #endif
69 #ifndef AUDIT_FAILURE
70 #	define AUDIT_FAILURE	"Failure Audit"
71 #endif
72 #ifndef AUDIT_SUCCESS
73 #	define AUDIT_SUCCESS	"Success Audit"
74 #endif
75 #ifndef CRITICAL_TYPE
76 #	define CRITICAL_TYPE	"Critical"
77 #endif
78 #ifndef VERBOSE_TYPE
79 #	define VERBOSE_TYPE	"Verbose"
80 #endif
81 
82 extern int	CONFIG_EVENTLOG_MAX_LINES_PER_SECOND;
83 
DelayLoadDllExceptionFilter(PEXCEPTION_POINTERS excpointers)84 LONG WINAPI	DelayLoadDllExceptionFilter(PEXCEPTION_POINTERS excpointers)
85 {
86 	LONG		disposition = EXCEPTION_EXECUTE_HANDLER;
87 	PDelayLoadInfo	delayloadinfo = (PDelayLoadInfo)(excpointers->ExceptionRecord->ExceptionInformation[0]);
88 
89 	switch (excpointers->ExceptionRecord->ExceptionCode)
90 	{
91 		case VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND):
92 			zabbix_log(LOG_LEVEL_DEBUG, "function %s was not found in %s",
93 					delayloadinfo->dlp.szProcName, delayloadinfo->szDll);
94 			break;
95 		case VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND):
96 			if (delayloadinfo->dlp.fImportByName)
97 			{
98 				zabbix_log(LOG_LEVEL_DEBUG, "function %s was not found in %s",
99 						delayloadinfo->dlp.szProcName, delayloadinfo->szDll);
100 			}
101 			else
102 			{
103 				zabbix_log(LOG_LEVEL_DEBUG, "function ordinal %d was not found in %s",
104 						delayloadinfo->dlp.dwOrdinal, delayloadinfo->szDll);
105 			}
106 			break;
107 		default:
108 			disposition = EXCEPTION_CONTINUE_SEARCH;
109 			break;
110 	}
111 
112 	return disposition;
113 }
114 
115 /* open event logger and return number of records */
zbx_open_eventlog(LPCTSTR wsource,HANDLE * eventlog_handle,zbx_uint64_t * FirstID,zbx_uint64_t * LastID,DWORD * error_code)116 static int	zbx_open_eventlog(LPCTSTR wsource, HANDLE *eventlog_handle, zbx_uint64_t *FirstID,
117 		zbx_uint64_t *LastID, DWORD *error_code)
118 {
119 	wchar_t	reg_path[MAX_PATH];
120 	HKEY	hk = NULL;
121 	DWORD	dwNumRecords, dwOldestRecord;
122 	int	ret = FAIL;
123 
124 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
125 
126 	*eventlog_handle = NULL;
127 
128 	/* Get path to eventlog */
129 	StringCchPrintf(reg_path, MAX_PATH, EVENTLOG_REG_PATH TEXT("%s"), wsource);
130 
131 	if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg_path, 0, KEY_READ, &hk))
132 	{
133 		*error_code = GetLastError();
134 		goto out;
135 	}
136 
137 	RegCloseKey(hk);
138 
139 	if (NULL == (*eventlog_handle = OpenEventLog(NULL, wsource)))	/* open log file */
140 	{
141 		*error_code = GetLastError();
142 		goto out;
143 	}
144 
145 	if (0 == GetNumberOfEventLogRecords(*eventlog_handle, &dwNumRecords) ||
146 			0 == GetOldestEventLogRecord(*eventlog_handle, &dwOldestRecord))
147 	{
148 		*error_code = GetLastError();
149 		CloseEventLog(*eventlog_handle);
150 		*eventlog_handle = NULL;
151 		goto out;
152 	}
153 
154 	*FirstID = dwOldestRecord;
155 	*LastID = dwOldestRecord + dwNumRecords - 1;
156 
157 	zabbix_log(LOG_LEVEL_DEBUG, "FirstID:" ZBX_FS_UI64 " LastID:" ZBX_FS_UI64 " numIDs:%lu",
158 			*FirstID, *LastID, dwNumRecords);
159 
160 	ret = SUCCEED;
161 out:
162 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
163 
164 	return ret;
165 }
166 
167 /* close event logger */
zbx_close_eventlog(HANDLE eventlog_handle)168 static void	zbx_close_eventlog(HANDLE eventlog_handle)
169 {
170 	if (NULL != eventlog_handle)
171 		CloseEventLog(eventlog_handle);
172 }
173 
174 /******************************************************************************
175  *                                                                            *
176  * Function: zbx_get_message_files                                            *
177  *                                                                            *
178  * Purpose: gets event message and parameter translation files from registry  *
179  *                                                                            *
180  * Parameters: szLogName         - [IN] the log name                          *
181  *             szSourceName      - [IN] the log source name                   *
182  *             pEventMessageFile - [OUT] the event message file               *
183  *             pParamMessageFile - [OUT] the parameter message file           *
184  *                                                                            *
185  ******************************************************************************/
zbx_get_message_files(const wchar_t * szLogName,const wchar_t * szSourceName,wchar_t ** pEventMessageFile,wchar_t ** pParamMessageFile)186 static void	zbx_get_message_files(const wchar_t *szLogName, const wchar_t *szSourceName, wchar_t **pEventMessageFile,
187 		wchar_t **pParamMessageFile)
188 {
189 	wchar_t	buf[MAX_PATH];
190 	HKEY	hKey = NULL;
191 	DWORD	szData;
192 
193 	/* Get path to message dll */
194 	StringCchPrintf(buf, MAX_PATH, EVENTLOG_REG_PATH TEXT("%s\\%s"), szLogName, szSourceName);
195 
196 	if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &hKey))
197 		return;
198 
199 	if (ERROR_SUCCESS == RegQueryValueEx(hKey, TEXT("EventMessageFile"), NULL, NULL, NULL, &szData))
200 	{
201 		*pEventMessageFile = zbx_malloc(*pEventMessageFile, szData);
202 		if (ERROR_SUCCESS != RegQueryValueEx(hKey, TEXT("EventMessageFile"), NULL, NULL,
203 				(unsigned char *)*pEventMessageFile, &szData))
204 		{
205 			zbx_free(*pEventMessageFile);
206 		}
207 	}
208 
209 	if (ERROR_SUCCESS == RegQueryValueEx(hKey, TEXT("ParameterMessageFile"), NULL, NULL, NULL, &szData))
210 	{
211 		*pParamMessageFile = zbx_malloc(*pParamMessageFile, szData);
212 		if (ERROR_SUCCESS != RegQueryValueEx(hKey, TEXT("ParameterMessageFile"), NULL, NULL,
213 				(unsigned char *)*pParamMessageFile, &szData))
214 		{
215 			zbx_free(*pParamMessageFile);
216 		}
217 	}
218 
219 	RegCloseKey(hKey);
220 }
221 
222 /******************************************************************************
223  *                                                                            *
224  * Function: zbx_load_message_file                                            *
225  *                                                                            *
226  * Purpose: load the specified message file, expanding environment variables  *
227  *          in the file name if necessary                                     *
228  *                                                                            *
229  * Parameters: szFileName - [IN] the message file name                        *
230  *                                                                            *
231  * Return value: Handle to the loaded library or NULL otherwise               *
232  *                                                                            *
233  ******************************************************************************/
zbx_load_message_file(const wchar_t * szFileName)234 static HINSTANCE	zbx_load_message_file(const wchar_t *szFileName)
235 {
236 	wchar_t		*dll_name = NULL;
237 	long int	sz, len = 0;
238 	HINSTANCE	res = NULL;
239 
240 	if (NULL == szFileName)
241 		return NULL;
242 
243 	do
244 	{
245 		if (0 != (sz = len))
246 			dll_name = zbx_realloc(dll_name, sz * sizeof(wchar_t));
247 
248 		len = ExpandEnvironmentStrings(szFileName, dll_name, sz);
249 	}
250 	while (0 != len && sz < len);
251 
252 	if (0 != len)
253 		res = LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_AS_DATAFILE);
254 
255 	zbx_free(dll_name);
256 
257 	return res;
258 }
259 
260 /******************************************************************************
261  *                                                                            *
262  * Function: zbx_format_message                                               *
263  *                                                                            *
264  * Purpose: extracts the specified message from a message file                *
265  *                                                                            *
266  * Parameters: hLib           - [IN] the message file handle                  *
267  *             dwMessageId    - [IN] the message identifier                   *
268  *             pInsertStrings - [IN] a list of insert strings, optional       *
269  *                                                                            *
270  * Return value: The formatted message converted to utf8 or NULL              *
271  *                                                                            *
272  * Comments: This function allocates memory for the returned message, which   *
273  *           must be freed by the caller later.                               *
274  *                                                                            *
275  ******************************************************************************/
zbx_format_message(HINSTANCE hLib,DWORD dwMessageId,wchar_t ** pInsertStrings)276 static char	*zbx_format_message(HINSTANCE hLib, DWORD dwMessageId, wchar_t **pInsertStrings)
277 {
278 	wchar_t *pMsgBuf = NULL;
279 	char	*message;
280 
281 	if (0 == FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER |
282 			FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_MAX_WIDTH_MASK,
283 			hLib, dwMessageId, MAKELANGID(LANG_NEUTRAL, SUBLANG_ENGLISH_US), (wchar_t *)&pMsgBuf, 0,
284 			(va_list *)pInsertStrings))
285 	{
286 		return NULL;
287 	}
288 
289 	message = zbx_unicode_to_utf8(pMsgBuf);
290 	zbx_rtrim(message, "\r\n ");
291 
292 	LocalFree((HLOCAL)pMsgBuf);
293 
294 	return message;
295 }
296 
297 /******************************************************************************
298  *                                                                            *
299  * Function: zbx_translate_message_params                                     *
300  *                                                                            *
301  * Purpose: translates message by replacing parameters %%<id> with translated *
302  *          values                                                            *
303  *                                                                            *
304  * Parameters: message - [IN/OUT] the message to translate                    *
305  *             hLib    - [IN] the parameter message file handle               *
306  *                                                                            *
307  ******************************************************************************/
zbx_translate_message_params(char ** message,HINSTANCE hLib)308 static void	zbx_translate_message_params(char **message, HINSTANCE hLib)
309 {
310 	char	*param, *pstart, *pend;
311 	int	dwMessageId;
312 	size_t	offset = 0;
313 
314 	while (1)
315 	{
316 		if (NULL == (pstart = strstr(*message + offset, "%%")))
317 			break;
318 
319 		pend = pstart + 2;
320 
321 		dwMessageId = atoi(pend);
322 
323 		while ('\0' != *pend && 0 != isdigit(*pend))
324 			pend++;
325 
326 		offset = pend - *message - 1;
327 
328 		if (NULL != (param = zbx_format_message(hLib, dwMessageId, NULL)))
329 		{
330 			zbx_replace_string(message, pstart - *message, &offset, param);
331 
332 			zbx_free(param);
333 		}
334 	}
335 }
336 
get_eventlog6_id(EVT_HANDLE * event_query,EVT_HANDLE * render_context,zbx_uint64_t * id,char ** error)337 static int get_eventlog6_id(EVT_HANDLE *event_query, EVT_HANDLE *render_context, zbx_uint64_t *id, char **error)
338 {
339 	int		ret = FAIL;
340 	DWORD		size_required_next = 0, size_required = 0, size = 0, status = 0, bookmarkedCount = 0;
341 	EVT_VARIANT*	renderedContent = NULL;
342 	EVT_HANDLE	event_bookmark = NULL;
343 
344 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
345 
346 	if (TRUE != EvtNext(*event_query, 1, &event_bookmark, INFINITE, 0, &size_required_next))
347 	{
348 		/* no data in eventlog */
349 		zabbix_log(LOG_LEVEL_DEBUG, "%s() EvtNext failed:%s", __func__, strerror_from_system(GetLastError()));
350 		*id = 0;
351 		ret = SUCCEED;
352 		goto out;
353 	}
354 
355 	/* obtain the information from selected events */
356 	if (TRUE != EvtRender(*render_context, event_bookmark, EvtRenderEventValues, size, renderedContent,
357 			&size_required, &bookmarkedCount))
358 	{
359 		/* information exceeds the allocated space */
360 		if (ERROR_INSUFFICIENT_BUFFER != (status = GetLastError()))
361 		{
362 			*error = zbx_dsprintf(*error, "EvtRender failed:%s", strerror_from_system(status));
363 			goto out;
364 		}
365 
366 		size = size_required;
367 		renderedContent = (EVT_VARIANT*)zbx_malloc(NULL, size);
368 
369 		if (TRUE != EvtRender(*render_context, event_bookmark, EvtRenderEventValues, size, renderedContent,
370 				&size_required, &bookmarkedCount))
371 		{
372 			*error = zbx_dsprintf(*error, "EvtRender failed:%s", strerror_from_system(GetLastError()));
373 			goto out;
374 		}
375 	}
376 
377 	*id = VAR_RECORD_NUMBER(renderedContent);
378 	ret = SUCCEED;
379 out:
380 	if (NULL != event_bookmark)
381 		EvtClose(event_bookmark);
382 
383 	zbx_free(renderedContent);
384 
385 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s id:" ZBX_FS_UI64, __func__, zbx_result_string(ret), id);
386 
387 	return ret;
388 }
389 
390 /* open eventlog using API 6 and return the number of records */
zbx_open_eventlog6(const wchar_t * wsource,zbx_uint64_t * lastlogsize,EVT_HANDLE * render_context,zbx_uint64_t * FirstID,zbx_uint64_t * LastID,char ** error)391 static int	zbx_open_eventlog6(const wchar_t *wsource, zbx_uint64_t *lastlogsize, EVT_HANDLE *render_context,
392 		zbx_uint64_t *FirstID, zbx_uint64_t *LastID, char **error)
393 {
394 	EVT_HANDLE	tmp_first_event_query = NULL;
395 	EVT_HANDLE	tmp_last_event_query = NULL;
396 	DWORD		status = 0;
397 	int		ret = FAIL;
398 
399 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() lastlogsize:" ZBX_FS_UI64, __func__, *lastlogsize);
400 
401 	*FirstID = 0;
402 	*LastID = 0;
403 
404 	/* get the number of the oldest record in the log				*/
405 	/* "EvtGetLogInfo()" does not work properly with "EvtLogOldestRecordNumber"	*/
406 	/* we have to get it from the first EventRecordID				*/
407 
408 	/* create the system render */
409 	if (NULL == (*render_context = EvtCreateRenderContext(RENDER_ITEMS_COUNT, RENDER_ITEMS, EvtRenderContextValues)))
410 	{
411 		*error = zbx_dsprintf(*error, "EvtCreateRenderContext failed:%s", strerror_from_system(GetLastError()));
412 		goto out;
413 	}
414 
415 	/* get all eventlog */
416 	if (NULL == (tmp_first_event_query = EvtQuery(NULL, wsource, NULL, EvtQueryChannelPath)))
417 	{
418 		if (ERROR_EVT_CHANNEL_NOT_FOUND == (status = GetLastError()))
419 			*error = zbx_dsprintf(*error, "EvtQuery channel missed:%s", strerror_from_system(status));
420 		else
421 			*error = zbx_dsprintf(*error, "EvtQuery failed:%s", strerror_from_system(status));
422 
423 		goto out;
424 	}
425 
426 	if (SUCCEED != get_eventlog6_id(&tmp_first_event_query, render_context, FirstID, error))
427 		goto out;
428 
429 	if (0 == *FirstID)
430 	{
431 		/* no data in eventlog */
432 		zabbix_log(LOG_LEVEL_DEBUG, "%s() first EvtNext failed", __func__);
433 		*FirstID = 1;
434 		*LastID = 1;
435 		*lastlogsize = 0;
436 		ret = SUCCEED;
437 		goto out;
438 	}
439 
440 	if (NULL == (tmp_last_event_query = EvtQuery(NULL, wsource, NULL,
441 			EvtQueryChannelPath | EvtQueryReverseDirection)))
442 	{
443 		if (ERROR_EVT_CHANNEL_NOT_FOUND == (status = GetLastError()))
444 			*error = zbx_dsprintf(*error, "EvtQuery channel missed:%s", strerror_from_system(status));
445 		else
446 			*error = zbx_dsprintf(*error, "EvtQuery failed:%s", strerror_from_system(status));
447 
448 		goto out;
449 	}
450 
451 	if (SUCCEED != get_eventlog6_id(&tmp_last_event_query, render_context, LastID, error) || 0 == *LastID)
452 	{
453 		/* no data in eventlog */
454 		zabbix_log(LOG_LEVEL_DEBUG, "%s() last EvtNext failed", __func__);
455 		*LastID = 1;
456 	}
457 	else
458 		*LastID += 1;	/* we should read the last record */
459 
460 	if (*lastlogsize >= *LastID)
461 	{
462 		*lastlogsize = *FirstID - 1;
463 		zabbix_log(LOG_LEVEL_WARNING, "lastlogsize is too big. It is set to:" ZBX_FS_UI64, *lastlogsize);
464 	}
465 
466 	ret = SUCCEED;
467 out:
468 	if (NULL != tmp_first_event_query)
469 		EvtClose(tmp_first_event_query);
470 	if (NULL != tmp_last_event_query)
471 		EvtClose(tmp_last_event_query);
472 
473 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s FirstID:" ZBX_FS_UI64 " LastID:" ZBX_FS_UI64,
474 			__func__, zbx_result_string(ret), *FirstID, *LastID);
475 
476 	return ret;
477 }
478 
479 /* get handles of eventlog */
zbx_get_handle_eventlog6(const wchar_t * wsource,zbx_uint64_t * lastlogsize,EVT_HANDLE * query,char ** error)480 static int	zbx_get_handle_eventlog6(const wchar_t *wsource, zbx_uint64_t *lastlogsize, EVT_HANDLE *query,
481 		char **error)
482 {
483 	wchar_t	*event_query = NULL;
484 	char	*tmp_str = NULL;
485 	int	ret = FAIL;
486 
487 	zabbix_log(LOG_LEVEL_DEBUG, "In %s(), previous lastlogsize:" ZBX_FS_UI64, __func__, *lastlogsize);
488 
489 	/* start building the query */
490 	tmp_str = zbx_dsprintf(NULL, "Event/System[EventRecordID>" ZBX_FS_UI64 "]", *lastlogsize);
491 	event_query = zbx_utf8_to_unicode(tmp_str);
492 
493 	/* create massive query for an event on a local computer*/
494 	*query = EvtQuery(NULL, wsource, event_query, EvtQueryChannelPath);
495 	if (NULL == *query)
496 	{
497 		DWORD	status;
498 
499 		if (ERROR_EVT_CHANNEL_NOT_FOUND == (status = GetLastError()))
500 			*error = zbx_dsprintf(*error, "EvtQuery channel missed:%s", strerror_from_system(status));
501 		else
502 			*error = zbx_dsprintf(*error, "EvtQuery failed:%s", strerror_from_system(status));
503 
504 		goto out;
505 	}
506 
507 	ret = SUCCEED;
508 out:
509 	zbx_free(tmp_str);
510 	zbx_free(event_query);
511 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
512 
513 	return ret;
514 }
515 
516 /* initialize event logs with Windows API version 6 */
initialize_eventlog6(const char * source,zbx_uint64_t * lastlogsize,zbx_uint64_t * FirstID,zbx_uint64_t * LastID,EVT_HANDLE * render_context,EVT_HANDLE * query,char ** error)517 int	initialize_eventlog6(const char *source, zbx_uint64_t *lastlogsize, zbx_uint64_t *FirstID,
518 		zbx_uint64_t *LastID, EVT_HANDLE *render_context, EVT_HANDLE *query, char **error)
519 {
520 	wchar_t	*wsource = NULL;
521 	int	ret = FAIL;
522 
523 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() source:'%s' previous lastlogsize:" ZBX_FS_UI64,
524 			__func__, source, *lastlogsize);
525 
526 	if (NULL == source || '\0' == *source)
527 	{
528 		*error = zbx_dsprintf(*error, "cannot open eventlog with empty name.");
529 		goto out;
530 	}
531 
532 	wsource = zbx_utf8_to_unicode(source);
533 
534 	if (SUCCEED != zbx_open_eventlog6(wsource, lastlogsize, render_context, FirstID, LastID, error))
535 	{
536 		zabbix_log(LOG_LEVEL_ERR, "cannot open eventlog '%s'", source);
537 		goto out;
538 	}
539 
540 	if (SUCCEED != zbx_get_handle_eventlog6(wsource, lastlogsize, query, error))
541 	{
542 		zabbix_log(LOG_LEVEL_ERR, "cannot get eventlog handle '%s'", source);
543 		goto out;
544 	}
545 
546 	ret = SUCCEED;
547 out:
548 	zbx_free(wsource);
549 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
550 
551 	return ret;
552 }
553 
554 /* expand the string message from a specific event handler */
expand_message6(const wchar_t * pname,EVT_HANDLE event)555 static char	*expand_message6(const wchar_t *pname, EVT_HANDLE event)
556 {
557 	wchar_t		*pmessage = NULL;
558 	EVT_HANDLE	provider = NULL;
559 	DWORD		require = 0;
560 	char		*out_message = NULL;
561 	char		*tmp_pname = NULL;
562 
563 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
564 
565 	if (NULL == (provider = EvtOpenPublisherMetadata(NULL, pname, NULL, 0, 0)))
566 	{
567 		tmp_pname = zbx_unicode_to_utf8(pname);
568 		zabbix_log(LOG_LEVEL_DEBUG, "provider '%s' could not be opened: %s",
569 				tmp_pname, strerror_from_system(GetLastError()));
570 		zbx_free(tmp_pname);
571 		goto out;
572 	}
573 
574 	if (TRUE != EvtFormatMessage(provider, event, 0, 0, NULL, EvtFormatMessageEvent, 0, NULL, &require))
575 	{
576 		if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
577 		{
578 			DWORD	error = ERROR_SUCCESS;
579 
580 			pmessage = zbx_malloc(pmessage, sizeof(WCHAR) * require);
581 
582 			if (TRUE != EvtFormatMessage(provider, event, 0, 0, NULL, EvtFormatMessageEvent, require,
583 					pmessage, &require))
584 			{
585 				error = GetLastError();
586 			}
587 
588 			if (ERROR_SUCCESS == error || ERROR_EVT_UNRESOLVED_VALUE_INSERT == error ||
589 					ERROR_EVT_UNRESOLVED_PARAMETER_INSERT == error ||
590 					ERROR_EVT_MAX_INSERTS_REACHED == error)
591 			{
592 				out_message = zbx_unicode_to_utf8(pmessage);
593 			}
594 			else
595 			{
596 				zabbix_log(LOG_LEVEL_DEBUG, "%s() cannot format message: %s", __func__,
597 						strerror_from_system(error));
598 				goto out;
599 			}
600 		}
601 	}
602 out:
603 	if (NULL != provider)
604 		EvtClose(provider);
605 	zbx_free(pmessage);
606 
607 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, out_message);
608 
609 	/* should be freed */
610 	return out_message;
611 }
612 
replace_sid_to_account(PSID sidVal,char ** out_message)613 static void	replace_sid_to_account(PSID sidVal, char **out_message)
614 {
615 	DWORD	nlen = MAX_NAME, dlen = MAX_NAME;
616 	wchar_t	name[MAX_NAME], dom[MAX_NAME], *sid = NULL;
617 	int	iUse;
618 	char	userName[MAX_NAME * 4], domName[MAX_NAME * 4], sidName[MAX_NAME * 4], *tmp, buffer[MAX_NAME * 8];
619 
620 	if (0 == LookupAccountSid(NULL, sidVal, name, &nlen, dom, &dlen, (PSID_NAME_USE)&iUse))
621 	{
622 		/* don't replace security ID if no mapping between account names and security IDs was done */
623 		zabbix_log(LOG_LEVEL_DEBUG, "LookupAccountSid failed:%s", strerror_from_system(GetLastError()));
624 		return;
625 	}
626 
627 	if (0 == nlen)
628 	{
629 		zabbix_log(LOG_LEVEL_DEBUG, "LookupAccountSid returned empty user name");
630 		return;
631 	}
632 
633 	if (0 == ConvertSidToStringSid(sidVal, &sid))
634 	{
635 		zabbix_log(LOG_LEVEL_DEBUG, "ConvertSidToStringSid failed:%s", strerror_from_system(GetLastError()));
636 		return;
637 	}
638 
639 	zbx_unicode_to_utf8_static(sid, sidName, sizeof(sidName));
640 	zbx_unicode_to_utf8_static(name, userName, sizeof(userName));
641 
642 	if (0 != dlen)
643 	{
644 		zbx_unicode_to_utf8_static(dom, domName, sizeof(domName));
645 		zbx_snprintf(buffer, sizeof(buffer), "%s\\%s", domName, userName);
646 	}
647 	else
648 		zbx_strlcpy(buffer, userName, sizeof(buffer));	/* NULL SID */
649 
650 	tmp = *out_message;
651 	*out_message = string_replace(*out_message, sidName, buffer);
652 
653 	LocalFree(sid);
654 	zbx_free(tmp);
655 }
656 
replace_sids_to_accounts(EVT_HANDLE event_bookmark,char ** out_message)657 static void	replace_sids_to_accounts(EVT_HANDLE event_bookmark, char **out_message)
658 {
659 	DWORD		status, dwBufferSize = 0, dwBufferUsed = 0, dwPropertyCount = 0, i;
660 	PEVT_VARIANT	renderedContent = NULL;
661 	EVT_HANDLE	render_context;
662 
663 	if (NULL == (render_context = EvtCreateRenderContext(0, NULL, EvtRenderContextUser)))
664 	{
665 		zabbix_log(LOG_LEVEL_WARNING, "EvtCreateRenderContext failed:%s", strerror_from_system(GetLastError()));
666 		goto cleanup;
667 	}
668 
669 	if (TRUE != EvtRender(render_context, event_bookmark, EvtRenderEventValues, dwBufferSize, renderedContent,
670 			&dwBufferUsed, &dwPropertyCount))
671 	{
672 		if (ERROR_INSUFFICIENT_BUFFER != (status = GetLastError()))
673 		{
674 			zabbix_log(LOG_LEVEL_WARNING, "EvtRender failed:%s", strerror_from_system(status));
675 			goto cleanup;
676 		}
677 
678 		dwBufferSize = dwBufferUsed;
679 		renderedContent = (PEVT_VARIANT)zbx_malloc(NULL, dwBufferSize);
680 
681 		if (TRUE != EvtRender(render_context, event_bookmark, EvtRenderEventValues, dwBufferSize,
682 				renderedContent, &dwBufferUsed, &dwPropertyCount))
683 		{
684 			zabbix_log(LOG_LEVEL_WARNING, "EvtRender failed:%s", strerror_from_system(GetLastError()));
685 			goto cleanup;
686 		}
687 	}
688 
689 	for (i = 0; i < dwPropertyCount; i++)
690 	{
691 		if (EvtVarTypeSid == renderedContent[i].Type)
692 			replace_sid_to_account(renderedContent[i].SidVal, out_message);
693 	}
694 cleanup:
695 	if (NULL != render_context)
696 		EvtClose(render_context);
697 
698 	zbx_free(renderedContent);
699 }
700 
701 /******************************************************************************
702  *                                                                            *
703  * Function: zbx_parse_eventlog_message6                                      *
704  *                                                                            *
705  * Purpose: details parse of a single EventLog record                         *
706  *                                                                            *
707  * Parameters: wsource        - [IN] EventLog file name                       *
708  *             render_context - [IN] the handle to the rendering context      *
709  *             event_bookmark - [IN/OUT] the handle of Event record for parse *
710  *             which          - [IN/OUT] the position of the EventLog record  *
711  *             out_severity   - [OUT] the ELR detail                          *
712  *             out_timestamp  - [OUT] the ELR detail                          *
713  *             out_provider   - [OUT] the ELR detail                          *
714  *             out_source     - [OUT] the ELR detail                          *
715  *             out_message    - [OUT] the ELR detail                          *
716  *             out_eventid    - [OUT] the ELR detail                          *
717  *             out_keywords   - [OUT] the ELR detail                          *
718  *             error          - [OUT] the error message in the case of        *
719  *                                    failure                                 *
720  *                                                                            *
721  * Return value: SUCCEED or FAIL                                              *
722  *                                                                            *
723  ******************************************************************************/
zbx_parse_eventlog_message6(const wchar_t * wsource,EVT_HANDLE * render_context,EVT_HANDLE * event_bookmark,zbx_uint64_t * which,unsigned short * out_severity,unsigned long * out_timestamp,char ** out_provider,char ** out_source,char ** out_message,unsigned long * out_eventid,zbx_uint64_t * out_keywords,char ** error)724 static int	zbx_parse_eventlog_message6(const wchar_t *wsource, EVT_HANDLE *render_context,
725 		EVT_HANDLE *event_bookmark, zbx_uint64_t *which, unsigned short *out_severity,
726 		unsigned long *out_timestamp, char **out_provider, char **out_source, char **out_message,
727 		unsigned long *out_eventid, zbx_uint64_t *out_keywords, char **error)
728 {
729 	EVT_VARIANT*		renderedContent = NULL;
730 	const wchar_t		*pprovider = NULL;
731 	char			*tmp_str = NULL;
732 	DWORD			size = 0, bookmarkedCount = 0, require = 0, error_code;
733 	const zbx_uint64_t	sec_1970 = 116444736000000000;
734 	const zbx_uint64_t	success_audit = 0x20000000000000;
735 	const zbx_uint64_t	failure_audit = 0x10000000000000;
736 	int			ret = FAIL;
737 
738 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() EventRecordID:" ZBX_FS_UI64, __func__, *which);
739 
740 	/* obtain the information from the selected events */
741 	if (TRUE != EvtRender(*render_context, *event_bookmark, EvtRenderEventValues, size, renderedContent,
742 			&require, &bookmarkedCount))
743 	{
744 		/* information exceeds the space allocated */
745 		if (ERROR_INSUFFICIENT_BUFFER != (error_code = GetLastError()))
746 		{
747 			*error = zbx_dsprintf(*error, "EvtRender failed: %s", strerror_from_system(error_code));
748 			goto out;
749 		}
750 
751 		size = require;
752 		renderedContent = (EVT_VARIANT *)zbx_malloc(NULL, size);
753 
754 		if (TRUE != EvtRender(*render_context, *event_bookmark, EvtRenderEventValues, size, renderedContent,
755 				&require, &bookmarkedCount))
756 		{
757 			*error = zbx_dsprintf(*error, "EvtRender failed: %s", strerror_from_system(GetLastError()));
758 			goto out;
759 		}
760 	}
761 
762 	pprovider = VAR_PROVIDER_NAME(renderedContent);
763 	*out_provider = zbx_unicode_to_utf8(pprovider);
764 	*out_source = NULL;
765 
766 	if (NULL != VAR_SOURCE_NAME(renderedContent))
767 	{
768 		*out_source = zbx_unicode_to_utf8(VAR_SOURCE_NAME(renderedContent));
769 	}
770 
771 	*out_keywords = VAR_KEYWORDS(renderedContent) & (success_audit | failure_audit);
772 	*out_severity = VAR_LEVEL(renderedContent);
773 	*out_timestamp = (unsigned long)((VAR_TIME_CREATED(renderedContent) - sec_1970) / 10000000);
774 	*out_eventid = VAR_EVENT_ID(renderedContent);
775 	*out_message = expand_message6(pprovider, *event_bookmark);
776 
777 	if (NULL != *out_message)
778 		replace_sids_to_accounts(*event_bookmark, out_message);
779 
780 	tmp_str = zbx_unicode_to_utf8(wsource);
781 
782 	if (VAR_RECORD_NUMBER(renderedContent) != *which)
783 	{
784 		zabbix_log(LOG_LEVEL_DEBUG, "%s() Overwriting expected EventRecordID:" ZBX_FS_UI64 " with the real"
785 				" EventRecordID:" ZBX_FS_UI64 " in eventlog '%s'", __func__, *which,
786 				VAR_RECORD_NUMBER(renderedContent), tmp_str);
787 		*which = VAR_RECORD_NUMBER(renderedContent);
788 	}
789 
790 	/* some events don't have enough information for making event message */
791 	if (NULL == *out_message)
792 	{
793 		*out_message = zbx_strdcatf(*out_message, "The description for Event ID:%lu in Source:'%s'"
794 				" cannot be found. Either the component that raises this event is not installed"
795 				" on your local computer or the installation is corrupted. You can install or repair"
796 				" the component on the local computer. If the event originated on another computer,"
797 				" the display information had to be saved with the event.", *out_eventid,
798 				NULL == *out_provider ? "" : *out_provider);
799 
800 		if (EvtVarTypeString == (VAR_EVENT_DATA_TYPE(renderedContent) & EVT_VARIANT_TYPE_MASK))
801 		{
802 			unsigned int	i;
803 			char		*data = NULL;
804 
805 			if (0 != (VAR_EVENT_DATA_TYPE(renderedContent) & EVT_VARIANT_TYPE_ARRAY) &&
806 				0 < VAR_EVENT_DATA_COUNT(renderedContent))
807 			{
808 				*out_message = zbx_strdcatf(*out_message, " The following information was included"
809 						" with the event: ");
810 
811 				for (i = 0; i < VAR_EVENT_DATA_COUNT(renderedContent); i++)
812 				{
813 					if (NULL != VAR_EVENT_DATA_STRING_ARRAY(renderedContent, i))
814 					{
815 						if (0 < i)
816 							*out_message = zbx_strdcat(*out_message, "; ");
817 
818 						data = zbx_unicode_to_utf8(VAR_EVENT_DATA_STRING_ARRAY(renderedContent,
819 								i));
820 						*out_message = zbx_strdcatf(*out_message, "%s", data);
821 						zbx_free(data);
822 					}
823 				}
824 			}
825 			else if (NULL != VAR_EVENT_DATA_STRING(renderedContent))
826 			{
827 				data = zbx_unicode_to_utf8(VAR_EVENT_DATA_STRING(renderedContent));
828 				*out_message = zbx_strdcatf(*out_message, "The following information was included"
829 						" with the event: %s", data);
830 				zbx_free(data);
831 			}
832 		}
833 	}
834 
835 	ret = SUCCEED;
836 out:
837 	EvtClose(*event_bookmark);
838 	*event_bookmark = NULL;
839 
840 	zbx_free(tmp_str);
841 	zbx_free(renderedContent);
842 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
843 
844 	return ret;
845 }
846 
847 /******************************************************************************
848  *                                                                            *
849  * Function: process_eventslog6                                               *
850  *                                                                            *
851  * Purpose:  batch processing of Event Log file                               *
852  *                                                                            *
853  * Parameters: server           - [IN] IP or Hostname of Zabbix server        *
854  *             port             - [IN] port of Zabbix server                  *
855  *             eventlog_name    - [IN] the name of the event log              *
856  *             render_context   - [IN] the handle to the rendering context    *
857  *             query            - [IN] the handle to the query results        *
858  *             lastlogsize      - [IN] position of the last processed record  *
859  *             FirstID          - [IN] first record in the EventLog file      *
860  *             LastID           - [IN] last record in the EventLog file       *
861  *             regexps          - [IN] set of regexp rules for Event Log test *
862  *             pattern          - [IN] buffer for read of data of EventLog    *
863  *             key_severity     - [IN] severity of logged data sources        *
864  *             key_source       - [IN] name of logged data source             *
865  *             key_logeventid   - [IN] the application-specific identifier    *
866  *                                     for the event                          *
867  *             rate             - [IN] threshold of records count at a time   *
868  *             process_value_cb - [IN] callback function for sending data to  *
869  *                                     the server                             *
870  *             metric           - [IN/OUT] parameters for EventLog process    *
871  *             lastlogsize_sent - [OUT] position of the last record sent to   *
872  *                                      the server                            *
873  *             error            - [OUT] the error message in the case of      *
874  *                                      failure                               *
875  *                                                                            *
876  * Return value: SUCCEED or FAIL                                              *
877  *                                                                            *
878  ******************************************************************************/
process_eventslog6(const char * server,unsigned short port,const char * eventlog_name,EVT_HANDLE * render_context,EVT_HANDLE * query,zbx_uint64_t lastlogsize,zbx_uint64_t FirstID,zbx_uint64_t LastID,zbx_vector_ptr_t * regexps,const char * pattern,const char * key_severity,const char * key_source,const char * key_logeventid,int rate,zbx_process_value_func_t process_value_cb,ZBX_ACTIVE_METRIC * metric,zbx_uint64_t * lastlogsize_sent,char ** error)879 int	process_eventslog6(const char *server, unsigned short port, const char *eventlog_name, EVT_HANDLE *render_context,
880 		EVT_HANDLE *query, zbx_uint64_t lastlogsize, zbx_uint64_t FirstID, zbx_uint64_t LastID,
881 		zbx_vector_ptr_t *regexps, const char *pattern, const char *key_severity, const char *key_source,
882 		const char *key_logeventid, int rate, zbx_process_value_func_t process_value_cb,
883 		ZBX_ACTIVE_METRIC *metric, zbx_uint64_t *lastlogsize_sent, char **error)
884 {
885 #	define EVT_ARRAY_SIZE	100
886 
887 	const char	*str_severity;
888 	zbx_uint64_t	keywords, i, reading_startpoint = 0;
889 	wchar_t		*eventlog_name_w = NULL;
890 	int		s_count = 0, p_count = 0, send_err = SUCCEED, ret = FAIL, match = SUCCEED;
891 	DWORD		required_buf_size = 0, error_code = ERROR_SUCCESS;
892 
893 	unsigned long	evt_timestamp, evt_eventid = 0;
894 	char		*evt_provider, *evt_source, *evt_message, str_logeventid[8];
895 	unsigned short	evt_severity;
896 	EVT_HANDLE	event_bookmarks[EVT_ARRAY_SIZE];
897 
898 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() source: '%s' previous lastlogsize: " ZBX_FS_UI64 ", FirstID: "
899 			ZBX_FS_UI64 ", LastID: " ZBX_FS_UI64, __func__, eventlog_name, lastlogsize, FirstID,
900 			LastID);
901 
902 	/* update counters */
903 	if (1 == metric->skip_old_data)
904 	{
905 		metric->lastlogsize = LastID - 1;
906 		metric->skip_old_data = 0;
907 		zabbix_log(LOG_LEVEL_DEBUG, "skipping existing data: lastlogsize:" ZBX_FS_UI64, lastlogsize);
908 		goto finish;
909 	}
910 
911 	if (NULL == *query)
912 	{
913 		zabbix_log(LOG_LEVEL_DEBUG, "%s() no EvtQuery handle", __func__);
914 		goto out;
915 	}
916 
917 	if (lastlogsize >= FirstID && lastlogsize < LastID)
918 		reading_startpoint = lastlogsize + 1;
919 	else
920 		reading_startpoint = FirstID;
921 
922 	if (reading_startpoint == LastID)	/* LastID = FirstID + count */
923 		goto finish;
924 
925 	eventlog_name_w = zbx_utf8_to_unicode(eventlog_name);
926 
927 	while (ERROR_SUCCESS == error_code)
928 	{
929 		/* get the entries */
930 		if (TRUE != EvtNext(*query, EVT_ARRAY_SIZE, event_bookmarks, INFINITE, 0, &required_buf_size))
931 		{
932 			/* The event reading query had less items than we calculated before. */
933 			/* Either the eventlog was cleaned or our calculations were wrong.   */
934 			/* Either way we can safely abort the query by setting NULL value    */
935 			/* and returning success, which is interpreted as empty eventlog.    */
936 			if (ERROR_NO_MORE_ITEMS == (error_code = GetLastError()))
937 				continue;
938 
939 			*error = zbx_dsprintf(*error, "EvtNext failed: %s, EventRecordID:" ZBX_FS_UI64,
940 					strerror_from_system(error_code), lastlogsize + 1);
941 			goto out;
942 		}
943 
944 		for (i = 0; i < required_buf_size; i++)
945 		{
946 			lastlogsize += 1;
947 
948 			if (SUCCEED != zbx_parse_eventlog_message6(eventlog_name_w, render_context, &event_bookmarks[i],
949 					&lastlogsize, &evt_severity, &evt_timestamp, &evt_provider, &evt_source,
950 					&evt_message, &evt_eventid, &keywords, error))
951 			{
952 				goto out;
953 			}
954 
955 			switch (evt_severity)
956 			{
957 				case WINEVENT_LEVEL_LOG_ALWAYS:
958 				case WINEVENT_LEVEL_INFO:
959 					if (0 != (keywords & WINEVENT_KEYWORD_AUDIT_FAILURE))
960 					{
961 						evt_severity = ITEM_LOGTYPE_FAILURE_AUDIT;
962 						str_severity = AUDIT_FAILURE;
963 						break;
964 					}
965 					else if (0 != (keywords & WINEVENT_KEYWORD_AUDIT_SUCCESS))
966 					{
967 						evt_severity = ITEM_LOGTYPE_SUCCESS_AUDIT;
968 						str_severity = AUDIT_SUCCESS;
969 						break;
970 					}
971 					else
972 						evt_severity = ITEM_LOGTYPE_INFORMATION;
973 						str_severity = INFORMATION_TYPE;
974 						break;
975 				case WINEVENT_LEVEL_WARNING:
976 					evt_severity = ITEM_LOGTYPE_WARNING;
977 					str_severity = WARNING_TYPE;
978 					break;
979 				case WINEVENT_LEVEL_ERROR:
980 					evt_severity = ITEM_LOGTYPE_ERROR;
981 					str_severity = ERROR_TYPE;
982 					break;
983 				case WINEVENT_LEVEL_CRITICAL:
984 					evt_severity = ITEM_LOGTYPE_CRITICAL;
985 					str_severity = CRITICAL_TYPE;
986 					break;
987 				case WINEVENT_LEVEL_VERBOSE:
988 					evt_severity = ITEM_LOGTYPE_VERBOSE;
989 					str_severity = VERBOSE_TYPE;
990 					break;
991 			}
992 
993 			zbx_snprintf(str_logeventid, sizeof(str_logeventid), "%lu", evt_eventid);
994 
995 			if (0 == p_count)
996 			{
997 				int	ret1, ret2, ret3, ret4;
998 
999 				if (FAIL == (ret1 = regexp_match_ex(regexps, evt_message, pattern,
1000 						ZBX_CASE_SENSITIVE)))
1001 				{
1002 					*error = zbx_strdup(*error,
1003 							"Invalid regular expression in the second parameter.");
1004 					match = FAIL;
1005 				}
1006 				else if (FAIL == (ret2 = regexp_match_ex(regexps, str_severity, key_severity,
1007 						ZBX_IGNORE_CASE)))
1008 				{
1009 					*error = zbx_strdup(*error,
1010 							"Invalid regular expression in the third parameter.");
1011 					match = FAIL;
1012 				}
1013 				else if (FAIL == (ret3 = regexp_match_ex(regexps, evt_provider, key_source,
1014 						ZBX_IGNORE_CASE)))
1015 				{
1016 					*error = zbx_strdup(*error,
1017 							"Invalid regular expression in the fourth parameter.");
1018 					match = FAIL;
1019 				}
1020 				else if (FAIL == (ret4 = regexp_match_ex(regexps, str_logeventid,
1021 						key_logeventid, ZBX_CASE_SENSITIVE)))
1022 				{
1023 					*error = zbx_strdup(*error,
1024 							"Invalid regular expression in the fifth parameter.");
1025 					match = FAIL;
1026 				}
1027 
1028 				if (FAIL == match)
1029 				{
1030 					zbx_free(evt_source);
1031 					zbx_free(evt_provider);
1032 					zbx_free(evt_message);
1033 
1034 					ret = FAIL;
1035 					break;
1036 				}
1037 
1038 				match = ZBX_REGEXP_MATCH == ret1 && ZBX_REGEXP_MATCH == ret2 &&
1039 						ZBX_REGEXP_MATCH == ret3 && ZBX_REGEXP_MATCH == ret4;
1040 			}
1041 			else
1042 			{
1043 				match = ZBX_REGEXP_MATCH == regexp_match_ex(regexps, evt_message, pattern,
1044 							ZBX_CASE_SENSITIVE) &&
1045 						ZBX_REGEXP_MATCH == regexp_match_ex(regexps, str_severity,
1046 							key_severity, ZBX_IGNORE_CASE) &&
1047 						ZBX_REGEXP_MATCH == regexp_match_ex(regexps, evt_provider,
1048 							key_source, ZBX_IGNORE_CASE) &&
1049 						ZBX_REGEXP_MATCH == regexp_match_ex(regexps, str_logeventid,
1050 							key_logeventid, ZBX_CASE_SENSITIVE);
1051 			}
1052 
1053 			if (1 == match)
1054 			{
1055 				send_err = process_value_cb(server, port, CONFIG_HOSTNAME, metric->key_orig,
1056 						evt_message, ITEM_STATE_NORMAL, &lastlogsize, NULL, &evt_timestamp,
1057 						evt_provider, &evt_severity, &evt_eventid,
1058 						metric->flags | ZBX_METRIC_FLAG_PERSISTENT);
1059 
1060 				if (SUCCEED == send_err)
1061 				{
1062 					*lastlogsize_sent = lastlogsize;
1063 					s_count++;
1064 				}
1065 			}
1066 			p_count++;
1067 
1068 			zbx_free(evt_source);
1069 			zbx_free(evt_provider);
1070 			zbx_free(evt_message);
1071 
1072 			if (SUCCEED == send_err)
1073 			{
1074 				metric->lastlogsize = lastlogsize;
1075 			}
1076 			else
1077 			{
1078 				/* buffer is full, stop processing active checks */
1079 				/* till the buffer is cleared */
1080 				break;
1081 			}
1082 
1083 			/* do not flood Zabbix server if file grows too fast */
1084 			if (s_count >= (rate * metric->refresh))
1085 				break;
1086 
1087 			/* do not flood local system if file grows too fast */
1088 			if (p_count >= (4 * rate * metric->refresh))
1089 				break;
1090 		}
1091 
1092 		if (i < required_buf_size)
1093 			error_code = ERROR_NO_MORE_ITEMS;
1094 	}
1095 finish:
1096 	ret = SUCCEED;
1097 out:
1098 	for (i = 0; i < required_buf_size; i++)
1099 	{
1100 		if (NULL != event_bookmarks[i])
1101 			EvtClose(event_bookmarks[i]);
1102 	}
1103 
1104 	zbx_free(eventlog_name_w);
1105 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s last eventid:%lu", __func__, zbx_result_string(ret), evt_eventid);
1106 
1107 	return ret;
1108 }
1109 
1110 /* finalize eventlog6 and free the handles */
finalize_eventlog6(EVT_HANDLE * render_context,EVT_HANDLE * query)1111 int	finalize_eventlog6(EVT_HANDLE *render_context, EVT_HANDLE *query)
1112 {
1113 	int		ret = FAIL;
1114 
1115 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
1116 
1117 	if (NULL != *query)
1118 	{
1119 		EvtClose(*query);
1120 		*query = NULL;
1121 	}
1122 
1123 	if (NULL != *render_context)
1124 	{
1125 		EvtClose(*render_context);
1126 		*render_context = NULL;
1127 	}
1128 
1129 	ret = SUCCEED;
1130 
1131 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
1132 
1133 	return ret;
1134 }
1135 
1136 /******************************************************************************
1137  *                                                                            *
1138  * Function: seek_eventlog                                                    *
1139  *                                                                            *
1140  * Purpose: try to set reading position in event log                          *
1141  *                                                                            *
1142  * Parameters: eventlog_handle - [IN] the handle to the event log to be read  *
1143  *             FirstID         - [IN] the first Event log record to be parse  *
1144  *             ReadDirection   - [IN] direction of reading:                   *
1145  *                                    EVENTLOG_FORWARDS_READ or               *
1146  *                                    EVENTLOG_BACKWARDS_READ                 *
1147  *             LastID          - [IN] position of last record in EventLog     *
1148  *             eventlog_name   - [IN] the name of the event log               *
1149  *             pELRs           - [IN/OUT] buffer for read of data of EventLog *
1150  *             buffer_size     - [IN/OUT] size of the pELRs                   *
1151  *             num_bytes_read  - [OUT] the number of bytes read from EventLog *
1152  *             error_code      - [OUT] error code (e.g. from ReadEventLog())  *
1153  *             error           - [OUT] the error message in the case of       *
1154  *                                     failure                                *
1155  *                                                                            *
1156  * Return value: SUCCEED or FAIL                                              *
1157  *                                                                            *
1158  ******************************************************************************/
seek_eventlog(HANDLE * eventlog_handle,zbx_uint64_t FirstID,DWORD ReadDirection,zbx_uint64_t LastID,const char * eventlog_name,BYTE ** pELRs,int * buffer_size,DWORD * num_bytes_read,DWORD * error_code,char ** error)1159 static int	seek_eventlog(HANDLE *eventlog_handle, zbx_uint64_t FirstID, DWORD ReadDirection,
1160 		zbx_uint64_t LastID, const char *eventlog_name, BYTE **pELRs, int *buffer_size, DWORD *num_bytes_read,
1161 		DWORD *error_code, char **error)
1162 {
1163 	DWORD		dwRecordNumber, required_buf_size;
1164 	zbx_uint64_t	skip_count = 0;
1165 
1166 	/* convert to DWORD to handle possible event record number wraparound */
1167 	dwRecordNumber = (DWORD)FirstID;
1168 
1169 	*error_code = ERROR_SUCCESS;
1170 
1171 	while (ERROR_SUCCESS == *error_code)
1172 	{
1173 		if (0 != ReadEventLog(eventlog_handle, EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ, dwRecordNumber,
1174 				*pELRs, *buffer_size, num_bytes_read, &required_buf_size))
1175 		{
1176 			return SUCCEED;
1177 		}
1178 
1179 		if (ERROR_INVALID_PARAMETER == (*error_code = GetLastError()))
1180 		{
1181 			/* See Microsoft Knowledge Base article, 177199 "BUG: ReadEventLog Fails with Error 87" */
1182 			/* how ReadEventLog() can fail with all valid parameters. */
1183 			/* Error code 87 is named ERROR_INVALID_PARAMETER. */
1184 			break;
1185 		}
1186 
1187 		if (ERROR_HANDLE_EOF == *error_code)
1188 			return SUCCEED;
1189 
1190 		if (ERROR_INSUFFICIENT_BUFFER == *error_code)
1191 		{
1192 			*buffer_size = required_buf_size;
1193 			*pELRs = (BYTE *)zbx_realloc((void *)*pELRs, *buffer_size);
1194 			*error_code = ERROR_SUCCESS;
1195 			continue;
1196 		}
1197 
1198 		*error = zbx_dsprintf(*error, "Cannot read eventlog '%s': %s.", eventlog_name,
1199 				strerror_from_system(*error_code));
1200 		return FAIL;
1201 	}
1202 
1203 	if (EVENTLOG_FORWARDS_READ == ReadDirection)
1204 	{
1205 		/* Error 87 when reading forwards is handled outside this function */
1206 		*error_code = ERROR_SUCCESS;
1207 		return SUCCEED;
1208 	}
1209 
1210 	/* fallback implementation to deal with Error 87 when reading backwards */
1211 
1212 	if (ERROR_INVALID_PARAMETER == *error_code)
1213 	{
1214 		if (LastID == FirstID)
1215 			skip_count = 1;
1216 		else
1217 			skip_count = LastID - FirstID;
1218 
1219 		zabbix_log(LOG_LEVEL_DEBUG, "In %s(): fallback error_code=%d skip_count="ZBX_FS_UI64, __func__,
1220 				*error_code, skip_count);
1221 	}
1222 
1223 	*error_code = ERROR_SUCCESS;
1224 
1225 	while (0 < skip_count && ERROR_SUCCESS == *error_code)
1226 	{
1227 		BYTE	*pEndOfRecords, *pELR;
1228 
1229 		if (0 == ReadEventLog(eventlog_handle, EVENTLOG_SEQUENTIAL_READ | ReadDirection, 0, *pELRs,
1230 				*buffer_size, num_bytes_read, &required_buf_size))
1231 		{
1232 			if (ERROR_INSUFFICIENT_BUFFER == (*error_code = GetLastError()))
1233 			{
1234 				*error_code = ERROR_SUCCESS;
1235 				*buffer_size = required_buf_size;
1236 				*pELRs = (BYTE *)zbx_realloc((void *)*pELRs, *buffer_size);
1237 				continue;
1238 			}
1239 
1240 			if (ERROR_HANDLE_EOF != *error_code)
1241 				break;
1242 
1243 			*error = zbx_dsprintf(*error, "Cannot read eventlog '%s': %s.", eventlog_name,
1244 					strerror_from_system(*error_code));
1245 			return FAIL;
1246 		}
1247 
1248 		pELR = *pELRs;
1249 		pEndOfRecords = *pELRs + *num_bytes_read;
1250 		*num_bytes_read = 0;	/* we can't reuse the buffer value because of the sort order */
1251 
1252 		while (pELR < pEndOfRecords)
1253 		{
1254 			if (0 == --skip_count)
1255 				break;
1256 
1257 			pELR += ((PEVENTLOGRECORD)pELR)->Length;
1258 		}
1259 	}
1260 
1261 	if (ERROR_HANDLE_EOF == *error_code)
1262 		*error_code = ERROR_SUCCESS;
1263 
1264 	return SUCCEED;
1265 }
1266 
1267 /******************************************************************************
1268  *                                                                            *
1269  * Function: zbx_parse_eventlog_message                                       *
1270  *                                                                            *
1271  * Purpose: details parse of a single Event Log record                        *
1272  *                                                                            *
1273  * Parameters: wsource       - [IN] EventLog file name                        *
1274  *             pELR          - [IN] buffer with single Event Log Record       *
1275  *             out_source    - [OUT] the ELR detail                           *
1276  *             out_message   - [OUT] the ELR detail                           *
1277  *             out_severity  - [OUT] the ELR detail                           *
1278  *             out_timestamp - [OUT] the ELR detail                           *
1279  *             out_eventid   - [OUT] the ELR detail                           *
1280  *                                                                            *
1281  ******************************************************************************/
1282 #define MAX_INSERT_STRS 100
zbx_parse_eventlog_message(const wchar_t * wsource,const EVENTLOGRECORD * pELR,char ** out_source,char ** out_message,unsigned short * out_severity,unsigned long * out_timestamp,unsigned long * out_eventid)1283 static void	zbx_parse_eventlog_message(const wchar_t *wsource, const EVENTLOGRECORD *pELR, char **out_source,
1284 		char **out_message, unsigned short *out_severity, unsigned long *out_timestamp,
1285 		unsigned long *out_eventid)
1286 {
1287 	wchar_t 	*pEventMessageFile = NULL, *pParamMessageFile = NULL, *pFile = NULL, *pNextFile = NULL, *pCh,
1288 			*aInsertStrings[MAX_INSERT_STRS];
1289 	HINSTANCE	hLib = NULL, hParamLib = NULL;
1290 	long		i;
1291 	int		err;
1292 
1293 	memset(aInsertStrings, 0, sizeof(aInsertStrings));
1294 
1295 	*out_message = NULL;
1296 	*out_severity = pELR->EventType;				/* return event type */
1297 	*out_timestamp = pELR->TimeGenerated;				/* return timestamp */
1298 	*out_eventid = pELR->EventID & 0xffff;
1299 	*out_source = zbx_unicode_to_utf8((wchar_t *)(pELR + 1));	/* copy source name */
1300 
1301 	/* get message file names */
1302 	zbx_get_message_files(wsource, (wchar_t *)(pELR + 1), &pEventMessageFile, &pParamMessageFile);
1303 
1304 	/* prepare insert string array */
1305 	if (0 < pELR->NumStrings)
1306 	{
1307 		pCh = (wchar_t *)((unsigned char *)pELR + pELR->StringOffset);
1308 
1309 		for (i = 0; i < pELR->NumStrings && i < MAX_INSERT_STRS; i++)
1310 		{
1311 			aInsertStrings[i] = pCh;
1312 			pCh += wcslen(pCh) + 1;
1313 		}
1314 	}
1315 
1316 	err = FAIL;
1317 
1318 	for (pFile = pEventMessageFile; NULL != pFile && err != SUCCEED; pFile = pNextFile)
1319 	{
1320 		if (NULL != (pNextFile = wcschr(pFile, TEXT(';'))))
1321 		{
1322 			*pNextFile = '\0';
1323 			pNextFile++;
1324 		}
1325 
1326 		if (NULL != (hLib = zbx_load_message_file(pFile)))
1327 		{
1328 			if (NULL != (*out_message = zbx_format_message(hLib, pELR->EventID, aInsertStrings)))
1329 			{
1330 				err = SUCCEED;
1331 
1332 				if (NULL != (hParamLib = zbx_load_message_file(pParamMessageFile)))
1333 				{
1334 					zbx_translate_message_params(out_message, hParamLib);
1335 					FreeLibrary(hParamLib);
1336 				}
1337 			}
1338 
1339 			FreeLibrary(hLib);
1340 		}
1341 	}
1342 
1343 	zbx_free(pEventMessageFile);
1344 	zbx_free(pParamMessageFile);
1345 
1346 	if (SUCCEED != err)
1347 	{
1348 		*out_message = zbx_strdcatf(*out_message, "The description for Event ID:%lu in Source:'%s'"
1349 				" cannot be found. The local computer may not have the necessary registry"
1350 				" information or message DLL files to display messages from a remote computer.",
1351 				*out_eventid, NULL == *out_source ? "" : *out_source);
1352 
1353 		if (0 < pELR->NumStrings)
1354 		{
1355 			char	*buf;
1356 
1357 			*out_message = zbx_strdcat(*out_message, " The following information is part of the event: ");
1358 
1359 			for (i = 0, pCh = (wchar_t *)((unsigned char *)pELR + pELR->StringOffset);
1360 					i < pELR->NumStrings;
1361 					i++, pCh += wcslen(pCh) + 1)
1362 			{
1363 				if (0 < i)
1364 					*out_message = zbx_strdcat(*out_message, "; ");
1365 
1366 				buf = zbx_unicode_to_utf8(pCh);
1367 				*out_message = zbx_strdcat(*out_message, buf);
1368 				zbx_free(buf);
1369 			}
1370 		}
1371 	}
1372 
1373 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
1374 
1375 	return;
1376 }
1377 
1378 /******************************************************************************
1379  *                                                                            *
1380  * Function: process_eventslog                                                *
1381  *                                                                            *
1382  * Purpose:  batch processing of Event Log file                               *
1383  *                                                                            *
1384  * Parameters: server           - [IN] IP or Hostname of Zabbix server        *
1385  *             port             - [IN] port of Zabbix server                  *
1386  *             eventlog_name    - [IN] the name of the event log              *
1387  *             regexps          - [IN] set of regexp rules for Event Log test *
1388  *             pattern          - [IN] buffer for read of data of EventLog    *
1389  *             key_severity     - [IN] severity of logged data sources        *
1390  *             key_source       - [IN] name of logged data source             *
1391  *             key_logeventid   - [IN] the application-specific identifier    *
1392  *                                     for the event                          *
1393  *             rate             - [IN] threshold of records count at a time   *
1394  *             process_value_cb - [IN] callback function for sending data to  *
1395  *                                     the server                             *
1396  *             metric           - [IN/OUT] parameters for EventLog process    *
1397  *             lastlogsize_sent - [OUT] position of the last record sent to   *
1398  *                                      the server                            *
1399  *             error            - [OUT] the error message in the case of      *
1400  *                                     failure                                *
1401  *                                                                            *
1402  * Return value: SUCCEED or FAIL                                              *
1403  *                                                                            *
1404  ******************************************************************************/
process_eventslog(const char * server,unsigned short port,const char * eventlog_name,zbx_vector_ptr_t * regexps,const char * pattern,const char * key_severity,const char * key_source,const char * key_logeventid,int rate,zbx_process_value_func_t process_value_cb,ZBX_ACTIVE_METRIC * metric,zbx_uint64_t * lastlogsize_sent,char ** error)1405 int	process_eventslog(const char *server, unsigned short port, const char *eventlog_name, zbx_vector_ptr_t *regexps,
1406 		const char *pattern, const char *key_severity, const char *key_source, const char *key_logeventid,
1407 		int rate, zbx_process_value_func_t process_value_cb, ZBX_ACTIVE_METRIC *metric,
1408 		zbx_uint64_t *lastlogsize_sent, char **error)
1409 {
1410 	int		ret = FAIL;
1411 	HANDLE		eventlog_handle = NULL;
1412 	wchar_t 	*eventlog_name_w;
1413 	zbx_uint64_t	FirstID, LastID, lastlogsize;
1414 	int		buffer_size = 64 * ZBX_KIBIBYTE;
1415 	DWORD		num_bytes_read = 0, required_buf_size, ReadDirection, error_code;
1416 	BYTE		*pELRs = NULL;
1417 	int		s_count, p_count, send_err = SUCCEED, match = SUCCEED;
1418 	unsigned long	timestamp = 0;
1419 	char		*source;
1420 
1421 	lastlogsize = metric->lastlogsize;
1422 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() source:'%s' lastlogsize:" ZBX_FS_UI64, __func__, eventlog_name,
1423 			lastlogsize);
1424 
1425 	/* From MSDN documentation:                                                                         */
1426 	/* The RecordNumber member of EVENTLOGRECORD contains the record number for the event log record.   */
1427 	/* The very first record written to an event log is record number 1, and other records are          */
1428 	/* numbered sequentially. If the record number reaches ULONG_MAX, the next record number will be 0, */
1429 	/* not 1; however, you use zero to seek to the record.                                              */
1430 	/*                                                                                                  */
1431 	/* This RecordNumber wraparound is handled simply by using 64bit integer to calculate record        */
1432 	/* numbers and then converting to DWORD values.                                                     */
1433 
1434 	if (NULL == eventlog_name || '\0' == *eventlog_name)
1435 	{
1436 		*error = zbx_strdup(*error, "Cannot open eventlog with empty name.");
1437 		return ret;
1438 	}
1439 
1440 	eventlog_name_w = zbx_utf8_to_unicode(eventlog_name);
1441 
1442 	if (SUCCEED != zbx_open_eventlog(eventlog_name_w, &eventlog_handle, &FirstID, &LastID, &error_code))
1443 	{
1444 		*error = zbx_dsprintf(*error, "Cannot open eventlog '%s': %s.", eventlog_name,
1445 				strerror_from_system(error_code));
1446 		goto out;
1447 	}
1448 
1449 	if (1 == metric->skip_old_data)
1450 	{
1451 		metric->lastlogsize = LastID;
1452 		metric->skip_old_data = 0;
1453 		zabbix_log(LOG_LEVEL_DEBUG, "skipping existing data: lastlogsize:" ZBX_FS_UI64, metric->lastlogsize);
1454 		goto finish;
1455 	}
1456 
1457 	/* Having lastlogsize greater than LastID means that there was oldest event record */
1458 	/* (FirstID) wraparound. In this case we must also wrap the lastlogsize value.     */
1459 	if (lastlogsize > LastID)
1460 		lastlogsize = (DWORD)lastlogsize;
1461 
1462 	ReadDirection = ((LastID - FirstID) / 2) > lastlogsize ? EVENTLOG_FORWARDS_READ : EVENTLOG_BACKWARDS_READ;
1463 
1464 	/* if the lastlogsize is still outside log record interval reset it to the oldest record number, */
1465 	/* otherwise set FirstID to the next record after lastlogsize, which is the first event record   */
1466 	/* to read                                                                                       */
1467 	if (lastlogsize > LastID || lastlogsize < FirstID)
1468 	{
1469 		lastlogsize = FirstID;
1470 		ReadDirection = 0;
1471 	}
1472 	else
1473 		FirstID = lastlogsize + 1;
1474 
1475 	pELRs = (BYTE*)zbx_malloc((void *)pELRs, buffer_size);
1476 
1477 	if (0 == ReadDirection)		/* read eventlog from the first record */
1478 	{
1479 		error_code = ERROR_SUCCESS;
1480 	}
1481 	else if (LastID < FirstID)	/* no new records */
1482 	{
1483 		error_code = ERROR_HANDLE_EOF;
1484 	}
1485 	else if (SUCCEED != seek_eventlog(eventlog_handle, FirstID, ReadDirection, LastID, eventlog_name, &pELRs,
1486 			&buffer_size, &num_bytes_read, &error_code, error))
1487 	{
1488 		goto out;
1489 	}
1490 
1491 	zabbix_log(LOG_LEVEL_TRACE, "%s(): state before EventLog reading: num_bytes_read=%u error=%s FirstID="
1492 			ZBX_FS_UI64 " LastID=" ZBX_FS_UI64 " lastlogsize=" ZBX_FS_UI64, __func__,
1493 			(unsigned int)num_bytes_read, strerror_from_system(error_code), FirstID, LastID, lastlogsize);
1494 
1495 	if (ERROR_HANDLE_EOF == error_code)
1496 		goto finish;
1497 
1498 	s_count = 0;
1499 	p_count = 0;
1500 
1501 	/* Read blocks of records until you reach the end of the log or an           */
1502 	/* error occurs. The records are read from oldest to newest. If the buffer   */
1503 	/* is not big enough to hold a complete event record, reallocate the buffer. */
1504 	while (ERROR_SUCCESS == error_code)
1505 	{
1506 		BYTE	*pELR, *pEndOfRecords;
1507 
1508 		if (0 == num_bytes_read && 0 == ReadEventLog(eventlog_handle,
1509 				EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ, 0,
1510 				pELRs, buffer_size, &num_bytes_read, &required_buf_size))
1511 		{
1512 			if (ERROR_INSUFFICIENT_BUFFER == (error_code = GetLastError()))
1513 			{
1514 				error_code = ERROR_SUCCESS;
1515 				buffer_size = required_buf_size;
1516 				pELRs = (BYTE *)zbx_realloc((void *)pELRs, buffer_size);
1517 				continue;
1518 			}
1519 
1520 			if (ERROR_HANDLE_EOF == error_code)
1521 				break;
1522 
1523 			*error = zbx_dsprintf(*error, "Cannot read eventlog '%s': %s.", eventlog_name,
1524 					strerror_from_system(error_code));
1525 			goto out;
1526 		}
1527 
1528 		pELR = pELRs;
1529 		pEndOfRecords = pELR + num_bytes_read;
1530 
1531 		zabbix_log(LOG_LEVEL_TRACE, "%s(): state before buffer parsing: num_bytes_read = %u RecordNumber = %d"
1532 				"FirstID = "ZBX_FS_UI64" LastID = "ZBX_FS_UI64" lastlogsize="ZBX_FS_UI64,
1533 				__func__, (unsigned int)num_bytes_read, ((PEVENTLOGRECORD)pELR)->RecordNumber,
1534 				FirstID, LastID, lastlogsize);
1535 		num_bytes_read = 0;
1536 
1537 		while (pELR < pEndOfRecords)
1538 		{
1539 			/* to prevent mismatch in comparing with RecordNumber in case of wrap-around, */
1540 			/* we look for using '=' */
1541 			if (0 != timestamp || (DWORD)FirstID == ((PEVENTLOGRECORD)pELR)->RecordNumber)
1542 			{
1543 				const char	*str_severity;
1544 				unsigned short	severity;
1545 				unsigned long	logeventid;
1546 				char		*value, str_logeventid[8];
1547 
1548 				/* increase counter only for records >= FirstID (start point for the search) */
1549 				/* to avoid wrap-around of the 32b RecordNumber we increase the 64b lastlogsize */
1550 				if (0 == timestamp)
1551 					lastlogsize = FirstID;
1552 				else
1553 					lastlogsize += 1;
1554 
1555 				zbx_parse_eventlog_message(eventlog_name_w, (EVENTLOGRECORD *)pELR, &source, &value,
1556 						&severity, &timestamp, &logeventid);
1557 
1558 				switch (severity)
1559 				{
1560 					case EVENTLOG_SUCCESS:
1561 					case EVENTLOG_INFORMATION_TYPE:
1562 						severity = ITEM_LOGTYPE_INFORMATION;
1563 						str_severity = INFORMATION_TYPE;
1564 						break;
1565 					case EVENTLOG_WARNING_TYPE:
1566 						severity = ITEM_LOGTYPE_WARNING;
1567 						str_severity = WARNING_TYPE;
1568 						break;
1569 					case EVENTLOG_ERROR_TYPE:
1570 						severity = ITEM_LOGTYPE_ERROR;
1571 						str_severity = ERROR_TYPE;
1572 						break;
1573 					case EVENTLOG_AUDIT_FAILURE:
1574 						severity = ITEM_LOGTYPE_FAILURE_AUDIT;
1575 						str_severity = AUDIT_FAILURE;
1576 						break;
1577 					case EVENTLOG_AUDIT_SUCCESS:
1578 						severity = ITEM_LOGTYPE_SUCCESS_AUDIT;
1579 						str_severity = AUDIT_SUCCESS;
1580 						break;
1581 				}
1582 
1583 				zbx_snprintf(str_logeventid, sizeof(str_logeventid), "%lu", logeventid);
1584 
1585 				if (0 == p_count)
1586 				{
1587 					int	ret1, ret2, ret3, ret4;
1588 
1589 					if (FAIL == (ret1 = regexp_match_ex(regexps, value, pattern,
1590 							ZBX_CASE_SENSITIVE)))
1591 					{
1592 						*error = zbx_strdup(*error,
1593 								"Invalid regular expression in the second parameter.");
1594 						match = FAIL;
1595 					}
1596 					else if (FAIL == (ret2 = regexp_match_ex(regexps, str_severity, key_severity,
1597 							ZBX_IGNORE_CASE)))
1598 					{
1599 						*error = zbx_strdup(*error,
1600 								"Invalid regular expression in the third parameter.");
1601 						match = FAIL;
1602 					}
1603 					else if (FAIL == (ret3 = regexp_match_ex(regexps, source, key_source,
1604 							ZBX_IGNORE_CASE)))
1605 					{
1606 						*error = zbx_strdup(*error,
1607 								"Invalid regular expression in the fourth parameter.");
1608 						match = FAIL;
1609 					}
1610 					else if (FAIL == (ret4 = regexp_match_ex(regexps, str_logeventid,
1611 							key_logeventid, ZBX_CASE_SENSITIVE)))
1612 					{
1613 						*error = zbx_strdup(*error,
1614 								"Invalid regular expression in the fifth parameter.");
1615 						match = FAIL;
1616 					}
1617 
1618 					if (FAIL == match)
1619 					{
1620 						zbx_free(source);
1621 						zbx_free(value);
1622 
1623 						ret = FAIL;
1624 						break;
1625 					}
1626 
1627 					match = ZBX_REGEXP_MATCH == ret1 && ZBX_REGEXP_MATCH == ret2 &&
1628 							ZBX_REGEXP_MATCH == ret3 && ZBX_REGEXP_MATCH == ret4;
1629 				}
1630 				else
1631 				{
1632 					match = ZBX_REGEXP_MATCH == regexp_match_ex(regexps, value, pattern,
1633 								ZBX_CASE_SENSITIVE) &&
1634 							ZBX_REGEXP_MATCH == regexp_match_ex(regexps, str_severity,
1635 								key_severity, ZBX_IGNORE_CASE) &&
1636 							ZBX_REGEXP_MATCH == regexp_match_ex(regexps, source,
1637 								key_source, ZBX_IGNORE_CASE) &&
1638 							ZBX_REGEXP_MATCH == regexp_match_ex(regexps, str_logeventid,
1639 								key_logeventid, ZBX_CASE_SENSITIVE);
1640 				}
1641 
1642 				if (1 == match)
1643 				{
1644 					send_err = process_value_cb(server, port, CONFIG_HOSTNAME, metric->key_orig,
1645 							value, ITEM_STATE_NORMAL, &lastlogsize, NULL, &timestamp,
1646 							source, &severity, &logeventid,
1647 							metric->flags | ZBX_METRIC_FLAG_PERSISTENT);
1648 
1649 					if (SUCCEED == send_err)
1650 					{
1651 						*lastlogsize_sent = lastlogsize;
1652 						s_count++;
1653 					}
1654 				}
1655 				p_count++;
1656 
1657 				zbx_free(source);
1658 				zbx_free(value);
1659 
1660 				if (SUCCEED == send_err)
1661 				{
1662 					metric->lastlogsize = lastlogsize;
1663 				}
1664 				else
1665 				{
1666 					/* buffer is full, stop processing active checks */
1667 					/* till the buffer is cleared */
1668 					break;
1669 				}
1670 
1671 				/* do not flood Zabbix server if file grows too fast */
1672 				if (s_count >= (rate * metric->refresh))
1673 					break;
1674 
1675 				/* do not flood local system if file grows too fast */
1676 				if (p_count >= (4 * rate * metric->refresh))
1677 					break;
1678 			}
1679 
1680 			pELR += ((PEVENTLOGRECORD)pELR)->Length;
1681 		}
1682 
1683 		if (pELR < pEndOfRecords)
1684 			error_code = ERROR_NO_MORE_ITEMS;
1685 	}
1686 
1687 finish:
1688 	ret = SUCCEED;
1689 out:
1690 	zbx_close_eventlog(eventlog_handle);
1691 	zbx_free(eventlog_name_w);
1692 	zbx_free(pELRs);
1693 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
1694 
1695 	return ret;
1696 }
1697 
process_eventlog_check(char * server,unsigned short port,zbx_vector_ptr_t * regexps,ZBX_ACTIVE_METRIC * metric,zbx_process_value_func_t process_value_cb,zbx_uint64_t * lastlogsize_sent,char ** error)1698 int	process_eventlog_check(char *server, unsigned short port, zbx_vector_ptr_t *regexps, ZBX_ACTIVE_METRIC *metric,
1699 		zbx_process_value_func_t process_value_cb, zbx_uint64_t *lastlogsize_sent, char **error)
1700 {
1701 	int 		ret = FAIL;
1702 	AGENT_REQUEST	request;
1703 	const char	*filename, *pattern, *maxlines_persec, *key_severity, *key_source, *key_logeventid, *skip;
1704 	int		rate;
1705 	OSVERSIONINFO	versionInfo;
1706 
1707 	init_request(&request);
1708 
1709 	if (SUCCEED != parse_item_key(metric->key, &request))
1710 	{
1711 		*error = zbx_strdup(*error, "Invalid item key format.");
1712 		goto out;
1713 	}
1714 
1715 	if (0 == get_rparams_num(&request))
1716 	{
1717 		*error = zbx_strdup(*error, "Invalid number of parameters.");
1718 		goto out;
1719 	}
1720 
1721 	if (7 < get_rparams_num(&request))
1722 	{
1723 		*error = zbx_strdup(*error, "Too many parameters.");
1724 		goto out;
1725 	}
1726 
1727 	if (NULL == (filename = get_rparam(&request, 0)) || '\0' == *filename)
1728 	{
1729 		*error = zbx_strdup(*error, "Invalid first parameter.");
1730 		goto out;
1731 	}
1732 
1733 	if (NULL == (pattern = get_rparam(&request, 1)))
1734 	{
1735 		pattern = "";
1736 	}
1737 	else if ('@' == *pattern && SUCCEED != zbx_global_regexp_exists(pattern + 1, regexps))
1738 	{
1739 		*error = zbx_dsprintf(*error, "Global regular expression \"%s\" does not exist.", pattern + 1);
1740 		goto out;
1741 	}
1742 
1743 	if (NULL == (key_severity = get_rparam(&request, 2)))
1744 	{
1745 		key_severity = "";
1746 	}
1747 	else if ('@' == *key_severity && SUCCEED != zbx_global_regexp_exists(key_severity + 1, regexps))
1748 	{
1749 		*error = zbx_dsprintf(*error, "Global regular expression \"%s\" does not exist.", key_severity + 1);
1750 		goto out;
1751 	}
1752 
1753 	if (NULL == (key_source = get_rparam(&request, 3)))
1754 	{
1755 		key_source = "";
1756 	}
1757 	else if ('@' == *key_source && SUCCEED != zbx_global_regexp_exists(key_source + 1, regexps))
1758 	{
1759 		*error = zbx_dsprintf(*error, "Global regular expression \"%s\" does not exist.", key_source + 1);
1760 		goto out;
1761 	}
1762 
1763 	if (NULL == (key_logeventid = get_rparam(&request, 4)))
1764 	{
1765 		key_logeventid = "";
1766 	}
1767 	else if ('@' == *key_logeventid && SUCCEED != zbx_global_regexp_exists(key_logeventid + 1, regexps))
1768 	{
1769 		*error = zbx_dsprintf(*error, "Global regular expression \"%s\" does not exist.", key_logeventid + 1);
1770 		goto out;
1771 	}
1772 
1773 	if (NULL == (maxlines_persec = get_rparam(&request, 5)) || '\0' == *maxlines_persec)
1774 	{
1775 		rate = CONFIG_EVENTLOG_MAX_LINES_PER_SECOND;
1776 	}
1777 	else if (MIN_VALUE_LINES > (rate = atoi(maxlines_persec)) || MAX_VALUE_LINES < rate)
1778 	{
1779 		*error = zbx_strdup(*error, "Invalid sixth parameter.");
1780 		goto out;
1781 	}
1782 
1783 	if (NULL == (skip = get_rparam(&request, 6)) || '\0' == *skip || 0 == strcmp(skip, "all"))
1784 	{
1785 		metric->skip_old_data = 0;
1786 	}
1787 	else if (0 != strcmp(skip, "skip"))
1788 	{
1789 		*error = zbx_strdup(*error, "Invalid seventh parameter.");
1790 		goto out;
1791 	}
1792 
1793 	versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1794 	GetVersionEx(&versionInfo);
1795 
1796 	if (versionInfo.dwMajorVersion >= 6)	/* Windows Vista, 7 or Server 2008 */
1797 	{
1798 		__try
1799 		{
1800 
1801 			zbx_uint64_t	lastlogsize = metric->lastlogsize;
1802 			EVT_HANDLE	eventlog6_render_context = NULL;
1803 			EVT_HANDLE	eventlog6_query = NULL;
1804 			zbx_uint64_t	eventlog6_firstid = 0;
1805 			zbx_uint64_t	eventlog6_lastid = 0;
1806 
1807 			if (SUCCEED != initialize_eventlog6(filename, &lastlogsize, &eventlog6_firstid,
1808 					&eventlog6_lastid, &eventlog6_render_context, &eventlog6_query, error))
1809 			{
1810 				finalize_eventlog6(&eventlog6_render_context, &eventlog6_query);
1811 				goto out;
1812 			}
1813 
1814 			ret = process_eventslog6(server, port, filename, &eventlog6_render_context, &eventlog6_query,
1815 					lastlogsize, eventlog6_firstid, eventlog6_lastid, regexps, pattern,
1816 					key_severity, key_source, key_logeventid, rate, process_value_cb, metric,
1817 					lastlogsize_sent, error);
1818 
1819 			finalize_eventlog6(&eventlog6_render_context, &eventlog6_query);
1820 		}
1821 		__except (DelayLoadDllExceptionFilter(GetExceptionInformation()))
1822 		{
1823 			zabbix_log(LOG_LEVEL_WARNING, "failed to process eventlog");
1824 		}
1825 	}
1826 	else if (versionInfo.dwMajorVersion < 6)    /* Windows versions before Vista */
1827 	{
1828 		ret = process_eventslog(server, port, filename, regexps, pattern, key_severity, key_source,
1829 				key_logeventid, rate, process_value_cb, metric, lastlogsize_sent, error);
1830 	}
1831 out:
1832 	free_request(&request);
1833 
1834 	return ret;
1835 }
1836