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:
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() {