From 1bbb880fd969f80d4bbd23174edc4069b10e2acc Mon Sep 17 00:00:00 2001 From: Paul Vriens Date: Fri, 13 Nov 2009 13:24:11 +0100 Subject: [PATCH] advapi32/tests: Add tests for reading and writing to a real eventlog. --- dlls/advapi32/tests/eventlog.c | 423 +++++++++++++++++++++++++++++++++ 1 file changed, 423 insertions(+) diff --git a/dlls/advapi32/tests/eventlog.c b/dlls/advapi32/tests/eventlog.c index e5b03702567..c989d4a3c8b 100644 --- a/dlls/advapi32/tests/eventlog.c +++ b/dlls/advapi32/tests/eventlog.c @@ -24,15 +24,19 @@ #include "winbase.h" #include "winerror.h" #include "winnt.h" +#include "winreg.h" +#include "sddl.h" #include "wine/test.h" +static BOOL (WINAPI *pCreateWellKnownSid)(WELL_KNOWN_SID_TYPE,PSID,PSID,DWORD*); static BOOL (WINAPI *pGetEventLogInformation)(HANDLE,DWORD,LPVOID,DWORD,LPDWORD); static void init_function_pointers(void) { HMODULE hadvapi32 = GetModuleHandleA("advapi32.dll"); + pCreateWellKnownSid = (void*)GetProcAddress(hadvapi32, "CreateWellKnownSid"); pGetEventLogInformation = (void*)GetProcAddress(hadvapi32, "GetEventLogInformation"); } @@ -611,6 +615,417 @@ static void test_clear(void) ok(DeleteFileA(backup), "Could not delete the backup file\n"); } +static const char eventlogsvc[] = "SYSTEM\\CurrentControlSet\\Services\\Eventlog"; +static const char eventlogname[] = "Wine"; +static const char eventsources[][11] = { "WineSrc", "WineSrc1", "WineSrc20", "WineSrc300" }; + +static BOOL create_new_eventlog(void) +{ + HKEY key, eventkey; + BOOL bret = FALSE; + LONG lret; + int i; + + /* First create our eventlog */ + lret = RegOpenKeyA(HKEY_LOCAL_MACHINE, eventlogsvc, &key); + /* FIXME: Wine stops here */ + if (lret != ERROR_SUCCESS) + { + skip("Could not open the EventLog service registry key\n"); + return FALSE; + } + lret = RegCreateKeyA(key, eventlogname, &eventkey); + if (lret != ERROR_SUCCESS) + { + skip("Could not create the eventlog '%s' registry key\n", eventlogname); + goto cleanup; + } + + /* Create some event sources, the registry value 'Sources' is updated automatically */ + for (i = 0; i < sizeof(eventsources)/sizeof(eventsources[0]); i++) + { + HKEY srckey; + + lret = RegCreateKeyA(eventkey, eventsources[i], &srckey); + if (lret != ERROR_SUCCESS) + { + skip("Could not create the eventsource '%s' registry key\n", eventsources[i]); + goto cleanup; + } + RegFlushKey(srckey); + RegCloseKey(srckey); + } + + bret = TRUE; + + /* The flushing of the registry (here and above) gives us some assurance + * that we are not to quickly writing events as 'Sources' could still be + * not updated. + */ + RegFlushKey(eventkey); +cleanup: + RegCloseKey(eventkey); + RegCloseKey(key); + + return bret; +} + +static const char *one_string[] = { "First string" }; +static const char *two_strings[] = { "First string", "Second string" }; +static const struct +{ + const char *evt_src; + WORD evt_type; + WORD evt_cat; + DWORD evt_id; + BOOL evt_sid; + WORD evt_numstrings; + const char **evt_strings; +} read_write [] = +{ + { eventlogname, EVENTLOG_INFORMATION_TYPE, 1, 1, FALSE, 1, one_string }, + { eventsources[0], EVENTLOG_WARNING_TYPE, 1, 2, FALSE, 0, NULL }, + { eventsources[1], EVENTLOG_AUDIT_FAILURE, 1, 3, FALSE, 2, two_strings }, + { eventsources[2], EVENTLOG_ERROR_TYPE, 1, 4, FALSE, 0, NULL }, + { eventsources[3], EVENTLOG_WARNING_TYPE, 1, 5, FALSE, 1, one_string }, + { eventlogname, EVENTLOG_SUCCESS, 2, 6, TRUE, 2, two_strings }, + { eventsources[0], EVENTLOG_AUDIT_FAILURE, 2, 7, TRUE, 0, NULL }, + { eventsources[1], EVENTLOG_AUDIT_SUCCESS, 2, 8, TRUE, 2, two_strings }, + { eventsources[2], EVENTLOG_WARNING_TYPE, 2, 9, TRUE, 0, NULL }, + { eventsources[3], EVENTLOG_ERROR_TYPE, 2, 10, TRUE, 1, one_string } +}; + +static void test_readwrite(void) +{ + HANDLE handle; + PSID user; + DWORD sidsize, count; + BOOL ret, sidavailable; + BOOL on_vista = FALSE; /* Used to indicate Vista or higher */ + int i; + char localcomputer[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD len = sizeof(localcomputer); + + if (pCreateWellKnownSid) + { + sidsize = SECURITY_MAX_SID_SIZE; + user = HeapAlloc(GetProcessHeap(), 0, sidsize); + SetLastError(0xdeadbeef); + pCreateWellKnownSid(WinInteractiveSid, NULL, user, &sidsize); + sidavailable = TRUE; + } + else + { + win_skip("Skipping some SID related tests\n"); + sidavailable = FALSE; + user = NULL; + } + + GetComputerNameA(localcomputer, &len); + + /* Write an event with an incorrect event type. This will fail on Windows 7 + * but succeed on all others, hence it's not part of the struct. + */ + handle = OpenEventLogA(NULL, eventlogname); + + SetLastError(0xdeadbeef); + ret = ReportEvent(handle, 0x20, 0, 0, NULL, 0, 0, NULL, NULL); + if (!ret && GetLastError() == ERROR_CRC) + { + win_skip("Win7 fails when using incorrect event types\n"); + ret = ReportEvent(handle, 0, 0, 0, NULL, 0, 0, NULL, NULL); + } + ok(ret, "Expected success : %d\n", GetLastError()); + + /* This will clear the eventlog. The record numbering for new + * events however differs on Vista+. Before Vista the first + * event would be numbered 1, on Vista+ it's now 2 as we already + * had one event. + */ + ClearEventLogA(handle, NULL); + CloseEventLog(handle); + + /* Write a bunch of events while using different event sources */ + for (i = 0; i < sizeof(read_write)/sizeof(read_write[0]); i++) + { + DWORD oldest; + BOOL run_sidtests = read_write[i].evt_sid & sidavailable; + + /* We don't need to use RegisterEventSource to report events */ + if (i % 2) + handle = OpenEventLogA(NULL, read_write[i].evt_src); + else + handle = RegisterEventSourceA(NULL, read_write[i].evt_src); + ok(handle != NULL, "Expected a handle\n"); + + SetLastError(0xdeadbeef); + ret = ReportEvent(handle, read_write[i].evt_type, read_write[i].evt_cat, + read_write[i].evt_id, run_sidtests ? user : NULL, + read_write[i].evt_numstrings, 0, read_write[i].evt_strings, NULL); + + count = 0xdeadbeef; + ret = GetNumberOfEventLogRecords(handle, &count); + ok(ret, "Expected success\n"); + ok(count == (i + 1), "Expected %d records, got %d\n", i + 1, count); + + oldest = 0xdeadbeef; + ret = GetOldestEventLogRecord(handle, &oldest); + ok(ret, "Expected success\n"); + ok(oldest == 1 || + oldest == 2, /* Vista+ */ + "Expected oldest to be 1 or 2, got %d\n", oldest); + if (oldest == 2) + on_vista = TRUE; + + if (i % 2) + ret = CloseEventLog(handle); + else + ret = DeregisterEventSource(handle); + ok(ret, "Expected success : %d\n", GetLastError()); + } + + handle = OpenEventLogA(NULL, eventlogname); + count = 0xdeadbeef; + ret = GetNumberOfEventLogRecords(handle, &count); + ok(ret, "Expected success\n"); + ok(count == i, "Expected %d records, got %d\n", i, count); + CloseEventLog(handle); + + if (count == 0) + { + skip("No events were written to the eventlog\n"); + return; + } + + /* Report only once */ + if (on_vista) + skip("There is no DWORD alignment for UserSid on Vista or higher\n"); + + /* Read all events from our created eventlog, one by one */ + handle = OpenEventLogA(NULL, eventlogname); + i = 0; + for (;;) + { + void *buf; + DWORD read, needed; + EVENTLOGRECORD *record; + char *sourcename, *computername; + int k; + char *ptr; + BOOL run_sidtests = read_write[i].evt_sid & sidavailable; + + buf = HeapAlloc(GetProcessHeap(), 0, sizeof(EVENTLOGRECORD)); + SetLastError(0xdeadbeef); + ret = ReadEventLogA(handle, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ, + 0, buf, sizeof(EVENTLOGRECORD), &read, &needed); + if (!ret && GetLastError() == ERROR_HANDLE_EOF) + { + HeapFree(GetProcessHeap(), 0, buf); + break; + } + ok(!ret, "Expected failure\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Expected ERROR_INVALID_PARAMETER, got %d\n",GetLastError()); + + buf = HeapReAlloc(GetProcessHeap(), 0, buf, needed); + ret = ReadEventLogA(handle, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ, + 0, buf, needed, &read, &needed); + ok(ret, "Expected success: %d\n", GetLastError()); + + record = (EVENTLOGRECORD *)buf; + + ok(record->Length == read, + "Expected %d, got %d\n", read, record->Length); + ok(record->Reserved == 0x654c664c, + "Expected 0x654c664c, got %d\n", record->Reserved); + ok(record->RecordNumber == i + 1 || + (on_vista && (record->RecordNumber == i + 2)), + "Expected %d or %d, got %d\n", i + 1, i + 2, record->RecordNumber); + ok(record->EventID == read_write[i].evt_id, + "Expected %d, got %d\n", read_write[i].evt_id, record->EventID); + ok(record->EventType == read_write[i].evt_type, + "Expected %d, got %d\n", read_write[i].evt_type, record->EventType); + ok(record->NumStrings == read_write[i].evt_numstrings, + "Expected %d, got %d\n", read_write[i].evt_numstrings, record->NumStrings); + ok(record->EventCategory == read_write[i].evt_cat, + "Expected %d, got %d\n", read_write[i].evt_cat, record->EventCategory); + + sourcename = (char *)((BYTE *)buf + sizeof(EVENTLOGRECORD)); + ok(!lstrcmpA(sourcename, read_write[i].evt_src), "Expected '%s', got '%s'\n", + read_write[i].evt_src, sourcename); + + computername = (char *)((BYTE *)buf + sizeof(EVENTLOGRECORD) + lstrlenA(sourcename) + 1); + ok(!lstrcmpiA(computername, localcomputer), "Expected '%s', got '%s'\n", + localcomputer, computername); + + /* Before Vista, UserSid was aligned on a DWORD boundary. Next to that if + * no padding was actually required a 0 DWORD was still used for padding. No + * application should be relying on the padding as we are working with offsets + * anyway. + */ + + if (!on_vista) + { + DWORD calculated_sidoffset = sizeof(EVENTLOGRECORD) + lstrlenA(sourcename) + 1 + lstrlenA(computername) + 1; + + /* We are already DWORD aligned, there should still be some padding */ + if ((((UINT_PTR)buf + calculated_sidoffset) % sizeof(DWORD)) == 0) + ok(*(DWORD_PTR *)((BYTE *)buf + calculated_sidoffset) == 0, "Expected 0\n"); + + ok((((UINT_PTR)buf + record->UserSidOffset) % sizeof(DWORD)) == 0, "Expected DWORD alignment\n"); + } + + if (run_sidtests) + { + ok(record->UserSidLength == sidsize, "Expected %d, got %d\n", sidsize, record->UserSidLength); + } + else + { + ok(record->StringOffset == record->UserSidOffset, "Expected offsets to be the same\n"); + ok(record->UserSidLength == 0, "Expected 0, got %d\n", record->UserSidLength); + } + + ok(record->DataLength == 0, "Expected 0, got %d\n", record->DataLength); + + ptr = (char *)((BYTE *)buf + record->StringOffset); + for (k = 0; k < record->NumStrings; k++) + { + ok(!lstrcmpA(ptr, two_strings[k]), "Expected '%s', got '%s'\n", two_strings[k], ptr); + ptr += lstrlenA(ptr) + 1; + } + + ok(record->Length == *(DWORD_PTR *)((BYTE *)buf + record->Length - sizeof(DWORD)), + "Expected the closing DWORD to contain the length of the record\n"); + + HeapFree(GetProcessHeap(), 0, buf); + i++; + } + CloseEventLog(handle); + + HeapFree(GetProcessHeap(), 0, user); + + /* Test clearing a real eventlog */ + handle = OpenEventLogA(NULL, eventlogname); + + SetLastError(0xdeadbeef); + ret = ClearEventLogA(handle, NULL); + ok(ret, "Expected success\n"); + + count = 0xdeadbeef; + ret = GetNumberOfEventLogRecords(handle, &count); + ok(ret, "Expected success\n"); + ok(count == 0, "Expected an empty eventlog, got %d records\n", count); + + CloseEventLog(handle); +} + +/* Before Vista: + * + * Creating an eventlog on Windows (via the registry) automatically leads + * to creation of a REG_MULTI_SZ named 'Sources'. This value lists all the + * potential event sources for this eventlog. 'Sources' is automatically + * updated when a new key (aka event source) is created. + * + * Although the updating of registry keys is almost instantaneously, we + * check it after some other tests to assure we are not querying the + * registry or file system to quickly. + * + * NT4 and higher: + * + * The eventlog file itself is also automatically created, even before we + * start writing events. + */ +static char eventlogfile[MAX_PATH]; +static void test_autocreation(void) +{ + HKEY key, eventkey; + DWORD type, size; + LONG ret; + int i; + char *p; + char sources[sizeof(eventsources)]; + char sysdir[MAX_PATH]; + + RegOpenKeyA(HKEY_LOCAL_MACHINE, eventlogsvc, &key); + RegOpenKeyA(key, eventlogname, &eventkey); + + size = sizeof(sources); + sources[0] = 0; + ret = RegQueryValueExA(eventkey, "Sources", NULL, &type, (LPBYTE)sources, &size); + if (ret == ERROR_SUCCESS) + { + char sources_verify[sizeof(eventsources)]; + + ok(type == REG_MULTI_SZ, "Expected a REG_MULTI_SZ, got %d\n", type); + + /* Build the expected string */ + memset(sources_verify, 0, sizeof(sources_verify)); + p = sources_verify; + for (i = sizeof(eventsources)/sizeof(eventsources[0]); i > 0; i--) + { + lstrcpyA(p, eventsources[i - 1]); + p += (lstrlenA(eventsources[i - 1]) + 1); + } + lstrcpyA(p, eventlogname); + + ok(!memcmp(sources, sources_verify, size), "Expected a correct 'Sources' value\n"); + } + + RegCloseKey(eventkey); + RegCloseKey(key); + + /* On Windows we also automatically get an eventlog file */ + GetSystemDirectoryA(sysdir, sizeof(sysdir)); + + /* NT4 - W2K3 */ + lstrcpyA(eventlogfile, sysdir); + lstrcatA(eventlogfile, "\\config\\"); + lstrcatA(eventlogfile, eventlogname); + lstrcatA(eventlogfile, ".evt"); + + if (GetFileAttributesA(eventlogfile) == INVALID_FILE_ATTRIBUTES) + { + /* Vista+ */ + lstrcpyA(eventlogfile, sysdir); + lstrcatA(eventlogfile, "\\winevt\\Logs\\"); + lstrcatA(eventlogfile, eventlogname); + lstrcatA(eventlogfile, ".evtx"); + } + + ok(GetFileAttributesA(eventlogfile) != INVALID_FILE_ATTRIBUTES, + "Expected an eventlog file\n"); +} + +static void cleanup_eventlog(void) +{ + BOOL bret; + LONG lret; + HKEY key; + int i; + char winesvc[MAX_PATH]; + + /* Delete the registry tree */ + lstrcpyA(winesvc, eventlogsvc); + lstrcatA(winesvc, "\\"); + lstrcatA(winesvc, eventlogname); + + RegOpenKeyA(HKEY_LOCAL_MACHINE, winesvc, &key); + for (i = 0; i < sizeof(eventsources)/sizeof(eventsources[0]); i++) + RegDeleteKeyA(key, eventsources[i]); + RegDeleteValueA(key, "Sources"); + RegCloseKey(key); + lret = RegDeleteKeyA(HKEY_LOCAL_MACHINE, winesvc); + todo_wine + ok(lret == ERROR_SUCCESS, "Could not delete the registry tree : %d\n", lret); + + /* A handle to the eventlog is locked by services.exe. We can only + * delete the eventlog file after reboot. + */ + bret = MoveFileExA(eventlogfile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); + todo_wine + ok(bret, "Expected MoveFileEx to succeed: %d\n", GetLastError()); +} + START_TEST(eventlog) { SetLastError(0xdeadbeef); @@ -632,4 +1047,12 @@ START_TEST(eventlog) test_openbackup(); test_read(); test_clear(); + + /* Functional tests */ + if (create_new_eventlog()) + { + test_readwrite(); + test_autocreation(); + } + cleanup_eventlog(); }