| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "sandbox/win/src/restricted_token_utils.h" |
| |
| #include <memory> |
| #include <vector> |
| |
| #include <optional> |
| #include "base/check.h" |
| #include "base/notreached.h" |
| #include "base/win/access_token.h" |
| #include "base/win/security_descriptor.h" |
| #include "sandbox/win/src/acl.h" |
| #include "sandbox/win/src/restricted_token.h" |
| #include "sandbox/win/src/sandbox_nt_util.h" |
| #include "sandbox/win/src/security_level.h" |
| #include "sandbox/win/src/win_utils.h" |
| |
| namespace sandbox { |
| |
| namespace { |
| |
| void AddSidException(std::vector<base::win::Sid>& sids, |
| base::win::WellKnownSid known_sid) { |
| sids.push_back(base::win::Sid::FromKnownSid(known_sid)); |
| } |
| |
| } // namespace |
| |
| std::optional<base::win::AccessToken> CreateRestrictedToken( |
| TokenLevel security_level, |
| IntegrityLevel integrity_level, |
| TokenType token_type, |
| bool lockdown_default_dacl, |
| const std::optional<base::win::Sid>& unique_restricted_sid) { |
| RestrictedToken restricted_token; |
| if (lockdown_default_dacl) { |
| restricted_token.SetLockdownDefaultDacl(); |
| } |
| if (unique_restricted_sid) { |
| restricted_token.AddDefaultDaclSid(*unique_restricted_sid, |
| base::win::SecurityAccessMode::kGrant, |
| GENERIC_ALL); |
| restricted_token.AddDefaultDaclSid( |
| base::win::WellKnownSid::kCreatorOwnerRights, |
| base::win::SecurityAccessMode::kGrant, READ_CONTROL); |
| } |
| |
| std::vector<std::wstring> privilege_exceptions; |
| std::vector<base::win::Sid> sid_exceptions; |
| |
| bool deny_sids = true; |
| bool remove_privileges = true; |
| bool remove_traverse_privilege = false; |
| |
| switch (security_level) { |
| case USER_UNPROTECTED: |
| deny_sids = false; |
| remove_privileges = false; |
| break; |
| case USER_RESTRICTED_SAME_ACCESS: |
| deny_sids = false; |
| remove_privileges = false; |
| restricted_token.AddRestrictingSidAllSids(); |
| break; |
| case USER_RESTRICTED_NON_ADMIN: |
| AddSidException(sid_exceptions, base::win::WellKnownSid::kBuiltinUsers); |
| AddSidException(sid_exceptions, base::win::WellKnownSid::kWorld); |
| AddSidException(sid_exceptions, base::win::WellKnownSid::kInteractive); |
| AddSidException(sid_exceptions, |
| base::win::WellKnownSid::kAuthenticatedUser); |
| restricted_token.AddRestrictingSid( |
| base::win::WellKnownSid::kBuiltinUsers); |
| restricted_token.AddRestrictingSid(base::win::WellKnownSid::kWorld); |
| restricted_token.AddRestrictingSid(base::win::WellKnownSid::kInteractive); |
| restricted_token.AddRestrictingSid( |
| base::win::WellKnownSid::kAuthenticatedUser); |
| restricted_token.AddRestrictingSid(base::win::WellKnownSid::kRestricted); |
| restricted_token.AddRestrictingSidCurrentUser(); |
| restricted_token.AddRestrictingSidLogonSession(); |
| if (unique_restricted_sid) { |
| restricted_token.AddRestrictingSid(*unique_restricted_sid); |
| } |
| break; |
| case USER_INTERACTIVE: |
| AddSidException(sid_exceptions, base::win::WellKnownSid::kBuiltinUsers); |
| AddSidException(sid_exceptions, base::win::WellKnownSid::kWorld); |
| AddSidException(sid_exceptions, base::win::WellKnownSid::kInteractive); |
| AddSidException(sid_exceptions, |
| base::win::WellKnownSid::kAuthenticatedUser); |
| restricted_token.AddRestrictingSid( |
| base::win::WellKnownSid::kBuiltinUsers); |
| restricted_token.AddRestrictingSid(base::win::WellKnownSid::kWorld); |
| restricted_token.AddRestrictingSid(base::win::WellKnownSid::kRestricted); |
| restricted_token.AddRestrictingSidCurrentUser(); |
| restricted_token.AddRestrictingSidLogonSession(); |
| if (unique_restricted_sid) { |
| restricted_token.AddRestrictingSid(*unique_restricted_sid); |
| } |
| break; |
| case USER_LIMITED: |
| AddSidException(sid_exceptions, base::win::WellKnownSid::kBuiltinUsers); |
| AddSidException(sid_exceptions, base::win::WellKnownSid::kWorld); |
| AddSidException(sid_exceptions, base::win::WellKnownSid::kInteractive); |
| restricted_token.AddRestrictingSid( |
| base::win::WellKnownSid::kBuiltinUsers); |
| restricted_token.AddRestrictingSid(base::win::WellKnownSid::kWorld); |
| restricted_token.AddRestrictingSid(base::win::WellKnownSid::kRestricted); |
| if (unique_restricted_sid) { |
| restricted_token.AddRestrictingSid(*unique_restricted_sid); |
| } |
| // This token has to be able to create objects in BNO, it needs the |
| // current logon sid in the token to achieve this. You should also set the |
| // process to be low integrity level so it can't access object created by |
| // other processes. |
| restricted_token.AddRestrictingSidLogonSession(); |
| break; |
| case USER_LOCKDOWN: |
| remove_traverse_privilege = true; |
| restricted_token.AddUserSidForDenyOnly(); |
| restricted_token.AddRestrictingSid(base::win::WellKnownSid::kNull); |
| if (unique_restricted_sid) { |
| restricted_token.AddRestrictingSid(*unique_restricted_sid); |
| } |
| break; |
| case USER_LAST: |
| return std::nullopt; |
| } |
| |
| if (deny_sids) { |
| restricted_token.AddAllSidsForDenyOnly(sid_exceptions); |
| } |
| |
| if (remove_privileges) { |
| restricted_token.DeleteAllPrivileges(remove_traverse_privilege); |
| } |
| |
| restricted_token.SetIntegrityLevel(integrity_level); |
| std::optional<base::win::AccessToken> result = |
| restricted_token.GetRestrictedToken(); |
| if (!result) { |
| return std::nullopt; |
| } |
| |
| if (token_type == TokenType::kPrimary) { |
| return result; |
| } |
| |
| result = result->DuplicateImpersonation( |
| base::win::SecurityImpersonationLevel::kImpersonation, TOKEN_ALL_ACCESS); |
| if (!result) { |
| return std::nullopt; |
| } |
| |
| return result; |
| } |
| |
| DWORD HardenTokenIntegrityLevelPolicy(const base::win::AccessToken& token) { |
| std::optional<base::win::SecurityDescriptor> sd = |
| base::win::SecurityDescriptor::FromHandle( |
| token.get(), base::win::SecurityObjectType::kKernel, |
| LABEL_SECURITY_INFORMATION); |
| if (!sd) { |
| return ::GetLastError(); |
| } |
| |
| // If no SACL then nothing to do. |
| if (!sd->sacl()) { |
| return ERROR_SUCCESS; |
| } |
| PACL sacl = sd->sacl()->get(); |
| |
| for (DWORD ace_index = 0; ace_index < sacl->AceCount; ++ace_index) { |
| PSYSTEM_MANDATORY_LABEL_ACE ace; |
| |
| if (::GetAce(sacl, ace_index, reinterpret_cast<LPVOID*>(&ace)) && |
| ace->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE) { |
| ace->Mask |= SYSTEM_MANDATORY_LABEL_NO_READ_UP | |
| SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP; |
| break; |
| } |
| } |
| if (!sd->WriteToHandle(token.get(), base::win::SecurityObjectType::kKernel, |
| LABEL_SECURITY_INFORMATION)) { |
| return ::GetLastError(); |
| } |
| |
| return ERROR_SUCCESS; |
| } |
| |
| } // namespace sandbox |