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