1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "sandbox/win/src/registry_dispatcher.h"
6
7#include "base/win/scoped_handle.h"
8#include "base/win/windows_version.h"
9#include "sandbox/win/src/crosscall_client.h"
10#include "sandbox/win/src/interception.h"
11#include "sandbox/win/src/interceptors.h"
12#include "sandbox/win/src/ipc_tags.h"
13#include "sandbox/win/src/sandbox_nt_util.h"
14#include "sandbox/win/src/policy_broker.h"
15#include "sandbox/win/src/policy_params.h"
16#include "sandbox/win/src/sandbox.h"
17#include "sandbox/win/src/registry_interception.h"
18#include "sandbox/win/src/registry_policy.h"
19
20namespace {
21
22// Builds a path using the root directory and the name.
23bool GetCompletePath(HANDLE root, const base::string16& name,
24                     base::string16* complete_name) {
25  if (root) {
26    if (!sandbox::GetPathFromHandle(root, complete_name))
27      return false;
28
29    *complete_name += L"\\";
30    *complete_name += name;
31  } else {
32    *complete_name = name;
33  }
34
35  return true;
36}
37
38}
39
40namespace sandbox {
41
42RegistryDispatcher::RegistryDispatcher(PolicyBase* policy_base)
43    : policy_base_(policy_base) {
44  static const IPCCall create_params = {
45    {IPC_NTCREATEKEY_TAG, WCHAR_TYPE, ULONG_TYPE, VOIDPTR_TYPE, ULONG_TYPE,
46     ULONG_TYPE, ULONG_TYPE},
47    reinterpret_cast<CallbackGeneric>(&RegistryDispatcher::NtCreateKey)
48  };
49
50  static const IPCCall open_params = {
51    {IPC_NTOPENKEY_TAG, WCHAR_TYPE, ULONG_TYPE, VOIDPTR_TYPE, ULONG_TYPE},
52    reinterpret_cast<CallbackGeneric>(&RegistryDispatcher::NtOpenKey)
53  };
54
55  ipc_calls_.push_back(create_params);
56  ipc_calls_.push_back(open_params);
57}
58
59bool RegistryDispatcher::SetupService(InterceptionManager* manager,
60                                      int service) {
61  if (IPC_NTCREATEKEY_TAG == service)
62    return INTERCEPT_NT(manager, NtCreateKey, CREATE_KEY_ID, 32);
63
64  if (IPC_NTOPENKEY_TAG == service) {
65    bool result = INTERCEPT_NT(manager, NtOpenKey, OPEN_KEY_ID, 16);
66    if (base::win::GetVersion() >= base::win::VERSION_WIN7 ||
67        (base::win::GetVersion() == base::win::VERSION_VISTA &&
68         base::win::OSInfo::GetInstance()->version_type() ==
69             base::win::SUITE_SERVER))
70      result &= INTERCEPT_NT(manager, NtOpenKeyEx, OPEN_KEY_EX_ID, 20);
71    return result;
72  }
73
74  return false;
75}
76
77bool RegistryDispatcher::NtCreateKey(
78    IPCInfo* ipc, base::string16* name, DWORD attributes, HANDLE root,
79    DWORD desired_access, DWORD title_index, DWORD create_options) {
80  base::win::ScopedHandle root_handle;
81  base::string16 real_path = *name;
82
83  // If there is a root directory, we need to duplicate the handle to make
84  // it valid in this process.
85  if (root) {
86    if (!::DuplicateHandle(ipc->client_info->process, root,
87                           ::GetCurrentProcess(), &root, 0, FALSE,
88                           DUPLICATE_SAME_ACCESS))
89      return false;
90
91    root_handle.Set(root);
92  }
93
94  if (!GetCompletePath(root, *name, &real_path))
95    return false;
96
97  const wchar_t* regname = real_path.c_str();
98  CountedParameterSet<OpenKey> params;
99  params[OpenKey::NAME] = ParamPickerMake(regname);
100  params[OpenKey::ACCESS] = ParamPickerMake(desired_access);
101
102  EvalResult result = policy_base_->EvalPolicy(IPC_NTCREATEKEY_TAG,
103                                               params.GetBase());
104
105  HANDLE handle;
106  NTSTATUS nt_status;
107  ULONG disposition = 0;
108  if (!RegistryPolicy::CreateKeyAction(result, *ipc->client_info, *name,
109                                       attributes, root, desired_access,
110                                       title_index, create_options, &handle,
111                                       &nt_status, &disposition)) {
112    ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
113    return true;
114  }
115
116  // Return operation status on the IPC.
117  ipc->return_info.extended[0].unsigned_int = disposition;
118  ipc->return_info.nt_status = nt_status;
119  ipc->return_info.handle = handle;
120  return true;
121}
122
123bool RegistryDispatcher::NtOpenKey(IPCInfo* ipc, base::string16* name,
124                                   DWORD attributes, HANDLE root,
125                                   DWORD desired_access) {
126  base::win::ScopedHandle root_handle;
127  base::string16 real_path = *name;
128
129  // If there is a root directory, we need to duplicate the handle to make
130  // it valid in this process.
131  if (root) {
132    if (!::DuplicateHandle(ipc->client_info->process, root,
133                           ::GetCurrentProcess(), &root, 0, FALSE,
134                           DUPLICATE_SAME_ACCESS))
135      return false;
136      root_handle.Set(root);
137  }
138
139  if (!GetCompletePath(root, *name, &real_path))
140    return false;
141
142  const wchar_t* regname = real_path.c_str();
143  CountedParameterSet<OpenKey> params;
144  params[OpenKey::NAME] = ParamPickerMake(regname);
145  params[OpenKey::ACCESS] = ParamPickerMake(desired_access);
146
147  EvalResult result = policy_base_->EvalPolicy(IPC_NTOPENKEY_TAG,
148                                               params.GetBase());
149  HANDLE handle;
150  NTSTATUS nt_status;
151  if (!RegistryPolicy::OpenKeyAction(result, *ipc->client_info, *name,
152                                     attributes, root, desired_access, &handle,
153                                     &nt_status)) {
154    ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
155    return true;
156  }
157
158  // Return operation status on the IPC.
159  ipc->return_info.nt_status = nt_status;
160  ipc->return_info.handle = handle;
161  return true;
162}
163
164}  // namespace sandbox
165