Skip to content

Commit

Permalink
[Windows] Refactor RestrictedToken to use base's AccessToken.
Browse files Browse the repository at this point in the history
This CL refactors the sandbox RestrictedToken class to use the base
library's AccessToken class. As part of that various areas have been
cleaned up, for example the support for arbitrary effective tokens has
been removed as it was used by a single test and in practice creating
a new process with a restricted token created from something other than
the current primary token is not possible as a non-administrator.
The CL also adds tests for the output of the CreateRestrictedToken
utility which didn't seem to exist.

Bug: 1394854
Change-Id: I0d2879759f9555dc607314bc1698f9b632a5b1df
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4192962
Commit-Queue: James Forshaw <forshaw@chromium.org>
Reviewed-by: Alex Gough <ajgo@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1096908}
  • Loading branch information
James Forshaw authored and Chromium LUCI CQ committed Jan 25, 2023
1 parent bfdd3a4 commit f771afc
Show file tree
Hide file tree
Showing 18 changed files with 718 additions and 1,026 deletions.
2 changes: 0 additions & 2 deletions sandbox/policy/win/sandbox_win_unittest.cc
Expand Up @@ -135,8 +135,6 @@ class TestTargetPolicy : public TargetPolicy {
ResultCode SetStderrHandle(HANDLE handle) override { return SBOX_ALL_OK; }
void AddHandleToShare(HANDLE handle) override {}

void SetEffectiveToken(HANDLE token) override {}

private:
TestTargetConfig config_;
};
Expand Down
112 changes: 23 additions & 89 deletions sandbox/win/src/acl.cc
Expand Up @@ -7,91 +7,9 @@
#include <windows.h>

#include "base/notreached.h"
#include "base/win/access_token.h"

namespace sandbox {

namespace {

absl::optional<DWORD> GetIntegrityLevelValue(IntegrityLevel integrity_level) {
switch (integrity_level) {
case INTEGRITY_LEVEL_SYSTEM:
return DWORD{SECURITY_MANDATORY_SYSTEM_RID};
case INTEGRITY_LEVEL_HIGH:
return DWORD{SECURITY_MANDATORY_HIGH_RID};
case INTEGRITY_LEVEL_MEDIUM:
return DWORD{SECURITY_MANDATORY_MEDIUM_RID};
case INTEGRITY_LEVEL_MEDIUM_LOW:
return DWORD{SECURITY_MANDATORY_MEDIUM_RID - 2048};
case INTEGRITY_LEVEL_LOW:
return DWORD{SECURITY_MANDATORY_LOW_RID};
case INTEGRITY_LEVEL_BELOW_LOW:
return DWORD{SECURITY_MANDATORY_LOW_RID - 2048};
case INTEGRITY_LEVEL_UNTRUSTED:
return DWORD{SECURITY_MANDATORY_UNTRUSTED_RID};
case INTEGRITY_LEVEL_LAST:
return absl::nullopt;
}

NOTREACHED();
return absl::nullopt;
}

bool AddSidToDefaultDacl(base::win::AccessToken& token,
const base::win::Sid& sid,
base::win::SecurityAccessMode access_mode,
ACCESS_MASK access) {
absl::optional<base::win::AccessControlList> dacl = token.DefaultDacl();
if (!dacl || !dacl->SetEntry(sid, access_mode, access, 0)) {
return false;
}
return token.SetDefaultDacl(*dacl);
}

} // namespace

bool AddSidToDefaultDacl(HANDLE token,
const base::win::Sid& sid,
base::win::SecurityAccessMode access_mode,
ACCESS_MASK access) {
absl::optional<base::win::AccessToken> query_token =
base::win::AccessToken::FromToken(token, TOKEN_ADJUST_DEFAULT);
if (!query_token)
return false;

return AddSidToDefaultDacl(*query_token, sid, access_mode, access);
}

bool AddSidToDefaultDacl(HANDLE token,
base::win::WellKnownSid known_sid,
base::win::SecurityAccessMode access_mode,
ACCESS_MASK access) {
return AddSidToDefaultDacl(token, base::win::Sid(known_sid), access_mode,
access);
}

bool RevokeLogonSidFromDefaultDacl(HANDLE token) {
absl::optional<base::win::AccessToken> query_token =
base::win::AccessToken::FromToken(token, TOKEN_ADJUST_DEFAULT);
if (!query_token)
return false;
absl::optional<base::win::Sid> logon_sid = query_token->LogonId();
if (!logon_sid)
return ::GetLastError() == ERROR_NOT_FOUND;

return AddSidToDefaultDacl(*query_token, *logon_sid,
base::win::SecurityAccessMode::kRevoke, 0);
}

bool AddUserSidToDefaultDacl(HANDLE token, ACCESS_MASK access) {
absl::optional<base::win::AccessToken> query_token =
base::win::AccessToken::FromToken(token, TOKEN_ADJUST_DEFAULT);
if (!query_token)
return false;
return AddSidToDefaultDacl(*query_token, query_token->User(),
base::win::SecurityAccessMode::kGrant, access);
}

bool AddKnownSidToObject(HANDLE object,
base::win::SecurityObjectType object_type,
const base::win::Sid& sid,
Expand Down Expand Up @@ -134,19 +52,35 @@ bool ReplacePackageSidInDacl(HANDLE object,
base::win::SecurityAccessMode::kGrant, access);
}

absl::optional<base::win::Sid> GetIntegrityLevelSid(
IntegrityLevel integrity_level) {
absl::optional<DWORD> value = GetIntegrityLevelValue(integrity_level);
if (!value)
return absl::nullopt;
return base::win::Sid::FromIntegrityLevel(*value);
absl::optional<DWORD> GetIntegrityLevelRid(IntegrityLevel integrity_level) {
switch (integrity_level) {
case INTEGRITY_LEVEL_SYSTEM:
return DWORD{SECURITY_MANDATORY_SYSTEM_RID};
case INTEGRITY_LEVEL_HIGH:
return DWORD{SECURITY_MANDATORY_HIGH_RID};
case INTEGRITY_LEVEL_MEDIUM:
return DWORD{SECURITY_MANDATORY_MEDIUM_RID};
case INTEGRITY_LEVEL_MEDIUM_LOW:
return DWORD{SECURITY_MANDATORY_MEDIUM_RID - 2048};
case INTEGRITY_LEVEL_LOW:
return DWORD{SECURITY_MANDATORY_LOW_RID};
case INTEGRITY_LEVEL_BELOW_LOW:
return DWORD{SECURITY_MANDATORY_LOW_RID - 2048};
case INTEGRITY_LEVEL_UNTRUSTED:
return DWORD{SECURITY_MANDATORY_UNTRUSTED_RID};
case INTEGRITY_LEVEL_LAST:
return absl::nullopt;
}

NOTREACHED();
return absl::nullopt;
}

DWORD SetObjectIntegrityLabel(HANDLE handle,
base::win::SecurityObjectType object_type,
DWORD mandatory_policy,
IntegrityLevel integrity_level) {
absl::optional<DWORD> value = GetIntegrityLevelValue(integrity_level);
absl::optional<DWORD> value = GetIntegrityLevelRid(integrity_level);
if (!value) {
return ERROR_INVALID_SID;
}
Expand Down
45 changes: 11 additions & 34 deletions sandbox/win/src/acl.h
Expand Up @@ -5,7 +5,6 @@
#ifndef SANDBOX_WIN_SRC_ACL_H_
#define SANDBOX_WIN_SRC_ACL_H_

#include "base/memory/free_deleter.h"
#include "base/win/access_control_list.h"
#include "base/win/security_descriptor.h"
#include "base/win/sid.h"
Expand All @@ -15,37 +14,16 @@

namespace sandbox {

// Adds an ACE represented by |sid| and |access| with |access_mode| to the
// default dacl present in the token.
bool AddSidToDefaultDacl(HANDLE token,
const base::win::Sid& sid,
base::win::SecurityAccessMode access_mode,
ACCESS_MASK access);

// Adds an ACE represented by |known_sid| and |access| with |access_mode| to the
// default dacl present in the token.
bool AddSidToDefaultDacl(HANDLE token,
base::win::WellKnownSid known_sid,
base::win::SecurityAccessMode access_mode,
ACCESS_MASK access);

// Revokes access to the logon SID for the default dacl present in the token.
bool RevokeLogonSidFromDefaultDacl(HANDLE token);

// Adds an ACE represented by the user sid and |access| to the default dacl
// present in the token.
bool AddUserSidToDefaultDacl(HANDLE token, ACCESS_MASK access);

// Adds an ACE represented by |known_sid|, |access_mode|, and |access| to
// the dacl of the kernel object referenced by |object| and of |object_type|.
// Adds an ACE represented by `known_sid`, `access_mode`, and `access` to
// the dacl of the kernel object referenced by `object` and of `object_type`.
bool AddKnownSidToObject(HANDLE object,
base::win::SecurityObjectType object_type,
base::win::WellKnownSid known_sid,
base::win::SecurityAccessMode access_mode,
ACCESS_MASK access);

// Adds an ACE represented by |sid|, |access_mode|, and |access| to
// the dacl of the kernel object referenced by |object| and of |object_type|.
// Adds an ACE represented by `sid`, `access_mode`, and `access` to
// the dacl of the kernel object referenced by `object` and of `object_type`.
bool AddKnownSidToObject(HANDLE object,
base::win::SecurityObjectType object_type,
const base::win::Sid& sid,
Expand All @@ -60,20 +38,19 @@ bool ReplacePackageSidInDacl(HANDLE object,
const base::win::Sid& package_sid,
ACCESS_MASK access);

// Returns the Sid associated with a given IntegrityLevel value. This returns
// an empty value if |integrity_level| is set to INTEGRITY_LEVEL_LAST.
absl::optional<base::win::Sid> GetIntegrityLevelSid(
IntegrityLevel integrity_level);
// Returns the RID associated with a given IntegrityLevel value. This returns
// an empty value if `integrity_level` is set to INTEGRITY_LEVEL_LAST.
absl::optional<DWORD> GetIntegrityLevelRid(IntegrityLevel integrity_level);

// Sets the integrity label on a object.
// |handle| should be an open handle with WRITE_OWNER access.
// |object_type| represents the kernel object type of the handle.
// |mandatory_policy| is the mandatory policy to use. This can be zero or more
// `handle` should be an open handle with WRITE_OWNER access.
// `object_type` represents the kernel object type of the handle.
// `mandatory_policy` is the mandatory policy to use. This can be zero or more
// of the following bit flags:
// SYSTEM_MANDATORY_LABEL_NO_WRITE_UP - Block write access.
// SYSTEM_MANDATORY_LABEL_NO_READ_UP - Block read access.
// SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP - Block execute access.
// |integrity_level| is the level to set.
// `integrity_level` is the level to set.
// If the function succeeds, the return value is ERROR_SUCCESS. If the
// function fails, the return value is the win32 error code corresponding to
// the error.
Expand Down
20 changes: 9 additions & 11 deletions sandbox/win/src/acl_unittest.cc
Expand Up @@ -19,10 +19,9 @@ void CheckGetIntegrityLevelSid(IntegrityLevel integrity_level,
absl::optional<base::win::Sid> sddl_sid =
base::win::Sid::FromSddlString(sddl);
ASSERT_TRUE(sddl_sid);
absl::optional<base::win::Sid> integrity_sid =
GetIntegrityLevelSid(integrity_level);
ASSERT_TRUE(integrity_sid);
EXPECT_EQ(*sddl_sid, *integrity_sid);
absl::optional<DWORD> integrity_value = GetIntegrityLevelRid(integrity_level);
ASSERT_TRUE(integrity_value);
EXPECT_EQ(*sddl_sid, base::win::Sid::FromIntegrityLevel(*integrity_value));
}

void CheckSetObjectIntegrityLabel(DWORD mandatory_policy,
Expand All @@ -49,18 +48,17 @@ void CheckSetObjectIntegrityLabel(DWORD mandatory_policy,
ASSERT_EQ(ace->Header.AceType, SYSTEM_MANDATORY_LABEL_ACE_TYPE);
EXPECT_EQ(ace->Header.AceFlags, 0);
EXPECT_EQ(ace->Mask, mandatory_policy);
absl::optional<base::win::Sid> integrity_sid =
GetIntegrityLevelSid(integrity_level);
ASSERT_TRUE(integrity_sid);
absl::optional<DWORD> rid = GetIntegrityLevelRid(integrity_level);
base::win::Sid sid = base::win::Sid::FromIntegrityLevel(*rid);
ASSERT_TRUE(::IsValidSid(&ace->SidStart));
EXPECT_TRUE(integrity_sid->Equal(&ace->SidStart));
EXPECT_TRUE(sid.Equal(&ace->SidStart));
}

} // namespace

// Checks the functionality of GetIntegrityLevelSid.
TEST(AclTest, GetIntegrityLevelSid) {
EXPECT_FALSE(GetIntegrityLevelSid(INTEGRITY_LEVEL_LAST));
// Checks the functionality of GetIntegrityLevelRid.
TEST(AclTest, GetIntegrityLevelRid) {
EXPECT_FALSE(GetIntegrityLevelRid(INTEGRITY_LEVEL_LAST));
CheckGetIntegrityLevelSid(INTEGRITY_LEVEL_SYSTEM, L"S-1-16-16384");
CheckGetIntegrityLevelSid(INTEGRITY_LEVEL_HIGH, L"S-1-16-12288");
CheckGetIntegrityLevelSid(INTEGRITY_LEVEL_MEDIUM, L"S-1-16-8192");
Expand Down
8 changes: 4 additions & 4 deletions sandbox/win/src/app_container_base.cc
Expand Up @@ -264,8 +264,8 @@ AppContainerBase::GetSecurityCapabilities() {

ResultCode AppContainerBase::BuildLowBoxToken(base::win::ScopedHandle* token) {
if (type_ == AppContainerType::kLowbox) {
if (!CreateLowBoxToken(token->get(), PRIMARY, package_sid_, capabilities_,
token)) {
if (!CreateLowBoxToken(token->get(), TokenType::kPrimary, package_sid_,
capabilities_, token)) {
return SBOX_ERROR_CANNOT_CREATE_LOWBOX_TOKEN;
}

Expand All @@ -274,8 +274,8 @@ ResultCode AppContainerBase::BuildLowBoxToken(base::win::ScopedHandle* token) {
package_sid_, TOKEN_ALL_ACCESS)) {
return SBOX_ERROR_CANNOT_MODIFY_LOWBOX_TOKEN_DACL;
}
} else if (!CreateLowBoxToken(nullptr, IMPERSONATION, package_sid_,
capabilities_, token)) {
} else if (!CreateLowBoxToken(nullptr, TokenType::kImpersonation,
package_sid_, capabilities_, token)) {
return SBOX_ERROR_CANNOT_CREATE_LOWBOX_IMPERSONATION_TOKEN;
}

Expand Down
25 changes: 0 additions & 25 deletions sandbox/win/src/policy_target_test.cc
Expand Up @@ -526,31 +526,6 @@ TEST(PolicyTargetTest, ShareHandleTest) {
::WaitForSingleObject(target.process_handle(), INFINITE);
}

// Dummy target that just reports that's it spawned correctly.
SBOX_TESTS_COMMAND int PolicyTargetTest_SetEffectiveToken(int argc,
wchar_t** argv) {
return SBOX_TEST_SUCCEEDED;
}

// Test whether after using SetEffectiveToken spawning a target works as
// expected.
TEST(PolicyTargetTest, SetEffectiveToken) {
TestRunner runner;
HANDLE token;

// Get current process token.
EXPECT_TRUE(
::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &token));

// Setup token guard.
base::win::ScopedHandle token_guard(token);

// Set token and run target.
runner.GetPolicy()->SetEffectiveToken(token_guard.Get());
EXPECT_EQ(SBOX_TEST_SUCCEEDED,
runner.RunTest(L"PolicyTargetTest_SetEffectiveToken"));
}

// Test if shared policies can be created by the broker.
TEST(SharedTargetConfig, BrokerConfigManagement) {
BrokerServices* broker = GetBroker();
Expand Down

0 comments on commit f771afc

Please sign in to comment.