-- v2: advapi32/tests: Add tests for WoW shared HKCU/Software key. kernelbase: Force WoW sharing on HKCU/Software key.
From: Paul Gofman pgofman@codeweavers.com
--- server/registry.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/server/registry.c b/server/registry.c index 804cfcc638b..aab18fc6061 100644 --- a/server/registry.c +++ b/server/registry.c @@ -97,7 +97,7 @@ struct key #define KEY_DELETED 0x0002 /* key has been deleted */ #define KEY_DIRTY 0x0004 /* key has been modified */ #define KEY_SYMLINK 0x0008 /* key is a symbolic link */ -#define KEY_WOWSHARE 0x0010 /* key is a Wow64 shared key (used for Software\Classes) */ +#define KEY_WOWREFLECT 0x0010 /* key is a Wow64 shared and reflected key (used for Software\Classes) */ #define KEY_PREDEF 0x0020 /* key is marked as predefined */
#define OBJ_KEY_WOW64 0x100000 /* magic flag added to attributes for WoW64 redirection */ @@ -518,7 +518,7 @@ static struct object *key_lookup_name( struct object *obj, struct unicode_str *n }
key = (struct key *)obj; - if (key && (key->flags & KEY_WOWSHARE) && (attr & OBJ_KEY_WOW64) && !name->str) + if (key && (key->flags & KEY_WOWREFLECT) && (attr & OBJ_KEY_WOW64) && !name->str) { key = get_parent( key ); release_object( obj ); @@ -545,7 +545,7 @@ static struct object *key_lookup_name( struct object *obj, struct unicode_str *n
if (!(found = find_subkey( key, &tmp, &index ))) { - if ((key->flags & KEY_WOWSHARE) && (attr & OBJ_KEY_WOW64)) + if ((key->flags & KEY_WOWREFLECT) && (attr & OBJ_KEY_WOW64)) { /* try in the 64-bit parent */ key = get_parent( key ); @@ -777,7 +777,7 @@ static struct key *grab_wow6432node( struct key *key ) struct key *ret = key->wow6432node;
if (!ret) return key; - if (ret->flags & KEY_WOWSHARE) return key; + if (ret->flags & KEY_WOWREFLECT) return key; grab_object( ret ); release_object( key ); return ret; @@ -823,7 +823,7 @@ static struct key *open_key( struct key *parent, const struct unicode_str *name, if (parent && !(access & KEY_WOW64_64KEY) && !is_wow6432node( name->str, name->len )) { key = get_wow6432node( parent ); - if (key && ((access & KEY_WOW64_32KEY) || (key->flags & KEY_WOWSHARE))) + if (key && ((access & KEY_WOW64_32KEY) || (key->flags & KEY_WOWREFLECT))) parent = key; }
@@ -849,7 +849,7 @@ static struct key *create_key( struct key *parent, const struct unicode_str *nam if (parent && !(access & KEY_WOW64_64KEY) && !is_wow6432node( name->str, name->len )) { key = get_wow6432node( parent ); - if (key && ((access & KEY_WOW64_32KEY) || (key->flags & KEY_WOWSHARE))) + if (key && ((access & KEY_WOW64_32KEY) || (key->flags & KEY_WOWREFLECT))) parent = key; }
@@ -1954,7 +1954,7 @@ void init_registry(void) } if ((key = create_key_recursive( hklm, &name, current_time ))) { - key->flags |= KEY_WOWSHARE; + key->flags |= KEY_WOWREFLECT; release_object( key ); } /* FIXME: handle HKCU too */
From: Paul Gofman pgofman@codeweavers.com
--- server/registry.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/server/registry.c b/server/registry.c index aab18fc6061..507793e5ed3 100644 --- a/server/registry.c +++ b/server/registry.c @@ -99,6 +99,7 @@ struct key #define KEY_SYMLINK 0x0008 /* key is a symbolic link */ #define KEY_WOWREFLECT 0x0010 /* key is a Wow64 shared and reflected key (used for Software\Classes) */ #define KEY_PREDEF 0x0020 /* key is marked as predefined */ +#define KEY_WOWSHARE 0x0040 /* key is Wow64 shared */
#define OBJ_KEY_WOW64 0x100000 /* magic flag added to attributes for WoW64 redirection */
@@ -604,7 +605,7 @@ static int key_link_name( struct object *obj, struct object_name *name, struct o for (i = ++parent_key->last_subkey; i > index; i--) parent_key->subkeys[i] = parent_key->subkeys[i - 1]; parent_key->subkeys[index] = (struct key *)grab_object( key ); - if (is_wow6432node( name->name, name->len ) && + if (!(parent_key->flags & KEY_WOWSHARE) && is_wow6432node( name->name, name->len ) && !is_wow6432node( parent_key->obj.name->name, parent_key->obj.name->len )) parent_key->wow6432node = key; name->parent = parent; @@ -1886,13 +1887,14 @@ void init_registry(void) 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\', 'P','e','r','f','l','i','b','\', '0','0','9'}; + static const WCHAR software[] = {'S','o','f','t','w','a','r','e',}; static const struct unicode_str root_name = { REGISTRY, sizeof(REGISTRY) }; static const struct unicode_str HKLM_name = { HKLM, sizeof(HKLM) }; static const struct unicode_str HKU_name = { HKU_default, sizeof(HKU_default) }; static const struct unicode_str perflib_name = { perflib, sizeof(perflib) };
WCHAR *current_user_path; - struct unicode_str current_user_str; + struct unicode_str current_user_str, name; struct key *key, *hklm, *hkcu; unsigned int i; char *p; @@ -1944,8 +1946,6 @@ void init_registry(void) /* set the shared flag on Software\Classes\Wow6432Node for all platforms */ for (i = 1; i < supported_machines_count; i++) { - struct unicode_str name; - switch (supported_machines[i]) { case IMAGE_FILE_MACHINE_I386: name.str = classes_i386; name.len = sizeof(classes_i386); break; @@ -1957,7 +1957,15 @@ void init_registry(void) key->flags |= KEY_WOWREFLECT; release_object( key ); } - /* FIXME: handle HKCU too */ + } + + name.str = software; + name.len = sizeof(software); + if ((key = create_key_recursive( hkcu, &name, current_time ))) + { + key->flags |= KEY_WOWSHARE; + key->wow6432node = NULL; + release_object( key ); }
if ((key = create_key_recursive( hklm, &perflib_name, current_time )))
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernelbase/registry.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-)
diff --git a/dlls/kernelbase/registry.c b/dlls/kernelbase/registry.c index 8471376bf96..24b4bd45d70 100644 --- a/dlls/kernelbase/registry.c +++ b/dlls/kernelbase/registry.c @@ -112,20 +112,15 @@ static BOOL is_classes_root( const UNICODE_STRING *name ) return (len >= classes_root_len - 1 && !wcsnicmp( name->Buffer, classes_root, min( len, classes_root_len ) )); }
-static BOOL is_classes_wow6432node( HKEY key ) +static KEY_NAME_INFORMATION *get_key_name( HKEY key, char *buffer, DWORD len ) { - char buffer[256], *buf_ptr = buffer; + char *buf_ptr = buffer; KEY_NAME_INFORMATION *info = (KEY_NAME_INFORMATION *)buffer; - DWORD len = sizeof(buffer); - UNICODE_STRING name; NTSTATUS status; - BOOL ret = FALSE;
- /* Obtain the name of the root key */ - status = NtQueryKey( key, KeyNameInformation, info, len, &len ); - if (status && status != STATUS_BUFFER_OVERFLOW) return FALSE; + status = NtQueryKey( key, KeyNameInformation, buf_ptr, len, &len ); + if (status && status != STATUS_BUFFER_OVERFLOW) return NULL;
- /* Retry with a dynamically allocated buffer */ while (status == STATUS_BUFFER_OVERFLOW) { if (buf_ptr != buffer) heap_free( buf_ptr ); @@ -133,9 +128,22 @@ static BOOL is_classes_wow6432node( HKEY key ) info = (KEY_NAME_INFORMATION *)buf_ptr; status = NtQueryKey( key, KeyNameInformation, info, len, &len ); } + if (!status) return (KEY_NAME_INFORMATION *)buf_ptr; + if (buf_ptr != buffer) heap_free( buf_ptr ); + return NULL; +} + +static BOOL is_classes_wow6432node( HKEY key ) +{ + KEY_NAME_INFORMATION *info; + char buffer[256]; + UNICODE_STRING name; + BOOL ret = FALSE; + + if (!(info = get_key_name( key, buffer, sizeof(buffer) ))) return FALSE;
/* Check if the key ends in Wow6432Node and if the root is the Classes key*/ - if (!status && info->NameLength / sizeof(WCHAR) >= 11) + if (info->NameLength / sizeof(WCHAR) >= 11) { name.Buffer = info->Name + info->NameLength / sizeof(WCHAR) - 11; name.Length = 11 * sizeof(WCHAR); @@ -146,8 +154,7 @@ static BOOL is_classes_wow6432node( HKEY key ) ret = is_classes_root( &name ); } } - - if (buf_ptr != buffer) heap_free( buf_ptr ); + if ((char *)info != buffer) heap_free( info );
return ret; }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernelbase/registry.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+)
diff --git a/dlls/kernelbase/registry.c b/dlls/kernelbase/registry.c index 24b4bd45d70..bb67eb4176a 100644 --- a/dlls/kernelbase/registry.c +++ b/dlls/kernelbase/registry.c @@ -159,6 +159,37 @@ static BOOL is_classes_wow6432node( HKEY key ) return ret; }
+static BOOL is_wow6432_shared( HANDLE key ) +{ + static const WCHAR users_root[] = L"\Registry\User\"; + const DWORD users_root_len = ARRAY_SIZE( users_root ) - 1; + static const WCHAR software[] = L"\Software"; + const DWORD software_len = ARRAY_SIZE( software ) - 1; + KEY_NAME_INFORMATION *info; + char buffer[256]; + BOOL ret = FALSE; + WCHAR *name; + DWORD len; + + if (!(info = get_key_name( key, buffer, sizeof(buffer) ))) return FALSE; + len = info->NameLength / sizeof(WCHAR); + if (len <= users_root_len) goto done; + name = info->Name; + if (wcsnicmp( name, users_root, users_root_len )) goto done; + name += users_root_len; + len -= users_root_len; + while (len && *name != '\') + { + ++name; + --len; + } + if (len != software_len) goto done; + ret = !wcsnicmp( name, software, software_len ); +done: + if ((char *)info != buffer) heap_free( info ); + return ret; +} + /* Open the Wow6432Node subkey of the specified key */ static HANDLE open_wow6432node( HANDLE key ) { @@ -173,6 +204,11 @@ static HANDLE open_wow6432node( HANDLE key ) attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; if (NtOpenKeyEx( &ret, MAXIMUM_ALLOWED | KEY_WOW64_64KEY, &attr, 0 )) return key; + if (is_wow6432_shared( key )) + { + NtClose( ret ); + return key; + } return ret; }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/advapi32/tests/registry.c | 72 ++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+)
diff --git a/dlls/advapi32/tests/registry.c b/dlls/advapi32/tests/registry.c index 0f77b2fa066..d21bd8fe741 100644 --- a/dlls/advapi32/tests/registry.c +++ b/dlls/advapi32/tests/registry.c @@ -3375,6 +3375,78 @@ static void test_redirection(void)
RegDeleteKeyA( key32, "" ); RegCloseKey( key32 ); + + /* HKCU\Software is shared. */ + err = RegCreateKeyExA( HKEY_CURRENT_USER, "Software\Wow6432Node\tmp", 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_32KEY, NULL, &key, NULL ); + ok( !err, "got %#lx.\n", err ); + RegDeleteKeyA( key, "" ); + RegCloseKey( key ); + + err = RegCreateKeyExA( HKEY_CURRENT_USER, "Software\TestKey", 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_32KEY, NULL, &root64, NULL ); + ok( !err, "got %#lx.\n", err ); + err = RegOpenKeyExA( HKEY_CURRENT_USER, "Software\Wow6432Node\TestKey", 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, &root32 ); + ok ( err == ERROR_FILE_NOT_FOUND, "got %#lx.\n", err ); + + err = RegCreateKeyExA( HKEY_CURRENT_USER, "Software\Wow6432Node\TestKey", 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_32KEY, NULL, &root32, NULL ); + ok( !err, "got %#lx.\n", err ); + + dw = 1; + err = RegSetKeyValueA( root64, NULL, "val", REG_DWORD, &dw, sizeof(dw) ); + ok( !err, "got %#lx.\n", err ); + + dw = 2; + err = RegSetKeyValueA( root32, NULL, "val", REG_DWORD, &dw, sizeof(dw) ); + ok( !err, "got %#lx.\n", err ); + + err = RegCreateKeyExA( root64, "subkey", 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL, &key64, NULL ); + ok( !err, "got %#lx.\n", err ); + dw = 1; + err = RegSetKeyValueA( key64, NULL, "val", REG_DWORD, &dw, sizeof(dw) ); + ok( !err, "got %#lx.\n", err ); + + err = RegCreateKeyExA( root32, "subkey", 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_32KEY, NULL, &key32, NULL ); + ok( !err, "got %#lx.\n", err ); + dw = 2; + err = RegSetKeyValueA( key32, NULL, "val", REG_DWORD, &dw, sizeof(dw) ); + ok( !err, "got %#lx.\n", err ); + + err = RegOpenKeyExA( HKEY_CURRENT_USER, "Software\TestKey", 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, &key ); + ok( !err, "got %#lx.\n", err ); + len = sizeof(dw); + err = RegQueryValueExA( key, "val", NULL, NULL, (BYTE *)&dw, &len ); + ok( !err, "got %#lx.\n", err ); + ok( dw == 1, "got %lu.\n", dw ); + RegCloseKey( key ); + err = RegOpenKeyExA( HKEY_CURRENT_USER, "Software\TestKey", 0, KEY_ALL_ACCESS | KEY_WOW64_32KEY, &key ); + ok( !err, "got %#lx.\n", err ); + len = sizeof(dw); + err = RegQueryValueExA( key, "val", NULL, NULL, (BYTE *)&dw, &len ); + ok( !err, "got %#lx.\n", err ); + ok( dw == 1, "got %lu.\n", dw ); + RegCloseKey( key ); + err = RegOpenKeyExA( HKEY_CURRENT_USER, "Software\TestKey\subkey", 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, &key ); + ok( !err, "got %#lx.\n", err ); + len = sizeof(dw); + err = RegQueryValueExA( key, "val", NULL, NULL, (BYTE *)&dw, &len ); + ok( !err, "got %#lx.\n", err ); + ok( dw == 1, "got %lu.\n", dw ); + RegCloseKey( key ); + err = RegOpenKeyExA( HKEY_CURRENT_USER, "Software\TestKey\subkey", 0, KEY_ALL_ACCESS | KEY_WOW64_32KEY, &key ); + ok( !err, "got %#lx.\n", err ); + len = sizeof(dw); + err = RegQueryValueExA( key, "val", NULL, NULL, (BYTE *)&dw, &len ); + ok( !err, "got %#lx.\n", err ); + ok( dw == 1, "got %lu.\n", dw ); + RegCloseKey( key ); + + RegDeleteKeyA( key64, "" ); + RegCloseKey( key64 ); + RegDeleteKeyA( key32, "" ); + RegCloseKey( key32 ); + RegDeleteKeyA( root32, "" ); + RegCloseKey( root32 ); + RegDeleteKeyA( root64, "" ); + RegCloseKey( root64 ); }
static void test_classesroot(void)
There are similar tests in `ntdll/tests/reg.c` for `Nt*Key`. Would we need some tests there as well to make sure the server changes are correct? `kernelbase` unfortunately implements these things slightly differently than `ntdll`.
Yes, indeed, thanks, I will add that.