// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "sandbox/win/src/sandbox_policy_base.h"

#include <sddl.h>
#include <stddef.h>
#include <stdint.h>

#include "base/callback.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "sandbox/win/src/acl.h"
#include "sandbox/win/src/filesystem_policy.h"
#include "sandbox/win/src/handle_policy.h"
#include "sandbox/win/src/interception.h"
#include "sandbox/win/src/job.h"
#include "sandbox/win/src/line_break_policy.h"
#include "sandbox/win/src/named_pipe_policy.h"
#include "sandbox/win/src/policy_broker.h"
#include "sandbox/win/src/policy_engine_processor.h"
#include "sandbox/win/src/policy_low_level.h"
#include "sandbox/win/src/process_mitigations.h"
#include "sandbox/win/src/process_mitigations_win32k_policy.h"
#include "sandbox/win/src/process_thread_policy.h"
#include "sandbox/win/src/registry_policy.h"
#include "sandbox/win/src/restricted_token_utils.h"
#include "sandbox/win/src/sandbox_policy.h"
#include "sandbox/win/src/sandbox_utils.h"
#include "sandbox/win/src/security_capabilities.h"
#include "sandbox/win/src/signed_policy.h"
#include "sandbox/win/src/sync_policy.h"
#include "sandbox/win/src/target_process.h"
#include "sandbox/win/src/top_level_dispatcher.h"
#include "sandbox/win/src/window.h"

namespace {

// The standard windows size for one memory page.
constexpr size_t kOneMemPage = 4096;
// The IPC and Policy shared memory sizes.
constexpr size_t kIPCMemSize = kOneMemPage * 2;
constexpr size_t kPolMemSize = kOneMemPage * 14;

// Helper function to allocate space (on the heap) for policy.
sandbox::PolicyGlobal* MakeBrokerPolicyMemory() {
  const size_t kTotalPolicySz = kPolMemSize;
  sandbox::PolicyGlobal* policy =
      static_cast<sandbox::PolicyGlobal*>(::operator new(kTotalPolicySz));
  DCHECK(policy);
  memset(policy, 0, kTotalPolicySz);
  policy->data_size = kTotalPolicySz - sizeof(sandbox::PolicyGlobal);
  return policy;
}

bool IsInheritableHandle(HANDLE handle) {
  if (!handle)
    return false;
  if (handle == INVALID_HANDLE_VALUE)
    return false;
  // File handles (FILE_TYPE_DISK) and pipe handles are known to be
  // inheritable.  Console handles (FILE_TYPE_CHAR) are not
  // inheritable via PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
  DWORD handle_type = GetFileType(handle);
  return handle_type == FILE_TYPE_DISK || handle_type == FILE_TYPE_PIPE;
}

}  // namespace

namespace sandbox {

SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level;
SANDBOX_INTERCEPT MitigationFlags g_shared_delayed_mitigations;

// Initializes static members. alternate_desktop_handle_ is a desktop on
// alternate_winstation_handle_, alternate_desktop_local_winstation_handle_ is a
// desktop on the same winstation as the parent process.
HWINSTA PolicyBase::alternate_winstation_handle_ = nullptr;
HDESK PolicyBase::alternate_desktop_handle_ = nullptr;
HDESK PolicyBase::alternate_desktop_local_winstation_handle_ = nullptr;
IntegrityLevel PolicyBase::alternate_desktop_integrity_level_label_ =
    INTEGRITY_LEVEL_SYSTEM;
IntegrityLevel
    PolicyBase::alternate_desktop_local_winstation_integrity_level_label_ =
        INTEGRITY_LEVEL_SYSTEM;

PolicyBase::PolicyBase()
    : ref_count(1),
      lockdown_level_(USER_LOCKDOWN),
      initial_level_(USER_LOCKDOWN),
      job_level_(JOB_LOCKDOWN),
      ui_exceptions_(0),
      memory_limit_(0),
      use_alternate_desktop_(false),
      use_alternate_winstation_(false),
      file_system_init_(false),
      relaxed_interceptions_(true),
      stdout_handle_(INVALID_HANDLE_VALUE),
      stderr_handle_(INVALID_HANDLE_VALUE),
      integrity_level_(INTEGRITY_LEVEL_LAST),
      delayed_integrity_level_(INTEGRITY_LEVEL_LAST),
      mitigations_(0),
      delayed_mitigations_(0),
      is_csrss_connected_(true),
      policy_maker_(nullptr),
      policy_(nullptr),
      lowbox_sid_(nullptr),
      lockdown_default_dacl_(false),
      add_restricting_random_sid_(false),
      enable_opm_redirection_(false),
      effective_token_(nullptr) {
  ::InitializeCriticalSection(&lock_);
  dispatcher_.reset(new TopLevelDispatcher(this));
}

PolicyBase::~PolicyBase() {
  TargetSet::iterator it;
  for (it = targets_.begin(); it != targets_.end(); ++it) {
    TargetProcess* target = (*it);
    delete target;
  }
  delete policy_maker_;
  delete policy_;

  if (lowbox_sid_)
    ::LocalFree(lowbox_sid_);

  ::DeleteCriticalSection(&lock_);
}

void PolicyBase::AddRef() {
  ::InterlockedIncrement(&ref_count);
}

void PolicyBase::Release() {
  if (0 == ::InterlockedDecrement(&ref_count))
    delete this;
}

ResultCode PolicyBase::SetTokenLevel(TokenLevel initial, TokenLevel lockdown) {
  if (initial < lockdown) {
    return SBOX_ERROR_BAD_PARAMS;
  }
  initial_level_ = initial;
  lockdown_level_ = lockdown;
  return SBOX_ALL_OK;
}

TokenLevel PolicyBase::GetInitialTokenLevel() const {
  return initial_level_;
}

TokenLevel PolicyBase::GetLockdownTokenLevel() const {
  return lockdown_level_;
}

void PolicyBase::SetDoNotUseRestrictingSIDs() {
  use_restricting_sids_ = false;
}

ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32_t ui_exceptions) {
  if (memory_limit_ && job_level == JOB_NONE) {
    return SBOX_ERROR_BAD_PARAMS;
  }
  job_level_ = job_level;
  ui_exceptions_ = ui_exceptions;
  return SBOX_ALL_OK;
}

JobLevel PolicyBase::GetJobLevel() const {
  return job_level_;
}

ResultCode PolicyBase::SetJobMemoryLimit(size_t memory_limit) {
  memory_limit_ = memory_limit;
  return SBOX_ALL_OK;
}

ResultCode PolicyBase::SetAlternateDesktop(bool alternate_winstation) {
  use_alternate_desktop_ = true;
  use_alternate_winstation_ = alternate_winstation;
  return CreateAlternateDesktop(alternate_winstation);
}

std::wstring PolicyBase::GetAlternateDesktop() const {
  // No alternate desktop or winstation. Return an empty string.
  if (!use_alternate_desktop_ && !use_alternate_winstation_) {
    return std::wstring();
  }

  if (use_alternate_winstation_) {
    // The desktop and winstation should have been created by now.
    // If we hit this scenario, it means that the user ignored the failure
    // during SetAlternateDesktop, so we ignore it here too.
    if (!alternate_desktop_handle_ || !alternate_winstation_handle_)
      return std::wstring();

    return GetFullDesktopName(alternate_winstation_handle_,
                              alternate_desktop_handle_);
  }

  if (!alternate_desktop_local_winstation_handle_)
    return std::wstring();

  return GetFullDesktopName(nullptr,
                            alternate_desktop_local_winstation_handle_);
}

ResultCode PolicyBase::CreateAlternateDesktop(bool alternate_winstation) {
  if (alternate_winstation) {
    // Check if it's already created.
    if (alternate_winstation_handle_ && alternate_desktop_handle_)
      return SBOX_ALL_OK;

    DCHECK(!alternate_winstation_handle_);
    // Create the window station.
    ResultCode result = CreateAltWindowStation(&alternate_winstation_handle_);
    if (SBOX_ALL_OK != result)
      return result;

    // Verify that everything is fine.
    if (!alternate_winstation_handle_ ||
        base::win::GetWindowObjectName(alternate_winstation_handle_).empty())
      return SBOX_ERROR_CANNOT_CREATE_DESKTOP;

    // Create the destkop.
    result = CreateAltDesktop(alternate_winstation_handle_,
                              &alternate_desktop_handle_);
    if (SBOX_ALL_OK != result)
      return result;

    // Verify that everything is fine.
    if (!alternate_desktop_handle_ ||
        base::win::GetWindowObjectName(alternate_desktop_handle_).empty()) {
      return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
    }
  } else {
    // Check if it already exists.
    if (alternate_desktop_local_winstation_handle_)
      return SBOX_ALL_OK;

    // Create the destkop.
    ResultCode result =
        CreateAltDesktop(nullptr, &alternate_desktop_local_winstation_handle_);
    if (SBOX_ALL_OK != result)
      return result;

    // Verify that everything is fine.
    if (!alternate_desktop_local_winstation_handle_ ||
        base::win::GetWindowObjectName(
            alternate_desktop_local_winstation_handle_)
            .empty()) {
      return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
    }
  }

  return SBOX_ALL_OK;
}

void PolicyBase::DestroyAlternateDesktop() {
  if (use_alternate_winstation_) {
    if (alternate_desktop_handle_) {
      ::CloseDesktop(alternate_desktop_handle_);
      alternate_desktop_handle_ = nullptr;
    }

    if (alternate_winstation_handle_) {
      ::CloseWindowStation(alternate_winstation_handle_);
      alternate_winstation_handle_ = nullptr;
    }
  } else {
    if (alternate_desktop_local_winstation_handle_) {
      ::CloseDesktop(alternate_desktop_local_winstation_handle_);
      alternate_desktop_local_winstation_handle_ = nullptr;
    }
  }
}

ResultCode PolicyBase::SetIntegrityLevel(IntegrityLevel integrity_level) {
  if (app_container_profile_)
    return SBOX_ERROR_BAD_PARAMS;
  integrity_level_ = integrity_level;
  return SBOX_ALL_OK;
}

IntegrityLevel PolicyBase::GetIntegrityLevel() const {
  return integrity_level_;
}

ResultCode PolicyBase::SetDelayedIntegrityLevel(
    IntegrityLevel integrity_level) {
  delayed_integrity_level_ = integrity_level;
  return SBOX_ALL_OK;
}

ResultCode PolicyBase::SetLowBox(const wchar_t* sid) {
  if (base::win::GetVersion() < base::win::Version::WIN8)
    return SBOX_ERROR_UNSUPPORTED;

  DCHECK(sid);
  if (lowbox_sid_ || app_container_profile_)
    return SBOX_ERROR_BAD_PARAMS;

  if (!ConvertStringSidToSid(sid, &lowbox_sid_))
    return SBOX_ERROR_INVALID_LOWBOX_SID;

  return SBOX_ALL_OK;
}

ResultCode PolicyBase::SetProcessMitigations(MitigationFlags flags) {
  // Prior to Win10 RS5 CreateProcess fails when AppContainer and mitigation
  // flags are enabled. Return an error on downlevel platforms if trying to
  // set new mitigations.
  if (app_container_profile_ &&
      base::win::GetVersion() < base::win::Version::WIN10_RS5) {
    return SBOX_ERROR_BAD_PARAMS;
  }
  if (!CanSetProcessMitigationsPreStartup(flags))
    return SBOX_ERROR_BAD_PARAMS;
  mitigations_ = flags;
  return SBOX_ALL_OK;
}

MitigationFlags PolicyBase::GetProcessMitigations() {
  return mitigations_;
}

ResultCode PolicyBase::SetDelayedProcessMitigations(MitigationFlags flags) {
  if (!CanSetProcessMitigationsPostStartup(flags))
    return SBOX_ERROR_BAD_PARAMS;
  delayed_mitigations_ = flags;
  return SBOX_ALL_OK;
}

MitigationFlags PolicyBase::GetDelayedProcessMitigations() const {
  return delayed_mitigations_;
}

void PolicyBase::SetStrictInterceptions() {
  relaxed_interceptions_ = false;
}

ResultCode PolicyBase::SetStdoutHandle(HANDLE handle) {
  if (!IsInheritableHandle(handle))
    return SBOX_ERROR_BAD_PARAMS;
  stdout_handle_ = handle;
  return SBOX_ALL_OK;
}

ResultCode PolicyBase::SetStderrHandle(HANDLE handle) {
  if (!IsInheritableHandle(handle))
    return SBOX_ERROR_BAD_PARAMS;
  stderr_handle_ = handle;
  return SBOX_ALL_OK;
}

ResultCode PolicyBase::AddRule(SubSystem subsystem,
                               Semantics semantics,
                               const wchar_t* pattern) {
  ResultCode result = AddRuleInternal(subsystem, semantics, pattern);
  LOG_IF(ERROR, result != SBOX_ALL_OK)
      << "Failed to add sandbox rule."
      << " error = " << result << ", subsystem = " << subsystem
      << ", semantics = " << semantics << ", pattern = '" << pattern << "'";
  return result;
}

ResultCode PolicyBase::AddDllToUnload(const wchar_t* dll_name) {
  blocklisted_dlls_.push_back(dll_name);
  return SBOX_ALL_OK;
}

ResultCode PolicyBase::AddKernelObjectToClose(const wchar_t* handle_type,
                                              const wchar_t* handle_name) {
  return handle_closer_.AddHandle(handle_type, handle_name);
}

void PolicyBase::AddHandleToShare(HANDLE handle) {
  CHECK(handle);
  CHECK_NE(handle, INVALID_HANDLE_VALUE);

  // Ensure the handle can be inherited.
  bool result =
      SetHandleInformation(handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
  PCHECK(result);

  handles_to_share_.push_back(handle);
}

void PolicyBase::SetLockdownDefaultDacl() {
  lockdown_default_dacl_ = true;
}

void PolicyBase::AddRestrictingRandomSid() {
  add_restricting_random_sid_ = true;
}

const base::HandlesToInheritVector& PolicyBase::GetHandlesBeingShared() {
  return handles_to_share_;
}

ResultCode PolicyBase::MakeJobObject(base::win::ScopedHandle* job) {
  if (job_level_ == JOB_NONE) {
    job->Close();
    return SBOX_ALL_OK;
  }

  // Create the windows job object.
  Job job_obj;
  DWORD result =
      job_obj.Init(job_level_, nullptr, ui_exceptions_, memory_limit_);
  if (ERROR_SUCCESS != result)
    return SBOX_ERROR_CANNOT_INIT_JOB;

  *job = job_obj.Take();
  return SBOX_ALL_OK;
}

ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial,
                                  base::win::ScopedHandle* lockdown,
                                  base::win::ScopedHandle* lowbox) {
  Sid random_sid = Sid::GenerateRandomSid();
  PSID random_sid_ptr = nullptr;
  if (add_restricting_random_sid_)
    random_sid_ptr = random_sid.GetPSID();

  // Create the 'naked' token. This will be the permanent token associated
  // with the process and therefore with any thread that is not impersonating.
  DWORD result = CreateRestrictedToken(
      effective_token_, lockdown_level_, integrity_level_, PRIMARY,
      lockdown_default_dacl_, random_sid_ptr, use_restricting_sids_, lockdown);
  if (ERROR_SUCCESS != result)
    return SBOX_ERROR_CANNOT_CREATE_RESTRICTED_TOKEN;

  // If we're launching on the alternate desktop we need to make sure the
  // integrity label on the object is no higher than the sandboxed process's
  // integrity level. So, we lower the label on the desktop process if it's
  // not already low enough for our process.
  if (use_alternate_desktop_ && integrity_level_ != INTEGRITY_LEVEL_LAST) {
    // Integrity label enum is reversed (higher level is a lower value).
    static_assert(INTEGRITY_LEVEL_SYSTEM < INTEGRITY_LEVEL_UNTRUSTED,
                  "Integrity level ordering reversed.");
    HDESK desktop_handle = nullptr;
    IntegrityLevel desktop_integrity_level_label;
    if (use_alternate_winstation_) {
      desktop_handle = alternate_desktop_handle_;
      desktop_integrity_level_label = alternate_desktop_integrity_level_label_;
    } else {
      desktop_handle = alternate_desktop_local_winstation_handle_;
      desktop_integrity_level_label =
          alternate_desktop_local_winstation_integrity_level_label_;
    }
    // If the desktop_handle hasn't been created for any reason, skip this.
    if (desktop_handle && desktop_integrity_level_label < integrity_level_) {
      result =
          SetObjectIntegrityLabel(desktop_handle, SE_WINDOW_OBJECT, L"",
                                  GetIntegrityLevelString(integrity_level_));
      if (ERROR_SUCCESS != result)
        return SBOX_ERROR_CANNOT_SET_DESKTOP_INTEGRITY;

      if (use_alternate_winstation_) {
        alternate_desktop_integrity_level_label_ = integrity_level_;
      } else {
        alternate_desktop_local_winstation_integrity_level_label_ =
            integrity_level_;
      }
    }
  }

  if (lowbox_sid_) {
    if (!lowbox_directory_.IsValid()) {
      result =
          CreateLowBoxObjectDirectory(lowbox_sid_, true, &lowbox_directory_);
      DCHECK(result == ERROR_SUCCESS);
    }

    // The order of handles isn't important in the CreateLowBoxToken call.
    // The kernel will maintain a reference to the object directory handle.
    HANDLE saved_handles[1] = {lowbox_directory_.Get()};
    DWORD saved_handles_count = lowbox_directory_.IsValid() ? 1 : 0;

    Sid package_sid(lowbox_sid_);
    SecurityCapabilities caps(package_sid);
    if (CreateLowBoxToken(lockdown->Get(), PRIMARY, &caps, saved_handles,
                          saved_handles_count, lowbox) != ERROR_SUCCESS) {
      return SBOX_ERROR_CANNOT_CREATE_LOWBOX_TOKEN;
    }

    if (!ReplacePackageSidInDacl(lowbox->Get(), SE_KERNEL_OBJECT, package_sid,
                                 TOKEN_ALL_ACCESS)) {
      return SBOX_ERROR_CANNOT_MODIFY_LOWBOX_TOKEN_DACL;
    }
  }

  // Create the 'better' token. We use this token as the one that the main
  // thread uses when booting up the process. It should contain most of
  // what we need (before reaching main( ))
  result = CreateRestrictedToken(
      effective_token_, initial_level_, integrity_level_, IMPERSONATION,
      lockdown_default_dacl_, random_sid_ptr, use_restricting_sids_, initial);
  if (ERROR_SUCCESS != result)
    return SBOX_ERROR_CANNOT_CREATE_RESTRICTED_IMP_TOKEN;

  return SBOX_ALL_OK;
}

PSID PolicyBase::GetLowBoxSid() const {
  return lowbox_sid_;
}

ResultCode PolicyBase::AddTarget(TargetProcess* target) {
  if (policy_)
    policy_maker_->Done();

  if (!ApplyProcessMitigationsToSuspendedProcess(target->Process(),
                                                 mitigations_)) {
    return SBOX_ERROR_APPLY_ASLR_MITIGATIONS;
  }

  ResultCode ret = SetupAllInterceptions(target);

  if (ret != SBOX_ALL_OK)
    return ret;

  if (!SetupHandleCloser(target))
    return SBOX_ERROR_SETUP_HANDLE_CLOSER;

  DWORD win_error = ERROR_SUCCESS;
  // Initialize the sandbox infrastructure for the target.
  // TODO(wfh) do something with win_error code here.
  ret = target->Init(dispatcher_.get(), policy_, kIPCMemSize, kPolMemSize,
                     &win_error);

  if (ret != SBOX_ALL_OK)
    return ret;

  g_shared_delayed_integrity_level = delayed_integrity_level_;
  ret = target->TransferVariable("g_shared_delayed_integrity_level",
                                 &g_shared_delayed_integrity_level,
                                 sizeof(g_shared_delayed_integrity_level));
  g_shared_delayed_integrity_level = INTEGRITY_LEVEL_LAST;
  if (SBOX_ALL_OK != ret)
    return ret;

  // Add in delayed mitigations and pseudo-mitigations enforced at startup.
  g_shared_delayed_mitigations =
      delayed_mitigations_ | FilterPostStartupProcessMitigations(mitigations_);
  if (!CanSetProcessMitigationsPostStartup(g_shared_delayed_mitigations))
    return SBOX_ERROR_BAD_PARAMS;

  ret = target->TransferVariable("g_shared_delayed_mitigations",
                                 &g_shared_delayed_mitigations,
                                 sizeof(g_shared_delayed_mitigations));
  g_shared_delayed_mitigations = 0;
  if (SBOX_ALL_OK != ret)
    return ret;

  AutoLock lock(&lock_);
  targets_.push_back(target);
  return SBOX_ALL_OK;
}

bool PolicyBase::OnJobEmpty(HANDLE job) {
  AutoLock lock(&lock_);
  TargetSet::iterator it;
  for (it = targets_.begin(); it != targets_.end(); ++it) {
    if ((*it)->Job() == job)
      break;
  }
  if (it == targets_.end()) {
    return false;
  }
  TargetProcess* target = *it;
  targets_.erase(it);
  delete target;
  return true;
}

ResultCode PolicyBase::SetDisconnectCsrss() {
// Does not work on 32-bit, and the ASAN runtime falls over with the
// CreateThread EAT patch used when this is enabled.
// See https://crbug.com/783296#c27.
#if defined(_WIN64) && !defined(ADDRESS_SANITIZER)
  if (base::win::GetVersion() >= base::win::Version::WIN10) {
    is_csrss_connected_ = false;
    return AddKernelObjectToClose(L"ALPC Port", nullptr);
  }
#endif  // !defined(_WIN64)
  return SBOX_ALL_OK;
}

EvalResult PolicyBase::EvalPolicy(IpcTag service,
                                  CountedParameterSetBase* params) {
  if (policy_) {
    if (!policy_->entry[static_cast<size_t>(service)]) {
      // There is no policy for this particular service. This is not a big
      // deal.
      return DENY_ACCESS;
    }
    for (size_t i = 0; i < params->count; i++) {
      if (!params->parameters[i].IsValid()) {
        NOTREACHED();
        return SIGNAL_ALARM;
      }
    }
    PolicyProcessor pol_evaluator(policy_->entry[static_cast<size_t>(service)]);
    PolicyResult result =
        pol_evaluator.Evaluate(kShortEval, params->parameters, params->count);
    if (POLICY_MATCH == result)
      return pol_evaluator.GetAction();

    DCHECK(POLICY_ERROR != result);
  }

  return DENY_ACCESS;
}

HANDLE PolicyBase::GetStdoutHandle() {
  return stdout_handle_;
}

HANDLE PolicyBase::GetStderrHandle() {
  return stderr_handle_;
}

void PolicyBase::SetEnableOPMRedirection() {
  enable_opm_redirection_ = true;
}

bool PolicyBase::GetEnableOPMRedirection() {
  return enable_opm_redirection_;
}

ResultCode PolicyBase::AddAppContainerProfile(const wchar_t* package_name,
                                              bool create_profile) {
  if (base::win::GetVersion() < base::win::Version::WIN8)
    return SBOX_ERROR_UNSUPPORTED;

  DCHECK(package_name);
  if (lowbox_sid_ || app_container_profile_ ||
      integrity_level_ != INTEGRITY_LEVEL_LAST) {
    return SBOX_ERROR_BAD_PARAMS;
  }

  if (create_profile) {
    app_container_profile_ = AppContainerProfileBase::Create(
        package_name, L"Chrome Sandbox", L"Profile for Chrome Sandbox");
  } else {
    app_container_profile_ = AppContainerProfileBase::Open(package_name);
  }
  if (!app_container_profile_)
    return SBOX_ERROR_CREATE_APPCONTAINER_PROFILE;

  // A bug exists in CreateProcess where enabling an AppContainer profile and
  // passing a set of mitigation flags will generate ERROR_INVALID_PARAMETER.
  // Apply best efforts here and convert set mitigations to delayed mitigations.
  // This bug looks to have been fixed in Win10 RS5, so exit early if possible.
  if (base::win::GetVersion() >= base::win::Version::WIN10_RS5)
    return SBOX_ALL_OK;

  delayed_mitigations_ =
      mitigations_ & GetAllowedPostStartupProcessMitigations();
  DCHECK(delayed_mitigations_ ==
         (mitigations_ & ~(MITIGATION_SEHOP |
                           MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION)));
  mitigations_ = 0;
  return SBOX_ALL_OK;
}

scoped_refptr<AppContainerProfile> PolicyBase::GetAppContainerProfile() {
  return GetAppContainerProfileBase();
}

void PolicyBase::SetEffectiveToken(HANDLE token) {
  CHECK(token);
  effective_token_ = token;
}

scoped_refptr<AppContainerProfileBase>
PolicyBase::GetAppContainerProfileBase() {
  return app_container_profile_;
}

ResultCode PolicyBase::SetupAllInterceptions(TargetProcess* target) {
  InterceptionManager manager(target, relaxed_interceptions_);

  if (policy_) {
    for (size_t i = 0; i < kMaxIpcTag; i++) {
      if (policy_->entry[i] &&
          !dispatcher_->SetupService(&manager, static_cast<IpcTag>(i)))
        return SBOX_ERROR_SETUP_INTERCEPTION_SERVICE;
    }
  }

  for (const std::wstring& dll : blocklisted_dlls_)
    manager.AddToUnloadModules(dll.c_str());

  if (!SetupBasicInterceptions(&manager, is_csrss_connected_))
    return SBOX_ERROR_SETUP_BASIC_INTERCEPTIONS;

  ResultCode rc = manager.InitializeInterceptions();
  if (rc != SBOX_ALL_OK)
    return rc;

  // Finally, setup imports on the target so the interceptions can work.
  if (!SetupNtdllImports(target))
    return SBOX_ERROR_SETUP_NTDLL_IMPORTS;

  return SBOX_ALL_OK;
}

bool PolicyBase::SetupHandleCloser(TargetProcess* target) {
  return handle_closer_.InitializeTargetHandles(target);
}

ResultCode PolicyBase::AddRuleInternal(SubSystem subsystem,
                                       Semantics semantics,
                                       const wchar_t* pattern) {
  if (!policy_) {
    policy_ = MakeBrokerPolicyMemory();
    DCHECK(policy_);
    policy_maker_ = new LowLevelPolicy(policy_);
    DCHECK(policy_maker_);
  }

  switch (subsystem) {
    case SUBSYS_FILES: {
      if (!file_system_init_) {
        if (!FileSystemPolicy::SetInitialRules(policy_maker_))
          return SBOX_ERROR_BAD_PARAMS;
        file_system_init_ = true;
      }
      if (!FileSystemPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
        NOTREACHED();
        return SBOX_ERROR_BAD_PARAMS;
      }
      break;
    }
    case SUBSYS_SYNC: {
      if (!SyncPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
        NOTREACHED();
        return SBOX_ERROR_BAD_PARAMS;
      }
      break;
    }
    case SUBSYS_PROCESS: {
      if (lockdown_level_ < USER_INTERACTIVE &&
          TargetPolicy::PROCESS_ALL_EXEC == semantics) {
        // This is unsupported. This is a huge security risk to give full access
        // to a process handle.
        return SBOX_ERROR_UNSUPPORTED;
      }
      if (!ProcessPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
        NOTREACHED();
        return SBOX_ERROR_BAD_PARAMS;
      }
      break;
    }
    case SUBSYS_NAMED_PIPES: {
      if (!NamedPipePolicy::GenerateRules(pattern, semantics, policy_maker_)) {
        NOTREACHED();
        return SBOX_ERROR_BAD_PARAMS;
      }
      break;
    }
    case SUBSYS_REGISTRY: {
      if (!RegistryPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
        NOTREACHED();
        return SBOX_ERROR_BAD_PARAMS;
      }
      break;
    }
    case SUBSYS_HANDLES: {
      if (!HandlePolicy::GenerateRules(pattern, semantics, policy_maker_)) {
        NOTREACHED();
        return SBOX_ERROR_BAD_PARAMS;
      }
      break;
    }

    case SUBSYS_WIN32K_LOCKDOWN: {
      // Win32k intercept rules only supported on Windows 8 and above. This must
      // match the version checks in process_mitigations.cc for consistency.
      if (base::win::GetVersion() >= base::win::Version::WIN8) {
        DCHECK_EQ(MITIGATION_WIN32K_DISABLE,
                  mitigations_ & MITIGATION_WIN32K_DISABLE)
            << "Enable MITIGATION_WIN32K_DISABLE before adding win32k policy "
               "rules.";
        if (!ProcessMitigationsWin32KLockdownPolicy::GenerateRules(
                pattern, semantics, policy_maker_)) {
          NOTREACHED();
          return SBOX_ERROR_BAD_PARAMS;
        }
      }
      break;
    }
    case SUBSYS_SIGNED_BINARY: {
      // Signed intercept rules only supported on Windows 10 TH2 and above. This
      // must match the version checks in process_mitigations.cc for
      // consistency.
      if (base::win::GetVersion() >= base::win::Version::WIN10_TH2) {
        DCHECK_EQ(MITIGATION_FORCE_MS_SIGNED_BINS,
                  (mitigations_ & MITIGATION_FORCE_MS_SIGNED_BINS) | (delayed_mitigations_ & MITIGATION_FORCE_MS_SIGNED_BINS))
            << "Enable MITIGATION_FORCE_MS_SIGNED_BINS before adding signed "
               "policy rules.";
        if (!SignedPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
          NOTREACHED();
          return SBOX_ERROR_BAD_PARAMS;
        }
      }
      break;
    }
    case SUBSYS_LINE_BREAK: {
      if (!LineBreakPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
        NOTREACHED();
        return SBOX_ERROR_BAD_PARAMS;
      }
      break;
    }

    default: { return SBOX_ERROR_UNSUPPORTED; }
  }

  return SBOX_ALL_OK;
}

}  // namespace sandbox
