text: // Copyright 2010-2016, 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 <atlsecurity.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, &wpath);
    wpath = L"\"" + wpath + L"\"";
    if (!arg.empty()) {
      wstring warg;
      Util::UTF8ToWide(arg, &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();
        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;
  }
  
  bool WinSandbox::EnsureAllApplicationPackagesPermisssion(
      const wstring &file_name) {
    // Get "All Application Packages" group SID.
    const ATL::CSid all_application_packages(
        Sid(WinBuiltinAnyPackageSid).GetPSID());
  
    // Get current DACL (Discretionary Access Control List) of |file_name|.
    ATL::CDacl dacl;
    if (!ATL::AtlGetDacl(file_name.c_str(), SE_FILE_OBJECT, &dacl)) {
      return false;
    }
  
    // As of Windows 10 Anniversary Update, following access masks (==0x1200a9)
    // are specified to files under Program Files by default.
    const ACCESS_MASK kDesiredMask =
        FILE_READ_DATA | FILE_READ_EA | FILE_EXECUTE | READ_CONTROL | SYNCHRONIZE;
  
    // Check if the desired ACE is already specified or not.
    for (UINT i = 0; i < dacl.GetAceCount(); ++i) {
      CSid ace_sid;
      ACCESS_MASK acess_mask = 0;
      BYTE ace_type = 0;
      dacl.GetAclEntry(i, &ace_sid, &acess_mask, &ace_type);
      if (ace_sid == all_application_packages &&
          ace_type == ACCESS_ALLOWED_ACE_TYPE &&
          (acess_mask & kDesiredMask) == kDesiredMask) {
        // This is the desired ACE.  There is nothing to do.
        return true;
      }
    }
  
    // We are here because there is no desired ACE.  Hence we do add it.
    if (!dacl.AddAllowedAce(
            all_application_packages, kDesiredMask, ACCESS_ALLOWED_ACE_TYPE)) {
      return false;
    }
    return ATL::AtlSetDacl(file_name.c_str(), SE_FILE_OBJECT, dacl);
  }
  
  }   // namespace mozc
  #endif  // OS_WIN
  