// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/win/security_descriptor.h" // clang-format off #include // Must be in front of other Windows header files. // clang-format on #include #include #include #include #include "base/files/file_path.h" #include "base/logging.h" #include "base/notreached.h" #include "base/numerics/checked_math.h" #include "base/win/scoped_localalloc.h" namespace base::win { namespace { template std::optional CloneValue(const std::optional& value) { if (!value) return std::nullopt; return value->Clone(); } PSID UnwrapSid(const std::optional& sid) { if (!sid) return nullptr; return sid->GetPSID(); } PACL UnwrapAcl(const std::optional& acl) { if (!acl) return nullptr; return acl->get(); } SE_OBJECT_TYPE ConvertObjectType(SecurityObjectType object_type) { switch (object_type) { case SecurityObjectType::kFile: return SE_FILE_OBJECT; case SecurityObjectType::kRegistry: return SE_REGISTRY_KEY; case SecurityObjectType::kWindowStation: case SecurityObjectType::kDesktop: return SE_WINDOW_OBJECT; case SecurityObjectType::kKernel: return SE_KERNEL_OBJECT; } return SE_UNKNOWN_OBJECT_TYPE; } GENERIC_MAPPING GetGenericMappingForType(SecurityObjectType object_type) { GENERIC_MAPPING generic_mapping = {}; switch (object_type) { case SecurityObjectType::kFile: generic_mapping.GenericRead = FILE_GENERIC_READ; generic_mapping.GenericWrite = FILE_GENERIC_WRITE; generic_mapping.GenericExecute = FILE_GENERIC_EXECUTE; generic_mapping.GenericAll = FILE_ALL_ACCESS; break; case SecurityObjectType::kRegistry: generic_mapping.GenericRead = KEY_READ; generic_mapping.GenericWrite = KEY_WRITE; generic_mapping.GenericExecute = KEY_EXECUTE; generic_mapping.GenericAll = KEY_ALL_ACCESS; break; case SecurityObjectType::kDesktop: generic_mapping.GenericRead = STANDARD_RIGHTS_READ | DESKTOP_READOBJECTS | DESKTOP_ENUMERATE; generic_mapping.GenericWrite = STANDARD_RIGHTS_WRITE | DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | DESKTOP_JOURNALRECORD | DESKTOP_JOURNALPLAYBACK | DESKTOP_WRITEOBJECTS; generic_mapping.GenericExecute = STANDARD_RIGHTS_EXECUTE | DESKTOP_SWITCHDESKTOP; generic_mapping.GenericAll = STANDARD_RIGHTS_REQUIRED | DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL | DESKTOP_JOURNALPLAYBACK | DESKTOP_JOURNALRECORD | DESKTOP_READOBJECTS | DESKTOP_SWITCHDESKTOP | DESKTOP_WRITEOBJECTS; break; case SecurityObjectType::kWindowStation: generic_mapping.GenericRead = STANDARD_RIGHTS_READ | WINSTA_ENUMDESKTOPS | WINSTA_ENUMERATE | WINSTA_READATTRIBUTES | WINSTA_READSCREEN; generic_mapping.GenericWrite = STANDARD_RIGHTS_WRITE | WINSTA_ACCESSCLIPBOARD | WINSTA_CREATEDESKTOP | WINSTA_WRITEATTRIBUTES; generic_mapping.GenericExecute = STANDARD_RIGHTS_EXECUTE | WINSTA_ACCESSGLOBALATOMS | WINSTA_EXITWINDOWS; generic_mapping.GenericAll = STANDARD_RIGHTS_REQUIRED | WINSTA_ACCESSCLIPBOARD | WINSTA_ACCESSGLOBALATOMS | WINSTA_CREATEDESKTOP | WINSTA_ENUMDESKTOPS | WINSTA_ENUMERATE | WINSTA_EXITWINDOWS | WINSTA_READATTRIBUTES | WINSTA_READSCREEN | WINSTA_WRITEATTRIBUTES; break; case SecurityObjectType::kKernel: NOTREACHED(); break; } return generic_mapping; } template std::optional 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*)) { PSECURITY_DESCRIPTOR sd = nullptr; DWORD error = get_sd(object, ConvertObjectType(object_type), security_info, nullptr, nullptr, nullptr, nullptr, &sd); if (error != ERROR_SUCCESS) { ::SetLastError(error); DPLOG(ERROR) << "Failed getting security descriptor for object."; return std::nullopt; } auto sd_ptr = TakeLocalAlloc(sd); return SecurityDescriptor::FromPointer(sd_ptr.get()); } template bool 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)) { security_info &= ~(PROTECTED_DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION | PROTECTED_SACL_SECURITY_INFORMATION | UNPROTECTED_SACL_SECURITY_INFORMATION); if (security_info & DACL_SECURITY_INFORMATION) { if (sd.dacl_protected()) { security_info |= PROTECTED_DACL_SECURITY_INFORMATION; } else { security_info |= UNPROTECTED_DACL_SECURITY_INFORMATION; } } if (security_info & SACL_SECURITY_INFORMATION) { if (sd.sacl_protected()) { security_info |= PROTECTED_SACL_SECURITY_INFORMATION; } else { security_info |= UNPROTECTED_SACL_SECURITY_INFORMATION; } } DWORD error = set_sd(object, ConvertObjectType(object_type), security_info, UnwrapSid(sd.owner()), UnwrapSid(sd.group()), UnwrapAcl(sd.dacl()), UnwrapAcl(sd.sacl())); if (error != ERROR_SUCCESS) { ::SetLastError(error); DPLOG(ERROR) << "Failed setting DACL for object."; return false; } return true; } std::optional GetSecurityDescriptorSid( PSECURITY_DESCRIPTOR sd, BOOL(WINAPI* get_sid)(PSECURITY_DESCRIPTOR, PSID*, LPBOOL)) { PSID sid; BOOL defaulted; if (!get_sid(sd, &sid, &defaulted) || !sid) { return std::nullopt; } return Sid::FromPSID(sid); } std::optional GetSecurityDescriptorAcl( PSECURITY_DESCRIPTOR sd, BOOL(WINAPI* get_acl)(PSECURITY_DESCRIPTOR, LPBOOL, PACL*, LPBOOL)) { PACL acl; BOOL present; BOOL defaulted; if (!get_acl(sd, &present, &acl, &defaulted) || !present) { return std::nullopt; } return AccessControlList::FromPACL(acl); } } // namespace SecurityDescriptor::SelfRelative::SelfRelative(const SelfRelative&) = default; SecurityDescriptor::SelfRelative::~SelfRelative() = default; SecurityDescriptor::SelfRelative::SelfRelative(std::vector&& sd) : sd_(sd) {} std::optional SecurityDescriptor::FromPointer( PSECURITY_DESCRIPTOR sd) { if (!sd || !::IsValidSecurityDescriptor(sd)) { ::SetLastError(ERROR_INVALID_SECURITY_DESCR); return std::nullopt; } SECURITY_DESCRIPTOR_CONTROL control; DWORD revision; if (!::GetSecurityDescriptorControl(sd, &control, &revision)) { return std::nullopt; } return SecurityDescriptor{ GetSecurityDescriptorSid(sd, ::GetSecurityDescriptorOwner), GetSecurityDescriptorSid(sd, ::GetSecurityDescriptorGroup), GetSecurityDescriptorAcl(sd, ::GetSecurityDescriptorDacl), !!(control & SE_DACL_PROTECTED), GetSecurityDescriptorAcl(sd, ::GetSecurityDescriptorSacl), !!(control & SE_SACL_PROTECTED)}; } std::optional SecurityDescriptor::FromFile( const base::FilePath& path, SECURITY_INFORMATION security_info) { return FromName(path.value(), SecurityObjectType::kFile, security_info); } std::optional SecurityDescriptor::FromName( const std::wstring& name, SecurityObjectType object_type, SECURITY_INFORMATION security_info) { return GetSecurityDescriptor(name.c_str(), object_type, security_info, ::GetNamedSecurityInfo); } std::optional SecurityDescriptor::FromHandle( HANDLE handle, SecurityObjectType object_type, SECURITY_INFORMATION security_info) { return GetSecurityDescriptor(handle, object_type, security_info, ::GetSecurityInfo); } std::optional SecurityDescriptor::FromSddl( const std::wstring& sddl) { PSECURITY_DESCRIPTOR sd; if (!::ConvertStringSecurityDescriptorToSecurityDescriptor( sddl.c_str(), SDDL_REVISION_1, &sd, nullptr)) { return std::nullopt; } auto sd_ptr = TakeLocalAlloc(sd); return FromPointer(sd_ptr.get()); } SecurityDescriptor::SecurityDescriptor() = default; SecurityDescriptor::SecurityDescriptor(SecurityDescriptor&&) = default; SecurityDescriptor& SecurityDescriptor::operator=(SecurityDescriptor&&) = default; SecurityDescriptor::~SecurityDescriptor() = default; bool SecurityDescriptor::WriteToFile(const base::FilePath& path, SECURITY_INFORMATION security_info) const { return WriteToName(path.value(), SecurityObjectType::kFile, security_info); } bool SecurityDescriptor::WriteToName(const std::wstring& name, SecurityObjectType object_type, SECURITY_INFORMATION security_info) const { return SetSecurityDescriptor( *this, const_cast(name.c_str()), object_type, security_info, ::SetNamedSecurityInfo); } bool SecurityDescriptor::WriteToHandle( HANDLE handle, SecurityObjectType object_type, SECURITY_INFORMATION security_info) const { return SetSecurityDescriptor(*this, handle, object_type, security_info, ::SetSecurityInfo); } std::optional SecurityDescriptor::ToSddl( SECURITY_INFORMATION security_info) const { SECURITY_DESCRIPTOR sd = {}; ToAbsolute(sd); LPWSTR sddl; if (!::ConvertSecurityDescriptorToStringSecurityDescriptor( &sd, SDDL_REVISION_1, security_info, &sddl, nullptr)) { return std::nullopt; } auto sddl_ptr = TakeLocalAlloc(sddl); return sddl_ptr.get(); } void SecurityDescriptor::ToAbsolute(SECURITY_DESCRIPTOR& sd) const { memset(&sd, 0, sizeof(sd)); sd.Revision = SECURITY_DESCRIPTOR_REVISION; sd.Owner = owner_ ? owner_->GetPSID() : nullptr; sd.Group = group_ ? group_->GetPSID() : nullptr; if (dacl_) { sd.Dacl = dacl_->get(); sd.Control |= SE_DACL_PRESENT; if (dacl_protected_) { sd.Control |= SE_DACL_PROTECTED; } } if (sacl_) { sd.Sacl = sacl_->get(); sd.Control |= SE_SACL_PRESENT; if (sacl_protected_) { sd.Control |= SE_SACL_PROTECTED; } } DCHECK(::IsValidSecurityDescriptor(&sd)); } std::optional SecurityDescriptor::ToSelfRelative() const { SECURITY_DESCRIPTOR sd = {}; ToAbsolute(sd); DWORD size = sizeof(SECURITY_DESCRIPTOR_MIN_LENGTH); std::vector buffer(SECURITY_DESCRIPTOR_MIN_LENGTH); if (::MakeSelfRelativeSD(&sd, buffer.data(), &size)) { return SelfRelative(std::move(buffer)); } if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) { return std::nullopt; } buffer.resize(size); if (!::MakeSelfRelativeSD(&sd, buffer.data(), &size)) { return std::nullopt; } return SelfRelative(std::move(buffer)); } SecurityDescriptor SecurityDescriptor::Clone() const { return SecurityDescriptor{CloneValue(owner_), CloneValue(group_), CloneValue(dacl_), dacl_protected_, CloneValue(sacl_), sacl_protected_}; } bool SecurityDescriptor::SetMandatoryLabel(DWORD integrity_level, DWORD inheritance, DWORD mandatory_policy) { std::optional sacl = AccessControlList::FromMandatoryLabel( integrity_level, inheritance, mandatory_policy); if (!sacl) { return false; } sacl_ = std::move(*sacl); return true; } bool SecurityDescriptor::SetDaclEntries( const std::vector& entries) { if (!dacl_) { dacl_ = AccessControlList{}; } return dacl_->SetEntries(entries); } bool SecurityDescriptor::SetDaclEntry(const Sid& sid, SecurityAccessMode mode, DWORD access_mask, DWORD inheritance) { if (!dacl_) { dacl_ = AccessControlList{}; } return dacl_->SetEntry(sid, mode, access_mask, inheritance); } bool SecurityDescriptor::SetDaclEntry(WellKnownSid known_sid, SecurityAccessMode mode, DWORD access_mask, DWORD inheritance) { return SetDaclEntry(Sid(known_sid), mode, access_mask, inheritance); } std::optional SecurityDescriptor::AccessCheck( const AccessToken& token, ACCESS_MASK desired_access, const GENERIC_MAPPING& generic_mapping) { GENERIC_MAPPING local_mapping = generic_mapping; ::MapGenericMask(&desired_access, &local_mapping); // Allocate a privilege set which could cover all possible privileges. DWORD priv_set_length = checked_cast( sizeof(PRIVILEGE_SET) + (token.Privileges().size() * sizeof(LUID_AND_ATTRIBUTES))); std::vector priv_set(priv_set_length); DWORD granted_access = 0; BOOL access_status = FALSE; SECURITY_DESCRIPTOR sd = {}; ToAbsolute(sd); if (!::AccessCheck(&sd, token.get(), desired_access, &local_mapping, reinterpret_cast(priv_set.data()), &priv_set_length, &granted_access, &access_status)) { return std::nullopt; } return AccessCheckResult{granted_access, !!access_status}; } std::optional SecurityDescriptor::AccessCheck( const AccessToken& token, ACCESS_MASK desired_access, SecurityObjectType object_type) { if (object_type == SecurityObjectType::kKernel) { ::SetLastError(ERROR_INVALID_PARAMETER); return std::nullopt; } return AccessCheck(token, desired_access, GetGenericMappingForType(object_type)); } SecurityDescriptor::SecurityDescriptor(std::optional&& owner, std::optional&& group, std::optional&& dacl, bool dacl_protected, std::optional&& sacl, bool sacl_protected) { owner_.swap(owner); group_.swap(group); dacl_.swap(dacl); dacl_protected_ = dacl_protected; sacl_.swap(sacl); sacl_protected_ = sacl_protected; } } // namespace base::win