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      result &= INTERCEPT_NT(manager, NtOpenKeyEx, OPEN_KEY_EX_ID, 20);
68    return result;
69  }
70
71  return false;
72}
73
74bool RegistryDispatcher::NtCreateKey(
75    IPCInfo* ipc, base::string16* name, DWORD attributes, HANDLE root,
76    DWORD desired_access, DWORD title_index, DWORD create_options) {
77  base::win::ScopedHandle root_handle;
78  base::string16 real_path = *name;
79
80  // If there is a root directory, we need to duplicate the handle to make
81  // it valid in this process.
82  if (root) {
83    if (!::DuplicateHandle(ipc->client_info->process, root,
84                           ::GetCurrentProcess(), &root, 0, FALSE,
85                           DUPLICATE_SAME_ACCESS))
86      return false;
87
88    root_handle.Set(root);
89  }
90
91  if (!GetCompletePath(root, *name, &real_path))
92    return false;
93
94  const wchar_t* regname = real_path.c_str();
95  CountedParameterSet<OpenKey> params;
96  params[OpenKey::NAME] = ParamPickerMake(regname);
97  params[OpenKey::ACCESS] = ParamPickerMake(desired_access);
98
99  EvalResult result = policy_base_->EvalPolicy(IPC_NTCREATEKEY_TAG,
100                                               params.GetBase());
101
102  HANDLE handle;
103  NTSTATUS nt_status;
104  ULONG disposition = 0;
105  if (!RegistryPolicy::CreateKeyAction(result, *ipc->client_info, *name,
106                                       attributes, root, desired_access,
107                                       title_index, create_options, &handle,
108                                       &nt_status, &disposition)) {
109    ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
110    return true;
111  }
112
113  // Return operation status on the IPC.
114  ipc->return_info.extended[0].unsigned_int = disposition;
115  ipc->return_info.nt_status = nt_status;
116  ipc->return_info.handle = handle;
117  return true;
118}
119
120bool RegistryDispatcher::NtOpenKey(IPCInfo* ipc, base::string16* name,
121                                   DWORD attributes, HANDLE root,
122                                   DWORD desired_access) {
123  base::win::ScopedHandle root_handle;
124  base::string16 real_path = *name;
125
126  // If there is a root directory, we need to duplicate the handle to make
127  // it valid in this process.
128  if (root) {
129    if (!::DuplicateHandle(ipc->client_info->process, root,
130                           ::GetCurrentProcess(), &root, 0, FALSE,
131                           DUPLICATE_SAME_ACCESS))
132      return false;
133      root_handle.Set(root);
134  }
135
136  if (!GetCompletePath(root, *name, &real_path))
137    return false;
138
139  const wchar_t* regname = real_path.c_str();
140  CountedParameterSet<OpenKey> params;
141  params[OpenKey::NAME] = ParamPickerMake(regname);
142  params[OpenKey::ACCESS] = ParamPickerMake(desired_access);
143
144  EvalResult result = policy_base_->EvalPolicy(IPC_NTOPENKEY_TAG,
145                                               params.GetBase());
146  HANDLE handle;
147  NTSTATUS nt_status;
148  if (!RegistryPolicy::OpenKeyAction(result, *ipc->client_info, *name,
149                                     attributes, root, desired_access, &handle,
150                                     &nt_status)) {
151    ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
152    return true;
153  }
154
155  // Return operation status on the IPC.
156  ipc->return_info.nt_status = nt_status;
157  ipc->return_info.handle = handle;
158  return true;
159}
160
161}  // namespace sandbox
162