blob: 79775659c597b43f667806d5ae7aaad52831f5fb [file] [log] [blame]
// 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/restricted_token.h"
#include <windows.h>
#include <stddef.h>
#include <memory>
#include <unordered_set>
#include <vector>
#include "base/logging.h"
#include "base/win/access_token.h"
#include "sandbox/win/src/acl.h"
namespace {
LUID ConvertToLuid(const CHROME_LUID& luid) {
LUID ret;
memcpy(&ret, &luid, sizeof(luid));
return ret;
}
CHROME_LUID ConvertToChromeLuid(const LUID& luid) {
CHROME_LUID ret;
memcpy(&ret, &luid, sizeof(luid));
return ret;
}
std::vector<SID_AND_ATTRIBUTES> ConvertToAttributes(
const std::vector<base::win::Sid>& sids,
DWORD attributes) {
std::vector<SID_AND_ATTRIBUTES> ret(sids.size());
for (size_t i = 0; i < sids.size(); ++i) {
ret[i].Attributes = attributes;
ret[i].Sid = sids[i].GetPSID();
}
return ret;
}
} // namespace
namespace sandbox {
RestrictedToken::RestrictedToken()
: integrity_level_(INTEGRITY_LEVEL_LAST),
init_(false),
lockdown_default_dacl_(false) {}
RestrictedToken::~RestrictedToken() {}
DWORD RestrictedToken::Init(const HANDLE effective_token) {
if (init_)
return ERROR_ALREADY_INITIALIZED;
HANDLE temp_token;
if (effective_token) {
// We duplicate the handle to be able to use it even if the original handle
// is closed.
if (!::DuplicateHandle(::GetCurrentProcess(), effective_token,
::GetCurrentProcess(), &temp_token, 0, false,
DUPLICATE_SAME_ACCESS)) {
return ::GetLastError();
}
} else {
if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
&temp_token)) {
return ::GetLastError();
}
}
effective_token_.Set(temp_token);
absl::optional<base::win::AccessToken> query_token =
base::win::AccessToken::FromToken(effective_token_.Get());
if (!query_token)
return ERROR_NO_TOKEN;
query_token_.swap(query_token);
init_ = true;
return ERROR_SUCCESS;
}
DWORD RestrictedToken::GetRestrictedToken(
base::win::ScopedHandle* token) const {
DCHECK(init_);
if (!init_)
return ERROR_NO_TOKEN;
std::vector<SID_AND_ATTRIBUTES> deny_sids =
ConvertToAttributes(sids_for_deny_only_, SE_GROUP_USE_FOR_DENY_ONLY);
std::vector<SID_AND_ATTRIBUTES> restrict_sids =
ConvertToAttributes(sids_to_restrict_, 0);
std::vector<LUID_AND_ATTRIBUTES> disable_privs(privileges_to_disable_.size());
for (size_t i = 0; i < privileges_to_disable_.size(); ++i) {
disable_privs[i].Attributes = 0;
disable_privs[i].Luid = ConvertToLuid(privileges_to_disable_[i]);
}
bool result = true;
HANDLE new_token_handle = nullptr;
if (!deny_sids.empty() || !restrict_sids.empty() || !disable_privs.empty()) {
result = ::CreateRestrictedToken(
effective_token_.Get(), 0, static_cast<DWORD>(deny_sids.size()),
deny_sids.data(), static_cast<DWORD>(disable_privs.size()),
disable_privs.data(), static_cast<DWORD>(restrict_sids.size()),
restrict_sids.data(), &new_token_handle);
} 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.
result = ::DuplicateTokenEx(effective_token_.Get(), TOKEN_ALL_ACCESS,
nullptr, SecurityIdentification, TokenPrimary,
&new_token_handle);
}
if (!result)
return ::GetLastError();
base::win::ScopedHandle new_token(new_token_handle);
if (lockdown_default_dacl_) {
// Don't add Restricted sid and also remove logon sid access.
if (!RevokeLogonSidFromDefaultDacl(new_token.Get()))
return ::GetLastError();
} else {
// Modify the default dacl on the token to contain Restricted.
if (!AddSidToDefaultDacl(new_token.Get(),
base::win::WellKnownSid::kRestricted,
SecurityAccessMode::kGrant, GENERIC_ALL)) {
return ::GetLastError();
}
}
for (const auto& default_dacl_sid : sids_for_default_dacl_) {
if (!AddSidToDefaultDacl(new_token.Get(), std::get<0>(default_dacl_sid),
std::get<1>(default_dacl_sid),
std::get<2>(default_dacl_sid))) {
return ::GetLastError();
}
}
// Add user to default dacl.
if (!AddUserSidToDefaultDacl(new_token.Get(), GENERIC_ALL))
return ::GetLastError();
DWORD error = SetTokenIntegrityLevel(new_token.Get(), integrity_level_);
if (ERROR_SUCCESS != error)
return error;
HANDLE token_handle;
if (!::DuplicateHandle(::GetCurrentProcess(), new_token.Get(),
::GetCurrentProcess(), &token_handle, TOKEN_ALL_ACCESS,
false, // Don't inherit.
0)) {
return ::GetLastError();
}
token->Set(token_handle);
return ERROR_SUCCESS;
}
DWORD RestrictedToken::GetRestrictedTokenForImpersonation(
base::win::ScopedHandle* token) const {
DCHECK(init_);
if (!init_)
return ERROR_NO_TOKEN;
base::win::ScopedHandle restricted_token;
DWORD err_code = GetRestrictedToken(&restricted_token);
if (ERROR_SUCCESS != err_code)
return err_code;
HANDLE impersonation_token_handle;
if (!::DuplicateTokenEx(restricted_token.Get(), TOKEN_ALL_ACCESS, nullptr,
SecurityImpersonation, TokenImpersonation,
&impersonation_token_handle)) {
return ::GetLastError();
}
token->Set(impersonation_token_handle);
return ERROR_SUCCESS;
}
DWORD RestrictedToken::AddAllSidsForDenyOnly(
const std::vector<base::win::Sid>& exceptions) {
DCHECK(init_);
if (!init_)
return ERROR_NO_TOKEN;
// Build the list of the deny only group SIDs
for (const base::win::AccessToken::Group& group : query_token_->Groups()) {
if (group.IsIntegrity() || group.IsLogonId())
continue;
bool should_ignore = false;
for (const base::win::Sid& sid : exceptions) {
if (sid == group.GetSid()) {
should_ignore = true;
break;
}
}
if (!should_ignore) {
sids_for_deny_only_.push_back(group.GetSid().Clone());
}
}
return ERROR_SUCCESS;
}
DWORD RestrictedToken::AddSidForDenyOnly(const base::win::Sid& sid) {
DCHECK(init_);
if (!init_)
return ERROR_NO_TOKEN;
sids_for_deny_only_.push_back(sid.Clone());
return ERROR_SUCCESS;
}
DWORD RestrictedToken::AddSidForDenyOnly(base::win::WellKnownSid known_sid) {
absl::optional<base::win::Sid> sid = base::win::Sid::FromKnownSid(known_sid);
if (!sid)
return ERROR_INVALID_SID;
return AddSidForDenyOnly(*sid);
}
DWORD RestrictedToken::AddUserSidForDenyOnly() {
DCHECK(init_);
if (!init_)
return ERROR_NO_TOKEN;
sids_for_deny_only_.push_back(query_token_->User());
return ERROR_SUCCESS;
}
DWORD RestrictedToken::DeleteAllPrivileges(
const std::vector<std::wstring>& exceptions) {
DCHECK(init_);
if (!init_)
return ERROR_NO_TOKEN;
std::unordered_set<std::wstring> privilege_set(exceptions.begin(),
exceptions.end());
// Build the list of privileges to disable
for (const base::win::AccessToken::Privilege& privilege :
query_token_->Privileges()) {
if (privilege_set.count(privilege.GetName()) == 0) {
privileges_to_disable_.push_back(privilege.GetLuid());
}
}
return ERROR_SUCCESS;
}
DWORD RestrictedToken::DeletePrivilege(const wchar_t* privilege) {
DCHECK(init_);
if (!init_)
return ERROR_NO_TOKEN;
LUID luid = {0};
if (::LookupPrivilegeValue(nullptr, privilege, &luid))
privileges_to_disable_.push_back(ConvertToChromeLuid(luid));
else
return ::GetLastError();
return ERROR_SUCCESS;
}
DWORD RestrictedToken::AddRestrictingSid(const base::win::Sid& sid) {
DCHECK(init_);
if (!init_)
return ERROR_NO_TOKEN;
sids_to_restrict_.push_back(sid.Clone()); // No attributes
return ERROR_SUCCESS;
}
DWORD RestrictedToken::AddRestrictingSid(base::win::WellKnownSid known_sid) {
absl::optional<base::win::Sid> sid = base::win::Sid::FromKnownSid(known_sid);
if (!sid)
return ERROR_INVALID_SID;
return AddRestrictingSid(*sid);
}
DWORD RestrictedToken::AddRestrictingSidLogonSession() {
DCHECK(init_);
if (!init_)
return ERROR_NO_TOKEN;
absl::optional<base::win::Sid> logon_sid = query_token_->LogonId();
if (logon_sid)
sids_to_restrict_.push_back(std::move(*logon_sid));
return ERROR_SUCCESS;
}
DWORD RestrictedToken::AddRestrictingSidCurrentUser() {
DCHECK(init_);
if (!init_)
return ERROR_NO_TOKEN;
sids_to_restrict_.push_back(query_token_->User());
return ERROR_SUCCESS;
}
DWORD RestrictedToken::AddRestrictingSidAllSids() {
DCHECK(init_);
if (!init_)
return ERROR_NO_TOKEN;
// Add the current user to the list.
DWORD error = AddRestrictingSidCurrentUser();
if (ERROR_SUCCESS != error)
return error;
for (const base::win::AccessToken::Group& group : query_token_->Groups()) {
if (group.IsIntegrity())
continue;
AddRestrictingSid(group.GetSid());
}
return ERROR_SUCCESS;
}
DWORD RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level) {
integrity_level_ = integrity_level;
return ERROR_SUCCESS;
}
void RestrictedToken::SetLockdownDefaultDacl() {
lockdown_default_dacl_ = true;
}
DWORD RestrictedToken::AddDefaultDaclSid(const base::win::Sid& sid,
SecurityAccessMode access_mode,
ACCESS_MASK access) {
DCHECK(init_);
if (!init_)
return ERROR_NO_TOKEN;
sids_for_default_dacl_.push_back(
std::make_tuple(sid.Clone(), access_mode, access));
return ERROR_SUCCESS;
}
DWORD RestrictedToken::AddDefaultDaclSid(base::win::WellKnownSid known_sid,
SecurityAccessMode access_mode,
ACCESS_MASK access) {
absl::optional<base::win::Sid> sid = base::win::Sid::FromKnownSid(known_sid);
if (!sid)
return ERROR_INVALID_SID;
return AddDefaultDaclSid(*sid, access_mode, access);
}
} // namespace sandbox