1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "base/win_sandbox.h"
31
32 // skipp all unless OS_WIN
33 #ifdef OS_WIN
34 #include <Windows.h>
35 #include <AclAPI.h>
36 #include <sddl.h>
37 #include <strsafe.h>
38
39 #include <atlsecurity.h>
40
41 #include <memory>
42 #include <string>
43
44 #include "base/logging.h"
45 #include "base/scoped_handle.h"
46 #include "base/system_util.h"
47 #include "base/util.h"
48
49 using std::unique_ptr;
50
51 namespace mozc {
52 namespace {
53
OpenEffectiveToken(const DWORD dwDesiredAccess,HANDLE * phToken)54 bool OpenEffectiveToken(const DWORD dwDesiredAccess, HANDLE *phToken) {
55 HANDLE hToken = nullptr;
56
57 if (!::OpenThreadToken(::GetCurrentThread(), dwDesiredAccess,
58 TRUE, &hToken)) {
59 if (ERROR_NO_TOKEN != ::GetLastError()) {
60 ::CloseHandle(hToken);
61 return false;
62 }
63 if (!::OpenProcessToken(::GetCurrentProcess(), dwDesiredAccess, &hToken)) {
64 ::CloseHandle(hToken);
65 return false;
66 }
67 }
68
69 *phToken = hToken;
70 return true;
71 }
72
AllocGetTokenInformation(const HANDLE hToken,const TOKEN_INFORMATION_CLASS TokenInfoClass,PVOID * ppInfo,DWORD * pdwSizeBc)73 bool AllocGetTokenInformation(const HANDLE hToken,
74 const TOKEN_INFORMATION_CLASS TokenInfoClass,
75 PVOID* ppInfo,
76 DWORD* pdwSizeBc) {
77 DWORD dwBufferSizeBc = 0;
78 DWORD dwReturnSizeBc = 0;
79
80 const BOOL bReturn = ::GetTokenInformation(hToken,
81 TokenInfoClass,
82 nullptr,
83 0,
84 &dwBufferSizeBc);
85 if (bReturn || (ERROR_INSUFFICIENT_BUFFER != ::GetLastError())) {
86 return false;
87 }
88
89 PVOID pBuffer = ::LocalAlloc(LPTR, dwBufferSizeBc);
90 if (pBuffer == nullptr) {
91 return false;
92 }
93
94 if (!::GetTokenInformation(hToken,
95 TokenInfoClass,
96 pBuffer,
97 dwBufferSizeBc,
98 &dwReturnSizeBc)) {
99 ::LocalFree(pBuffer);
100 return false;
101 }
102
103 if (ppInfo != nullptr) {
104 *ppInfo = pBuffer;
105 }
106
107 if (pdwSizeBc != nullptr) {
108 *pdwSizeBc = dwReturnSizeBc;
109 }
110
111 return true;
112 }
113
GetTokenUserSidStringW(const HANDLE hToken,PWSTR * pwszSidString)114 bool GetTokenUserSidStringW(const HANDLE hToken,
115 PWSTR *pwszSidString) {
116 PTOKEN_USER pTokenUser = nullptr;
117 PWSTR wszSidString = nullptr;
118
119 if (AllocGetTokenInformation(hToken, TokenUser,
120 reinterpret_cast<PVOID *>(&pTokenUser),
121 nullptr) &&
122 ::ConvertSidToStringSidW(pTokenUser->User.Sid, &wszSidString)) {
123 ::LocalFree(pTokenUser);
124 *pwszSidString = wszSidString;
125 return true;
126 }
127
128 if (wszSidString != nullptr) {
129 ::LocalFree(wszSidString);
130 }
131
132 if (pTokenUser != nullptr) {
133 ::LocalFree(pTokenUser);
134 }
135
136 return false;
137 }
138
GetTokenPrimaryGroupSidStringW(const HANDLE hToken,PWSTR * pwszSidString)139 bool GetTokenPrimaryGroupSidStringW(const HANDLE hToken,
140 PWSTR *pwszSidString) {
141 PTOKEN_PRIMARY_GROUP pTokenPrimaryGroup = nullptr;
142 PWSTR wszSidString = nullptr;
143
144 if (AllocGetTokenInformation(hToken, TokenPrimaryGroup,
145 reinterpret_cast<PVOID *>(&pTokenPrimaryGroup),
146 nullptr) &&
147 ::ConvertSidToStringSidW(pTokenPrimaryGroup->PrimaryGroup,
148 &wszSidString)) {
149 ::LocalFree(pTokenPrimaryGroup);
150 *pwszSidString = wszSidString;
151 return true;
152 }
153
154 if (wszSidString != nullptr) {
155 ::LocalFree(wszSidString);
156 }
157
158 if (pTokenPrimaryGroup != nullptr) {
159 ::LocalFree(pTokenPrimaryGroup);
160 }
161
162 return false;
163 }
164
165 class ScopedLocalFreeInvoker {
166 public:
ScopedLocalFreeInvoker(void * address)167 explicit ScopedLocalFreeInvoker(void *address) : address_(address) {}
~ScopedLocalFreeInvoker()168 ~ScopedLocalFreeInvoker() {
169 if (address_ != nullptr) {
170 ::LocalFree(address_);
171 address_ = nullptr;
172 }
173 }
174
175 private:
176 void *address_;
177
178 DISALLOW_COPY_AND_ASSIGN(ScopedLocalFreeInvoker);
179 };
180
GetUserSid(std::wstring * token_user_sid,std::wstring * token_primary_group_sid)181 bool GetUserSid(std::wstring *token_user_sid,
182 std::wstring *token_primary_group_sid) {
183 DCHECK(token_user_sid);
184 DCHECK(token_primary_group_sid);
185 token_user_sid->clear();
186 token_primary_group_sid->clear();
187
188 ScopedHandle token;
189 {
190 HANDLE hToken = nullptr;
191 if (!OpenEffectiveToken(TOKEN_QUERY, &hToken)) {
192 LOG(ERROR) << "OpenEffectiveToken failed " << ::GetLastError();
193 return false;
194 }
195 token.reset(hToken);
196 }
197
198 // Get token user SID
199 {
200 wchar_t* sid_string = nullptr;
201 if (!GetTokenUserSidStringW(token.get(), &sid_string)) {
202 LOG(ERROR) << "GetTokenUserSidStringW failed " << ::GetLastError();
203 return false;
204 }
205 *token_user_sid = sid_string;
206 ::LocalFree(sid_string);
207 }
208
209 // Get token primary group SID
210 {
211 wchar_t* sid_string = nullptr;
212 if (!GetTokenPrimaryGroupSidStringW(token.get(), &sid_string)) {
213 LOG(ERROR) << "GetTokenPrimaryGroupSidStringW failed "
214 << ::GetLastError();
215 return false;
216 }
217 *token_primary_group_sid = sid_string;
218 ::LocalFree(sid_string);
219 }
220
221 return true;
222 }
223
Allow(const std::wstring & access_right,const std::wstring & account_sid)224 std::wstring Allow(const std::wstring &access_right,
225 const std::wstring &account_sid) {
226 return (std::wstring(L"(") + SDDL_ACCESS_ALLOWED + L";;" +
227 access_right + L";;;" + account_sid + L")");
228 }
229
Deny(const std::wstring & access_right,const std::wstring & account_sid)230 std::wstring Deny(const std::wstring &access_right,
231 const std::wstring &account_sid) {
232 return (std::wstring(L"(") + SDDL_ACCESS_DENIED + L";;" +
233 access_right + L";;;" + account_sid + L")");
234 }
235
MandatoryLevel(const std::wstring & mandatory_label,const std::wstring & integrity_levels)236 std::wstring MandatoryLevel(const std::wstring &mandatory_label,
237 const std::wstring &integrity_levels) {
238 return (std::wstring(L"(") + SDDL_MANDATORY_LABEL + L";;" +
239 mandatory_label + L";;;" + integrity_levels + L")");
240 }
241
242 // SDDL_ALL_APP_PACKAGES is available on Windows SDK 8.0 and later.
243 #ifndef SDDL_ALL_APP_PACKAGES
244 #define SDDL_ALL_APP_PACKAGES L"AC"
245 #endif // SDDL_ALL_APP_PACKAGES
246
247 // SDDL for PROCESS_QUERY_INFORMATION is not defined. So use hex digits instead.
248 #ifndef SDDL_PROCESS_QUERY_INFORMATION
249 static_assert(PROCESS_QUERY_INFORMATION == 0x0400,
250 "PROCESS_QUERY_INFORMATION must be 0x0400");
251 #define SDDL_PROCESS_QUERY_INFORMATION L"0x0400"
252 #endif // SDDL_PROCESS_QUERY_INFORMATION
253
254 // SDDL for PROCESS_QUERY_LIMITED_INFORMATION is not defined. So use hex digits
255 // instead.
256 #ifndef SDDL_PROCESS_QUERY_LIMITED_INFORMATION
257 static_assert(PROCESS_QUERY_LIMITED_INFORMATION == 0x1000,
258 "PROCESS_QUERY_LIMITED_INFORMATION must be 0x1000");
259 #define SDDL_PROCESS_QUERY_LIMITED_INFORMATION L"0x1000"
260 #endif // SDDL_PROCESS_QUERY_LIMITED_INFORMATION
261
262 } // namespace
263
GetSDDL(ObjectSecurityType shareble_object_type,const std::wstring & token_user_sid,const std::wstring & token_primary_group_sid,bool is_windows_8_or_later)264 std::wstring WinSandbox::GetSDDL(ObjectSecurityType shareble_object_type,
265 const std::wstring &token_user_sid,
266 const std::wstring &token_primary_group_sid,
267 bool is_windows_8_or_later) {
268 // See http://social.msdn.microsoft.com/Forums/en-US/windowssecurity/thread/e92502b1-0b9f-4e02-9d72-e4e47e924a8f/
269 // for how to acess named objects from an AppContainer.
270
271 std::wstring dacl;
272 std::wstring sacl;
273 switch (shareble_object_type) {
274 case WinSandbox::kSharablePipe:
275 // Strip implicit owner rights
276 // http://technet.microsoft.com/en-us/library/dd125370.aspx
277 dacl += Allow(L"", SDDL_OWNER_RIGHTS);
278 // Deny remote acccess
279 dacl += Deny(SDDL_GENERIC_ALL, SDDL_NETWORK);
280 // Allow general access to LocalSystem
281 dacl += Allow(SDDL_GENERIC_ALL, SDDL_LOCAL_SYSTEM);
282 // Allow general access to Built-in Administorators
283 dacl += Allow(SDDL_GENERIC_ALL, SDDL_BUILTIN_ADMINISTRATORS);
284 if (is_windows_8_or_later) {
285 // Allow general access to ALL APPLICATION PACKAGES
286 dacl += Allow(SDDL_GENERIC_ALL, SDDL_ALL_APP_PACKAGES);
287 }
288 // Allow general access to the current user
289 dacl += Allow(SDDL_GENERIC_ALL, token_user_sid);
290 // Allow read/write access to low integrity
291 sacl += MandatoryLevel(SDDL_NO_EXECUTE_UP, SDDL_ML_LOW);
292 break;
293 case WinSandbox::kLooseSharablePipe:
294 // Strip implicit owner rights
295 // http://technet.microsoft.com/en-us/library/dd125370.aspx
296 dacl += Allow(L"", SDDL_OWNER_RIGHTS);
297 // Deny remote acccess
298 dacl += Deny(SDDL_GENERIC_ALL, SDDL_NETWORK);
299 // Allow general access to LocalSystem
300 dacl += Allow(SDDL_GENERIC_ALL, SDDL_LOCAL_SYSTEM);
301 // Allow general access to Built-in Administorators
302 dacl += Allow(SDDL_GENERIC_ALL, SDDL_BUILTIN_ADMINISTRATORS);
303 if (is_windows_8_or_later) {
304 // Allow general access to ALL APPLICATION PACKAGES
305 dacl += Allow(SDDL_GENERIC_ALL, SDDL_ALL_APP_PACKAGES);
306 }
307 // Allow general access to the current user
308 dacl += Allow(SDDL_GENERIC_ALL, token_user_sid);
309 // Skip 2nd-phase ACL validation against restricted tokens.
310 dacl += Allow(SDDL_GENERIC_ALL, SDDL_RESTRICTED_CODE);
311 // Allow read/write access to low integrity
312 sacl += MandatoryLevel(SDDL_NO_EXECUTE_UP, SDDL_ML_LOW);
313 break;
314 case WinSandbox::kSharableEvent:
315 // Strip implicit owner rights
316 // http://technet.microsoft.com/en-us/library/dd125370.aspx
317 dacl += Allow(L"", SDDL_OWNER_RIGHTS);
318 // Allow general access to LocalSystem
319 dacl += Allow(SDDL_GENERIC_ALL, SDDL_LOCAL_SYSTEM);
320 // Allow general access to Built-in Administorators
321 dacl += Allow(SDDL_GENERIC_ALL, SDDL_BUILTIN_ADMINISTRATORS);
322 if (is_windows_8_or_later) {
323 // Allow state change/synchronize to ALL APPLICATION PACKAGES
324 dacl += Allow(SDDL_GENERIC_EXECUTE, SDDL_ALL_APP_PACKAGES);
325 }
326 // Allow general access to the current user
327 dacl += Allow(SDDL_GENERIC_ALL, token_user_sid);
328 // Skip 2nd-phase ACL validation against restricted tokens regarding
329 // change/synchronize.
330 dacl += Allow(SDDL_GENERIC_EXECUTE, SDDL_RESTRICTED_CODE);
331 // Allow read/write access to low integrity
332 sacl += MandatoryLevel(SDDL_NO_EXECUTE_UP, SDDL_ML_LOW);
333 break;
334 case WinSandbox::kSharableMutex:
335 // Strip implicit owner rights
336 // http://technet.microsoft.com/en-us/library/dd125370.aspx
337 dacl += Allow(L"", SDDL_OWNER_RIGHTS);
338 // Allow general access to LocalSystem
339 dacl += Allow(SDDL_GENERIC_ALL, SDDL_LOCAL_SYSTEM);
340 // Allow general access to Built-in Administorators
341 dacl += Allow(SDDL_GENERIC_ALL, SDDL_BUILTIN_ADMINISTRATORS);
342 if (is_windows_8_or_later) {
343 // Allow state change/synchronize to ALL APPLICATION PACKAGES
344 dacl += Allow(SDDL_GENERIC_EXECUTE, SDDL_ALL_APP_PACKAGES);
345 }
346 // Allow general access to the current user
347 dacl += Allow(SDDL_GENERIC_ALL, token_user_sid);
348 // Skip 2nd-phase ACL validation against restricted tokens regarding
349 // change/synchronize.
350 dacl += Allow(SDDL_GENERIC_EXECUTE, SDDL_RESTRICTED_CODE);
351 // Allow read/write access to low integrity
352 sacl += MandatoryLevel(SDDL_NO_EXECUTE_UP, SDDL_ML_LOW);
353 break;
354 case WinSandbox::kSharableFileForRead:
355 // Strip implicit owner rights
356 // http://technet.microsoft.com/en-us/library/dd125370.aspx
357 dacl += Allow(L"", SDDL_OWNER_RIGHTS);
358 // Allow general access to LocalSystem
359 dacl += Allow(SDDL_GENERIC_ALL, SDDL_LOCAL_SYSTEM);
360 // Allow general access to Built-in Administorators
361 dacl += Allow(SDDL_GENERIC_ALL, SDDL_BUILTIN_ADMINISTRATORS);
362 // Allow read access to low integrity
363 if (is_windows_8_or_later) {
364 // Allow general read access to ALL APPLICATION PACKAGES
365 dacl += Allow(SDDL_GENERIC_READ, SDDL_ALL_APP_PACKAGES);
366 }
367 // Allow general access to the current user
368 dacl += Allow(SDDL_GENERIC_ALL, token_user_sid);
369 // Skip 2nd-phase ACL validation against restricted tokens regarding
370 // general read access.
371 dacl += Allow(SDDL_GENERIC_READ, SDDL_RESTRICTED_CODE);
372 // Allow read access to low integrity
373 sacl += MandatoryLevel(
374 SDDL_NO_WRITE_UP SDDL_NO_EXECUTE_UP, SDDL_ML_LOW);
375 break;
376 case WinSandbox::kIPCServerProcess:
377 // Strip implicit owner rights
378 // http://technet.microsoft.com/en-us/library/dd125370.aspx
379 dacl += Allow(L"", SDDL_OWNER_RIGHTS);
380 // Allow general access to LocalSystem
381 dacl += Allow(SDDL_GENERIC_ALL, SDDL_LOCAL_SYSTEM);
382 // Allow general access to Built-in Administorators
383 dacl += Allow(SDDL_GENERIC_ALL, SDDL_BUILTIN_ADMINISTRATORS);
384 if (is_windows_8_or_later) {
385 // Allow PROCESS_QUERY_LIMITED_INFORMATION to ALL APPLICATION PACKAGES
386 dacl += Allow(SDDL_PROCESS_QUERY_LIMITED_INFORMATION,
387 SDDL_ALL_APP_PACKAGES);
388 }
389 // Allow general access to the current user
390 dacl += Allow(SDDL_GENERIC_ALL, token_user_sid);
391 // Allow PROCESS_QUERY_LIMITED_INFORMATION to restricted tokens
392 dacl += Allow(SDDL_PROCESS_QUERY_LIMITED_INFORMATION,
393 SDDL_RESTRICTED_CODE);
394 break;
395 case WinSandbox::kPrivateObject:
396 default:
397 // Strip implicit owner rights
398 // http://technet.microsoft.com/en-us/library/dd125370.aspx
399 dacl += Allow(L"", SDDL_OWNER_RIGHTS);
400 // Allow general access to LocalSystem
401 dacl += Allow(SDDL_GENERIC_ALL, SDDL_LOCAL_SYSTEM);
402 // Allow general access to Built-in Administorators
403 dacl += Allow(SDDL_GENERIC_ALL, SDDL_BUILTIN_ADMINISTRATORS);
404 // Allow general access to the current user
405 dacl += Allow(SDDL_GENERIC_ALL, token_user_sid);
406 break;
407 }
408
409 std::wstring sddl;
410 // Owner SID
411 sddl += ((SDDL_OWNER SDDL_DELIMINATOR) + token_user_sid);
412 // Primary Group SID
413 sddl += ((SDDL_GROUP SDDL_DELIMINATOR) + token_primary_group_sid);
414 // DACL
415 if (!dacl.empty()) {
416 sddl += ((SDDL_DACL SDDL_DELIMINATOR) + dacl);
417 }
418 // SACL
419 if (!sacl.empty()) {
420 sddl += ((SDDL_SACL SDDL_DELIMINATOR) + sacl);
421 }
422
423 return sddl;
424 }
425
Sid(const SID * sid)426 Sid::Sid(const SID *sid) {
427 ::CopySid(sizeof(sid_), sid_, const_cast<SID*>(sid));
428 };
429
Sid(WELL_KNOWN_SID_TYPE type)430 Sid::Sid(WELL_KNOWN_SID_TYPE type) {
431 DWORD size_sid = sizeof(sid_);
432 ::CreateWellKnownSid(type, nullptr, sid_, &size_sid);
433 }
434
GetPSID() const435 const SID *Sid::GetPSID() const {
436 return reinterpret_cast<SID*>(const_cast<BYTE*>(sid_));
437 }
438
GetPSID()439 SID *Sid::GetPSID() {
440 return reinterpret_cast<SID*>(const_cast<BYTE*>(sid_));
441 }
442
GetName() const443 std::wstring Sid::GetName() const {
444 wchar_t *ptr = nullptr;
445 Sid temp_sid(GetPSID());
446 ConvertSidToStringSidW(temp_sid.GetPSID(), &ptr);
447 std::wstring name = ptr;
448 ::LocalFree(ptr);
449 return name;
450 }
451
GetAccountName() const452 std::wstring Sid::GetAccountName() const {
453 wchar_t *ptr = nullptr;
454 DWORD name_size = 0;
455 DWORD domain_name_size = 0;
456 SID_NAME_USE name_use;
457 Sid temp_sid(GetPSID());
458 ::LookupAccountSid(nullptr, temp_sid.GetPSID(), nullptr, &name_size,
459 nullptr, &domain_name_size, &name_use);
460 if (domain_name_size == 0) {
461 if (name_size == 0) {
462 // Use string SID instead.
463 return GetName();
464 }
465 unique_ptr<wchar_t[]> name_buffer(new wchar_t[name_size]);
466 ::LookupAccountSid(nullptr, temp_sid.GetPSID(), name_buffer.get(),
467 &name_size, nullptr, &domain_name_size, &name_use);
468 return std::wstring(L"/") + name_buffer.get();
469 }
470 unique_ptr<wchar_t[]> name_buffer(new wchar_t[name_size]);
471 unique_ptr<wchar_t[]> domain_name_buffer(new wchar_t[domain_name_size]);
472 ::LookupAccountSid(nullptr, temp_sid.GetPSID(), name_buffer.get(), &name_size,
473 domain_name_buffer.get(), &domain_name_size, &name_use);
474 const std::wstring domain_name = std::wstring(domain_name_buffer.get());
475 const std::wstring user_name = std::wstring(name_buffer.get());
476 return domain_name + L"/" + user_name;
477 }
478
479 // make SecurityAttributes for the named pipe.
MakeSecurityAttributes(ObjectSecurityType shareble_object_type,SECURITY_ATTRIBUTES * security_attributes)480 bool WinSandbox::MakeSecurityAttributes(
481 ObjectSecurityType shareble_object_type,
482 SECURITY_ATTRIBUTES *security_attributes) {
483 std::wstring token_user_sid;
484 std::wstring token_primary_group_sid;
485 if (!GetUserSid(&token_user_sid, &token_primary_group_sid)) {
486 return false;
487 }
488
489 const std::wstring &sddl = GetSDDL(
490 shareble_object_type, token_user_sid, token_primary_group_sid,
491 SystemUtil::IsWindows8OrLater());
492
493 // Create self-relative SD
494 PSECURITY_DESCRIPTOR self_relative_desc = nullptr;
495 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(
496 sddl.c_str(),
497 SDDL_REVISION_1,
498 &self_relative_desc,
499 nullptr)) {
500 if (self_relative_desc != nullptr) {
501 ::LocalFree(self_relative_desc);
502 }
503 LOG(ERROR)
504 << "ConvertStringSecurityDescriptorToSecurityDescriptorW failed: "
505 << ::GetLastError();
506 return false;
507 }
508
509 // Set up security attributes
510 security_attributes->nLength= sizeof(SECURITY_ATTRIBUTES);
511 security_attributes->lpSecurityDescriptor= self_relative_desc;
512 security_attributes->bInheritHandle= FALSE;
513
514 return true;
515 }
516
AddKnownSidToKernelObject(HANDLE object,const SID * known_sid,DWORD inheritance_flag,ACCESS_MASK access_mask)517 bool WinSandbox::AddKnownSidToKernelObject(HANDLE object, const SID *known_sid,
518 DWORD inheritance_flag,
519 ACCESS_MASK access_mask) {
520 // We must pass |&descriptor| because 6th argument (|&old_dacl|) is
521 // non-null. Actually, returned |old_dacl| points the memory block
522 // of |descriptor|, which must be freed by ::LocalFree API.
523 // http://msdn.microsoft.com/en-us/library/aa446654.aspx
524 PSECURITY_DESCRIPTOR descriptor = nullptr;
525 PACL old_dacl = nullptr;
526 DWORD error = ::GetSecurityInfo(
527 object, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr,
528 &old_dacl, nullptr, &descriptor);
529 // You need not to free |old_dacl| because |old_dacl| points inside of
530 // |descriptor|.
531 ScopedLocalFreeInvoker descripter_deleter(descriptor);
532
533 if (error != ERROR_SUCCESS) {
534 DLOG(ERROR) << "GetSecurityInfo failed" << error;
535 return false;
536 }
537
538 EXPLICIT_ACCESS new_access = {};
539 new_access.grfAccessMode = GRANT_ACCESS;
540 new_access.grfAccessPermissions = access_mask;
541 new_access.grfInheritance = inheritance_flag;
542 new_access.Trustee.pMultipleTrustee = nullptr;
543 new_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
544 new_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
545 // When |TrusteeForm| is TRUSTEE_IS_SID, |ptstrName| is a pointer to the SID
546 // of the trustee.
547 // http://msdn.microsoft.com/en-us/library/aa379636.aspx
548 new_access.Trustee.ptstrName =
549 reinterpret_cast<wchar_t *>(const_cast<SID *>(known_sid));
550
551 PACL new_dacl = nullptr;
552 error = ::SetEntriesInAcl(1, &new_access, old_dacl, &new_dacl);
553 ScopedLocalFreeInvoker new_decl_deleter(new_dacl);
554 if (error != ERROR_SUCCESS) {
555 DLOG(ERROR) << "SetEntriesInAcl failed" << error;
556 return false;
557 }
558
559 error = ::SetSecurityInfo(
560 object, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr,
561 new_dacl, nullptr);
562 if (error != ERROR_SUCCESS) {
563 DLOG(ERROR) << "SetSecurityInfo failed" << error;
564 return false;
565 }
566
567 return true;
568 }
569
570 // Local functions for SpawnSandboxedProcess.
571 namespace {
572 // This Windows job object wrapper class corresponds to the Job class in
573 // Chromium sandbox library with JOB_LOCKDOWN except for LockedDownJob does not
574 // set JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, which is not required by Mozc.
575 // http://src.chromium.org/viewvc/chrome/trunk/src/sandbox/src/security_level.h?view=markup
576 class LockedDownJob {
577 public:
LockedDownJob()578 LockedDownJob() : job_handle_(nullptr) {}
579
~LockedDownJob()580 ~LockedDownJob() {
581 if (job_handle_ != nullptr) {
582 ::CloseHandle(job_handle_);
583 job_handle_ = nullptr;
584 };
585 }
586
IsValid() const587 bool IsValid() const {
588 return (job_handle_ != nullptr);
589 }
590
Init(const wchar_t * job_name,bool allow_ui_operation)591 DWORD Init(const wchar_t *job_name, bool allow_ui_operation) {
592 if (job_handle_ != nullptr) {
593 return ERROR_ALREADY_INITIALIZED;
594 }
595 job_handle_ = ::CreateJobObject(nullptr, job_name);
596 if (job_handle_ == nullptr) {
597 return ::GetLastError();
598 }
599 {
600 JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {};
601 limit_info.BasicLimitInformation.ActiveProcessLimit = 1;
602 limit_info.BasicLimitInformation.LimitFlags =
603 // Mozc does not use JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE so that the
604 // child process can continue running even after the parent is
605 // terminated.
606 // JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE |
607 JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION |
608 JOB_OBJECT_LIMIT_ACTIVE_PROCESS;
609 if (::SetInformationJobObject(job_handle_,
610 JobObjectExtendedLimitInformation,
611 &limit_info,
612 sizeof(limit_info))) {
613 return ::GetLastError();
614 }
615 }
616
617 if (!allow_ui_operation) {
618 JOBOBJECT_BASIC_UI_RESTRICTIONS ui_restrictions = {};
619 ui_restrictions.UIRestrictionsClass =
620 JOB_OBJECT_UILIMIT_WRITECLIPBOARD |
621 JOB_OBJECT_UILIMIT_READCLIPBOARD |
622 JOB_OBJECT_UILIMIT_HANDLES |
623 JOB_OBJECT_UILIMIT_GLOBALATOMS |
624 JOB_OBJECT_UILIMIT_DISPLAYSETTINGS |
625 JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS |
626 JOB_OBJECT_UILIMIT_DESKTOP |
627 JOB_OBJECT_UILIMIT_EXITWINDOWS;
628 if (!::SetInformationJobObject(job_handle_,
629 JobObjectBasicUIRestrictions,
630 &ui_restrictions,
631 sizeof(ui_restrictions))) {
632 return ::GetLastError();
633 }
634 }
635 return ERROR_SUCCESS;
636 }
637
AssignProcessToJob(HANDLE process_handle)638 DWORD AssignProcessToJob(HANDLE process_handle) {
639 if (job_handle_ == nullptr) {
640 return ERROR_NO_DATA;
641 }
642 if (!::AssignProcessToJobObject(job_handle_, process_handle)) {
643 return ::GetLastError();
644 }
645 return ERROR_SUCCESS;
646 }
647
648 private:
649 HANDLE job_handle_;
650
651 DISALLOW_COPY_AND_ASSIGN(LockedDownJob);
652 };
653
CreateSuspendedRestrictedProcess(unique_ptr<wchar_t[]> * command_line,const WinSandbox::SecurityInfo & info,ScopedHandle * process_handle,ScopedHandle * thread_handle,DWORD * pid)654 bool CreateSuspendedRestrictedProcess(unique_ptr<wchar_t[]> *command_line,
655 const WinSandbox::SecurityInfo &info,
656 ScopedHandle *process_handle,
657 ScopedHandle *thread_handle,
658 DWORD *pid) {
659 HANDLE process_token_ret = nullptr;
660 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
661 &process_token_ret)) {
662 return false;
663 }
664 ScopedHandle process_token(process_token_ret);
665
666 ScopedHandle primary_token;
667 if (!WinSandbox::GetRestrictedTokenHandle(process_token.get(),
668 info.primary_level,
669 info.integrity_level,
670 &primary_token)) {
671 return false;
672 }
673
674 ScopedHandle impersonation_token;
675 if (!WinSandbox::GetRestrictedTokenHandleForImpersonation(
676 process_token.get(),
677 info.impersonation_level,
678 info.integrity_level,
679 &impersonation_token)) {
680 return false;
681 }
682
683 PSECURITY_ATTRIBUTES security_attributes_ptr = nullptr;
684 SECURITY_ATTRIBUTES security_attributes = {};
685 if (WinSandbox::MakeSecurityAttributes(WinSandbox::kIPCServerProcess,
686 &security_attributes)) {
687 security_attributes_ptr = &security_attributes;
688 // Override the impersonation thread token's DACL to avoid http://b/1728895
689 // On Windows Server, the objects created by a member of
690 // the built-in administrators group do not always explicitly
691 // allow the current user to access the objects.
692 // Instead, such objects implicitly allow the user by allowing
693 // the built-in administratros group.
694 // However, Mozc asks Sandbox to remove the built-in administrators
695 // group from the current user's groups. Thus the impersonation thread
696 // cannot even look at its own thread token.
697 // That prevents GetRunLevel() from verifying its own thread identity.
698 // Note: Overriding the thread token's
699 // DACL will not elevate the thread's running context.
700 if (!::SetKernelObjectSecurity(
701 impersonation_token.get(),
702 DACL_SECURITY_INFORMATION,
703 security_attributes_ptr->lpSecurityDescriptor)) {
704 const DWORD last_error = ::GetLastError();
705 DLOG(ERROR) << "SetKernelObjectSecurity failed. Error: " << last_error;
706 return false;
707 }
708 }
709
710 DWORD creation_flags = info.creation_flags | CREATE_SUSPENDED;
711 // Note: If the current process is already in a job, you cannot use
712 // CREATE_BREAKAWAY_FROM_JOB. See b/1571395
713 if (info.use_locked_down_job) {
714 creation_flags |= CREATE_BREAKAWAY_FROM_JOB;
715 }
716
717 const wchar_t *startup_directory =
718 (info.in_system_dir ? SystemUtil::GetSystemDir() : nullptr);
719
720 STARTUPINFO startup_info = {sizeof(STARTUPINFO)};
721 PROCESS_INFORMATION process_info = {};
722 // 3rd parameter of CreateProcessAsUser must be a writable buffer.
723 if (!::CreateProcessAsUser(primary_token.get(),
724 nullptr, // No application name.
725 command_line->get(), // must be writable.
726 security_attributes_ptr,
727 nullptr,
728 FALSE, // Do not inherit handles.
729 creation_flags,
730 nullptr, // Use the environment of the caller.
731 startup_directory,
732 &startup_info,
733 &process_info)) {
734 const DWORD last_error = ::GetLastError();
735 DLOG(ERROR) << "CreateProcessAsUser failed. Error: " << last_error;
736 return false;
737 }
738
739 if (security_attributes_ptr != nullptr) {
740 ::LocalFree(security_attributes_ptr->lpSecurityDescriptor);
741 }
742
743 // Change the token of the main thread of the new process for the
744 // impersonation token with more rights.
745 if (!::SetThreadToken(&process_info.hThread, impersonation_token.get())) {
746 const DWORD last_error = ::GetLastError();
747 DLOG(ERROR) << "SetThreadToken failed. Error: " << last_error;
748 ::TerminateProcess(process_info.hProcess, 0);
749 ::CloseHandle(process_info.hProcess);
750 ::CloseHandle(process_info.hThread);
751 return false;
752 }
753 if (thread_handle != nullptr) {
754 thread_handle->reset(process_info.hThread);
755 } else {
756 ::CloseHandle(process_info.hThread);
757 }
758 if (process_handle != nullptr) {
759 process_handle->reset(process_info.hProcess);
760 } else {
761 ::CloseHandle(process_info.hProcess);
762 }
763 if (pid != nullptr) {
764 *pid = process_info.dwProcessId;
765 }
766
767 return true;
768 }
769
SpawnSandboxedProcessImpl(unique_ptr<wchar_t[]> * command_line,const WinSandbox::SecurityInfo & info,DWORD * pid)770 bool SpawnSandboxedProcessImpl(unique_ptr<wchar_t[]> *command_line,
771 const WinSandbox::SecurityInfo &info,
772 DWORD *pid) {
773 LockedDownJob job;
774
775 if (info.use_locked_down_job) {
776 const DWORD error_code = job.Init(nullptr, info.allow_ui_operation);
777 if (error_code != ERROR_SUCCESS) {
778 return false;
779 }
780 }
781
782 ScopedHandle thread_handle;
783 ScopedHandle process_handle;
784 if (!CreateSuspendedRestrictedProcess(
785 command_line, info, &process_handle, &thread_handle, pid)) {
786 return false;
787 }
788
789 if (job.IsValid()) {
790 const DWORD error_code = job.AssignProcessToJob(process_handle.get());
791 if (error_code != ERROR_SUCCESS) {
792 ::TerminateProcess(process_handle.get(), 0);
793 return false;
794 }
795 }
796
797 ::ResumeThread(thread_handle.get());
798
799 return true;
800 }
801
802 } // namespace
803
SecurityInfo()804 WinSandbox::SecurityInfo::SecurityInfo()
805 : primary_level(WinSandbox::USER_LOCKDOWN),
806 impersonation_level(WinSandbox::USER_LOCKDOWN),
807 integrity_level(WinSandbox::INTEGRITY_LEVEL_SYSTEM),
808 creation_flags(0),
809 use_locked_down_job(false),
810 allow_ui_operation(false),
811 in_system_dir(false) {}
812
SpawnSandboxedProcess(const string & path,const string & arg,const SecurityInfo & info,DWORD * pid)813 bool WinSandbox::SpawnSandboxedProcess(const string &path,
814 const string &arg,
815 const SecurityInfo &info,
816 DWORD *pid) {
817 std::wstring wpath;
818 Util::UTF8ToWide(path, &wpath);
819 wpath = L"\"" + wpath + L"\"";
820 if (!arg.empty()) {
821 std::wstring warg;
822 Util::UTF8ToWide(arg, &warg);
823 wpath += L" ";
824 wpath += warg;
825 }
826
827 unique_ptr<wchar_t[]> wpath2(new wchar_t[wpath.size() + 1]);
828 if (0 != wcscpy_s(wpath2.get(), wpath.size() + 1, wpath.c_str())) {
829 return false;
830 }
831
832 if (!SpawnSandboxedProcessImpl(&wpath2, info, pid)) {
833 return false;
834 }
835
836 return true;
837 }
838
839 // Utility functions and classes for GetRestrictionInfo
840 namespace {
841 template <typename T>
842 class Optional {
843 public:
Optional()844 Optional()
845 : value_(T()),
846 has_value_(false) {}
Optional(const T & src)847 explicit Optional(const T &src)
848 : value_(src),
849 has_value_(true) {}
value() const850 const T &value() const {
851 return value_;
852 }
None()853 static Optional<T> None() {
854 return Optional<T>();
855 }
mutable_value()856 T *mutable_value() {
857 has_value_ = true;
858 return &value_;
859 }
has_value() const860 bool has_value() const {
861 return has_value_;
862 }
Clear()863 void Clear() {
864 has_value_ = false;
865 }
866 private:
867 T value_;
868 bool has_value_;
869 };
870
871 // A utility class for GetTokenInformation API.
872 // This class manages data buffer into which |TokenDataType| type data
873 // is filled.
874 template<TOKEN_INFORMATION_CLASS TokenClass, typename TokenDataType>
875 class ScopedTokenInfo {
876 public:
ScopedTokenInfo(HANDLE token)877 explicit ScopedTokenInfo(HANDLE token) : initialized_(false) {
878 DWORD num_bytes = 0;
879 ::GetTokenInformation(token, TokenClass, nullptr, 0, &num_bytes);
880 if (num_bytes == 0) {
881 return;
882 }
883 buffer_.reset(new BYTE[num_bytes]);
884 TokenDataType *all_token_groups =
885 reinterpret_cast<TokenDataType *>(buffer_.get());
886 if (!::GetTokenInformation(token, TokenClass, all_token_groups,
887 num_bytes, &num_bytes)) {
888 const DWORD last_error = ::GetLastError();
889 DLOG(ERROR) << "GetTokenInformation failed. Last error: " << last_error;
890 buffer_.reset();
891 return;
892 }
893 initialized_ = true;
894 }
get() const895 TokenDataType *get() const {
896 return reinterpret_cast<TokenDataType *>(buffer_.get());
897 }
operator ->() const898 TokenDataType *operator->() const {
899 return get();
900 }
901 private:
902 unique_ptr<BYTE[]> buffer_;
903 bool initialized_;
904 };
905
906 // Wrapper class for the SID_AND_ATTRIBUTES structure.
907 class SidAndAttributes {
908 public:
SidAndAttributes()909 SidAndAttributes()
910 : sid_(static_cast<SID *>(nullptr)),
911 attributes_(0) {}
SidAndAttributes(Sid sid,DWORD attributes)912 SidAndAttributes(Sid sid, DWORD attributes)
913 : sid_(sid),
914 attributes_(attributes) {}
sid() const915 const Sid &sid() const {
916 return sid_;
917 }
attributes() const918 const DWORD &attributes() const {
919 return attributes_;
920 }
HasAttribute(DWORD attribute) const921 const bool HasAttribute(DWORD attribute) const {
922 return (attributes_ & attribute) == attribute;
923 }
924 private:
925 Sid sid_;
926 DWORD attributes_;
927 };
928
929 // Returns all the 'TokenGroups' information of the specified |token_handle|.
GetAllTokenGroups(HANDLE token_handle)930 std::vector<SidAndAttributes> GetAllTokenGroups(HANDLE token_handle) {
931 std::vector<SidAndAttributes> result;
932 ScopedTokenInfo<TokenGroups, TOKEN_GROUPS> all_token_groups(token_handle);
933 if (all_token_groups.get() == nullptr) {
934 return result;
935 }
936 for (size_t i = 0; i < all_token_groups->GroupCount; ++i) {
937 Sid sid(static_cast<SID *>(all_token_groups->Groups[i].Sid));
938 const DWORD attributes = all_token_groups->Groups[i].Attributes;
939 result.push_back(SidAndAttributes(sid, attributes));
940 }
941 return result;
942 }
943
FilterByHavingAttribute(const std::vector<SidAndAttributes> & source,DWORD attribute)944 std::vector<SidAndAttributes> FilterByHavingAttribute(
945 const std::vector<SidAndAttributes> &source, DWORD attribute) {
946 std::vector<SidAndAttributes> result;
947 for (size_t i = 0; i < source.size(); ++i) {
948 if (source[i].HasAttribute(attribute)) {
949 result.push_back(source[i]);
950 }
951 }
952 return result;
953 }
954
FilterByNotHavingAttribute(const std::vector<SidAndAttributes> & source,DWORD attribute)955 std::vector<SidAndAttributes> FilterByNotHavingAttribute(
956 const std::vector<SidAndAttributes> &source, DWORD attribute) {
957 std::vector<SidAndAttributes> result;
958 for (size_t i = 0; i < source.size(); ++i) {
959 if (!source[i].HasAttribute(attribute)) {
960 result.push_back(source[i]);
961 }
962 }
963 return result;
964 }
965
966 template <size_t NumExceptions>
FilterSidExceptFor(const std::vector<SidAndAttributes> & source_sids,const WELL_KNOWN_SID_TYPE (& exception_sids)[NumExceptions])967 std::vector<Sid> FilterSidExceptFor(
968 const std::vector<SidAndAttributes> &source_sids,
969 const WELL_KNOWN_SID_TYPE (&exception_sids)[NumExceptions]) {
970 std::vector<Sid> result;
971 // find logon_sid.
972 for (size_t i = 0; i < source_sids.size(); ++i) {
973 bool in_the_exception_list = false;
974 for (size_t j = 0; j < NumExceptions; ++j) {
975 // These variables must be non-const because EqualSid API requires
976 // non-const pointer.
977 Sid source = source_sids[i].sid();
978 Sid except(exception_sids[j]);
979 if (::EqualSid(source.GetPSID(), except.GetPSID())) {
980 in_the_exception_list = true;
981 break;
982 }
983 }
984 if (!in_the_exception_list) {
985 result.push_back(source_sids[i].sid());
986 }
987 }
988 return result;
989 }
990
991 template <size_t NumExceptions>
FilterPrivilegesExceptFor(const std::vector<LUID_AND_ATTRIBUTES> & source_privileges,const wchar_t * (& exception_privileges)[NumExceptions])992 std::vector<LUID> FilterPrivilegesExceptFor(
993 const std::vector<LUID_AND_ATTRIBUTES> &source_privileges,
994 const wchar_t *(&exception_privileges)[NumExceptions]) {
995 std::vector<LUID> result;
996 for (size_t i = 0; i < source_privileges.size(); ++i) {
997 bool in_the_exception_list = false;
998 for (size_t j = 0; j < NumExceptions; ++j) {
999 const LUID source = source_privileges[i].Luid;
1000 LUID except = {};
1001 ::LookupPrivilegeValue(nullptr, exception_privileges[j], &except);
1002 if ((source.HighPart == except.HighPart) &&
1003 (source.LowPart == except.LowPart)) {
1004 in_the_exception_list = true;
1005 break;
1006 }
1007 }
1008 if (!in_the_exception_list) {
1009 result.push_back(source_privileges[i].Luid);
1010 }
1011 }
1012 return result;
1013 }
1014
GetUserSid(HANDLE token)1015 Optional<SidAndAttributes> GetUserSid(HANDLE token) {
1016 ScopedTokenInfo<TokenUser, TOKEN_USER> token_user(token);
1017 if (token_user.get() == nullptr) {
1018 return Optional<SidAndAttributes>::None();
1019 }
1020
1021 Sid sid(static_cast<SID *>(token_user->User.Sid));
1022 const DWORD attributes = token_user->User.Attributes;
1023 return Optional<SidAndAttributes>(SidAndAttributes(sid, attributes));
1024 }
1025
GetPrivileges(HANDLE token)1026 std::vector<LUID_AND_ATTRIBUTES> GetPrivileges(HANDLE token) {
1027 std::vector<LUID_AND_ATTRIBUTES> result;
1028 ScopedTokenInfo<TokenPrivileges, TOKEN_PRIVILEGES> token_privileges(token);
1029 if (token_privileges.get() == nullptr) {
1030 return result;
1031 }
1032
1033 for (size_t i = 0; i < token_privileges->PrivilegeCount; ++i) {
1034 result.push_back(token_privileges->Privileges[i]);
1035 }
1036
1037 return result;
1038 }
1039
CreateRestrictedTokenImpl(HANDLE effective_token,WinSandbox::TokenLevel security_level,ScopedHandle * restricted_token)1040 bool CreateRestrictedTokenImpl(HANDLE effective_token,
1041 WinSandbox::TokenLevel security_level,
1042 ScopedHandle *restricted_token) {
1043 const std::vector<Sid> sids_to_disable =
1044 WinSandbox::GetSidsToDisable(effective_token, security_level);
1045 const std::vector<LUID> privileges_to_disable =
1046 WinSandbox::GetPrivilegesToDisable(effective_token, security_level);
1047 const std::vector<Sid> sids_to_restrict =
1048 WinSandbox::GetSidsToRestrict(effective_token, security_level);
1049
1050 if ((sids_to_disable.size() == 0) &&
1051 (privileges_to_disable.size() == 0) &&
1052 (sids_to_restrict.size() == 0)) {
1053 // Duplicate the token even if it's not modified at this point
1054 // because any subsequent changes to this token would also affect the
1055 // current process.
1056 HANDLE new_token = nullptr;
1057 const BOOL result = ::DuplicateTokenEx(
1058 effective_token, TOKEN_ALL_ACCESS, nullptr,
1059 SecurityIdentification, TokenPrimary, &new_token);
1060 if (result == FALSE) {
1061 return false;
1062 }
1063 restricted_token->reset(new_token);
1064 return true;
1065 }
1066
1067 unique_ptr<SID_AND_ATTRIBUTES[]> sids_to_disable_array;
1068 std::vector<Sid> sids_to_disable_array_buffer = sids_to_disable;
1069 {
1070 const size_t size = sids_to_disable.size();
1071 if (size > 0) {
1072 sids_to_disable_array.reset(new SID_AND_ATTRIBUTES[size]);
1073 for (size_t i = 0; i < size; ++i) {
1074 sids_to_disable_array[i].Attributes = SE_GROUP_USE_FOR_DENY_ONLY;
1075 sids_to_disable_array[i].Sid =
1076 sids_to_disable_array_buffer[i].GetPSID();
1077 }
1078 }
1079 }
1080
1081 unique_ptr<LUID_AND_ATTRIBUTES[]> privileges_to_disable_array;
1082 {
1083 const size_t size = privileges_to_disable.size();
1084 if (size > 0) {
1085 privileges_to_disable_array.reset(new LUID_AND_ATTRIBUTES[size]);
1086 for (unsigned int i = 0; i < size; ++i) {
1087 privileges_to_disable_array[i].Attributes = 0;
1088 privileges_to_disable_array[i].Luid = privileges_to_disable[i];
1089 }
1090 }
1091 }
1092
1093 unique_ptr<SID_AND_ATTRIBUTES[]> sids_to_restrict_array;
1094 std::vector<Sid> sids_to_restrict_array_buffer = sids_to_restrict;
1095 {
1096 const size_t size = sids_to_restrict.size();
1097 if (size > 0) {
1098 sids_to_restrict_array.reset(new SID_AND_ATTRIBUTES[size]);
1099 for (size_t i = 0; i < size; ++i) {
1100 sids_to_restrict_array[i].Attributes = 0;
1101 sids_to_restrict_array[i].Sid =
1102 sids_to_restrict_array_buffer[i].GetPSID();
1103 }
1104 }
1105 }
1106
1107 HANDLE new_token = nullptr;
1108 const BOOL result = ::CreateRestrictedToken(effective_token,
1109 SANDBOX_INERT, // This flag is used on Windows 7
1110 static_cast<DWORD>(sids_to_disable.size()),
1111 sids_to_disable_array.get(),
1112 static_cast<DWORD>(privileges_to_disable.size()),
1113 privileges_to_disable_array.get(),
1114 static_cast<DWORD>(sids_to_restrict.size()),
1115 sids_to_restrict_array.get(),
1116 &new_token);
1117 if (result == FALSE) {
1118 return false;
1119 }
1120 restricted_token->reset(new_token);
1121 return true;
1122 }
1123
AddSidToDefaultDacl(HANDLE token,const Sid & sid,ACCESS_MASK access)1124 bool AddSidToDefaultDacl(HANDLE token, const Sid& sid, ACCESS_MASK access) {
1125 if (token == nullptr) {
1126 return false;
1127 }
1128
1129 ScopedTokenInfo<TokenDefaultDacl, TOKEN_DEFAULT_DACL> default_dacl(token);
1130 if (default_dacl.get() == nullptr) {
1131 return false;
1132 }
1133
1134 ACL* new_dacl = nullptr;
1135 {
1136 EXPLICIT_ACCESS new_access = {};
1137 new_access.grfAccessMode = GRANT_ACCESS;
1138 new_access.grfAccessPermissions = access;
1139 new_access.grfInheritance = NO_INHERITANCE;
1140
1141 new_access.Trustee.pMultipleTrustee = nullptr;
1142 new_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
1143 new_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
1144 Sid temp_sid(sid);
1145 new_access.Trustee.ptstrName = reinterpret_cast<LPWSTR>(temp_sid.GetPSID());
1146 const DWORD result = ::SetEntriesInAcl(
1147 1, &new_access, default_dacl->DefaultDacl, &new_dacl);
1148 if (result != ERROR_SUCCESS) {
1149 return false;
1150 }
1151 }
1152
1153 TOKEN_DEFAULT_DACL new_token_dacl = {new_dacl};
1154 const BOOL result = ::SetTokenInformation(
1155 token, TokenDefaultDacl, &new_token_dacl, sizeof(new_token_dacl));
1156 ::LocalFree(new_dacl);
1157 return (result != FALSE);
1158 }
1159
GetPredefinedSidString(WinSandbox::IntegrityLevel integrity_level)1160 const wchar_t *GetPredefinedSidString(
1161 WinSandbox::IntegrityLevel integrity_level) {
1162 // Defined in the following documents.
1163 // http://msdn.microsoft.com/en-us/library/cc980032.aspx
1164 // http://support.microsoft.com/kb/243330
1165 switch (integrity_level) {
1166 case WinSandbox::INTEGRITY_LEVEL_SYSTEM:
1167 return L"S-1-16-16384";
1168 case WinSandbox::INTEGRITY_LEVEL_HIGH:
1169 return L"S-1-16-12288";
1170 case WinSandbox::INTEGRITY_LEVEL_MEDIUM_PLUS:
1171 return L"S-1-16-8448";
1172 case WinSandbox::INTEGRITY_LEVEL_MEDIUM:
1173 return L"S-1-16-8192";
1174 case WinSandbox::INTEGRITY_LEVEL_LOW:
1175 return L"S-1-16-4096";
1176 case WinSandbox::INTEGRITY_LEVEL_UNTRUSTED:
1177 return L"S-1-16-0";
1178 case WinSandbox::INTEGRITY_LEVEL_LAST:
1179 return nullptr;
1180 }
1181
1182 return nullptr;
1183 }
1184
SetTokenIntegrityLevel(HANDLE token,WinSandbox::IntegrityLevel integrity_level)1185 bool SetTokenIntegrityLevel(HANDLE token,
1186 WinSandbox::IntegrityLevel integrity_level) {
1187 const wchar_t* sid_string = GetPredefinedSidString(integrity_level);
1188 if (sid_string == nullptr) {
1189 // do not change the integrity level.
1190 return true;
1191 }
1192
1193 PSID integrity_sid = nullptr;
1194 if (!::ConvertStringSidToSid(sid_string, &integrity_sid)) {
1195 return false;
1196 }
1197 TOKEN_MANDATORY_LABEL label = { {integrity_sid, SE_GROUP_INTEGRITY} };
1198 const DWORD size = sizeof(TOKEN_MANDATORY_LABEL) +
1199 ::GetLengthSid(integrity_sid);
1200 const BOOL result = ::SetTokenInformation(
1201 token, TokenIntegrityLevel, &label, size);
1202 ::LocalFree(integrity_sid);
1203
1204 return (result != FALSE);
1205 }
1206
1207 } // namespace
1208
GetSidsToDisable(HANDLE effective_token,TokenLevel security_level)1209 std::vector<Sid> WinSandbox::GetSidsToDisable(HANDLE effective_token,
1210 TokenLevel security_level) {
1211 const std::vector<SidAndAttributes> all_token_groups =
1212 GetAllTokenGroups(effective_token);
1213 const Optional<SidAndAttributes> current_user_sid =
1214 GetUserSid(effective_token);
1215 const std::vector<SidAndAttributes> normal_tokens =
1216 FilterByNotHavingAttribute(
1217 FilterByNotHavingAttribute(all_token_groups, SE_GROUP_LOGON_ID),
1218 SE_GROUP_INTEGRITY);
1219
1220 std::vector<Sid> sids_to_disable;
1221 switch (security_level) {
1222 case USER_UNPROTECTED:
1223 case USER_RESTRICTED_SAME_ACCESS:
1224 sids_to_disable.clear();
1225 break;
1226 case USER_NON_ADMIN:
1227 case USER_INTERACTIVE: {
1228 const WELL_KNOWN_SID_TYPE kSidExceptions[] = {
1229 WinBuiltinUsersSid,
1230 WinWorldSid,
1231 WinInteractiveSid,
1232 WinAuthenticatedUserSid,
1233 };
1234 sids_to_disable = FilterSidExceptFor(normal_tokens, kSidExceptions);
1235 break;
1236 }
1237 case USER_LIMITED: {
1238 const WELL_KNOWN_SID_TYPE kSidExceptions[] = {
1239 WinBuiltinUsersSid,
1240 WinWorldSid,
1241 WinInteractiveSid,
1242 };
1243 sids_to_disable = FilterSidExceptFor(normal_tokens, kSidExceptions);
1244 break;
1245 }
1246 case USER_RESTRICTED:
1247 case USER_LOCKDOWN:
1248 if (current_user_sid.has_value()) {
1249 sids_to_disable.push_back(current_user_sid.value().sid());
1250 }
1251 for (size_t i = 0; i < normal_tokens.size(); ++i) {
1252 sids_to_disable.push_back(normal_tokens[i].sid());
1253 }
1254 break;
1255 default:
1256 DLOG(FATAL) << "unexpeced TokenLevel";
1257 break;
1258 }
1259 return sids_to_disable;
1260 }
1261
GetPrivilegesToDisable(HANDLE effective_token,TokenLevel security_level)1262 std::vector<LUID> WinSandbox::GetPrivilegesToDisable(HANDLE effective_token,
1263 TokenLevel security_level ) {
1264 const std::vector<LUID_AND_ATTRIBUTES> all_privileges =
1265 GetPrivileges(effective_token);
1266
1267 std::vector<LUID> privileges_to_disable;
1268 switch (security_level) {
1269 case USER_UNPROTECTED:
1270 case USER_RESTRICTED_SAME_ACCESS:
1271 privileges_to_disable.clear();
1272 break;
1273 case USER_NON_ADMIN:
1274 case USER_INTERACTIVE:
1275 case USER_LIMITED:
1276 case USER_RESTRICTED: {
1277 const wchar_t* kPrivilegeExceptions[] = {
1278 SE_CHANGE_NOTIFY_NAME,
1279 };
1280 privileges_to_disable = FilterPrivilegesExceptFor(all_privileges,
1281 kPrivilegeExceptions);
1282 break;
1283 }
1284 case USER_LOCKDOWN:
1285 for (size_t i = 0; i < all_privileges.size(); ++i) {
1286 privileges_to_disable.push_back(all_privileges[i].Luid);
1287 }
1288 break;
1289 default:
1290 DLOG(FATAL) << "unexpeced TokenLevel";
1291 break;
1292 }
1293 return privileges_to_disable;
1294 }
1295
GetSidsToRestrict(HANDLE effective_token,TokenLevel security_level)1296 std::vector<Sid> WinSandbox::GetSidsToRestrict(HANDLE effective_token,
1297 TokenLevel security_level) {
1298 const std::vector<SidAndAttributes> all_token_groups =
1299 GetAllTokenGroups(effective_token);
1300 const Optional<SidAndAttributes> current_user_sid =
1301 GetUserSid(effective_token);
1302 const std::vector<SidAndAttributes> token_logon_session =
1303 FilterByHavingAttribute(all_token_groups, SE_GROUP_LOGON_ID);
1304
1305 std::vector<Sid> sids_to_restrict;
1306 switch (security_level) {
1307 case USER_UNPROTECTED:
1308 sids_to_restrict.clear();
1309 break;
1310 case USER_RESTRICTED_SAME_ACCESS: {
1311 if (current_user_sid.has_value()) {
1312 sids_to_restrict.push_back(current_user_sid.value().sid());
1313 }
1314 const std::vector<SidAndAttributes> tokens =
1315 FilterByNotHavingAttribute(all_token_groups, SE_GROUP_INTEGRITY);
1316 for (size_t i = 0; i < tokens.size(); ++i) {
1317 sids_to_restrict.push_back(tokens[i].sid());
1318 }
1319 break;
1320 }
1321 case USER_NON_ADMIN:
1322 sids_to_restrict.clear();
1323 break;
1324 case USER_INTERACTIVE:
1325 sids_to_restrict.push_back(Sid(WinBuiltinUsersSid));
1326 sids_to_restrict.push_back(Sid(WinWorldSid));
1327 sids_to_restrict.push_back(Sid(WinRestrictedCodeSid));
1328 if (current_user_sid.has_value()) {
1329 sids_to_restrict.push_back(current_user_sid.value().sid());
1330 }
1331 for (size_t i = 0; i < token_logon_session.size(); ++i) {
1332 sids_to_restrict.push_back(token_logon_session[i].sid());
1333 }
1334 break;
1335 case USER_LIMITED:
1336 sids_to_restrict.push_back(Sid(WinBuiltinUsersSid));
1337 sids_to_restrict.push_back(Sid(WinWorldSid));
1338 sids_to_restrict.push_back(Sid(WinRestrictedCodeSid));
1339 // On Windows Vista, the following token (current logon sid) is required
1340 // to create objects in BNO. Consider to use low integrity level
1341 // so that it cannot access object created by other processes.
1342 for (size_t i = 0; i < token_logon_session.size(); ++i) {
1343 sids_to_restrict.push_back(token_logon_session[i].sid());
1344 }
1345 break;
1346 case USER_RESTRICTED:
1347 sids_to_restrict.push_back(Sid(WinRestrictedCodeSid));
1348 break;
1349 case USER_LOCKDOWN:
1350 sids_to_restrict.push_back(Sid(WinNullSid));
1351 break;
1352 default:
1353 DLOG(FATAL) << "unexpeced TokenLevel";
1354 break;
1355 }
1356 return sids_to_restrict;
1357 }
1358
GetRestrictedTokenHandle(HANDLE effective_token,TokenLevel security_level,IntegrityLevel integrity_level,ScopedHandle * restricted_token)1359 bool WinSandbox::GetRestrictedTokenHandle(
1360 HANDLE effective_token,
1361 TokenLevel security_level,
1362 IntegrityLevel integrity_level,
1363 ScopedHandle* restricted_token) {
1364 ScopedHandle new_token;
1365 if (!CreateRestrictedTokenImpl(effective_token, security_level,
1366 &new_token)) {
1367 return false;
1368 }
1369
1370 // Modify the default dacl on the token to contain Restricted and the user.
1371 if (!AddSidToDefaultDacl(new_token.get(), Sid(WinRestrictedCodeSid),
1372 GENERIC_ALL)) {
1373 return false;
1374 }
1375
1376 {
1377 ScopedTokenInfo<TokenUser, TOKEN_USER> token_user(new_token.get());
1378 if (token_user.get() == nullptr) {
1379 return false;
1380 }
1381 Sid user_sid(static_cast<SID *>(token_user->User.Sid));
1382 if (!AddSidToDefaultDacl(new_token.get(), user_sid, GENERIC_ALL)) {
1383 return false;
1384 }
1385 }
1386
1387 if (!SetTokenIntegrityLevel(new_token.get(), integrity_level)) {
1388 return false;
1389 }
1390
1391 HANDLE token_handle = nullptr;
1392 const BOOL result = ::DuplicateHandle(
1393 ::GetCurrentProcess(),
1394 new_token.get(),
1395 ::GetCurrentProcess(),
1396 &token_handle,
1397 TOKEN_ALL_ACCESS,
1398 FALSE,
1399 0);
1400 if (result == FALSE) {
1401 return false;
1402 }
1403 restricted_token->reset(token_handle);
1404
1405 return true;
1406 }
1407
GetRestrictedTokenHandleForImpersonation(HANDLE effective_token,TokenLevel security_level,IntegrityLevel integrity_level,ScopedHandle * restricted_token)1408 bool WinSandbox::GetRestrictedTokenHandleForImpersonation(
1409 HANDLE effective_token,
1410 TokenLevel security_level,
1411 IntegrityLevel integrity_level,
1412 ScopedHandle* restricted_token) {
1413 ScopedHandle new_token;
1414 if (!GetRestrictedTokenHandle(effective_token, security_level,
1415 integrity_level, &new_token)) {
1416 return false;
1417 }
1418
1419 HANDLE impersonation_token_ret = nullptr;
1420 if (!::DuplicateToken(new_token.get(), SecurityImpersonation,
1421 &impersonation_token_ret)) {
1422 return false;
1423 }
1424 ScopedHandle impersonation_token(impersonation_token_ret);
1425
1426 HANDLE restricted_token_ret = nullptr;
1427 if (!::DuplicateHandle(::GetCurrentProcess(), impersonation_token.get(),
1428 ::GetCurrentProcess(), &restricted_token_ret,
1429 TOKEN_ALL_ACCESS, FALSE, 0)) {
1430 return false;
1431 }
1432 restricted_token->reset(restricted_token_ret);
1433 return true;
1434 }
1435
EnsureAllApplicationPackagesPermisssion(const std::wstring & file_name)1436 bool WinSandbox::EnsureAllApplicationPackagesPermisssion(
1437 const std::wstring &file_name) {
1438 // Get "All Application Packages" group SID.
1439 const ATL::CSid all_application_packages(
1440 Sid(WinBuiltinAnyPackageSid).GetPSID());
1441
1442 // Get current DACL (Discretionary Access Control List) of |file_name|.
1443 ATL::CDacl dacl;
1444 if (!ATL::AtlGetDacl(file_name.c_str(), SE_FILE_OBJECT, &dacl)) {
1445 return false;
1446 }
1447
1448 // As of Windows 10 Anniversary Update, following access masks (==0x1200a9)
1449 // are specified to files under Program Files by default.
1450 const ACCESS_MASK kDesiredMask =
1451 FILE_READ_DATA | FILE_READ_EA | FILE_EXECUTE | READ_CONTROL | SYNCHRONIZE;
1452
1453 // Check if the desired ACE is already specified or not.
1454 for (UINT i = 0; i < dacl.GetAceCount(); ++i) {
1455 CSid ace_sid;
1456 ACCESS_MASK acess_mask = 0;
1457 BYTE ace_type = 0;
1458 dacl.GetAclEntry(i, &ace_sid, &acess_mask, &ace_type);
1459 if (ace_sid == all_application_packages &&
1460 ace_type == ACCESS_ALLOWED_ACE_TYPE &&
1461 (acess_mask & kDesiredMask) == kDesiredMask) {
1462 // This is the desired ACE. There is nothing to do.
1463 return true;
1464 }
1465 }
1466
1467 // We are here because there is no desired ACE. Hence we do add it.
1468 if (!dacl.AddAllowedAce(
1469 all_application_packages, kDesiredMask, ACCESS_ALLOWED_ACE_TYPE)) {
1470 return false;
1471 }
1472 return ATL::AtlSetDacl(file_name.c_str(), SE_FILE_OBJECT, dacl);
1473 }
1474
1475 } // namespace mozc
1476 #endif // OS_WIN
1477