178 lines
5.5 KiB
C++
178 lines
5.5 KiB
C++
|
|
// Copyright 2022 The Chromium Authors
|
||
|
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
|
// found in the LICENSE file.
|
||
|
|
|
||
|
|
#include "base/win/access_control_list.h"
|
||
|
|
|
||
|
|
#include <aclapi.h>
|
||
|
|
#include <windows.h>
|
||
|
|
|
||
|
|
#include <utility>
|
||
|
|
#include <vector>
|
||
|
|
|
||
|
|
#include "base/check.h"
|
||
|
|
#include "base/logging.h"
|
||
|
|
#include "base/notreached.h"
|
||
|
|
#include "base/numerics/checked_math.h"
|
||
|
|
#include "base/win/scoped_localalloc.h"
|
||
|
|
|
||
|
|
namespace base::win {
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
std::unique_ptr<uint8_t[]> AclToBuffer(const ACL* acl) {
|
||
|
|
if (!acl) {
|
||
|
|
return nullptr;
|
||
|
|
}
|
||
|
|
size_t size = acl->AclSize;
|
||
|
|
DCHECK(size >= sizeof(*acl));
|
||
|
|
std::unique_ptr<uint8_t[]> ptr = std::make_unique<uint8_t[]>(size);
|
||
|
|
memcpy(ptr.get(), acl, size);
|
||
|
|
return ptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::unique_ptr<uint8_t[]> EmptyAclToBuffer() {
|
||
|
|
ACL acl = {};
|
||
|
|
acl.AclRevision = ACL_REVISION;
|
||
|
|
acl.AclSize = static_cast<WORD>(sizeof(acl));
|
||
|
|
return AclToBuffer(&acl);
|
||
|
|
}
|
||
|
|
|
||
|
|
ACCESS_MODE ConvertAccessMode(SecurityAccessMode access_mode) {
|
||
|
|
switch (access_mode) {
|
||
|
|
case SecurityAccessMode::kGrant:
|
||
|
|
return GRANT_ACCESS;
|
||
|
|
case SecurityAccessMode::kSet:
|
||
|
|
return SET_ACCESS;
|
||
|
|
case SecurityAccessMode::kDeny:
|
||
|
|
return DENY_ACCESS;
|
||
|
|
case SecurityAccessMode::kRevoke:
|
||
|
|
return REVOKE_ACCESS;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
std::unique_ptr<uint8_t[]> AddACEToAcl(
|
||
|
|
ACL* old_acl,
|
||
|
|
const std::vector<ExplicitAccessEntry>& entries) {
|
||
|
|
std::vector<EXPLICIT_ACCESS> access_entries(entries.size());
|
||
|
|
auto entries_interator = access_entries.begin();
|
||
|
|
for (const ExplicitAccessEntry& entry : entries) {
|
||
|
|
EXPLICIT_ACCESS& new_access = *entries_interator++;
|
||
|
|
new_access.grfAccessMode = ConvertAccessMode(entry.mode());
|
||
|
|
new_access.grfAccessPermissions = entry.access_mask();
|
||
|
|
new_access.grfInheritance = entry.inheritance();
|
||
|
|
::BuildTrusteeWithSid(&new_access.Trustee, entry.sid().GetPSID());
|
||
|
|
}
|
||
|
|
|
||
|
|
PACL new_acl = nullptr;
|
||
|
|
DWORD error = ::SetEntriesInAcl(checked_cast<ULONG>(access_entries.size()),
|
||
|
|
access_entries.data(), old_acl, &new_acl);
|
||
|
|
if (error != ERROR_SUCCESS) {
|
||
|
|
::SetLastError(error);
|
||
|
|
DPLOG(ERROR) << "Failed adding ACEs to ACL";
|
||
|
|
return nullptr;
|
||
|
|
}
|
||
|
|
auto new_acl_ptr = TakeLocalAlloc(new_acl);
|
||
|
|
return AclToBuffer(new_acl_ptr.get());
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
ExplicitAccessEntry ExplicitAccessEntry::Clone() const {
|
||
|
|
return ExplicitAccessEntry{sid_, mode_, access_mask_, inheritance_};
|
||
|
|
}
|
||
|
|
|
||
|
|
ExplicitAccessEntry::ExplicitAccessEntry(const Sid& sid,
|
||
|
|
SecurityAccessMode mode,
|
||
|
|
DWORD access_mask,
|
||
|
|
DWORD inheritance)
|
||
|
|
: sid_(sid.Clone()),
|
||
|
|
mode_(mode),
|
||
|
|
access_mask_(access_mask),
|
||
|
|
inheritance_(inheritance) {}
|
||
|
|
|
||
|
|
ExplicitAccessEntry::ExplicitAccessEntry(WellKnownSid known_sid,
|
||
|
|
SecurityAccessMode mode,
|
||
|
|
DWORD access_mask,
|
||
|
|
DWORD inheritance)
|
||
|
|
: ExplicitAccessEntry(Sid(known_sid), mode, access_mask, inheritance) {}
|
||
|
|
|
||
|
|
ExplicitAccessEntry::ExplicitAccessEntry(ExplicitAccessEntry&&) = default;
|
||
|
|
ExplicitAccessEntry& ExplicitAccessEntry::operator=(ExplicitAccessEntry&&) =
|
||
|
|
default;
|
||
|
|
ExplicitAccessEntry::~ExplicitAccessEntry() = default;
|
||
|
|
|
||
|
|
absl::optional<AccessControlList> AccessControlList::FromPACL(ACL* acl) {
|
||
|
|
if (acl && !::IsValidAcl(acl)) {
|
||
|
|
::SetLastError(ERROR_INVALID_ACL);
|
||
|
|
return absl::nullopt;
|
||
|
|
}
|
||
|
|
return AccessControlList{acl};
|
||
|
|
}
|
||
|
|
|
||
|
|
absl::optional<AccessControlList> AccessControlList::FromMandatoryLabel(
|
||
|
|
DWORD integrity_level,
|
||
|
|
DWORD inheritance,
|
||
|
|
DWORD mandatory_policy) {
|
||
|
|
Sid sid = Sid::FromIntegrityLevel(integrity_level);
|
||
|
|
// Get total ACL length. SYSTEM_MANDATORY_LABEL_ACE contains the first DWORD
|
||
|
|
// of the SID so remove it from total.
|
||
|
|
DWORD length = sizeof(ACL) + sizeof(SYSTEM_MANDATORY_LABEL_ACE) +
|
||
|
|
::GetLengthSid(sid.GetPSID()) - sizeof(DWORD);
|
||
|
|
std::unique_ptr<uint8_t[]> sacl_ptr = std::make_unique<uint8_t[]>(length);
|
||
|
|
PACL sacl = reinterpret_cast<PACL>(sacl_ptr.get());
|
||
|
|
|
||
|
|
if (!::InitializeAcl(sacl, length, ACL_REVISION)) {
|
||
|
|
return absl::nullopt;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!::AddMandatoryAce(sacl, ACL_REVISION, inheritance, mandatory_policy,
|
||
|
|
sid.GetPSID())) {
|
||
|
|
return absl::nullopt;
|
||
|
|
}
|
||
|
|
|
||
|
|
DCHECK(::IsValidAcl(sacl));
|
||
|
|
AccessControlList ret;
|
||
|
|
ret.acl_ = std::move(sacl_ptr);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
AccessControlList::AccessControlList() : acl_(EmptyAclToBuffer()) {}
|
||
|
|
AccessControlList::AccessControlList(AccessControlList&&) = default;
|
||
|
|
AccessControlList& AccessControlList::operator=(AccessControlList&&) = default;
|
||
|
|
AccessControlList::~AccessControlList() = default;
|
||
|
|
|
||
|
|
bool AccessControlList::SetEntries(
|
||
|
|
const std::vector<ExplicitAccessEntry>& entries) {
|
||
|
|
if (entries.empty())
|
||
|
|
return true;
|
||
|
|
|
||
|
|
std::unique_ptr<uint8_t[]> acl = AddACEToAcl(get(), entries);
|
||
|
|
if (!acl)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
acl_ = std::move(acl);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool AccessControlList::SetEntry(const Sid& sid,
|
||
|
|
SecurityAccessMode mode,
|
||
|
|
DWORD access_mask,
|
||
|
|
DWORD inheritance) {
|
||
|
|
std::vector<ExplicitAccessEntry> ace_list;
|
||
|
|
ace_list.emplace_back(sid, mode, access_mask, inheritance);
|
||
|
|
return SetEntries(ace_list);
|
||
|
|
}
|
||
|
|
|
||
|
|
AccessControlList AccessControlList::Clone() const {
|
||
|
|
return AccessControlList{get()};
|
||
|
|
}
|
||
|
|
|
||
|
|
void AccessControlList::Clear() {
|
||
|
|
acl_ = EmptyAclToBuffer();
|
||
|
|
}
|
||
|
|
|
||
|
|
AccessControlList::AccessControlList(const ACL* acl) : acl_(AclToBuffer(acl)) {}
|
||
|
|
|
||
|
|
} // namespace base::win
|