blob: b1a6b9a11bfcdcb48ae22f6dd15d2d18647d62e0 [file] [log] [blame]
// 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