| // 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.h" |
| |
| #include <windows.h> |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "base/ranges/algorithm.h" |
| #include "base/win/access_token.h" |
| #include "base/win/security_util.h" |
| #include "sandbox/win/src/acl.h" |
| |
| namespace sandbox { |
| |
| RestrictedToken::RestrictedToken() = default; |
| RestrictedToken::~RestrictedToken() = default; |
| |
| std::optional<base::win::AccessToken> RestrictedToken::GetRestrictedToken() |
| const { |
| std::optional<base::win::AccessToken> token = |
| base::win::AccessToken::FromCurrentProcess(/*impersonation=*/false, |
| TOKEN_ALL_ACCESS); |
| if (!token) { |
| return std::nullopt; |
| } |
| return CreateRestricted(*token); |
| } |
| |
| void RestrictedToken::AddAllSidsForDenyOnly( |
| const std::vector<base::win::Sid>& exceptions) { |
| add_all_sids_for_deny_only_ = true; |
| add_all_exceptions_ = base::win::CloneSidVector(exceptions); |
| } |
| |
| void RestrictedToken::AddSidForDenyOnly(const base::win::Sid& sid) { |
| sids_for_deny_only_.push_back(sid.Clone()); |
| } |
| |
| void RestrictedToken::AddSidForDenyOnly(base::win::WellKnownSid known_sid) { |
| sids_for_deny_only_.emplace_back(known_sid); |
| } |
| |
| void RestrictedToken::AddUserSidForDenyOnly() { |
| add_user_sid_for_deny_only_ = true; |
| } |
| |
| void RestrictedToken::DeleteAllPrivileges(bool remove_traversal_privilege) { |
| delete_all_privileges_ = true; |
| remove_traversal_privilege_ = remove_traversal_privilege; |
| } |
| |
| void RestrictedToken::AddRestrictingSid(const base::win::Sid& sid) { |
| sids_to_restrict_.push_back(sid.Clone()); |
| } |
| |
| void RestrictedToken::AddRestrictingSid(base::win::WellKnownSid known_sid) { |
| sids_to_restrict_.emplace_back(known_sid); |
| } |
| |
| void RestrictedToken::AddRestrictingSidLogonSession() { |
| add_restricting_sid_logon_session_ = true; |
| } |
| |
| void RestrictedToken::AddRestrictingSidCurrentUser() { |
| add_restricting_sid_current_user_ = true; |
| } |
| |
| void RestrictedToken::AddRestrictingSidAllSids() { |
| add_restricting_sid_all_sids_ = true; |
| AddRestrictingSidCurrentUser(); |
| } |
| |
| void RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level) { |
| integrity_rid_ = GetIntegrityLevelRid(integrity_level); |
| } |
| |
| void RestrictedToken::SetLockdownDefaultDacl() { |
| lockdown_default_dacl_ = true; |
| } |
| |
| void RestrictedToken::AddDefaultDaclSid( |
| const base::win::Sid& sid, |
| base::win::SecurityAccessMode access_mode, |
| ACCESS_MASK access) { |
| sids_for_default_dacl_.emplace_back(sid.Clone(), access_mode, access, 0); |
| } |
| |
| void RestrictedToken::AddDefaultDaclSid( |
| base::win::WellKnownSid known_sid, |
| base::win::SecurityAccessMode access_mode, |
| ACCESS_MASK access) { |
| sids_for_default_dacl_.emplace_back(known_sid, access_mode, access, 0); |
| } |
| |
| std::optional<base::win::AccessToken> |
| RestrictedToken::GetRestrictedTokenForTesting(base::win::AccessToken& token) { |
| return CreateRestricted(token); |
| } |
| |
| std::vector<base::win::Sid> RestrictedToken::BuildDenyOnlySids( |
| const base::win::AccessToken& token) const { |
| std::vector<base::win::Sid> sids = |
| base::win::CloneSidVector(sids_for_deny_only_); |
| if (add_user_sid_for_deny_only_) { |
| sids.push_back(token.User()); |
| } |
| if (add_all_sids_for_deny_only_) { |
| // Build the list of the deny only group SIDs |
| for (const base::win::AccessToken::Group& group : token.Groups()) { |
| if (group.IsIntegrity() || group.IsLogonId()) { |
| continue; |
| } |
| if (base::ranges::find(add_all_exceptions_, group.GetSid()) == |
| add_all_exceptions_.end()) { |
| sids.push_back(group.GetSid().Clone()); |
| } |
| } |
| } |
| return sids; |
| } |
| |
| std::vector<base::win::Sid> RestrictedToken::BuildRestrictedSids( |
| const base::win::AccessToken& token) const { |
| std::vector<base::win::Sid> sids = |
| base::win::CloneSidVector(sids_to_restrict_); |
| if (add_restricting_sid_current_user_) { |
| sids.push_back(token.User()); |
| } |
| if (add_restricting_sid_all_sids_) { |
| for (const base::win::AccessToken::Group& group : token.Groups()) { |
| if (group.IsIntegrity()) { |
| continue; |
| } |
| sids.push_back(group.GetSid().Clone()); |
| } |
| } |
| if (add_restricting_sid_logon_session_) { |
| std::optional<base::win::Sid> logon_sid = token.LogonId(); |
| if (logon_sid.has_value()) { |
| sids.push_back(std::move(*logon_sid)); |
| } |
| } |
| return sids; |
| } |
| |
| std::optional<base::win::AccessToken> RestrictedToken::CreateRestricted( |
| const base::win::AccessToken& token) const { |
| std::optional<base::win::AccessToken> new_token; |
| |
| std::vector<base::win::Sid> deny_sids = BuildDenyOnlySids(token); |
| std::vector<base::win::Sid> restrict_sids = BuildRestrictedSids(token); |
| if (!deny_sids.empty() || !restrict_sids.empty() || delete_all_privileges_) { |
| new_token = token.CreateRestricted( |
| delete_all_privileges_ ? DISABLE_MAX_PRIVILEGE : 0, deny_sids, {}, |
| restrict_sids, TOKEN_ALL_ACCESS); |
| } else { |
| // Duplicate the token even if it's not modified at this point |
| // because any subsequent changes to this token would also affect the |
| // current process. |
| new_token = token.DuplicatePrimary(TOKEN_ALL_ACCESS); |
| } |
| |
| if (!new_token) { |
| return std::nullopt; |
| } |
| |
| if (delete_all_privileges_ && remove_traversal_privilege_ && |
| !new_token->RemoveAllPrivileges()) { |
| return std::nullopt; |
| } |
| |
| std::vector<base::win::ExplicitAccessEntry> dacl_entries; |
| |
| std::optional<base::win::AccessControlList> dacl = new_token->DefaultDacl(); |
| if (!dacl) { |
| return std::nullopt; |
| } |
| |
| if (lockdown_default_dacl_) { |
| // Don't add Restricted sid and also remove logon sid access. |
| std::optional<base::win::Sid> logon_sid = new_token->LogonId(); |
| if (logon_sid.has_value()) { |
| dacl_entries.emplace_back(*logon_sid, |
| base::win::SecurityAccessMode::kRevoke, 0, 0); |
| } else { |
| DWORD last_error = ::GetLastError(); |
| if (last_error != ERROR_NOT_FOUND) { |
| return std::nullopt; |
| } |
| } |
| } else { |
| dacl_entries.emplace_back(base::win::WellKnownSid::kRestricted, |
| base::win::SecurityAccessMode::kGrant, |
| GENERIC_ALL, 0); |
| } |
| |
| for (const base::win::ExplicitAccessEntry& entry : sids_for_default_dacl_) { |
| dacl_entries.push_back(entry.Clone()); |
| } |
| |
| dacl_entries.emplace_back( |
| new_token->User(), base::win::SecurityAccessMode::kGrant, GENERIC_ALL, 0); |
| |
| if (!dacl->SetEntries(dacl_entries)) { |
| return std::nullopt; |
| } |
| |
| if (!new_token->SetDefaultDacl(*dacl)) { |
| return std::nullopt; |
| } |
| |
| if (integrity_rid_.has_value()) { |
| if (!new_token->SetIntegrityLevel(*integrity_rid_)) { |
| return std::nullopt; |
| } |
| } |
| |
| return new_token; |
| } |
| |
| } // namespace sandbox |