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(const std::optional<AccessControlList> & acl)39 PACL UnwrapAcl(const 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 break;
109 }
110 return generic_mapping;
111 }
112
113 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 *))114 std::optional<SecurityDescriptor> GetSecurityDescriptor(
115 T object,
116 SecurityObjectType object_type,
117 SECURITY_INFORMATION security_info,
118 DWORD(WINAPI* get_sd)(T,
119 SE_OBJECT_TYPE,
120 SECURITY_INFORMATION,
121 PSID*,
122 PSID*,
123 PACL*,
124 PACL*,
125 PSECURITY_DESCRIPTOR*)) {
126 PSECURITY_DESCRIPTOR sd = nullptr;
127
128 DWORD error = get_sd(object, ConvertObjectType(object_type), security_info,
129 nullptr, nullptr, nullptr, nullptr, &sd);
130 if (error != ERROR_SUCCESS) {
131 ::SetLastError(error);
132 DPLOG(ERROR) << "Failed getting security descriptor for object.";
133 return std::nullopt;
134 }
135 auto sd_ptr = TakeLocalAlloc(sd);
136 return SecurityDescriptor::FromPointer(sd_ptr.get());
137 }
138
139 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))140 bool SetSecurityDescriptor(const SecurityDescriptor& sd,
141 T object,
142 SecurityObjectType object_type,
143 SECURITY_INFORMATION security_info,
144 DWORD(WINAPI* set_sd)(T,
145 SE_OBJECT_TYPE,
146 SECURITY_INFORMATION,
147 PSID,
148 PSID,
149 PACL,
150 PACL)) {
151 security_info &= ~(PROTECTED_DACL_SECURITY_INFORMATION |
152 UNPROTECTED_DACL_SECURITY_INFORMATION |
153 PROTECTED_SACL_SECURITY_INFORMATION |
154 UNPROTECTED_SACL_SECURITY_INFORMATION);
155 if (security_info & DACL_SECURITY_INFORMATION) {
156 if (sd.dacl_protected()) {
157 security_info |= PROTECTED_DACL_SECURITY_INFORMATION;
158 } else {
159 security_info |= UNPROTECTED_DACL_SECURITY_INFORMATION;
160 }
161 }
162 if (security_info & SACL_SECURITY_INFORMATION) {
163 if (sd.sacl_protected()) {
164 security_info |= PROTECTED_SACL_SECURITY_INFORMATION;
165 } else {
166 security_info |= UNPROTECTED_SACL_SECURITY_INFORMATION;
167 }
168 }
169 DWORD error = set_sd(object, ConvertObjectType(object_type), security_info,
170 UnwrapSid(sd.owner()), UnwrapSid(sd.group()),
171 UnwrapAcl(sd.dacl()), UnwrapAcl(sd.sacl()));
172 if (error != ERROR_SUCCESS) {
173 ::SetLastError(error);
174 DPLOG(ERROR) << "Failed setting DACL for object.";
175 return false;
176 }
177 return true;
178 }
179
GetSecurityDescriptorSid(PSECURITY_DESCRIPTOR sd,BOOL (WINAPI * get_sid)(PSECURITY_DESCRIPTOR,PSID *,LPBOOL))180 std::optional<Sid> GetSecurityDescriptorSid(
181 PSECURITY_DESCRIPTOR sd,
182 BOOL(WINAPI* get_sid)(PSECURITY_DESCRIPTOR, PSID*, LPBOOL)) {
183 PSID sid;
184 BOOL defaulted;
185 if (!get_sid(sd, &sid, &defaulted) || !sid) {
186 return std::nullopt;
187 }
188 return Sid::FromPSID(sid);
189 }
190
GetSecurityDescriptorAcl(PSECURITY_DESCRIPTOR sd,BOOL (WINAPI * get_acl)(PSECURITY_DESCRIPTOR,LPBOOL,PACL *,LPBOOL))191 std::optional<AccessControlList> GetSecurityDescriptorAcl(
192 PSECURITY_DESCRIPTOR sd,
193 BOOL(WINAPI* get_acl)(PSECURITY_DESCRIPTOR, LPBOOL, PACL*, LPBOOL)) {
194 PACL acl;
195 BOOL present;
196 BOOL defaulted;
197 if (!get_acl(sd, &present, &acl, &defaulted) || !present) {
198 return std::nullopt;
199 }
200 return AccessControlList::FromPACL(acl);
201 }
202
203 } // namespace
204
205 SecurityDescriptor::SelfRelative::SelfRelative(const SelfRelative&) = default;
206 SecurityDescriptor::SelfRelative::~SelfRelative() = default;
SelfRelative(std::vector<uint8_t> && sd)207 SecurityDescriptor::SelfRelative::SelfRelative(std::vector<uint8_t>&& sd)
208 : sd_(sd) {}
209
FromPointer(PSECURITY_DESCRIPTOR sd)210 std::optional<SecurityDescriptor> SecurityDescriptor::FromPointer(
211 PSECURITY_DESCRIPTOR sd) {
212 if (!sd || !::IsValidSecurityDescriptor(sd)) {
213 ::SetLastError(ERROR_INVALID_SECURITY_DESCR);
214 return std::nullopt;
215 }
216
217 SECURITY_DESCRIPTOR_CONTROL control;
218 DWORD revision;
219 if (!::GetSecurityDescriptorControl(sd, &control, &revision)) {
220 return std::nullopt;
221 }
222
223 return SecurityDescriptor{
224 GetSecurityDescriptorSid(sd, ::GetSecurityDescriptorOwner),
225 GetSecurityDescriptorSid(sd, ::GetSecurityDescriptorGroup),
226 GetSecurityDescriptorAcl(sd, ::GetSecurityDescriptorDacl),
227 !!(control & SE_DACL_PROTECTED),
228 GetSecurityDescriptorAcl(sd, ::GetSecurityDescriptorSacl),
229 !!(control & SE_SACL_PROTECTED)};
230 }
231
FromFile(const base::FilePath & path,SECURITY_INFORMATION security_info)232 std::optional<SecurityDescriptor> SecurityDescriptor::FromFile(
233 const base::FilePath& path,
234 SECURITY_INFORMATION security_info) {
235 return FromName(path.value(), SecurityObjectType::kFile, security_info);
236 }
237
FromName(const std::wstring & name,SecurityObjectType object_type,SECURITY_INFORMATION security_info)238 std::optional<SecurityDescriptor> SecurityDescriptor::FromName(
239 const std::wstring& name,
240 SecurityObjectType object_type,
241 SECURITY_INFORMATION security_info) {
242 return GetSecurityDescriptor(name.c_str(), object_type, security_info,
243 ::GetNamedSecurityInfo);
244 }
245
FromHandle(HANDLE handle,SecurityObjectType object_type,SECURITY_INFORMATION security_info)246 std::optional<SecurityDescriptor> SecurityDescriptor::FromHandle(
247 HANDLE handle,
248 SecurityObjectType object_type,
249 SECURITY_INFORMATION security_info) {
250 return GetSecurityDescriptor<HANDLE>(handle, object_type, security_info,
251 ::GetSecurityInfo);
252 }
253
FromSddl(const std::wstring & sddl)254 std::optional<SecurityDescriptor> SecurityDescriptor::FromSddl(
255 const std::wstring& sddl) {
256 PSECURITY_DESCRIPTOR sd;
257 if (!::ConvertStringSecurityDescriptorToSecurityDescriptor(
258 sddl.c_str(), SDDL_REVISION_1, &sd, nullptr)) {
259 return std::nullopt;
260 }
261 auto sd_ptr = TakeLocalAlloc(sd);
262 return FromPointer(sd_ptr.get());
263 }
264
265 SecurityDescriptor::SecurityDescriptor() = default;
266 SecurityDescriptor::SecurityDescriptor(SecurityDescriptor&&) = default;
267 SecurityDescriptor& SecurityDescriptor::operator=(SecurityDescriptor&&) =
268 default;
269 SecurityDescriptor::~SecurityDescriptor() = default;
270
WriteToFile(const base::FilePath & path,SECURITY_INFORMATION security_info) const271 bool SecurityDescriptor::WriteToFile(const base::FilePath& path,
272 SECURITY_INFORMATION security_info) const {
273 return WriteToName(path.value(), SecurityObjectType::kFile, security_info);
274 }
275
WriteToName(const std::wstring & name,SecurityObjectType object_type,SECURITY_INFORMATION security_info) const276 bool SecurityDescriptor::WriteToName(const std::wstring& name,
277 SecurityObjectType object_type,
278 SECURITY_INFORMATION security_info) const {
279 return SetSecurityDescriptor<wchar_t*>(
280 *this, const_cast<wchar_t*>(name.c_str()), object_type, security_info,
281 ::SetNamedSecurityInfo);
282 }
283
WriteToHandle(HANDLE handle,SecurityObjectType object_type,SECURITY_INFORMATION security_info) const284 bool SecurityDescriptor::WriteToHandle(
285 HANDLE handle,
286 SecurityObjectType object_type,
287 SECURITY_INFORMATION security_info) const {
288 return SetSecurityDescriptor<HANDLE>(*this, handle, object_type,
289 security_info, ::SetSecurityInfo);
290 }
291
ToSddl(SECURITY_INFORMATION security_info) const292 std::optional<std::wstring> SecurityDescriptor::ToSddl(
293 SECURITY_INFORMATION security_info) const {
294 SECURITY_DESCRIPTOR sd = {};
295 ToAbsolute(sd);
296 LPWSTR sddl;
297 if (!::ConvertSecurityDescriptorToStringSecurityDescriptor(
298 &sd, SDDL_REVISION_1, security_info, &sddl, nullptr)) {
299 return std::nullopt;
300 }
301 auto sddl_ptr = TakeLocalAlloc(sddl);
302 return sddl_ptr.get();
303 }
304
ToAbsolute(SECURITY_DESCRIPTOR & sd) const305 void SecurityDescriptor::ToAbsolute(SECURITY_DESCRIPTOR& sd) const {
306 memset(&sd, 0, sizeof(sd));
307 sd.Revision = SECURITY_DESCRIPTOR_REVISION;
308 sd.Owner = owner_ ? owner_->GetPSID() : nullptr;
309 sd.Group = group_ ? group_->GetPSID() : nullptr;
310 if (dacl_) {
311 sd.Dacl = dacl_->get();
312 sd.Control |= SE_DACL_PRESENT;
313 if (dacl_protected_) {
314 sd.Control |= SE_DACL_PROTECTED;
315 }
316 }
317 if (sacl_) {
318 sd.Sacl = sacl_->get();
319 sd.Control |= SE_SACL_PRESENT;
320 if (sacl_protected_) {
321 sd.Control |= SE_SACL_PROTECTED;
322 }
323 }
324 DCHECK(::IsValidSecurityDescriptor(&sd));
325 }
326
327 std::optional<SecurityDescriptor::SelfRelative>
ToSelfRelative() const328 SecurityDescriptor::ToSelfRelative() const {
329 SECURITY_DESCRIPTOR sd = {};
330 ToAbsolute(sd);
331 DWORD size = sizeof(SECURITY_DESCRIPTOR_MIN_LENGTH);
332 std::vector<uint8_t> buffer(SECURITY_DESCRIPTOR_MIN_LENGTH);
333 if (::MakeSelfRelativeSD(&sd, buffer.data(), &size)) {
334 return SelfRelative(std::move(buffer));
335 }
336
337 if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
338 return std::nullopt;
339 }
340
341 buffer.resize(size);
342 if (!::MakeSelfRelativeSD(&sd, buffer.data(), &size)) {
343 return std::nullopt;
344 }
345 return SelfRelative(std::move(buffer));
346 }
347
Clone() const348 SecurityDescriptor SecurityDescriptor::Clone() const {
349 return SecurityDescriptor{CloneValue(owner_), CloneValue(group_),
350 CloneValue(dacl_), dacl_protected_,
351 CloneValue(sacl_), sacl_protected_};
352 }
353
SetMandatoryLabel(DWORD integrity_level,DWORD inheritance,DWORD mandatory_policy)354 bool SecurityDescriptor::SetMandatoryLabel(DWORD integrity_level,
355 DWORD inheritance,
356 DWORD mandatory_policy) {
357 std::optional<AccessControlList> sacl = AccessControlList::FromMandatoryLabel(
358 integrity_level, inheritance, mandatory_policy);
359 if (!sacl) {
360 return false;
361 }
362 sacl_ = std::move(*sacl);
363 return true;
364 }
365
SetDaclEntries(const std::vector<ExplicitAccessEntry> & entries)366 bool SecurityDescriptor::SetDaclEntries(
367 const std::vector<ExplicitAccessEntry>& entries) {
368 if (!dacl_) {
369 dacl_ = AccessControlList{};
370 }
371 return dacl_->SetEntries(entries);
372 }
373
SetDaclEntry(const Sid & sid,SecurityAccessMode mode,DWORD access_mask,DWORD inheritance)374 bool SecurityDescriptor::SetDaclEntry(const Sid& sid,
375 SecurityAccessMode mode,
376 DWORD access_mask,
377 DWORD inheritance) {
378 if (!dacl_) {
379 dacl_ = AccessControlList{};
380 }
381 return dacl_->SetEntry(sid, mode, access_mask, inheritance);
382 }
383
SetDaclEntry(WellKnownSid known_sid,SecurityAccessMode mode,DWORD access_mask,DWORD inheritance)384 bool SecurityDescriptor::SetDaclEntry(WellKnownSid known_sid,
385 SecurityAccessMode mode,
386 DWORD access_mask,
387 DWORD inheritance) {
388 return SetDaclEntry(Sid(known_sid), mode, access_mask, inheritance);
389 }
390
AccessCheck(const AccessToken & token,ACCESS_MASK desired_access,const GENERIC_MAPPING & generic_mapping)391 std::optional<AccessCheckResult> SecurityDescriptor::AccessCheck(
392 const AccessToken& token,
393 ACCESS_MASK desired_access,
394 const GENERIC_MAPPING& generic_mapping) {
395 GENERIC_MAPPING local_mapping = generic_mapping;
396 ::MapGenericMask(&desired_access, &local_mapping);
397
398 // Allocate a privilege set which could cover all possible privileges.
399 DWORD priv_set_length = checked_cast<DWORD>(
400 sizeof(PRIVILEGE_SET) +
401 (token.Privileges().size() * sizeof(LUID_AND_ATTRIBUTES)));
402 std::vector<char> priv_set(priv_set_length);
403 DWORD granted_access = 0;
404 BOOL access_status = FALSE;
405 SECURITY_DESCRIPTOR sd = {};
406 ToAbsolute(sd);
407 if (!::AccessCheck(&sd, token.get(), desired_access, &local_mapping,
408 reinterpret_cast<PPRIVILEGE_SET>(priv_set.data()),
409 &priv_set_length, &granted_access, &access_status)) {
410 return std::nullopt;
411 }
412 return AccessCheckResult{granted_access, !!access_status};
413 }
414
AccessCheck(const AccessToken & token,ACCESS_MASK desired_access,SecurityObjectType object_type)415 std::optional<AccessCheckResult> SecurityDescriptor::AccessCheck(
416 const AccessToken& token,
417 ACCESS_MASK desired_access,
418 SecurityObjectType object_type) {
419 if (object_type == SecurityObjectType::kKernel) {
420 ::SetLastError(ERROR_INVALID_PARAMETER);
421 return std::nullopt;
422 }
423 return AccessCheck(token, desired_access,
424 GetGenericMappingForType(object_type));
425 }
426
SecurityDescriptor(std::optional<Sid> && owner,std::optional<Sid> && group,std::optional<AccessControlList> && dacl,bool dacl_protected,std::optional<AccessControlList> && sacl,bool sacl_protected)427 SecurityDescriptor::SecurityDescriptor(std::optional<Sid>&& owner,
428 std::optional<Sid>&& group,
429 std::optional<AccessControlList>&& dacl,
430 bool dacl_protected,
431 std::optional<AccessControlList>&& sacl,
432 bool sacl_protected) {
433 owner_.swap(owner);
434 group_.swap(group);
435 dacl_.swap(dacl);
436 dacl_protected_ = dacl_protected;
437 sacl_.swap(sacl);
438 sacl_protected_ = sacl_protected;
439 }
440
441 } // namespace base::win
442