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