Commit c1ce57ea authored by James Forshaw's avatar James Forshaw Committed by Commit Bot

[Windows] Add support for random restricted SID.

This CL adds support to the sandbox to define a random restricted SID
for a single process. This combined with modifications to the DACL allows
a process to access itself but no other process defined using the same
lockdown type. This CL does not currently enable it for any sandbox type.

Bug: 1057218
Change-Id: Iadd98cdc7331f5fe0c9ae8f414be8af64c5eeb1c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2085751Reviewed-by: default avatarWill Harris <wfh@chromium.org>
Commit-Queue: James Forshaw <forshaw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#747008}
parent 5d7e77c0
......@@ -146,6 +146,14 @@ DWORD RestrictedToken::GetRestrictedToken(
}
}
for (const auto& default_dacl_sid : sids_for_default_dacl_) {
if (!AddSidToDefaultDacl(new_token.Get(), std::get<0>(default_dacl_sid),
std::get<1>(default_dacl_sid),
std::get<2>(default_dacl_sid))) {
return ::GetLastError();
}
}
// Add user to default dacl.
if (!AddUserSidToDefaultDacl(new_token.Get(), GENERIC_ALL))
return ::GetLastError();
......@@ -410,4 +418,15 @@ void RestrictedToken::SetLockdownDefaultDacl() {
lockdown_default_dacl_ = true;
}
DWORD RestrictedToken::AddDefaultDaclSid(const Sid& sid,
ACCESS_MODE access_mode,
ACCESS_MASK access) {
DCHECK(init_);
if (!init_)
return ERROR_NO_TOKEN;
sids_for_default_dacl_.push_back(std::make_tuple(sid, access_mode, access));
return ERROR_SUCCESS;
}
} // namespace sandbox
......@@ -10,6 +10,7 @@
#include <vector>
#include <string>
#include <tuple>
#include "base/macros.h"
#include "base/win/scoped_handle.h"
......@@ -174,6 +175,12 @@ class RestrictedToken {
// default DACL when created.
void SetLockdownDefaultDacl();
// Add a SID to the default DACL. These SIDs are added regardless of the
// SetLockdownDefaultDacl state.
DWORD AddDefaultDaclSid(const Sid& sid,
ACCESS_MODE access_mode,
ACCESS_MASK access);
private:
// The list of restricting sids in the restricted token.
std::vector<Sid> sids_to_restrict_;
......@@ -181,6 +188,8 @@ class RestrictedToken {
std::vector<LUID> privileges_to_disable_;
// The list of sids to mark as Deny Only in the restricted token.
std::vector<Sid> sids_for_deny_only_;
// The list of sids to add to the default DACL of the restricted token.
std::vector<std::tuple<Sid, ACCESS_MODE, ACCESS_MASK>> sids_for_default_dacl_;
// The token to restrict. Can only be set in a constructor.
base::win::ScopedHandle effective_token_;
// The token integrity level. Only valid on Vista.
......
......@@ -42,6 +42,44 @@ int RunOpenProcessTest(bool unsandboxed,
.c_str());
}
int RunRestrictedOpenProcessTest(bool unsandboxed,
bool lockdown_dacl,
DWORD access_mask) {
TestRunner runner(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LIMITED);
runner.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_LOW);
runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW);
if (lockdown_dacl) {
runner.GetPolicy()->SetLockdownDefaultDacl();
runner.GetPolicy()->AddRestrictingRandomSid();
}
runner.SetAsynchronous(true);
// This spins up a GPU level process, we don't care about the result.
runner.RunTest(L"IntegrationTestsTest_args 1");
TestRunner runner2(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LIMITED);
runner2.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_LOW);
runner2.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW);
runner2.SetUnsandboxed(unsandboxed);
return runner2.RunTest(
base::StringPrintf(L"RestrictedTokenTest_openprocess %d 0x%08X",
runner.process_id(), access_mask)
.c_str());
}
int RunRestrictedSelfOpenProcessTest(bool add_random_sid, DWORD access_mask) {
TestRunner runner(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LIMITED);
runner.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_LOW);
runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW);
runner.GetPolicy()->SetLockdownDefaultDacl();
if (add_random_sid)
runner.GetPolicy()->AddRestrictingRandomSid();
return runner.RunTest(
base::StringPrintf(L"RestrictedTokenTest_currentprocess_dup 0x%08X",
access_mask)
.c_str());
}
} // namespace
// Opens a process based on a PID and access mask passed on the command line.
......@@ -62,6 +100,31 @@ SBOX_TESTS_COMMAND int RestrictedTokenTest_openprocess(int argc,
return SBOX_TEST_DENIED;
}
// Opens a process through duplication. This is to avoid the OpenProcess hook.
SBOX_TESTS_COMMAND int RestrictedTokenTest_currentprocess_dup(int argc,
wchar_t** argv) {
if (argc < 1)
return SBOX_TEST_NOT_FOUND;
DWORD desired_access = wcstoul(argv[0], nullptr, 0);
HANDLE dup_handle;
if (!::DuplicateHandle(::GetCurrentProcess(), ::GetCurrentProcess(),
::GetCurrentProcess(), &dup_handle, 0, FALSE, 0)) {
return SBOX_TEST_FIRST_ERROR;
}
base::win::ScopedHandle process_handle(dup_handle);
if (::DuplicateHandle(::GetCurrentProcess(), process_handle.Get(),
::GetCurrentProcess(), &dup_handle, desired_access,
FALSE, 0)) {
::CloseHandle(dup_handle);
return SBOX_TEST_SUCCEEDED;
}
if (::GetLastError() != ERROR_ACCESS_DENIED)
return SBOX_TEST_SECOND_ERROR;
return SBOX_TEST_DENIED;
}
// Opens a the process token and checks if it's restricted.
SBOX_TESTS_COMMAND int RestrictedTokenTest_IsRestricted(int argc,
wchar_t** argv) {
......@@ -106,4 +169,30 @@ TEST(RestrictedTokenTest, CheckNonAdminRestricted) {
runner_restricted.RunTest(L"RestrictedTokenTest_IsRestricted"));
}
TEST(RestrictedTokenTest, OpenProcessSameSandboxRandomSid) {
// Test process to process open when not using random SID.
ASSERT_EQ(SBOX_TEST_SUCCEEDED,
RunRestrictedOpenProcessTest(false, false, GENERIC_ALL));
// Test process to process open when using random SID.
ASSERT_EQ(SBOX_TEST_DENIED,
RunRestrictedOpenProcessTest(false, true, MAXIMUM_ALLOWED));
// Test process to process open when not using random SID and opening from
// unsandboxed.
ASSERT_EQ(SBOX_TEST_SUCCEEDED,
RunRestrictedOpenProcessTest(true, false, GENERIC_ALL));
// Test process to process open when using random SID and opening from
// unsandboxed.
ASSERT_EQ(SBOX_TEST_SUCCEEDED,
RunRestrictedOpenProcessTest(true, true, GENERIC_ALL));
}
TEST(RestrictedTokenTest, OpenProcessSelfRandomSid) {
// Test process can't open self when not using random SID.
ASSERT_EQ(SBOX_TEST_DENIED,
RunRestrictedSelfOpenProcessTest(false, PROCESS_ALL_ACCESS));
// Test process can open self when using random SID.
ASSERT_EQ(SBOX_TEST_SUCCEEDED,
RunRestrictedSelfOpenProcessTest(true, PROCESS_ALL_ACCESS));
}
} // namespace sandbox
......@@ -20,11 +20,19 @@ namespace sandbox {
namespace {
void TestDefaultDalc(bool restricted_required) {
void TestDefaultDalc(bool restricted_required, bool additional_sid_required) {
RestrictedToken token;
ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
if (!restricted_required)
token.SetLockdownDefaultDacl();
ATL::CSid additional_sid = ATL::Sids::Guests();
ATL::CSid additional_sid2 = ATL::Sids::Batch();
if (additional_sid_required) {
token.AddDefaultDaclSid(Sid(additional_sid.GetPSID()), GRANT_ACCESS,
READ_CONTROL);
token.AddDefaultDaclSid(Sid(additional_sid2.GetPSID()), DENY_ACCESS,
GENERIC_ALL);
}
ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
......@@ -44,20 +52,31 @@ void TestDefaultDalc(bool restricted_required) {
bool restricted_found = false;
bool logon_sid_found = false;
bool additional_sid_found = false;
bool additional_sid2_found = false;
unsigned int ace_count = dacl.GetAceCount();
for (unsigned int i = 0; i < ace_count; ++i) {
ATL::CSid sid;
ACCESS_MASK mask = 0;
dacl.GetAclEntry(i, &sid, &mask);
BYTE ace_type = 0;
dacl.GetAclEntry(i, &sid, &mask, &ace_type);
if (sid == ATL::Sids::RestrictedCode() && mask == GENERIC_ALL) {
restricted_found = true;
} else if (sid == logon_sid) {
logon_sid_found = true;
} else if (sid == additional_sid && mask == READ_CONTROL &&
ace_type == ACCESS_ALLOWED_ACE_TYPE) {
additional_sid_found = true;
} else if (sid == additional_sid2 && mask == GENERIC_ALL &&
ace_type == ACCESS_DENIED_ACE_TYPE) {
additional_sid2_found = true;
}
}
ASSERT_EQ(restricted_required, restricted_found);
ASSERT_EQ(additional_sid_required, additional_sid_found);
ASSERT_EQ(additional_sid_required, additional_sid2_found);
if (!restricted_required)
ASSERT_FALSE(logon_sid_found);
}
......@@ -325,13 +344,24 @@ TEST(RestrictedTokenTest, ResultToken) {
// Verifies that the token created has "Restricted" in its default dacl.
TEST(RestrictedTokenTest, DefaultDacl) {
TestDefaultDalc(true);
TestDefaultDalc(true, false);
}
// Verifies that the token created does not have "Restricted" in its default
// dacl.
TEST(RestrictedTokenTest, DefaultDaclLockdown) {
TestDefaultDalc(false);
TestDefaultDalc(false, false);
}
// Verifies that the token created has an additional SID in its default dacl.
TEST(RestrictedTokenTest, DefaultDaclWithAddition) {
TestDefaultDalc(true, true);
}
// Verifies that the token created does not have "Restricted" in its default
// dacl and also has an additional SID.
TEST(RestrictedTokenTest, DefaultDaclLockdownWithAddition) {
TestDefaultDalc(false, true);
}
// Tests the method "AddSidForDenyOnly".
......
......@@ -56,11 +56,18 @@ DWORD CreateRestrictedToken(HANDLE effective_token,
IntegrityLevel integrity_level,
TokenType token_type,
bool lockdown_default_dacl,
PSID unique_restricted_sid,
base::win::ScopedHandle* token) {
RestrictedToken restricted_token;
restricted_token.Init(effective_token);
if (lockdown_default_dacl)
restricted_token.SetLockdownDefaultDacl();
if (unique_restricted_sid) {
restricted_token.AddDefaultDaclSid(Sid(unique_restricted_sid), GRANT_ACCESS,
GENERIC_ALL);
restricted_token.AddDefaultDaclSid(Sid(WinCreatorOwnerRightsSid),
GRANT_ACCESS, READ_CONTROL);
}
std::vector<std::wstring> privilege_exceptions;
std::vector<Sid> sid_exceptions;
......@@ -105,6 +112,8 @@ DWORD CreateRestrictedToken(HANDLE effective_token,
restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
restricted_token.AddRestrictingSidCurrentUser();
restricted_token.AddRestrictingSidLogonSession();
if (unique_restricted_sid)
restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
break;
}
case USER_INTERACTIVE: {
......@@ -118,6 +127,8 @@ DWORD CreateRestrictedToken(HANDLE effective_token,
restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
restricted_token.AddRestrictingSidCurrentUser();
restricted_token.AddRestrictingSidLogonSession();
if (unique_restricted_sid)
restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
break;
}
case USER_LIMITED: {
......@@ -128,6 +139,8 @@ DWORD CreateRestrictedToken(HANDLE effective_token,
restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
restricted_token.AddRestrictingSid(WinWorldSid);
restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
if (unique_restricted_sid)
restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
// This token has to be able to create objects in BNO.
// Unfortunately, on Vista+, it needs the current logon sid
......@@ -141,11 +154,15 @@ DWORD CreateRestrictedToken(HANDLE effective_token,
privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
restricted_token.AddUserSidForDenyOnly();
restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
if (unique_restricted_sid)
restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
break;
}
case USER_LOCKDOWN: {
restricted_token.AddUserSidForDenyOnly();
restricted_token.AddRestrictingSid(WinNullSid);
if (unique_restricted_sid)
restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
break;
}
default: { return ERROR_BAD_ARGUMENTS; }
......
......@@ -38,6 +38,7 @@ DWORD CreateRestrictedToken(HANDLE effective_token,
IntegrityLevel integrity_level,
TokenType token_type,
bool lockdown_default_dacl,
PSID unique_restricted_sid,
base::win::ScopedHandle* token);
// Sets the integrity label on a object handle.
......
......@@ -252,6 +252,10 @@ class TargetPolicy {
// resources.
virtual void SetLockdownDefaultDacl() = 0;
// Adds a restricting random SID to the restricted SIDs list as well as
// the default DACL.
virtual void AddRestrictingRandomSid() = 0;
// Enable OPM API redirection when in Win32k lockdown.
virtual void SetEnableOPMRedirection() = 0;
// Enable OPM API emulation when in Win32k lockdown.
......
......@@ -109,6 +109,7 @@ PolicyBase::PolicyBase()
policy_(nullptr),
lowbox_sid_(nullptr),
lockdown_default_dacl_(false),
add_restricting_random_sid_(false),
enable_opm_redirection_(false),
effective_token_(nullptr) {
::InitializeCriticalSection(&lock_);
......@@ -389,6 +390,10 @@ void PolicyBase::SetLockdownDefaultDacl() {
lockdown_default_dacl_ = true;
}
void PolicyBase::AddRestrictingRandomSid() {
add_restricting_random_sid_ = true;
}
const base::HandlesToInheritVector& PolicyBase::GetHandlesBeingShared() {
return handles_to_share_;
}
......@@ -413,11 +418,16 @@ ResultCode PolicyBase::MakeJobObject(base::win::ScopedHandle* job) {
ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial,
base::win::ScopedHandle* lockdown,
base::win::ScopedHandle* lowbox) {
Sid random_sid = Sid::GenerateRandomSid();
PSID random_sid_ptr = nullptr;
if (add_restricting_random_sid_)
random_sid_ptr = random_sid.GetPSID();
// Create the 'naked' token. This will be the permanent token associated
// with the process and therefore with any thread that is not impersonating.
DWORD result =
CreateRestrictedToken(effective_token_, lockdown_level_, integrity_level_,
PRIMARY, lockdown_default_dacl_, lockdown);
DWORD result = CreateRestrictedToken(
effective_token_, lockdown_level_, integrity_level_, PRIMARY,
lockdown_default_dacl_, random_sid_ptr, lockdown);
if (ERROR_SUCCESS != result)
return SBOX_ERROR_CANNOT_CREATE_RESTRICTED_TOKEN;
......@@ -484,9 +494,9 @@ ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial,
// Create the 'better' token. We use this token as the one that the main
// thread uses when booting up the process. It should contain most of
// what we need (before reaching main( ))
result =
CreateRestrictedToken(effective_token_, initial_level_, integrity_level_,
IMPERSONATION, lockdown_default_dacl_, initial);
result = CreateRestrictedToken(
effective_token_, initial_level_, integrity_level_, IMPERSONATION,
lockdown_default_dacl_, random_sid_ptr, initial);
if (ERROR_SUCCESS != result)
return SBOX_ERROR_CANNOT_CREATE_RESTRICTED_IMP_TOKEN;
......
......@@ -73,6 +73,7 @@ class PolicyBase final : public TargetPolicy {
const wchar_t* handle_name) override;
void AddHandleToShare(HANDLE handle) override;
void SetLockdownDefaultDacl() override;
void AddRestrictingRandomSid() override;
void SetEnableOPMRedirection() override;
bool GetEnableOPMRedirection() override;
ResultCode AddAppContainerProfile(const wchar_t* package_name,
......@@ -168,6 +169,7 @@ class PolicyBase final : public TargetPolicy {
base::win::ScopedHandle lowbox_directory_;
std::unique_ptr<Dispatcher> dispatcher_;
bool lockdown_default_dacl_;
bool add_restricting_random_sid_;
static HDESK alternate_desktop_handle_;
static HWINSTA alternate_winstation_handle_;
......
......@@ -7,8 +7,10 @@
#include <memory>
#include <sddl.h>
#include <stdlib.h>
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/win/windows_version.h"
#include "sandbox/win/src/win_utils.h"
......@@ -132,6 +134,14 @@ Sid Sid::AllRestrictedApplicationPackages() {
return FromSubAuthorities(&package_authority, 2, sub_authorities);
}
Sid Sid::GenerateRandomSid() {
SID_IDENTIFIER_AUTHORITY package_authority = {SECURITY_NULL_SID_AUTHORITY};
DWORD sub_authorities[4] = {};
base::RandBytes(&sub_authorities, sizeof(sub_authorities));
return FromSubAuthorities(&package_authority, _countof(sub_authorities),
sub_authorities);
}
PSID Sid::GetPSID() const {
return const_cast<BYTE*>(sid_);
}
......
......@@ -52,6 +52,8 @@ class Sid {
PDWORD sub_authorities);
// Create the restricted all application packages sid.
static Sid AllRestrictedApplicationPackages();
// Generate a random SID value.
static Sid GenerateRandomSid();
// Returns sid_.
PSID GetPSID() const;
......
......@@ -179,4 +179,12 @@ TEST(SidTest, SubAuthorities) {
ASSERT_TRUE(EqualSid(sid_admin, ATL::Sids::Admins()));
}
TEST(SidTest, RandomSid) {
Sid sid1 = Sid::GenerateRandomSid();
ASSERT_TRUE(sid1.IsValid());
Sid sid2 = Sid::GenerateRandomSid();
ASSERT_TRUE(sid2.IsValid());
ASSERT_FALSE(::EqualSid(sid1.GetPSID(), sid2.GetPSID()));
}
} // namespace sandbox
......@@ -131,6 +131,7 @@ class TestTargetPolicy : public sandbox::TargetPolicy {
}
void AddHandleToShare(HANDLE handle) override {}
void SetLockdownDefaultDacl() override {}
void AddRestrictingRandomSid() override {}
void SetEnableOPMRedirection() override {}
bool GetEnableOPMRedirection() override { return false; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment