| // Copyright 2010-2015, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "base/win_sandbox.h" |
| |
| // skipp all unless OS_WIN |
| #ifdef OS_WIN |
| #include <Windows.h> |
| #include <AclAPI.h> |
| #include <sddl.h> |
| #include <strsafe.h> |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/logging.h" |
| #include "base/scoped_handle.h" |
| #include "base/system_util.h" |
| #include "base/util.h" |
| |
| using std::unique_ptr; |
| |
| namespace mozc { |
| namespace { |
| |
| bool OpenEffectiveToken(const DWORD dwDesiredAccess, HANDLE *phToken) { |
| HANDLE hToken = nullptr; |
| |
| if (!::OpenThreadToken(::GetCurrentThread(), dwDesiredAccess, |
| TRUE, &hToken)) { |
| if (ERROR_NO_TOKEN != ::GetLastError()) { |
| ::CloseHandle(hToken); |
| return false; |
| } |
| if (!::OpenProcessToken(::GetCurrentProcess(), dwDesiredAccess, &hToken)) { |
| ::CloseHandle(hToken); |
| return false; |
| } |
| } |
| |
| *phToken = hToken; |
| return true; |
| } |
| |
| bool AllocGetTokenInformation(const HANDLE hToken, |
| const TOKEN_INFORMATION_CLASS TokenInfoClass, |
| PVOID* ppInfo, |
| DWORD* pdwSizeBc) { |
| DWORD dwBufferSizeBc = 0; |
| DWORD dwReturnSizeBc = 0; |
| |
| const BOOL bReturn = ::GetTokenInformation(hToken, |
| TokenInfoClass, |
| nullptr, |
| 0, |
| &dwBufferSizeBc); |
| if (bReturn || (ERROR_INSUFFICIENT_BUFFER != ::GetLastError())) { |
| return false; |
| } |
| |
| PVOID pBuffer = ::LocalAlloc(LPTR, dwBufferSizeBc); |
| if (pBuffer == nullptr) { |
| return false; |
| } |
| |
| if (!::GetTokenInformation(hToken, |
| TokenInfoClass, |
| pBuffer, |
| dwBufferSizeBc, |
| &dwReturnSizeBc)) { |
| ::LocalFree(pBuffer); |
| return false; |
| } |
| |
| if (ppInfo != nullptr) { |
| *ppInfo = pBuffer; |
| } |
| |
| if (pdwSizeBc != nullptr) { |
| *pdwSizeBc = dwReturnSizeBc; |
| } |
| |
| return true; |
| } |
| |
| bool GetTokenUserSidStringW(const HANDLE hToken, |
| PWSTR *pwszSidString) { |
| PTOKEN_USER pTokenUser = nullptr; |
| PWSTR wszSidString = nullptr; |
| |
| if (AllocGetTokenInformation(hToken, TokenUser, |
| reinterpret_cast<PVOID *>(&pTokenUser), |
| nullptr) && |
| ::ConvertSidToStringSidW(pTokenUser->User.Sid, &wszSidString)) { |
| ::LocalFree(pTokenUser); |
| *pwszSidString = wszSidString; |
| return true; |
| } |
| |
| if (wszSidString != nullptr) { |
| ::LocalFree(wszSidString); |
| } |
| |
| if (pTokenUser != nullptr) { |
| ::LocalFree(pTokenUser); |
| } |
| |
| return false; |
| } |
| |
| bool GetTokenPrimaryGroupSidStringW(const HANDLE hToken, |
| PWSTR *pwszSidString) { |
| PTOKEN_PRIMARY_GROUP pTokenPrimaryGroup = nullptr; |
| PWSTR wszSidString = nullptr; |
| |
| if (AllocGetTokenInformation(hToken, TokenPrimaryGroup, |
| reinterpret_cast<PVOID *>(&pTokenPrimaryGroup), |
| nullptr) && |
| ::ConvertSidToStringSidW(pTokenPrimaryGroup->PrimaryGroup, |
| &wszSidString)) { |
| ::LocalFree(pTokenPrimaryGroup); |
| *pwszSidString = wszSidString; |
| return true; |
| } |
| |
| if (wszSidString != nullptr) { |
| ::LocalFree(wszSidString); |
| } |
| |
| if (pTokenPrimaryGroup != nullptr) { |
| ::LocalFree(pTokenPrimaryGroup); |
| } |
| |
| return false; |
| } |
| |
| class ScopedLocalFreeInvoker { |
| public: |
| explicit ScopedLocalFreeInvoker(void *address) : address_(address) {} |
| ~ScopedLocalFreeInvoker() { |
| if (address_ != nullptr) { |
| ::LocalFree(address_); |
| address_ = nullptr; |
| } |
| } |
| |
| private: |
| void *address_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScopedLocalFreeInvoker); |
| }; |
| |
| bool GetUserSid(wstring *token_user_sid, wstring *token_primary_group_sid) { |
| DCHECK(token_user_sid); |
| DCHECK(token_primary_group_sid); |
| token_user_sid->clear(); |
| token_primary_group_sid->clear(); |
| |
| ScopedHandle token; |
| { |
| HANDLE hToken = nullptr; |
| if (!OpenEffectiveToken(TOKEN_QUERY, &hToken)) { |
| LOG(ERROR) << "OpenEffectiveToken failed " << ::GetLastError(); |
| return false; |
| } |
| token.reset(hToken); |
| } |
| |
| // Get token user SID |
| { |
| wchar_t* sid_string = nullptr; |
| if (!GetTokenUserSidStringW(token.get(), &sid_string)) { |
| LOG(ERROR) << "GetTokenUserSidStringW failed " << ::GetLastError(); |
| return false; |
| } |
| *token_user_sid = sid_string; |
| ::LocalFree(sid_string); |
| } |
| |
| // Get token primary group SID |
| { |
| wchar_t* sid_string = nullptr; |
| if (!GetTokenPrimaryGroupSidStringW(token.get(), &sid_string)) { |
| LOG(ERROR) << "GetTokenPrimaryGroupSidStringW failed " |
| << ::GetLastError(); |
| return false; |
| } |
| *token_primary_group_sid = sid_string; |
| ::LocalFree(sid_string); |
| } |
| |
| return true; |
| } |
| |
| wstring Allow(const wstring &access_right, const wstring &account_sid) { |
| return (wstring(L"(") + SDDL_ACCESS_ALLOWED + L";;" + |
| access_right + L";;;" + account_sid + L")"); |
| } |
| |
| wstring Deny(const wstring &access_right, const wstring &account_sid) { |
| return (wstring(L"(") + SDDL_ACCESS_DENIED + L";;" + |
| access_right + L";;;" + account_sid + L")"); |
| } |
| |
| wstring MandatoryLevel(const wstring &mandatory_label, |
| const wstring &integrity_levels) { |
| return (wstring(L"(") + SDDL_MANDATORY_LABEL + L";;" + |
| mandatory_label + L";;;" + integrity_levels + L")"); |
| } |
| |
| // SDDL_ALL_APP_PACKAGES is available on Windows SDK 8.0 and later. |
| #ifndef SDDL_ALL_APP_PACKAGES |
| #define SDDL_ALL_APP_PACKAGES L"AC" |
| #endif // SDDL_ALL_APP_PACKAGES |
| |
| // SDDL for PROCESS_QUERY_INFORMATION is not defined. So use hex digits instead. |
| #ifndef SDDL_PROCESS_QUERY_INFORMATION |
| static_assert(PROCESS_QUERY_INFORMATION == 0x0400, |
| "PROCESS_QUERY_INFORMATION must be 0x0400"); |
| #define SDDL_PROCESS_QUERY_INFORMATION L"0x0400" |
| #endif // SDDL_PROCESS_QUERY_INFORMATION |
| |
| // SDDL for PROCESS_QUERY_LIMITED_INFORMATION is not defined. So use hex digits |
| // instead. |
| #ifndef SDDL_PROCESS_QUERY_LIMITED_INFORMATION |
| static_assert(PROCESS_QUERY_LIMITED_INFORMATION == 0x1000, |
| "PROCESS_QUERY_LIMITED_INFORMATION must be 0x1000"); |
| #define SDDL_PROCESS_QUERY_LIMITED_INFORMATION L"0x1000" |
| #endif // SDDL_PROCESS_QUERY_LIMITED_INFORMATION |
| |
| } // namespace |
| |
| wstring WinSandbox::GetSDDL(ObjectSecurityType shareble_object_type, |
| const wstring &token_user_sid, |
| const wstring &token_primary_group_sid, |
| bool is_windows_8_or_later) { |
| // See http://social.msdn.microsoft.com/Forums/en-US/windowssecurity/thread/e92502b1-0b9f-4e02-9d72-e4e47e924a8f/ |
| // for how to acess named objects from an AppContainer. |
| |
| wstring dacl; |
| wstring sacl; |
| switch (shareble_object_type) { |
| case WinSandbox::kSharablePipe: |
| // Strip implicit owner rights |
| // http://technet.microsoft.com/en-us/library/dd125370.aspx |
| dacl += Allow(L"", SDDL_OWNER_RIGHTS); |
| // Deny remote acccess |
| dacl += Deny(SDDL_GENERIC_ALL, SDDL_NETWORK); |
| // Allow general access to LocalSystem |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_LOCAL_SYSTEM); |
| // Allow general access to Built-in Administorators |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_BUILTIN_ADMINISTRATORS); |
| if (is_windows_8_or_later) { |
| // Allow general access to ALL APPLICATION PACKAGES |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_ALL_APP_PACKAGES); |
| } |
| // Allow general access to the current user |
| dacl += Allow(SDDL_GENERIC_ALL, token_user_sid); |
| // Allow read/write access to low integrity |
| sacl += MandatoryLevel(SDDL_NO_EXECUTE_UP, SDDL_ML_LOW); |
| break; |
| case WinSandbox::kLooseSharablePipe: |
| // Strip implicit owner rights |
| // http://technet.microsoft.com/en-us/library/dd125370.aspx |
| dacl += Allow(L"", SDDL_OWNER_RIGHTS); |
| // Deny remote acccess |
| dacl += Deny(SDDL_GENERIC_ALL, SDDL_NETWORK); |
| // Allow general access to LocalSystem |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_LOCAL_SYSTEM); |
| // Allow general access to Built-in Administorators |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_BUILTIN_ADMINISTRATORS); |
| if (is_windows_8_or_later) { |
| // Allow general access to ALL APPLICATION PACKAGES |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_ALL_APP_PACKAGES); |
| } |
| // Allow general access to the current user |
| dacl += Allow(SDDL_GENERIC_ALL, token_user_sid); |
| // Skip 2nd-phase ACL validation against restricted tokens. |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_RESTRICTED_CODE); |
| // Allow read/write access to low integrity |
| sacl += MandatoryLevel(SDDL_NO_EXECUTE_UP, SDDL_ML_LOW); |
| break; |
| case WinSandbox::kSharableEvent: |
| // Strip implicit owner rights |
| // http://technet.microsoft.com/en-us/library/dd125370.aspx |
| dacl += Allow(L"", SDDL_OWNER_RIGHTS); |
| // Allow general access to LocalSystem |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_LOCAL_SYSTEM); |
| // Allow general access to Built-in Administorators |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_BUILTIN_ADMINISTRATORS); |
| if (is_windows_8_or_later) { |
| // Allow state change/synchronize to ALL APPLICATION PACKAGES |
| dacl += Allow(SDDL_GENERIC_EXECUTE, SDDL_ALL_APP_PACKAGES); |
| } |
| // Allow general access to the current user |
| dacl += Allow(SDDL_GENERIC_ALL, token_user_sid); |
| // Skip 2nd-phase ACL validation against restricted tokens regarding |
| // change/synchronize. |
| dacl += Allow(SDDL_GENERIC_EXECUTE, SDDL_RESTRICTED_CODE); |
| // Allow read/write access to low integrity |
| sacl += MandatoryLevel(SDDL_NO_EXECUTE_UP, SDDL_ML_LOW); |
| break; |
| case WinSandbox::kSharableMutex: |
| // Strip implicit owner rights |
| // http://technet.microsoft.com/en-us/library/dd125370.aspx |
| dacl += Allow(L"", SDDL_OWNER_RIGHTS); |
| // Allow general access to LocalSystem |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_LOCAL_SYSTEM); |
| // Allow general access to Built-in Administorators |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_BUILTIN_ADMINISTRATORS); |
| if (is_windows_8_or_later) { |
| // Allow state change/synchronize to ALL APPLICATION PACKAGES |
| dacl += Allow(SDDL_GENERIC_EXECUTE, SDDL_ALL_APP_PACKAGES); |
| } |
| // Allow general access to the current user |
| dacl += Allow(SDDL_GENERIC_ALL, token_user_sid); |
| // Skip 2nd-phase ACL validation against restricted tokens regarding |
| // change/synchronize. |
| dacl += Allow(SDDL_GENERIC_EXECUTE, SDDL_RESTRICTED_CODE); |
| // Allow read/write access to low integrity |
| sacl += MandatoryLevel(SDDL_NO_EXECUTE_UP, SDDL_ML_LOW); |
| break; |
| case WinSandbox::kSharableFileForRead: |
| // Strip implicit owner rights |
| // http://technet.microsoft.com/en-us/library/dd125370.aspx |
| dacl += Allow(L"", SDDL_OWNER_RIGHTS); |
| // Allow general access to LocalSystem |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_LOCAL_SYSTEM); |
| // Allow general access to Built-in Administorators |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_BUILTIN_ADMINISTRATORS); |
| // Allow read access to low integrity |
| if (is_windows_8_or_later) { |
| // Allow general read access to ALL APPLICATION PACKAGES |
| dacl += Allow(SDDL_GENERIC_READ, SDDL_ALL_APP_PACKAGES); |
| } |
| // Allow general access to the current user |
| dacl += Allow(SDDL_GENERIC_ALL, token_user_sid); |
| // Skip 2nd-phase ACL validation against restricted tokens regarding |
| // general read access. |
| dacl += Allow(SDDL_GENERIC_READ, SDDL_RESTRICTED_CODE); |
| // Allow read access to low integrity |
| sacl += MandatoryLevel( |
| SDDL_NO_WRITE_UP SDDL_NO_EXECUTE_UP, SDDL_ML_LOW); |
| break; |
| case WinSandbox::kIPCServerProcess: |
| // Strip implicit owner rights |
| // http://technet.microsoft.com/en-us/library/dd125370.aspx |
| dacl += Allow(L"", SDDL_OWNER_RIGHTS); |
| // Allow general access to LocalSystem |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_LOCAL_SYSTEM); |
| // Allow general access to Built-in Administorators |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_BUILTIN_ADMINISTRATORS); |
| if (is_windows_8_or_later) { |
| // Allow PROCESS_QUERY_LIMITED_INFORMATION to ALL APPLICATION PACKAGES |
| dacl += Allow(SDDL_PROCESS_QUERY_LIMITED_INFORMATION, |
| SDDL_ALL_APP_PACKAGES); |
| } |
| // Allow general access to the current user |
| dacl += Allow(SDDL_GENERIC_ALL, token_user_sid); |
| // Allow PROCESS_QUERY_LIMITED_INFORMATION to restricted tokens |
| dacl += Allow(SDDL_PROCESS_QUERY_LIMITED_INFORMATION, |
| SDDL_RESTRICTED_CODE); |
| break; |
| case WinSandbox::kPrivateObject: |
| default: |
| // Strip implicit owner rights |
| // http://technet.microsoft.com/en-us/library/dd125370.aspx |
| dacl += Allow(L"", SDDL_OWNER_RIGHTS); |
| // Allow general access to LocalSystem |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_LOCAL_SYSTEM); |
| // Allow general access to Built-in Administorators |
| dacl += Allow(SDDL_GENERIC_ALL, SDDL_BUILTIN_ADMINISTRATORS); |
| // Allow general access to the current user |
| dacl += Allow(SDDL_GENERIC_ALL, token_user_sid); |
| break; |
| } |
| |
| wstring sddl; |
| // Owner SID |
| sddl += ((SDDL_OWNER SDDL_DELIMINATOR) + token_user_sid); |
| // Primary Group SID |
| sddl += ((SDDL_GROUP SDDL_DELIMINATOR) + token_primary_group_sid); |
| // DACL |
| if (!dacl.empty()) { |
| sddl += ((SDDL_DACL SDDL_DELIMINATOR) + dacl); |
| } |
| // SACL |
| if (!sacl.empty()) { |
| sddl += ((SDDL_SACL SDDL_DELIMINATOR) + sacl); |
| } |
| |
| return sddl; |
| } |
| |
| Sid::Sid(const SID *sid) { |
| ::CopySid(sizeof(sid_), sid_, const_cast<SID*>(sid)); |
| }; |
| |
| Sid::Sid(WELL_KNOWN_SID_TYPE type) { |
| DWORD size_sid = sizeof(sid_); |
| ::CreateWellKnownSid(type, nullptr, sid_, &size_sid); |
| } |
| |
| const SID *Sid::GetPSID() const { |
| return reinterpret_cast<SID*>(const_cast<BYTE*>(sid_)); |
| } |
| |
| SID *Sid::GetPSID() { |
| return reinterpret_cast<SID*>(const_cast<BYTE*>(sid_)); |
| } |
| |
| wstring Sid::GetName() const { |
| wchar_t *ptr = nullptr; |
| Sid temp_sid(GetPSID()); |
| ConvertSidToStringSidW(temp_sid.GetPSID(), &ptr); |
| wstring name = ptr; |
| ::LocalFree(ptr); |
| return name; |
| } |
| |
| wstring Sid::GetAccountName() const { |
| wchar_t *ptr = nullptr; |
| DWORD name_size = 0; |
| DWORD domain_name_size = 0; |
| SID_NAME_USE name_use; |
| Sid temp_sid(GetPSID()); |
| ::LookupAccountSid(nullptr, temp_sid.GetPSID(), nullptr, &name_size, |
| nullptr, &domain_name_size, &name_use); |
| if (domain_name_size == 0) { |
| if (name_size == 0) { |
| // Use string SID instead. |
| return GetName(); |
| } |
| unique_ptr<wchar_t[]> name_buffer(new wchar_t[name_size]); |
| ::LookupAccountSid(nullptr, temp_sid.GetPSID(), name_buffer.get(), |
| &name_size, nullptr, &domain_name_size, &name_use); |
| return wstring(L"/") + name_buffer.get(); |
| } |
| unique_ptr<wchar_t[]> name_buffer(new wchar_t[name_size]); |
| unique_ptr<wchar_t[]> domain_name_buffer(new wchar_t[domain_name_size]); |
| ::LookupAccountSid(nullptr, temp_sid.GetPSID(), name_buffer.get(), &name_size, |
| domain_name_buffer.get(), &domain_name_size, &name_use); |
| const wstring domain_name = wstring(domain_name_buffer.get()); |
| const wstring user_name = wstring(name_buffer.get()); |
| return domain_name + L"/" + user_name; |
| } |
| |
| // make SecurityAttributes for the named pipe. |
| bool WinSandbox::MakeSecurityAttributes( |
| ObjectSecurityType shareble_object_type, |
| SECURITY_ATTRIBUTES *security_attributes) { |
| wstring token_user_sid; |
| wstring token_primary_group_sid; |
| if (!GetUserSid(&token_user_sid, &token_primary_group_sid)) { |
| return false; |
| } |
| |
| const wstring &sddl = GetSDDL( |
| shareble_object_type, token_user_sid, token_primary_group_sid, |
| SystemUtil::IsWindows8OrLater()); |
| |
| // Create self-relative SD |
| PSECURITY_DESCRIPTOR self_relative_desc = nullptr; |
| if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW( |
| sddl.c_str(), |
| SDDL_REVISION_1, |
| &self_relative_desc, |
| nullptr)) { |
| if (self_relative_desc != nullptr) { |
| ::LocalFree(self_relative_desc); |
| } |
| LOG(ERROR) |
| << "ConvertStringSecurityDescriptorToSecurityDescriptorW failed: " |
| << ::GetLastError(); |
| return false; |
| } |
| |
| // Set up security attributes |
| security_attributes->nLength= sizeof(SECURITY_ATTRIBUTES); |
| security_attributes->lpSecurityDescriptor= self_relative_desc; |
| security_attributes->bInheritHandle= FALSE; |
| |
| return true; |
| } |
| |
| bool WinSandbox::AddKnownSidToKernelObject(HANDLE object, const SID *known_sid, |
| DWORD inheritance_flag, |
| ACCESS_MASK access_mask) { |
| // We must pass |&descriptor| because 6th argument (|&old_dacl|) is |
| // non-null. Actually, returned |old_dacl| points the memory block |
| // of |descriptor|, which must be freed by ::LocalFree API. |
| // http://msdn.microsoft.com/en-us/library/aa446654.aspx |
| PSECURITY_DESCRIPTOR descriptor = nullptr; |
| PACL old_dacl = nullptr; |
| DWORD error = ::GetSecurityInfo( |
| object, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, |
| &old_dacl, nullptr, &descriptor); |
| // You need not to free |old_dacl| because |old_dacl| points inside of |
| // |descriptor|. |
| ScopedLocalFreeInvoker descripter_deleter(descriptor); |
| |
| if (error != ERROR_SUCCESS) { |
| DLOG(ERROR) << "GetSecurityInfo failed" << error; |
| return false; |
| } |
| |
| EXPLICIT_ACCESS new_access = {}; |
| new_access.grfAccessMode = GRANT_ACCESS; |
| new_access.grfAccessPermissions = access_mask; |
| new_access.grfInheritance = inheritance_flag; |
| new_access.Trustee.pMultipleTrustee = nullptr; |
| new_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; |
| new_access.Trustee.TrusteeForm = TRUSTEE_IS_SID; |
| // When |TrusteeForm| is TRUSTEE_IS_SID, |ptstrName| is a pointer to the SID |
| // of the trustee. |
| // http://msdn.microsoft.com/en-us/library/aa379636.aspx |
| new_access.Trustee.ptstrName = |
| reinterpret_cast<wchar_t *>(const_cast<SID *>(known_sid)); |
| |
| PACL new_dacl = nullptr; |
| error = ::SetEntriesInAcl(1, &new_access, old_dacl, &new_dacl); |
| ScopedLocalFreeInvoker new_decl_deleter(new_dacl); |
| if (error != ERROR_SUCCESS) { |
| DLOG(ERROR) << "SetEntriesInAcl failed" << error; |
| return false; |
| } |
| |
| error = ::SetSecurityInfo( |
| object, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, |
| new_dacl, nullptr); |
| if (error != ERROR_SUCCESS) { |
| DLOG(ERROR) << "SetSecurityInfo failed" << error; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Local functions for SpawnSandboxedProcess. |
| namespace { |
| // This Windows job object wrapper class corresponds to the Job class in |
| // Chromium sandbox library with JOB_LOCKDOWN except for LockedDownJob does not |
| // set JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, which is not required by Mozc. |
| // http://src.chromium.org/viewvc/chrome/trunk/src/sandbox/src/security_level.h?view=markup |
| class LockedDownJob { |
| public: |
| LockedDownJob() : job_handle_(nullptr) {} |
| |
| ~LockedDownJob() { |
| if (job_handle_ != nullptr) { |
| ::CloseHandle(job_handle_); |
| job_handle_ = nullptr; |
| }; |
| } |
| |
| bool IsValid() const { |
| return (job_handle_ != nullptr); |
| } |
| |
| DWORD Init(const wchar_t *job_name, bool allow_ui_operation) { |
| if (job_handle_ != nullptr) { |
| return ERROR_ALREADY_INITIALIZED; |
| } |
| job_handle_ = ::CreateJobObject(nullptr, job_name); |
| if (job_handle_ == nullptr) { |
| return ::GetLastError(); |
| } |
| { |
| JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {}; |
| limit_info.BasicLimitInformation.ActiveProcessLimit = 1; |
| limit_info.BasicLimitInformation.LimitFlags = |
| // Mozc does not use JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE so that the |
| // child process can continue running even after the parent is |
| // terminated. |
| // JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | |
| JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION | |
| JOB_OBJECT_LIMIT_ACTIVE_PROCESS; |
| if (::SetInformationJobObject(job_handle_, |
| JobObjectExtendedLimitInformation, |
| &limit_info, |
| sizeof(limit_info))) { |
| return ::GetLastError(); |
| } |
| } |
| |
| if (!allow_ui_operation) { |
| JOBOBJECT_BASIC_UI_RESTRICTIONS ui_restrictions = {}; |
| ui_restrictions.UIRestrictionsClass = |
| JOB_OBJECT_UILIMIT_WRITECLIPBOARD | |
| JOB_OBJECT_UILIMIT_READCLIPBOARD | |
| JOB_OBJECT_UILIMIT_HANDLES | |
| JOB_OBJECT_UILIMIT_GLOBALATOMS | |
| JOB_OBJECT_UILIMIT_DISPLAYSETTINGS | |
| JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS | |
| JOB_OBJECT_UILIMIT_DESKTOP | |
| JOB_OBJECT_UILIMIT_EXITWINDOWS; |
| if (!::SetInformationJobObject(job_handle_, |
| JobObjectBasicUIRestrictions, |
| &ui_restrictions, |
| sizeof(ui_restrictions))) { |
| return ::GetLastError(); |
| } |
| } |
| return ERROR_SUCCESS; |
| } |
| |
| DWORD AssignProcessToJob(HANDLE process_handle) { |
| if (job_handle_ == nullptr) { |
| return ERROR_NO_DATA; |
| } |
| if (!::AssignProcessToJobObject(job_handle_, process_handle)) { |
| return ::GetLastError(); |
| } |
| return ERROR_SUCCESS; |
| } |
| |
| private: |
| HANDLE job_handle_; |
| |
| DISALLOW_COPY_AND_ASSIGN(LockedDownJob); |
| }; |
| |
| bool CreateSuspendedRestrictedProcess(unique_ptr<wchar_t[]> *command_line, |
| const WinSandbox::SecurityInfo &info, |
| ScopedHandle *process_handle, |
| ScopedHandle *thread_handle, |
| DWORD *pid) { |
| HANDLE process_token_ret = nullptr; |
| if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, |
| &process_token_ret)) { |
| return false; |
| } |
| ScopedHandle process_token(process_token_ret); |
| |
| ScopedHandle primary_token; |
| if (!WinSandbox::GetRestrictedTokenHandle(process_token.get(), |
| info.primary_level, |
| info.integrity_level, |
| &primary_token)) { |
| return false; |
| } |
| |
| ScopedHandle impersonation_token; |
| if (!WinSandbox::GetRestrictedTokenHandleForImpersonation( |
| process_token.get(), |
| info.impersonation_level, |
| info.integrity_level, |
| &impersonation_token)) { |
| return false; |
| } |
| |
| PSECURITY_ATTRIBUTES security_attributes_ptr = nullptr; |
| SECURITY_ATTRIBUTES security_attributes = {}; |
| if (WinSandbox::MakeSecurityAttributes(WinSandbox::kIPCServerProcess, |
| &security_attributes)) { |
| security_attributes_ptr = &security_attributes; |
| // Override the impersonation thread token's DACL to avoid http://b/1728895 |
| // On Windows Server, the objects created by a member of |
| // the built-in administrators group do not always explicitly |
| // allow the current user to access the objects. |
| // Instead, such objects implicitly allow the user by allowing |
| // the built-in administratros group. |
| // However, Mozc asks Sandbox to remove the built-in administrators |
| // group from the current user's groups. Thus the impersonation thread |
| // cannot even look at its own thread token. |
| // That prevents GetRunLevel() from verifying its own thread identity. |
| // Note: Overriding the thread token's |
| // DACL will not elevate the thread's running context. |
| if (!::SetKernelObjectSecurity( |
| impersonation_token.get(), |
| DACL_SECURITY_INFORMATION, |
| security_attributes_ptr->lpSecurityDescriptor)) { |
| const DWORD last_error = ::GetLastError(); |
| DLOG(ERROR) << "SetKernelObjectSecurity failed. Error: " << last_error; |
| return false; |
| } |
| } |
| |
| DWORD creation_flags = info.creation_flags | CREATE_SUSPENDED; |
| // Note: If the current process is already in a job, you cannot use |
| // CREATE_BREAKAWAY_FROM_JOB. See b/1571395 |
| if (info.use_locked_down_job) { |
| creation_flags |= CREATE_BREAKAWAY_FROM_JOB; |
| } |
| |
| const wchar_t *startup_directory = |
| (info.in_system_dir ? SystemUtil::GetSystemDir() : nullptr); |
| |
| STARTUPINFO startup_info = {sizeof(STARTUPINFO)}; |
| PROCESS_INFORMATION process_info = {}; |
| // 3rd parameter of CreateProcessAsUser must be a writable buffer. |
| if (!::CreateProcessAsUser(primary_token.get(), |
| nullptr, // No application name. |
| command_line->get(), // must be writable. |
| security_attributes_ptr, |
| nullptr, |
| FALSE, // Do not inherit handles. |
| creation_flags, |
| nullptr, // Use the environment of the caller. |
| startup_directory, |
| &startup_info, |
| &process_info)) { |
| const DWORD last_error = ::GetLastError(); |
| DLOG(ERROR) << "CreateProcessAsUser failed. Error: " << last_error; |
| return false; |
| } |
| |
| if (security_attributes_ptr != nullptr) { |
| ::LocalFree(security_attributes_ptr->lpSecurityDescriptor); |
| } |
| |
| // Change the token of the main thread of the new process for the |
| // impersonation token with more rights. |
| if (!::SetThreadToken(&process_info.hThread, impersonation_token.get())) { |
| const DWORD last_error = ::GetLastError(); |
| DLOG(ERROR) << "SetThreadToken failed. Error: " << last_error; |
| ::TerminateProcess(process_info.hProcess, 0); |
| ::CloseHandle(process_info.hProcess); |
| ::CloseHandle(process_info.hThread); |
| return false; |
| } |
| if (thread_handle != nullptr) { |
| thread_handle->reset(process_info.hThread); |
| } else { |
| ::CloseHandle(process_info.hThread); |
| } |
| if (process_handle != nullptr) { |
| process_handle->reset(process_info.hProcess); |
| } else { |
| ::CloseHandle(process_info.hProcess); |
| } |
| if (pid != nullptr) { |
| *pid = process_info.dwProcessId; |
| } |
| |
| return true; |
| } |
| |
| bool SpawnSandboxedProcessImpl(unique_ptr<wchar_t[]> *command_line, |
| const WinSandbox::SecurityInfo &info, |
| DWORD *pid) { |
| LockedDownJob job; |
| |
| if (info.use_locked_down_job) { |
| const DWORD error_code = job.Init(nullptr, info.allow_ui_operation); |
| if (error_code != ERROR_SUCCESS) { |
| return false; |
| } |
| } |
| |
| ScopedHandle thread_handle; |
| ScopedHandle process_handle; |
| if (!CreateSuspendedRestrictedProcess( |
| command_line, info, &process_handle, &thread_handle, pid)) { |
| return false; |
| } |
| |
| if (job.IsValid()) { |
| const DWORD error_code = job.AssignProcessToJob(process_handle.get()); |
| if (error_code != ERROR_SUCCESS) { |
| ::TerminateProcess(process_handle.get(), 0); |
| return false; |
| } |
| } |
| |
| ::ResumeThread(thread_handle.get()); |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| WinSandbox::SecurityInfo::SecurityInfo() |
| : primary_level(WinSandbox::USER_LOCKDOWN), |
| impersonation_level(WinSandbox::USER_LOCKDOWN), |
| integrity_level(WinSandbox::INTEGRITY_LEVEL_SYSTEM), |
| creation_flags(0), |
| use_locked_down_job(false), |
| allow_ui_operation(false), |
| in_system_dir(false) {} |
| |
| bool WinSandbox::SpawnSandboxedProcess(const string &path, |
| const string &arg, |
| const SecurityInfo &info, |
| DWORD *pid) { |
| wstring wpath; |
| Util::UTF8ToWide(path.c_str(), &wpath); |
| wpath = L"\"" + wpath + L"\""; |
| if (!arg.empty()) { |
| wstring warg; |
| Util::UTF8ToWide(arg.c_str(), &warg); |
| wpath += L" "; |
| wpath += warg; |
| } |
| |
| unique_ptr<wchar_t[]> wpath2(new wchar_t[wpath.size() + 1]); |
| if (0 != wcscpy_s(wpath2.get(), wpath.size() + 1, wpath.c_str())) { |
| return false; |
| } |
| |
| if (!SpawnSandboxedProcessImpl(&wpath2, info, pid)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Utility functions and classes for GetRestrictionInfo |
| namespace { |
| template <typename T> |
| class Optional { |
| public: |
| Optional() |
| : value_(T()), |
| has_value_(false) {} |
| explicit Optional(const T &src) |
| : value_(src), |
| has_value_(true) {} |
| const T &value() const { |
| return value_; |
| } |
| static Optional<T> None() { |
| return Optional<T>(); |
| } |
| T *mutable_value() { |
| has_value_ = true; |
| return &value_; |
| } |
| bool has_value() const { |
| return has_value_; |
| } |
| void Clear() { |
| has_value_ = false; |
| } |
| private: |
| T value_; |
| bool has_value_; |
| }; |
| |
| // A utility class for GetTokenInformation API. |
| // This class manages data buffer into which |TokenDataType| type data |
| // is filled. |
| template<TOKEN_INFORMATION_CLASS TokenClass, typename TokenDataType> |
| class ScopedTokenInfo { |
| public: |
| explicit ScopedTokenInfo(HANDLE token) : initialized_(false) { |
| DWORD num_bytes = 0; |
| ::GetTokenInformation(token, TokenClass, nullptr, 0, &num_bytes); |
| if (num_bytes == 0) { |
| return; |
| } |
| buffer_.reset(new BYTE[num_bytes]); |
| TokenDataType *all_token_groups = |
| reinterpret_cast<TokenDataType *>(buffer_.get()); |
| if (!::GetTokenInformation(token, TokenClass, all_token_groups, |
| num_bytes, &num_bytes)) { |
| const DWORD last_error = ::GetLastError(); |
| DLOG(ERROR) << "GetTokenInformation failed. Last error: " << last_error; |
| buffer_.reset(nullptr); |
| return; |
| } |
| initialized_ = true; |
| } |
| TokenDataType *get() const { |
| return reinterpret_cast<TokenDataType *>(buffer_.get()); |
| } |
| TokenDataType *operator->() const { |
| return get(); |
| } |
| private: |
| unique_ptr<BYTE[]> buffer_; |
| bool initialized_; |
| }; |
| |
| // Wrapper class for the SID_AND_ATTRIBUTES structure. |
| class SidAndAttributes { |
| public: |
| SidAndAttributes() |
| : sid_(static_cast<SID *>(nullptr)), |
| attributes_(0) {} |
| SidAndAttributes(Sid sid, DWORD attributes) |
| : sid_(sid), |
| attributes_(attributes) {} |
| const Sid &sid() const { |
| return sid_; |
| } |
| const DWORD &attributes() const { |
| return attributes_; |
| } |
| const bool HasAttribute(DWORD attribute) const { |
| return (attributes_ & attribute) == attribute; |
| } |
| private: |
| Sid sid_; |
| DWORD attributes_; |
| }; |
| |
| // Returns all the 'TokenGroups' information of the specified |token_handle|. |
| vector<SidAndAttributes> GetAllTokenGroups(HANDLE token_handle) { |
| vector<SidAndAttributes> result; |
| ScopedTokenInfo<TokenGroups, TOKEN_GROUPS> all_token_groups(token_handle); |
| if (all_token_groups.get() == nullptr) { |
| return result; |
| } |
| for (size_t i = 0; i < all_token_groups->GroupCount; ++i) { |
| Sid sid(static_cast<SID *>(all_token_groups->Groups[i].Sid)); |
| const DWORD attributes = all_token_groups->Groups[i].Attributes; |
| result.push_back(SidAndAttributes(sid, attributes)); |
| } |
| return result; |
| } |
| |
| vector<SidAndAttributes> FilterByHavingAttribute( |
| const vector<SidAndAttributes> &source, DWORD attribute) { |
| vector<SidAndAttributes> result; |
| for (size_t i = 0; i < source.size(); ++i) { |
| if (source[i].HasAttribute(attribute)) { |
| result.push_back(source[i]); |
| } |
| } |
| return result; |
| } |
| |
| vector<SidAndAttributes> FilterByNotHavingAttribute( |
| const vector<SidAndAttributes> &source, DWORD attribute) { |
| vector<SidAndAttributes> result; |
| for (size_t i = 0; i < source.size(); ++i) { |
| if (!source[i].HasAttribute(attribute)) { |
| result.push_back(source[i]); |
| } |
| } |
| return result; |
| } |
| |
| template <size_t NumExceptions> |
| vector<Sid> FilterSidExceptFor( |
| const vector<SidAndAttributes> &source_sids, |
| const WELL_KNOWN_SID_TYPE (&exception_sids)[NumExceptions]) { |
| vector<Sid> result; |
| // find logon_sid. |
| for (size_t i = 0; i < source_sids.size(); ++i) { |
| bool in_the_exception_list = false; |
| for (size_t j = 0; j < NumExceptions; ++j) { |
| // These variables must be non-const because EqualSid API requires |
| // non-const pointer. |
| Sid source = source_sids[i].sid(); |
| Sid except(exception_sids[j]); |
| if (::EqualSid(source.GetPSID(), except.GetPSID())) { |
| in_the_exception_list = true; |
| break; |
| } |
| } |
| if (!in_the_exception_list) { |
| result.push_back(source_sids[i].sid()); |
| } |
| } |
| return result; |
| } |
| |
| template <size_t NumExceptions> |
| vector<LUID> FilterPrivilegesExceptFor( |
| const vector<LUID_AND_ATTRIBUTES> &source_privileges, |
| const wchar_t *(&exception_privileges)[NumExceptions]) { |
| vector<LUID> result; |
| for (size_t i = 0; i < source_privileges.size(); ++i) { |
| bool in_the_exception_list = false; |
| for (size_t j = 0; j < NumExceptions; ++j) { |
| const LUID source = source_privileges[i].Luid; |
| LUID except = {}; |
| ::LookupPrivilegeValue(nullptr, exception_privileges[j], &except); |
| if ((source.HighPart == except.HighPart) && |
| (source.LowPart == except.LowPart)) { |
| in_the_exception_list = true; |
| break; |
| } |
| } |
| if (!in_the_exception_list) { |
| result.push_back(source_privileges[i].Luid); |
| } |
| } |
| return result; |
| } |
| |
| Optional<SidAndAttributes> GetUserSid(HANDLE token) { |
| ScopedTokenInfo<TokenUser, TOKEN_USER> token_user(token); |
| if (token_user.get() == nullptr) { |
| return Optional<SidAndAttributes>::None(); |
| } |
| |
| Sid sid(static_cast<SID *>(token_user->User.Sid)); |
| const DWORD attributes = token_user->User.Attributes; |
| return Optional<SidAndAttributes>(SidAndAttributes(sid, attributes)); |
| } |
| |
| vector<LUID_AND_ATTRIBUTES> GetPrivileges(HANDLE token) { |
| vector<LUID_AND_ATTRIBUTES> result; |
| ScopedTokenInfo<TokenPrivileges, TOKEN_PRIVILEGES> token_privileges(token); |
| if (token_privileges.get() == nullptr) { |
| return result; |
| } |
| |
| for (size_t i = 0; i < token_privileges->PrivilegeCount; ++i) { |
| result.push_back(token_privileges->Privileges[i]); |
| } |
| |
| return result; |
| } |
| |
| bool CreateRestrictedTokenImpl(HANDLE effective_token, |
| WinSandbox::TokenLevel security_level, |
| ScopedHandle *restricted_token) { |
| const vector<Sid> sids_to_disable = |
| WinSandbox::GetSidsToDisable(effective_token, security_level); |
| const vector<LUID> privileges_to_disable = |
| WinSandbox::GetPrivilegesToDisable(effective_token, security_level); |
| const vector<Sid> sids_to_restrict = |
| WinSandbox::GetSidsToRestrict(effective_token, security_level); |
| |
| if ((sids_to_disable.size() == 0) && |
| (privileges_to_disable.size() == 0) && |
| (sids_to_restrict.size() == 0)) { |
| // Duplicate the token even if it's not modified at this point |
| // because any subsequent changes to this token would also affect the |
| // current process. |
| HANDLE new_token = nullptr; |
| const BOOL result = ::DuplicateTokenEx( |
| effective_token, TOKEN_ALL_ACCESS, nullptr, |
| SecurityIdentification, TokenPrimary, &new_token); |
| if (result == FALSE) { |
| return false; |
| } |
| restricted_token->reset(new_token); |
| return true; |
| } |
| |
| unique_ptr<SID_AND_ATTRIBUTES[]> sids_to_disable_array; |
| vector<Sid> sids_to_disable_array_buffer = sids_to_disable; |
| { |
| const size_t size = sids_to_disable.size(); |
| if (size > 0) { |
| sids_to_disable_array.reset(new SID_AND_ATTRIBUTES[size]); |
| for (size_t i = 0; i < size; ++i) { |
| sids_to_disable_array[i].Attributes = SE_GROUP_USE_FOR_DENY_ONLY; |
| sids_to_disable_array[i].Sid = |
| sids_to_disable_array_buffer[i].GetPSID(); |
| } |
| } |
| } |
| |
| unique_ptr<LUID_AND_ATTRIBUTES[]> privileges_to_disable_array; |
| { |
| const size_t size = privileges_to_disable.size(); |
| if (size > 0) { |
| privileges_to_disable_array.reset(new LUID_AND_ATTRIBUTES[size]); |
| for (unsigned int i = 0; i < size; ++i) { |
| privileges_to_disable_array[i].Attributes = 0; |
| privileges_to_disable_array[i].Luid = privileges_to_disable[i]; |
| } |
| } |
| } |
| |
| unique_ptr<SID_AND_ATTRIBUTES[]> sids_to_restrict_array; |
| vector<Sid> sids_to_restrict_array_buffer = sids_to_restrict; |
| { |
| const size_t size = sids_to_restrict.size(); |
| if (size > 0) { |
| sids_to_restrict_array.reset(new SID_AND_ATTRIBUTES[size]); |
| for (size_t i = 0; i < size; ++i) { |
| sids_to_restrict_array[i].Attributes = 0; |
| sids_to_restrict_array[i].Sid = |
| sids_to_restrict_array_buffer[i].GetPSID(); |
| } |
| } |
| } |
| |
| HANDLE new_token = nullptr; |
| const BOOL result = ::CreateRestrictedToken(effective_token, |
| SANDBOX_INERT, // This flag is used on Windows 7 |
| static_cast<DWORD>(sids_to_disable.size()), |
| sids_to_disable_array.get(), |
| static_cast<DWORD>(privileges_to_disable.size()), |
| privileges_to_disable_array.get(), |
| static_cast<DWORD>(sids_to_restrict.size()), |
| sids_to_restrict_array.get(), |
| &new_token); |
| if (result == FALSE) { |
| return false; |
| } |
| restricted_token->reset(new_token); |
| return true; |
| } |
| |
| bool AddSidToDefaultDacl(HANDLE token, const Sid& sid, ACCESS_MASK access) { |
| if (token == nullptr) { |
| return false; |
| } |
| |
| ScopedTokenInfo<TokenDefaultDacl, TOKEN_DEFAULT_DACL> default_dacl(token); |
| if (default_dacl.get() == nullptr) { |
| return false; |
| } |
| |
| ACL* new_dacl = nullptr; |
| { |
| EXPLICIT_ACCESS new_access = {}; |
| new_access.grfAccessMode = GRANT_ACCESS; |
| new_access.grfAccessPermissions = access; |
| new_access.grfInheritance = NO_INHERITANCE; |
| |
| new_access.Trustee.pMultipleTrustee = nullptr; |
| new_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; |
| new_access.Trustee.TrusteeForm = TRUSTEE_IS_SID; |
| Sid temp_sid(sid); |
| new_access.Trustee.ptstrName = reinterpret_cast<LPWSTR>(temp_sid.GetPSID()); |
| const DWORD result = ::SetEntriesInAcl( |
| 1, &new_access, default_dacl->DefaultDacl, &new_dacl); |
| if (result != ERROR_SUCCESS) { |
| return false; |
| } |
| } |
| |
| TOKEN_DEFAULT_DACL new_token_dacl = {new_dacl}; |
| const BOOL result = ::SetTokenInformation( |
| token, TokenDefaultDacl, &new_token_dacl, sizeof(new_token_dacl)); |
| ::LocalFree(new_dacl); |
| return (result != FALSE); |
| } |
| |
| const wchar_t *GetPredefinedSidString( |
| WinSandbox::IntegrityLevel integrity_level) { |
| // Defined in the following documents. |
| // http://msdn.microsoft.com/en-us/library/cc980032.aspx |
| // http://support.microsoft.com/kb/243330 |
| switch (integrity_level) { |
| case WinSandbox::INTEGRITY_LEVEL_SYSTEM: |
| return L"S-1-16-16384"; |
| case WinSandbox::INTEGRITY_LEVEL_HIGH: |
| return L"S-1-16-12288"; |
| case WinSandbox::INTEGRITY_LEVEL_MEDIUM_PLUS: |
| return L"S-1-16-8448"; |
| case WinSandbox::INTEGRITY_LEVEL_MEDIUM: |
| return L"S-1-16-8192"; |
| case WinSandbox::INTEGRITY_LEVEL_LOW: |
| return L"S-1-16-4096"; |
| case WinSandbox::INTEGRITY_LEVEL_UNTRUSTED: |
| return L"S-1-16-0"; |
| case WinSandbox::INTEGRITY_LEVEL_LAST: |
| return nullptr; |
| } |
| |
| return nullptr; |
| } |
| |
| bool SetTokenIntegrityLevel(HANDLE token, |
| WinSandbox::IntegrityLevel integrity_level) { |
| const wchar_t* sid_string = GetPredefinedSidString(integrity_level); |
| if (sid_string == nullptr) { |
| // do not change the integrity level. |
| return true; |
| } |
| |
| PSID integrity_sid = nullptr; |
| if (!::ConvertStringSidToSid(sid_string, &integrity_sid)) { |
| return false; |
| } |
| TOKEN_MANDATORY_LABEL label = { {integrity_sid, SE_GROUP_INTEGRITY} }; |
| const DWORD size = sizeof(TOKEN_MANDATORY_LABEL) + |
| ::GetLengthSid(integrity_sid); |
| const BOOL result = ::SetTokenInformation( |
| token, TokenIntegrityLevel, &label, size); |
| ::LocalFree(integrity_sid); |
| |
| return (result != FALSE); |
| } |
| |
| } // namespace |
| |
| vector<Sid> WinSandbox::GetSidsToDisable(HANDLE effective_token, |
| TokenLevel security_level) { |
| const vector<SidAndAttributes> all_token_groups = |
| GetAllTokenGroups(effective_token); |
| const Optional<SidAndAttributes> current_user_sid = |
| GetUserSid(effective_token); |
| const vector<SidAndAttributes> normal_tokens = |
| FilterByNotHavingAttribute( |
| FilterByNotHavingAttribute(all_token_groups, SE_GROUP_LOGON_ID), |
| SE_GROUP_INTEGRITY); |
| |
| vector<Sid> sids_to_disable; |
| switch (security_level) { |
| case USER_UNPROTECTED: |
| case USER_RESTRICTED_SAME_ACCESS: |
| sids_to_disable.clear(); |
| break; |
| case USER_NON_ADMIN: |
| case USER_INTERACTIVE: { |
| const WELL_KNOWN_SID_TYPE kSidExceptions[] = { |
| WinBuiltinUsersSid, |
| WinWorldSid, |
| WinInteractiveSid, |
| WinAuthenticatedUserSid, |
| }; |
| sids_to_disable = FilterSidExceptFor(normal_tokens, kSidExceptions); |
| break; |
| } |
| case USER_LIMITED: { |
| const WELL_KNOWN_SID_TYPE kSidExceptions[] = { |
| WinBuiltinUsersSid, |
| WinWorldSid, |
| WinInteractiveSid, |
| }; |
| sids_to_disable = FilterSidExceptFor(normal_tokens, kSidExceptions); |
| break; |
| } |
| case USER_RESTRICTED: |
| case USER_LOCKDOWN: |
| if (current_user_sid.has_value()) { |
| sids_to_disable.push_back(current_user_sid.value().sid()); |
| } |
| for (size_t i = 0; i < normal_tokens.size(); ++i) { |
| sids_to_disable.push_back(normal_tokens[i].sid()); |
| } |
| break; |
| default: |
| DLOG(FATAL) << "unexpeced TokenLevel"; |
| break; |
| } |
| return sids_to_disable; |
| } |
| |
| vector<LUID> WinSandbox::GetPrivilegesToDisable(HANDLE effective_token, |
| TokenLevel security_level ) { |
| const vector<LUID_AND_ATTRIBUTES> all_privileges = |
| GetPrivileges(effective_token); |
| |
| vector<LUID> privileges_to_disable; |
| switch (security_level) { |
| case USER_UNPROTECTED: |
| case USER_RESTRICTED_SAME_ACCESS: |
| privileges_to_disable.clear(); |
| break; |
| case USER_NON_ADMIN: |
| case USER_INTERACTIVE: |
| case USER_LIMITED: |
| case USER_RESTRICTED: { |
| const wchar_t* kPrivilegeExceptions[] = { |
| SE_CHANGE_NOTIFY_NAME, |
| }; |
| privileges_to_disable = FilterPrivilegesExceptFor(all_privileges, |
| kPrivilegeExceptions); |
| break; |
| } |
| case USER_LOCKDOWN: |
| for (size_t i = 0; i < all_privileges.size(); ++i) { |
| privileges_to_disable.push_back(all_privileges[i].Luid); |
| } |
| break; |
| default: |
| DLOG(FATAL) << "unexpeced TokenLevel"; |
| break; |
| } |
| return privileges_to_disable; |
| } |
| |
| vector<Sid> WinSandbox::GetSidsToRestrict(HANDLE effective_token, |
| TokenLevel security_level) { |
| const vector<SidAndAttributes> all_token_groups = |
| GetAllTokenGroups(effective_token); |
| const Optional<SidAndAttributes> current_user_sid = |
| GetUserSid(effective_token); |
| const vector<SidAndAttributes> token_logon_session = |
| FilterByHavingAttribute(all_token_groups, SE_GROUP_LOGON_ID); |
| |
| vector<Sid> sids_to_restrict; |
| switch (security_level) { |
| case USER_UNPROTECTED: |
| sids_to_restrict.clear(); |
| break; |
| case USER_RESTRICTED_SAME_ACCESS: { |
| if (current_user_sid.has_value()) { |
| sids_to_restrict.push_back(current_user_sid.value().sid()); |
| } |
| const vector<SidAndAttributes> tokens = |
| FilterByNotHavingAttribute(all_token_groups, SE_GROUP_INTEGRITY); |
| for (size_t i = 0; i < tokens.size(); ++i) { |
| sids_to_restrict.push_back(tokens[i].sid()); |
| } |
| break; |
| } |
| case USER_NON_ADMIN: |
| sids_to_restrict.clear(); |
| break; |
| case USER_INTERACTIVE: |
| sids_to_restrict.push_back(Sid(WinBuiltinUsersSid)); |
| sids_to_restrict.push_back(Sid(WinWorldSid)); |
| sids_to_restrict.push_back(Sid(WinRestrictedCodeSid)); |
| if (current_user_sid.has_value()) { |
| sids_to_restrict.push_back(current_user_sid.value().sid()); |
| } |
| for (size_t i = 0; i < token_logon_session.size(); ++i) { |
| sids_to_restrict.push_back(token_logon_session[i].sid()); |
| } |
| break; |
| case USER_LIMITED: |
| sids_to_restrict.push_back(Sid(WinBuiltinUsersSid)); |
| sids_to_restrict.push_back(Sid(WinWorldSid)); |
| sids_to_restrict.push_back(Sid(WinRestrictedCodeSid)); |
| // On Windows Vista, the following token (current logon sid) is required |
| // to create objects in BNO. Consider to use low integrity level |
| // so that it cannot access object created by other processes. |
| for (size_t i = 0; i < token_logon_session.size(); ++i) { |
| sids_to_restrict.push_back(token_logon_session[i].sid()); |
| } |
| break; |
| case USER_RESTRICTED: |
| sids_to_restrict.push_back(Sid(WinRestrictedCodeSid)); |
| break; |
| case USER_LOCKDOWN: |
| sids_to_restrict.push_back(Sid(WinNullSid)); |
| break; |
| default: |
| DLOG(FATAL) << "unexpeced TokenLevel"; |
| break; |
| } |
| return sids_to_restrict; |
| } |
| |
| bool WinSandbox::GetRestrictedTokenHandle( |
| HANDLE effective_token, |
| TokenLevel security_level, |
| IntegrityLevel integrity_level, |
| ScopedHandle* restricted_token) { |
| ScopedHandle new_token; |
| if (!CreateRestrictedTokenImpl(effective_token, security_level, |
| &new_token)) { |
| return false; |
| } |
| |
| // Modify the default dacl on the token to contain Restricted and the user. |
| if (!AddSidToDefaultDacl(new_token.get(), Sid(WinRestrictedCodeSid), |
| GENERIC_ALL)) { |
| return false; |
| } |
| |
| { |
| ScopedTokenInfo<TokenUser, TOKEN_USER> token_user(new_token.get()); |
| if (token_user.get() == nullptr) { |
| return false; |
| } |
| Sid user_sid(static_cast<SID *>(token_user->User.Sid)); |
| if (!AddSidToDefaultDacl(new_token.get(), user_sid, GENERIC_ALL)) { |
| return false; |
| } |
| } |
| |
| if (!SetTokenIntegrityLevel(new_token.get(), integrity_level)) { |
| return false; |
| } |
| |
| HANDLE token_handle = nullptr; |
| const BOOL result = ::DuplicateHandle( |
| ::GetCurrentProcess(), |
| new_token.get(), |
| ::GetCurrentProcess(), |
| &token_handle, |
| TOKEN_ALL_ACCESS, |
| FALSE, |
| 0); |
| if (result == FALSE) { |
| return false; |
| } |
| restricted_token->reset(token_handle); |
| |
| return true; |
| } |
| |
| bool WinSandbox::GetRestrictedTokenHandleForImpersonation( |
| HANDLE effective_token, |
| TokenLevel security_level, |
| IntegrityLevel integrity_level, |
| ScopedHandle* restricted_token) { |
| ScopedHandle new_token; |
| if (!GetRestrictedTokenHandle(effective_token, security_level, |
| integrity_level, &new_token)) { |
| return false; |
| } |
| |
| HANDLE impersonation_token_ret = nullptr; |
| if (!::DuplicateToken(new_token.get(), SecurityImpersonation, |
| &impersonation_token_ret)) { |
| return false; |
| } |
| ScopedHandle impersonation_token(impersonation_token_ret); |
| |
| HANDLE restricted_token_ret = nullptr; |
| if (!::DuplicateHandle(::GetCurrentProcess(), impersonation_token.get(), |
| ::GetCurrentProcess(), &restricted_token_ret, |
| TOKEN_ALL_ACCESS, FALSE, 0)) { |
| return false; |
| } |
| restricted_token->reset(restricted_token_ret); |
| return true; |
| } |
| |
| } // namespace mozc |
| #endif // OS_WIN |