1 // Copyright (c) 2012 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 <windows.h>
6
7 #include <sddl.h>
8
9 #include <memory>
10 #include <string>
11 #include <vector>
12
13 #include "base/rand_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/win/scoped_handle.h"
16 #include "base/win/scoped_process_information.h"
17 #include "base/win/windows_version.h"
18 #include "sandbox/win/src/app_container_profile_base.h"
19 #include "sandbox/win/src/sync_policy_test.h"
20 #include "sandbox/win/src/win_utils.h"
21 #include "sandbox/win/tests/common/controller.h"
22 #include "sandbox/win/tests/common/test_utils.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 namespace sandbox {
26
27 namespace {
28
29 const wchar_t kAppContainerSid[] =
30 L"S-1-15-2-3251537155-1984446955-2931258699-841473695-1938553385-"
31 L"924012148-2839372144";
32
GenerateRandomPackageName()33 std::wstring GenerateRandomPackageName() {
34 return base::StringPrintf(L"%016lX%016lX", base::RandUint64(),
35 base::RandUint64());
36 }
37
TokenTypeToName(TOKEN_TYPE token_type)38 const char* TokenTypeToName(TOKEN_TYPE token_type) {
39 return token_type == ::TokenPrimary ? "Primary Token" : "Impersonation Token";
40 }
41
CheckToken(HANDLE token,TOKEN_TYPE token_type,PSECURITY_CAPABILITIES security_capabilities,BOOL restricted)42 void CheckToken(HANDLE token,
43 TOKEN_TYPE token_type,
44 PSECURITY_CAPABILITIES security_capabilities,
45 BOOL restricted) {
46 ASSERT_EQ(restricted, ::IsTokenRestricted(token))
47 << TokenTypeToName(token_type);
48
49 DWORD appcontainer;
50 DWORD return_length;
51 ASSERT_TRUE(::GetTokenInformation(token, ::TokenIsAppContainer, &appcontainer,
52 sizeof(appcontainer), &return_length))
53 << TokenTypeToName(token_type);
54 ASSERT_TRUE(appcontainer) << TokenTypeToName(token_type);
55 TOKEN_TYPE token_type_real;
56 ASSERT_TRUE(::GetTokenInformation(token, ::TokenType, &token_type_real,
57 sizeof(token_type_real), &return_length))
58 << TokenTypeToName(token_type);
59 ASSERT_EQ(token_type_real, token_type) << TokenTypeToName(token_type);
60 if (token_type == ::TokenImpersonation) {
61 SECURITY_IMPERSONATION_LEVEL imp_level;
62 ASSERT_TRUE(::GetTokenInformation(token, ::TokenImpersonationLevel,
63 &imp_level, sizeof(imp_level),
64 &return_length))
65 << TokenTypeToName(token_type);
66 ASSERT_EQ(imp_level, ::SecurityImpersonation)
67 << TokenTypeToName(token_type);
68 }
69
70 std::unique_ptr<Sid> package_sid;
71 ASSERT_TRUE(GetTokenAppContainerSid(token, &package_sid))
72 << TokenTypeToName(token_type);
73 EXPECT_TRUE(::EqualSid(security_capabilities->AppContainerSid,
74 package_sid->GetPSID()))
75 << TokenTypeToName(token_type);
76
77 std::vector<SidAndAttributes> capabilities;
78 ASSERT_TRUE(GetTokenGroups(token, ::TokenCapabilities, &capabilities))
79 << TokenTypeToName(token_type);
80
81 ASSERT_EQ(capabilities.size(), security_capabilities->CapabilityCount)
82 << TokenTypeToName(token_type);
83 for (size_t index = 0; index < capabilities.size(); ++index) {
84 EXPECT_EQ(capabilities[index].GetAttributes(),
85 security_capabilities->Capabilities[index].Attributes)
86 << TokenTypeToName(token_type);
87 EXPECT_TRUE(::EqualSid(capabilities[index].GetPSID(),
88 security_capabilities->Capabilities[index].Sid))
89 << TokenTypeToName(token_type);
90 }
91 }
92
CheckProcessToken(HANDLE process,PSECURITY_CAPABILITIES security_capabilities,bool restricted)93 void CheckProcessToken(HANDLE process,
94 PSECURITY_CAPABILITIES security_capabilities,
95 bool restricted) {
96 HANDLE token_handle;
97 ASSERT_TRUE(::OpenProcessToken(process, TOKEN_ALL_ACCESS, &token_handle));
98 base::win::ScopedHandle token(token_handle);
99 CheckToken(token_handle, ::TokenPrimary, security_capabilities, restricted);
100 }
101
CheckThreadToken(HANDLE thread,PSECURITY_CAPABILITIES security_capabilities,bool restricted)102 void CheckThreadToken(HANDLE thread,
103 PSECURITY_CAPABILITIES security_capabilities,
104 bool restricted) {
105 HANDLE token_handle;
106 ASSERT_TRUE(::OpenThreadToken(thread, TOKEN_ALL_ACCESS, TRUE, &token_handle));
107 base::win::ScopedHandle token(token_handle);
108 CheckToken(token_handle, ::TokenImpersonation, security_capabilities,
109 restricted);
110 }
111
112 // Check for LPAC using an access check. We could query for a security attribute
113 // but that's undocumented and has the potential to change.
CheckLpacToken(HANDLE process)114 void CheckLpacToken(HANDLE process) {
115 HANDLE token_handle;
116 ASSERT_TRUE(::OpenProcessToken(process, TOKEN_ALL_ACCESS, &token_handle));
117 base::win::ScopedHandle token(token_handle);
118 ASSERT_TRUE(
119 ::DuplicateToken(token.Get(), ::SecurityImpersonation, &token_handle));
120 token.Set(token_handle);
121 PSECURITY_DESCRIPTOR security_desc_ptr;
122 // AC is AllPackages, S-1-15-2-2 is AllRestrictedPackages. An LPAC token
123 // will get granted access of 2, where as a normal AC token will get 3.
124 ASSERT_TRUE(::ConvertStringSecurityDescriptorToSecurityDescriptor(
125 L"O:SYG:SYD:(A;;0x3;;;WD)(A;;0x1;;;AC)(A;;0x2;;;S-1-15-2-2)",
126 SDDL_REVISION_1, &security_desc_ptr, nullptr));
127 std::unique_ptr<void, LocalFreeDeleter> security_desc(security_desc_ptr);
128 GENERIC_MAPPING generic_mapping = {};
129 PRIVILEGE_SET priv_set = {};
130 DWORD priv_set_length = sizeof(PRIVILEGE_SET);
131 DWORD granted_access;
132 BOOL access_status;
133 ASSERT_TRUE(::AccessCheck(security_desc_ptr, token.Get(), MAXIMUM_ALLOWED,
134 &generic_mapping, &priv_set, &priv_set_length,
135 &granted_access, &access_status));
136 ASSERT_TRUE(access_status);
137 ASSERT_EQ(DWORD{2}, granted_access);
138 }
139
140 class AppContainerProfileTest : public ::testing::Test {
141 public:
SetUp()142 void SetUp() override {
143 if (base::win::GetVersion() < base::win::Version::WIN8)
144 return;
145 package_name_ = GenerateRandomPackageName();
146 broker_services_ = GetBroker();
147 policy_ = broker_services_->CreatePolicy();
148 ASSERT_EQ(SBOX_ALL_OK,
149 policy_->SetProcessMitigations(MITIGATION_HEAP_TERMINATE));
150 ASSERT_EQ(SBOX_ALL_OK,
151 policy_->AddAppContainerProfile(package_name_.c_str(), true));
152 // For testing purposes we known the base class so cast directly.
153 profile_ = static_cast<AppContainerProfileBase*>(
154 policy_->GetAppContainerProfile().get());
155 }
156
TearDown()157 void TearDown() override {
158 if (scoped_process_info_.IsValid())
159 ::TerminateProcess(scoped_process_info_.process_handle(), 0);
160 if (profile_)
161 AppContainerProfileBase::Delete(package_name_.c_str());
162 }
163
164 protected:
CreateProcess()165 void CreateProcess() {
166 // Get the path to the sandboxed app.
167 wchar_t prog_name[MAX_PATH] = {};
168 ASSERT_NE(DWORD{0}, ::GetModuleFileNameW(nullptr, prog_name, MAX_PATH));
169
170 PROCESS_INFORMATION process_info = {};
171 ResultCode last_warning = SBOX_ALL_OK;
172 DWORD last_error = 0;
173 ResultCode result = broker_services_->SpawnTarget(
174 prog_name, prog_name, policy_, &last_warning, &last_error,
175 &process_info);
176 ASSERT_EQ(SBOX_ALL_OK, result) << "Last Error: " << last_error;
177 scoped_process_info_.Set(process_info);
178 }
179
180 std::wstring package_name_;
181 BrokerServices* broker_services_;
182 scoped_refptr<AppContainerProfileBase> profile_;
183 scoped_refptr<TargetPolicy> policy_;
184 base::win::ScopedProcessInformation scoped_process_info_;
185 };
186
187 } // namespace
188
189
TEST(AppContainerTest,DenyOpenEventForLowBox)190 TEST(AppContainerTest, DenyOpenEventForLowBox) {
191 if (base::win::GetVersion() < base::win::Version::WIN8)
192 return;
193
194 TestRunner runner(JOB_UNPROTECTED, USER_UNPROTECTED, USER_UNPROTECTED);
195
196 EXPECT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetLowBox(kAppContainerSid));
197 // Run test once, this ensures the app container directory exists, we
198 // ignore the result.
199 runner.RunTest(L"Event_Open f test");
200 std::wstring event_name = L"AppContainerNamedObjects\\";
201 event_name += kAppContainerSid;
202 event_name += L"\\test";
203
204 base::win::ScopedHandle event(
205 ::CreateEvent(nullptr, false, false, event_name.c_str()));
206 ASSERT_TRUE(event.IsValid());
207
208 EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test"));
209 }
210
TEST_F(AppContainerProfileTest,CheckIncompatibleOptions)211 TEST_F(AppContainerProfileTest, CheckIncompatibleOptions) {
212 if (!profile_)
213 return;
214 EXPECT_EQ(SBOX_ERROR_BAD_PARAMS,
215 policy_->SetIntegrityLevel(INTEGRITY_LEVEL_UNTRUSTED));
216 EXPECT_EQ(SBOX_ERROR_BAD_PARAMS, policy_->SetLowBox(kAppContainerSid));
217
218 MitigationFlags expected_mitigations = 0;
219 MitigationFlags expected_delayed = MITIGATION_HEAP_TERMINATE;
220 sandbox::ResultCode expected_result = SBOX_ERROR_BAD_PARAMS;
221
222 if (base::win::GetVersion() >= base::win::Version::WIN10_RS5) {
223 expected_mitigations = MITIGATION_HEAP_TERMINATE;
224 expected_delayed = 0;
225 expected_result = SBOX_ALL_OK;
226 }
227
228 EXPECT_EQ(expected_mitigations, policy_->GetProcessMitigations());
229 EXPECT_EQ(expected_delayed, policy_->GetDelayedProcessMitigations());
230 EXPECT_EQ(expected_result,
231 policy_->SetProcessMitigations(MITIGATION_HEAP_TERMINATE));
232 }
233
TEST_F(AppContainerProfileTest,NoCapabilities)234 TEST_F(AppContainerProfileTest, NoCapabilities) {
235 if (!profile_)
236 return;
237
238 policy_->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
239 policy_->SetJobLevel(JOB_NONE, 0);
240
241 CreateProcess();
242 auto security_capabilities = profile_->GetSecurityCapabilities();
243
244 CheckProcessToken(scoped_process_info_.process_handle(),
245 security_capabilities.get(), FALSE);
246 CheckThreadToken(scoped_process_info_.thread_handle(),
247 security_capabilities.get(), FALSE);
248 }
249
TEST_F(AppContainerProfileTest,NoCapabilitiesRestricted)250 TEST_F(AppContainerProfileTest, NoCapabilitiesRestricted) {
251 if (!profile_)
252 return;
253
254 policy_->SetTokenLevel(USER_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS);
255 policy_->SetJobLevel(JOB_NONE, 0);
256
257 CreateProcess();
258 auto security_capabilities = profile_->GetSecurityCapabilities();
259
260 CheckProcessToken(scoped_process_info_.process_handle(),
261 security_capabilities.get(), TRUE);
262 CheckThreadToken(scoped_process_info_.thread_handle(),
263 security_capabilities.get(), TRUE);
264 }
265
TEST_F(AppContainerProfileTest,WithCapabilities)266 TEST_F(AppContainerProfileTest, WithCapabilities) {
267 if (!profile_)
268 return;
269
270 profile_->AddCapability(kInternetClient);
271 profile_->AddCapability(kInternetClientServer);
272 policy_->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
273 policy_->SetJobLevel(JOB_NONE, 0);
274
275 CreateProcess();
276 auto security_capabilities = profile_->GetSecurityCapabilities();
277
278 CheckProcessToken(scoped_process_info_.process_handle(),
279 security_capabilities.get(), FALSE);
280 CheckThreadToken(scoped_process_info_.thread_handle(),
281 security_capabilities.get(), FALSE);
282 }
283
TEST_F(AppContainerProfileTest,WithCapabilitiesRestricted)284 TEST_F(AppContainerProfileTest, WithCapabilitiesRestricted) {
285 if (!profile_)
286 return;
287
288 profile_->AddCapability(kInternetClient);
289 profile_->AddCapability(kInternetClientServer);
290 policy_->SetTokenLevel(USER_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS);
291 policy_->SetJobLevel(JOB_NONE, 0);
292
293 CreateProcess();
294 auto security_capabilities = profile_->GetSecurityCapabilities();
295
296 CheckProcessToken(scoped_process_info_.process_handle(),
297 security_capabilities.get(), TRUE);
298 CheckThreadToken(scoped_process_info_.thread_handle(),
299 security_capabilities.get(), TRUE);
300 }
301
TEST_F(AppContainerProfileTest,WithImpersonationCapabilities)302 TEST_F(AppContainerProfileTest, WithImpersonationCapabilities) {
303 if (!profile_)
304 return;
305
306 profile_->AddCapability(kInternetClient);
307 profile_->AddCapability(kInternetClientServer);
308 profile_->AddImpersonationCapability(kPrivateNetworkClientServer);
309 profile_->AddImpersonationCapability(kPicturesLibrary);
310 policy_->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
311 policy_->SetJobLevel(JOB_NONE, 0);
312
313 CreateProcess();
314 auto security_capabilities = profile_->GetSecurityCapabilities();
315
316 CheckProcessToken(scoped_process_info_.process_handle(),
317 security_capabilities.get(), FALSE);
318 SecurityCapabilities impersonation_security_capabilities(
319 profile_->GetPackageSid(), profile_->GetImpersonationCapabilities());
320 CheckThreadToken(scoped_process_info_.thread_handle(),
321 &impersonation_security_capabilities, FALSE);
322 }
323
TEST_F(AppContainerProfileTest,NoCapabilitiesLPAC)324 TEST_F(AppContainerProfileTest, NoCapabilitiesLPAC) {
325 if (base::win::GetVersion() < base::win::Version::WIN10_RS1)
326 return;
327
328 profile_->SetEnableLowPrivilegeAppContainer(true);
329 policy_->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
330 policy_->SetJobLevel(JOB_NONE, 0);
331
332 CreateProcess();
333 auto security_capabilities = profile_->GetSecurityCapabilities();
334
335 CheckProcessToken(scoped_process_info_.process_handle(),
336 security_capabilities.get(), FALSE);
337 CheckThreadToken(scoped_process_info_.thread_handle(),
338 security_capabilities.get(), FALSE);
339 CheckLpacToken(scoped_process_info_.process_handle());
340 }
341
342 } // namespace sandbox
343