diff options
-rw-r--r-- | sandbox/win/src/app_container_test.cc | 17 | ||||
-rw-r--r-- | sandbox/win/src/broker_services.cc | 4 | ||||
-rw-r--r-- | sandbox/win/src/nt_internals.h | 24 | ||||
-rw-r--r-- | sandbox/win/src/policy_target.cc | 10 | ||||
-rw-r--r-- | sandbox/win/src/sandbox_policy.h | 4 | ||||
-rw-r--r-- | sandbox/win/src/sandbox_policy_base.cc | 57 | ||||
-rw-r--r-- | sandbox/win/src/sandbox_policy_base.h | 4 | ||||
-rw-r--r-- | sandbox/win/src/sync_policy_test.cc | 3 | ||||
-rw-r--r-- | sandbox/win/src/target_process.cc | 72 | ||||
-rw-r--r-- | sandbox/win/src/target_process.h | 3 |
10 files changed, 173 insertions, 25 deletions
diff --git a/sandbox/win/src/app_container_test.cc b/sandbox/win/src/app_container_test.cc index 1bfab2c..ced5cbd 100644 --- a/sandbox/win/src/app_container_test.cc +++ b/sandbox/win/src/app_container_test.cc @@ -141,4 +141,21 @@ TEST(AppContainerTest, RequiresImpersonation) { runner.GetPolicy()->SetAppContainer(kAppContainerSid)); } +TEST(AppContainerTest, DenyOpenEventForLowBox) { + if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8) + return; + + TestRunner runner(JOB_UNPROTECTED, USER_UNPROTECTED, USER_UNPROTECTED); + + base::win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, L"test")); + ASSERT_TRUE(event.IsValid()); + + EXPECT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetLowBox(kAppContainerSid)); + + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test")); +} + +// TODO(shrikant): Please add some tests to prove usage of lowbox token like +// socket connection to local server in lock down mode. + } // namespace sandbox diff --git a/sandbox/win/src/broker_services.cc b/sandbox/win/src/broker_services.cc index 8fa7f0d..8a29f9b 100644 --- a/sandbox/win/src/broker_services.cc +++ b/sandbox/win/src/broker_services.cc @@ -349,6 +349,9 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, // This downcast is safe as long as we control CreatePolicy() PolicyBase* policy_base = static_cast<PolicyBase*>(policy); + if (policy_base->GetAppContainer() && policy_base->GetLowBoxSid()) + return SBOX_ERROR_BAD_PARAMS; + // Construct the tokens and the job object that we are going to associate // with the soon to be created target process. HANDLE initial_token_temp; @@ -482,6 +485,7 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, thread_pool_); DWORD win_result = target->Create(exe_path, command_line, inherit_handles, + policy_base->GetLowBoxSid() ? true : false, startup_info, &process_info); if (ERROR_SUCCESS != win_result) return SpawnCleanup(target, win_result); diff --git a/sandbox/win/src/nt_internals.h b/sandbox/win/src/nt_internals.h index 2fe27aa..45511a1 100644 --- a/sandbox/win/src/nt_internals.h +++ b/sandbox/win/src/nt_internals.h @@ -656,5 +656,29 @@ typedef NTSTATUS (WINAPI* NtOpenSymbolicLinkObjectFunction) ( #define DIRECTORY_CREATE_SUBDIRECTORY 0x0008 #define DIRECTORY_ALL_ACCESS 0x000F +typedef NTSTATUS (WINAPI* NtCreateLowBoxToken)( + OUT PHANDLE token, + IN HANDLE original_handle, + IN ACCESS_MASK access, + IN POBJECT_ATTRIBUTES object_attribute, + IN PSID appcontainer_sid, + IN DWORD capabilityCount, + IN PSID_AND_ATTRIBUTES capabilities, + IN DWORD handle_count, + IN PHANDLE handles); + +typedef NTSTATUS(WINAPI *NtSetInformationProcess)( + IN HANDLE process_handle, + IN ULONG info_class, + IN PVOID process_information, + IN ULONG information_length); + +struct PROCESS_ACCESS_TOKEN { + HANDLE token; + HANDLE thread; +}; + +const unsigned int NtProcessInformationAccessToken = 9; + #endif // SANDBOX_WIN_SRC_NT_INTERNALS_H__ diff --git a/sandbox/win/src/policy_target.cc b/sandbox/win/src/policy_target.cc index 84b7203..fb464cc 100644 --- a/sandbox/win/src/policy_target.cc +++ b/sandbox/win/src/policy_target.cc @@ -79,16 +79,6 @@ NTSTATUS WINAPI TargetNtSetInformationThread( break; if (ThreadImpersonationToken != thread_info_class) break; - if (!thread_information) - break; - HANDLE token; - if (sizeof(token) > thread_information_bytes) - break; - - NTSTATUS ret = CopyData(&token, thread_information, sizeof(token)); - if (!NT_SUCCESS(ret) || NULL != token) - break; - // This is a revert to self. return STATUS_SUCCESS; } while (false); diff --git a/sandbox/win/src/sandbox_policy.h b/sandbox/win/src/sandbox_policy.h index 22a2049..6f096fb 100644 --- a/sandbox/win/src/sandbox_policy.h +++ b/sandbox/win/src/sandbox_policy.h @@ -183,6 +183,10 @@ class TargetPolicy { // Sets a capability to be enabled for the sandboxed process' AppContainer. virtual ResultCode SetCapability(const wchar_t* sid) = 0; + // Sets the LowBox token for sandboxed process. This is mutually exclusive + // with SetAppContainer method. + virtual ResultCode SetLowBox(const wchar_t* sid) = 0; + // Sets the mitigations enabled when the process is created. Most of these // are implemented as attributes passed via STARTUPINFOEX. So they take // effect before any thread in the target executes. The declaration of diff --git a/sandbox/win/src/sandbox_policy_base.cc b/sandbox/win/src/sandbox_policy_base.cc index d3c920e..f5ed7e4 100644 --- a/sandbox/win/src/sandbox_policy_base.cc +++ b/sandbox/win/src/sandbox_policy_base.cc @@ -98,7 +98,8 @@ PolicyBase::PolicyBase() mitigations_(0), delayed_mitigations_(0), policy_maker_(NULL), - policy_(NULL) { + policy_(NULL), + lowbox_sid_(NULL) { ::InitializeCriticalSection(&lock_); // Initialize the IPC dispatcher array. memset(&ipc_targets_, NULL, sizeof(ipc_targets_)); @@ -152,6 +153,10 @@ PolicyBase::~PolicyBase() { delete ipc_targets_[IPC_DUPLICATEHANDLEPROXY_TAG]; delete policy_maker_; delete policy_; + + if (lowbox_sid_) + ::LocalFree(lowbox_sid_); + ::DeleteCriticalSection(&lock_); } @@ -310,6 +315,10 @@ ResultCode PolicyBase::SetAppContainer(const wchar_t* sid) { if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8) return SBOX_ALL_OK; + // SetLowBox and SetAppContainer are mutually exclusive. + if (lowbox_sid_) + return SBOX_ERROR_UNSUPPORTED; + // Windows refuses to work with an impersonation token for a process inside // an AppContainer. If the caller wants to use a more privileged initial // token, or if the lockdown level will prevent the process from starting, @@ -331,6 +340,25 @@ ResultCode PolicyBase::SetCapability(const wchar_t* sid) { return SBOX_ALL_OK; } +ResultCode PolicyBase::SetLowBox(const wchar_t* sid) { + if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8) + return SBOX_ERROR_UNSUPPORTED; + + // SetLowBox and SetAppContainer are mutually exclusive. + if (appcontainer_list_.get()) + return SBOX_ERROR_UNSUPPORTED; + + DCHECK(sid); + + if (lowbox_sid_) + return SBOX_ERROR_BAD_PARAMS; + + if (!ConvertStringSidToSid(sid, &lowbox_sid_)) + return SBOX_ERROR_GENERIC; + + return SBOX_ALL_OK; +} + ResultCode PolicyBase::SetProcessMitigations( MitigationFlags flags) { if (!CanSetProcessMitigationsPreStartup(flags)) @@ -448,6 +476,11 @@ ResultCode PolicyBase::MakeJobObject(HANDLE* job) { } ResultCode PolicyBase::MakeTokens(HANDLE* initial, HANDLE* lockdown) { + if (appcontainer_list_.get() && appcontainer_list_->HasAppContainer() && + lowbox_sid_) { + return SBOX_ERROR_BAD_PARAMS; + } + // 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(lockdown, lockdown_level_, @@ -476,6 +509,9 @@ ResultCode PolicyBase::MakeTokens(HANDLE* initial, HANDLE* lockdown) { alternate_desktop_integrity_level_label_ = integrity_level_; } + // We are maintaining two mutually exclusive approaches. One is to start an + // AppContainer process through StartupInfoEx and other is replacing + // existing token with LowBox token after process creation. if (appcontainer_list_.get() && appcontainer_list_->HasAppContainer()) { // Windows refuses to work with an impersonation token. See SetAppContainer // implementation for more details. @@ -484,6 +520,21 @@ ResultCode PolicyBase::MakeTokens(HANDLE* initial, HANDLE* lockdown) { *initial = INVALID_HANDLE_VALUE; return SBOX_ALL_OK; + } else if (lowbox_sid_) { + NtCreateLowBoxToken CreateLowBoxToken = NULL; + ResolveNTFunctionPtr("NtCreateLowBoxToken", &CreateLowBoxToken); + OBJECT_ATTRIBUTES obj_attr; + InitializeObjectAttributes(&obj_attr, NULL, 0, NULL, NULL); + HANDLE token_lowbox = NULL; + NTSTATUS status = CreateLowBoxToken(&token_lowbox, *lockdown, + TOKEN_ALL_ACCESS, &obj_attr, + lowbox_sid_, 0, NULL, 0, NULL); + if (!NT_SUCCESS(status)) + return SBOX_ERROR_GENERIC; + + DCHECK(token_lowbox); + ::CloseHandle(*lockdown); + *lockdown = token_lowbox; } // Create the 'better' token. We use this token as the one that the main @@ -505,6 +556,10 @@ const AppContainerAttributes* PolicyBase::GetAppContainer() const { return appcontainer_list_.get(); } +const PSID PolicyBase::GetLowBoxSid() const { + return lowbox_sid_; +} + bool PolicyBase::AddTarget(TargetProcess* target) { if (NULL != policy_) policy_maker_->Done(); diff --git a/sandbox/win/src/sandbox_policy_base.h b/sandbox/win/src/sandbox_policy_base.h index 54b0b0b..ea0f3e6 100644 --- a/sandbox/win/src/sandbox_policy_base.h +++ b/sandbox/win/src/sandbox_policy_base.h @@ -56,6 +56,7 @@ class PolicyBase : public Dispatcher, public TargetPolicy { IntegrityLevel integrity_level) override; virtual ResultCode SetAppContainer(const wchar_t* sid) override; virtual ResultCode SetCapability(const wchar_t* sid) override; + virtual ResultCode SetLowBox(const wchar_t* sid) override; virtual ResultCode SetProcessMitigations(MitigationFlags flags) override; virtual MitigationFlags GetProcessMitigations() override; virtual ResultCode SetDelayedProcessMitigations( @@ -86,6 +87,8 @@ class PolicyBase : public Dispatcher, public TargetPolicy { const AppContainerAttributes* GetAppContainer() const; + const PSID GetLowBoxSid() const; + // Adds a target process to the internal list of targets. Internally a // call to TargetProcess::Init() is issued. bool AddTarget(TargetProcess* target); @@ -158,6 +161,7 @@ class PolicyBase : public Dispatcher, public TargetPolicy { HandleCloser handle_closer_; std::vector<base::string16> capabilities_; scoped_ptr<AppContainerAttributes> appcontainer_list_; + PSID lowbox_sid_; static HDESK alternate_desktop_handle_; static HWINSTA alternate_winstation_handle_; diff --git a/sandbox/win/src/sync_policy_test.cc b/sandbox/win/src/sync_policy_test.cc index 5368943..9fc08f4 100644 --- a/sandbox/win/src/sync_policy_test.cc +++ b/sandbox/win/src/sync_policy_test.cc @@ -29,7 +29,8 @@ SBOX_TESTS_COMMAND int Event_Open(int argc, wchar_t **argv) { return SBOX_TEST_SUCCEEDED; if (ERROR_ACCESS_DENIED == error_open || - ERROR_BAD_PATHNAME == error_open) + ERROR_BAD_PATHNAME == error_open || + ERROR_FILE_NOT_FOUND == error_open) return SBOX_TEST_DENIED; return SBOX_TEST_FAILED; diff --git a/sandbox/win/src/target_process.cc b/sandbox/win/src/target_process.cc index 2c5bf3b..e0284c3 100644 --- a/sandbox/win/src/target_process.cc +++ b/sandbox/win/src/target_process.cc @@ -14,6 +14,7 @@ #include "sandbox/win/src/policy_low_level.h" #include "sandbox/win/src/sandbox_types.h" #include "sandbox/win/src/sharedmem_ipc_server.h" +#include "sandbox/win/src/win_utils.h" namespace { @@ -113,8 +114,15 @@ TargetProcess::~TargetProcess() { DWORD TargetProcess::Create(const wchar_t* exe_path, const wchar_t* command_line, bool inherit_handles, + bool set_lockdown_token_after_create, const base::win::StartupInformation& startup_info, base::win::ScopedProcessInformation* target_info) { + if (set_lockdown_token_after_create && + base::win::GetVersion() < base::win::VERSION_WIN8) { + // We don't allow set_lockdown_token_after_create below Windows 8. + return ERROR_INVALID_PARAMETER; + } + exe_name_.reset(_wcsdup(exe_path)); // the command line needs to be writable by CreateProcess(). @@ -133,22 +141,40 @@ DWORD TargetProcess::Create(const wchar_t* exe_path, flags |= CREATE_BREAKAWAY_FROM_JOB; } + base::win::ScopedHandle scoped_lockdown_token(lockdown_token_.Take()); PROCESS_INFORMATION temp_process_info = {}; - if (!::CreateProcessAsUserW(lockdown_token_.Get(), - exe_path, - cmd_line.get(), - NULL, // No security attribute. - NULL, // No thread attribute. - inherit_handles, - flags, - NULL, // Use the environment of the caller. - NULL, // Use current directory of the caller. - startup_info.startup_info(), - &temp_process_info)) { - return ::GetLastError(); + if (set_lockdown_token_after_create) { + // First create process with a default token and then replace it later, + // after setting primary thread token. This is required for setting + // an AppContainer token along with an impersonation token. + if (!::CreateProcess(exe_path, + cmd_line.get(), + NULL, // No security attribute. + NULL, // No thread attribute. + inherit_handles, + flags, + NULL, // Use the environment of the caller. + NULL, // Use current directory of the caller. + startup_info.startup_info(), + &temp_process_info)) { + return ::GetLastError(); + } + } else { + if (!::CreateProcessAsUserW(scoped_lockdown_token.Get(), + exe_path, + cmd_line.get(), + NULL, // No security attribute. + NULL, // No thread attribute. + inherit_handles, + flags, + NULL, // Use the environment of the caller. + NULL, // Use current directory of the caller. + startup_info.startup_info(), + &temp_process_info)) { + return ::GetLastError(); + } } base::win::ScopedProcessInformation process_info(temp_process_info); - lockdown_token_.Close(); DWORD win_result = ERROR_SUCCESS; @@ -176,6 +202,26 @@ DWORD TargetProcess::Create(const wchar_t* exe_path, initial_token_.Close(); } + if (set_lockdown_token_after_create) { + PROCESS_ACCESS_TOKEN process_access_token; + process_access_token.thread = process_info.thread_handle(); + process_access_token.token = scoped_lockdown_token.Get(); + + NtSetInformationProcess SetInformationProcess = NULL; + ResolveNTFunctionPtr("NtSetInformationProcess", &SetInformationProcess); + + NTSTATUS status = SetInformationProcess( + process_info.process_handle(), + static_cast<PROCESS_INFORMATION_CLASS>(NtProcessInformationAccessToken), + &process_access_token, + sizeof(process_access_token)); + if (!NT_SUCCESS(status)) { + win_result = ::GetLastError(); + ::TerminateProcess(process_info.process_handle(), 0); // exit code + return win_result; + } + } + CONTEXT context; context.ContextFlags = CONTEXT_ALL; if (!::GetThreadContext(process_info.thread_handle(), &context)) { diff --git a/sandbox/win/src/target_process.h b/sandbox/win/src/target_process.h index 9a8dded..cf5ad9f 100644 --- a/sandbox/win/src/target_process.h +++ b/sandbox/win/src/target_process.h @@ -45,9 +45,12 @@ class TargetProcess { void Release() {} // Creates the new target process. The process is created suspended. + // When |set_lockdown_token_after_create| is set, the lockdown token + // is replaced after the process is created DWORD Create(const wchar_t* exe_path, const wchar_t* command_line, bool inherit_handles, + bool set_lockdown_token_after_create, const base::win::StartupInformation& startup_info, base::win::ScopedProcessInformation* target_info); |