michael@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include michael@0: michael@0: #include "sandbox/win/src/registry_policy.h" michael@0: michael@0: #include "base/logging.h" michael@0: #include "sandbox/win/src/ipc_tags.h" michael@0: #include "sandbox/win/src/policy_engine_opcodes.h" michael@0: #include "sandbox/win/src/policy_params.h" michael@0: #include "sandbox/win/src/sandbox_utils.h" michael@0: #include "sandbox/win/src/sandbox_types.h" michael@0: #include "sandbox/win/src/win_utils.h" michael@0: michael@0: namespace { michael@0: michael@0: static const DWORD kAllowedRegFlags = KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | michael@0: KEY_NOTIFY | KEY_READ | GENERIC_READ | michael@0: GENERIC_EXECUTE | READ_CONTROL; michael@0: michael@0: // Opens the key referenced by |obj_attributes| with |access| and michael@0: // checks what permission was given. Remove the WRITE flags and update michael@0: // |access| with the new value. michael@0: NTSTATUS TranslateMaximumAllowed(OBJECT_ATTRIBUTES* obj_attributes, michael@0: DWORD* access) { michael@0: NtOpenKeyFunction NtOpenKey = NULL; michael@0: ResolveNTFunctionPtr("NtOpenKey", &NtOpenKey); michael@0: michael@0: NtCloseFunction NtClose = NULL; michael@0: ResolveNTFunctionPtr("NtClose", &NtClose); michael@0: michael@0: NtQueryObjectFunction NtQueryObject = NULL; michael@0: ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject); michael@0: michael@0: // Open the key. michael@0: HANDLE handle; michael@0: NTSTATUS status = NtOpenKey(&handle, *access, obj_attributes); michael@0: if (!NT_SUCCESS(status)) michael@0: return status; michael@0: michael@0: OBJECT_BASIC_INFORMATION info = {0}; michael@0: status = NtQueryObject(handle, ObjectBasicInformation, &info, sizeof(info), michael@0: NULL); michael@0: NtClose(handle); michael@0: if (!NT_SUCCESS(status)) michael@0: return status; michael@0: michael@0: *access = info.GrantedAccess & kAllowedRegFlags; michael@0: return STATUS_SUCCESS; michael@0: } michael@0: michael@0: NTSTATUS NtCreateKeyInTarget(HANDLE* target_key_handle, michael@0: ACCESS_MASK desired_access, michael@0: OBJECT_ATTRIBUTES* obj_attributes, michael@0: ULONG title_index, michael@0: UNICODE_STRING* class_name, michael@0: ULONG create_options, michael@0: ULONG* disposition, michael@0: HANDLE target_process) { michael@0: NtCreateKeyFunction NtCreateKey = NULL; michael@0: ResolveNTFunctionPtr("NtCreateKey", &NtCreateKey); michael@0: michael@0: if (MAXIMUM_ALLOWED & desired_access) { michael@0: NTSTATUS status = TranslateMaximumAllowed(obj_attributes, &desired_access); michael@0: if (!NT_SUCCESS(status)) michael@0: return STATUS_ACCESS_DENIED; michael@0: } michael@0: michael@0: HANDLE local_handle = INVALID_HANDLE_VALUE; michael@0: NTSTATUS status = NtCreateKey(&local_handle, desired_access, obj_attributes, michael@0: title_index, class_name, create_options, michael@0: disposition); michael@0: if (!NT_SUCCESS(status)) michael@0: return status; michael@0: michael@0: if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, michael@0: target_process, target_key_handle, 0, FALSE, michael@0: DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { michael@0: return STATUS_ACCESS_DENIED; michael@0: } michael@0: return STATUS_SUCCESS; michael@0: } michael@0: michael@0: NTSTATUS NtOpenKeyInTarget(HANDLE* target_key_handle, michael@0: ACCESS_MASK desired_access, michael@0: OBJECT_ATTRIBUTES* obj_attributes, michael@0: HANDLE target_process) { michael@0: NtOpenKeyFunction NtOpenKey = NULL; michael@0: ResolveNTFunctionPtr("NtOpenKey", &NtOpenKey); michael@0: michael@0: if (MAXIMUM_ALLOWED & desired_access) { michael@0: NTSTATUS status = TranslateMaximumAllowed(obj_attributes, &desired_access); michael@0: if (!NT_SUCCESS(status)) michael@0: return STATUS_ACCESS_DENIED; michael@0: } michael@0: michael@0: HANDLE local_handle = INVALID_HANDLE_VALUE; michael@0: NTSTATUS status = NtOpenKey(&local_handle, desired_access, obj_attributes); michael@0: michael@0: if (!NT_SUCCESS(status)) michael@0: return status; michael@0: michael@0: if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, michael@0: target_process, target_key_handle, 0, FALSE, michael@0: DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { michael@0: return STATUS_ACCESS_DENIED; michael@0: } michael@0: return STATUS_SUCCESS; michael@0: } michael@0: michael@0: } michael@0: michael@0: namespace sandbox { michael@0: michael@0: bool RegistryPolicy::GenerateRules(const wchar_t* name, michael@0: TargetPolicy::Semantics semantics, michael@0: LowLevelPolicy* policy) { michael@0: std::wstring resovled_name(name); michael@0: if (resovled_name.empty()) { michael@0: return false; michael@0: } michael@0: michael@0: if (!ResolveRegistryName(resovled_name, &resovled_name)) michael@0: return false; michael@0: michael@0: name = resovled_name.c_str(); michael@0: michael@0: EvalResult result = ASK_BROKER; michael@0: michael@0: PolicyRule open(result); michael@0: PolicyRule create(result); michael@0: michael@0: switch (semantics) { michael@0: case TargetPolicy::REG_ALLOW_READONLY: { michael@0: // We consider all flags that are not known to be readonly as potentially michael@0: // used for write. Here we also support MAXIMUM_ALLOWED, but we are going michael@0: // to expand it to read-only before the call. michael@0: DWORD restricted_flags = ~(kAllowedRegFlags | MAXIMUM_ALLOWED); michael@0: open.AddNumberMatch(IF_NOT, OpenKey::ACCESS, restricted_flags, AND); michael@0: create.AddNumberMatch(IF_NOT, OpenKey::ACCESS, restricted_flags, AND); michael@0: break; michael@0: } michael@0: case TargetPolicy::REG_ALLOW_ANY: { michael@0: break; michael@0: } michael@0: default: { michael@0: NOTREACHED(); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (!create.AddStringMatch(IF, OpenKey::NAME, name, CASE_INSENSITIVE) || michael@0: !policy->AddRule(IPC_NTCREATEKEY_TAG, &create)) { michael@0: return false; michael@0: } michael@0: michael@0: if (!open.AddStringMatch(IF, OpenKey::NAME, name, CASE_INSENSITIVE) || michael@0: !policy->AddRule(IPC_NTOPENKEY_TAG, &open)) { michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool RegistryPolicy::CreateKeyAction(EvalResult eval_result, michael@0: const ClientInfo& client_info, michael@0: const std::wstring &key, michael@0: uint32 attributes, michael@0: HANDLE root_directory, michael@0: uint32 desired_access, michael@0: uint32 title_index, michael@0: uint32 create_options, michael@0: HANDLE* handle, michael@0: NTSTATUS* nt_status, michael@0: ULONG* disposition) { michael@0: // The only action supported is ASK_BROKER which means create the requested michael@0: // file as specified. michael@0: if (ASK_BROKER != eval_result) { michael@0: *nt_status = STATUS_ACCESS_DENIED; michael@0: return false; michael@0: } michael@0: michael@0: // We don't support creating link keys, volatile keys or backup/restore. michael@0: if (create_options) { michael@0: *nt_status = STATUS_ACCESS_DENIED; michael@0: return false; michael@0: } michael@0: michael@0: UNICODE_STRING uni_name = {0}; michael@0: OBJECT_ATTRIBUTES obj_attributes = {0}; michael@0: InitObjectAttribs(key, attributes, root_directory, &obj_attributes, michael@0: &uni_name); michael@0: *nt_status = NtCreateKeyInTarget(handle, desired_access, &obj_attributes, michael@0: title_index, NULL, create_options, michael@0: disposition, client_info.process); michael@0: return true; michael@0: } michael@0: michael@0: bool RegistryPolicy::OpenKeyAction(EvalResult eval_result, michael@0: const ClientInfo& client_info, michael@0: const std::wstring &key, michael@0: uint32 attributes, michael@0: HANDLE root_directory, michael@0: uint32 desired_access, michael@0: HANDLE* handle, michael@0: NTSTATUS* nt_status) { michael@0: // The only action supported is ASK_BROKER which means open the requested michael@0: // file as specified. michael@0: if (ASK_BROKER != eval_result) { michael@0: *nt_status = STATUS_ACCESS_DENIED; michael@0: return true; michael@0: } michael@0: michael@0: UNICODE_STRING uni_name = {0}; michael@0: OBJECT_ATTRIBUTES obj_attributes = {0}; michael@0: InitObjectAttribs(key, attributes, root_directory, &obj_attributes, michael@0: &uni_name); michael@0: *nt_status = NtOpenKeyInTarget(handle, desired_access, &obj_attributes, michael@0: client_info.process); michael@0: return true; michael@0: } michael@0: michael@0: } // namespace sandbox