[advapi32_winetest]
authorChristoph von Wittich <christoph_vw@reactos.org>
Wed, 23 Dec 2009 15:15:54 +0000 (15:15 +0000)
committerChristoph von Wittich <christoph_vw@reactos.org>
Wed, 23 Dec 2009 15:15:54 +0000 (15:15 +0000)
sync advapi32_winetest with wine 1.1.35

svn path=/trunk/; revision=44736

rostests/winetests/advapi32/crypt.c
rostests/winetests/advapi32/eventlog.c [new file with mode: 0644]
rostests/winetests/advapi32/registry.c
rostests/winetests/advapi32/security.c
rostests/winetests/advapi32/service.c

index 4ce8907..f317237 100644 (file)
@@ -234,6 +234,7 @@ static void test_incorrect_api_usage(void)
     result = pCryptCreateHash(hProv, CALG_SHA, 0, 0, &hHash);
     ok (result, "%d\n", GetLastError());
     if (!result) return;
+    pCryptDestroyHash(hHash);
 
     result = pCryptGenKey(hProv, CALG_RC4, 0, &hKey);
     ok (result, "%d\n", GetLastError());
@@ -557,6 +558,7 @@ static void test_enum_providers(void)
            ok(!strcmp(pszProvName, provider), "expected %s, got %s\n", pszProvName, provider);
        ok(cbName==providerLen, "expected %d, got %d\n", cbName, providerLen);
 
+       LocalFree(pszProvName);
        LocalFree(provider);
 }
 
@@ -844,6 +846,7 @@ static void test_get_default_provider(void)
            ok(!strcmp(pszProvName, provName), "expected %s, got %s\n", pszProvName, provName);
        ok(provNameSize==cbProvName, "expected %d, got %d\n", cbProvName, provNameSize);
 
+       LocalFree(pszProvName);
        LocalFree(provName);
 }
 
@@ -933,17 +936,156 @@ static void test_machine_guid(void)
    RegCloseKey(key);
 }
 
+#define key_length 16
+
+static const unsigned char key[key_length] =
+    { 0xbf, 0xf6, 0x83, 0x4b, 0x3e, 0xa3, 0x23, 0xdd,
+      0x96, 0x78, 0x70, 0x8e, 0xa1, 0x9d, 0x3b, 0x40 };
+
+static void test_rc2_keylen(void)
+{
+    struct KeyBlob
+    {
+        BLOBHEADER header;
+        DWORD key_size;
+        BYTE key_data[2048];
+    } key_blob;
+
+    HCRYPTPROV provider;
+    HCRYPTKEY hkey = 0;
+    BOOL ret;
+
+    SetLastError(0xdeadbeef);
+    ret = pCryptAcquireContextA(&provider, NULL, NULL,
+                                PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
+    ok(ret, "CryptAcquireContext error %u\n", GetLastError());
+    if (ret)
+    {
+        key_blob.header.bType = PLAINTEXTKEYBLOB;
+        key_blob.header.bVersion = CUR_BLOB_VERSION;
+        key_blob.header.reserved = 0;
+        key_blob.header.aiKeyAlg = CALG_RC2;
+        key_blob.key_size = sizeof(key);
+        memcpy(key_blob.key_data, key, key_length);
+
+        /* Importing a 16-byte key works with the default provider. */
+        SetLastError(0xdeadbeef);
+        ret = pCryptImportKey(provider, (BYTE*)&key_blob,
+                          sizeof(BLOBHEADER)+sizeof(DWORD)+key_blob.key_size,
+                          0, CRYPT_IPSEC_HMAC_KEY, &hkey);
+        /* CRYPT_IPSEC_HMAC_KEY is not supported on W2K and lower */
+        todo_wine
+        ok(ret ||
+           broken(!ret && GetLastError() == NTE_BAD_FLAGS),
+           "CryptImportKey error %08x\n", GetLastError());
+
+        if (ret)
+            pCryptDestroyKey(hkey);
+        pCryptReleaseContext(provider, 0);
+    }
+
+    SetLastError(0xdeadbeef);
+    ret = pCryptAcquireContextA(&provider, NULL, MS_DEF_PROV,
+                                PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
+    ok(ret, "CryptAcquireContext error %08x\n", GetLastError());
+
+    if (ret)
+    {
+        /* Importing a 16-byte key doesn't work with the base provider.. */
+        SetLastError(0xdeadbeef);
+        ret = pCryptImportKey(provider, (BYTE*)&key_blob,
+                              sizeof(BLOBHEADER)+sizeof(DWORD)+key_blob.key_size,
+                              0, 0, &hkey);
+        ok(!ret && (GetLastError() == NTE_BAD_DATA ||
+                    GetLastError() == NTE_BAD_LEN || /* Win7 */
+                    GetLastError() == NTE_BAD_TYPE || /* W2K */
+                    GetLastError() == NTE_PERM), /* Win9x, WinMe and NT4 */
+           "unexpected error %08x\n", GetLastError());
+        /* but importing an 56-bit (7-byte) key does.. */
+        key_blob.key_size = 7;
+        SetLastError(0xdeadbeef);
+        ret = pCryptImportKey(provider, (BYTE*)&key_blob,
+                              sizeof(BLOBHEADER)+sizeof(DWORD)+key_blob.key_size,
+                              0, 0, &hkey);
+        ok(ret ||
+           broken(!ret && GetLastError() == NTE_BAD_TYPE) || /* W2K */
+           broken(!ret && GetLastError() == NTE_PERM), /* Win9x, WinMe and NT4 */
+           "CryptAcquireContext error %08x\n", GetLastError());
+        if (ret)
+            pCryptDestroyKey(hkey);
+        /* as does importing a 16-byte key with the base provider when
+         * CRYPT_IPSEC_HMAC_KEY is specified.
+         */
+        key_blob.key_size = sizeof(key);
+        SetLastError(0xdeadbeef);
+        ret = pCryptImportKey(provider, (BYTE*)&key_blob,
+                              sizeof(BLOBHEADER)+sizeof(DWORD)+key_blob.key_size,
+                              0, CRYPT_IPSEC_HMAC_KEY, &hkey);
+        /* CRYPT_IPSEC_HMAC_KEY is not supported on W2K and lower */
+        todo_wine
+        ok(ret ||
+           broken(!ret && GetLastError() == NTE_BAD_FLAGS),
+           "CryptImportKey error %08x\n", GetLastError());
+        if (ret)
+            pCryptDestroyKey(hkey);
+
+        pCryptReleaseContext(provider, 0);
+    }
+
+    key_blob.key_size = sizeof(key);
+    SetLastError(0xdeadbeef);
+    ret = pCryptAcquireContextA(&provider, NULL, NULL,
+                                PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
+    ok(ret, "CryptAcquireContext error %08x\n", GetLastError());
+
+    if (ret)
+    {
+        /* Importing a 16-byte key also works with the default provider when
+         * CRYPT_IPSEC_HMAC_KEY is specified.
+         */
+        SetLastError(0xdeadbeef);
+        ret = pCryptImportKey(provider, (BYTE*)&key_blob,
+                              sizeof(BLOBHEADER)+sizeof(DWORD)+key_blob.key_size,
+                              0, CRYPT_IPSEC_HMAC_KEY, &hkey);
+        todo_wine
+        ok(ret ||
+           broken(!ret && GetLastError() == NTE_BAD_FLAGS),
+           "CryptImportKey error %08x\n", GetLastError());
+        if (ret)
+            pCryptDestroyKey(hkey);
+
+        /* There is no apparent limit to the size of the input key when
+         * CRYPT_IPSEC_HMAC_KEY is specified.
+         */
+        key_blob.key_size = sizeof(key_blob.key_data);
+        SetLastError(0xdeadbeef);
+        ret = pCryptImportKey(provider, (BYTE*)&key_blob,
+                              sizeof(BLOBHEADER)+sizeof(DWORD)+key_blob.key_size,
+                              0, CRYPT_IPSEC_HMAC_KEY, &hkey);
+        todo_wine
+        ok(ret ||
+           broken(!ret && GetLastError() == NTE_BAD_FLAGS),
+           "CryptImportKey error %08x\n", GetLastError());
+        if (ret)
+            pCryptDestroyKey(hkey);
+
+        pCryptReleaseContext(provider, 0);
+    }
+}
+
 START_TEST(crypt)
 {
-       init_function_pointers();
-       if(pCryptAcquireContextA && pCryptReleaseContext) {
+    init_function_pointers();
+    if (pCryptAcquireContextA && pCryptReleaseContext)
+    {
+       test_rc2_keylen();
        init_environment();
        test_acquire_context();
        test_incorrect_api_usage();
        test_verify_sig();
        test_machine_guid();
        clean_up_environment();
-       }
+    }
        
        test_enum_providers();
        test_enum_provider_types();
diff --git a/rostests/winetests/advapi32/eventlog.c b/rostests/winetests/advapi32/eventlog.c
new file mode 100644 (file)
index 0000000..c0f516a
--- /dev/null
@@ -0,0 +1,1144 @@
+/*
+ * Unit tests for Event Logging functions
+ *
+ * Copyright (c) 2009 Paul Vriens
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#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 BOOL (WINAPI *pGetComputerNameExA)(COMPUTER_NAME_FORMAT,LPSTR,LPDWORD);
+static BOOL (WINAPI *pWow64DisableWow64FsRedirection)(PVOID *);
+static BOOL (WINAPI *pWow64RevertWow64FsRedirection)(PVOID);
+
+static void init_function_pointers(void)
+{
+    HMODULE hadvapi32 = GetModuleHandleA("advapi32.dll");
+    HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
+
+    pCreateWellKnownSid = (void*)GetProcAddress(hadvapi32, "CreateWellKnownSid");
+    pGetEventLogInformation = (void*)GetProcAddress(hadvapi32, "GetEventLogInformation");
+
+    pGetComputerNameExA = (void*)GetProcAddress(hkernel32, "GetComputerNameExA");
+    pWow64DisableWow64FsRedirection = (void*)GetProcAddress(hkernel32, "Wow64DisableWow64FsRedirection");
+    pWow64RevertWow64FsRedirection = (void*)GetProcAddress(hkernel32, "Wow64RevertWow64FsRedirection");
+}
+
+static void create_backup(const char *filename)
+{
+    HANDLE handle;
+
+    DeleteFileA(filename);
+    handle = OpenEventLogA(NULL, "Application");
+    BackupEventLogA(handle, filename);
+    CloseEventLog(handle);
+
+    todo_wine
+    ok(GetFileAttributesA(filename) != INVALID_FILE_ATTRIBUTES, "Expected a backup file\n");
+}
+
+static void test_open_close(void)
+{
+    HANDLE handle;
+    BOOL ret;
+
+    SetLastError(0xdeadbeef);
+    ret = CloseEventLog(NULL);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE ||
+       GetLastError() == ERROR_NOACCESS, /* W2K */
+       "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    handle = OpenEventLogA(NULL, NULL);
+    ok(handle == NULL, "Didn't expect a handle\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    handle = OpenEventLogA("IDontExist", NULL);
+    ok(handle == NULL, "Didn't expect a handle\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    handle = OpenEventLogA("IDontExist", "deadbeef");
+    ok(handle == NULL, "Didn't expect a handle\n");
+    ok(GetLastError() == RPC_S_SERVER_UNAVAILABLE ||
+       GetLastError() == RPC_S_INVALID_NET_ADDR, /* Some Vista and Win7 */
+       "Expected RPC_S_SERVER_UNAVAILABLE, got %d\n", GetLastError());
+
+    /* This one opens the Application log */
+    handle = OpenEventLogA(NULL, "deadbeef");
+    ok(handle != NULL, "Expected a handle\n");
+    ret = CloseEventLog(handle);
+    ok(ret, "Expected success\n");
+    /* Close a second time */
+    SetLastError(0xdeadbeef);
+    ret = CloseEventLog(handle);
+    todo_wine
+    {
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+    }
+
+    /* Empty servername should be read as local server */
+    handle = OpenEventLogA("", "Application");
+    ok(handle != NULL, "Expected a handle\n");
+    CloseEventLog(handle);
+
+    handle = OpenEventLogA(NULL, "Application");
+    ok(handle != NULL, "Expected a handle\n");
+    CloseEventLog(handle);
+}
+
+static void test_info(void)
+{
+    HANDLE handle;
+    BOOL ret;
+    DWORD needed;
+    EVENTLOG_FULL_INFORMATION efi;
+
+    if (!pGetEventLogInformation)
+    {
+        /* NT4 */
+        win_skip("GetEventLogInformation is not available\n");
+        return;
+    }
+    SetLastError(0xdeadbeef);
+    ret = pGetEventLogInformation(NULL, 1, NULL, 0, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_LEVEL, "Expected ERROR_INVALID_LEVEL, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pGetEventLogInformation(NULL, EVENTLOG_FULL_INFO, NULL, 0, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+
+    handle = OpenEventLogA(NULL, "Application");
+
+    SetLastError(0xdeadbeef);
+    ret = pGetEventLogInformation(handle, EVENTLOG_FULL_INFO, NULL, 0, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == RPC_X_NULL_REF_POINTER, "Expected RPC_X_NULL_REF_POINTER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pGetEventLogInformation(handle, EVENTLOG_FULL_INFO, NULL, 0, &needed);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == RPC_X_NULL_REF_POINTER, "Expected RPC_X_NULL_REF_POINTER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pGetEventLogInformation(handle, EVENTLOG_FULL_INFO, (LPVOID)&efi, 0, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == RPC_X_NULL_REF_POINTER, "Expected RPC_X_NULL_REF_POINTER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    needed = 0xdeadbeef;
+    efi.dwFull = 0xdeadbeef;
+    ret = pGetEventLogInformation(handle, EVENTLOG_FULL_INFO, (LPVOID)&efi, 0, &needed);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
+    ok(needed == sizeof(EVENTLOG_FULL_INFORMATION), "Expected sizeof(EVENTLOG_FULL_INFORMATION), got %d\n", needed);
+    ok(efi.dwFull == 0xdeadbeef, "Expected no change to the dwFull member\n");
+
+    /* Not that we care, but on success last error is set to ERROR_IO_PENDING */
+    efi.dwFull = 0xdeadbeef;
+    needed *= 2;
+    ret = pGetEventLogInformation(handle, EVENTLOG_FULL_INFO, (LPVOID)&efi, needed, &needed);
+    ok(ret, "Expected success\n");
+    ok(needed == sizeof(EVENTLOG_FULL_INFORMATION), "Expected sizeof(EVENTLOG_FULL_INFORMATION), got %d\n", needed);
+    ok(efi.dwFull == 0 || efi.dwFull == 1, "Expected 0 (not full) or 1 (full), got %d\n", efi.dwFull);
+
+    CloseEventLog(handle);
+}
+
+static void test_count(void)
+{
+    HANDLE handle;
+    BOOL ret;
+    DWORD count;
+    const char backup[] = "backup.evt";
+
+    SetLastError(0xdeadbeef);
+    ret = GetNumberOfEventLogRecords(NULL, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    count = 0xdeadbeef;
+    ret = GetNumberOfEventLogRecords(NULL, &count);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+    ok(count == 0xdeadbeef, "Expected count to stay unchanged\n");
+
+    handle = OpenEventLogA(NULL, "Application");
+
+    SetLastError(0xdeadbeef);
+    ret = GetNumberOfEventLogRecords(handle, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    count = 0xdeadbeef;
+    ret = GetNumberOfEventLogRecords(handle, &count);
+    ok(ret, "Expected success\n");
+    ok(count != 0xdeadbeef, "Expected the number of records\n");
+
+    CloseEventLog(handle);
+
+    /* Make a backup eventlog to work with */
+    create_backup(backup);
+
+    handle = OpenBackupEventLogA(NULL, backup);
+    todo_wine
+    ok(handle != NULL, "Expected a handle\n");
+
+    /* Does GetNumberOfEventLogRecords work with backup eventlogs? */
+    count = 0xdeadbeef;
+    ret = GetNumberOfEventLogRecords(handle, &count);
+    todo_wine
+    {
+    ok(ret, "Expected success\n");
+    ok(count != 0xdeadbeef, "Expected the number of records\n");
+    }
+
+    CloseEventLog(handle);
+    DeleteFileA(backup);
+}
+
+static void test_oldest(void)
+{
+    HANDLE handle;
+    BOOL ret;
+    DWORD oldest;
+    const char backup[] = "backup.evt";
+
+    SetLastError(0xdeadbeef);
+    ret = GetOldestEventLogRecord(NULL, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    oldest = 0xdeadbeef;
+    ret = GetOldestEventLogRecord(NULL, &oldest);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+    ok(oldest == 0xdeadbeef, "Expected oldest to stay unchanged\n");
+
+    handle = OpenEventLogA(NULL, "Application");
+
+    SetLastError(0xdeadbeef);
+    ret = GetOldestEventLogRecord(handle, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    oldest = 0xdeadbeef;
+    ret = GetOldestEventLogRecord(handle, &oldest);
+    ok(ret, "Expected success\n");
+    ok(oldest != 0xdeadbeef, "Expected the number of the oldest record\n");
+
+    CloseEventLog(handle);
+
+    /* Make a backup eventlog to work with */
+    create_backup(backup);
+
+    handle = OpenBackupEventLogA(NULL, backup);
+    todo_wine
+    ok(handle != NULL, "Expected a handle\n");
+
+    /* Does GetOldestEventLogRecord work with backup eventlogs? */
+    oldest = 0xdeadbeef;
+    ret = GetOldestEventLogRecord(handle, &oldest);
+    todo_wine
+    {
+    ok(ret, "Expected success\n");
+    ok(oldest != 0xdeadbeef, "Expected the number of the oldest record\n");
+    }
+
+    CloseEventLog(handle);
+    DeleteFileA(backup);
+}
+
+static void test_backup(void)
+{
+    HANDLE handle;
+    BOOL ret;
+    const char backup[] = "backup.evt";
+    const char backup2[] = "backup2.evt";
+
+    SetLastError(0xdeadbeef);
+    ret = BackupEventLogA(NULL, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = BackupEventLogA(NULL, backup);
+    ok(!ret, "Expected failure\n");
+    ok(GetFileAttributesA(backup) == INVALID_FILE_ATTRIBUTES, "Expected no backup file\n");
+
+    handle = OpenEventLogA(NULL, "Application");
+
+    SetLastError(0xdeadbeef);
+    ret = BackupEventLogA(handle, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    ret = BackupEventLogA(handle, backup);
+    ok(ret, "Expected success\n");
+    todo_wine
+    ok(GetFileAttributesA(backup) != INVALID_FILE_ATTRIBUTES, "Expected a backup file\n");
+
+    /* Try to overwrite */
+    SetLastError(0xdeadbeef);
+    ret = BackupEventLogA(handle, backup);
+    todo_wine
+    {
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_ALREADY_EXISTS, "Expected ERROR_ALREADY_EXISTS, got %d\n", GetLastError());
+    }
+
+    CloseEventLog(handle);
+
+    /* Can we make a backup of a backup? */
+    handle = OpenBackupEventLogA(NULL, backup);
+    todo_wine
+    ok(handle != NULL, "Expected a handle\n");
+
+    ret = BackupEventLogA(handle, backup2);
+    todo_wine
+    {
+    ok(ret, "Expected success\n");
+    ok(GetFileAttributesA(backup2) != INVALID_FILE_ATTRIBUTES, "Expected a backup file\n");
+    }
+
+    CloseEventLog(handle);
+    DeleteFileA(backup);
+    DeleteFileA(backup2);
+}
+
+static void test_read(void)
+{
+    HANDLE handle;
+    BOOL ret;
+    DWORD count, toread, read, needed;
+    void *buf;
+
+    SetLastError(0xdeadbeef);
+    ret = ReadEventLogA(NULL, 0, 0, NULL, 0, NULL, NULL);
+    ok(!ret, "Expected failure\n");
+    todo_wine
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    read = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = ReadEventLogA(NULL, 0, 0, NULL, 0, &read, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(read == 0xdeadbeef, "Expected 'read' parameter to remain unchanged\n");
+    todo_wine
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    needed = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = ReadEventLogA(NULL, 0, 0, NULL, 0, NULL, &needed);
+    ok(!ret, "Expected failure\n");
+    ok(needed == 0xdeadbeef, "Expected 'needed' parameter to remain unchanged\n");
+    todo_wine
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    /* 'read' and 'needed' are only filled when the needed buffer size is passed back or when the call succeeds */
+    SetLastError(0xdeadbeef);
+    ret = ReadEventLogA(NULL, 0, 0, NULL, 0, &read, &needed);
+    ok(!ret, "Expected failure\n");
+    todo_wine
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = ReadEventLogA(NULL, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ, 0, NULL, 0, NULL, NULL);
+    ok(!ret, "Expected failure\n");
+    todo_wine
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = ReadEventLogA(NULL, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ, 0, NULL, 0, &read, &needed);
+    ok(!ret, "Expected failure\n");
+    todo_wine
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    buf = NULL;
+    SetLastError(0xdeadbeef);
+    ret = ReadEventLogA(NULL, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ,
+                        0, buf, sizeof(EVENTLOGRECORD), &read, &needed);
+    ok(!ret, "Expected failure\n");
+    todo_wine
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    buf = HeapAlloc(GetProcessHeap(), 0, sizeof(EVENTLOGRECORD));
+    SetLastError(0xdeadbeef);
+    ret = ReadEventLogA(NULL, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ,
+                        0, buf, sizeof(EVENTLOGRECORD), &read, &needed);
+    ok(!ret, "Expected failure\n");
+    todo_wine
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+    HeapFree(GetProcessHeap(), 0, buf);
+
+    handle = OpenEventLogA(NULL, "Application");
+
+    /* Show that we need the proper dwFlags with a (for the rest) proper call */
+    buf = HeapAlloc(GetProcessHeap(), 0, sizeof(EVENTLOGRECORD));
+
+    SetLastError(0xdeadbeef);
+    ret = ReadEventLogA(handle, 0, 0, buf, sizeof(EVENTLOGRECORD), &read, &needed);
+    ok(!ret, "Expected failure\n");
+    todo_wine
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = ReadEventLogA(handle, EVENTLOG_SEQUENTIAL_READ, 0, buf, sizeof(EVENTLOGRECORD), &read, &needed);
+    ok(!ret, "Expected failure\n");
+    todo_wine
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = ReadEventLogA(handle, EVENTLOG_SEEK_READ, 0, buf, sizeof(EVENTLOGRECORD), &read, &needed);
+    ok(!ret, "Expected failure\n");
+    todo_wine
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = ReadEventLogA(handle, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ | EVENTLOG_BACKWARDS_READ,
+                        0, buf, sizeof(EVENTLOGRECORD), &read, &needed);
+    ok(!ret, "Expected failure\n");
+    todo_wine
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = ReadEventLogA(handle, EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ | EVENTLOG_BACKWARDS_READ,
+                        0, buf, sizeof(EVENTLOGRECORD), &read, &needed);
+    ok(!ret, "Expected failure\n");
+    todo_wine
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = ReadEventLogA(handle, EVENTLOG_SEEK_READ | EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ,
+                        0, buf, sizeof(EVENTLOGRECORD), &read, &needed);
+    ok(!ret, "Expected failure\n");
+    todo_wine
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    HeapFree(GetProcessHeap(), 0, buf);
+
+    /* First check if there are any records (in practice only on Wine: FIXME) */
+    count = 0;
+    GetNumberOfEventLogRecords(handle, &count);
+    if (!count)
+    {
+        skip("No records in the 'Application' log\n");
+        CloseEventLog(handle);
+        return;
+    }
+
+    /* Get the buffer size for the first record */
+    buf = HeapAlloc(GetProcessHeap(), 0, sizeof(EVENTLOGRECORD));
+    read = needed = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = ReadEventLogA(handle, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ,
+                        0, buf, sizeof(EVENTLOGRECORD), &read, &needed);
+    ok(!ret, "Expected failure\n");
+    ok(read == 0, "Expected no bytes read\n");
+    ok(needed > sizeof(EVENTLOGRECORD), "Expected the needed buffersize to be bigger than sizeof(EVENTLOGRECORD)\n");
+    ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    /* Read the first record */
+    toread = needed;
+    buf = HeapReAlloc(GetProcessHeap(), 0, buf, toread);
+    read = needed = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = ReadEventLogA(handle, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ, 0, buf, toread, &read, &needed);
+    ok(ret, "Expected success\n");
+    ok(read == toread ||
+       broken(read < toread), /* NT4 wants a buffer size way bigger than just 1 record */
+       "Expected the requested size to be read\n");
+    ok(needed == 0, "Expected no extra bytes to be read\n");
+    HeapFree(GetProcessHeap(), 0, buf);
+
+    CloseEventLog(handle);
+}
+
+static void test_openbackup(void)
+{
+    HANDLE handle, handle2, file;
+    DWORD written;
+    const char backup[] = "backup.evt";
+    const char text[] = "Just some text";
+
+    SetLastError(0xdeadbeef);
+    handle = OpenBackupEventLogA(NULL, NULL);
+    ok(handle == NULL, "Didn't expect a handle\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    handle = OpenBackupEventLogA(NULL, "idontexist.evt");
+    ok(handle == NULL, "Didn't expect a handle\n");
+    ok(GetLastError() == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    handle = OpenBackupEventLogA("IDontExist", NULL);
+    ok(handle == NULL, "Didn't expect a handle\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    handle = OpenBackupEventLogA("IDontExist", "idontexist.evt");
+    ok(handle == NULL, "Didn't expect a handle\n");
+    ok(GetLastError() == RPC_S_SERVER_UNAVAILABLE ||
+       GetLastError() == RPC_S_INVALID_NET_ADDR, /* Some Vista and Win7 */
+       "Expected RPC_S_SERVER_UNAVAILABLE, got %d\n", GetLastError());
+
+    /* Make a backup eventlog to work with */
+    create_backup(backup);
+
+    /* FIXME: Wine stops here */
+    if (GetFileAttributesA(backup) == INVALID_FILE_ATTRIBUTES)
+    {
+        skip("We don't have a backup eventlog to work with\n");
+        return;
+    }
+
+    SetLastError(0xdeadbeef);
+    handle = OpenBackupEventLogA("IDontExist", backup);
+    ok(handle == NULL, "Didn't expect a handle\n");
+    ok(GetLastError() == RPC_S_SERVER_UNAVAILABLE ||
+       GetLastError() == RPC_S_INVALID_NET_ADDR, /* Some Vista and Win7 */
+       "Expected RPC_S_SERVER_UNAVAILABLE, got %d\n", GetLastError());
+
+    /* Empty servername should be read as local server */
+    handle = OpenBackupEventLogA("", backup);
+    ok(handle != NULL, "Expected a handle\n");
+    CloseEventLog(handle);
+
+    handle = OpenBackupEventLogA(NULL, backup);
+    ok(handle != NULL, "Expected a handle\n");
+
+    /* Can we open that same backup eventlog more than once? */
+    handle2 = OpenBackupEventLogA(NULL, backup);
+    ok(handle2 != NULL, "Expected a handle\n");
+    ok(handle2 != handle, "Didn't expect the same handle\n");
+    CloseEventLog(handle2);
+
+    CloseEventLog(handle);
+    DeleteFileA(backup);
+
+    /* Is there any content checking done? */
+    file = CreateFileA(backup, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
+    CloseHandle(file);
+    SetLastError(0xdeadbeef);
+    handle = OpenBackupEventLogA(NULL, backup);
+    ok(handle == NULL, "Didn't expect a handle\n");
+    ok(GetLastError() == ERROR_NOT_ENOUGH_MEMORY ||
+       GetLastError() == ERROR_EVENTLOG_FILE_CORRUPT, /* Vista and Win7 */
+       "Expected ERROR_NOT_ENOUGH_MEMORY, got %d\n", GetLastError());
+    CloseEventLog(handle);
+    DeleteFileA(backup);
+
+    file = CreateFileA(backup, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
+    WriteFile(file, text, sizeof(text), &written, NULL);
+    CloseHandle(file);
+    SetLastError(0xdeadbeef);
+    handle = OpenBackupEventLogA(NULL, backup);
+    ok(handle == NULL, "Didn't expect a handle\n");
+    ok(GetLastError() == ERROR_EVENTLOG_FILE_CORRUPT, "Expected ERROR_EVENTLOG_FILE_CORRUPT, got %d\n", GetLastError());
+    CloseEventLog(handle);
+    DeleteFileA(backup);
+}
+
+static void test_clear(void)
+{
+    HANDLE handle;
+    BOOL ret;
+    const char backup[] = "backup.evt";
+    const char backup2[] = "backup2.evt";
+
+    SetLastError(0xdeadbeef);
+    ret = ClearEventLogA(NULL, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+
+    /* Make a backup eventlog to work with */
+    create_backup(backup);
+
+    SetLastError(0xdeadbeef);
+    ret = ClearEventLogA(NULL, backup);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+
+    handle = OpenBackupEventLogA(NULL, backup);
+    todo_wine
+    ok(handle != NULL, "Expected a handle\n");
+
+    /* A real eventlog would fail with ERROR_ALREADY_EXISTS */
+    SetLastError(0xdeadbeef);
+    ret = ClearEventLogA(handle, backup);
+    ok(!ret, "Expected failure\n");
+    /* The eventlog service runs under an account that doesn't have the necessary
+     * permissions on the users home directory on a default Vista+ system.
+     */
+    ok(GetLastError() == ERROR_INVALID_HANDLE ||
+       GetLastError() == ERROR_ACCESS_DENIED, /* Vista+ */
+       "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+
+    /* Show that ClearEventLog only works for real eventlogs. */
+    SetLastError(0xdeadbeef);
+    ret = ClearEventLogA(handle, backup2);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+    ok(GetFileAttributesA(backup2) == INVALID_FILE_ATTRIBUTES, "Expected no backup file\n");
+
+    SetLastError(0xdeadbeef);
+    ret = ClearEventLogA(handle, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+
+    CloseEventLog(handle);
+    todo_wine
+    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, W2K8 or Win7 */
+    int i;
+    char *localcomputer = NULL;
+    DWORD size;
+
+    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;
+    }
+
+    /* 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);
+    if (!handle)
+    {
+        /* Intermittently seen on NT4 when tests are run immediately after boot */
+        win_skip("Could not get a handle to the eventlog\n");
+        goto cleanup;
+    }
+
+    count = 0xdeadbeef;
+    GetNumberOfEventLogRecords(handle, &count);
+    if (count != 0)
+    {
+        /* Needed for W2K3 without a service pack */
+        win_skip("We most likely opened the Application eventlog\n");
+        CloseEventLog(handle);
+        Sleep(2000);
+
+        handle = OpenEventLogA(NULL, eventlogname);
+        count = 0xdeadbeef;
+        GetNumberOfEventLogRecords(handle, &count);
+        if (count != 0)
+        {
+            win_skip("We didn't open our new eventlog\n");
+            CloseEventLog(handle);
+            goto cleanup;
+        }
+    }
+
+    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);
+    }
+    else
+    {
+        void *buf;
+        DWORD read, needed;
+        EVENTLOGRECORD *record;
+
+        /* Needed to catch earlier Vista (with no ServicePack for example) */
+        buf = HeapAlloc(GetProcessHeap(), 0, sizeof(EVENTLOGRECORD));
+        ReadEventLogA(handle, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ,
+                      0, buf, sizeof(EVENTLOGRECORD), &read, &needed);
+
+        buf = HeapReAlloc(GetProcessHeap(), 0, buf, needed);
+        ReadEventLogA(handle, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ,
+                      0, buf, needed, &read, &needed);
+
+        record = (EVENTLOGRECORD *)buf;
+
+        /* Vista and W2K8 return EVENTLOG_SUCCESS, Windows versions before return
+         * the written eventtype (0x20 in this case).
+         */
+        if (record->EventType == EVENTLOG_SUCCESS)
+            on_vista = TRUE;
+
+        HeapFree(GetProcessHeap(), 0, buf);
+    }
+    ok(ret, "Expected success : %d\n", GetLastError());
+
+    /* This will clear the eventlog. The record numbering for new
+     * events however differs on Vista SP1+. Before Vista the first
+     * event would be numbered 1, on Vista SP1+ it's higher as we already
+     * had at least one event (more in case of multiple test runs without
+     * a reboot).
+     */
+    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 > 1 && oldest != 0xdeadbeef), /* Vista SP1+, W2K8 and Win7 */
+           "Expected oldest to be 1 or higher, got %d\n", oldest);
+        if (oldest > 1 && oldest != 0xdeadbeef)
+            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");
+        goto cleanup;
+    }
+
+    /* Report only once */
+    if (on_vista)
+        skip("There is no DWORD alignment enforced for UserSid on Vista, W2K8 or Win7\n");
+
+    if (on_vista && pGetComputerNameExA)
+    {
+        /* New Vista+ behavior */
+        size = 0;
+        SetLastError(0xdeadbeef);
+        pGetComputerNameExA(ComputerNameDnsFullyQualified, NULL, &size);
+        localcomputer = HeapAlloc(GetProcessHeap(), 0, size);
+        pGetComputerNameExA(ComputerNameDnsFullyQualified, localcomputer, &size);
+    }
+    else
+    {
+        size = MAX_COMPUTERNAME_LENGTH + 1;
+        localcomputer = HeapAlloc(GetProcessHeap(), 0, size);
+        GetComputerNameA(localcomputer, &size);
+    }
+
+    /* 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 + 1)),
+           "Expected %d or higher, got %d\n", i + 1, 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 *)((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 *)((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);
+
+    /* 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);
+
+cleanup:
+    HeapFree(GetProcessHeap(), 0, localcomputer);
+    HeapFree(GetProcessHeap(), 0, user);
+}
+
+/* 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];
+    void *redir = 0;
+
+    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 (size : %d)\n", size);
+    }
+
+    RegCloseKey(eventkey);
+    RegCloseKey(key);
+
+    /* The directory that holds the eventlog files could be redirected */
+    if (pWow64DisableWow64FsRedirection)
+        pWow64DisableWow64FsRedirection(&redir);
+
+    /* 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");
+
+    if (pWow64RevertWow64FsRedirection)
+        pWow64RevertWow64FsRedirection(redir);
+}
+
+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);
+    CloseEventLog(NULL);
+    if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+    {
+        win_skip("Event log functions are not implemented\n");
+        return;
+    }
+
+    init_function_pointers();
+
+    /* Parameters only */
+    test_open_close();
+    test_info();
+    test_count();
+    test_oldest();
+    test_backup();
+    test_openbackup();
+    test_read();
+    test_clear();
+
+    /* Functional tests */
+    if (create_new_eventlog())
+    {
+        test_readwrite();
+        test_autocreation();
+    }
+    cleanup_eventlog();
+}
index abc2eb4..dee7664 100644 (file)
@@ -956,6 +956,28 @@ static void test_reg_create_key(void)
     /* clean up */
     RegDeleteKey(hkey2, "");
     RegDeleteKey(hkey1, "");
+    RegCloseKey(hkey2);
+    RegCloseKey(hkey1);
+
+    /* test creation of volatile keys */
+    ret = RegCreateKeyExA(hkey_main, "Volatile", 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey1, NULL);
+    ok(!ret, "RegCreateKeyExA failed with error %d\n", ret);
+    ret = RegCreateKeyExA(hkey1, "Subkey2", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey2, NULL);
+    ok(ret == ERROR_CHILD_MUST_BE_VOLATILE || broken(!ret), /* win9x */
+       "RegCreateKeyExA failed with error %d\n", ret);
+    if (!ret) RegCloseKey( hkey2 );
+    ret = RegCreateKeyExA(hkey1, "Subkey2", 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey2, NULL);
+    ok(!ret, "RegCreateKeyExA failed with error %d\n", ret);
+    RegCloseKey(hkey2);
+    /* should succeed if the key already exists */
+    ret = RegCreateKeyExA(hkey1, "Subkey2", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey2, NULL);
+    ok(!ret, "RegCreateKeyExA failed with error %d\n", ret);
+
+    /* clean up */
+    RegDeleteKey(hkey2, "");
+    RegDeleteKey(hkey1, "");
+    RegCloseKey(hkey2);
+    RegCloseKey(hkey1);
 
     /*  beginning backslash character */
     ret = RegCreateKeyExA(hkey_main, "\\Subkey3", 0, NULL, 0, KEY_NOTIFY, NULL, &hkey1, NULL);
@@ -964,6 +986,7 @@ static void test_reg_create_key(void)
     else {
         ok(!ret, "RegCreateKeyExA failed with error %d\n", ret);
         RegDeleteKey(hkey1, NULL);
+        RegCloseKey(hkey1);
     }
 
     /* WOW64 flags - open an existing key */
index dfcd45b..001b909 100644 (file)
@@ -79,6 +79,7 @@ typedef LPSTR (WINAPI *fnGetTrusteeNameA)( PTRUSTEEA pTrustee );
 typedef BOOL (WINAPI *fnMakeSelfRelativeSD)( PSECURITY_DESCRIPTOR, PSECURITY_DESCRIPTOR, LPDWORD );
 typedef BOOL (WINAPI *fnConvertSidToStringSidA)( PSID pSid, LPSTR *str );
 typedef BOOL (WINAPI *fnConvertStringSidToSidA)( LPCSTR str, PSID pSid );
+static BOOL (WINAPI *pCheckTokenMembership)(HANDLE, PSID, PBOOL);
 static BOOL (WINAPI *pConvertStringSecurityDescriptorToSecurityDescriptorA)(LPCSTR, DWORD,
                                                                             PSECURITY_DESCRIPTOR*, PULONG );
 static BOOL (WINAPI *pConvertStringSecurityDescriptorToSecurityDescriptorW)(LPCWSTR, DWORD,
@@ -153,6 +154,7 @@ static void init(void)
     pAddAccessAllowedAceEx = (void *)GetProcAddress(hmod, "AddAccessAllowedAceEx");
     pAddAccessDeniedAceEx = (void *)GetProcAddress(hmod, "AddAccessDeniedAceEx");
     pAddAuditAccessAceEx = (void *)GetProcAddress(hmod, "AddAuditAccessAceEx");
+    pCheckTokenMembership = (void *)GetProcAddress(hmod, "CheckTokenMembership");
     pConvertStringSecurityDescriptorToSecurityDescriptorA =
         (void *)GetProcAddress(hmod, "ConvertStringSecurityDescriptorToSecurityDescriptorA" );
     pConvertStringSecurityDescriptorToSecurityDescriptorW =
@@ -254,6 +256,7 @@ static void test_sid(void)
     ok(pisid->SubAuthority[0] == 21, "Invalid subauthority 0 - expceted 21, got %d\n", pisid->SubAuthority[0]);
     ok(pisid->SubAuthority[3] == 4576, "Invalid subauthority 0 - expceted 4576, got %d\n", pisid->SubAuthority[3]);
     LocalFree(str);
+    LocalFree(psid);
 
     for( i = 0; i < sizeof(refs) / sizeof(refs[0]); i++ )
     {
@@ -1226,10 +1229,18 @@ static void test_token_attr(void)
     ok(ret, "OpenProcessToken failed with error %d\n", GetLastError());
 
     /* groups */
+    SetLastError(0xdeadbeef);
     ret = GetTokenInformation(Token, TokenGroups, NULL, 0, &Size);
+    ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+        "GetTokenInformation(TokenGroups) %s with error %d\n",
+        ret ? "succeeded" : "failed", GetLastError());
     Groups = HeapAlloc(GetProcessHeap(), 0, Size);
+    SetLastError(0xdeadbeef);
     ret = GetTokenInformation(Token, TokenGroups, Groups, Size, &Size);
     ok(ret, "GetTokenInformation(TokenGroups) failed with error %d\n", GetLastError());
+    ok(GetLastError() == 0xdeadbeef,
+       "GetTokenInformation shouldn't have set last error to %d\n",
+       GetLastError());
     trace("TokenGroups:\n");
     for (i = 0; i < Groups->GroupCount; i++)
     {
@@ -1238,12 +1249,12 @@ static void test_token_attr(void)
         DWORD DomainLength = 255;
         TCHAR Domain[255];
         SID_NAME_USE SidNameUse;
-        pConvertSidToStringSidA(Groups->Groups[i].Sid, &SidString);
         Name[0] = '\0';
         Domain[0] = '\0';
         ret = LookupAccountSid(NULL, Groups->Groups[i].Sid, Name, &NameLength, Domain, &DomainLength, &SidNameUse);
         if (ret)
         {
+            pConvertSidToStringSidA(Groups->Groups[i].Sid, &SidString);
             trace("%s, %s\\%s use: %d attr: 0x%08x\n", SidString, Domain, Name, SidNameUse, Groups->Groups[i].Attributes);
             LocalFree(SidString);
         }
@@ -1478,6 +1489,8 @@ static void test_CreateWellKnownSid(void)
             ok(memcmp(buf2, sid_buffer, cb) == 0, "SID create with domain is different than without (%d)\n", i);
         }
     }
+
+    LocalFree(domainsid);
 }
 
 static void test_LookupAccountSid(void)
@@ -2047,7 +2060,8 @@ static void test_LookupAccountName(void)
         domain = HeapAlloc(GetProcessHeap(), 0, domain_size);
         ret = LookupAccountNameA(NULL, computer_name, psid, &sid_size, domain, &domain_size, &sid_use);
         ok(ret, "LookupAccountNameA failed: %d\n", GetLastError());
-        ok(sid_use == SidTypeDomain, "expected SidTypeDomain, got %d\n", sid_use);
+        ok(sid_use == SidTypeDomain ||
+           (sid_use == SidTypeUser && ! strcmp(computer_name, user_name)), "expected SidTypeDomain for %s, got %d\n", computer_name, sid_use);
         HeapFree(GetProcessHeap(), 0, domain);
         HeapFree(GetProcessHeap(), 0, psid);
     }
@@ -2528,11 +2542,19 @@ static void test_SetEntriesInAcl(void)
     ExplicitAccess.grfAccessPermissions = KEY_WRITE;
     ExplicitAccess.grfAccessMode = GRANT_ACCESS;
     ExplicitAccess.grfInheritance = NO_INHERITANCE;
-    ExplicitAccess.Trustee.pMultipleTrustee = NULL;
-    ExplicitAccess.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+    ExplicitAccess.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
     ExplicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_SID;
-    ExplicitAccess.Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
     ExplicitAccess.Trustee.ptstrName = EveryoneSid;
+    ExplicitAccess.Trustee.MultipleTrusteeOperation = 0xDEADBEEF;
+    ExplicitAccess.Trustee.pMultipleTrustee = (PVOID)0xDEADBEEF;
+    res = pSetEntriesInAclW(1, &ExplicitAccess, OldAcl, &NewAcl);
+    ok(res == ERROR_SUCCESS, "SetEntriesInAclW failed: %u\n", res);
+    ok(NewAcl != NULL, "returned acl was NULL\n");
+    LocalFree(NewAcl);
+
+    ExplicitAccess.Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
+    ExplicitAccess.Trustee.pMultipleTrustee = NULL;
+    ExplicitAccess.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
     res = pSetEntriesInAclW(1, &ExplicitAccess, OldAcl, &NewAcl);
     ok(res == ERROR_SUCCESS, "SetEntriesInAclW failed: %u\n", res);
     ok(NewAcl != NULL, "returned acl was NULL\n");
@@ -2651,6 +2673,50 @@ static void test_ConvertStringSecurityDescriptor(void)
     BOOL ret;
     PSECURITY_DESCRIPTOR pSD;
     static const WCHAR Blank[] = { 0 };
+    int i;
+    static const struct
+    {
+        const char *sidstring;
+        DWORD      revision;
+        BOOL       ret;
+        DWORD      GLE;
+        DWORD      altGLE;
+    } cssd[] =
+    {
+        { "D:(A;;GA;;;WD)",                  0xdeadbeef,      FALSE, ERROR_UNKNOWN_REVISION },
+        /* test ACE string type */
+        { "D:(A;;GA;;;WD)",                  SDDL_REVISION_1, TRUE },
+        { "D:(D;;GA;;;WD)",                  SDDL_REVISION_1, TRUE },
+        { "ERROR:(D;;GA;;;WD)",              SDDL_REVISION_1, FALSE, ERROR_INVALID_PARAMETER },
+        /* test ACE string with spaces */
+        { " D:(D;;GA;;;WD)",                SDDL_REVISION_1, TRUE },
+        { "D: (D;;GA;;;WD)",                SDDL_REVISION_1, TRUE },
+        { "D:( D;;GA;;;WD)",                SDDL_REVISION_1, TRUE },
+        { "D:(D ;;GA;;;WD)",                SDDL_REVISION_1, FALSE, RPC_S_INVALID_STRING_UUID, ERROR_INVALID_ACL }, /* Vista+ */
+        { "D:(D; ;GA;;;WD)",                SDDL_REVISION_1, TRUE },
+        { "D:(D;; GA;;;WD)",                SDDL_REVISION_1, TRUE },
+        { "D:(D;;GA ;;;WD)",                SDDL_REVISION_1, FALSE, ERROR_INVALID_ACL },
+        { "D:(D;;GA; ;;WD)",                SDDL_REVISION_1, TRUE },
+        { "D:(D;;GA;; ;WD)",                SDDL_REVISION_1, TRUE },
+        { "D:(D;;GA;;; WD)",                SDDL_REVISION_1, TRUE },
+        { "D:(D;;GA;;;WD )",                SDDL_REVISION_1, TRUE },
+        /* test ACE string access rights */
+        { "D:(A;;GA;;;WD)",                  SDDL_REVISION_1, TRUE },
+        { "D:(A;;GRGWGX;;;WD)",              SDDL_REVISION_1, TRUE },
+        { "D:(A;;RCSDWDWO;;;WD)",            SDDL_REVISION_1, TRUE },
+        { "D:(A;;RPWPCCDCLCSWLODTCR;;;WD)",  SDDL_REVISION_1, TRUE },
+        { "D:(A;;FAFRFWFX;;;WD)",            SDDL_REVISION_1, TRUE },
+        { "D:(A;;KAKRKWKX;;;WD)",            SDDL_REVISION_1, TRUE },
+        { "D:(A;;0xFFFFFFFF;;;WD)",          SDDL_REVISION_1, TRUE },
+        { "S:(AU;;0xFFFFFFFF;;;WD)",         SDDL_REVISION_1, TRUE },
+        /* test ACE string access right error case */
+        { "D:(A;;ROB;;;WD)",                 SDDL_REVISION_1, FALSE, ERROR_INVALID_ACL },
+        /* test behaviour with empty strings */
+        { "",                                SDDL_REVISION_1, TRUE },
+        /* test ACE string SID */
+        { "D:(D;;GA;;;S-1-0-0)",             SDDL_REVISION_1, TRUE },
+        { "D:(D;;GA;;;Nonexistent account)", SDDL_REVISION_1, FALSE, ERROR_INVALID_ACL, ERROR_INVALID_SID } /* W2K */
+    };
 
     if (!pConvertStringSecurityDescriptorToSecurityDescriptorA)
     {
@@ -2658,82 +2724,22 @@ static void test_ConvertStringSecurityDescriptor(void)
         return;
     }
 
-    SetLastError(0xdeadbeef);
-    ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
-        "D:(A;;GA;;;WD)", 0xdeadbeef, &pSD, NULL);
-    ok(!ret && GetLastError() == ERROR_UNKNOWN_REVISION,
-        "ConvertStringSecurityDescriptorToSecurityDescriptor should have failed with ERROR_UNKNOWN_REVISION instead of %d\n",
-        GetLastError());
-
-    /* test ACE string type */
-    SetLastError(0xdeadbeef);
-    ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
-        "D:(A;;GA;;;WD)", SDDL_REVISION_1, &pSD, NULL);
-    ok(ret, "ConvertStringSecurityDescriptorToSecurityDescriptor failed with error %d\n", GetLastError());
-    LocalFree(pSD);
-
-    SetLastError(0xdeadbeef);
-    ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
-        "D:(D;;GA;;;WD)", SDDL_REVISION_1, &pSD, NULL);
-    ok(ret, "ConvertStringSecurityDescriptorToSecurityDescriptor failed with error %d\n", GetLastError());
-    LocalFree(pSD);
-
-    SetLastError(0xdeadbeef);
-    ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
-        "ERROR:(D;;GA;;;WD)", SDDL_REVISION_1, &pSD, NULL);
-    ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER,
-        "ConvertStringSecurityDescriptorToSecurityDescriptor should have failed with ERROR_INVALID_PARAMETER instead of %d\n",
-        GetLastError());
-
-    /* test ACE string access rights */
-    SetLastError(0xdeadbeef);
-    ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
-        "D:(A;;GA;;;WD)", SDDL_REVISION_1, &pSD, NULL);
-    ok(ret, "ConvertStringSecurityDescriptorToSecurityDescriptor failed with error %d\n", GetLastError());
-    LocalFree(pSD);
-    SetLastError(0xdeadbeef);
-    ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
-        "D:(A;;GRGWGX;;;WD)", SDDL_REVISION_1, &pSD, NULL);
-    ok(ret, "ConvertStringSecurityDescriptorToSecurityDescriptor failed with error %d\n", GetLastError());
-    LocalFree(pSD);
-    SetLastError(0xdeadbeef);
-    ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
-        "D:(A;;RCSDWDWO;;;WD)", SDDL_REVISION_1, &pSD, NULL);
-    ok(ret, "ConvertStringSecurityDescriptorToSecurityDescriptor failed with error %d\n", GetLastError());
-    LocalFree(pSD);
-    SetLastError(0xdeadbeef);
-    ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
-        "D:(A;;RPWPCCDCLCSWLODTCR;;;WD)", SDDL_REVISION_1, &pSD, NULL);
-    ok(ret, "ConvertStringSecurityDescriptorToSecurityDescriptor failed with error %d\n", GetLastError());
-    LocalFree(pSD);
-    SetLastError(0xdeadbeef);
-    ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
-        "D:(A;;FAFRFWFX;;;WD)", SDDL_REVISION_1, &pSD, NULL);
-    ok(ret, "ConvertStringSecurityDescriptorToSecurityDescriptor failed with error %d\n", GetLastError());
-    LocalFree(pSD);
-    SetLastError(0xdeadbeef);
-    ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
-        "D:(A;;KAKRKWKX;;;WD)", SDDL_REVISION_1, &pSD, NULL);
-    ok(ret, "ConvertStringSecurityDescriptorToSecurityDescriptor failed with error %d\n", GetLastError());
-    LocalFree(pSD);
-    SetLastError(0xdeadbeef);
-    ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
-        "D:(A;;0xFFFFFFFF;;;WD)", SDDL_REVISION_1, &pSD, NULL);
-    ok(ret, "ConvertStringSecurityDescriptorToSecurityDescriptor failed with error %d\n", GetLastError());
-    LocalFree(pSD);
-    SetLastError(0xdeadbeef);
-    ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
-        "S:(AU;;0xFFFFFFFF;;;WD)", SDDL_REVISION_1, &pSD, NULL);
-    ok(ret, "ConvertStringSecurityDescriptorToSecurityDescriptor failed with error %d\n", GetLastError());
-    LocalFree(pSD);
+    for (i = 0; i < sizeof(cssd)/sizeof(cssd[0]); i++)
+    {
+        DWORD GLE;
 
-    /* test ACE string access right error case */
-    SetLastError(0xdeadbeef);
-    ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
-        "D:(A;;ROB;;;WD)", SDDL_REVISION_1, &pSD, NULL);
-    ok(!ret && GetLastError() == ERROR_INVALID_ACL,
-        "ConvertStringSecurityDescriptorToSecurityDescriptor should have failed with ERROR_INVALID_ACL instead of %d\n",
-        GetLastError());
+        SetLastError(0xdeadbeef);
+        ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
+            cssd[i].sidstring, cssd[i].revision, &pSD, NULL);
+        GLE = GetLastError();
+        ok(ret == cssd[i].ret, "(%02d) Expected %s (%d)\n", i, cssd[i].ret ? "success" : "failure", GLE);
+        if (!cssd[i].ret)
+            ok(GLE == cssd[i].GLE ||
+               (cssd[i].altGLE && GLE == cssd[i].altGLE),
+               "(%02d) Unexpected last error %d\n", i, GLE);
+        if (ret)
+            LocalFree(pSD);
+    }
 
     /* test behaviour with NULL parameters */
     SetLastError(0xdeadbeef);
@@ -2766,29 +2772,11 @@ static void test_ConvertStringSecurityDescriptor(void)
         GetLastError());
 
     /* test behaviour with empty strings */
-    SetLastError(0xdeadbeef);
-    ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
-        "", SDDL_REVISION_1, &pSD, NULL);
-    ok(ret, "ConvertStringSecurityDescriptorToSecurityDescriptor failed with error %d\n", GetLastError());
-
     SetLastError(0xdeadbeef);
     ret = pConvertStringSecurityDescriptorToSecurityDescriptorW(
         Blank, SDDL_REVISION_1, &pSD, NULL);
     ok(ret, "ConvertStringSecurityDescriptorToSecurityDescriptor failed with error %d\n", GetLastError());
-
-    /* test ACE string SID */
-    SetLastError(0xdeadbeef);
-    ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
-        "D:(D;;GA;;;S-1-0-0)", SDDL_REVISION_1, &pSD, NULL);
-    ok(ret, "ConvertStringSecurityDescriptorToSecurityDescriptor failed with error %d\n", GetLastError());
     LocalFree(pSD);
-
-    SetLastError(0xdeadbeef);
-    ret = pConvertStringSecurityDescriptorToSecurityDescriptorA(
-        "D:(D;;GA;;;Nonexistent account)", SDDL_REVISION_1, &pSD, NULL);
-    ok(!ret, "Expected failure, got %d\n", ret);
-    ok(GetLastError() == ERROR_INVALID_ACL || GetLastError() == ERROR_INVALID_SID,
-       "Expected ERROR_INVALID_ACL or ERROR_INVALID_SID, got %d\n", GetLastError());
 }
 
 static void test_ConvertSecurityDescriptorToString(void)
@@ -2900,6 +2888,9 @@ static void test_ConvertSecurityDescriptorToString(void)
         CHECK_ONE_OF_AND_FREE("O:SYG:S-1-5-21-93476-23408-4576D:S:(AU;OICINPIOIDSAFA;CCDCLCSWRPRC;;;SU)(AU;NPSA;0x12019f;;;SU)", /* XP */
             "O:SYG:S-1-5-21-93476-23408-4576D:NO_ACCESS_CONTROLS:(AU;OICINPIOIDSAFA;CCDCLCSWRPRC;;;SU)(AU;NPSA;0x12019f;;;SU)" /* Vista */);
     }
+
+    LocalFree(psid2);
+    LocalFree(psid);
 }
 
 static void test_SetSecurityDescriptorControl (PSECURITY_DESCRIPTOR sec)
@@ -3171,8 +3162,10 @@ static void test_GetSecurityInfo(void)
     ok(sd != NULL, "GetSecurityInfo\n");
     ok(owner != NULL, "GetSecurityInfo\n");
     ok(group != NULL, "GetSecurityInfo\n");
-    ok(dacl != NULL, "GetSecurityInfo\n");
-    ok(IsValidAcl(dacl), "GetSecurityInfo\n");
+    if (dacl != NULL)
+        ok(IsValidAcl(dacl), "GetSecurityInfo\n");
+    else
+        win_skip("No ACL information returned\n");
 
     LocalFree(sd);
 
@@ -3191,8 +3184,10 @@ static void test_GetSecurityInfo(void)
     ok(ret == ERROR_SUCCESS, "GetSecurityInfo returned %d\n", ret);
     ok(owner != NULL, "GetSecurityInfo\n");
     ok(group != NULL, "GetSecurityInfo\n");
-    ok(dacl != NULL, "GetSecurityInfo\n");
-    ok(IsValidAcl(dacl), "GetSecurityInfo\n");
+    if (dacl != NULL)
+        ok(IsValidAcl(dacl), "GetSecurityInfo\n");
+    else
+        win_skip("No ACL information returned\n");
 
     CloseHandle(obj);
 }
@@ -3220,11 +3215,140 @@ static void test_GetSidSubAuthority(void)
     ok(*pGetSidSubAuthority(psid,1) == 93476,"GetSidSubAuthority gave %d expected 93476\n",*pGetSidSubAuthority(psid,1));
     ok(GetLastError() == 0,"GetLastError returned %d instead of 0\n",GetLastError());
     SetLastError(0xbebecaca);
-    todo_wine ok(*pGetSidSubAuthority(psid,4) == 0,"GetSidSubAuthority gave %d,expected 0\n",*pGetSidSubAuthority(psid,4));
+    ok(pGetSidSubAuthority(psid,4) != NULL,"Expected out of bounds GetSidSubAuthority to return a non-NULL pointer\n");
     ok(GetLastError() == 0,"GetLastError returned %d instead of 0\n",GetLastError());
     LocalFree(psid);
 }
 
+static void test_CheckTokenMembership(void)
+{
+    PTOKEN_GROUPS token_groups;
+    DWORD size;
+    HANDLE process_token, token;
+    BOOL is_member;
+    BOOL ret;
+    DWORD i;
+
+    if (!pCheckTokenMembership)
+    {
+        win_skip("CheckTokenMembership is not available\n");
+        return;
+    }
+    ret = OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE|TOKEN_QUERY, &process_token);
+    ok(ret, "OpenProcessToken failed with error %d\n", GetLastError());
+
+    ret = DuplicateToken(process_token, SecurityImpersonation, &token);
+    ok(ret, "DuplicateToken failed with error %d\n", GetLastError());
+
+    /* groups */
+    ret = GetTokenInformation(token, TokenGroups, NULL, 0, &size);
+    ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+        "GetTokenInformation(TokenGroups) %s with error %d\n",
+        ret ? "succeeded" : "failed", GetLastError());
+    token_groups = HeapAlloc(GetProcessHeap(), 0, size);
+    ret = GetTokenInformation(token, TokenGroups, token_groups, size, &size);
+    ok(ret, "GetTokenInformation(TokenGroups) failed with error %d\n", GetLastError());
+
+    for (i = 0; i < token_groups->GroupCount; i++)
+    {
+        if (token_groups->Groups[i].Attributes & SE_GROUP_ENABLED)
+            break;
+    }
+
+    if (i == token_groups->GroupCount)
+    {
+        HeapFree(GetProcessHeap(), 0, token_groups);
+        CloseHandle(token);
+        skip("user not a member of any group\n");
+        return;
+    }
+
+    ret = pCheckTokenMembership(token, token_groups->Groups[i].Sid, &is_member);
+    ok(ret, "CheckTokenMembership failed with error %d\n", GetLastError());
+    ok(is_member, "CheckTokenMembership should have detected sid as member\n");
+
+    ret = pCheckTokenMembership(NULL, token_groups->Groups[i].Sid, &is_member);
+    ok(ret, "CheckTokenMembership failed with error %d\n", GetLastError());
+    ok(is_member, "CheckTokenMembership should have detected sid as member\n");
+
+    ret = pCheckTokenMembership(process_token, token_groups->Groups[i].Sid, &is_member);
+todo_wine {
+    ok(!ret && GetLastError() == ERROR_NO_IMPERSONATION_TOKEN,
+        "CheckTokenMembership with process token %s with error %d\n",
+        ret ? "succeeded" : "failed", GetLastError());
+    ok(!is_member, "CheckTokenMembership should have cleared is_member\n");
+}
+
+    HeapFree(GetProcessHeap(), 0, token_groups);
+    CloseHandle(token);
+    CloseHandle(process_token);
+}
+
+static void test_EqualSid(void)
+{
+    PSID sid1, sid2;
+    BOOL ret;
+    SID_IDENTIFIER_AUTHORITY SIDAuthWorld = { SECURITY_WORLD_SID_AUTHORITY };
+    SID_IDENTIFIER_AUTHORITY SIDAuthNT = { SECURITY_NT_AUTHORITY };
+
+    SetLastError(0xdeadbeef);
+    ret = AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID,
+        DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &sid1);
+    if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+    {
+        win_skip("AllocateAndInitializeSid is not implemented\n");
+        return;
+    }
+    ok(ret, "AllocateAndInitializeSid failed with error %d\n", GetLastError());
+    ok(GetLastError() == 0xdeadbeef,
+       "AllocateAndInitializeSid shouldn't have set last error to %d\n",
+       GetLastError());
+
+    ret = AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID,
+        0, 0, 0, 0, 0, 0, 0, &sid2);
+    ok(ret, "AllocateAndInitializeSid failed with error %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = EqualSid(sid1, sid2);
+    ok(!ret, "World and domain admins sids shouldn't have been equal\n");
+    ok(GetLastError() == ERROR_SUCCESS ||
+       broken(GetLastError() == 0xdeadbeef), /* NT4 */
+       "EqualSid should have set last error to ERROR_SUCCESS instead of %d\n",
+       GetLastError());
+
+    SetLastError(0xdeadbeef);
+    sid2 = FreeSid(sid2);
+    ok(!sid2, "FreeSid should have returned NULL instead of %p\n", sid2);
+    ok(GetLastError() == 0xdeadbeef,
+       "FreeSid shouldn't have set last error to %d\n",
+       GetLastError());
+
+    ret = AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID,
+        DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &sid2);
+    ok(ret, "AllocateAndInitializeSid failed with error %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = EqualSid(sid1, sid2);
+    ok(ret, "Same sids should have been equal\n");
+    ok(GetLastError() == ERROR_SUCCESS ||
+       broken(GetLastError() == 0xdeadbeef), /* NT4 */
+       "EqualSid should have set last error to ERROR_SUCCESS instead of %d\n",
+       GetLastError());
+
+    ((SID *)sid2)->Revision = 2;
+    SetLastError(0xdeadbeef);
+    ret = EqualSid(sid1, sid2);
+    ok(!ret, "EqualSid with invalid sid should have returned FALSE\n");
+    ok(GetLastError() == ERROR_SUCCESS ||
+       broken(GetLastError() == 0xdeadbeef), /* NT4 */
+       "EqualSid should have set last error to ERROR_SUCCESS instead of %d\n",
+       GetLastError());
+    ((SID *)sid2)->Revision = SID_REVISION;
+
+    FreeSid(sid1);
+    FreeSid(sid2);
+}
+
 START_TEST(security)
 {
     init();
@@ -3255,4 +3379,6 @@ START_TEST(security)
     test_acls();
     test_GetSecurityInfo();
     test_GetSidSubAuthority();
+    test_CheckTokenMembership();
+    test_EqualSid();
 }
index 0851384..17f89d1 100644 (file)
@@ -38,6 +38,9 @@ static BOOL (WINAPI *pChangeServiceConfig2A)(SC_HANDLE,DWORD,LPVOID);
 static BOOL (WINAPI *pEnumServicesStatusExA)(SC_HANDLE, SC_ENUM_TYPE, DWORD,
                                              DWORD, LPBYTE, DWORD, LPDWORD,
                                              LPDWORD, LPDWORD, LPCSTR);
+static BOOL (WINAPI *pEnumServicesStatusExW)(SC_HANDLE, SC_ENUM_TYPE, DWORD,
+                                             DWORD, LPBYTE, DWORD, LPDWORD,
+                                             LPDWORD, LPDWORD, LPCWSTR);
 static DWORD (WINAPI *pGetSecurityInfo)(HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
                                         PSID*, PSID*, PACL*, PACL*, PSECURITY_DESCRIPTOR*);
 static BOOL (WINAPI *pQueryServiceConfig2A)(SC_HANDLE,DWORD,LPBYTE,DWORD,LPDWORD);
@@ -51,6 +54,7 @@ static void init_function_pointers(void)
 
     pChangeServiceConfig2A = (void*)GetProcAddress(hadvapi32, "ChangeServiceConfig2A");
     pEnumServicesStatusExA= (void*)GetProcAddress(hadvapi32, "EnumServicesStatusExA");
+    pEnumServicesStatusExW= (void*)GetProcAddress(hadvapi32, "EnumServicesStatusExW");
     pGetSecurityInfo = (void *)GetProcAddress(hadvapi32, "GetSecurityInfo");
     pQueryServiceConfig2A= (void*)GetProcAddress(hadvapi32, "QueryServiceConfig2A");
     pQueryServiceConfig2W= (void*)GetProcAddress(hadvapi32, "QueryServiceConfig2W");
@@ -1030,6 +1034,7 @@ static void test_enum_svc(void)
     SC_HANDLE scm_handle;
     BOOL ret;
     DWORD bufsize, needed, returned, resume;
+    DWORD neededW, returnedW;
     DWORD tempneeded, tempreturned, missing;
     DWORD servicecountactive, servicecountinactive;
     ENUM_SERVICE_STATUS *services;
@@ -1160,6 +1165,12 @@ static void test_enum_svc(void)
        "Expected ERROR_MORE_DATA, got %d\n", GetLastError());
     }
 
+    /* Test to show we get the same needed buffer size for the W-call */
+    neededW = 0xdeadbeef;
+    ret = EnumServicesStatusW(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0,
+                              &neededW, &returnedW, NULL);
+    ok(neededW == needed, "Expected needed buffersize to be the same for A- and W-calls\n");
+
     /* Store the needed bytes */
     tempneeded = needed;
 
@@ -1506,6 +1517,12 @@ static void test_enum_svc(void)
        "Expected ERROR_MORE_DATA, got %d\n", GetLastError());
     }
 
+    /* Test to show we get the same needed buffer size for the W-call */
+    neededW = 0xdeadbeef;
+    ret = pEnumServicesStatusExW(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL,
+                                 NULL, 0, &neededW, &returnedW, NULL, NULL);
+    ok(neededW == needed, "Expected needed buffersize to be the same for A- and W-calls\n");
+
     /* Store the needed bytes */
     tempneeded = needed;