• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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