• 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/security_descriptor.h"
6 
7 // clang-format off
8 #include <windows.h>  // Must be in front of other Windows header files.
9 // clang-format on
10 
11 #include <aclapi.h>
12 #include <sddl.h>
13 
14 #include <utility>
15 #include <vector>
16 
17 #include "base/files/file_path.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 template <typename T>
CloneValue(const std::optional<T> & value)27 std::optional<T> CloneValue(const std::optional<T>& value) {
28   if (!value)
29     return std::nullopt;
30   return value->Clone();
31 }
32 
UnwrapSid(const std::optional<Sid> & sid)33 PSID UnwrapSid(const std::optional<Sid>& sid) {
34   if (!sid)
35     return nullptr;
36   return sid->GetPSID();
37 }
38 
UnwrapAcl(std::optional<AccessControlList> & acl)39 PACL UnwrapAcl(std::optional<AccessControlList>& acl) {
40   if (!acl)
41     return nullptr;
42   return acl->get();
43 }
44 
ConvertObjectType(SecurityObjectType object_type)45 SE_OBJECT_TYPE ConvertObjectType(SecurityObjectType object_type) {
46   switch (object_type) {
47     case SecurityObjectType::kFile:
48       return SE_FILE_OBJECT;
49     case SecurityObjectType::kRegistry:
50       return SE_REGISTRY_KEY;
51     case SecurityObjectType::kWindowStation:
52     case SecurityObjectType::kDesktop:
53       return SE_WINDOW_OBJECT;
54     case SecurityObjectType::kKernel:
55       return SE_KERNEL_OBJECT;
56   }
57   return SE_UNKNOWN_OBJECT_TYPE;
58 }
59 
GetGenericMappingForType(SecurityObjectType object_type)60 GENERIC_MAPPING GetGenericMappingForType(SecurityObjectType object_type) {
61   GENERIC_MAPPING generic_mapping = {};
62   switch (object_type) {
63     case SecurityObjectType::kFile:
64       generic_mapping.GenericRead = FILE_GENERIC_READ;
65       generic_mapping.GenericWrite = FILE_GENERIC_WRITE;
66       generic_mapping.GenericExecute = FILE_GENERIC_EXECUTE;
67       generic_mapping.GenericAll = FILE_ALL_ACCESS;
68       break;
69     case SecurityObjectType::kRegistry:
70       generic_mapping.GenericRead = KEY_READ;
71       generic_mapping.GenericWrite = KEY_WRITE;
72       generic_mapping.GenericExecute = KEY_EXECUTE;
73       generic_mapping.GenericAll = KEY_ALL_ACCESS;
74       break;
75     case SecurityObjectType::kDesktop:
76       generic_mapping.GenericRead =
77           STANDARD_RIGHTS_READ | DESKTOP_READOBJECTS | DESKTOP_ENUMERATE;
78       generic_mapping.GenericWrite =
79           STANDARD_RIGHTS_WRITE | DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU |
80           DESKTOP_HOOKCONTROL | DESKTOP_JOURNALRECORD |
81           DESKTOP_JOURNALPLAYBACK | DESKTOP_WRITEOBJECTS;
82       generic_mapping.GenericExecute =
83           STANDARD_RIGHTS_EXECUTE | DESKTOP_SWITCHDESKTOP;
84       generic_mapping.GenericAll =
85           STANDARD_RIGHTS_REQUIRED | DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
86           DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL | DESKTOP_JOURNALPLAYBACK |
87           DESKTOP_JOURNALRECORD | DESKTOP_READOBJECTS | DESKTOP_SWITCHDESKTOP |
88           DESKTOP_WRITEOBJECTS;
89       break;
90     case SecurityObjectType::kWindowStation:
91       generic_mapping.GenericRead = STANDARD_RIGHTS_READ | WINSTA_ENUMDESKTOPS |
92                                     WINSTA_ENUMERATE | WINSTA_READATTRIBUTES |
93                                     WINSTA_READSCREEN;
94       generic_mapping.GenericWrite =
95           STANDARD_RIGHTS_WRITE | WINSTA_ACCESSCLIPBOARD |
96           WINSTA_CREATEDESKTOP | WINSTA_WRITEATTRIBUTES;
97       generic_mapping.GenericExecute = STANDARD_RIGHTS_EXECUTE |
98                                        WINSTA_ACCESSGLOBALATOMS |
99                                        WINSTA_EXITWINDOWS;
100       generic_mapping.GenericAll =
101           STANDARD_RIGHTS_REQUIRED | WINSTA_ACCESSCLIPBOARD |
102           WINSTA_ACCESSGLOBALATOMS | WINSTA_CREATEDESKTOP |
103           WINSTA_ENUMDESKTOPS | WINSTA_ENUMERATE | WINSTA_EXITWINDOWS |
104           WINSTA_READATTRIBUTES | WINSTA_READSCREEN | WINSTA_WRITEATTRIBUTES;
105       break;
106     case SecurityObjectType::kKernel:
107       NOTREACHED();
108   }
109   return generic_mapping;
110 }
111 
112 template <typename T>
GetSecurityDescriptor(T object,SecurityObjectType object_type,SECURITY_INFORMATION security_info,DWORD (WINAPI * get_sd)(T,SE_OBJECT_TYPE,SECURITY_INFORMATION,PSID *,PSID *,PACL *,PACL *,PSECURITY_DESCRIPTOR *))113 std::optional<SecurityDescriptor> GetSecurityDescriptor(
114     T object,
115     SecurityObjectType object_type,
116     SECURITY_INFORMATION security_info,
117     DWORD(WINAPI* get_sd)(T,
118                           SE_OBJECT_TYPE,
119                           SECURITY_INFORMATION,
120                           PSID*,
121                           PSID*,
122                           PACL*,
123                           PACL*,
124                           PSECURITY_DESCRIPTOR*)) {
125   PSECURITY_DESCRIPTOR sd = nullptr;
126 
127   DWORD error = get_sd(object, ConvertObjectType(object_type), security_info,
128                        nullptr, nullptr, nullptr, nullptr, &sd);
129   if (error != ERROR_SUCCESS) {
130     ::SetLastError(error);
131     DPLOG(ERROR) << "Failed getting security descriptor for object.";
132     return std::nullopt;
133   }
134   auto sd_ptr = TakeLocalAlloc(sd);
135   return SecurityDescriptor::FromPointer(sd_ptr.get());
136 }
137 
138 template <typename T>
SetSecurityDescriptor(const SecurityDescriptor & sd,T object,SecurityObjectType object_type,SECURITY_INFORMATION security_info,DWORD (WINAPI * set_sd)(T,SE_OBJECT_TYPE,SECURITY_INFORMATION,PSID,PSID,PACL,PACL))139 bool SetSecurityDescriptor(const SecurityDescriptor& sd,
140                            T object,
141                            SecurityObjectType object_type,
142                            SECURITY_INFORMATION security_info,
143                            DWORD(WINAPI* set_sd)(T,
144                                                  SE_OBJECT_TYPE,
145                                                  SECURITY_INFORMATION,
146                                                  PSID,
147                                                  PSID,
148                                                  PACL,
149                                                  PACL)) {
150   auto security_descriptor = sd.Clone();
151 
152   security_info &= ~(PROTECTED_DACL_SECURITY_INFORMATION |
153                      UNPROTECTED_DACL_SECURITY_INFORMATION |
154                      PROTECTED_SACL_SECURITY_INFORMATION |
155                      UNPROTECTED_SACL_SECURITY_INFORMATION);
156   if (security_info & DACL_SECURITY_INFORMATION) {
157     if (security_descriptor.dacl_protected()) {
158       security_info |= PROTECTED_DACL_SECURITY_INFORMATION;
159     } else {
160       security_info |= UNPROTECTED_DACL_SECURITY_INFORMATION;
161     }
162   }
163   if (security_info & SACL_SECURITY_INFORMATION) {
164     if (security_descriptor.sacl_protected()) {
165       security_info |= PROTECTED_SACL_SECURITY_INFORMATION;
166     } else {
167       security_info |= UNPROTECTED_SACL_SECURITY_INFORMATION;
168     }
169   }
170   DWORD error = set_sd(object, ConvertObjectType(object_type), security_info,
171                        UnwrapSid(security_descriptor.owner()),
172                        UnwrapSid(security_descriptor.group()),
173                        UnwrapAcl(security_descriptor.dacl()),
174                        UnwrapAcl(security_descriptor.sacl()));
175   if (error != ERROR_SUCCESS) {
176     ::SetLastError(error);
177     DPLOG(ERROR) << "Failed setting DACL for object.";
178     return false;
179   }
180   return true;
181 }
182 
GetSecurityDescriptorSid(PSECURITY_DESCRIPTOR sd,BOOL (WINAPI * get_sid)(PSECURITY_DESCRIPTOR,PSID *,LPBOOL))183 std::optional<Sid> GetSecurityDescriptorSid(
184     PSECURITY_DESCRIPTOR sd,
185     BOOL(WINAPI* get_sid)(PSECURITY_DESCRIPTOR, PSID*, LPBOOL)) {
186   PSID sid;
187   BOOL defaulted;
188   if (!get_sid(sd, &sid, &defaulted) || !sid) {
189     return std::nullopt;
190   }
191   return Sid::FromPSID(sid);
192 }
193 
GetSecurityDescriptorAcl(PSECURITY_DESCRIPTOR sd,BOOL (WINAPI * get_acl)(PSECURITY_DESCRIPTOR,LPBOOL,PACL *,LPBOOL))194 std::optional<AccessControlList> GetSecurityDescriptorAcl(
195     PSECURITY_DESCRIPTOR sd,
196     BOOL(WINAPI* get_acl)(PSECURITY_DESCRIPTOR, LPBOOL, PACL*, LPBOOL)) {
197   PACL acl;
198   BOOL present;
199   BOOL defaulted;
200   if (!get_acl(sd, &present, &acl, &defaulted) || !present) {
201     return std::nullopt;
202   }
203   return AccessControlList::FromPACL(acl);
204 }
205 
206 }  // namespace
207 
208 SecurityDescriptor::SelfRelative::SelfRelative(const SelfRelative&) = default;
209 SecurityDescriptor::SelfRelative::~SelfRelative() = default;
SelfRelative(std::vector<uint8_t> && sd)210 SecurityDescriptor::SelfRelative::SelfRelative(std::vector<uint8_t>&& sd)
211     : sd_(sd) {}
212 
FromPointer(PSECURITY_DESCRIPTOR sd)213 std::optional<SecurityDescriptor> SecurityDescriptor::FromPointer(
214     PSECURITY_DESCRIPTOR sd) {
215   if (!sd || !::IsValidSecurityDescriptor(sd)) {
216     ::SetLastError(ERROR_INVALID_SECURITY_DESCR);
217     return std::nullopt;
218   }
219 
220   SECURITY_DESCRIPTOR_CONTROL control;
221   DWORD revision;
222   if (!::GetSecurityDescriptorControl(sd, &control, &revision)) {
223     return std::nullopt;
224   }
225 
226   return SecurityDescriptor{
227       GetSecurityDescriptorSid(sd, ::GetSecurityDescriptorOwner),
228       GetSecurityDescriptorSid(sd, ::GetSecurityDescriptorGroup),
229       GetSecurityDescriptorAcl(sd, ::GetSecurityDescriptorDacl),
230       !!(control & SE_DACL_PROTECTED),
231       GetSecurityDescriptorAcl(sd, ::GetSecurityDescriptorSacl),
232       !!(control & SE_SACL_PROTECTED)};
233 }
234 
FromFile(const base::FilePath & path,SECURITY_INFORMATION security_info)235 std::optional<SecurityDescriptor> SecurityDescriptor::FromFile(
236     const base::FilePath& path,
237     SECURITY_INFORMATION security_info) {
238   return FromName(path.value(), SecurityObjectType::kFile, security_info);
239 }
240 
FromName(const std::wstring & name,SecurityObjectType object_type,SECURITY_INFORMATION security_info)241 std::optional<SecurityDescriptor> SecurityDescriptor::FromName(
242     const std::wstring& name,
243     SecurityObjectType object_type,
244     SECURITY_INFORMATION security_info) {
245   return GetSecurityDescriptor(name.c_str(), object_type, security_info,
246                                ::GetNamedSecurityInfo);
247 }
248 
FromHandle(HANDLE handle,SecurityObjectType object_type,SECURITY_INFORMATION security_info)249 std::optional<SecurityDescriptor> SecurityDescriptor::FromHandle(
250     HANDLE handle,
251     SecurityObjectType object_type,
252     SECURITY_INFORMATION security_info) {
253   return GetSecurityDescriptor<HANDLE>(handle, object_type, security_info,
254                                        ::GetSecurityInfo);
255 }
256 
FromSddl(const std::wstring & sddl)257 std::optional<SecurityDescriptor> SecurityDescriptor::FromSddl(
258     const std::wstring& sddl) {
259   PSECURITY_DESCRIPTOR sd;
260   if (!::ConvertStringSecurityDescriptorToSecurityDescriptor(
261           sddl.c_str(), SDDL_REVISION_1, &sd, nullptr)) {
262     return std::nullopt;
263   }
264   auto sd_ptr = TakeLocalAlloc(sd);
265   return FromPointer(sd_ptr.get());
266 }
267 
268 SecurityDescriptor::SecurityDescriptor() = default;
269 SecurityDescriptor::SecurityDescriptor(SecurityDescriptor&&) = default;
270 SecurityDescriptor& SecurityDescriptor::operator=(SecurityDescriptor&&) =
271     default;
272 SecurityDescriptor::~SecurityDescriptor() = default;
273 
WriteToFile(const base::FilePath & path,SECURITY_INFORMATION security_info) const274 bool SecurityDescriptor::WriteToFile(const base::FilePath& path,
275                                      SECURITY_INFORMATION security_info) const {
276   return WriteToName(path.value(), SecurityObjectType::kFile, security_info);
277 }
278 
WriteToName(const std::wstring & name,SecurityObjectType object_type,SECURITY_INFORMATION security_info) const279 bool SecurityDescriptor::WriteToName(const std::wstring& name,
280                                      SecurityObjectType object_type,
281                                      SECURITY_INFORMATION security_info) const {
282   return SetSecurityDescriptor<wchar_t*>(
283       *this, const_cast<wchar_t*>(name.c_str()), object_type, security_info,
284       ::SetNamedSecurityInfo);
285 }
286 
WriteToHandle(HANDLE handle,SecurityObjectType object_type,SECURITY_INFORMATION security_info) const287 bool SecurityDescriptor::WriteToHandle(
288     HANDLE handle,
289     SecurityObjectType object_type,
290     SECURITY_INFORMATION security_info) const {
291   return SetSecurityDescriptor<HANDLE>(*this, handle, object_type,
292                                        security_info, ::SetSecurityInfo);
293 }
294 
ToSddl(SECURITY_INFORMATION security_info) const295 std::optional<std::wstring> SecurityDescriptor::ToSddl(
296     SECURITY_INFORMATION security_info) const {
297   // `ToAbsolute()` is not const-qualified as it returns non-const pointers by
298   // populating the `SECURITY_DESCRIPTOR` with them. Since we're in a const-
299   // qualified member method, we need to clone ourselves and call `ToAbsolute()`
300   // on the clone.
301   auto self = Clone();
302   SECURITY_DESCRIPTOR sd = {};
303   self.ToAbsolute(sd);
304 
305   LPWSTR sddl;
306   if (!::ConvertSecurityDescriptorToStringSecurityDescriptor(
307           &sd, SDDL_REVISION_1, security_info, &sddl, nullptr)) {
308     return std::nullopt;
309   }
310   auto sddl_ptr = TakeLocalAlloc(sddl);
311   return sddl_ptr.get();
312 }
313 
ToAbsolute(SECURITY_DESCRIPTOR & sd)314 void SecurityDescriptor::ToAbsolute(SECURITY_DESCRIPTOR& sd) {
315   memset(&sd, 0, sizeof(sd));
316   sd.Revision = SECURITY_DESCRIPTOR_REVISION;
317   sd.Owner = owner_ ? owner_->GetPSID() : nullptr;
318   sd.Group = group_ ? group_->GetPSID() : nullptr;
319   if (dacl_) {
320     sd.Dacl = dacl_->get();
321     sd.Control |= SE_DACL_PRESENT;
322     if (dacl_protected_) {
323       sd.Control |= SE_DACL_PROTECTED;
324     }
325   }
326   if (sacl_) {
327     sd.Sacl = sacl_->get();
328     sd.Control |= SE_SACL_PRESENT;
329     if (sacl_protected_) {
330       sd.Control |= SE_SACL_PROTECTED;
331     }
332   }
333   DCHECK(::IsValidSecurityDescriptor(&sd));
334 }
335 
336 std::optional<SecurityDescriptor::SelfRelative>
ToSelfRelative() const337 SecurityDescriptor::ToSelfRelative() const {
338   // `ToAbsolute()` is not const-qualified as it returns non-const pointers by
339   // populating the `SECURITY_DESCRIPTOR` with them. Since we're in a const-
340   // qualified member method, we need to clone ourselves and call `ToAbsolute()`
341   // on the clone.
342   auto self = Clone();
343   SECURITY_DESCRIPTOR sd = {};
344   self.ToAbsolute(sd);
345 
346   DWORD size = sizeof(SECURITY_DESCRIPTOR_MIN_LENGTH);
347   std::vector<uint8_t> buffer(SECURITY_DESCRIPTOR_MIN_LENGTH);
348   if (::MakeSelfRelativeSD(&sd, buffer.data(), &size)) {
349     return SelfRelative(std::move(buffer));
350   }
351 
352   if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
353     return std::nullopt;
354   }
355 
356   buffer.resize(size);
357   if (!::MakeSelfRelativeSD(&sd, buffer.data(), &size)) {
358     return std::nullopt;
359   }
360   return SelfRelative(std::move(buffer));
361 }
362 
Clone() const363 SecurityDescriptor SecurityDescriptor::Clone() const {
364   return SecurityDescriptor{CloneValue(owner_), CloneValue(group_),
365                             CloneValue(dacl_),  dacl_protected_,
366                             CloneValue(sacl_),  sacl_protected_};
367 }
368 
SetMandatoryLabel(DWORD integrity_level,DWORD inheritance,DWORD mandatory_policy)369 bool SecurityDescriptor::SetMandatoryLabel(DWORD integrity_level,
370                                            DWORD inheritance,
371                                            DWORD mandatory_policy) {
372   std::optional<AccessControlList> sacl = AccessControlList::FromMandatoryLabel(
373       integrity_level, inheritance, mandatory_policy);
374   if (!sacl) {
375     return false;
376   }
377   sacl_ = std::move(*sacl);
378   return true;
379 }
380 
SetDaclEntries(const std::vector<ExplicitAccessEntry> & entries)381 bool SecurityDescriptor::SetDaclEntries(
382     const std::vector<ExplicitAccessEntry>& entries) {
383   if (!dacl_) {
384     dacl_ = AccessControlList{};
385   }
386   return dacl_->SetEntries(entries);
387 }
388 
SetDaclEntry(const Sid & sid,SecurityAccessMode mode,DWORD access_mask,DWORD inheritance)389 bool SecurityDescriptor::SetDaclEntry(const Sid& sid,
390                                       SecurityAccessMode mode,
391                                       DWORD access_mask,
392                                       DWORD inheritance) {
393   if (!dacl_) {
394     dacl_ = AccessControlList{};
395   }
396   return dacl_->SetEntry(sid, mode, access_mask, inheritance);
397 }
398 
SetDaclEntry(WellKnownSid known_sid,SecurityAccessMode mode,DWORD access_mask,DWORD inheritance)399 bool SecurityDescriptor::SetDaclEntry(WellKnownSid known_sid,
400                                       SecurityAccessMode mode,
401                                       DWORD access_mask,
402                                       DWORD inheritance) {
403   return SetDaclEntry(Sid(known_sid), mode, access_mask, inheritance);
404 }
405 
AccessCheck(const AccessToken & token,ACCESS_MASK desired_access,const GENERIC_MAPPING & generic_mapping)406 std::optional<AccessCheckResult> SecurityDescriptor::AccessCheck(
407     const AccessToken& token,
408     ACCESS_MASK desired_access,
409     const GENERIC_MAPPING& generic_mapping) {
410   GENERIC_MAPPING local_mapping = generic_mapping;
411   ::MapGenericMask(&desired_access, &local_mapping);
412 
413   // Allocate a privilege set which could cover all possible privileges.
414   DWORD priv_set_length = checked_cast<DWORD>(
415       sizeof(PRIVILEGE_SET) +
416       (token.Privileges().size() * sizeof(LUID_AND_ATTRIBUTES)));
417   std::vector<char> priv_set(priv_set_length);
418   DWORD granted_access = 0;
419   BOOL access_status = FALSE;
420   SECURITY_DESCRIPTOR sd = {};
421   ToAbsolute(sd);
422   if (!::AccessCheck(&sd, token.get(), desired_access, &local_mapping,
423                      reinterpret_cast<PPRIVILEGE_SET>(priv_set.data()),
424                      &priv_set_length, &granted_access, &access_status)) {
425     return std::nullopt;
426   }
427   return AccessCheckResult{granted_access, !!access_status};
428 }
429 
AccessCheck(const AccessToken & token,ACCESS_MASK desired_access,SecurityObjectType object_type)430 std::optional<AccessCheckResult> SecurityDescriptor::AccessCheck(
431     const AccessToken& token,
432     ACCESS_MASK desired_access,
433     SecurityObjectType object_type) {
434   if (object_type == SecurityObjectType::kKernel) {
435     ::SetLastError(ERROR_INVALID_PARAMETER);
436     return std::nullopt;
437   }
438   return AccessCheck(token, desired_access,
439                      GetGenericMappingForType(object_type));
440 }
441 
SecurityDescriptor(std::optional<Sid> && owner,std::optional<Sid> && group,std::optional<AccessControlList> && dacl,bool dacl_protected,std::optional<AccessControlList> && sacl,bool sacl_protected)442 SecurityDescriptor::SecurityDescriptor(std::optional<Sid>&& owner,
443                                        std::optional<Sid>&& group,
444                                        std::optional<AccessControlList>&& dacl,
445                                        bool dacl_protected,
446                                        std::optional<AccessControlList>&& sacl,
447                                        bool sacl_protected) {
448   owner_.swap(owner);
449   group_.swap(group);
450   dacl_.swap(dacl);
451   dacl_protected_ = dacl_protected;
452   sacl_.swap(sacl);
453   sacl_protected_ = sacl_protected;
454 }
455 
456 }  // namespace base::win
457