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 <aclapi.h>
8 #include <windows.h>
9
10 #include <utility>
11 #include <vector>
12
13 #include "base/check.h"
14 #include "base/logging.h"
15 #include "base/notreached.h"
16 #include "base/numerics/checked_math.h"
17 #include "base/win/scoped_localalloc.h"
18
19 namespace base::win {
20
21 namespace {
22
AclToBuffer(const ACL * acl)23 std::unique_ptr<uint8_t[]> AclToBuffer(const ACL* acl) {
24 if (!acl) {
25 return nullptr;
26 }
27 size_t size = acl->AclSize;
28 DCHECK(size >= sizeof(*acl));
29 std::unique_ptr<uint8_t[]> ptr = std::make_unique<uint8_t[]>(size);
30 memcpy(ptr.get(), acl, size);
31 return ptr;
32 }
33
EmptyAclToBuffer()34 std::unique_ptr<uint8_t[]> EmptyAclToBuffer() {
35 ACL acl = {};
36 acl.AclRevision = ACL_REVISION;
37 acl.AclSize = static_cast<WORD>(sizeof(acl));
38 return AclToBuffer(&acl);
39 }
40
ConvertAccessMode(SecurityAccessMode access_mode)41 ACCESS_MODE ConvertAccessMode(SecurityAccessMode access_mode) {
42 switch (access_mode) {
43 case SecurityAccessMode::kGrant:
44 return GRANT_ACCESS;
45 case SecurityAccessMode::kSet:
46 return SET_ACCESS;
47 case SecurityAccessMode::kDeny:
48 return DENY_ACCESS;
49 case SecurityAccessMode::kRevoke:
50 return REVOKE_ACCESS;
51 }
52 }
53
AddACEToAcl(ACL * old_acl,const std::vector<ExplicitAccessEntry> & entries)54 std::unique_ptr<uint8_t[]> AddACEToAcl(
55 ACL* old_acl,
56 const std::vector<ExplicitAccessEntry>& entries) {
57 std::vector<EXPLICIT_ACCESS> access_entries(entries.size());
58 auto entries_interator = access_entries.begin();
59 for (const ExplicitAccessEntry& entry : entries) {
60 EXPLICIT_ACCESS& new_access = *entries_interator++;
61 new_access.grfAccessMode = ConvertAccessMode(entry.mode());
62 new_access.grfAccessPermissions = entry.access_mask();
63 new_access.grfInheritance = entry.inheritance();
64 ::BuildTrusteeWithSid(&new_access.Trustee, entry.sid().GetPSID());
65 }
66
67 PACL new_acl = nullptr;
68 DWORD error = ::SetEntriesInAcl(checked_cast<ULONG>(access_entries.size()),
69 access_entries.data(), old_acl, &new_acl);
70 if (error != ERROR_SUCCESS) {
71 ::SetLastError(error);
72 DPLOG(ERROR) << "Failed adding ACEs to ACL";
73 return nullptr;
74 }
75 auto new_acl_ptr = TakeLocalAlloc(new_acl);
76 return AclToBuffer(new_acl_ptr.get());
77 }
78
79 } // namespace
80
Clone() const81 ExplicitAccessEntry ExplicitAccessEntry::Clone() const {
82 return ExplicitAccessEntry{sid_, mode_, access_mask_, inheritance_};
83 }
84
ExplicitAccessEntry(const Sid & sid,SecurityAccessMode mode,DWORD access_mask,DWORD inheritance)85 ExplicitAccessEntry::ExplicitAccessEntry(const Sid& sid,
86 SecurityAccessMode mode,
87 DWORD access_mask,
88 DWORD inheritance)
89 : sid_(sid.Clone()),
90 mode_(mode),
91 access_mask_(access_mask),
92 inheritance_(inheritance) {}
93
ExplicitAccessEntry(WellKnownSid known_sid,SecurityAccessMode mode,DWORD access_mask,DWORD inheritance)94 ExplicitAccessEntry::ExplicitAccessEntry(WellKnownSid known_sid,
95 SecurityAccessMode mode,
96 DWORD access_mask,
97 DWORD inheritance)
98 : ExplicitAccessEntry(Sid(known_sid), mode, access_mask, inheritance) {}
99
100 ExplicitAccessEntry::ExplicitAccessEntry(ExplicitAccessEntry&&) = default;
101 ExplicitAccessEntry& ExplicitAccessEntry::operator=(ExplicitAccessEntry&&) =
102 default;
103 ExplicitAccessEntry::~ExplicitAccessEntry() = default;
104
FromPACL(ACL * acl)105 absl::optional<AccessControlList> AccessControlList::FromPACL(ACL* acl) {
106 if (acl && !::IsValidAcl(acl)) {
107 ::SetLastError(ERROR_INVALID_ACL);
108 return absl::nullopt;
109 }
110 return AccessControlList{acl};
111 }
112
FromMandatoryLabel(DWORD integrity_level,DWORD inheritance,DWORD mandatory_policy)113 absl::optional<AccessControlList> AccessControlList::FromMandatoryLabel(
114 DWORD integrity_level,
115 DWORD inheritance,
116 DWORD mandatory_policy) {
117 Sid sid = Sid::FromIntegrityLevel(integrity_level);
118 // Get total ACL length. SYSTEM_MANDATORY_LABEL_ACE contains the first DWORD
119 // of the SID so remove it from total.
120 DWORD length = sizeof(ACL) + sizeof(SYSTEM_MANDATORY_LABEL_ACE) +
121 ::GetLengthSid(sid.GetPSID()) - sizeof(DWORD);
122 std::unique_ptr<uint8_t[]> sacl_ptr = std::make_unique<uint8_t[]>(length);
123 PACL sacl = reinterpret_cast<PACL>(sacl_ptr.get());
124
125 if (!::InitializeAcl(sacl, length, ACL_REVISION)) {
126 return absl::nullopt;
127 }
128
129 if (!::AddMandatoryAce(sacl, ACL_REVISION, inheritance, mandatory_policy,
130 sid.GetPSID())) {
131 return absl::nullopt;
132 }
133
134 DCHECK(::IsValidAcl(sacl));
135 AccessControlList ret;
136 ret.acl_ = std::move(sacl_ptr);
137 return ret;
138 }
139
AccessControlList()140 AccessControlList::AccessControlList() : acl_(EmptyAclToBuffer()) {}
141 AccessControlList::AccessControlList(AccessControlList&&) = default;
142 AccessControlList& AccessControlList::operator=(AccessControlList&&) = default;
143 AccessControlList::~AccessControlList() = default;
144
SetEntries(const std::vector<ExplicitAccessEntry> & entries)145 bool AccessControlList::SetEntries(
146 const std::vector<ExplicitAccessEntry>& entries) {
147 if (entries.empty())
148 return true;
149
150 std::unique_ptr<uint8_t[]> acl = AddACEToAcl(get(), entries);
151 if (!acl)
152 return false;
153
154 acl_ = std::move(acl);
155 return true;
156 }
157
SetEntry(const Sid & sid,SecurityAccessMode mode,DWORD access_mask,DWORD inheritance)158 bool AccessControlList::SetEntry(const Sid& sid,
159 SecurityAccessMode mode,
160 DWORD access_mask,
161 DWORD inheritance) {
162 std::vector<ExplicitAccessEntry> ace_list;
163 ace_list.emplace_back(sid, mode, access_mask, inheritance);
164 return SetEntries(ace_list);
165 }
166
Clone() const167 AccessControlList AccessControlList::Clone() const {
168 return AccessControlList{get()};
169 }
170
Clear()171 void AccessControlList::Clear() {
172 acl_ = EmptyAclToBuffer();
173 }
174
AccessControlList(const ACL * acl)175 AccessControlList::AccessControlList(const ACL* acl) : acl_(AclToBuffer(acl)) {}
176
177 } // namespace base::win
178