Merge branch 'reg_shared_hkcu' into 'master'

server, kernelbase: Force WoW sharing on HKCU/Software key.

See merge request wine/wine!6072
This commit is contained in:
Paul Gofman 2024-11-19 22:10:34 +00:00
commit 786b3e4812
4 changed files with 203 additions and 24 deletions

View file

@ -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)

View file

@ -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,9 +154,39 @@ static BOOL is_classes_wow6432node( HKEY key )
ret = is_classes_root( &name );
}
}
if ((char *)info != buffer) heap_free( info );
if (buf_ptr != buffer) heap_free( buf_ptr );
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;
}
@ -166,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;
}

View file

@ -1454,7 +1454,7 @@ static void test_redirection(void)
KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
KEY_FULL_INFORMATION *full_info = (KEY_FULL_INFORMATION *)buffer;
DWORD dw, len;
HANDLE key, key32, key64, root, root32, root64;
HANDLE key, key32, key64, root, root32, root64, hkcu;
int subkeys64, subkeys32;
if (ptr_size != 64)
@ -2012,6 +2012,62 @@ static void test_redirection(void)
pNtDeleteKey( key32 );
pNtClose( key32 );
/* HKCU\\Software is shared. */
pRtlOpenCurrentUser( KEY_ALL_ACCESS, (HANDLE *)&hkcu );
attr.RootDirectory = hkcu;
pRtlInitUnicodeString( &str, L"Software\\Wow6432Node\\tmp" );
status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
ok( !status, "got %#lx.\n", status );
pNtDeleteKey( key );
pNtClose( key );
pRtlInitUnicodeString( &str, L"Software\\TestKey" );
status = pNtCreateKey( &root64, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
ok( !status, "got %#lx.\n", status );
pRtlInitUnicodeString( &str, L"Software\\Wow6432Node\\TestKey" );
status = pNtOpenKey( &root32, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr );
ok ( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx.\n", status );
status = pNtCreateKey( &root32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
ok( !status, "got %#lx.\n", status );
dw = 1;
status = pNtSetValueKey( root64, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
ok( !status, "got %#lx.\n", status );
dw = 2;
status = pNtSetValueKey( root32, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
ok( !status, "got %#lx.\n", status );
attr.RootDirectory = root64;
pRtlInitUnicodeString( &str, L"subkey" );
status = pNtCreateKey( &key64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
ok( !status, "got %#lx.\n", status );
dw = 1;
status = pNtSetValueKey( key64, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
ok( !status, "got %#lx.\n", status );
attr.RootDirectory = root32;
status = pNtCreateKey( &key32, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
ok( !status, "got %#lx.\n", status );
dw = 2;
status = pNtSetValueKey( key32, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
ok( !status, "got %#lx.\n", status );
check_key_value( hkcu, "Software\\TestKey", KEY_WOW64_64KEY, 1 );
check_key_value( hkcu, "Software\\TestKey", KEY_WOW64_32KEY, 1 );
check_key_value( hkcu, "Software\\TestKey\\subkey", KEY_WOW64_64KEY, 1 );
check_key_value( hkcu, "Software\\TestKey\\subkey", KEY_WOW64_32KEY, 1 );
pNtDeleteKey( key64 );
pNtClose( key64 );
pNtDeleteKey( key32 );
pNtClose( key32 );
pNtDeleteKey( root32 );
pNtClose( root32 );
pNtDeleteKey( root64 );
pNtClose( root64 );
pNtClose( hkcu );
}
static void test_long_value_name(void)

View file

@ -97,8 +97,9 @@ 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 KEY_WOWSHARE 0x0040 /* key is Wow64 shared */
#define OBJ_KEY_WOW64 0x100000 /* magic flag added to attributes for WoW64 redirection */
@ -518,7 +519,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 +546,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 );
@ -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;
@ -777,7 +778,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 +824,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 +850,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;
}
@ -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;
@ -1954,10 +1954,18 @@ 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 */
}
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 )))