diff --git a/dlls/ntdll/tests/reg.c b/dlls/ntdll/tests/reg.c index f75194f7dde..35210655517 100644 --- a/dlls/ntdll/tests/reg.c +++ b/dlls/ntdll/tests/reg.c @@ -638,22 +638,18 @@ static void test_symlinks(void) /* REG_SZ is not allowed */ status = pNtSetValueKey( link, &symlink_str, 0, REG_SZ, target, target_len ); - todo_wine ok( status == STATUS_ACCESS_DENIED, "NtSetValueKey wrong status 0x%08x\n", status ); status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) ); ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status ); /* other values are not allowed */ status = pNtSetValueKey( link, &link_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) ); - todo_wine ok( status == STATUS_ACCESS_DENIED, "NtSetValueKey wrong status 0x%08x\n", status ); /* try opening the target through the link */ attr.ObjectName = &link_str; status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); - todo_wine ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status ); - if (!status) pNtClose( key ); attr.ObjectName = &target_str; status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); @@ -670,14 +666,10 @@ static void test_symlinks(void) len = sizeof(buffer); status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len ); - todo_wine - { ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + sizeof(DWORD), "wrong len %u\n", len ); - } status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - todo_wine ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status ); /* REG_LINK can be created in non-link keys */ @@ -699,11 +691,8 @@ static void test_symlinks(void) len = sizeof(buffer); status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len ); - todo_wine - { ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + sizeof(DWORD), "wrong len %u\n", len ); - } status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status ); @@ -719,24 +708,18 @@ static void test_symlinks(void) len = sizeof(buffer); status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - todo_wine - { ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR), "wrong len %u\n", len ); - } pNtClose( key ); status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); len = sizeof(buffer); status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - todo_wine - { ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR), "wrong len %u\n", len ); - } pNtClose( key ); /* reopen the link from itself */ @@ -748,24 +731,18 @@ static void test_symlinks(void) ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status ); len = sizeof(buffer); status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - todo_wine - { ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR), "wrong len %u\n", len ); - } pNtClose( key ); status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); len = sizeof(buffer); status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - todo_wine - { ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR), "wrong len %u\n", len ); - } pNtClose( key ); if (0) /* crashes the Windows kernel in most versions */ @@ -793,9 +770,7 @@ static void test_symlinks(void) attr.Attributes = 0; attr.ObjectName = &link_str; status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); - todo_wine ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status ); - if (!status) pNtClose( key ); /* relative symlink, works only on win2k */ status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, targetW+1, sizeof(targetW)-2*sizeof(WCHAR) ); @@ -804,12 +779,9 @@ static void test_symlinks(void) status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); ok( status == STATUS_SUCCESS || status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status ); - if (!status) pNtClose( key ); status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 ); - todo_wine ok( status == STATUS_OBJECT_NAME_COLLISION, "NtCreateKey failed: 0x%08x\n", status ); - if (!status) pNtClose( key ); status = pNtDeleteKey( link ); ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status ); @@ -832,10 +804,8 @@ static void test_symlinks(void) ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status ); status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); - todo_wine ok( status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_NAME_TOO_LONG, "NtOpenKey failed: 0x%08x\n", status ); - if (!status) pNtClose( key ); attr.Attributes = OBJ_OPENLINK; status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); diff --git a/server/registry.c b/server/registry.c index afdffed04f4..eb89800ae9b 100644 --- a/server/registry.c +++ b/server/registry.c @@ -83,6 +83,7 @@ struct key #define KEY_VOLATILE 0x0001 /* key is volatile (not saved to disk) */ #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 */ /* a key value */ struct key_value @@ -107,7 +108,12 @@ static const timeout_t ticks_1601_to_1970 = (timeout_t)86400 * (369 * 365 + 89) static const timeout_t save_period = 30 * -TICKS_PER_SEC; /* delay between periodic saves */ static struct timeout_user *save_timeout_user; /* saving timer */ +static const WCHAR root_name[] = { '\\','R','e','g','i','s','t','r','y','\\' }; +static const WCHAR symlink_value[] = {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e'}; +static const struct unicode_str symlink_str = { symlink_value, sizeof(symlink_value) }; + static void set_periodic_save_timer(void); +static struct key_value *find_value( const struct key *key, const struct unicode_str *name, int *index ); /* information about where to save a registry branch */ struct save_branch_info @@ -351,8 +357,6 @@ static void key_destroy( struct object *obj ) /* get the request vararg as registry path */ static inline void get_req_path( struct unicode_str *str, int skip_root ) { - static const WCHAR root_name[] = { '\\','R','e','g','i','s','t','r','y','\\' }; - str->str = get_req_data(); str->len = (get_req_data_size() / sizeof(WCHAR)) * sizeof(WCHAR); @@ -576,8 +580,38 @@ static struct key *find_subkey( const struct key *key, const struct unicode_str return NULL; } +/* follow a symlink and return the resolved key */ +static struct key *follow_symlink( struct key *key, int iteration ) +{ + struct unicode_str path, token; + struct key_value *value; + int index; + + if (iteration > 16) return NULL; + if (!(key->flags & KEY_SYMLINK)) return key; + if (!(value = find_value( key, &symlink_str, &index ))) return NULL; + + path.str = value->data; + path.len = (value->len / sizeof(WCHAR)) * sizeof(WCHAR); + if (path.len <= sizeof(root_name)) return NULL; + if (memicmpW( path.str, root_name, sizeof(root_name)/sizeof(WCHAR) )) return NULL; + path.str += sizeof(root_name) / sizeof(WCHAR); + path.len -= sizeof(root_name); + + key = root_key; + token.str = NULL; + if (!get_path_token( &path, &token )) return NULL; + while (token.len) + { + if (!(key = find_subkey( key, &token, &index ))) break; + if (!(key = follow_symlink( key, iteration + 1 ))) break; + get_path_token( &path, &token ); + } + return key; +} + /* open a subkey */ -static struct key *open_key( struct key *key, const struct unicode_str *name ) +static struct key *open_key( struct key *key, const struct unicode_str *name, unsigned int attributes ) { int index; struct unicode_str token; @@ -589,19 +623,31 @@ static struct key *open_key( struct key *key, const struct unicode_str *name ) if (!(key = find_subkey( key, &token, &index ))) { set_error( STATUS_OBJECT_NAME_NOT_FOUND ); - break; + return NULL; } get_path_token( name, &token ); + if (!token.len) break; + if (!(key = follow_symlink( key, 0 ))) + { + set_error( STATUS_OBJECT_NAME_NOT_FOUND ); + return NULL; + } } + if (!(attributes & OBJ_OPENLINK) && !(key = follow_symlink( key, 0 ))) + { + set_error( STATUS_OBJECT_NAME_NOT_FOUND ); + return NULL; + } if (debug_level > 1) dump_operation( key, NULL, "Open" ); - if (key) grab_object( key ); + grab_object( key ); return key; } /* create a subkey */ static struct key *create_key( struct key *key, const struct unicode_str *name, - const struct unicode_str *class, int flags, timeout_t modif, int *created ) + const struct unicode_str *class, int flags, unsigned int options, + unsigned int attributes, timeout_t modif, int *created ) { struct key *base; int index; @@ -622,12 +668,35 @@ static struct key *create_key( struct key *key, const struct unicode_str *name, if (!(subkey = find_subkey( key, &token, &index ))) break; key = subkey; get_path_token( name, &token ); + if (!token.len) break; + if (!(subkey = follow_symlink( subkey, 0 ))) + { + set_error( STATUS_OBJECT_NAME_NOT_FOUND ); + return NULL; + } } /* create the remaining part */ - if (!token.len) goto done; - if (!(flags & KEY_VOLATILE) && (key->flags & KEY_VOLATILE)) + if (!token.len) + { + if (options & REG_OPTION_CREATE_LINK) + { + set_error( STATUS_OBJECT_NAME_COLLISION ); + return NULL; + } + if (!(attributes & OBJ_OPENLINK) && !(key = follow_symlink( key, 0 ))) + { + set_error( STATUS_OBJECT_NAME_NOT_FOUND ); + return NULL; + } + goto done; + } + if (options & REG_OPTION_VOLATILE) + { + flags = (flags & ~KEY_DIRTY) | KEY_VOLATILE; + } + else if (key->flags & KEY_VOLATILE) { set_error( STATUS_CHILD_MUST_BE_VOLATILE ); return NULL; @@ -648,6 +717,8 @@ static struct key *create_key( struct key *key, const struct unicode_str *name, return NULL; } } + /* FIXME: no saving of symlinks yet */ + if (options & REG_OPTION_CREATE_LINK) key->flags |= KEY_SYMLINK | KEY_VOLATILE; done: if (debug_level > 1) dump_operation( key, NULL, "Create" ); @@ -879,6 +950,16 @@ static void set_value( struct key *key, const struct unicode_str *name, } } + if (key->flags & KEY_SYMLINK) + { + if (type != REG_LINK || name->len != symlink_str.len || + memicmpW( name->str, symlink_str.str, name->len / sizeof(WCHAR) )) + { + set_error( STATUS_ACCESS_DENIED ); + return; + } + } + if (len && !(ptr = memdup( data, len ))) return; if (!value) @@ -1140,7 +1221,7 @@ static struct key *load_key( struct key *base, const char *buffer, int flags, } name.str = p; name.len = len - (p - info->tmp + 1) * sizeof(WCHAR); - return create_key( base, &name, NULL, flags, modif, &res ); + return create_key( base, &name, NULL, flags, 0, 0, modif, &res ); } /* parse a comma-separated list of hex digits */ @@ -1448,7 +1529,7 @@ void init_registry(void) /* load system.reg into Registry\Machine */ - if (!(key = create_key( root_key, &HKLM_name, NULL, 0, current_time, &dummy ))) + if (!(key = create_key( root_key, &HKLM_name, NULL, 0, 0, 0, current_time, &dummy ))) fatal_error( "could not create Machine registry key\n" ); load_init_registry_from_file( "system.reg", key ); @@ -1456,7 +1537,7 @@ void init_registry(void) /* load userdef.reg into Registry\User\.Default */ - if (!(key = create_key( root_key, &HKU_name, NULL, 0, current_time, &dummy ))) + if (!(key = create_key( root_key, &HKU_name, NULL, 0, 0, 0, current_time, &dummy ))) fatal_error( "could not create User\\.Default registry key\n" ); load_init_registry_from_file( "userdef.reg", key ); @@ -1467,7 +1548,7 @@ void init_registry(void) /* FIXME: match default user in token.c. should get from process token instead */ current_user_path = format_user_registry_path( security_interactive_sid, ¤t_user_str ); if (!current_user_path || - !(key = create_key( root_key, ¤t_user_str, NULL, 0, current_time, &dummy ))) + !(key = create_key( root_key, ¤t_user_str, NULL, 0, 0, 0, current_time, &dummy ))) fatal_error( "could not create HKEY_CURRENT_USER registry key\n" ); free( current_user_path ); load_init_registry_from_file( "user.reg", key ); @@ -1660,9 +1741,8 @@ DECL_HANDLER(create_key) /* NOTE: no access rights are required from the parent handle to create a key */ if ((parent = get_parent_hkey_obj( req->parent ))) { - int flags = (req->options & REG_OPTION_VOLATILE) ? KEY_VOLATILE : KEY_DIRTY; - - if ((key = create_key( parent, &name, &class, flags, current_time, &reply->created ))) + if ((key = create_key( parent, &name, &class, KEY_DIRTY, req->options, + req->attributes, current_time, &reply->created ))) { reply->hkey = alloc_handle( current->process, key, access, req->attributes ); release_object( key ); @@ -1683,7 +1763,7 @@ DECL_HANDLER(open_key) if ((parent = get_parent_hkey_obj( req->parent ))) { get_req_path( &name, !req->parent ); - if ((key = open_key( parent, &name ))) + if ((key = open_key( parent, &name, req->attributes ))) { reply->hkey = alloc_handle( current->process, key, access, req->attributes ); release_object( key ); @@ -1817,7 +1897,7 @@ DECL_HANDLER(load_registry) { int dummy; get_req_path( &name, !req->hkey ); - if ((key = create_key( parent, &name, NULL, KEY_DIRTY, current_time, &dummy ))) + if ((key = create_key( parent, &name, NULL, KEY_DIRTY, 0, 0, current_time, &dummy ))) { load_registry( key, req->file ); release_object( key );