chromium/sandbox/win/src/restricted_token.cc

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

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

#include <windows.h>

#include <stddef.h>

#include <memory>
#include <utility>
#include <vector>

#include "base/logging.h"
#include "base/ranges/algorithm.h"
#include "base/win/access_token.h"
#include "base/win/security_util.h"
#include "sandbox/win/src/acl.h"

namespace sandbox {

RestrictedToken::RestrictedToken() = default;
RestrictedToken::~RestrictedToken() = default;

std::optional<base::win::AccessToken> RestrictedToken::GetRestrictedToken()
    const {
  std::optional<base::win::AccessToken> token =
      base::win::AccessToken::FromCurrentProcess(/*impersonation=*/false,
                                                 TOKEN_ALL_ACCESS);
  if (!token) {
    return std::nullopt;
  }
  return CreateRestricted(*token);
}

void RestrictedToken::AddAllSidsForDenyOnly(
    const std::vector<base::win::Sid>& exceptions) {
  add_all_sids_for_deny_only_ = true;
  add_all_exceptions_ = base::win::CloneSidVector(exceptions);
}

void RestrictedToken::AddSidForDenyOnly(const base::win::Sid& sid) {
  sids_for_deny_only_.push_back(sid.Clone());
}

void RestrictedToken::AddSidForDenyOnly(base::win::WellKnownSid known_sid) {
  sids_for_deny_only_.emplace_back(known_sid);
}

void RestrictedToken::AddUserSidForDenyOnly() {
  add_user_sid_for_deny_only_ = true;
}

void RestrictedToken::DeleteAllPrivileges(bool remove_traversal_privilege) {
  delete_all_privileges_ = true;
  remove_traversal_privilege_ = remove_traversal_privilege;
}

void RestrictedToken::AddRestrictingSid(const base::win::Sid& sid) {
  sids_to_restrict_.push_back(sid.Clone());
}

void RestrictedToken::AddRestrictingSid(base::win::WellKnownSid known_sid) {
  sids_to_restrict_.emplace_back(known_sid);
}

void RestrictedToken::AddRestrictingSidLogonSession() {
  add_restricting_sid_logon_session_ = true;
}

void RestrictedToken::AddRestrictingSidCurrentUser() {
  add_restricting_sid_current_user_ = true;
}

void RestrictedToken::AddRestrictingSidAllSids() {
  add_restricting_sid_all_sids_ = true;
  AddRestrictingSidCurrentUser();
}

void RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level) {
  integrity_rid_ = GetIntegrityLevelRid(integrity_level);
}

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

void RestrictedToken::AddDefaultDaclSid(
    const base::win::Sid& sid,
    base::win::SecurityAccessMode access_mode,
    ACCESS_MASK access) {
  sids_for_default_dacl_.emplace_back(sid.Clone(), access_mode, access, 0);
}

void RestrictedToken::AddDefaultDaclSid(
    base::win::WellKnownSid known_sid,
    base::win::SecurityAccessMode access_mode,
    ACCESS_MASK access) {
  sids_for_default_dacl_.emplace_back(known_sid, access_mode, access, 0);
}

std::optional<base::win::AccessToken>
RestrictedToken::GetRestrictedTokenForTesting(base::win::AccessToken& token) {
  return CreateRestricted(token);
}

std::vector<base::win::Sid> RestrictedToken::BuildDenyOnlySids(
    const base::win::AccessToken& token) const {
  std::vector<base::win::Sid> sids =
      base::win::CloneSidVector(sids_for_deny_only_);
  if (add_user_sid_for_deny_only_) {
    sids.push_back(token.User());
  }
  if (add_all_sids_for_deny_only_) {
    // Build the list of the deny only group SIDs
    for (const base::win::AccessToken::Group& group : token.Groups()) {
      if (group.IsIntegrity() || group.IsLogonId()) {
        continue;
      }
      if (base::ranges::find(add_all_exceptions_, group.GetSid()) ==
          add_all_exceptions_.end()) {
        sids.push_back(group.GetSid().Clone());
      }
    }
  }
  return sids;
}

std::vector<base::win::Sid> RestrictedToken::BuildRestrictedSids(
    const base::win::AccessToken& token) const {
  std::vector<base::win::Sid> sids =
      base::win::CloneSidVector(sids_to_restrict_);
  if (add_restricting_sid_current_user_) {
    sids.push_back(token.User());
  }
  if (add_restricting_sid_all_sids_) {
    for (const base::win::AccessToken::Group& group : token.Groups()) {
      if (group.IsIntegrity()) {
        continue;
      }
      sids.push_back(group.GetSid().Clone());
    }
  }
  if (add_restricting_sid_logon_session_) {
    std::optional<base::win::Sid> logon_sid = token.LogonId();
    if (logon_sid.has_value()) {
      sids.push_back(std::move(*logon_sid));
    }
  }
  return sids;
}

std::optional<base::win::AccessToken> RestrictedToken::CreateRestricted(
    const base::win::AccessToken& token) const {
  std::optional<base::win::AccessToken> new_token;

  std::vector<base::win::Sid> deny_sids = BuildDenyOnlySids(token);
  std::vector<base::win::Sid> restrict_sids = BuildRestrictedSids(token);
  if (!deny_sids.empty() || !restrict_sids.empty() || delete_all_privileges_) {
    new_token = token.CreateRestricted(
        delete_all_privileges_ ? DISABLE_MAX_PRIVILEGE : 0, deny_sids, {},
        restrict_sids, TOKEN_ALL_ACCESS);
  } else {
    // 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.
    new_token = token.DuplicatePrimary(TOKEN_ALL_ACCESS);
  }

  if (!new_token) {
    return std::nullopt;
  }

  if (delete_all_privileges_ && remove_traversal_privilege_ &&
      !new_token->RemoveAllPrivileges()) {
    return std::nullopt;
  }

  std::vector<base::win::ExplicitAccessEntry> dacl_entries;

  std::optional<base::win::AccessControlList> dacl = new_token->DefaultDacl();
  if (!dacl) {
    return std::nullopt;
  }

  if (lockdown_default_dacl_) {
    // Don't add Restricted sid and also remove logon sid access.
    std::optional<base::win::Sid> logon_sid = new_token->LogonId();
    if (logon_sid.has_value()) {
      dacl_entries.emplace_back(*logon_sid,
                                base::win::SecurityAccessMode::kRevoke, 0, 0);
    } else {
      DWORD last_error = ::GetLastError();
      if (last_error != ERROR_NOT_FOUND) {
        return std::nullopt;
      }
    }
  } else {
    dacl_entries.emplace_back(base::win::WellKnownSid::kRestricted,
                              base::win::SecurityAccessMode::kGrant,
                              GENERIC_ALL, 0);
  }

  for (const base::win::ExplicitAccessEntry& entry : sids_for_default_dacl_) {
    dacl_entries.push_back(entry.Clone());
  }

  dacl_entries.emplace_back(
      new_token->User(), base::win::SecurityAccessMode::kGrant, GENERIC_ALL, 0);

  if (!dacl->SetEntries(dacl_entries)) {
    return std::nullopt;
  }

  if (!new_token->SetDefaultDacl(*dacl)) {
    return std::nullopt;
  }

  if (integrity_rid_.has_value()) {
    if (!new_token->SetIntegrityLevel(*integrity_rid_)) {
      return std::nullopt;
    }
  }

  return new_token;
}

}  // namespace sandbox