1// Copyright (c) 2006-2010 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 <shlobj.h>
6
7#include "testing/gtest/include/gtest/gtest.h"
8#include "sandbox/win/src/registry_policy.h"
9#include "sandbox/win/src/sandbox.h"
10#include "sandbox/win/src/sandbox_policy.h"
11#include "sandbox/win/src/sandbox_factory.h"
12#include "sandbox/win/src/nt_internals.h"
13#include "sandbox/win/src/win_utils.h"
14#include "sandbox/win/tests/common/controller.h"
15
16namespace {
17
18static const DWORD kAllowedRegFlags = KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS |
19                                      KEY_NOTIFY | KEY_READ | GENERIC_READ |
20                                      GENERIC_EXECUTE | READ_CONTROL;
21
22#define BINDNTDLL(name) \
23  name ## Function name = reinterpret_cast<name ## Function>( \
24    ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name))
25
26bool IsKeyOpenForRead(HKEY handle) {
27  BINDNTDLL(NtQueryObject);
28
29  OBJECT_BASIC_INFORMATION info = {0};
30  NTSTATUS status = NtQueryObject(handle, ObjectBasicInformation, &info,
31                                  sizeof(info), NULL);
32
33  if (!NT_SUCCESS(status))
34    return false;
35
36  if ((info.GrantedAccess & (~kAllowedRegFlags)) != 0)
37    return false;
38  return true;
39}
40
41}
42
43namespace sandbox {
44
45SBOX_TESTS_COMMAND int Reg_OpenKey(int argc, wchar_t **argv) {
46  if (argc != 4)
47    return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
48
49  REGSAM desired_access = 0;
50  ULONG options = 0;
51  if (wcscmp(argv[1], L"read") == 0) {
52    desired_access = KEY_READ;
53  } else if (wcscmp(argv[1], L"write") == 0) {
54    desired_access = KEY_ALL_ACCESS;
55  } else if (wcscmp(argv[1], L"link") == 0) {
56    options = REG_OPTION_CREATE_LINK;
57    desired_access = KEY_ALL_ACCESS;
58  } else {
59    desired_access = MAXIMUM_ALLOWED;
60  }
61
62  HKEY root = GetReservedKeyFromName(argv[2]);
63  HKEY key;
64  LRESULT result = 0;
65
66  if (wcscmp(argv[0], L"create") == 0)
67    result = ::RegCreateKeyEx(root, argv[3], 0, NULL, options, desired_access,
68                              NULL, &key, NULL);
69  else
70    result = ::RegOpenKeyEx(root, argv[3], 0, desired_access, &key);
71
72  if (ERROR_SUCCESS == result) {
73    if (MAXIMUM_ALLOWED == desired_access) {
74      if (!IsKeyOpenForRead(key)) {
75        ::RegCloseKey(key);
76        return SBOX_TEST_FAILED;
77      }
78    }
79    ::RegCloseKey(key);
80    return SBOX_TEST_SUCCEEDED;
81  } else if (ERROR_ACCESS_DENIED == result) {
82    return SBOX_TEST_DENIED;
83  }
84
85  return SBOX_TEST_FAILED;
86}
87
88TEST(RegistryPolicyTest, TestKeyAnyAccess) {
89  TestRunner runner;
90  EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
91                             TargetPolicy::REG_ALLOW_READONLY,
92                             L"HKEY_LOCAL_MACHINE"));
93
94  EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
95                             TargetPolicy::REG_ALLOW_ANY,
96                             L"HKEY_LOCAL_MACHINE\\Software\\Microsoft"));
97
98  // Tests read access on key allowed for read-write.
99  EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(
100      L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software\\microsoft"));
101
102  EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(
103      L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software\\microsoft"));
104
105  if (::IsUserAnAdmin()) {
106    // Tests write access on key allowed for read-write.
107    EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(
108        L"Reg_OpenKey create write HKEY_LOCAL_MACHINE software\\microsoft"));
109
110    EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(
111        L"Reg_OpenKey open write HKEY_LOCAL_MACHINE software\\microsoft"));
112  }
113
114  // Tests subdirectory access on keys where we don't have subdirectory acess.
115  EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Reg_OpenKey create read "
116      L"HKEY_LOCAL_MACHINE software\\microsoft\\Windows"));
117
118  EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Reg_OpenKey open read "
119      L"HKEY_LOCAL_MACHINE software\\microsoft\\windows"));
120
121  // Tests to see if we can create keys where we dont have subdirectory access.
122  // This is denied.
123  EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Reg_OpenKey create write "
124      L"HKEY_LOCAL_MACHINE software\\Microsoft\\google_unit_tests"));
125
126  RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Microsoft\\google_unit_tests");
127
128  // Tests if we need to handle differently the "\\" at the end.
129  // This is denied. We need to add both rules.
130  EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(
131      L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software\\microsoft\\"));
132
133  EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(
134      L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software\\microsoft\\"));
135}
136
137TEST(RegistryPolicyTest, TestKeyNoAccess) {
138  TestRunner runner;
139
140  EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
141                             TargetPolicy::REG_ALLOW_READONLY,
142                             L"HKEY_LOCAL_MACHINE"));
143
144  // Tests read access where we don't have access at all.
145  EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(
146      L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software"));
147
148  EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(
149      L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software"));
150}
151
152TEST(RegistryPolicyTest, TestKeyReadOnlyAccess) {
153  TestRunner runner;
154
155  EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
156                             TargetPolicy::REG_ALLOW_READONLY,
157                             L"HKEY_LOCAL_MACHINE"));
158
159  EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
160                             TargetPolicy::REG_ALLOW_READONLY,
161                             L"HKEY_LOCAL_MACHINE\\Software\\Policies"));
162
163  EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
164                             TargetPolicy::REG_ALLOW_READONLY,
165                             L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*"));
166
167  // Tests subdirectory acess on keys where we have subdirectory acess.
168  EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Reg_OpenKey create read "
169      L"HKEY_LOCAL_MACHINE software\\Policies\\microsoft"));
170
171  EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Reg_OpenKey open read "
172      L"HKEY_LOCAL_MACHINE software\\Policies\\microsoft"));
173
174  // Tests to see if we can create keys where we have subdirectory access.
175  EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Reg_OpenKey create write "
176      L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests"));
177
178  RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Policies\\google_unit_tests");
179}
180
181TEST(RegistryPolicyTest, TestKeyAllAccessSubDir) {
182  TestRunner runner;
183
184  EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
185                             TargetPolicy::REG_ALLOW_READONLY,
186                             L"HKEY_LOCAL_MACHINE"));
187
188  EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
189                             TargetPolicy::REG_ALLOW_ANY,
190                             L"HKEY_LOCAL_MACHINE\\Software\\Policies"));
191
192  EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
193                             TargetPolicy::REG_ALLOW_ANY,
194                             L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*"));
195
196  if (::IsUserAnAdmin()) {
197    // Tests to see if we can create keys where we have subdirectory access.
198    EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Reg_OpenKey create write "
199        L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests"));
200
201    RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Policies\\google_unit_tests");
202  }
203}
204
205TEST(RegistryPolicyTest, TestKeyCreateLink) {
206  TestRunner runner;
207
208  EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
209                             TargetPolicy::REG_ALLOW_READONLY,
210                             L"HKEY_LOCAL_MACHINE"));
211
212  EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
213                             TargetPolicy::REG_ALLOW_ANY,
214                             L"HKEY_LOCAL_MACHINE\\Software\\Policies"));
215
216  EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
217                             TargetPolicy::REG_ALLOW_ANY,
218                             L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*"));
219
220  // Tests to see if we can create a registry link key.
221  // NOTE: In theory here we should make sure to check for SBOX_TEST_DENIED
222  // instead of !SBOX_TEST_SUCCEEDED, but unfortunately the result is not
223  // access denied. Internally RegCreateKeyEx (At least on Vista 64) tries to
224  // create the link, and we return successfully access denied, then, it
225  // decides to try to break the path in multiple chunks, and create the links
226  // one by one. In this scenario, it tries to create "HKLM\Software" as a
227  // link key, which obviously fail with STATUS_OBJECT_NAME_COLLISION, and
228  // this is what is returned to the user.
229  EXPECT_NE(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Reg_OpenKey create link "
230      L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests"));
231
232  // In case our code fails, and the call works, we need to delete the new
233  // link. There is no api for this, so we need to use the NT call.
234  HKEY key = NULL;
235  LRESULT result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
236                                  L"software\\Policies\\google_unit_tests",
237                                  REG_OPTION_OPEN_LINK, MAXIMUM_ALLOWED,
238                                  &key);
239
240  if (!result) {
241    HMODULE ntdll = GetModuleHandle(L"ntdll.dll");
242    NtDeleteKeyFunction NtDeleteKey =
243        reinterpret_cast<NtDeleteKeyFunction>(GetProcAddress(ntdll,
244                                                             "NtDeleteKey"));
245    NtDeleteKey(key);
246  }
247}
248
249TEST(RegistryPolicyTest, TestKeyReadOnlyHKCU) {
250  TestRunner runner;
251  EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
252                             TargetPolicy::REG_ALLOW_READONLY,
253                             L"HKEY_CURRENT_USER"));
254
255  EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
256                             TargetPolicy::REG_ALLOW_READONLY,
257                             L"HKEY_CURRENT_USER\\Software"));
258
259  EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
260                             TargetPolicy::REG_ALLOW_READONLY,
261                             L"HKEY_USERS\\.default"));
262
263  EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
264                             TargetPolicy::REG_ALLOW_READONLY,
265                             L"HKEY_USERS\\.default\\software"));
266
267  // Tests read access where we only have read-only access.
268  EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(
269      L"Reg_OpenKey create read HKEY_CURRENT_USER software"));
270
271  EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(
272      L"Reg_OpenKey open read HKEY_CURRENT_USER software"));
273
274  // Tests write access where we only have read-only acess.
275  EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(
276      L"Reg_OpenKey create write HKEY_CURRENT_USER software"));
277
278  EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(
279      L"Reg_OpenKey open write HKEY_CURRENT_USER software"));
280
281  // Tests maximum allowed access where we only have read-only access.
282  EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(
283      L"Reg_OpenKey create maximum_allowed HKEY_CURRENT_USER software"));
284
285  EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(
286      L"Reg_OpenKey open maximum_allowed HKEY_CURRENT_USER software"));
287}
288
289}  // namespace sandbox
290