From ae8fa62fc739c0dda71934cd235315ce74d3c46a Mon Sep 17 00:00:00 2001 From: Amine Khaldi Date: Mon, 3 Oct 2011 15:18:15 +0000 Subject: [PATCH] [ADVAPI32_WINETEST] * Sync with Wine 1.3.29. svn path=/trunk/; revision=53961 --- rostests/winetests/advapi32/cred.c | 8 +- rostests/winetests/advapi32/crypt.c | 12 +- rostests/winetests/advapi32/crypt_lmhash.c | 94 +-- rostests/winetests/advapi32/crypt_md4.c | 24 +- rostests/winetests/advapi32/crypt_md5.c | 16 +- rostests/winetests/advapi32/eventlog.c | 15 +- rostests/winetests/advapi32/lsa.c | 4 +- rostests/winetests/advapi32/registry.c | 674 ++++++++++++---- rostests/winetests/advapi32/security.c | 897 ++++++++++++++++++--- rostests/winetests/advapi32/service.c | 418 +++++++--- 10 files changed, 1680 insertions(+), 482 deletions(-) diff --git a/rostests/winetests/advapi32/cred.c b/rostests/winetests/advapi32/cred.c index 6b7fa26654b..c2d9027e1de 100644 --- a/rostests/winetests/advapi32/cred.c +++ b/rostests/winetests/advapi32/cred.c @@ -156,10 +156,10 @@ static void test_CredReadDomainCredentialsA(void) * does not check for NULL output pointers and try to zero them out early */ if(0) { - ok(!pCredReadDomainCredentialsA(&info, 0, NULL, &creds) && - GetLastError() == ERROR_INVALID_PARAMETER, "!\n"); - ok(!pCredReadDomainCredentialsA(&info, 0, &count, NULL) && - GetLastError() == ERROR_INVALID_PARAMETER, "!\n"); + ret = pCredReadDomainCredentialsA(&info, 0, NULL, &creds); + ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "!\n"); + ret = pCredReadDomainCredentialsA(&info, 0, &count, NULL); + ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "!\n"); } SetLastError(0xdeadbeef); diff --git a/rostests/winetests/advapi32/crypt.c b/rostests/winetests/advapi32/crypt.c index 6cfbbbf8254..11802757602 100644 --- a/rostests/winetests/advapi32/crypt.c +++ b/rostests/winetests/advapi32/crypt.c @@ -238,6 +238,9 @@ static void test_incorrect_api_usage(void) if (!result) return; pCryptDestroyHash(hHash); + result = pCryptGenKey(0, CALG_RC4, 0, &hKey); + ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%d\n", GetLastError()); + result = pCryptGenKey(hProv, CALG_RC4, 0, &hKey); ok (result, "%d\n", GetLastError()); if (!result) return; @@ -414,6 +417,7 @@ static void test_verify_sig(void) CRYPT_NEWKEYSET); if (!ret && GetLastError() == NTE_EXISTS) ret = pCryptAcquireContextA(&prov, szKeySet, NULL, PROV_RSA_FULL, 0); + ok(ret, "CryptAcquireContextA failed: %08x\n", GetLastError()); ret = pCryptImportKey(prov, (LPBYTE)privKey, sizeof(privKey), 0, 0, &key); ok(ret, "CryptImportKey failed: %08x\n", GetLastError()); ret = pCryptCreateHash(prov, CALG_MD5, 0, 0, &hash); @@ -898,6 +902,7 @@ static void test_set_provider_ex(void) /* call CryptGetDefaultProvider to see if they match */ result = pCryptGetDefaultProviderA(PROV_RSA_FULL, NULL, CRYPT_MACHINE_DEFAULT, NULL, &cbProvName); + ok(result, "%d\n", GetLastError()); if (!(pszProvName = LocalAlloc(LMEM_ZEROINIT, cbProvName))) goto reset; @@ -945,7 +950,8 @@ static void test_machine_guid(void) r); /* Create and release a provider */ ret = pCryptAcquireContextA(&hCryptProv, szKeySet, NULL, PROV_RSA_FULL, 0); - ok(ret, "CryptAcquireContextA failed: %08x\n", GetLastError()); + ok(ret || broken(!ret && GetLastError() == NTE_KEYSET_ENTRY_BAD /* NT4 */), + "CryptAcquireContextA failed: %08x\n", GetLastError()); pCryptReleaseContext(hCryptProv, 0); if (restoreGuid) @@ -992,7 +998,6 @@ static void test_rc2_keylen(void) 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()); @@ -1040,7 +1045,6 @@ static void test_rc2_keylen(void) 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()); @@ -1065,7 +1069,6 @@ static void test_rc2_keylen(void) 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()); @@ -1080,7 +1083,6 @@ static void test_rc2_keylen(void) 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()); diff --git a/rostests/winetests/advapi32/crypt_lmhash.c b/rostests/winetests/advapi32/crypt_lmhash.c index 253090a970e..a6e812e54a9 100644 --- a/rostests/winetests/advapi32/crypt_lmhash.c +++ b/rostests/winetests/advapi32/crypt_lmhash.c @@ -34,56 +34,44 @@ struct ustring { unsigned char *Buffer; }; -typedef NTSTATUS (WINAPI *fnSystemFunction001)(const BYTE *, const BYTE *, LPBYTE); -typedef NTSTATUS (WINAPI *fnSystemFunction002)(const BYTE *, const BYTE *, LPBYTE); -typedef NTSTATUS (WINAPI *fnSystemFunction003)(const BYTE *, LPBYTE); -typedef NTSTATUS (WINAPI *fnSystemFunction004)(const struct ustring *, const struct ustring *, struct ustring *); -typedef NTSTATUS (WINAPI *fnSystemFunction005)(const struct ustring *, const struct ustring *, struct ustring *); -typedef VOID (WINAPI *fnSystemFunction006)( PCSTR passwd, PSTR lmhash ); -typedef NTSTATUS (WINAPI *fnSystemFunction008)(const BYTE *, const BYTE *, LPBYTE); -typedef NTSTATUS (WINAPI *fnSystemFunction009)(const BYTE *, const BYTE *, LPBYTE); typedef int (WINAPI *descrypt)(unsigned char *, unsigned char *, unsigned char *); -typedef NTSTATUS (WINAPI *fnSystemFunction030)(const void*, const void*); -typedef NTSTATUS (WINAPI *fnSystemFunction032)(struct ustring *, const struct ustring *); - -fnSystemFunction001 pSystemFunction001; -fnSystemFunction002 pSystemFunction002; -fnSystemFunction003 pSystemFunction003; -fnSystemFunction004 pSystemFunction004; -fnSystemFunction004 pSystemFunction005; -fnSystemFunction006 pSystemFunction006; -fnSystemFunction008 pSystemFunction008; -fnSystemFunction008 pSystemFunction009; +static NTSTATUS (WINAPI *pSystemFunction001)(const BYTE *, const BYTE *, LPBYTE); +static NTSTATUS (WINAPI *pSystemFunction002)(const BYTE *, const BYTE *, LPBYTE); +static NTSTATUS (WINAPI *pSystemFunction003)(const BYTE *, LPBYTE); +static NTSTATUS (WINAPI *pSystemFunction004)(const struct ustring *, const struct ustring *, struct ustring *); +static NTSTATUS (WINAPI *pSystemFunction005)(const struct ustring *, const struct ustring *, struct ustring *); +static VOID (WINAPI *pSystemFunction006)( PCSTR passwd, PSTR lmhash ); +static NTSTATUS (WINAPI *pSystemFunction008)(const BYTE *, const BYTE *, LPBYTE); +static NTSTATUS (WINAPI *pSystemFunction009)(const BYTE *, const BYTE *, LPBYTE); +static NTSTATUS (WINAPI *pSystemFunction032)(struct ustring *, const struct ustring *); /* encrypt two blocks */ -descrypt pSystemFunction012; -descrypt pSystemFunction014; -descrypt pSystemFunction016; -descrypt pSystemFunction018; -descrypt pSystemFunction020; -descrypt pSystemFunction022; +static descrypt pSystemFunction012; +static descrypt pSystemFunction014; +static descrypt pSystemFunction016; +static descrypt pSystemFunction018; +static descrypt pSystemFunction020; +static descrypt pSystemFunction022; /* decrypt two blocks */ -descrypt pSystemFunction013; -descrypt pSystemFunction015; -descrypt pSystemFunction017; -descrypt pSystemFunction019; -descrypt pSystemFunction021; -descrypt pSystemFunction023; +static descrypt pSystemFunction013; +static descrypt pSystemFunction015; +static descrypt pSystemFunction017; +static descrypt pSystemFunction019; +static descrypt pSystemFunction021; +static descrypt pSystemFunction023; /* encrypt two blocks with a 32bit key */ -descrypt pSystemFunction024; -descrypt pSystemFunction025; +static descrypt pSystemFunction024; +static descrypt pSystemFunction025; /* decrypt two blocks with a 32bit key */ -descrypt pSystemFunction026; -descrypt pSystemFunction027; +static descrypt pSystemFunction026; +static descrypt pSystemFunction027; typedef int (WINAPI *memcmpfunc)(unsigned char *, unsigned char *); -memcmpfunc pSystemFunction030; -memcmpfunc pSystemFunction031; - -fnSystemFunction032 pSystemFunction032; +static memcmpfunc pSystemFunction030; +static memcmpfunc pSystemFunction031; static void test_SystemFunction006(void) { @@ -223,6 +211,7 @@ static void test_SystemFunction003(void) memset(output, 0, sizeof output); r = pSystemFunction002(data, key, output); + ok(r == STATUS_SUCCESS, "function failed\n"); ok( !memcmp(exp2, output, sizeof output), "decrypted message wrong\n"); } @@ -296,16 +285,19 @@ static void test_SystemFunction004(void) memset(output, 0, sizeof output); r = pSystemFunction002(out.Buffer, key.Buffer, output); + ok(r == STATUS_SUCCESS, "function failed\n"); ok(((unsigned int*)output)[0] == in.Length, "crypted length wrong\n"); ok(((unsigned int*)output)[1] == 1, "crypted value wrong\n"); memset(output, 0, sizeof output); r = pSystemFunction002(out.Buffer+8, key.Buffer, output); + ok(r == STATUS_SUCCESS, "function failed\n"); ok(!memcmp(output, inbuf, sizeof output), "crypted data wrong\n"); memset(output, 0, sizeof output); r = pSystemFunction002(out.Buffer+16, key.Buffer, output); + ok(r == STATUS_SUCCESS, "function failed\n"); ok(!memcmp(output, inbuf, sizeof output), "crypted data wrong\n"); } @@ -508,12 +500,6 @@ static void test_memcmpfunc(memcmpfunc fn) return; } - if (0) - { - /* crashes */ - r = fn(NULL, NULL); - } - memset(arg1, 0, sizeof arg1); memset(arg2, 0, sizeof arg2); arg1[0x10] = 1; @@ -545,49 +531,49 @@ START_TEST(crypt_lmhash) { HMODULE module = GetModuleHandleA("advapi32.dll"); - pSystemFunction001 = (fnSystemFunction001)GetProcAddress( module, "SystemFunction001" ); + pSystemFunction001 = (void *)GetProcAddress( module, "SystemFunction001" ); if (pSystemFunction001) test_SystemFunction001(); else win_skip("SystemFunction001 is not available\n"); - pSystemFunction002 = (fnSystemFunction002)GetProcAddress( module, "SystemFunction002" ); + pSystemFunction002 = (void *)GetProcAddress( module, "SystemFunction002" ); if (pSystemFunction002) test_SystemFunction002(); else win_skip("SystemFunction002 is not available\n"); - pSystemFunction003 = (fnSystemFunction003)GetProcAddress( module, "SystemFunction003" ); + pSystemFunction003 = (void *)GetProcAddress( module, "SystemFunction003" ); if (pSystemFunction003) test_SystemFunction003(); else win_skip("SystemFunction002 is not available\n"); - pSystemFunction004 = (fnSystemFunction004)GetProcAddress( module, "SystemFunction004" ); + pSystemFunction004 = (void *)GetProcAddress( module, "SystemFunction004" ); if (pSystemFunction004) test_SystemFunction004(); else win_skip("SystemFunction004 is not available\n"); - pSystemFunction005 = (fnSystemFunction005)GetProcAddress( module, "SystemFunction005" ); + pSystemFunction005 = (void *)GetProcAddress( module, "SystemFunction005" ); if (pSystemFunction005) test_SystemFunction005(); else win_skip("SystemFunction005 is not available\n"); - pSystemFunction006 = (fnSystemFunction006)GetProcAddress( module, "SystemFunction006" ); + pSystemFunction006 = (void *)GetProcAddress( module, "SystemFunction006" ); if (pSystemFunction006) test_SystemFunction006(); else win_skip("SystemFunction006 is not available\n"); - pSystemFunction008 = (fnSystemFunction008)GetProcAddress( module, "SystemFunction008" ); + pSystemFunction008 = (void *)GetProcAddress( module, "SystemFunction008" ); if (pSystemFunction008) test_SystemFunction008(); else win_skip("SystemFunction008 is not available\n"); - pSystemFunction009 = (fnSystemFunction009)GetProcAddress( module, "SystemFunction009" ); + pSystemFunction009 = (void *)GetProcAddress( module, "SystemFunction009" ); if (pSystemFunction009) test_SystemFunction009(); else @@ -641,7 +627,7 @@ START_TEST(crypt_lmhash) test_memcmpfunc(pSystemFunction030); test_memcmpfunc(pSystemFunction031); - pSystemFunction032 = (fnSystemFunction032)GetProcAddress( module, "SystemFunction032" ); + pSystemFunction032 = (void *)GetProcAddress( module, "SystemFunction032" ); if (pSystemFunction032) test_SystemFunction032(); else diff --git a/rostests/winetests/advapi32/crypt_md4.c b/rostests/winetests/advapi32/crypt_md4.c index e866910d970..e67becb1f03 100644 --- a/rostests/winetests/advapi32/crypt_md4.c +++ b/rostests/winetests/advapi32/crypt_md4.c @@ -36,18 +36,14 @@ typedef struct unsigned char digest[16]; } MD4_CTX; -typedef VOID (WINAPI *fnMD4Init)( MD4_CTX *ctx ); -typedef VOID (WINAPI *fnMD4Update)( MD4_CTX *ctx, const unsigned char *src, const int len ); -typedef VOID (WINAPI *fnMD4Final)( MD4_CTX *ctx ); -typedef int (WINAPI *fnSystemFunction007)(const UNICODE_STRING *, LPBYTE); +static VOID (WINAPI *pMD4Init)( MD4_CTX *ctx ); +static VOID (WINAPI *pMD4Update)( MD4_CTX *ctx, const unsigned char *src, const int len ); +static VOID (WINAPI *pMD4Final)( MD4_CTX *ctx ); +static int (WINAPI *pSystemFunction007)(const UNICODE_STRING *, LPBYTE); typedef int (WINAPI *md4hashfunc)(LPVOID, const LPBYTE, LPBYTE); -fnMD4Init pMD4Init; -fnMD4Update pMD4Update; -fnMD4Final pMD4Final; -fnSystemFunction007 pSystemFunction007; -md4hashfunc pSystemFunction010; -md4hashfunc pSystemFunction011; +static md4hashfunc pSystemFunction010; +static md4hashfunc pSystemFunction011; #define ctxcmp( a, b ) memcmp( a, b, FIELD_OFFSET( MD4_CTX, in ) ) @@ -149,16 +145,16 @@ START_TEST(crypt_md4) module = GetModuleHandleA( "advapi32.dll" ); - pMD4Init = (fnMD4Init)GetProcAddress( module, "MD4Init" ); - pMD4Update = (fnMD4Update)GetProcAddress( module, "MD4Update" ); - pMD4Final = (fnMD4Final)GetProcAddress( module, "MD4Final" ); + pMD4Init = (void *)GetProcAddress( module, "MD4Init" ); + pMD4Update = (void *)GetProcAddress( module, "MD4Update" ); + pMD4Final = (void *)GetProcAddress( module, "MD4Final" ); if (pMD4Init && pMD4Update && pMD4Final) test_md4_ctx(); else win_skip("MD4Init and/or MD4Update and/or MD4Final are not available\n"); - pSystemFunction007 = (fnSystemFunction007)GetProcAddress( module, "SystemFunction007" ); + pSystemFunction007 = (void *)GetProcAddress( module, "SystemFunction007" ); if (pSystemFunction007) test_SystemFunction007(); else diff --git a/rostests/winetests/advapi32/crypt_md5.c b/rostests/winetests/advapi32/crypt_md5.c index 9136dfbc28c..4cf88688ea6 100644 --- a/rostests/winetests/advapi32/crypt_md5.c +++ b/rostests/winetests/advapi32/crypt_md5.c @@ -33,13 +33,9 @@ typedef struct unsigned char digest[16]; } MD5_CTX; -typedef VOID (WINAPI *fnMD5Init)( MD5_CTX *ctx ); -typedef VOID (WINAPI *fnMD5Update)( MD5_CTX *ctx, const unsigned char *src, const int len ); -typedef VOID (WINAPI *fnMD5Final)( MD5_CTX *ctx ); - -fnMD5Init pMD5Init; -fnMD5Update pMD5Update; -fnMD5Final pMD5Final; +static VOID (WINAPI *pMD5Init)( MD5_CTX *ctx ); +static VOID (WINAPI *pMD5Update)( MD5_CTX *ctx, const unsigned char *src, const int len ); +static VOID (WINAPI *pMD5Final)( MD5_CTX *ctx ); #define ctxcmp( a, b ) memcmp( a, b, FIELD_OFFSET( MD5_CTX, in ) ) @@ -79,9 +75,9 @@ static void test_md5_ctx(void) module = GetModuleHandleA("advapi32.dll"); - pMD5Init = (fnMD5Init)GetProcAddress( module, "MD5Init" ); - pMD5Update = (fnMD5Update)GetProcAddress( module, "MD5Update" ); - pMD5Final = (fnMD5Final)GetProcAddress( module, "MD5Final" ); + pMD5Init = (void *)GetProcAddress( module, "MD5Init" ); + pMD5Update = (void *)GetProcAddress( module, "MD5Update" ); + pMD5Final = (void *)GetProcAddress( module, "MD5Final" ); if (!pMD5Init || !pMD5Update || !pMD5Final) { diff --git a/rostests/winetests/advapi32/eventlog.c b/rostests/winetests/advapi32/eventlog.c index ff3f658c184..543b41cbaf8 100644 --- a/rostests/winetests/advapi32/eventlog.c +++ b/rostests/winetests/advapi32/eventlog.c @@ -633,7 +633,7 @@ static BOOL create_new_eventlog(void) HKEY key, eventkey; BOOL bret = FALSE; LONG lret; - int i; + DWORD i; /* First create our eventlog */ lret = RegOpenKeyA(HKEY_LOCAL_MACHINE, eventlogsvc, &key); @@ -711,7 +711,7 @@ static void test_readwrite(void) DWORD sidsize, count; BOOL ret, sidavailable; BOOL on_vista = FALSE; /* Used to indicate Vista, W2K8 or Win7 */ - int i; + DWORD i; char *localcomputer = NULL; DWORD size; @@ -826,21 +826,24 @@ static void test_readwrite(void) 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); + ok(ret, "Expected ReportEvent success : %d\n", GetLastError()); count = 0xdeadbeef; + SetLastError(0xdeadbeef); ret = GetNumberOfEventLogRecords(handle, &count); - ok(ret, "Expected success\n"); + ok(ret, "Expected GetNumberOfEventLogRecords success : %d\n", GetLastError()); 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(ret, "Expected GetOldestEventLogRecord success : %d\n", GetLastError()); 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; + SetLastError(0xdeadbeef); if (i % 2) ret = CloseEventLog(handle); else @@ -1092,7 +1095,7 @@ static void cleanup_eventlog(void) BOOL bret; LONG lret; HKEY key; - int i; + DWORD i; char winesvc[MAX_PATH]; /* Delete the registry tree */ @@ -1144,6 +1147,6 @@ START_TEST(eventlog) { test_readwrite(); test_autocreation(); + cleanup_eventlog(); } - cleanup_eventlog(); } diff --git a/rostests/winetests/advapi32/lsa.c b/rostests/winetests/advapi32/lsa.c index efb75bbe2ba..e5b84c7526e 100644 --- a/rostests/winetests/advapi32/lsa.c +++ b/rostests/winetests/advapi32/lsa.c @@ -249,13 +249,13 @@ static void test_LsaLookupNames2(void) if (!pLsaLookupNames2) { - win_skip("LsaLookupNames2 not avaliable\n"); + win_skip("LsaLookupNames2 not available\n"); return; } if (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH) { - skip("Non-english locale (skipping LsaLookupNames2 tests)\n"); + skip("Non-English locale (skipping LsaLookupNames2 tests)\n"); return; } diff --git a/rostests/winetests/advapi32/registry.c b/rostests/winetests/advapi32/registry.c index 30dda7e827e..1021fc740d4 100644 --- a/rostests/winetests/advapi32/registry.c +++ b/rostests/winetests/advapi32/registry.c @@ -2,6 +2,7 @@ * Unit tests for registry functions * * Copyright (c) 2002 Alexandre Julliard + * Copyright (c) 2010 André Hentschel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,6 +29,7 @@ #include "winreg.h" #include "winsvc.h" #include "winerror.h" +#include "aclapi.h" static HKEY hkey_main; static DWORD GLE; @@ -187,8 +189,7 @@ static void _test_hkey_main_Value_A(int line, LPCSTR name, LPCSTR string, str_byte_len = (string ? lstrlenA(string) : 0) + 1; lok(type == REG_SZ, "RegQueryValueExA/1 returned type %d\n", type); - lok(cbData == full_byte_len || cbData == str_byte_len /* Win9x */, - "cbData=%d instead of %d or %d\n", cbData, full_byte_len, str_byte_len); + lok(cbData == full_byte_len, "cbData=%d instead of %d or %d\n", cbData, full_byte_len, str_byte_len); value = HeapAlloc(GetProcessHeap(), 0, cbData+1); memset(value, 0xbd, cbData+1); @@ -199,8 +200,7 @@ static void _test_hkey_main_Value_A(int line, LPCSTR name, LPCSTR string, if (!string) { /* When cbData == 0, RegQueryValueExA() should not modify the buffer */ - lok(*value == 0xbd || (cbData == 1 && *value == '\0') /* Win9x */, - "RegQueryValueExA overflowed: cbData=%u *value=%02x\n", cbData, *value); + lok(*value == 0xbd, "RegQueryValueExA overflowed: cbData=%u *value=%02x\n", cbData, *value); } else { @@ -301,19 +301,13 @@ static void test_set_value(void) /* only REG_SZ is supported on NT*/ ret = RegSetValueA(hkey_main, NULL, REG_BINARY, string2A, sizeof(string2A)); - /* NT: ERROR_INVALID_PARAMETER, 9x: ERROR_SUCCESS */ - ok(ret == ERROR_INVALID_PARAMETER || broken(ret == ERROR_SUCCESS), - "got %d (expected ERROR_INVALID_PARAMETER or ERROR_SUCCESS)\n", ret); + ok(ret == ERROR_INVALID_PARAMETER, "got %d (expected ERROR_INVALID_PARAMETER)\n", ret); ret = RegSetValueA(hkey_main, NULL, REG_EXPAND_SZ, string2A, sizeof(string2A)); - /* NT: ERROR_INVALID_PARAMETER, 9x: ERROR_SUCCESS */ - ok(ret == ERROR_INVALID_PARAMETER || broken(ret == ERROR_SUCCESS), - "got %d (expected ERROR_INVALID_PARAMETER or ERROR_SUCCESS)\n", ret); + ok(ret == ERROR_INVALID_PARAMETER, "got %d (expected ERROR_INVALID_PARAMETER)\n", ret); ret = RegSetValueA(hkey_main, NULL, REG_MULTI_SZ, string2A, sizeof(string2A)); - /* NT: ERROR_INVALID_PARAMETER, 9x: ERROR_SUCCESS */ - ok(ret == ERROR_INVALID_PARAMETER || broken(ret == ERROR_SUCCESS), - "got %d (expected ERROR_INVALID_PARAMETER or ERROR_SUCCESS)\n", ret); + ok(ret == ERROR_INVALID_PARAMETER, "got %d (expected ERROR_INVALID_PARAMETER)\n", ret); /* Test RegSetValueExA with a 'zero-byte' string (as Office 2003 does). * Surprisingly enough we're supposed to get zero bytes out of it. @@ -347,9 +341,6 @@ static void test_set_value(void) test_hkey_main_Value_A(name2A, string2A, sizeof(string2A)); test_hkey_main_Value_W(name2W, string2W, sizeof(string2W)); - /* 9x doesn't support W-calls, so don't test them then */ - if(GLE == ERROR_CALL_NOT_IMPLEMENTED) return; - if (0) { /* Crashes on NT4, Windows 2000 and XP SP1 */ @@ -462,7 +453,7 @@ static void test_enum_value(void) res = RegEnumValueA( test_key, 0, value, &val_count, NULL, &type, (LPBYTE)data, &data_count ); ok( res == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", res ); ok( val_count == 2, "val_count set to %d\n", val_count ); - ok( data_count == 7, "data_count set to %d instead of 7\n", data_count ); + ok( data_count == 7 || broken( data_count == 8 ), "data_count set to %d instead of 7\n", data_count ); ok( type == REG_SZ, "type %d is not REG_SZ\n", type ); ok( !strcmp( value, "xxxxxxxxxx" ), "value set to '%s'\n", value ); ok( !strcmp( data, "xxxxxxxxxx" ), "data set to '%s'\n", data ); @@ -475,14 +466,13 @@ static void test_enum_value(void) strcpy( data, "xxxxxxxxxx" ); res = RegEnumValueA( test_key, 0, value, &val_count, NULL, &type, (LPBYTE)data, &data_count ); ok( res == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", res ); - /* Win9x returns 2 as specified by MSDN but NT returns 3... */ - ok( val_count == 2 || val_count == 3, "val_count set to %d\n", val_count ); - ok( data_count == 7, "data_count set to %d instead of 7\n", data_count ); + ok( val_count == 3, "val_count set to %d\n", val_count ); + ok( data_count == 7 || broken( data_count == 8 ), "data_count set to %d instead of 7\n", data_count ); ok( type == REG_SZ, "type %d is not REG_SZ\n", type ); /* v5.1.2600.0 (XP Home and Professional) does not touch value or data in this case */ ok( !strcmp( value, "Te" ) || !strcmp( value, "xxxxxxxxxx" ), "value set to '%s' instead of 'Te' or 'xxxxxxxxxx'\n", value ); - ok( !strcmp( data, "foobar" ) || !strcmp( data, "xxxxxxx" ), + ok( !strcmp( data, "foobar" ) || !strcmp( data, "xxxxxxx" ) || broken( !strcmp( data, "xxxxxxxx" ) && data_count == 8 ), "data set to '%s' instead of 'foobar' or 'xxxxxxx'\n", data ); /* overflow empty name */ @@ -494,11 +484,11 @@ static void test_enum_value(void) res = RegEnumValueA( test_key, 0, value, &val_count, NULL, &type, (LPBYTE)data, &data_count ); ok( res == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", res ); ok( val_count == 0, "val_count set to %d\n", val_count ); - ok( data_count == 7, "data_count set to %d instead of 7\n", data_count ); + ok( data_count == 7 || broken( data_count == 8 ), "data_count set to %d instead of 7\n", data_count ); ok( type == REG_SZ, "type %d is not REG_SZ\n", type ); ok( !strcmp( value, "xxxxxxxxxx" ), "value set to '%s'\n", value ); /* v5.1.2600.0 (XP Home and Professional) does not touch data in this case */ - ok( !strcmp( data, "foobar" ) || !strcmp( data, "xxxxxxx" ), + ok( !strcmp( data, "foobar" ) || !strcmp( data, "xxxxxxx" ) || broken( !strcmp( data, "xxxxxxxx" ) && data_count == 8 ), "data set to '%s' instead of 'foobar' or 'xxxxxxx'\n", data ); /* overflow data */ @@ -617,20 +607,7 @@ static void test_query_value_ex(void) size = 0xdeadbeef; ret = RegQueryValueExA(HKEY_CLASSES_ROOT, "Nonexistent Value", NULL, &type, NULL, &size); ok(ret == ERROR_FILE_NOT_FOUND, "expected ERROR_FILE_NOT_FOUND, got %d\n", ret); - /* the type parameter is cleared on Win9x, but is set to a random value on - * NT, so don't do that test there. The size parameter is left untouched on Win9x - * but cleared on NT+, this can be tested on all platforms. - */ - if (GetVersion() & 0x80000000) - { - ok(type == 0, "type should have been set to 0 instead of 0x%x\n", type); - ok(size == 0xdeadbeef, "size should have been left untouched (0xdeadbeef)\n"); - } - else - { - trace("test_query_value_ex: type set to: 0x%08x\n", type); - ok(size == 0, "size should have been set to 0 instead of %d\n", size); - } + ok(size == 0, "size should have been set to 0 instead of %d\n", size); size = sizeof(buffer); ret = RegQueryValueExA(HKEY_CLASSES_ROOT, "Nonexistent Value", NULL, &type, buffer, &size); @@ -850,6 +827,14 @@ static void test_reg_open_key(void) DWORD ret = 0; HKEY hkResult = NULL; HKEY hkPreserve = NULL; + HKEY hkRoot64 = NULL; + HKEY hkRoot32 = NULL; + BOOL bRet; + SID_IDENTIFIER_AUTHORITY sid_authority = {SECURITY_WORLD_SID_AUTHORITY}; + PSID world_sid; + EXPLICIT_ACCESSA access; + PACL key_acl; + SECURITY_DESCRIPTOR *sd; /* successful open */ ret = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Test", &hkResult); @@ -857,53 +842,48 @@ static void test_reg_open_key(void) ok(hkResult != NULL, "expected hkResult != NULL\n"); hkPreserve = hkResult; - /* these tests fail on Win9x, but we want to be compatible with NT, so - * run them if we can */ - if (!(GetVersion() & 0x80000000)) - { - /* open same key twice */ - ret = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Test", &hkResult); - ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret); - ok(hkResult != hkPreserve, "epxected hkResult != hkPreserve\n"); - ok(hkResult != NULL, "hkResult != NULL\n"); - RegCloseKey(hkResult); - - /* open nonexistent key - * check that hkResult is set to NULL - */ - hkResult = hkPreserve; - ret = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Nonexistent", &hkResult); - ok(ret == ERROR_FILE_NOT_FOUND, "expected ERROR_FILE_NOT_FOUND, got %d\n", ret); - ok(hkResult == NULL, "expected hkResult == NULL\n"); - - /* open the same nonexistent key again to make sure the key wasn't created */ - hkResult = hkPreserve; - ret = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Nonexistent", &hkResult); - ok(ret == ERROR_FILE_NOT_FOUND, "expected ERROR_FILE_NOT_FOUND, got %d\n", ret); - ok(hkResult == NULL, "expected hkResult == NULL\n"); - - /* send in NULL lpSubKey - * check that hkResult receives the value of hKey - */ - hkResult = hkPreserve; - ret = RegOpenKeyA(HKEY_CURRENT_USER, NULL, &hkResult); - ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret); - ok(hkResult == HKEY_CURRENT_USER, "expected hkResult == HKEY_CURRENT_USER\n"); - - /* send empty-string in lpSubKey */ - hkResult = hkPreserve; - ret = RegOpenKeyA(HKEY_CURRENT_USER, "", &hkResult); - ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret); - ok(hkResult == HKEY_CURRENT_USER, "expected hkResult == HKEY_CURRENT_USER\n"); - - /* send in NULL lpSubKey and NULL hKey - * hkResult is set to NULL - */ - hkResult = hkPreserve; - ret = RegOpenKeyA(NULL, NULL, &hkResult); - ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret); - ok(hkResult == NULL, "expected hkResult == NULL\n"); - } + /* open same key twice */ + ret = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Test", &hkResult); + ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret); + ok(hkResult != hkPreserve, "expected hkResult != hkPreserve\n"); + ok(hkResult != NULL, "hkResult != NULL\n"); + RegCloseKey(hkResult); + + /* open nonexistent key + * check that hkResult is set to NULL + */ + hkResult = hkPreserve; + ret = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Nonexistent", &hkResult); + ok(ret == ERROR_FILE_NOT_FOUND, "expected ERROR_FILE_NOT_FOUND, got %d\n", ret); + ok(hkResult == NULL, "expected hkResult == NULL\n"); + + /* open the same nonexistent key again to make sure the key wasn't created */ + hkResult = hkPreserve; + ret = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Nonexistent", &hkResult); + ok(ret == ERROR_FILE_NOT_FOUND, "expected ERROR_FILE_NOT_FOUND, got %d\n", ret); + ok(hkResult == NULL, "expected hkResult == NULL\n"); + + /* send in NULL lpSubKey + * check that hkResult receives the value of hKey + */ + hkResult = hkPreserve; + ret = RegOpenKeyA(HKEY_CURRENT_USER, NULL, &hkResult); + ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret); + ok(hkResult == HKEY_CURRENT_USER, "expected hkResult == HKEY_CURRENT_USER\n"); + + /* send empty-string in lpSubKey */ + hkResult = hkPreserve; + ret = RegOpenKeyA(HKEY_CURRENT_USER, "", &hkResult); + ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret); + ok(hkResult == HKEY_CURRENT_USER, "expected hkResult == HKEY_CURRENT_USER\n"); + + /* send in NULL lpSubKey and NULL hKey + * hkResult is set to NULL + */ + hkResult = hkPreserve; + ret = RegOpenKeyA(NULL, NULL, &hkResult); + ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret); + ok(hkResult == NULL, "expected hkResult == NULL\n"); /* only send NULL hKey * the value of hkResult remains unchanged @@ -928,7 +908,6 @@ static void test_reg_open_key(void) /* beginning backslash character */ ret = RegOpenKeyA(HKEY_CURRENT_USER, "\\Software\\Wine\\Test", &hkResult); ok(ret == ERROR_BAD_PATHNAME || /* NT/2k/XP */ - ret == ERROR_FILE_NOT_FOUND || /* Win9x,ME */ broken(ret == ERROR_SUCCESS), /* wow64 */ "expected ERROR_BAD_PATHNAME or ERROR_FILE_NOT_FOUND, got %d\n", ret); if (!ret) RegCloseKey(hkResult); @@ -936,9 +915,8 @@ static void test_reg_open_key(void) hkResult = NULL; ret = RegOpenKeyExA(HKEY_CLASSES_ROOT, "\\clsid", 0, KEY_QUERY_VALUE, &hkResult); ok(ret == ERROR_SUCCESS || /* 2k/XP */ - ret == ERROR_BAD_PATHNAME || /* NT */ - ret == ERROR_FILE_NOT_FOUND /* Win9x,ME */ - , "expected ERROR_SUCCESS, ERROR_BAD_PATHNAME or ERROR_FILE_NOT_FOUND, got %d\n", ret); + ret == ERROR_BAD_PATHNAME, /* NT */ + "expected ERROR_SUCCESS, ERROR_BAD_PATHNAME or ERROR_FILE_NOT_FOUND, got %d\n", ret); RegCloseKey(hkResult); /* WOW64 flags */ @@ -953,12 +931,98 @@ static void test_reg_open_key(void) ok((ret == ERROR_SUCCESS && hkResult != NULL) || broken(ret == ERROR_ACCESS_DENIED /* NT4, win2k */), "RegOpenKeyEx with KEY_WOW64_64KEY failed (err=%u)\n", ret); RegCloseKey(hkResult); + + /* Try using WOW64 flags when opening a key with a DACL set to verify that + * the registry access check is performed correctly. Redirection isn't + * being tested, so the tests don't care about whether the process is + * running under WOW64. */ + if (!pIsWow64Process) + { + win_skip("WOW64 flags are not recognized\n"); + return; + } + + ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, NULL, 0, + KEY_WOW64_32KEY | KEY_ALL_ACCESS, NULL, &hkRoot32, NULL); + ok(ret == ERROR_SUCCESS && hkRoot32 != NULL, + "RegCreateKeyEx with KEY_WOW64_32KEY failed (err=%u)\n", ret); + + ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, NULL, 0, + KEY_WOW64_64KEY | KEY_ALL_ACCESS, NULL, &hkRoot64, NULL); + ok(ret == ERROR_SUCCESS && hkRoot64 != NULL, + "RegCreateKeyEx with KEY_WOW64_64KEY failed (err=%u)\n", ret); + + bRet = AllocateAndInitializeSid(&sid_authority, 1, SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, &world_sid); + ok(bRet == TRUE, + "Expected AllocateAndInitializeSid to return TRUE, got %d, last error %u\n", bRet, GetLastError()); + + access.grfAccessPermissions = GENERIC_ALL | STANDARD_RIGHTS_ALL; + access.grfAccessMode = SET_ACCESS; + access.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; + access.Trustee.pMultipleTrustee = NULL; + access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + access.Trustee.TrusteeForm = TRUSTEE_IS_SID; + access.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + access.Trustee.ptstrName = (char *)world_sid; + + ret = SetEntriesInAclA(1, &access, NULL, &key_acl); + ok(ret == ERROR_SUCCESS, + "Expected SetEntriesInAclA to return ERROR_SUCCESS, got %u, last error %u\n", ret, GetLastError()); + + sd = HeapAlloc(GetProcessHeap(), 0, SECURITY_DESCRIPTOR_MIN_LENGTH); + bRet = InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION); + ok(bRet == TRUE, + "Expected InitializeSecurityDescriptor to return TRUE, got %d, last error %u\n", bRet, GetLastError()); + + bRet = SetSecurityDescriptorDacl(sd, TRUE, key_acl, FALSE); + ok(bRet == TRUE, + "Expected SetSecurityDescriptorDacl to return TRUE, got %d, last error %u\n", bRet, GetLastError()); + + /* The "sanctioned" methods of setting a registry ACL aren't implemented in Wine. */ + bRet = SetKernelObjectSecurity(hkRoot64, DACL_SECURITY_INFORMATION, sd); + ok(bRet == TRUE, + "Expected SetKernelObjectSecurity to return TRUE, got %d, last error %u\n", bRet, GetLastError()); + + bRet = SetKernelObjectSecurity(hkRoot32, DACL_SECURITY_INFORMATION, sd); + ok(bRet == TRUE, + "Expected SetKernelObjectSecurity to return TRUE, got %d, last error %u\n", bRet, GetLastError()); + + hkResult = NULL; + ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, KEY_WOW64_64KEY | KEY_READ, &hkResult); + ok(ret == ERROR_SUCCESS && hkResult != NULL, + "RegOpenKeyEx with KEY_WOW64_64KEY failed (err=%u)\n", ret); + RegCloseKey(hkResult); + + hkResult = NULL; + ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, KEY_WOW64_32KEY | KEY_READ, &hkResult); + ok(ret == ERROR_SUCCESS && hkResult != NULL, + "RegOpenKeyEx with KEY_WOW64_32KEY failed (err=%u)\n", ret); + RegCloseKey(hkResult); + + HeapFree(GetProcessHeap(), 0, sd); + LocalFree(key_acl); + FreeSid(world_sid); + RegDeleteKeyA(hkRoot64, ""); + RegCloseKey(hkRoot64); + RegDeleteKeyA(hkRoot32, ""); + RegCloseKey(hkRoot32); } static void test_reg_create_key(void) { LONG ret; HKEY hkey1, hkey2; + HKEY hkRoot64 = NULL; + HKEY hkRoot32 = NULL; + DWORD dwRet; + BOOL bRet; + SID_IDENTIFIER_AUTHORITY sid_authority = {SECURITY_WORLD_SID_AUTHORITY}; + PSID world_sid; + EXPLICIT_ACCESSA access; + PACL key_acl; + SECURITY_DESCRIPTOR *sd; + ret = RegCreateKeyExA(hkey_main, "Subkey1", 0, NULL, 0, KEY_NOTIFY, NULL, &hkey1, NULL); ok(!ret, "RegCreateKeyExA failed with error %d\n", ret); /* should succeed: all versions of Windows ignore the access rights @@ -976,8 +1040,7 @@ static void test_reg_create_key(void) 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); + ok(ret == ERROR_CHILD_MUST_BE_VOLATILE, "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); @@ -1014,6 +1077,84 @@ static void test_reg_create_key(void) ok((ret == ERROR_SUCCESS && hkey1 != NULL) || broken(ret == ERROR_ACCESS_DENIED /* NT4, win2k */), "RegOpenKeyEx with KEY_WOW64_64KEY failed (err=%u)\n", ret); RegCloseKey(hkey1); + + /* Try using WOW64 flags when opening a key with a DACL set to verify that + * the registry access check is performed correctly. Redirection isn't + * being tested, so the tests don't care about whether the process is + * running under WOW64. */ + if (!pIsWow64Process) + { + win_skip("WOW64 flags are not recognized\n"); + return; + } + + ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, NULL, 0, + KEY_WOW64_32KEY | KEY_ALL_ACCESS, NULL, &hkRoot32, NULL); + ok(ret == ERROR_SUCCESS && hkRoot32 != NULL, + "RegCreateKeyEx with KEY_WOW64_32KEY failed (err=%d)\n", ret); + + ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, NULL, 0, + KEY_WOW64_64KEY | KEY_ALL_ACCESS, NULL, &hkRoot64, NULL); + ok(ret == ERROR_SUCCESS && hkRoot64 != NULL, + "RegCreateKeyEx with KEY_WOW64_64KEY failed (err=%d)\n", ret); + + bRet = AllocateAndInitializeSid(&sid_authority, 1, SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, &world_sid); + ok(bRet == TRUE, + "Expected AllocateAndInitializeSid to return TRUE, got %d, last error %u\n", bRet, GetLastError()); + + access.grfAccessPermissions = GENERIC_ALL | STANDARD_RIGHTS_ALL; + access.grfAccessMode = SET_ACCESS; + access.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; + access.Trustee.pMultipleTrustee = NULL; + access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + access.Trustee.TrusteeForm = TRUSTEE_IS_SID; + access.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + access.Trustee.ptstrName = (char *)world_sid; + + dwRet = SetEntriesInAclA(1, &access, NULL, &key_acl); + ok(ret == ERROR_SUCCESS, + "Expected SetEntriesInAclA to return ERROR_SUCCESS, got %u, last error %u\n", dwRet, GetLastError()); + + sd = HeapAlloc(GetProcessHeap(), 0, SECURITY_DESCRIPTOR_MIN_LENGTH); + bRet = InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION); + ok(bRet == TRUE, + "Expected InitializeSecurityDescriptor to return TRUE, got %d, last error %u\n", bRet, GetLastError()); + + bRet = SetSecurityDescriptorDacl(sd, TRUE, key_acl, FALSE); + ok(bRet == TRUE, + "Expected SetSecurityDescriptorDacl to return TRUE, got %d, last error %u\n", bRet, GetLastError()); + + /* The "sanctioned" methods of setting a registry ACL aren't implemented in Wine. */ + bRet = SetKernelObjectSecurity(hkRoot64, DACL_SECURITY_INFORMATION, sd); + ok(bRet == TRUE, + "Expected SetKernelObjectSecurity to return TRUE, got %d, last error %u\n", bRet, GetLastError()); + + bRet = SetKernelObjectSecurity(hkRoot32, DACL_SECURITY_INFORMATION, sd); + ok(bRet == TRUE, + "Expected SetKernelObjectSecurity to return TRUE, got %d, last error %u\n", bRet, GetLastError()); + + hkey1 = NULL; + ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, NULL, 0, + KEY_WOW64_64KEY | KEY_READ, NULL, &hkey1, NULL); + ok(ret == ERROR_SUCCESS && hkey1 != NULL, + "RegOpenKeyEx with KEY_WOW64_64KEY failed (err=%u)\n", ret); + RegCloseKey(hkey1); + + hkey1 = NULL; + ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, NULL, 0, + KEY_WOW64_32KEY | KEY_READ, NULL, &hkey1, NULL); + ok(ret == ERROR_SUCCESS && hkey1 != NULL, + "RegOpenKeyEx with KEY_WOW64_32KEY failed (err=%u)\n", ret); + RegCloseKey(hkey1); + + HeapFree(GetProcessHeap(), 0, sd); + LocalFree(key_acl); + FreeSid(world_sid); + RegDeleteKeyA(hkRoot64, ""); + RegCloseKey(hkRoot64); + RegDeleteKeyA(hkRoot32, ""); + RegCloseKey(hkRoot32); } static void test_reg_close_key(void) @@ -1025,6 +1166,7 @@ static void test_reg_close_key(void) * hkHandle remains changed after call to RegCloseKey */ ret = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Test", &hkHandle); + ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret); ret = RegCloseKey(hkHandle); ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret); @@ -1335,62 +1477,58 @@ static void test_string_termination(void) ret = RegQueryValueExA(subkey, "stringtest", NULL, NULL, buffer, &outsize); ok(ret == ERROR_MORE_DATA, "RegQueryValueExA returned: %d\n", ret); - /* Off-by-two RegSetValueExA -> no trailing '\0', except on Win9x */ + /* Off-by-two RegSetValueExA -> no trailing '\0' */ insize=sizeof(string)-2; ret = RegSetValueExA(subkey, "stringtest", 0, REG_SZ, (BYTE*)string, insize); ok(ret == ERROR_SUCCESS, "RegSetValueExA failed: %d\n", ret); outsize=0; ret = RegQueryValueExA(subkey, "stringtest", NULL, NULL, NULL, &outsize); ok(ret == ERROR_SUCCESS, "RegQueryValueExA failed: %d\n", ret); - ok(outsize == insize || broken(outsize == sizeof(string)) /* Win9x */, - "wrong size %u != %u\n", outsize, insize); + ok(outsize == insize, "wrong size %u != %u\n", outsize, insize); - if (outsize == insize) - { - /* RegQueryValueExA may return a string with no trailing '\0' */ - outsize=insize; - memset(buffer, 0xbd, sizeof(buffer)); - ret = RegQueryValueExA(subkey, "stringtest", NULL, NULL, buffer, &outsize); - ok(ret == ERROR_SUCCESS, "RegQueryValueExA failed: %d\n", ret); - ok(outsize == insize, "wrong size: %u != %u\n", outsize, insize); - ok(memcmp(buffer, string, outsize) == 0, "bad string: %s/%u != %s\n", - wine_debugstr_an((char*)buffer, outsize), outsize, string); - ok(buffer[insize] == 0xbd, "buffer overflow at %u %02x\n", insize, buffer[insize]); + /* RegQueryValueExA may return a string with no trailing '\0' */ + outsize=insize; + memset(buffer, 0xbd, sizeof(buffer)); + ret = RegQueryValueExA(subkey, "stringtest", NULL, NULL, buffer, &outsize); + ok(ret == ERROR_SUCCESS, "RegQueryValueExA failed: %d\n", ret); + ok(outsize == insize, "wrong size: %u != %u\n", outsize, insize); + ok(memcmp(buffer, string, outsize) == 0, "bad string: %s/%u != %s\n", + wine_debugstr_an((char*)buffer, outsize), outsize, string); + ok(buffer[insize] == 0xbd, "buffer overflow at %u %02x\n", insize, buffer[insize]); - /* RegQueryValueExA adds a trailing '\0' if there is room */ - outsize=insize+1; - memset(buffer, 0xbd, sizeof(buffer)); - ret = RegQueryValueExA(subkey, "stringtest", NULL, NULL, buffer, &outsize); - ok(ret == ERROR_SUCCESS, "RegQueryValueExA failed: %d\n", ret); - ok(outsize == insize, "wrong size: %u != %u\n", outsize, insize); - ok(memcmp(buffer, string, outsize) == 0, "bad string: %s/%u != %s\n", - wine_debugstr_an((char*)buffer, outsize), outsize, string); - ok(buffer[insize] == 0, "buffer overflow at %u %02x\n", insize, buffer[insize]); + /* RegQueryValueExA adds a trailing '\0' if there is room */ + outsize=insize+1; + memset(buffer, 0xbd, sizeof(buffer)); + ret = RegQueryValueExA(subkey, "stringtest", NULL, NULL, buffer, &outsize); + ok(ret == ERROR_SUCCESS, "RegQueryValueExA failed: %d\n", ret); + ok(outsize == insize, "wrong size: %u != %u\n", outsize, insize); + ok(memcmp(buffer, string, outsize) == 0, "bad string: %s/%u != %s\n", + wine_debugstr_an((char*)buffer, outsize), outsize, string); + ok(buffer[insize] == 0, "buffer overflow at %u %02x\n", insize, buffer[insize]); - /* RegEnumValueA may return a string with no trailing '\0' */ - outsize=insize; - memset(buffer, 0xbd, sizeof(buffer)); - nsize=sizeof(name); - ret = RegEnumValueA(subkey, 0, name, &nsize, NULL, NULL, buffer, &outsize); - ok(ret == ERROR_SUCCESS, "RegEnumValueA failed: %d\n", ret); - ok(strcmp(name, "stringtest") == 0, "wrong name: %s\n", name); - ok(outsize == insize, "wrong size: %u != %u\n", outsize, insize); - ok(memcmp(buffer, string, outsize) == 0, "bad string: %s/%u != %s\n", - wine_debugstr_an((char*)buffer, outsize), outsize, string); - ok(buffer[insize] == 0xbd, "buffer overflow at %u %02x\n", insize, buffer[insize]); + /* RegEnumValueA may return a string with no trailing '\0' */ + outsize=insize; + memset(buffer, 0xbd, sizeof(buffer)); + nsize=sizeof(name); + ret = RegEnumValueA(subkey, 0, name, &nsize, NULL, NULL, buffer, &outsize); + ok(ret == ERROR_SUCCESS, "RegEnumValueA failed: %d\n", ret); + ok(strcmp(name, "stringtest") == 0, "wrong name: %s\n", name); + ok(outsize == insize, "wrong size: %u != %u\n", outsize, insize); + ok(memcmp(buffer, string, outsize) == 0, "bad string: %s/%u != %s\n", + wine_debugstr_an((char*)buffer, outsize), outsize, string); + ok(buffer[insize] == 0xbd, "buffer overflow at %u %02x\n", insize, buffer[insize]); - /* RegEnumValueA adds a trailing '\0' if there is room */ - outsize=insize+1; - memset(buffer, 0xbd, sizeof(buffer)); - nsize=sizeof(name); - ret = RegEnumValueA(subkey, 0, name, &nsize, NULL, NULL, buffer, &outsize); - ok(ret == ERROR_SUCCESS, "RegEnumValueA failed: %d\n", ret); - ok(strcmp(name, "stringtest") == 0, "wrong name: %s\n", name); - ok(outsize == insize, "wrong size: %u != %u\n", outsize, insize); - ok(memcmp(buffer, string, outsize) == 0, "bad string: %s/%u != %s\n", - wine_debugstr_an((char*)buffer, outsize), outsize, string); - ok(buffer[insize] == 0, "buffer overflow at %u %02x\n", insize, buffer[insize]); - } + /* RegEnumValueA adds a trailing '\0' if there is room */ + outsize=insize+1; + memset(buffer, 0xbd, sizeof(buffer)); + nsize=sizeof(name); + ret = RegEnumValueA(subkey, 0, name, &nsize, NULL, NULL, buffer, &outsize); + ok(ret == ERROR_SUCCESS, "RegEnumValueA failed: %d\n", ret); + ok(strcmp(name, "stringtest") == 0, "wrong name: %s\n", name); + ok(outsize == insize, "wrong size: %u != %u\n", outsize, insize); + ok(memcmp(buffer, string, outsize) == 0, "bad string: %s/%u != %s\n", + wine_debugstr_an((char*)buffer, outsize), outsize, string); + ok(buffer[insize] == 0, "buffer overflow at %u %02x\n", insize, buffer[insize]); RegDeleteKeyA(subkey, ""); RegCloseKey(subkey); @@ -1912,6 +2050,269 @@ static void test_redirection(void) RegCloseKey( root64 ); } +static void test_classesroot(void) +{ + HKEY hkey, hklm, hkcr, hkeysub1, hklmsub1, hkcrsub1, hklmsub2, hkcrsub2; + DWORD size = 8; + DWORD type = REG_SZ; + static CHAR buffer[8]; + LONG res; + + /* create a key in the user's classes */ + if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Classes\\WineTestCls", &hkey )) + { + delete_key( hkey ); + RegCloseKey( hkey ); + } + res = RegCreateKeyExA( HKEY_CURRENT_USER, "Software\\Classes\\WineTestCls", 0, NULL, 0, + KEY_QUERY_VALUE|KEY_SET_VALUE, NULL, &hkey, NULL ); + if (res == ERROR_ACCESS_DENIED) + { + skip("not enough privileges to add a user class\n"); + return; + } + + /* try to open that key in hkcr */ + res = RegOpenKeyExA( HKEY_CLASSES_ROOT, "WineTestCls", 0, + KEY_QUERY_VALUE|KEY_SET_VALUE, &hkcr ); + todo_wine ok(res == ERROR_SUCCESS || + broken(res == ERROR_FILE_NOT_FOUND /* WinNT */), + "test key not found in hkcr: %d\n", res); + if (res) + { + skip("HKCR key merging not supported\n"); + delete_key( hkey ); + RegCloseKey( hkey ); + return; + } + + /* set a value in user's classes */ + res = RegSetValueExA(hkey, "val1", 0, REG_SZ, (const BYTE *)"user", sizeof("user")); + ok(res == ERROR_SUCCESS, "RegSetValueExA failed: %d, GLE=%x\n", res, GetLastError()); + + /* try to find the value in hkcr */ + res = RegQueryValueExA(hkcr, "val1", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d\n", res); + ok(!strcmp( buffer, "user" ), "value set to '%s'\n", buffer ); + + /* modify the value in hkcr */ + res = RegSetValueExA(hkcr, "val1", 0, REG_SZ, (const BYTE *)"hkcr", sizeof("hkcr")); + ok(res == ERROR_SUCCESS, "RegSetValueExA failed: %d, GLE=%x\n", res, GetLastError()); + + /* check if the value is also modified in user's classes */ + res = RegQueryValueExA(hkey, "val1", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d, GLE=%x\n", res, GetLastError()); + ok(!strcmp( buffer, "hkcr" ), "value set to '%s'\n", buffer ); + + /* set a value in hkcr */ + res = RegSetValueExA(hkcr, "val0", 0, REG_SZ, (const BYTE *)"hkcr", sizeof("hkcr")); + ok(res == ERROR_SUCCESS, "RegSetValueExA failed: %d, GLE=%x\n", res, GetLastError()); + + /* try to find the value in user's classes */ + res = RegQueryValueExA(hkcr, "val0", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d\n", res); + ok(!strcmp( buffer, "hkcr" ), "value set to '%s'\n", buffer ); + + /* modify the value in user's classes */ + res = RegSetValueExA(hkcr, "val0", 0, REG_SZ, (const BYTE *)"user", sizeof("user")); + ok(res == ERROR_SUCCESS, "RegSetValueExA failed: %d, GLE=%x\n", res, GetLastError()); + + /* check if the value is also modified in hkcr */ + res = RegQueryValueExA(hkey, "val0", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d, GLE=%x\n", res, GetLastError()); + ok(!strcmp( buffer, "user" ), "value set to '%s'\n", buffer ); + + /* cleanup */ + delete_key( hkey ); + delete_key( hkcr ); + RegCloseKey( hkey ); + RegCloseKey( hkcr ); + + /* create a key in the hklm classes */ + if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Classes\\WineTestCls", &hklm )) + { + delete_key( hklm ); + RegCloseKey( hklm ); + } + res = RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\\Classes\\WineTestCls", 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, NULL, &hklm, NULL ); + if (res == ERROR_ACCESS_DENIED) + { + skip("not enough privileges to add a system class\n"); + return; + } + + /* try to open that key in hkcr */ + res = RegOpenKeyExA( HKEY_CLASSES_ROOT, "WineTestCls", 0, + KEY_QUERY_VALUE|KEY_SET_VALUE, &hkcr ); + ok(res == ERROR_SUCCESS, + "test key not found in hkcr: %d\n", res); + if (res) + { + delete_key( hklm ); + RegCloseKey( hklm ); + return; + } + + /* set a value in hklm classes */ + res = RegSetValueExA(hklm, "val2", 0, REG_SZ, (const BYTE *)"hklm", sizeof("hklm")); + ok(res == ERROR_SUCCESS, "RegSetValueExA failed: %d, GLE=%x\n", res, GetLastError()); + + /* try to find the value in hkcr */ + res = RegQueryValueExA(hkcr, "val2", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d\n", res); + ok(!strcmp( buffer, "hklm" ), "value set to '%s'\n", buffer ); + + /* modify the value in hkcr */ + res = RegSetValueExA(hkcr, "val2", 0, REG_SZ, (const BYTE *)"hkcr", sizeof("hkcr")); + ok(res == ERROR_SUCCESS, "RegSetValueExA failed: %d, GLE=%x\n", res, GetLastError()); + + /* check that the value is not modified in hklm classes */ + res = RegQueryValueExA(hklm, "val2", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d, GLE=%x\n", res, GetLastError()); + ok(!strcmp( buffer, "hklm" ), "value set to '%s'\n", buffer ); + + if (RegCreateKeyExA( HKEY_CURRENT_USER, "Software\\Classes\\WineTestCls", 0, NULL, 0, + KEY_QUERY_VALUE|KEY_SET_VALUE, NULL, &hkey, NULL )) return; + + /* try to open that key in hkcr */ + res = RegOpenKeyExA( HKEY_CLASSES_ROOT, "WineTestCls", 0, + KEY_QUERY_VALUE|KEY_SET_VALUE, &hkcr ); + ok(res == ERROR_SUCCESS, + "test key not found in hkcr: %d\n", res); + + /* set a value in user's classes */ + res = RegSetValueExA(hkey, "val2", 0, REG_SZ, (const BYTE *)"user", sizeof("user")); + ok(res == ERROR_SUCCESS, "RegSetValueExA failed: %d, GLE=%x\n", res, GetLastError()); + + /* try to find the value in hkcr */ + res = RegQueryValueExA(hkcr, "val2", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d\n", res); + ok(!strcmp( buffer, "user" ), "value set to '%s'\n", buffer ); + + /* modify the value in hklm */ + res = RegSetValueExA(hklm, "val2", 0, REG_SZ, (const BYTE *)"hklm", sizeof("hklm")); + ok(res == ERROR_SUCCESS, "RegSetValueExA failed: %d, GLE=%x\n", res, GetLastError()); + + /* check that the value is not overwritten in hkcr or user's classes */ + res = RegQueryValueExA(hkcr, "val2", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d\n", res); + ok(!strcmp( buffer, "user" ), "value set to '%s'\n", buffer ); + res = RegQueryValueExA(hkey, "val2", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d, GLE=%x\n", res, GetLastError()); + ok(!strcmp( buffer, "user" ), "value set to '%s'\n", buffer ); + + /* modify the value in hkcr */ + res = RegSetValueExA(hkcr, "val2", 0, REG_SZ, (const BYTE *)"hkcr", sizeof("hkcr")); + ok(res == ERROR_SUCCESS, "RegSetValueExA failed: %d, GLE=%x\n", res, GetLastError()); + + /* check that the value is overwritten in hklm and user's classes */ + res = RegQueryValueExA(hkcr, "val2", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d\n", res); + ok(!strcmp( buffer, "hkcr" ), "value set to '%s'\n", buffer ); + res = RegQueryValueExA(hkey, "val2", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d, GLE=%x\n", res, GetLastError()); + ok(!strcmp( buffer, "hkcr" ), "value set to '%s'\n", buffer ); + + /* create a subkey in hklm */ + if (RegCreateKeyExA( hklm, "subkey1", 0, NULL, 0, + KEY_QUERY_VALUE|KEY_SET_VALUE, NULL, &hklmsub1, NULL )) return; + /* try to open that subkey in hkcr */ + res = RegOpenKeyExA( hkcr, "subkey1", 0, KEY_QUERY_VALUE|KEY_SET_VALUE, &hkcrsub1 ); + ok(res == ERROR_SUCCESS, "test key not found in hkcr: %d\n", res); + + /* set a value in hklm classes */ + res = RegSetValueExA(hklmsub1, "subval1", 0, REG_SZ, (const BYTE *)"hklm", sizeof("hklm")); + ok(res == ERROR_SUCCESS, "RegSetValueExA failed: %d, GLE=%x\n", res, GetLastError()); + + /* try to find the value in hkcr */ + res = RegQueryValueExA(hkcrsub1, "subval1", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d\n", res); + ok(!strcmp( buffer, "hklm" ), "value set to '%s'\n", buffer ); + + /* modify the value in hkcr */ + res = RegSetValueExA(hkcrsub1, "subval1", 0, REG_SZ, (const BYTE *)"hkcr", sizeof("hkcr")); + ok(res == ERROR_SUCCESS, "RegSetValueExA failed: %d, GLE=%x\n", res, GetLastError()); + + /* check that the value is modified in hklm classes */ + res = RegQueryValueExA(hklmsub1, "subval1", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d, GLE=%x\n", res, GetLastError()); + ok(!strcmp( buffer, "hkcr" ), "value set to '%s'\n", buffer ); + + /* create a subkey in user's classes */ + if (RegCreateKeyExA( hkey, "subkey1", 0, NULL, 0, + KEY_QUERY_VALUE|KEY_SET_VALUE, NULL, &hkeysub1, NULL )) return; + + /* set a value in user's classes */ + res = RegSetValueExA(hkeysub1, "subval1", 0, REG_SZ, (const BYTE *)"user", sizeof("user")); + ok(res == ERROR_SUCCESS, "RegSetValueExA failed: %d, GLE=%x\n", res, GetLastError()); + + /* try to find the value in hkcr */ + res = RegQueryValueExA(hkcrsub1, "subval1", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d\n", res); + ok(!strcmp( buffer, "user" ), "value set to '%s'\n", buffer ); + + /* modify the value in hklm */ + res = RegSetValueExA(hklmsub1, "subval1", 0, REG_SZ, (const BYTE *)"hklm", sizeof("hklm")); + ok(res == ERROR_SUCCESS, "RegSetValueExA failed: %d, GLE=%x\n", res, GetLastError()); + + /* check that the value is not overwritten in hkcr or user's classes */ + res = RegQueryValueExA(hkcrsub1, "subval1", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d\n", res); + ok(!strcmp( buffer, "user" ), "value set to '%s'\n", buffer ); + res = RegQueryValueExA(hkeysub1, "subval1", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d, GLE=%x\n", res, GetLastError()); + ok(!strcmp( buffer, "user" ), "value set to '%s'\n", buffer ); + + /* modify the value in hkcr */ + res = RegSetValueExA(hkcrsub1, "subval1", 0, REG_SZ, (const BYTE *)"hkcr", sizeof("hkcr")); + ok(res == ERROR_SUCCESS, "RegSetValueExA failed: %d, GLE=%x\n", res, GetLastError()); + + /* check that the value is not overwritten in hklm, but in user's classes */ + res = RegQueryValueExA(hklmsub1, "subval1", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d\n", res); + ok(!strcmp( buffer, "hklm" ), "value set to '%s'\n", buffer ); + res = RegQueryValueExA(hkeysub1, "subval1", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d, GLE=%x\n", res, GetLastError()); + ok(!strcmp( buffer, "hkcr" ), "value set to '%s'\n", buffer ); + + /* new subkey in hkcr */ + if (RegCreateKeyExA( hkcr, "subkey2", 0, NULL, 0, + KEY_QUERY_VALUE|KEY_SET_VALUE, NULL, &hkcrsub2, NULL )) return; + res = RegSetValueExA(hkcrsub2, "subval1", 0, REG_SZ, (const BYTE *)"hkcr", sizeof("hkcr")); + ok(res == ERROR_SUCCESS, "RegSetValueExA failed: %d, GLE=%x\n", res, GetLastError()); + + /* try to open that new subkey in user's classes and hklm */ + res = RegOpenKeyExA( hkey, "subkey2", 0, KEY_QUERY_VALUE|KEY_SET_VALUE, &hklmsub2 ); + ok(res != ERROR_SUCCESS, "test key found in user's classes: %d\n", res); + hklmsub2 = 0; + res = RegOpenKeyExA( hklm, "subkey2", 0, KEY_QUERY_VALUE|KEY_SET_VALUE, &hklmsub2 ); + ok(res == ERROR_SUCCESS, "test key not found in hklm: %d\n", res); + + /* check that the value is present in hklm */ + res = RegQueryValueExA(hklmsub2, "subval1", NULL, &type, (LPBYTE)buffer, &size); + ok(res == ERROR_SUCCESS, "RegQueryValueExA failed: %d\n", res); + ok(!strcmp( buffer, "hkcr" ), "value set to '%s'\n", buffer ); + + /* final cleanup */ + delete_key( hkey ); + delete_key( hklm ); + delete_key( hkcr ); + delete_key( hkeysub1 ); + delete_key( hklmsub1 ); + delete_key( hkcrsub1 ); + delete_key( hklmsub2 ); + delete_key( hkcrsub2 ); + RegCloseKey( hkey ); + RegCloseKey( hklm ); + RegCloseKey( hkcr ); + RegCloseKey( hkeysub1 ); + RegCloseKey( hklmsub1 ); + RegCloseKey( hkcrsub1 ); + RegCloseKey( hklmsub2 ); + RegCloseKey( hkcrsub2 ); +} + static void test_deleted_key(void) { HKEY hkey, hkey2; @@ -2004,6 +2405,7 @@ START_TEST(registry) test_string_termination(); test_symlinks(); test_redirection(); + test_classesroot(); /* SaveKey/LoadKey require the SE_BACKUP_NAME privilege to be set */ if (set_privileges(SE_BACKUP_NAME, TRUE) && diff --git a/rostests/winetests/advapi32/security.c b/rostests/winetests/advapi32/security.c index cd4624aea79..4cc140277ed 100644 --- a/rostests/winetests/advapi32/security.c +++ b/rostests/winetests/advapi32/security.c @@ -2,6 +2,7 @@ * Unit tests for security functions * * Copyright (c) 2004 Mike McCormack + * Copyright (c) 2011 Dmitry Timoshkov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -62,23 +63,23 @@ typedef struct _OBJECT_BASIC_INFORMATION { static BOOL (WINAPI *pAddAccessAllowedAceEx)(PACL, DWORD, DWORD, DWORD, PSID); static BOOL (WINAPI *pAddAccessDeniedAceEx)(PACL, DWORD, DWORD, DWORD, PSID); static BOOL (WINAPI *pAddAuditAccessAceEx)(PACL, DWORD, DWORD, DWORD, PSID, BOOL, BOOL); -typedef VOID (WINAPI *fnBuildTrusteeWithSidA)( PTRUSTEEA pTrustee, PSID pSid ); -typedef VOID (WINAPI *fnBuildTrusteeWithNameA)( PTRUSTEEA pTrustee, LPSTR pName ); -typedef VOID (WINAPI *fnBuildTrusteeWithObjectsAndNameA)( PTRUSTEEA pTrustee, +static VOID (WINAPI *pBuildTrusteeWithSidA)( PTRUSTEEA pTrustee, PSID pSid ); +static VOID (WINAPI *pBuildTrusteeWithNameA)( PTRUSTEEA pTrustee, LPSTR pName ); +static VOID (WINAPI *pBuildTrusteeWithObjectsAndNameA)( PTRUSTEEA pTrustee, POBJECTS_AND_NAME_A pObjName, SE_OBJECT_TYPE ObjectType, LPSTR ObjectTypeName, LPSTR InheritedObjectTypeName, LPSTR Name ); -typedef VOID (WINAPI *fnBuildTrusteeWithObjectsAndSidA)( PTRUSTEEA pTrustee, +static VOID (WINAPI *pBuildTrusteeWithObjectsAndSidA)( PTRUSTEEA pTrustee, POBJECTS_AND_SID pObjSid, GUID* pObjectGuid, GUID* pInheritedObjectGuid, PSID pSid ); -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 LPSTR (WINAPI *pGetTrusteeNameA)( PTRUSTEEA pTrustee ); +static BOOL (WINAPI *pMakeSelfRelativeSD)( PSECURITY_DESCRIPTOR, PSECURITY_DESCRIPTOR, LPDWORD ); +static BOOL (WINAPI *pConvertSidToStringSidA)( PSID pSid, LPSTR *str ); +static BOOL (WINAPI *pConvertStringSidToSidA)( LPCSTR str, PSID pSid ); static BOOL (WINAPI *pCheckTokenMembership)(HANDLE, PSID, PBOOL); static BOOL (WINAPI *pConvertStringSecurityDescriptorToSecurityDescriptorA)(LPCSTR, DWORD, PSECURITY_DESCRIPTOR*, PULONG ); @@ -86,9 +87,9 @@ static BOOL (WINAPI *pConvertStringSecurityDescriptorToSecurityDescriptorW)(LPCW PSECURITY_DESCRIPTOR*, PULONG ); static BOOL (WINAPI *pConvertSecurityDescriptorToStringSecurityDescriptorA)(PSECURITY_DESCRIPTOR, DWORD, SECURITY_INFORMATION, LPSTR *, PULONG ); -typedef BOOL (WINAPI *fnGetFileSecurityA)(LPCSTR, SECURITY_INFORMATION, +static BOOL (WINAPI *pGetFileSecurityA)(LPCSTR, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, DWORD, LPDWORD); -typedef BOOL (WINAPI *fnSetFileSecurityA)(LPCSTR, SECURITY_INFORMATION, +static BOOL (WINAPI *pSetFileSecurityA)(LPCSTR, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR); static DWORD (WINAPI *pGetNamedSecurityInfoA)(LPSTR, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID*, PSID*, PACL*, PACL*, @@ -96,46 +97,31 @@ static DWORD (WINAPI *pGetNamedSecurityInfoA)(LPSTR, SE_OBJECT_TYPE, SECURITY_IN static PDWORD (WINAPI *pGetSidSubAuthority)(PSID, DWORD); static PUCHAR (WINAPI *pGetSidSubAuthorityCount)(PSID); static BOOL (WINAPI *pIsValidSid)(PSID); -typedef DWORD (WINAPI *fnRtlAdjustPrivilege)(ULONG,BOOLEAN,BOOLEAN,PBOOLEAN); -typedef BOOL (WINAPI *fnCreateWellKnownSid)(WELL_KNOWN_SID_TYPE,PSID,PSID,DWORD*); -typedef BOOL (WINAPI *fnDuplicateTokenEx)(HANDLE,DWORD,LPSECURITY_ATTRIBUTES, +static DWORD (WINAPI *pRtlAdjustPrivilege)(ULONG,BOOLEAN,BOOLEAN,PBOOLEAN); +static BOOL (WINAPI *pCreateWellKnownSid)(WELL_KNOWN_SID_TYPE,PSID,PSID,DWORD*); +static BOOL (WINAPI *pDuplicateTokenEx)(HANDLE,DWORD,LPSECURITY_ATTRIBUTES, SECURITY_IMPERSONATION_LEVEL,TOKEN_TYPE,PHANDLE); -typedef NTSTATUS (WINAPI *fnLsaQueryInformationPolicy)(LSA_HANDLE,POLICY_INFORMATION_CLASS,PVOID*); -typedef NTSTATUS (WINAPI *fnLsaClose)(LSA_HANDLE); -typedef NTSTATUS (WINAPI *fnLsaFreeMemory)(PVOID); -typedef NTSTATUS (WINAPI *fnLsaOpenPolicy)(PLSA_UNICODE_STRING,PLSA_OBJECT_ATTRIBUTES,ACCESS_MASK,PLSA_HANDLE); +static NTSTATUS (WINAPI *pLsaQueryInformationPolicy)(LSA_HANDLE,POLICY_INFORMATION_CLASS,PVOID*); +static NTSTATUS (WINAPI *pLsaClose)(LSA_HANDLE); +static NTSTATUS (WINAPI *pLsaFreeMemory)(PVOID); +static NTSTATUS (WINAPI *pLsaOpenPolicy)(PLSA_UNICODE_STRING,PLSA_OBJECT_ATTRIBUTES,ACCESS_MASK,PLSA_HANDLE); static NTSTATUS (WINAPI *pNtQueryObject)(HANDLE,OBJECT_INFORMATION_CLASS,PVOID,ULONG,PULONG); static DWORD (WINAPI *pSetEntriesInAclW)(ULONG, PEXPLICIT_ACCESSW, PACL, PACL*); +static DWORD (WINAPI *pSetEntriesInAclA)(ULONG, PEXPLICIT_ACCESSA, PACL, PACL*); static BOOL (WINAPI *pSetSecurityDescriptorControl)(PSECURITY_DESCRIPTOR, SECURITY_DESCRIPTOR_CONTROL, SECURITY_DESCRIPTOR_CONTROL); static DWORD (WINAPI *pGetSecurityInfo)(HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID*, PSID*, PACL*, PACL*, PSECURITY_DESCRIPTOR*); static NTSTATUS (WINAPI *pNtAccessCheck)(PSECURITY_DESCRIPTOR, HANDLE, ACCESS_MASK, PGENERIC_MAPPING, PPRIVILEGE_SET, PULONG, PULONG, NTSTATUS*); +static BOOL (WINAPI *pCreateRestrictedToken)(HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, + PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE); static HMODULE hmod; static int myARGC; static char** myARGV; -fnBuildTrusteeWithSidA pBuildTrusteeWithSidA; -fnBuildTrusteeWithNameA pBuildTrusteeWithNameA; -fnBuildTrusteeWithObjectsAndNameA pBuildTrusteeWithObjectsAndNameA; -fnBuildTrusteeWithObjectsAndSidA pBuildTrusteeWithObjectsAndSidA; -fnGetTrusteeNameA pGetTrusteeNameA; -fnMakeSelfRelativeSD pMakeSelfRelativeSD; -fnConvertSidToStringSidA pConvertSidToStringSidA; -fnConvertStringSidToSidA pConvertStringSidToSidA; -fnGetFileSecurityA pGetFileSecurityA; -fnSetFileSecurityA pSetFileSecurityA; -fnRtlAdjustPrivilege pRtlAdjustPrivilege; -fnCreateWellKnownSid pCreateWellKnownSid; -fnDuplicateTokenEx pDuplicateTokenEx; -fnLsaQueryInformationPolicy pLsaQueryInformationPolicy; -fnLsaClose pLsaClose; -fnLsaFreeMemory pLsaFreeMemory; -fnLsaOpenPolicy pLsaOpenPolicy; - struct sidRef { SID_IDENTIFIER_AUTHORITY auth; @@ -161,17 +147,19 @@ static void init(void) (void *)GetProcAddress(hmod, "ConvertStringSecurityDescriptorToSecurityDescriptorW" ); pConvertSecurityDescriptorToStringSecurityDescriptorA = (void *)GetProcAddress(hmod, "ConvertSecurityDescriptorToStringSecurityDescriptorA" ); - pGetFileSecurityA = (fnGetFileSecurityA)GetProcAddress(hmod, "GetFileSecurityA" ); - pSetFileSecurityA = (fnSetFileSecurityA)GetProcAddress(hmod, "SetFileSecurityA" ); - pCreateWellKnownSid = (fnCreateWellKnownSid)GetProcAddress( hmod, "CreateWellKnownSid" ); + pGetFileSecurityA = (void *)GetProcAddress(hmod, "GetFileSecurityA" ); + pSetFileSecurityA = (void *)GetProcAddress(hmod, "SetFileSecurityA" ); + pCreateWellKnownSid = (void *)GetProcAddress( hmod, "CreateWellKnownSid" ); pGetNamedSecurityInfoA = (void *)GetProcAddress(hmod, "GetNamedSecurityInfoA"); pGetSidSubAuthority = (void *)GetProcAddress(hmod, "GetSidSubAuthority"); pGetSidSubAuthorityCount = (void *)GetProcAddress(hmod, "GetSidSubAuthorityCount"); pIsValidSid = (void *)GetProcAddress(hmod, "IsValidSid"); pMakeSelfRelativeSD = (void *)GetProcAddress(hmod, "MakeSelfRelativeSD"); pSetEntriesInAclW = (void *)GetProcAddress(hmod, "SetEntriesInAclW"); + pSetEntriesInAclA = (void *)GetProcAddress(hmod, "SetEntriesInAclA"); pSetSecurityDescriptorControl = (void *)GetProcAddress(hmod, "SetSecurityDescriptorControl"); pGetSecurityInfo = (void *)GetProcAddress(hmod, "GetSecurityInfo"); + pCreateRestrictedToken = (void *)GetProcAddress(hmod, "CreateRestrictedToken"); myARGC = winetest_get_mainargs( &myARGV ); } @@ -216,12 +204,10 @@ static void test_sid(void) BOOL r; LPSTR str = NULL; - pConvertSidToStringSidA = (fnConvertSidToStringSidA) - GetProcAddress( hmod, "ConvertSidToStringSidA" ); + pConvertSidToStringSidA = (void *)GetProcAddress( hmod, "ConvertSidToStringSidA" ); if( !pConvertSidToStringSidA ) return; - pConvertStringSidToSidA = (fnConvertStringSidToSidA) - GetProcAddress( hmod, "ConvertStringSidToSidA" ); + pConvertStringSidToSidA = (void *)GetProcAddress( hmod, "ConvertStringSidToSidA" ); if( !pConvertStringSidToSidA ) return; @@ -253,8 +239,8 @@ static void test_sid(void) ok(pConvertStringSidToSidA("S-1-5-21-93476-23408-4576", &psid), "ConvertStringSidToSidA failed\n"); pisid = psid; ok(pisid->SubAuthorityCount == 4, "Invalid sub authority count - expected 4, got %d\n", pisid->SubAuthorityCount); - 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]); + ok(pisid->SubAuthority[0] == 21, "Invalid subauthority 0 - expected 21, got %d\n", pisid->SubAuthority[0]); + ok(pisid->SubAuthority[3] == 4576, "Invalid subauthority 0 - expected 4576, got %d\n", pisid->SubAuthority[3]); LocalFree(str); LocalFree(psid); @@ -355,16 +341,11 @@ static void test_trustee(void) memset( &ZeroGuid, 0x00, sizeof (ZeroGuid) ); - pBuildTrusteeWithSidA = (fnBuildTrusteeWithSidA) - GetProcAddress( hmod, "BuildTrusteeWithSidA" ); - pBuildTrusteeWithNameA = (fnBuildTrusteeWithNameA) - GetProcAddress( hmod, "BuildTrusteeWithNameA" ); - pBuildTrusteeWithObjectsAndNameA = (fnBuildTrusteeWithObjectsAndNameA) - GetProcAddress (hmod, "BuildTrusteeWithObjectsAndNameA" ); - pBuildTrusteeWithObjectsAndSidA = (fnBuildTrusteeWithObjectsAndSidA) - GetProcAddress (hmod, "BuildTrusteeWithObjectsAndSidA" ); - pGetTrusteeNameA = (fnGetTrusteeNameA) - GetProcAddress (hmod, "GetTrusteeNameA" ); + pBuildTrusteeWithSidA = (void *)GetProcAddress( hmod, "BuildTrusteeWithSidA" ); + pBuildTrusteeWithNameA = (void *)GetProcAddress( hmod, "BuildTrusteeWithNameA" ); + pBuildTrusteeWithObjectsAndNameA = (void *)GetProcAddress (hmod, "BuildTrusteeWithObjectsAndNameA" ); + pBuildTrusteeWithObjectsAndSidA = (void *)GetProcAddress (hmod, "BuildTrusteeWithObjectsAndSidA" ); + pGetTrusteeNameA = (void *)GetProcAddress (hmod, "GetTrusteeNameA" ); if( !pBuildTrusteeWithSidA || !pBuildTrusteeWithNameA || !pBuildTrusteeWithObjectsAndNameA || !pBuildTrusteeWithObjectsAndSidA || !pGetTrusteeNameA ) @@ -714,12 +695,13 @@ static void test_FileSecurity(void) char wintmpdir [MAX_PATH]; char path [MAX_PATH]; char file [MAX_PATH]; - BOOL rc; - HANDLE fh; - DWORD sdSize; - DWORD retSize; + HANDLE fh, token; + DWORD sdSize, retSize, rc, granted, priv_set_len; + PRIVILEGE_SET priv_set; + BOOL status; BYTE *sd; - SECURITY_INFORMATION const request = OWNER_SECURITY_INFORMATION + GENERIC_MAPPING mapping = { FILE_READ_DATA, FILE_WRITE_DATA, FILE_EXECUTE, FILE_ALL_ACCESS }; + const SECURITY_INFORMATION request = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION; @@ -833,6 +815,238 @@ cleanup: /* Remove temporary file and directory */ DeleteFileA(file); RemoveDirectoryA(path); + + /* Test file access permissions for a file with FILE_ATTRIBUTE_ARCHIVE */ + SetLastError(0xdeadbeef); + rc = GetTempPath(sizeof(wintmpdir), wintmpdir); + ok(rc, "GetTempPath error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + rc = GetTempFileName(wintmpdir, "tmp", 0, file); + ok(rc, "GetTempFileName error %d\n", GetLastError()); + + rc = GetFileAttributes(file); + rc &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; + ok(rc == FILE_ATTRIBUTE_ARCHIVE, "expected FILE_ATTRIBUTE_ARCHIVE got %#x\n", rc); + + retSize = 0xdeadbeef; + rc = GetFileSecurity(file, OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, + NULL, 0, &sdSize); + ok(!rc, "GetFileSecurity should fail\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "expected ERROR_INSUFFICIENT_BUFFER got %d\n", GetLastError()); + ok(sdSize > sizeof(SECURITY_DESCRIPTOR), "got sd size %d\n", sdSize); + + sd = HeapAlloc(GetProcessHeap (), 0, sdSize); + retSize = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = GetFileSecurity(file, OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, + sd, sdSize, &retSize); + ok(rc, "GetFileSecurity error %d\n", GetLastError()); + ok(retSize == sdSize || broken(retSize == 0) /* NT4 */, "expected %d, got %d\n", sdSize, retSize); + + SetLastError(0xdeadbeef); + rc = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token); + ok(!rc, "OpenThreadToken should fail\n"); + ok(GetLastError() == ERROR_NO_TOKEN, "expected ERROR_NO_TOKEN, got %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + rc = ImpersonateSelf(SecurityIdentification); + ok(rc, "ImpersonateSelf error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + rc = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token); + ok(rc, "OpenThreadToken error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + rc = RevertToSelf(); + ok(rc, "RevertToSelf error %d\n", GetLastError()); + + priv_set_len = sizeof(priv_set); + granted = 0xdeadbeef; + status = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = AccessCheck(sd, token, FILE_READ_DATA, &mapping, &priv_set, &priv_set_len, &granted, &status); + ok(rc, "AccessCheck error %d\n", GetLastError()); + ok(status == 1, "expected 1, got %d\n", status); + ok(granted == FILE_READ_DATA, "expected FILE_READ_DATA, got %#x\n", granted); + + granted = 0xdeadbeef; + status = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = AccessCheck(sd, token, FILE_WRITE_DATA, &mapping, &priv_set, &priv_set_len, &granted, &status); + ok(rc, "AccessCheck error %d\n", GetLastError()); + ok(status == 1, "expected 1, got %d\n", status); + ok(granted == FILE_WRITE_DATA, "expected FILE_WRITE_DATA, got %#x\n", granted); + + granted = 0xdeadbeef; + status = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = AccessCheck(sd, token, FILE_EXECUTE, &mapping, &priv_set, &priv_set_len, &granted, &status); + ok(rc, "AccessCheck error %d\n", GetLastError()); + ok(status == 1, "expected 1, got %d\n", status); + ok(granted == FILE_EXECUTE, "expected FILE_EXECUTE, got %#x\n", granted); + + granted = 0xdeadbeef; + status = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = AccessCheck(sd, token, DELETE, &mapping, &priv_set, &priv_set_len, &granted, &status); + ok(rc, "AccessCheck error %d\n", GetLastError()); + ok(status == 1, "expected 1, got %d\n", status); + ok(granted == DELETE, "expected DELETE, got %#x\n", granted); + + granted = 0xdeadbeef; + status = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = AccessCheck(sd, token, FILE_DELETE_CHILD, &mapping, &priv_set, &priv_set_len, &granted, &status); + ok(rc, "AccessCheck error %d\n", GetLastError()); + ok(status == 1, "expected 1, got %d\n", status); + ok(granted == FILE_DELETE_CHILD, "expected FILE_DELETE_CHILD, got %#x\n", granted); + + granted = 0xdeadbeef; + status = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = AccessCheck(sd, token, 0x1ff, &mapping, &priv_set, &priv_set_len, &granted, &status); + ok(rc, "AccessCheck error %d\n", GetLastError()); + ok(status == 1, "expected 1, got %d\n", status); + ok(granted == 0x1ff, "expected 0x1ff, got %#x\n", granted); + + granted = 0xdeadbeef; + status = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = AccessCheck(sd, token, FILE_ALL_ACCESS, &mapping, &priv_set, &priv_set_len, &granted, &status); + ok(rc, "AccessCheck error %d\n", GetLastError()); + ok(status == 1, "expected 1, got %d\n", status); + ok(granted == FILE_ALL_ACCESS, "expected FILE_ALL_ACCESS, got %#x\n", granted); + + SetLastError(0xdeadbeef); + rc = AccessCheck(sd, token, 0xffffffff, &mapping, &priv_set, &priv_set_len, &granted, &status); + ok(!rc, "AccessCheck should fail\n"); + ok(GetLastError() == ERROR_GENERIC_NOT_MAPPED, "expected ERROR_GENERIC_NOT_MAPPED, got %d\n", GetLastError()); + + /* Test file access permissions for a file with FILE_ATTRIBUTE_READONLY */ + SetLastError(0xdeadbeef); + fh = CreateFile(file, FILE_READ_DATA, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0); + ok(fh != INVALID_HANDLE_VALUE, "CreateFile error %d\n", GetLastError()); + retSize = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = WriteFile(fh, "1", 1, &retSize, NULL); + ok(!rc, "WriteFile should fail\n"); + ok(GetLastError() == ERROR_ACCESS_DENIED, "expected ERROR_ACCESS_DENIED, got %d\n", GetLastError()); + ok(retSize == 0, "expected 0, got %d\n", retSize); + CloseHandle(fh); + + rc = GetFileAttributes(file); + rc &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; +todo_wine + ok(rc == (FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY), + "expected FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY got %#x\n", rc); + + SetLastError(0xdeadbeef); + rc = SetFileAttributes(file, FILE_ATTRIBUTE_ARCHIVE); + ok(rc, "SetFileAttributes error %d\n", GetLastError()); + SetLastError(0xdeadbeef); + rc = DeleteFile(file); + ok(rc, "DeleteFile error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + fh = CreateFile(file, FILE_READ_DATA, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0); + ok(fh != INVALID_HANDLE_VALUE, "CreateFile error %d\n", GetLastError()); + retSize = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = WriteFile(fh, "1", 1, &retSize, NULL); + ok(!rc, "WriteFile should fail\n"); + ok(GetLastError() == ERROR_ACCESS_DENIED, "expected ERROR_ACCESS_DENIED, got %d\n", GetLastError()); + ok(retSize == 0, "expected 0, got %d\n", retSize); + CloseHandle(fh); + + rc = GetFileAttributes(file); + rc &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; + ok(rc == (FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY), + "expected FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY got %#x\n", rc); + + retSize = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = GetFileSecurity(file, OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, + sd, sdSize, &retSize); + ok(rc, "GetFileSecurity error %d\n", GetLastError()); + ok(retSize == sdSize || broken(retSize == 0) /* NT4 */, "expected %d, got %d\n", sdSize, retSize); + + priv_set_len = sizeof(priv_set); + granted = 0xdeadbeef; + status = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = AccessCheck(sd, token, FILE_READ_DATA, &mapping, &priv_set, &priv_set_len, &granted, &status); + ok(rc, "AccessCheck error %d\n", GetLastError()); + ok(status == 1, "expected 1, got %d\n", status); + ok(granted == FILE_READ_DATA, "expected FILE_READ_DATA, got %#x\n", granted); + + granted = 0xdeadbeef; + status = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = AccessCheck(sd, token, FILE_WRITE_DATA, &mapping, &priv_set, &priv_set_len, &granted, &status); + ok(rc, "AccessCheck error %d\n", GetLastError()); +todo_wine { + ok(status == 1, "expected 1, got %d\n", status); + ok(granted == FILE_WRITE_DATA, "expected FILE_WRITE_DATA, got %#x\n", granted); +} + granted = 0xdeadbeef; + status = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = AccessCheck(sd, token, FILE_EXECUTE, &mapping, &priv_set, &priv_set_len, &granted, &status); + ok(rc, "AccessCheck error %d\n", GetLastError()); + ok(status == 1, "expected 1, got %d\n", status); + ok(granted == FILE_EXECUTE, "expected FILE_EXECUTE, got %#x\n", granted); + + granted = 0xdeadbeef; + status = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = AccessCheck(sd, token, DELETE, &mapping, &priv_set, &priv_set_len, &granted, &status); + ok(rc, "AccessCheck error %d\n", GetLastError()); +todo_wine { + ok(status == 1, "expected 1, got %d\n", status); + ok(granted == DELETE, "expected DELETE, got %#x\n", granted); +} + granted = 0xdeadbeef; + status = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = AccessCheck(sd, token, FILE_DELETE_CHILD, &mapping, &priv_set, &priv_set_len, &granted, &status); + ok(rc, "AccessCheck error %d\n", GetLastError()); +todo_wine { + ok(status == 1, "expected 1, got %d\n", status); + ok(granted == FILE_DELETE_CHILD, "expected FILE_DELETE_CHILD, got %#x\n", granted); +} + granted = 0xdeadbeef; + status = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = AccessCheck(sd, token, 0x1ff, &mapping, &priv_set, &priv_set_len, &granted, &status); + ok(rc, "AccessCheck error %d\n", GetLastError()); +todo_wine { + ok(status == 1, "expected 1, got %d\n", status); + ok(granted == 0x1ff, "expected 0x1ff, got %#x\n", granted); +} + granted = 0xdeadbeef; + status = 0xdeadbeef; + SetLastError(0xdeadbeef); + rc = AccessCheck(sd, token, FILE_ALL_ACCESS, &mapping, &priv_set, &priv_set_len, &granted, &status); + ok(rc, "AccessCheck error %d\n", GetLastError()); +todo_wine { + ok(status == 1, "expected 1, got %d\n", status); + ok(granted == FILE_ALL_ACCESS, "expected FILE_ALL_ACCESS, got %#x\n", granted); +} + SetLastError(0xdeadbeef); + rc = DeleteFile(file); + ok(!rc, "DeleteFile should fail\n"); + ok(GetLastError() == ERROR_ACCESS_DENIED, "expected ERROR_ACCESS_DENIED, got %d\n", GetLastError()); + SetLastError(0xdeadbeef); + rc = SetFileAttributes(file, FILE_ATTRIBUTE_ARCHIVE); + ok(rc, "SetFileAttributes error %d\n", GetLastError()); + SetLastError(0xdeadbeef); + rc = DeleteFile(file); + ok(rc, "DeleteFile error %d\n", GetLastError()); + + CloseHandle(token); + HeapFree(GetProcessHeap(), 0, sd); } static void test_AccessCheck(void) @@ -862,8 +1076,7 @@ static void test_AccessCheck(void) skip("not running on NT, skipping test\n"); return; } - pRtlAdjustPrivilege = (fnRtlAdjustPrivilege) - GetProcAddress(NtDllModule, "RtlAdjustPrivilege"); + pRtlAdjustPrivilege = (void *)GetProcAddress(NtDllModule, "RtlAdjustPrivilege"); if (!pRtlAdjustPrivilege) { win_skip("missing RtlAdjustPrivilege, skipping test\n"); @@ -1229,6 +1442,24 @@ static void test_token_attr(void) ok(ret, "OpenProcessToken failed with error %d\n", GetLastError()); /* groups */ + /* insufficient buffer length */ + SetLastError(0xdeadbeef); + Size2 = 0; + ret = GetTokenInformation(Token, TokenGroups, NULL, 0, &Size2); + ok(Size2 > 1, "got %d\n", Size2); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "%d with error %d\n", ret, GetLastError()); + Size2 -= 1; + Groups = HeapAlloc(GetProcessHeap(), 0, Size2); + memset(Groups, 0xcc, Size2); + Size = 0; + ret = GetTokenInformation(Token, TokenGroups, Groups, Size2, &Size); + ok(Size > 1, "got %d\n", Size); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "%d with error %d\n", ret, GetLastError()); + ok(*((BYTE*)Groups) == 0xcc, "buffer altered\n"); + HeapFree(GetProcessHeap(), 0, Groups); + SetLastError(0xdeadbeef); ret = GetTokenInformation(Token, TokenGroups, NULL, 0, &Size); ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, @@ -1336,14 +1567,18 @@ static void test_token_attr(void) ret = GetTokenInformation(Token, TokenDefaultDacl, Dacl, Size, &Size2); ok(ret, "GetTokenInformation(TokenDefaultDacl) failed with error %u\n", GetLastError()); ok(Dacl->DefaultDacl == NULL, "expected NULL, got %p\n", Dacl->DefaultDacl); - ok(Size2 == sizeof(TOKEN_DEFAULT_DACL), "got %u expected sizeof(TOKEN_DEFAULT_DACL)\n", Size2); + ok(Size2 == sizeof(TOKEN_DEFAULT_DACL) || broken(Size2 == 2*sizeof(TOKEN_DEFAULT_DACL)), /* WoW64 */ + "got %u expected sizeof(TOKEN_DEFAULT_DACL)\n", Size2); Dacl->DefaultDacl = acl; ret = SetTokenInformation(Token, TokenDefaultDacl, Dacl, Size); ok(ret, "SetTokenInformation(TokenDefaultDacl) failed with error %u\n", GetLastError()); - ret = GetTokenInformation(Token, TokenDefaultDacl, Dacl, Size, &Size2); - ok(ret, "GetTokenInformation(TokenDefaultDacl) failed with error %u\n", GetLastError()); + if (Size2 == sizeof(TOKEN_DEFAULT_DACL)) { + ret = GetTokenInformation(Token, TokenDefaultDacl, Dacl, Size, &Size2); + ok(ret, "GetTokenInformation(TokenDefaultDacl) failed with error %u\n", GetLastError()); + } else + win_skip("TOKEN_DEFAULT_DACL size too small on WoW64\n"); HeapFree(GetProcessHeap(), 0, Dacl); CloseHandle(Token); @@ -1377,7 +1612,7 @@ static void test_sid_str(PSID * sid) } } -struct well_known_sid_value +static const struct well_known_sid_value { BOOL without_domain; const char *sid_string; @@ -1452,7 +1687,7 @@ static void test_CreateWellKnownSid(void) for (i = 0; i < sizeof(well_known_sid_values)/sizeof(well_known_sid_values[0]); i++) { - struct well_known_sid_value *value = &well_known_sid_values[i]; + const struct well_known_sid_value *value = &well_known_sid_values[i]; char sid_buffer[SECURITY_MAX_SID_SIZE]; LPSTR str; DWORD cb; @@ -1505,7 +1740,7 @@ static void test_LookupAccountSid(void) PSID pUsersSid = NULL; SID_NAME_USE use; BOOL ret; - DWORD size,cbti = 0; + DWORD error, size, cbti = 0; MAX_SID max_sid; CHAR *str_sidA; int i; @@ -1553,7 +1788,7 @@ static void test_LookupAccountSid(void) acc_sizeA = 0; dom_sizeA = MAX_PATH; accountA[0] = 0; - ret = LookupAccountSidA(NULL, pUsersSid, accountA, &acc_sizeA, domainA, &dom_sizeA, &use); + LookupAccountSidA(NULL, pUsersSid, accountA, &acc_sizeA, domainA, &dom_sizeA, &use); /* this can fail or succeed depending on OS version but the size will always be returned */ ok(acc_sizeA == real_acc_sizeA + 1, "LookupAccountSidA() Expected acc_size = %u, got %u\n", @@ -1562,7 +1797,7 @@ static void test_LookupAccountSid(void) /* try a 0 sized account buffer */ acc_sizeA = 0; dom_sizeA = MAX_PATH; - ret = LookupAccountSidA(NULL, pUsersSid, NULL, &acc_sizeA, domainA, &dom_sizeA, &use); + LookupAccountSidA(NULL, pUsersSid, NULL, &acc_sizeA, domainA, &dom_sizeA, &use); /* this can fail or succeed depending on OS version but the size will always be returned */ ok(acc_sizeA == real_acc_sizeA + 1, "LookupAccountSid() Expected acc_size = %u, got %u\n", @@ -1581,7 +1816,7 @@ static void test_LookupAccountSid(void) dom_sizeA = 0; acc_sizeA = MAX_PATH; accountA[0] = 0; - ret = LookupAccountSidA(NULL, pUsersSid, accountA, &acc_sizeA, domainA, &dom_sizeA, &use); + LookupAccountSidA(NULL, pUsersSid, accountA, &acc_sizeA, domainA, &dom_sizeA, &use); /* this can fail or succeed depending on OS version but the size will always be returned */ ok(dom_sizeA == real_dom_sizeA + 1, "LookupAccountSidA() Expected dom_size = %u, got %u\n", @@ -1590,7 +1825,7 @@ static void test_LookupAccountSid(void) /* try a 0 sized domain buffer */ dom_sizeA = 0; acc_sizeA = MAX_PATH; - ret = LookupAccountSidA(NULL, pUsersSid, accountA, &acc_sizeA, NULL, &dom_sizeA, &use); + LookupAccountSidA(NULL, pUsersSid, accountA, &acc_sizeA, NULL, &dom_sizeA, &use); /* this can fail or succeed depending on OS version but the size will always be returned */ ok(dom_sizeA == real_dom_sizeA + 1, "LookupAccountSidA() Expected dom_size = %u, got %u\n", @@ -1624,7 +1859,7 @@ static void test_LookupAccountSid(void) acc_sizeW = 0; dom_sizeW = MAX_PATH; accountW[0] = 0; - ret = LookupAccountSidW(NULL, pUsersSid, accountW, &acc_sizeW, domainW, &dom_sizeW, &use); + LookupAccountSidW(NULL, pUsersSid, accountW, &acc_sizeW, domainW, &dom_sizeW, &use); /* this can fail or succeed depending on OS version but the size will always be returned */ ok(acc_sizeW == real_acc_sizeW + 1, "LookupAccountSidW() Expected acc_size = %u, got %u\n", @@ -1633,7 +1868,7 @@ static void test_LookupAccountSid(void) /* try a 0 sized account buffer */ acc_sizeW = 0; dom_sizeW = MAX_PATH; - ret = LookupAccountSidW(NULL, pUsersSid, NULL, &acc_sizeW, domainW, &dom_sizeW, &use); + LookupAccountSidW(NULL, pUsersSid, NULL, &acc_sizeW, domainW, &dom_sizeW, &use); /* this can fail or succeed depending on OS version but the size will always be returned */ ok(acc_sizeW == real_acc_sizeW + 1, "LookupAccountSidW() Expected acc_size = %u, got %u\n", @@ -1652,7 +1887,7 @@ static void test_LookupAccountSid(void) dom_sizeW = 0; acc_sizeW = MAX_PATH; accountW[0] = 0; - ret = LookupAccountSidW(NULL, pUsersSid, accountW, &acc_sizeW, domainW, &dom_sizeW, &use); + LookupAccountSidW(NULL, pUsersSid, accountW, &acc_sizeW, domainW, &dom_sizeW, &use); /* this can fail or succeed depending on OS version but the size will always be returned */ ok(dom_sizeW == real_dom_sizeW + 1, "LookupAccountSidW() Expected dom_size = %u, got %u\n", @@ -1661,18 +1896,30 @@ static void test_LookupAccountSid(void) /* try a 0 sized domain buffer */ dom_sizeW = 0; acc_sizeW = MAX_PATH; - ret = LookupAccountSidW(NULL, pUsersSid, accountW, &acc_sizeW, NULL, &dom_sizeW, &use); + LookupAccountSidW(NULL, pUsersSid, accountW, &acc_sizeW, NULL, &dom_sizeW, &use); /* this can fail or succeed depending on OS version but the size will always be returned */ ok(dom_sizeW == real_dom_sizeW + 1, "LookupAccountSidW() Expected dom_size = %u, got %u\n", real_dom_sizeW + 1, dom_sizeW); + acc_sizeW = dom_sizeW = use = 0; + SetLastError(0xdeadbeef); + ret = LookupAccountSidW(NULL, pUsersSid, NULL, &acc_sizeW, NULL, &dom_sizeW, &use); + error = GetLastError(); + ok(!ret, "LookupAccountSidW failed %u\n", GetLastError()); + ok(error == ERROR_INSUFFICIENT_BUFFER, "expected ERROR_INSUFFICIENT_BUFFER, got %u\n", error); + ok(acc_sizeW, "expected non-zero account size\n"); + ok(dom_sizeW, "expected non-zero domain size\n"); + ok(!use, "expected zero use %u\n", use); + FreeSid(pUsersSid); /* Test LookupAccountSid with Sid retrieved from token information. This assumes this process is running under the account of the current user.*/ ret = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY|TOKEN_DUPLICATE, &hToken); + ok(ret, "OpenProcessToken failed with error %d\n", GetLastError()); ret = GetTokenInformation(hToken, TokenUser, NULL, 0, &cbti); + ok(!ret, "GetTokenInformation failed with error %d\n", GetLastError()); ptiUser = HeapAlloc(GetProcessHeap(), 0, cbti); if (GetTokenInformation(hToken, TokenUser, ptiUser, cbti, &cbti)) { @@ -1682,10 +1929,7 @@ static void test_LookupAccountSid(void) user_sizeA = MAX_PATH; ret = GetUserNameA(usernameA , &user_sizeA); ok(ret, "GetUserNameA() Expected TRUE, got FALSE\n"); - todo_wine - { - ok(lstrcmpA(usernameA, accountA) == 0, "LookupAccountSidA() Expected account name: %s got: %s\n", usernameA, accountA ); - } + ok(lstrcmpA(usernameA, accountA) == 0, "LookupAccountSidA() Expected account name: %s got: %s\n", usernameA, accountA ); } HeapFree(GetProcessHeap(), 0, ptiUser); @@ -1715,10 +1959,10 @@ static void test_LookupAccountSid(void) } } - pLsaQueryInformationPolicy = (fnLsaQueryInformationPolicy)GetProcAddress( hmod, "LsaQueryInformationPolicy"); - pLsaOpenPolicy = (fnLsaOpenPolicy)GetProcAddress( hmod, "LsaOpenPolicy"); - pLsaFreeMemory = (fnLsaFreeMemory)GetProcAddress( hmod, "LsaFreeMemory"); - pLsaClose = (fnLsaClose)GetProcAddress( hmod, "LsaClose"); + pLsaQueryInformationPolicy = (void *)GetProcAddress( hmod, "LsaQueryInformationPolicy"); + pLsaOpenPolicy = (void *)GetProcAddress( hmod, "LsaOpenPolicy"); + pLsaFreeMemory = (void *)GetProcAddress( hmod, "LsaFreeMemory"); + pLsaClose = (void *)GetProcAddress( hmod, "LsaClose"); if (pLsaQueryInformationPolicy && pLsaOpenPolicy && pLsaFreeMemory && pLsaClose) { @@ -1825,6 +2069,7 @@ static void check_wellknown_name(const char* name, WELL_KNOWN_SID_TYPE result) sid_size = 0; domain_size = 0; ret = LookupAccountNameA(NULL, name, NULL, &sid_size, NULL, &domain_size, &sid_use); + ok(!ret, " %s Should have failed to lookup account name\n", name); psid = HeapAlloc(GetProcessHeap(),0,sid_size); domain = HeapAlloc(GetProcessHeap(),0,domain_size); ret = LookupAccountNameA(NULL, name, psid, &sid_size, domain, &domain_size, &sid_use); @@ -1888,12 +2133,6 @@ static void test_LookupAccountName(void) user_size = UNLEN + 1; SetLastError(0xdeadbeef); ret = GetUserNameA(user_name, &user_size); - if (!ret && (GetLastError() == ERROR_NOT_LOGGED_ON)) - { - /* Probably on win9x where the user used 'Cancel' instead of properly logging in */ - skip("Cannot get the user name (win9x and not logged in properly)\n"); - return; - } ok(ret, "Failed to get user name : %d\n", GetLastError()); /* get sizes */ @@ -1925,20 +2164,17 @@ static void test_LookupAccountName(void) get_sid_info(psid, &account, &sid_dom); ok(ret, "Failed to lookup account name\n"); ok(sid_size == GetLengthSid(psid), "Expected %d, got %d\n", GetLengthSid(psid), sid_size); - todo_wine - { - ok(!lstrcmp(account, user_name), "Expected %s, got %s\n", user_name, account); - ok(!lstrcmp(domain, sid_dom), "Expected %s, got %s\n", sid_dom, domain); - } + ok(!lstrcmp(account, user_name), "Expected %s, got %s\n", user_name, account); + ok(!lstrcmp(domain, sid_dom), "Expected %s, got %s\n", sid_dom, domain); ok(domain_size == domain_save - 1, "Expected %d, got %d\n", domain_save - 1, domain_size); ok(strlen(domain) == domain_size, "Expected %d, got %d\n", lstrlen(domain), domain_size); ok(sid_use == SidTypeUser, "Expected SidTypeUser (%d), got %d\n", SidTypeUser, sid_use); domain_size = domain_save; sid_size = sid_save; - if (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH) + if (PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH) { - skip("Non-english locale (test with hardcoded 'Everyone')\n"); + skip("Non-English locale (test with hardcoded 'Everyone')\n"); } else { @@ -2074,9 +2310,9 @@ static void test_LookupAccountName(void) return; } - if (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH) + if (PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH) { - skip("Non-english locale (skipping well known name creation tests)\n"); + skip("Non-English locale (skipping well known name creation tests)\n"); return; } @@ -2305,8 +2541,8 @@ static void test_process_security(void) psa.bInheritHandle = TRUE; /* Doesn't matter what ACL say we should get full access for ourselves */ - ok(CreateProcessA( NULL, buffer, &psa, NULL, FALSE, 0, NULL, NULL, &startup, &info ), - "CreateProcess with err:%d\n", GetLastError()); + res = CreateProcessA( NULL, buffer, &psa, NULL, FALSE, 0, NULL, NULL, &startup, &info ); + ok(res, "CreateProcess with err:%d\n", GetLastError()); TEST_GRANTED_ACCESS2( info.hProcess, PROCESS_ALL_ACCESS, STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL ); winetest_wait_child_process( info.hProcess ); @@ -2330,9 +2566,9 @@ static void test_process_security_child(void) ok(handle != NULL, "OpenProcess(PROCESS_TERMINATE) with err:%d\n", GetLastError()); TEST_GRANTED_ACCESS( handle, PROCESS_TERMINATE ); - ok(DuplicateHandle( GetCurrentProcess(), handle, GetCurrentProcess(), - &handle1, 0, TRUE, DUPLICATE_SAME_ACCESS ), - "duplicating handle err:%d\n", GetLastError()); + ret = DuplicateHandle( GetCurrentProcess(), handle, GetCurrentProcess(), + &handle1, 0, TRUE, DUPLICATE_SAME_ACCESS ); + ok(ret, "duplicating handle err:%d\n", GetLastError()); TEST_GRANTED_ACCESS( handle1, PROCESS_TERMINATE ); CloseHandle( handle1 ); @@ -2356,23 +2592,23 @@ static void test_process_security_child(void) ok(handle == NULL, "OpenProcess(PROCESS_ALL_ACCESS) should have failed\n"); /* Documented privilege elevation */ - ok(DuplicateHandle( GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), - &handle, 0, TRUE, DUPLICATE_SAME_ACCESS ), - "duplicating handle err:%d\n", GetLastError()); + ret = DuplicateHandle( GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), + &handle, 0, TRUE, DUPLICATE_SAME_ACCESS ); + ok(ret, "duplicating handle err:%d\n", GetLastError()); TEST_GRANTED_ACCESS2( handle, PROCESS_ALL_ACCESS, STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL ); CloseHandle( handle ); /* Same only explicitly asking for all access rights */ - ok(DuplicateHandle( GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), - &handle, PROCESS_ALL_ACCESS, TRUE, 0 ), - "duplicating handle err:%d\n", GetLastError()); + ret = DuplicateHandle( GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), + &handle, PROCESS_ALL_ACCESS, TRUE, 0 ); + ok(ret, "duplicating handle err:%d\n", GetLastError()); TEST_GRANTED_ACCESS2( handle, PROCESS_ALL_ACCESS, PROCESS_ALL_ACCESS | PROCESS_QUERY_LIMITED_INFORMATION ); - ok(DuplicateHandle( GetCurrentProcess(), handle, GetCurrentProcess(), - &handle1, PROCESS_VM_READ, TRUE, 0 ), - "duplicating handle err:%d\n", GetLastError()); + ret = DuplicateHandle( GetCurrentProcess(), handle, GetCurrentProcess(), + &handle1, PROCESS_VM_READ, TRUE, 0 ); + ok(ret, "duplicating handle err:%d\n", GetLastError()); TEST_GRANTED_ACCESS( handle1, PROCESS_VM_READ ); CloseHandle( handle1 ); CloseHandle( handle ); @@ -2391,7 +2627,7 @@ static void test_impersonation_level(void) HKEY hkey; DWORD error; - pDuplicateTokenEx = (fnDuplicateTokenEx) GetProcAddress(hmod, "DuplicateTokenEx"); + pDuplicateTokenEx = (void *)GetProcAddress(hmod, "DuplicateTokenEx"); if( !pDuplicateTokenEx ) { win_skip("DuplicateTokenEx is not available\n"); return; @@ -2490,7 +2726,7 @@ static void test_impersonation_level(void) HeapFree(GetProcessHeap(), 0, PrivilegeSet); } -static void test_SetEntriesInAcl(void) +static void test_SetEntriesInAclW(void) { DWORD res; PSID EveryoneSid = NULL, UsersSid = NULL; @@ -2561,13 +2797,13 @@ static void test_SetEntriesInAcl(void) ok(NewAcl != NULL, "returned acl was NULL\n"); LocalFree(NewAcl); - if (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH) + if (PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH) { - skip("Non-english locale (test with hardcoded 'Everyone')\n"); + skip("Non-English locale (test with hardcoded 'Everyone')\n"); } else { - ExplicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_USER; + ExplicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_NAME; ExplicitAccess.Trustee.ptstrName = (LPWSTR)wszEveryone; res = pSetEntriesInAclW(1, &ExplicitAccess, OldAcl, &NewAcl); ok(res == ERROR_SUCCESS, "SetEntriesInAclW failed: %u\n", res); @@ -2583,7 +2819,7 @@ static void test_SetEntriesInAcl(void) broken(NewAcl != NULL), /* NT4 */ "returned acl wasn't NULL: %p\n", NewAcl); - ExplicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_USER; + ExplicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_NAME; ExplicitAccess.Trustee.MultipleTrusteeOperation = TRUSTEE_IS_IMPERSONATE; res = pSetEntriesInAclW(1, &ExplicitAccess, OldAcl, &NewAcl); ok(res == ERROR_INVALID_PARAMETER || @@ -2601,7 +2837,7 @@ static void test_SetEntriesInAcl(void) LocalFree(NewAcl); } - ExplicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_USER; + ExplicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_NAME; ExplicitAccess.Trustee.ptstrName = (LPWSTR)wszCurrentUser; res = pSetEntriesInAclW(1, &ExplicitAccess, OldAcl, &NewAcl); ok(res == ERROR_SUCCESS, "SetEntriesInAclW failed: %u\n", res); @@ -2621,6 +2857,137 @@ static void test_SetEntriesInAcl(void) HeapFree(GetProcessHeap(), 0, OldAcl); } +static void test_SetEntriesInAclA(void) +{ + DWORD res; + PSID EveryoneSid = NULL, UsersSid = NULL; + PACL OldAcl = NULL, NewAcl; + SID_IDENTIFIER_AUTHORITY SIDAuthWorld = { SECURITY_WORLD_SID_AUTHORITY }; + SID_IDENTIFIER_AUTHORITY SIDAuthNT = { SECURITY_NT_AUTHORITY }; + EXPLICIT_ACCESS ExplicitAccess; + static const CHAR szEveryone[] = {'E','v','e','r','y','o','n','e',0}; + static const CHAR szCurrentUser[] = { 'C','U','R','R','E','N','T','_','U','S','E','R','\0'}; + + if (!pSetEntriesInAclA) + { + win_skip("SetEntriesInAclA is not available\n"); + return; + } + + NewAcl = (PACL)0xdeadbeef; + res = pSetEntriesInAclA(0, NULL, NULL, &NewAcl); + if(res == ERROR_CALL_NOT_IMPLEMENTED) + { + win_skip("SetEntriesInAclA is not implemented\n"); + return; + } + ok(res == ERROR_SUCCESS, "SetEntriesInAclA failed: %u\n", res); + ok(NewAcl == NULL || + broken(NewAcl != NULL), /* NT4 */ + "NewAcl=%p, expected NULL\n", NewAcl); + LocalFree(NewAcl); + + OldAcl = HeapAlloc(GetProcessHeap(), 0, 256); + res = InitializeAcl(OldAcl, 256, ACL_REVISION); + if(!res && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + { + win_skip("ACLs not implemented - skipping tests\n"); + HeapFree(GetProcessHeap(), 0, OldAcl); + return; + } + ok(res, "InitializeAcl failed with error %d\n", GetLastError()); + + res = AllocateAndInitializeSid( &SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &EveryoneSid); + ok(res, "AllocateAndInitializeSid failed with error %d\n", GetLastError()); + + res = AllocateAndInitializeSid( &SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &UsersSid); + ok(res, "AllocateAndInitializeSid failed with error %d\n", GetLastError()); + + res = AddAccessAllowedAce(OldAcl, ACL_REVISION, KEY_READ, UsersSid); + ok(res, "AddAccessAllowedAce failed with error %d\n", GetLastError()); + + ExplicitAccess.grfAccessPermissions = KEY_WRITE; + ExplicitAccess.grfAccessMode = GRANT_ACCESS; + ExplicitAccess.grfInheritance = NO_INHERITANCE; + ExplicitAccess.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + ExplicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_SID; + ExplicitAccess.Trustee.ptstrName = EveryoneSid; + ExplicitAccess.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ExplicitAccess.Trustee.pMultipleTrustee = NULL; + res = pSetEntriesInAclA(1, &ExplicitAccess, OldAcl, &NewAcl); + ok(res == ERROR_SUCCESS, "SetEntriesInAclA 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 = pSetEntriesInAclA(1, &ExplicitAccess, OldAcl, &NewAcl); + ok(res == ERROR_SUCCESS, "SetEntriesInAclA failed: %u\n", res); + ok(NewAcl != NULL, "returned acl was NULL\n"); + LocalFree(NewAcl); + + if (PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH) + { + skip("Non-English locale (test with hardcoded 'Everyone')\n"); + } + else + { + ExplicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_NAME; + ExplicitAccess.Trustee.ptstrName = (LPSTR)szEveryone; + res = pSetEntriesInAclA(1, &ExplicitAccess, OldAcl, &NewAcl); + ok(res == ERROR_SUCCESS, "SetEntriesInAclA failed: %u\n", res); + ok(NewAcl != NULL, "returned acl was NULL\n"); + LocalFree(NewAcl); + + ExplicitAccess.Trustee.TrusteeForm = TRUSTEE_BAD_FORM; + res = pSetEntriesInAclA(1, &ExplicitAccess, OldAcl, &NewAcl); + ok(res == ERROR_INVALID_PARAMETER || + broken(res == ERROR_NOT_SUPPORTED), /* NT4 */ + "SetEntriesInAclA failed: %u\n", res); + ok(NewAcl == NULL || + broken(NewAcl != NULL), /* NT4 */ + "returned acl wasn't NULL: %p\n", NewAcl); + + ExplicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_NAME; + ExplicitAccess.Trustee.MultipleTrusteeOperation = TRUSTEE_IS_IMPERSONATE; + res = pSetEntriesInAclA(1, &ExplicitAccess, OldAcl, &NewAcl); + ok(res == ERROR_INVALID_PARAMETER || + broken(res == ERROR_NOT_SUPPORTED), /* NT4 */ + "SetEntriesInAclA failed: %u\n", res); + ok(NewAcl == NULL || + broken(NewAcl != NULL), /* NT4 */ + "returned acl wasn't NULL: %p\n", NewAcl); + + ExplicitAccess.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ExplicitAccess.grfAccessMode = SET_ACCESS; + res = pSetEntriesInAclA(1, &ExplicitAccess, OldAcl, &NewAcl); + ok(res == ERROR_SUCCESS, "SetEntriesInAclA failed: %u\n", res); + ok(NewAcl != NULL, "returned acl was NULL\n"); + LocalFree(NewAcl); + } + + ExplicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_NAME; + ExplicitAccess.Trustee.ptstrName = (LPSTR)szCurrentUser; + res = pSetEntriesInAclA(1, &ExplicitAccess, OldAcl, &NewAcl); + ok(res == ERROR_SUCCESS, "SetEntriesInAclA failed: %u\n", res); + ok(NewAcl != NULL, "returned acl was NULL\n"); + LocalFree(NewAcl); + + ExplicitAccess.grfAccessMode = REVOKE_ACCESS; + ExplicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_SID; + ExplicitAccess.Trustee.ptstrName = UsersSid; + res = pSetEntriesInAclA(1, &ExplicitAccess, OldAcl, &NewAcl); + ok(res == ERROR_SUCCESS, "SetEntriesInAclA failed: %u\n", res); + ok(NewAcl != NULL, "returned acl was NULL\n"); + LocalFree(NewAcl); + + FreeSid(UsersSid); + FreeSid(EveryoneSid); + HeapFree(GetProcessHeap(), 0, OldAcl); +} + static void test_GetNamedSecurityInfoA(void) { PSECURITY_DESCRIPTOR pSecDesc; @@ -2628,10 +2995,11 @@ static void test_GetNamedSecurityInfoA(void) SECURITY_DESCRIPTOR_CONTROL control; PSID owner; PSID group; + PACL dacl; BOOL owner_defaulted; BOOL group_defaulted; DWORD error; - BOOL ret; + BOOL ret, isNT4; CHAR windows_dir[MAX_PATH]; if (!pGetNamedSecurityInfoA) @@ -2660,13 +3028,38 @@ static void test_GetNamedSecurityInfoA(void) broken((control & (SE_SELF_RELATIVE|SE_DACL_PRESENT)) == SE_DACL_PRESENT), /* NT4 */ "control (0x%x) doesn't have (SE_SELF_RELATIVE|SE_DACL_PRESENT) flags set\n", control); ok(revision == SECURITY_DESCRIPTOR_REVISION1, "revision was %d instead of 1\n", revision); + + isNT4 = (control & (SE_SELF_RELATIVE|SE_DACL_PRESENT)) == SE_DACL_PRESENT; + ret = GetSecurityDescriptorOwner(pSecDesc, &owner, &owner_defaulted); ok(ret, "GetSecurityDescriptorOwner failed with error %d\n", GetLastError()); ok(owner != NULL, "owner should not be NULL\n"); + ret = GetSecurityDescriptorGroup(pSecDesc, &group, &group_defaulted); ok(ret, "GetSecurityDescriptorGroup failed with error %d\n", GetLastError()); ok(group != NULL, "group should not be NULL\n"); LocalFree(pSecDesc); + + + /* NULL descriptor tests */ + if(isNT4) + { + win_skip("NT4 does not support GetNamedSecutityInfo with a NULL descriptor\n"); + return; + } + + error = pGetNamedSecurityInfoA(windows_dir, SE_FILE_OBJECT,DACL_SECURITY_INFORMATION, + NULL, NULL, NULL, NULL, NULL); + ok(error==ERROR_INVALID_PARAMETER, "GetNamedSecurityInfo failed with error %d\n", error); + + error = pGetNamedSecurityInfoA(windows_dir, SE_FILE_OBJECT,DACL_SECURITY_INFORMATION, + NULL, NULL, &dacl, NULL, NULL); + ok(!error, "GetNamedSecurityInfo failed with error %d\n", error); + ok(dacl != NULL, "dacl should not be NULL\n"); + + error = pGetNamedSecurityInfoA(windows_dir, SE_FILE_OBJECT,OWNER_SECURITY_INFORMATION, + NULL, NULL, &dacl, NULL, NULL); + ok(error==ERROR_INVALID_PARAMETER, "GetNamedSecurityInfo failed with error %d\n", error); } static void test_ConvertStringSecurityDescriptor(void) @@ -2942,7 +3335,6 @@ static void test_SetSecurityDescriptorControl (PSECURITY_DESCRIPTOR sec) GetSecurityDescriptorControl(sec, &test, &dwRevision); expect_eq(test, ctrl, int, "%x"); - ctrl = ref; setOrClear ^= bitOfInterest; SetLastError (0xbebecaca); pSetSecurityDescriptorControl (sec, bitOfInterest, setOrClear); @@ -2994,6 +3386,7 @@ static void test_PrivateObjectSecurity(void) LPSTR string; ULONG len; PSECURITY_DESCRIPTOR buf; + BOOL ret; if (!pConvertStringSecurityDescriptorToSecurityDescriptorA) { @@ -3032,25 +3425,26 @@ static void test_PrivateObjectSecurity(void) GetSecurityDescriptorControl(sec, &ctrl, &dwRevision); expect_eq(ctrl, 0x9014, int, "%x"); - ok(GetPrivateObjectSecurity(sec, GROUP_SECURITY_INFORMATION, buf, dwDescSize, &retSize), - "GetPrivateObjectSecurity failed (err=%u)\n", GetLastError()); + ret = GetPrivateObjectSecurity(sec, GROUP_SECURITY_INFORMATION, buf, dwDescSize, &retSize); + ok(ret, "GetPrivateObjectSecurity failed (err=%u)\n", GetLastError()); ok(retSize <= dwDescSize, "Buffer too small (%d vs %d)\n", retSize, dwDescSize); ok(pConvertSecurityDescriptorToStringSecurityDescriptorA(buf, SDDL_REVISION_1, sec_info, &string, &len), "Conversion failed\n"); CHECK_RESULT_AND_FREE("G:S-1-5-21-93476-23408-4576"); GetSecurityDescriptorControl(buf, &ctrl, &dwRevision); expect_eq(ctrl, 0x8000, int, "%x"); - ok(GetPrivateObjectSecurity(sec, GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, buf, dwDescSize, &retSize), - "GetPrivateObjectSecurity failed (err=%u)\n", GetLastError()); + ret = GetPrivateObjectSecurity(sec, GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, buf, dwDescSize, &retSize); + ok(ret, "GetPrivateObjectSecurity failed (err=%u)\n", GetLastError()); ok(retSize <= dwDescSize, "Buffer too small (%d vs %d)\n", retSize, dwDescSize); - ok(pConvertSecurityDescriptorToStringSecurityDescriptorA(buf, SDDL_REVISION_1, sec_info, &string, &len), "Conversion failed err=%u\n", GetLastError()); + ret = pConvertSecurityDescriptorToStringSecurityDescriptorA(buf, SDDL_REVISION_1, sec_info, &string, &len); + ok(ret, "Conversion failed err=%u\n", GetLastError()); CHECK_ONE_OF_AND_FREE("G:S-1-5-21-93476-23408-4576D:(A;NP;GAGXGWGR;;;SU)(A;IOID;CCDC;;;SU)(D;OICI;0xffffffff;;;S-1-5-21-93476-23408-4576)", "G:S-1-5-21-93476-23408-4576D:P(A;NP;GAGXGWGR;;;SU)(A;IOID;CCDC;;;SU)(D;OICI;0xffffffff;;;S-1-5-21-93476-23408-4576)"); /* Win7 */ GetSecurityDescriptorControl(buf, &ctrl, &dwRevision); expect_eq(ctrl & (~ SE_DACL_PROTECTED), 0x8004, int, "%x"); - ok(GetPrivateObjectSecurity(sec, sec_info, buf, dwDescSize, &retSize), - "GetPrivateObjectSecurity failed (err=%u)\n", GetLastError()); + ret = GetPrivateObjectSecurity(sec, sec_info, buf, dwDescSize, &retSize); + ok(ret, "GetPrivateObjectSecurity failed (err=%u)\n", GetLastError()); ok(retSize == dwDescSize, "Buffer too small (%d vs %d)\n", retSize, dwDescSize); ok(pConvertSecurityDescriptorToStringSecurityDescriptorA(buf, SDDL_REVISION_1, sec_info, &string, &len), "Conversion failed\n"); CHECK_ONE_OF_AND_FREE("O:SY" @@ -3264,21 +3658,23 @@ static void test_CheckTokenMembership(void) return; } + is_member = FALSE; 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"); + is_member = FALSE; 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"); + is_member = TRUE; + SetLastError(0xdeadbeef); 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); @@ -3350,6 +3746,241 @@ static void test_EqualSid(void) FreeSid(sid2); } +static void test_GetUserNameA(void) +{ + char buffer[UNLEN + 1], filler[UNLEN + 1]; + DWORD required_len, buffer_len; + BOOL ret; + + /* Test crashes on Windows. */ + if (0) + { + SetLastError(0xdeadbeef); + GetUserNameA(NULL, NULL); + } + + SetLastError(0xdeadbeef); + required_len = 0; + ret = GetUserNameA(NULL, &required_len); + ok(ret == FALSE, "GetUserNameA returned %d\n", ret); + ok(required_len != 0, "Outputted buffer length was %u\n", required_len); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Last error was %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + required_len = 1; + ret = GetUserNameA(NULL, &required_len); + ok(ret == FALSE, "GetUserNameA returned %d\n", ret); + ok(required_len != 0 && required_len != 1, "Outputted buffer length was %u\n", required_len); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Last error was %u\n", GetLastError()); + + /* Tests crashes on Windows. */ + if (0) + { + SetLastError(0xdeadbeef); + required_len = UNLEN + 1; + GetUserNameA(NULL, &required_len); + + SetLastError(0xdeadbeef); + GetUserNameA(buffer, NULL); + } + + memset(filler, 'x', sizeof(filler)); + + /* Note that GetUserNameA on XP and newer outputs the number of bytes + * required for a Unicode string, which affects a test in the next block. */ + SetLastError(0xdeadbeef); + memcpy(buffer, filler, sizeof(filler)); + required_len = 0; + ret = GetUserNameA(buffer, &required_len); + ok(ret == FALSE, "GetUserNameA returned %d\n", ret); + ok(!memcmp(buffer, filler, sizeof(filler)), "Output buffer was altered\n"); + ok(required_len != 0, "Outputted buffer length was %u\n", required_len); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Last error was %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + memcpy(buffer, filler, sizeof(filler)); + buffer_len = required_len; + ret = GetUserNameA(buffer, &buffer_len); + ok(ret == TRUE, "GetUserNameA returned %d, last error %u\n", ret, GetLastError()); + ok(memcmp(buffer, filler, sizeof(filler)) != 0, "Output buffer was untouched\n"); + ok(buffer_len == required_len || + broken(buffer_len == required_len / sizeof(WCHAR)), /* XP+ */ + "Outputted buffer length was %u\n", buffer_len); + + /* Use the reported buffer size from the last GetUserNameA call and pass + * a length that is one less than the required value. */ + SetLastError(0xdeadbeef); + memcpy(buffer, filler, sizeof(filler)); + buffer_len--; + ret = GetUserNameA(buffer, &buffer_len); + ok(ret == FALSE, "GetUserNameA returned %d\n", ret); + ok(!memcmp(buffer, filler, sizeof(filler)), "Output buffer was untouched\n"); + ok(buffer_len == required_len, "Outputted buffer length was %u\n", buffer_len); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Last error was %u\n", GetLastError()); +} + +static void test_GetUserNameW(void) +{ + WCHAR buffer[UNLEN + 1], filler[UNLEN + 1]; + DWORD required_len, buffer_len; + BOOL ret; + + /* Test crashes on Windows. */ + if (0) + { + SetLastError(0xdeadbeef); + GetUserNameW(NULL, NULL); + } + + SetLastError(0xdeadbeef); + required_len = 0; + ret = GetUserNameW(NULL, &required_len); + ok(ret == FALSE, "GetUserNameW returned %d\n", ret); + ok(required_len != 0, "Outputted buffer length was %u\n", required_len); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Last error was %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + required_len = 1; + ret = GetUserNameW(NULL, &required_len); + ok(ret == FALSE, "GetUserNameW returned %d\n", ret); + ok(required_len != 0 && required_len != 1, "Outputted buffer length was %u\n", required_len); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Last error was %u\n", GetLastError()); + + /* Tests crash on Windows. */ + if (0) + { + SetLastError(0xdeadbeef); + required_len = UNLEN + 1; + GetUserNameW(NULL, &required_len); + + SetLastError(0xdeadbeef); + GetUserNameW(buffer, NULL); + } + + memset(filler, 'x', sizeof(filler)); + + SetLastError(0xdeadbeef); + memcpy(buffer, filler, sizeof(filler)); + required_len = 0; + ret = GetUserNameW(buffer, &required_len); + ok(ret == FALSE, "GetUserNameW returned %d\n", ret); + ok(!memcmp(buffer, filler, sizeof(filler)), "Output buffer was altered\n"); + ok(required_len != 0, "Outputted buffer length was %u\n", required_len); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Last error was %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + memcpy(buffer, filler, sizeof(filler)); + buffer_len = required_len; + ret = GetUserNameW(buffer, &buffer_len); + ok(ret == TRUE, "GetUserNameW returned %d, last error %u\n", ret, GetLastError()); + ok(memcmp(buffer, filler, sizeof(filler)) != 0, "Output buffer was untouched\n"); + ok(buffer_len == required_len, "Outputted buffer length was %u\n", buffer_len); + + /* GetUserNameW on XP and newer writes a truncated portion of the username string to the buffer. */ + SetLastError(0xdeadbeef); + memcpy(buffer, filler, sizeof(filler)); + buffer_len--; + ret = GetUserNameW(buffer, &buffer_len); + ok(ret == FALSE, "GetUserNameW returned %d\n", ret); + ok(!memcmp(buffer, filler, sizeof(filler)) || + broken(memcmp(buffer, filler, sizeof(filler)) != 0), /* XP+ */ + "Output buffer was altered\n"); + ok(buffer_len == required_len, "Outputted buffer length was %u\n", buffer_len); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Last error was %u\n", GetLastError()); +} + +static void test_CreateRestrictedToken(void) +{ + HANDLE process_token, token, r_token; + PTOKEN_GROUPS token_groups, groups2; + SID_AND_ATTRIBUTES sattr; + BOOL is_member; + DWORD size; + BOOL ret; + DWORD i, j; + + if (!pCreateRestrictedToken) + { + win_skip("CreateRestrictedToken is not available\n"); + return; + } + + ret = OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE|TOKEN_QUERY, &process_token); + ok(ret, "got error %d\n", GetLastError()); + + ret = DuplicateTokenEx(process_token, TOKEN_DUPLICATE|TOKEN_ADJUST_GROUPS|TOKEN_QUERY, + NULL, SecurityImpersonation, TokenImpersonation, &token); + ok(ret, "got error %d\n", GetLastError()); + + /* groups */ + ret = GetTokenInformation(token, TokenGroups, NULL, 0, &size); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "got %d with error %d\n", ret, GetLastError()); + token_groups = HeapAlloc(GetProcessHeap(), 0, size); + ret = GetTokenInformation(token, TokenGroups, token_groups, size, &size); + ok(ret, "got 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; + } + + is_member = FALSE; + ret = pCheckTokenMembership(token, token_groups->Groups[i].Sid, &is_member); + ok(ret, "got error %d\n", GetLastError()); + ok(is_member, "not a member\n"); + + /* disable a SID in new token */ + sattr.Sid = token_groups->Groups[i].Sid; + sattr.Attributes = 0; + r_token = NULL; + ret = pCreateRestrictedToken(token, 0, 1, &sattr, 0, NULL, 0, NULL, &r_token); + todo_wine ok(ret, "got error %d\n", GetLastError()); + + if (ret) + { + /* check if a SID is enabled */ + is_member = TRUE; + ret = pCheckTokenMembership(r_token, token_groups->Groups[i].Sid, &is_member); + ok(ret, "got error %d\n", GetLastError()); + ok(!is_member, "not a member\n"); + + ret = GetTokenInformation(r_token, TokenGroups, NULL, 0, &size); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d with error %d\n", + ret, GetLastError()); + groups2 = HeapAlloc(GetProcessHeap(), 0, size); + ret = GetTokenInformation(r_token, TokenGroups, groups2, size, &size); + ok(ret, "got error %d\n", GetLastError()); + + for (j = 0; j < groups2->GroupCount; j++) + { + if (EqualSid(groups2->Groups[j].Sid, token_groups->Groups[i].Sid)) + break; + } + + ok(groups2->Groups[j].Attributes & SE_GROUP_USE_FOR_DENY_ONLY, + "got wrong attributes\n"); + ok((groups2->Groups[j].Attributes & SE_GROUP_ENABLED) == 0, + "got wrong attributes\n"); + + HeapFree(GetProcessHeap(), 0, groups2); + } + + HeapFree(GetProcessHeap(), 0, token_groups); + CloseHandle(r_token); + CloseHandle(token); + CloseHandle(process_token); +} + START_TEST(security) { init(); @@ -3372,7 +4003,8 @@ START_TEST(security) test_security_descriptor(); test_process_security(); test_impersonation_level(); - test_SetEntriesInAcl(); + test_SetEntriesInAclW(); + test_SetEntriesInAclA(); test_GetNamedSecurityInfoA(); test_ConvertStringSecurityDescriptor(); test_ConvertSecurityDescriptorToString(); @@ -3382,4 +4014,7 @@ START_TEST(security) test_GetSidSubAuthority(); test_CheckTokenMembership(); test_EqualSid(); + test_GetUserNameA(); + test_GetUserNameW(); + test_CreateRestrictedToken(); } diff --git a/rostests/winetests/advapi32/service.c b/rostests/winetests/advapi32/service.c index 269ff6156af..ecf3a5e5f79 100644 --- a/rostests/winetests/advapi32/service.c +++ b/rostests/winetests/advapi32/service.c @@ -34,6 +34,7 @@ //static const CHAR spooler[] = "Spooler"; /* Should be available on all platforms */ static const CHAR spooler[] = "Eventlog"; /* All platform except reactos :-/ */ +static const CHAR* selfname; static BOOL (WINAPI *pChangeServiceConfig2A)(SC_HANDLE,DWORD,LPVOID); static BOOL (WINAPI *pEnumServicesStatusExA)(SC_HANDLE, SC_ENUM_TYPE, DWORD, @@ -336,6 +337,13 @@ static void test_create_delete_svc(void) ok(!svc_handle1, "Expected failure\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + /* Test if ServiceType can be a combined one for drivers */ + SetLastError(0xdeadbeef); + svc_handle1 = CreateServiceA(scm_handle, servicename, NULL, 0, SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER, + SERVICE_BOOT_START, 0, pathname, NULL, NULL, NULL, NULL, NULL); + ok(!svc_handle1, "Expected failure\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + /* The service already exists (check first, just in case) */ svc_handle1 = OpenServiceA(scm_handle, spooler, GENERIC_READ); if (svc_handle1) @@ -602,7 +610,7 @@ static void test_get_displayname(void) ok(ret, "Expected success, got error %u\n", GetLastError()); /* Test that shows that if the buffersize is enough, it's not changed */ ok(displaysize == tempsize * 2, "Expected no change for the needed buffer size\n"); - ok(lstrlen(displayname) == tempsize/2, + ok(strlen(displayname) == tempsize/2, "Expected the buffer to be twice the length of the string\n") ; /* Do the buffer(size) tests also for GetServiceDisplayNameW */ @@ -672,7 +680,7 @@ static void test_get_displayname(void) displaysize = -1; ret = GetServiceDisplayNameA(scm_handle, servicename, NULL, &displaysize); ok(!ret, "Expected failure\n"); - ok(displaysize == lstrlen(servicename) * 2, + ok(displaysize == strlen(servicename) * 2, "Expected the displaysize to be twice the size of the servicename\n"); ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError()); @@ -857,7 +865,7 @@ static void test_get_servicekeyname(void) ok(ret, "Expected success, got error %u\n", GetLastError()); if (ret) { - ok(lstrlen(servicename) == tempsize/2, + ok(strlen(servicename) == tempsize/2, "Expected the buffer to be twice the length of the string\n") ; ok(!lstrcmpi(servicename, spooler), "Expected %s, got %s\n", spooler, servicename); ok(servicesize == (tempsize * 2), @@ -871,7 +879,7 @@ static void test_get_servicekeyname(void) ok(ret, "Expected success, got error %u\n", GetLastError()); if (ret) { - ok(lstrlen(servicename) == tempsize/2, + ok(strlen(servicename) == tempsize/2, "Expected the buffer to be twice the length of the string\n") ; ok(servicesize == lstrlenW(servicenameW), "Expected servicesize not to change if buffer not insufficient\n") ; @@ -919,7 +927,6 @@ static void test_query_svc(void) SetLastError(0xdeadbeef); ret = QueryServiceStatus(svc_handle, NULL); ok(!ret, "Expected failure\n"); - todo_wine ok(GetLastError() == ERROR_INVALID_ADDRESS || GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */, "Unexpected last error %d\n", GetLastError()); @@ -943,6 +950,12 @@ static void test_query_svc(void) CloseServiceHandle(svc_handle); /* More or less the same tests for QueryServiceStatusEx */ + if (!pQueryServiceStatusEx) + { + win_skip( "QueryServiceStatusEx not available\n" ); + CloseServiceHandle(scm_handle); + return; + } /* Open service with not enough rights to query the status */ svc_handle = OpenServiceA(scm_handle, spooler, STANDARD_RIGHTS_READ); @@ -951,7 +964,6 @@ static void test_query_svc(void) SetLastError(0xdeadbeef); ret = pQueryServiceStatusEx(NULL, 1, NULL, 0, NULL); ok(!ret, "Expected failure\n"); - todo_wine ok(GetLastError() == ERROR_INVALID_LEVEL, "Expected ERROR_INVALID_LEVEL, got %d\n", GetLastError()); @@ -961,8 +973,8 @@ static void test_query_svc(void) /* Only info level is correct. It looks like the buffer/size is checked second */ SetLastError(0xdeadbeef); - ret = pQueryServiceStatusEx(NULL, 0, NULL, 0, &needed); - /* NT4 and Wine check the handle first */ + ret = pQueryServiceStatusEx(NULL, SC_STATUS_PROCESS_INFO, NULL, 0, &needed); + /* NT4 checks the handle first */ if (GetLastError() != ERROR_INVALID_HANDLE) { ok(!ret, "Expected failure\n"); @@ -976,7 +988,7 @@ static void test_query_svc(void) statusproc = HeapAlloc(GetProcessHeap(), 0, sizeof(SERVICE_STATUS_PROCESS)); bufsize = needed; SetLastError(0xdeadbeef); - ret = pQueryServiceStatusEx(NULL, 0, (BYTE*)statusproc, bufsize, &needed); + ret = pQueryServiceStatusEx(NULL, SC_STATUS_PROCESS_INFO, (BYTE*)statusproc, bufsize, &needed); ok(!ret, "Expected failure\n"); ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); @@ -984,25 +996,22 @@ static void test_query_svc(void) /* Correct handle and info level */ SetLastError(0xdeadbeef); - ret = pQueryServiceStatusEx(svc_handle, 0, NULL, 0, &needed); + ret = pQueryServiceStatusEx(svc_handle, SC_STATUS_PROCESS_INFO, NULL, 0, &needed); /* NT4 doesn't return the needed size */ if (GetLastError() != ERROR_INVALID_PARAMETER) { ok(!ret, "Expected failure\n"); - todo_wine - { ok(needed == sizeof(SERVICE_STATUS_PROCESS), "Needed buffersize is wrong : %d\n", needed); ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError()); - } } /* All parameters are OK but we don't have enough rights */ statusproc = HeapAlloc(GetProcessHeap(), 0, sizeof(SERVICE_STATUS_PROCESS)); bufsize = sizeof(SERVICE_STATUS_PROCESS); SetLastError(0xdeadbeef); - ret = pQueryServiceStatusEx(svc_handle, 0, (BYTE*)statusproc, bufsize, &needed); + ret = pQueryServiceStatusEx(svc_handle, SC_STATUS_PROCESS_INFO, (BYTE*)statusproc, bufsize, &needed); ok(!ret, "Expected failure\n"); ok(GetLastError() == ERROR_ACCESS_DENIED, "Expected ERROR_ACCESS_DENIED, got %d\n", GetLastError()); @@ -1016,7 +1025,7 @@ static void test_query_svc(void) statusproc = HeapAlloc(GetProcessHeap(), 0, sizeof(SERVICE_STATUS_PROCESS)); bufsize = sizeof(SERVICE_STATUS_PROCESS); SetLastError(0xdeadbeef); - ret = pQueryServiceStatusEx(svc_handle, 0, (BYTE*)statusproc, bufsize, &needed); + ret = pQueryServiceStatusEx(svc_handle, SC_STATUS_PROCESS_INFO, (BYTE*)statusproc, bufsize, &needed); ok(ret, "Expected success, got error %u\n", GetLastError()); if (statusproc->dwCurrentState == SERVICE_RUNNING) ok(statusproc->dwProcessId != 0, @@ -1039,14 +1048,20 @@ static void test_enum_svc(void) DWORD tempneeded, tempreturned, missing; DWORD servicecountactive, servicecountinactive; ENUM_SERVICE_STATUS *services; + ENUM_SERVICE_STATUSW *servicesW; ENUM_SERVICE_STATUS_PROCESS *exservices; - INT i; + UINT i; /* All NULL or wrong */ SetLastError(0xdeadbeef); ret = EnumServicesStatusA(NULL, 1, 0, NULL, 0, NULL, NULL, NULL); ok(!ret, "Expected failure\n"); - todo_wine + ok(GetLastError() == ERROR_INVALID_HANDLE, + "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = EnumServicesStatusW(NULL, 1, 0, NULL, 0, NULL, NULL, NULL); + ok(!ret, "Expected failure\n"); ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); @@ -1057,7 +1072,13 @@ static void test_enum_svc(void) SetLastError(0xdeadbeef); ret = EnumServicesStatusA(scm_handle, 1, 0, NULL, 0, NULL, NULL, NULL); ok(!ret, "Expected failure\n"); - todo_wine + ok(GetLastError() == ERROR_INVALID_ADDRESS || + GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */, + "Unexpected last error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = EnumServicesStatusW(scm_handle, 1, 0, NULL, 0, NULL, NULL, NULL); + ok(!ret, "Expected failure\n"); ok(GetLastError() == ERROR_INVALID_ADDRESS || GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */, "Unexpected last error %d\n", GetLastError()); @@ -1068,7 +1089,15 @@ static void test_enum_svc(void) ret = EnumServicesStatusA(scm_handle, 0, 0, NULL, 0, NULL, &returned, NULL); ok(!ret, "Expected failure\n"); ok(returned == 0xdeadbeef, "Expected no change to the number of services variable\n"); - todo_wine + ok(GetLastError() == ERROR_INVALID_ADDRESS || + GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */, + "Unexpected last error %d\n", GetLastError()); + + returned = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = EnumServicesStatusW(scm_handle, 0, 0, NULL, 0, NULL, &returned, NULL); + ok(!ret, "Expected failure\n"); + ok(returned == 0xdeadbeef, "Expected no change to the number of services variable\n"); ok(GetLastError() == ERROR_INVALID_ADDRESS || GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */, "Unexpected last error %d\n", GetLastError()); @@ -1080,7 +1109,16 @@ static void test_enum_svc(void) ok(!ret, "Expected failure\n"); ok(needed == 0xdeadbeef || broken(needed != 0xdeadbeef), /* nt4 */ "Expected no change to the needed buffer variable\n"); - todo_wine + ok(GetLastError() == ERROR_INVALID_ADDRESS || + GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */, + "Unexpected last error %d\n", GetLastError()); + + needed = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = EnumServicesStatusW(scm_handle, 0, 0, NULL, 0, &needed, NULL, NULL); + ok(!ret, "Expected failure\n"); + ok(needed == 0xdeadbeef || broken(needed != 0xdeadbeef), /* nt4 */ + "Expected no change to the needed buffer variable\n"); ok(GetLastError() == ERROR_INVALID_ADDRESS || GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */, "Unexpected last error %d\n", GetLastError()); @@ -1091,59 +1129,93 @@ static void test_enum_svc(void) SetLastError(0xdeadbeef); ret = EnumServicesStatusA(scm_handle, 0, 0, NULL, 0, &needed, &returned, NULL); ok(!ret, "Expected failure\n"); - todo_wine - { ok(needed == 0 || broken(needed != 0), /* nt4 */ "Expected needed buffer size to be set to 0, got %d\n", needed); ok(returned == 0, "Expected number of services to be set to 0, got %d\n", returned); ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); - } - /* No valid servicetype and servicestate */ + needed = 0xdeadbeef; + returned = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = EnumServicesStatusW(scm_handle, 0, 0, NULL, 0, &needed, &returned, NULL); + ok(!ret, "Expected failure\n"); + ok(needed == 0 || broken(needed != 0), /* nt4 */ + "Expected needed buffer size to be set to 0, got %d\n", needed); + ok(returned == 0 || broken(returned != 0), /* nt4 */ + "Expected number of services to be set to 0, got %d\n", returned); + ok(GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* No valid servicestate */ needed = 0xdeadbeef; returned = 0xdeadbeef; SetLastError(0xdeadbeef); ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, 0, NULL, 0, &needed, &returned, NULL); ok(!ret, "Expected failure\n"); - todo_wine - { ok(needed == 0 || broken(needed != 0), /* nt4 */ "Expected needed buffer size to be set to 0, got %d\n", needed); ok(returned == 0, "Expected number of services to be set to 0, got %d\n", returned); ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); - } - /* No valid servicetype and servicestate */ needed = 0xdeadbeef; returned = 0xdeadbeef; SetLastError(0xdeadbeef); - ret = EnumServicesStatusA(scm_handle, 0, SERVICE_STATE_ALL, NULL, 0, - &needed, &returned, NULL); + ret = EnumServicesStatusW(scm_handle, SERVICE_WIN32, 0, NULL, 0, &needed, &returned, NULL); + ok(!ret, "Expected failure\n"); + ok(needed == 0 || broken(needed != 0), /* nt4 */ + "Expected needed buffer size to be set to 0, got %d\n", needed); + ok(returned == 0 || broken(returned != 0), /* nt4 */ + "Expected number of services to be set to 0, got %d\n", returned); + ok(GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* No valid servicetype */ + needed = 0xdeadbeef; + returned = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = EnumServicesStatusA(scm_handle, 0, SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL); ok(!ret, "Expected failure\n"); - todo_wine - { ok(needed == 0 || broken(needed != 0), /* nt4 */ "Expected needed buffer size to be set to 0, got %d\n", needed); ok(returned == 0, "Expected number of services to be set to 0, got %d\n", returned); ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); - } + + needed = 0xdeadbeef; + returned = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = EnumServicesStatusW(scm_handle, 0, SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL); + ok(!ret, "Expected failure\n"); + ok(needed == 0 || broken(needed != 0), /* nt4 */ + "Expected needed buffer size to be set to 0, got %d\n", needed); + ok(returned == 0 || broken(returned != 0), /* nt4 */ + "Expected number of services to be set to 0, got %d\n", returned); + ok(GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); /* All parameters are correct but our access rights are wrong */ needed = 0xdeadbeef; returned = 0xdeadbeef; SetLastError(0xdeadbeef); - ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, - &needed, &returned, NULL); + ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL); ok(!ret, "Expected failure\n"); - todo_wine - { ok(needed == 0 || broken(needed != 0), /* nt4 */ "Expected needed buffer size to be set to 0, got %d\n", needed); ok(returned == 0, "Expected number of services to be set to 0, got %d\n", returned); - } + ok(GetLastError() == ERROR_ACCESS_DENIED, + "Expected ERROR_ACCESS_DENIED, got %d\n", GetLastError()); + + needed = 0xdeadbeef; + returned = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = EnumServicesStatusW(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL); + ok(!ret, "Expected failure\n"); + ok(needed == 0 || broken(needed != 0), /* nt4 */ + "Expected needed buffer size to be set to 0, got %d\n", needed); + ok(returned == 0 || broken(returned != 0), /* nt4 */ + "Expected number of services to be set to 0, got %d\n", returned); ok(GetLastError() == ERROR_ACCESS_DENIED, "Expected ERROR_ACCESS_DENIED, got %d\n", GetLastError()); @@ -1155,22 +1227,24 @@ static void test_enum_svc(void) needed = 0xdeadbeef; returned = 0xdeadbeef; SetLastError(0xdeadbeef); - ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, - &needed, &returned, NULL); + ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL); ok(!ret, "Expected failure\n"); - todo_wine - { ok(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size for this one service\n"); ok(returned == 0, "Expected no service returned, got %d\n", returned); ok(GetLastError() == ERROR_MORE_DATA, "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); + returnedW = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = EnumServicesStatusW(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &neededW, &returnedW, NULL); + ok(!ret, "Expected failure\n"); + ok(neededW != 0xdeadbeef && neededW > 0, "Expected the needed buffer size for this one service\n"); ok(neededW == needed, "Expected needed buffersize to be the same for A- and W-calls\n"); + ok(returnedW == 0, "Expected no service returned, got %d\n", returnedW); + ok(GetLastError() == ERROR_MORE_DATA, + "Expected ERROR_MORE_DATA, got %d\n", GetLastError()); /* Store the needed bytes */ tempneeded = needed; @@ -1183,17 +1257,26 @@ static void test_enum_svc(void) SetLastError(0xdeadbeef); ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, services, bufsize, &needed, &returned, NULL); - todo_wine - { ok(ret, "Expected success, got error %u\n", GetLastError()); ok(needed == 0, "Expected needed buffer to be 0 as we are done\n"); ok(returned != 0xdeadbeef && returned > 0, "Expected some returned services\n"); - } HeapFree(GetProcessHeap(), 0, services); /* Store the number of returned services */ tempreturned = returned; + servicesW = HeapAlloc(GetProcessHeap(), 0, neededW); + bufsize = neededW; + neededW = 0xdeadbeef; + returnedW = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = EnumServicesStatusW(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, + servicesW, bufsize, &neededW, &returnedW, NULL); + ok(ret, "Expected success, got error %u\n", GetLastError()); + ok(neededW == 0, "Expected needed buffer to be 0 as we are done\n"); + ok(returnedW != 0xdeadbeef && returnedW > 0, "Expected some returned services\n"); + HeapFree(GetProcessHeap(), 0, servicesW); + /* Allocate less than the needed bytes and don't specify a resume handle */ services = HeapAlloc(GetProcessHeap(), 0, tempneeded); bufsize = (tempreturned - 1) * sizeof(ENUM_SERVICE_STATUS); @@ -1203,13 +1286,10 @@ static void test_enum_svc(void) ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, services, bufsize, &needed, &returned, NULL); ok(!ret, "Expected failure\n"); - todo_wine - { ok(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size for this one service\n"); ok(returned < tempreturned, "Expected fewer services to be returned\n"); ok(GetLastError() == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", GetLastError()); - } /* Allocate less than the needed bytes, this time with a correct resume handle */ bufsize = (tempreturned - 1) * sizeof(ENUM_SERVICE_STATUS); @@ -1220,14 +1300,11 @@ static void test_enum_svc(void) ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, services, bufsize, &needed, &returned, &resume); ok(!ret, "Expected failure\n"); - todo_wine - { ok(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size for this one service\n"); ok(returned < tempreturned, "Expected fewer services to be returned\n"); - ok(resume, "Expected a resume handle\n"); + todo_wine ok(resume, "Expected a resume handle\n"); ok(GetLastError() == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", GetLastError()); - } /* Fetch the missing services but pass a bigger buffer size */ missing = tempreturned - returned; @@ -1237,12 +1314,9 @@ static void test_enum_svc(void) SetLastError(0xdeadbeef); ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, services, bufsize, &needed, &returned, &resume); - todo_wine - { ok(ret, "Expected success, got error %u\n", GetLastError()); ok(needed == 0, "Expected needed buffer to be 0 as we are done\n"); ok(returned == missing, "Expected %u services to be returned\n", missing); - } ok(resume == 0, "Expected the resume handle to be 0\n"); HeapFree(GetProcessHeap(), 0, services); @@ -1287,7 +1361,6 @@ static void test_enum_svc(void) HeapFree(GetProcessHeap(), 0, services); /* Check if total is the same as active and inactive win32 services */ - todo_wine ok(returned == (servicecountactive + servicecountinactive), "Something wrong in the calculation\n"); @@ -1324,26 +1397,22 @@ static void test_enum_svc(void) } HeapFree(GetProcessHeap(), 0, services); -#if 0 - /* These tests don't make sense on a real system because no test can determine - * how many service should be active or inactive. - */ - todo_wine - { ok(servicecountactive == 0, "Active services mismatch %u\n", servicecountactive); ok(servicecountinactive == 0, "Inactive services mismatch %u\n", servicecountinactive); - } -#endif CloseServiceHandle(scm_handle); /* More or less the same for EnumServicesStatusExA */ + if (!pEnumServicesStatusExA) + { + win_skip( "EnumServicesStatusExA not available\n" ); + return; + } /* All NULL or wrong */ SetLastError(0xdeadbeef); ret = pEnumServicesStatusExA(NULL, 1, 0, 0, NULL, 0, NULL, NULL, NULL, NULL); ok(!ret, "Expected failure\n"); - todo_wine ok(GetLastError() == ERROR_INVALID_LEVEL, "Expected ERROR_INVALID_LEVEL, got %d\n", GetLastError()); @@ -1351,7 +1420,6 @@ static void test_enum_svc(void) SetLastError(0xdeadbeef); ret = pEnumServicesStatusExA(NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL); ok(!ret, "Expected failure\n"); - todo_wine ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); @@ -1362,7 +1430,6 @@ static void test_enum_svc(void) SetLastError(0xdeadbeef); ret = pEnumServicesStatusExA(scm_handle, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL); ok(!ret, "Expected failure\n"); - todo_wine ok(GetLastError() == ERROR_INVALID_ADDRESS || GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */, "Unexpected last error %d\n", GetLastError()); @@ -1374,7 +1441,6 @@ static void test_enum_svc(void) ok(!ret, "Expected failure\n"); ok(needed == 0xdeadbeef || broken(needed != 0xdeadbeef), /* nt4 */ "Expected no change to the needed buffer variable\n"); - todo_wine ok(GetLastError() == ERROR_INVALID_ADDRESS || GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */, "Unexpected last error %d\n", GetLastError()); @@ -1384,13 +1450,10 @@ static void test_enum_svc(void) SetLastError(0xdeadbeef); ret = pEnumServicesStatusExA(scm_handle, 0, 0, 0, NULL, 0, NULL, &returned, NULL, NULL); ok(!ret, "Expected failure\n"); - todo_wine - { ok(returned == 0xdeadbeef, "Expected no change to the number of services variable\n"); ok(GetLastError() == ERROR_INVALID_ADDRESS || GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */, "Unexpected last error %d\n", GetLastError()); - } /* No valid servicetype and servicestate */ needed = 0xdeadbeef; @@ -1399,13 +1462,10 @@ static void test_enum_svc(void) ret = pEnumServicesStatusExA(scm_handle, 0, 0, 0, NULL, 0, &needed, &returned, NULL, NULL); ok(!ret, "Expected failure\n"); ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); - todo_wine - { ok(needed == 0 || broken(needed != 0), /* nt4 */ "Expected needed buffer size to be set to 0, got %d\n", needed); ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); - } /* No valid servicestate */ needed = 0xdeadbeef; @@ -1415,13 +1475,10 @@ static void test_enum_svc(void) &needed, &returned, NULL, NULL); ok(!ret, "Expected failure\n"); ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); - todo_wine - { ok(needed == 0 || broken(needed != 0), /* nt4 */ "Expected needed buffer size to be set to 0, got %d\n", needed); ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); - } /* No valid servicetype */ needed = 0xdeadbeef; @@ -1431,13 +1488,10 @@ static void test_enum_svc(void) &needed, &returned, NULL, NULL); ok(!ret, "Expected failure\n"); ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); - todo_wine - { ok(needed == 0 || broken(needed != 0), /* nt4 */ "Expected needed buffer size to be set to 0, got %d\n", needed); ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); - } /* No valid servicetype and servicestate and unknown service group */ needed = 0xdeadbeef; @@ -1447,13 +1501,10 @@ static void test_enum_svc(void) &returned, NULL, "deadbeef_group"); ok(!ret, "Expected failure\n"); ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); - todo_wine - { ok(needed == 0 || broken(needed != 0), /* nt4 */ "Expected needed buffer size to be set to 0, got %d\n", needed); ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); - } /* All parameters are correct but our access rights are wrong */ needed = 0xdeadbeef; @@ -1462,7 +1513,6 @@ static void test_enum_svc(void) ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL, NULL); ok(!ret, "Expected failure\n"); - todo_wine ok(needed == 0 || broken(needed != 0), /* nt4 */ "Expected needed buffer size to be set to 0, got %d\n", needed); ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); @@ -1478,7 +1528,6 @@ static void test_enum_svc(void) ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL, "deadbeef_group"); ok(!ret, "Expected failure\n"); - todo_wine ok(needed == 0 || broken(needed != 0), /* nt4 */ "Expected needed buffer size to be set to 0, got %d\n", needed); ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); @@ -1497,12 +1546,9 @@ static void test_enum_svc(void) NULL, 0, &needed, &returned, NULL, "deadbeef_group"); ok(!ret, "Expected failure\n"); ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); - todo_wine - { ok(needed == 0, "Expected needed buffer size to be set to 0, got %d\n", needed); ok(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST, "Expected ERROR_SERVICE_DOES_NOT_EXIST, got %d\n", GetLastError()); - } /* TODO: Create a test that makes sure we enumerate all services that don't * belong to a group. (specifying ""). @@ -1516,17 +1562,15 @@ static void test_enum_svc(void) NULL, 0, &needed, &returned, NULL, NULL); ok(!ret, "Expected failure\n"); ok(returned == 0, "Expected no service returned, got %d\n", returned); - todo_wine - { ok(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size\n"); ok(GetLastError() == ERROR_MORE_DATA, "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(!ret, "Expected failure\n"); ok(neededW == needed, "Expected needed buffersize to be the same for A- and W-calls\n"); /* Store the needed bytes */ @@ -1540,12 +1584,9 @@ static void test_enum_svc(void) SetLastError(0xdeadbeef); ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL, (BYTE*)exservices, bufsize, &needed, &returned, NULL, NULL); - todo_wine - { ok(ret, "Expected success, got error %u\n", GetLastError()); ok(needed == 0, "Expected needed buffer to be 0 as we are done\n"); ok(returned == tempreturned, "Expected the same number of service from this function\n"); - } HeapFree(GetProcessHeap(), 0, exservices); /* Store the number of returned services */ @@ -1560,13 +1601,10 @@ static void test_enum_svc(void) ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL, (BYTE*)exservices, bufsize, &needed, &returned, NULL, NULL); ok(!ret, "Expected failure\n"); - todo_wine - { ok(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size\n"); ok(returned < tempreturned, "Expected fewer services to be returned\n"); ok(GetLastError() == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", GetLastError()); - } /* Allocate less than the needed bytes, this time with a correct resume handle */ bufsize = (tempreturned - 1) * sizeof(ENUM_SERVICE_STATUS); @@ -1577,14 +1615,11 @@ static void test_enum_svc(void) ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL, (BYTE*)exservices, bufsize, &needed, &returned, &resume, NULL); ok(!ret, "Expected failure\n"); - todo_wine - { ok(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size\n"); ok(returned < tempreturned, "Expected fewer services to be returned\n"); - ok(resume, "Expected a resume handle\n"); + todo_wine ok(resume, "Expected a resume handle\n"); ok(GetLastError() == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", GetLastError()); - } /* Fetch that last service but pass a bigger buffer size */ missing = tempreturned - returned; @@ -1594,11 +1629,8 @@ static void test_enum_svc(void) SetLastError(0xdeadbeef); ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL, (BYTE*)exservices, bufsize, &needed, &returned, &resume, NULL); - todo_wine - { ok(ret, "Expected success, got error %u\n", GetLastError()); ok(needed == 0, "Expected needed buffer to be 0 as we are done\n"); - } ok(returned == missing, "Expected %u services to be returned\n", missing); ok(resume == 0, "Expected the resume handle to be 0\n"); HeapFree(GetProcessHeap(), 0, exservices); @@ -1638,18 +1670,19 @@ static void test_enum_svc(void) "Something wrong in the calculation\n"); /* Get all drivers and services */ - pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32 | SERVICE_DRIVER, - SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL, NULL); + ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32 | SERVICE_DRIVER, + SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL, NULL); + ok(!ret, "Expected failure\n"); exservices = HeapAlloc(GetProcessHeap(), 0, needed); - pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32 | SERVICE_DRIVER, - SERVICE_STATE_ALL, (BYTE*)exservices, needed, &needed, &returned, NULL, NULL); + ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32 | SERVICE_DRIVER, + SERVICE_STATE_ALL, (BYTE*)exservices, needed, &needed, &returned, NULL, NULL); + ok(ret, "Expected success %u\n", GetLastError()); /* Loop through all those returned drivers and services */ for (i = 0; i < returned; i++) { SERVICE_STATUS_PROCESS status = exservices[i].ServiceStatusProcess; - /* lpServiceName and lpDisplayName should always be filled */ ok(lstrlenA(exservices[i].lpServiceName) > 0, "Expected a service name\n"); ok(lstrlenA(exservices[i].lpDisplayName) > 0, "Expected a display name\n"); @@ -1687,13 +1720,8 @@ static void test_enum_svc(void) } HeapFree(GetProcessHeap(), 0, exservices); -#if 0 - /* These tests don't make sense on a real system because no test can determine - * how many service should be active or inactive. - */ ok(servicecountactive == 0, "Active services mismatch %u\n", servicecountactive); ok(servicecountinactive == 0, "Inactive services mismatch %u\n", servicecountinactive); -#endif CloseServiceHandle(scm_handle); } @@ -1822,9 +1850,10 @@ static void test_sequence(void) } ok(!strcmp(config->lpServiceStartName, localsystem), "Expected 'LocalSystem', got '%s'\n", config->lpServiceStartName); ok(!strcmp(config->lpDisplayName, displayname), "Expected '%s', got '%s'\n", displayname, config->lpDisplayName); - - ok(ChangeServiceConfigA(svc_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_ERROR_NORMAL, NULL, "TestGroup2", NULL, NULL, NULL, NULL, displayname2), - "ChangeServiceConfig failed (err=%d)\n", GetLastError()); + + ret = ChangeServiceConfigA(svc_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_ERROR_NORMAL, NULL, "TestGroup2", + NULL, NULL, NULL, NULL, displayname2); + ok(ret, "ChangeServiceConfig failed (err=%d)\n", GetLastError()); QueryServiceConfigA(svc_handle, NULL, 0, &needed); config = HeapReAlloc(GetProcessHeap(), 0, config, needed); @@ -2037,6 +2066,141 @@ cleanup: CloseServiceHandle(scm_handle); } +static DWORD try_start_stop(SC_HANDLE svc_handle, const char* name, DWORD is_nt4) +{ + BOOL ret; + DWORD le1, le2; + SERVICE_STATUS status; + + ret = StartServiceA(svc_handle, 0, NULL); + le1 = GetLastError(); + ok(!ret, "%s: StartServiceA() should have failed\n", name); + + if (pQueryServiceStatusEx) + { + DWORD needed; + SERVICE_STATUS_PROCESS statusproc; + + ret = pQueryServiceStatusEx(svc_handle, SC_STATUS_PROCESS_INFO, (BYTE*)&statusproc, sizeof(statusproc), &needed); + ok(ret, "%s: QueryServiceStatusEx() failed le=%u\n", name, GetLastError()); + ok(statusproc.dwCurrentState == SERVICE_STOPPED, "%s: should be stopped state=%x\n", name, statusproc.dwCurrentState); + ok(statusproc.dwProcessId == 0, "%s: ProcessId should be 0 instead of %x\n", name, statusproc.dwProcessId); + } + + ret = StartServiceA(svc_handle, 0, NULL); + le2 = GetLastError(); + ok(!ret, "%s: StartServiceA() should have failed\n", name); + ok(le2 == le1, "%s: the second try should yield the same error: %u != %u\n", name, le1, le2); + + status.dwCurrentState = 0xdeadbeef; + ret = ControlService(svc_handle, SERVICE_CONTROL_STOP, &status); + le2 = GetLastError(); + ok(!ret, "%s: ControlService() should have failed\n", name); + ok(le2 == ERROR_SERVICE_NOT_ACTIVE, "%s: %d != ERROR_SERVICE_NOT_ACTIVE\n", name, le2); + ok(status.dwCurrentState == SERVICE_STOPPED || + broken(is_nt4), /* NT4 returns a random value */ + "%s: should be stopped state=%x\n", name, status.dwCurrentState); + + return le1; +} + +static void test_start_stop(void) +{ + BOOL ret; + SC_HANDLE scm_handle, svc_handle; + DWORD le, is_nt4; + static const char servicename[] = "Winetest"; + char cmd[MAX_PATH+20]; + const char* displayname; + + SetLastError(0xdeadbeef); + scm_handle = OpenSCManagerA(NULL, NULL, GENERIC_ALL); + if (!scm_handle) + { + if(GetLastError() == ERROR_ACCESS_DENIED) + skip("Not enough rights to get a handle to the manager\n"); + else + ok(FALSE, "Could not get a handle to the manager: %d\n", GetLastError()); + return; + } + + /* Detect NT4 */ + svc_handle = OpenServiceA(scm_handle, NULL, GENERIC_READ); + is_nt4=(svc_handle == NULL && GetLastError() == ERROR_INVALID_PARAMETER); + + /* Do some cleanup in case a previous run crashed */ + svc_handle = OpenServiceA(scm_handle, servicename, GENERIC_ALL); + if (svc_handle) + { + DeleteService(svc_handle); + CloseServiceHandle(svc_handle); + } + + /* Create a dummy disabled service */ + sprintf(cmd, "\"%s\" service exit", selfname); + displayname = "Winetest Disabled Service"; + svc_handle = CreateServiceA(scm_handle, servicename, displayname, + GENERIC_ALL, SERVICE_INTERACTIVE_PROCESS | SERVICE_WIN32_OWN_PROCESS, + SERVICE_DISABLED, SERVICE_ERROR_IGNORE, cmd, NULL, + NULL, NULL, NULL, NULL); + if (!svc_handle) + { + if(GetLastError() == ERROR_ACCESS_DENIED) + skip("Not enough rights to create the service\n"); + else + ok(FALSE, "Could not create the service: %d\n", GetLastError()); + goto cleanup; + } + le = try_start_stop(svc_handle, displayname, is_nt4); + ok(le == ERROR_SERVICE_DISABLED, "%d != ERROR_SERVICE_DISABLED\n", le); + + /* Then one with a bad path */ + displayname = "Winetest Bad Path"; + ret = ChangeServiceConfigA(svc_handle, SERVICE_NO_CHANGE, SERVICE_DEMAND_START, SERVICE_NO_CHANGE, "c:\\no_such_file.exe", NULL, NULL, NULL, NULL, NULL, displayname); + ok(ret, "ChangeServiceConfig() failed le=%u\n", GetLastError()); + try_start_stop(svc_handle, displayname, is_nt4); + + if (is_nt4) + { + /* NT4 does not detect when a service fails to start and uses an + * insanely long timeout: 120s. So skip the rest of the tests. + */ + win_skip("Skip some service start/stop tests on NT4\n"); + goto cleanup; + } + + /* Again with a process that exits right away */ + displayname = "Winetest Exit Service"; + ret = ChangeServiceConfigA(svc_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, cmd, NULL, NULL, NULL, NULL, NULL, displayname); + ok(ret, "ChangeServiceConfig() failed le=%u\n", GetLastError()); + le = try_start_stop(svc_handle, displayname, is_nt4); + ok(le == ERROR_SERVICE_REQUEST_TIMEOUT, "%d != ERROR_SERVICE_REQUEST_TIMEOUT\n", le); + + /* And finally with a service that plays dead, forcing a timeout. + * This time we will put no quotes. That should work too, even if there are + * spaces in the path. + */ + sprintf(cmd, "%s service sleep", selfname); + displayname = "Winetest Sleep Service"; + ret = ChangeServiceConfigA(svc_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, cmd, NULL, NULL, NULL, NULL, NULL, displayname); + ok(ret, "ChangeServiceConfig() failed le=%u\n", GetLastError()); + + le = try_start_stop(svc_handle, displayname, is_nt4); + ok(le == ERROR_SERVICE_REQUEST_TIMEOUT, "%d != ERROR_SERVICE_REQUEST_TIMEOUT\n", le); + +cleanup: + if (svc_handle) + { + DeleteService(svc_handle); + CloseServiceHandle(svc_handle); + } + + /* Wait a while. The following test does a CreateService again */ + Sleep(1000); + + CloseServiceHandle(scm_handle); +} + static void test_refcount(void) { SC_HANDLE scm_handle, svc_handle1, svc_handle2, svc_handle3, svc_handle4, svc_handle5; @@ -2140,6 +2304,19 @@ static void test_refcount(void) START_TEST(service) { SC_HANDLE scm_handle; + int myARGC; + char** myARGV; + + myARGC = winetest_get_mainargs(&myARGV); + selfname = myARGV[0]; + if (myARGC >= 3) + { + if (strcmp(myARGV[2], "sleep") == 0) + /* Cause a service startup timeout */ + Sleep(90000); + /* then, or if myARGV[2] == "exit", just exit */ + return; + } /* Bail out if we are on win98 */ SetLastError(0xdeadbeef); @@ -2166,6 +2343,7 @@ START_TEST(service) /* Test the creation, querying and deletion of a service */ test_sequence(); test_queryconfig2(); + test_start_stop(); /* The main reason for this test is to check if any refcounting is used * and what the rules are */