1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/win/access_control_list.h"
6
7 #include <windows.h>
8
9 #include <aclapi.h>
10 #include <stdint.h>
11
12 #include <utility>
13 #include <vector>
14
15 #include "base/check.h"
16 #include "base/compiler_specific.h"
17 #include "base/containers/heap_array.h"
18 #include "base/logging.h"
19 #include "base/notreached.h"
20 #include "base/numerics/checked_math.h"
21 #include "base/win/scoped_localalloc.h"
22
23 namespace base::win {
24
25 namespace {
26
AclToBuffer(const ACL * acl)27 base::HeapArray<uint8_t> AclToBuffer(const ACL* acl) {
28 if (!acl) {
29 return {};
30 }
31 const size_t size = acl->AclSize;
32 CHECK_GE(size, sizeof(*acl));
33 // SAFETY: ACL structure is followed in memory by ACEs. The size of the ACL
34 // struct and all the data related to it is placed in `acl->AclSize`, thus it
35 // is safe to copy `acl->AclSize` bytes starting at address of `acl`. The fact
36 // that the data pertaining to ACL is placed after the ACL structure in memory
37 // is also the reason why we cannot use `base::span_from_ref()` here.
38 return base::HeapArray<uint8_t>::CopiedFrom(
39 UNSAFE_BUFFERS(base::span(reinterpret_cast<const uint8_t*>(acl), size)));
40 }
41
EmptyAclToBuffer()42 base::HeapArray<uint8_t> EmptyAclToBuffer() {
43 ACL acl = {};
44 acl.AclRevision = ACL_REVISION;
45 acl.AclSize = static_cast<WORD>(sizeof(acl));
46 return AclToBuffer(&acl);
47 }
48
ConvertAccessMode(SecurityAccessMode access_mode)49 ACCESS_MODE ConvertAccessMode(SecurityAccessMode access_mode) {
50 switch (access_mode) {
51 case SecurityAccessMode::kGrant:
52 return GRANT_ACCESS;
53 case SecurityAccessMode::kSet:
54 return SET_ACCESS;
55 case SecurityAccessMode::kDeny:
56 return DENY_ACCESS;
57 case SecurityAccessMode::kRevoke:
58 return REVOKE_ACCESS;
59 }
60 }
61
62 // Note: on error, this function returns an empty heap array. If such an array
63 // were placed inside `AccessControlList::acl_`, it would cause the access
64 // control list to become a null ACL (allowing everyone access!).
AddACEToAcl(ACL * old_acl,const std::vector<ExplicitAccessEntry> & entries)65 base::HeapArray<uint8_t> AddACEToAcl(
66 ACL* old_acl,
67 const std::vector<ExplicitAccessEntry>& entries) {
68 std::vector<EXPLICIT_ACCESS> access_entries(entries.size());
69 auto entries_interator = access_entries.begin();
70 for (const ExplicitAccessEntry& entry : entries) {
71 EXPLICIT_ACCESS& new_access = *entries_interator++;
72 new_access.grfAccessMode = ConvertAccessMode(entry.mode());
73 new_access.grfAccessPermissions = entry.access_mask();
74 new_access.grfInheritance = entry.inheritance();
75 ::BuildTrusteeWithSid(&new_access.Trustee, entry.sid().GetPSID());
76 }
77
78 PACL new_acl = nullptr;
79 DWORD error = ::SetEntriesInAcl(checked_cast<ULONG>(access_entries.size()),
80 access_entries.data(), old_acl, &new_acl);
81 if (error != ERROR_SUCCESS) {
82 ::SetLastError(error);
83 DPLOG(ERROR) << "Failed adding ACEs to ACL";
84 return {};
85 }
86 auto new_acl_ptr = TakeLocalAlloc(new_acl);
87 return AclToBuffer(new_acl_ptr.get());
88 }
89
90 } // namespace
91
Clone() const92 ExplicitAccessEntry ExplicitAccessEntry::Clone() const {
93 return ExplicitAccessEntry{sid_, mode_, access_mask_, inheritance_};
94 }
95
ExplicitAccessEntry(const Sid & sid,SecurityAccessMode mode,DWORD access_mask,DWORD inheritance)96 ExplicitAccessEntry::ExplicitAccessEntry(const Sid& sid,
97 SecurityAccessMode mode,
98 DWORD access_mask,
99 DWORD inheritance)
100 : sid_(sid.Clone()),
101 mode_(mode),
102 access_mask_(access_mask),
103 inheritance_(inheritance) {}
104
ExplicitAccessEntry(WellKnownSid known_sid,SecurityAccessMode mode,DWORD access_mask,DWORD inheritance)105 ExplicitAccessEntry::ExplicitAccessEntry(WellKnownSid known_sid,
106 SecurityAccessMode mode,
107 DWORD access_mask,
108 DWORD inheritance)
109 : ExplicitAccessEntry(Sid(known_sid), mode, access_mask, inheritance) {}
110
111 ExplicitAccessEntry::ExplicitAccessEntry(ExplicitAccessEntry&&) = default;
112 ExplicitAccessEntry& ExplicitAccessEntry::operator=(ExplicitAccessEntry&&) =
113 default;
114 ExplicitAccessEntry::~ExplicitAccessEntry() = default;
115
FromPACL(ACL * acl)116 std::optional<AccessControlList> AccessControlList::FromPACL(ACL* acl) {
117 if (acl && !::IsValidAcl(acl)) {
118 ::SetLastError(ERROR_INVALID_ACL);
119 return std::nullopt;
120 }
121 return AccessControlList{acl};
122 }
123
FromMandatoryLabel(DWORD integrity_level,DWORD inheritance,DWORD mandatory_policy)124 std::optional<AccessControlList> AccessControlList::FromMandatoryLabel(
125 DWORD integrity_level,
126 DWORD inheritance,
127 DWORD mandatory_policy) {
128 Sid sid = Sid::FromIntegrityLevel(integrity_level);
129 // Get total ACL length. SYSTEM_MANDATORY_LABEL_ACE contains the first DWORD
130 // of the SID so remove it from total.
131 DWORD length = sizeof(ACL) + sizeof(SYSTEM_MANDATORY_LABEL_ACE) +
132 ::GetLengthSid(sid.GetPSID()) - sizeof(DWORD);
133 auto sacl_ptr = base::HeapArray<uint8_t>::Uninit(length);
134 PACL sacl = reinterpret_cast<PACL>(sacl_ptr.data());
135
136 if (!::InitializeAcl(sacl, length, ACL_REVISION)) {
137 return std::nullopt;
138 }
139
140 if (!::AddMandatoryAce(sacl, ACL_REVISION, inheritance, mandatory_policy,
141 sid.GetPSID())) {
142 return std::nullopt;
143 }
144
145 DCHECK(::IsValidAcl(sacl));
146 AccessControlList ret;
147 ret.acl_ = std::move(sacl_ptr);
148 return ret;
149 }
150
AccessControlList()151 AccessControlList::AccessControlList() : acl_(EmptyAclToBuffer()) {}
152 AccessControlList::AccessControlList(AccessControlList&&) = default;
153 AccessControlList& AccessControlList::operator=(AccessControlList&&) = default;
154 AccessControlList::~AccessControlList() = default;
155
SetEntries(const std::vector<ExplicitAccessEntry> & entries)156 bool AccessControlList::SetEntries(
157 const std::vector<ExplicitAccessEntry>& entries) {
158 if (entries.empty())
159 return true;
160
161 base::HeapArray<uint8_t> acl = AddACEToAcl(get(), entries);
162 if (acl.empty()) {
163 return false;
164 }
165
166 acl_ = std::move(acl);
167 return true;
168 }
169
SetEntry(const Sid & sid,SecurityAccessMode mode,DWORD access_mask,DWORD inheritance)170 bool AccessControlList::SetEntry(const Sid& sid,
171 SecurityAccessMode mode,
172 DWORD access_mask,
173 DWORD inheritance) {
174 std::vector<ExplicitAccessEntry> ace_list;
175 ace_list.emplace_back(sid, mode, access_mask, inheritance);
176 return SetEntries(ace_list);
177 }
178
Clone() const179 AccessControlList AccessControlList::Clone() const {
180 return AccessControlList{get()};
181 }
182
Clear()183 void AccessControlList::Clear() {
184 acl_ = EmptyAclToBuffer();
185 }
186
AccessControlList(const ACL * acl)187 AccessControlList::AccessControlList(const ACL* acl) : acl_(AclToBuffer(acl)) {}
188
189 } // namespace base::win
190