tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 494b473266d420c34cddaf70e0698c6ce3d7b157
parent bceb8a919a51f5cbfe296284f61e8225457db5d7
Author: Bob Owen <bobowencode@gmail.com>
Date:   Wed, 19 Nov 2025 12:34:47 +0000

Bug 1999783 - Lower process's ACL integrity at end of LowerToken. r=handyman

This uses a lower level function for setting the label instead if the chromium
wrappers, so that processes that haven't loaded ntmarta.dll don't need to.

Differential Revision: https://phabricator.services.mozilla.com/D272317

Diffstat:
Msecurity/sandbox/chromium-shim/patches/32_set_delayed_integrity_on_process_acl.patch | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msecurity/sandbox/chromium/sandbox/win/src/target_services.cc | 35++++++++++++++++++++++++++---------
2 files changed, 86 insertions(+), 27 deletions(-)

diff --git a/security/sandbox/chromium-shim/patches/32_set_delayed_integrity_on_process_acl.patch b/security/sandbox/chromium-shim/patches/32_set_delayed_integrity_on_process_acl.patch @@ -8,30 +8,15 @@ Bug 1889932 p1: Set process ACL to the delayed integrity level in LowerToken. r= This allows us to maintain the same access to our process when the integrity level on our access token is dropped. +This uses a lower level function for setting the label instead if the chromium +wrappers, so that processes that haven't loaded ntmarta.dll don't need to. Differential Revision: https://phabricator.services.mozilla.com/D206784 diff --git a/sandbox/win/src/target_services.cc b/sandbox/win/src/target_services.cc --- a/sandbox/win/src/target_services.cc +++ b/sandbox/win/src/target_services.cc -@@ -117,16 +117,24 @@ DWORD SetTokenIntegrityLevel(HANDLE toke - - bool SetProcessIntegrityLevel(IntegrityLevel integrity_level) { - absl::optional<DWORD> rid = GetIntegrityLevelRid(integrity_level); - if (!rid) { - // No mandatory level specified, we don't change it. - return true; - } - -+ // Set integrity level for our process ACL, so we retain access to it. -+ // We ignore failures because this is not a security measure, but some -+ // functionality may fail later in the process. -+ DWORD rv = SetObjectIntegrityLabel(::GetCurrentProcess(), -+ base::win::SecurityObjectType::kKernel, -+ 0, integrity_level); -+ DCHECK(rv == ERROR_SUCCESS); -+ - absl::optional<base::win::AccessToken> token = +@@ -126,16 +126,39 @@ bool SetProcessIntegrityLevel(IntegrityLevel integrity_level) { base::win::AccessToken::FromCurrentProcess(/*impersonation=*/false, TOKEN_ADJUST_DEFAULT); if (!token) { @@ -39,3 +24,60 @@ diff --git a/sandbox/win/src/target_services.cc b/sandbox/win/src/target_service } return token->SetIntegrityLevel(*rid); } + ++void SetProcessAclIntegrityLevel(IntegrityLevel integrity_level) { ++ absl::optional<DWORD> rid = GetIntegrityLevelRid(integrity_level); ++ if (!rid) { ++ // No mandatory level specified, we don't change it. ++ return; ++ } ++ ++ // Set the integrity level for our process ACL, so we retain access to it. ++ // We ignore failures in non-debug because this is not a security measure, ++ // but some functionality may fail later in the process. ++ base::win::SecurityDescriptor sdWrapper; ++ if (!sdWrapper.SetMandatoryLabel(*rid, 0, 0)) { ++ DCHECK(false); ++ return; ++ } ++ ++ SECURITY_DESCRIPTOR sd; ++ sdWrapper.ToAbsolute(sd); ++ BOOL success = ::SetKernelObjectSecurity(::GetCurrentProcess(), ++ LABEL_SECURITY_INFORMATION, &sd); ++ DCHECK(success); ++} ++ + // Used as storage for g_target_services, because other allocation facilities + // are not available early. We can't use a regular function static because on + // VS2015, because the CRT tries to acquire a lock to guard initialization, but + // this code runs before the CRT is initialized. + char g_target_services_memory[sizeof(TargetServicesBase)]; + TargetServicesBase* g_target_services = nullptr; + + } // namespace +@@ -171,21 +194,23 @@ void TargetServicesBase::LowerToken() { + if (ERROR_SUCCESS != ::RegDisablePredefinedCache()) + ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CACHEDISABLE); + if (!WarmupWindowsLocales()) + ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_WARMUP); + bool is_csrss_connected = true; + if (!CloseOpenHandles(&is_csrss_connected)) + ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CLOSEHANDLES); + process_state_.SetCsrssConnected(is_csrss_connected); +- // Enabling mitigations must happen last otherwise handle closing breaks ++ // Enabling mitigations must happen after the above measures otherwise handle ++ // closing breaks. + if (g_shared_delayed_mitigations && + !LockDownSecurityMitigations(g_shared_delayed_mitigations)) { + ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_MITIGATION); + } ++ SetProcessAclIntegrityLevel(g_shared_delayed_integrity_level); + } + + ProcessState* TargetServicesBase::GetState() { + return &process_state_; + } + + TargetServicesBase* TargetServicesBase::GetInstance() { + // Leak on purpose TargetServicesBase. diff --git a/security/sandbox/chromium/sandbox/win/src/target_services.cc b/security/sandbox/chromium/sandbox/win/src/target_services.cc @@ -122,14 +122,6 @@ bool SetProcessIntegrityLevel(IntegrityLevel integrity_level) { return true; } - // Set integrity level for our process ACL, so we retain access to it. - // We ignore failures because this is not a security measure, but some - // functionality may fail later in the process. - DWORD rv = SetObjectIntegrityLabel(::GetCurrentProcess(), - base::win::SecurityObjectType::kKernel, - 0, integrity_level); - DCHECK(rv == ERROR_SUCCESS); - absl::optional<base::win::AccessToken> token = base::win::AccessToken::FromCurrentProcess(/*impersonation=*/false, TOKEN_ADJUST_DEFAULT); @@ -139,6 +131,29 @@ bool SetProcessIntegrityLevel(IntegrityLevel integrity_level) { return token->SetIntegrityLevel(*rid); } +void SetProcessAclIntegrityLevel(IntegrityLevel integrity_level) { + absl::optional<DWORD> rid = GetIntegrityLevelRid(integrity_level); + if (!rid) { + // No mandatory level specified, we don't change it. + return; + } + + // Set the integrity level for our process ACL, so we retain access to it. + // We ignore failures in non-debug because this is not a security measure, + // but some functionality may fail later in the process. + base::win::SecurityDescriptor sdWrapper; + if (!sdWrapper.SetMandatoryLabel(*rid, 0, 0)) { + DCHECK(false); + return; + } + + SECURITY_DESCRIPTOR sd; + sdWrapper.ToAbsolute(sd); + BOOL success = ::SetKernelObjectSecurity(::GetCurrentProcess(), + LABEL_SECURITY_INFORMATION, &sd); + DCHECK(success); +} + // Used as storage for g_target_services, because other allocation facilities // are not available early. We can't use a regular function static because on // VS2015, because the CRT tries to acquire a lock to guard initialization, but @@ -184,11 +199,13 @@ void TargetServicesBase::LowerToken() { if (!CloseOpenHandles(&is_csrss_connected)) ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CLOSEHANDLES); process_state_.SetCsrssConnected(is_csrss_connected); - // Enabling mitigations must happen last otherwise handle closing breaks + // Enabling mitigations must happen after the above measures otherwise handle + // closing breaks. if (g_shared_delayed_mitigations && !LockDownSecurityMitigations(g_shared_delayed_mitigations)) { ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_MITIGATION); } + SetProcessAclIntegrityLevel(g_shared_delayed_integrity_level); } ProcessState* TargetServicesBase::GetState() {