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