This source file includes following definitions.
- CreateRestrictedToken
- CreateWindowStationAndDesktop
- target_command_
- LaunchProcess
- Send
- CloseChannel
- KillProcess
- OnMessageReceived
- OnChannelConnected
- OnChannelError
- ReportFatalError
- ReportProcessLaunched
#include "remoting/host/win/unprivileged_process_delegate.h"
#include <sddl.h>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string16.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
#include "ipc/ipc_channel.h"
#include "ipc/ipc_channel_proxy.h"
#include "ipc/ipc_message.h"
#include "remoting/base/typed_buffer.h"
#include "remoting/host/ipc_constants.h"
#include "remoting/host/ipc_util.h"
#include "remoting/host/win/launch_process_with_token.h"
#include "remoting/host/win/security_descriptor.h"
#include "remoting/host/win/window_station_and_desktop.h"
#include "sandbox/win/src/restricted_token.h"
using base::win::ScopedHandle;
namespace remoting {
namespace {
const char kDesktopSdFormat[] = "O:SYG:SYD:(A;;0xf01ff;;;SY)(A;;0xf01ff;;;%s)";
const char kLowIntegrityMandatoryLabel[] = "S:(ML;CIOI;NW;;;LW)";
const char kWindowStationSdFormat[] = "O:SYG:SYD:(A;CIOIIO;GA;;;SY)"
"(A;CIOIIO;GA;;;%1$s)(A;NP;0xf037f;;;SY)(A;NP;0xf037f;;;%1$s)";
const char kWorkerProcessSd[] = "O:SYG:SYD:(A;;GA;;;SY)(A;;0x120401;;;BA)";
const char kWorkerThreadSd[] = "O:SYG:SYD:(A;;GA;;;SY)(A;;0x120801;;;BA)";
bool CreateRestrictedToken(ScopedHandle* token_out) {
HANDLE temp_handle;
if (!LogonUser(L"LocalService", L"NT AUTHORITY", NULL, LOGON32_LOGON_SERVICE,
LOGON32_PROVIDER_DEFAULT, &temp_handle)) {
return false;
}
ScopedHandle token(temp_handle);
sandbox::RestrictedToken restricted_token;
if (restricted_token.Init(token) != ERROR_SUCCESS)
return false;
if (restricted_token.DeleteAllPrivileges(NULL) != ERROR_SUCCESS)
return false;
if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
if (restricted_token.SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW)
!= ERROR_SUCCESS) {
return false;
}
}
if (restricted_token.GetRestrictedTokenHandle(&temp_handle) ==
ERROR_SUCCESS) {
token_out->Set(temp_handle);
return true;
}
return false;
}
bool CreateWindowStationAndDesktop(ScopedSid logon_sid,
WindowStationAndDesktop* handles_out) {
std::string logon_sid_string = ConvertSidToString(logon_sid.get());
if (logon_sid_string.empty()) {
LOG_GETLASTERROR(ERROR) << "Failed to convert a SID to string";
return false;
}
std::string desktop_sddl =
base::StringPrintf(kDesktopSdFormat, logon_sid_string.c_str());
std::string window_station_sddl =
base::StringPrintf(kWindowStationSdFormat, logon_sid_string.c_str());
if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
desktop_sddl += kLowIntegrityMandatoryLabel;
window_station_sddl += kLowIntegrityMandatoryLabel;
}
ScopedSd desktop_sd = ConvertSddlToSd(desktop_sddl);
ScopedSd window_station_sd = ConvertSddlToSd(window_station_sddl);
if (!desktop_sd || !window_station_sd) {
LOG_GETLASTERROR(ERROR) << "Failed to create a security descriptor.";
return false;
}
HWINSTA current_window_station = GetProcessWindowStation();
std::string window_station_name = base::StringPrintf(
"chromoting-%d-%d",
base::GetCurrentProcId(),
base::RandInt(1, std::numeric_limits<int>::max()));
DWORD window_station_flags = 0;
if (base::win::GetVersion() >= base::win::VERSION_VISTA)
window_station_flags = CWF_CREATE_ONLY;
DWORD desired_access =
WINSTA_ALL_ACCESS | DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER;
SECURITY_ATTRIBUTES security_attributes = {0};
security_attributes.nLength = sizeof(security_attributes);
security_attributes.lpSecurityDescriptor = window_station_sd.get();
security_attributes.bInheritHandle = TRUE;
WindowStationAndDesktop handles;
handles.SetWindowStation(CreateWindowStation(
base::UTF8ToUTF16(window_station_name).c_str(), window_station_flags,
desired_access, &security_attributes));
if (!handles.window_station()) {
LOG_GETLASTERROR(ERROR) << "CreateWindowStation() failed";
return false;
}
if (!SetProcessWindowStation(handles.window_station())) {
LOG_GETLASTERROR(ERROR) << "SetProcessWindowStation() failed";
return false;
}
desired_access = DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW |
DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | DESKTOP_JOURNALRECORD |
DESKTOP_JOURNALPLAYBACK | DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS |
DESKTOP_SWITCHDESKTOP | DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER;
security_attributes.nLength = sizeof(security_attributes);
security_attributes.lpSecurityDescriptor = desktop_sd.get();
security_attributes.bInheritHandle = TRUE;
handles.SetDesktop(CreateDesktop(L"Default", NULL, NULL, 0, desired_access,
&security_attributes));
if (!SetProcessWindowStation(current_window_station)) {
LOG_GETLASTERROR(ERROR) << "SetProcessWindowStation() failed";
return false;
}
if (!handles.desktop()) {
LOG_GETLASTERROR(ERROR) << "CreateDesktop() failed";
return false;
}
handles.Swap(*handles_out);
return true;
}
}
UnprivilegedProcessDelegate::UnprivilegedProcessDelegate(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
scoped_ptr<CommandLine> target_command)
: io_task_runner_(io_task_runner),
event_handler_(NULL),
target_command_(target_command.Pass()) {
}
UnprivilegedProcessDelegate::~UnprivilegedProcessDelegate() {
DCHECK(CalledOnValidThread());
DCHECK(!channel_);
DCHECK(!worker_process_.IsValid());
}
void UnprivilegedProcessDelegate::LaunchProcess(
WorkerProcessLauncher* event_handler) {
DCHECK(CalledOnValidThread());
DCHECK(!event_handler_);
event_handler_ = event_handler;
scoped_ptr<IPC::ChannelProxy> server;
ScopedHandle token;
if (!CreateRestrictedToken(&token)) {
LOG_GETLASTERROR(ERROR)
<< "Failed to create a restricted LocalService token";
ReportFatalError();
return;
}
ScopedSid logon_sid = GetLogonSid(token);
if (!logon_sid) {
LOG_GETLASTERROR(ERROR) << "Failed to retrieve the logon SID";
ReportFatalError();
return;
}
ScopedSd process_sd = ConvertSddlToSd(kWorkerProcessSd);
ScopedSd thread_sd = ConvertSddlToSd(kWorkerThreadSd);
if (!process_sd || !thread_sd) {
LOG_GETLASTERROR(ERROR) << "Failed to create a security descriptor";
ReportFatalError();
return;
}
SECURITY_ATTRIBUTES process_attributes;
process_attributes.nLength = sizeof(process_attributes);
process_attributes.lpSecurityDescriptor = process_sd.get();
process_attributes.bInheritHandle = FALSE;
SECURITY_ATTRIBUTES thread_attributes;
thread_attributes.nLength = sizeof(thread_attributes);
thread_attributes.lpSecurityDescriptor = thread_sd.get();
thread_attributes.bInheritHandle = FALSE;
ScopedHandle worker_process;
{
base::AutoLock lock(g_inherit_handles_lock.Get());
HANDLE temp_handle;
if (!CreateConnectedIpcChannel(io_task_runner_, this, &temp_handle,
&server)) {
ReportFatalError();
return;
}
ScopedHandle client(temp_handle);
std::string pipe_handle = base::StringPrintf(
"%d", reinterpret_cast<ULONG_PTR>(client.Get()));
CommandLine command_line(target_command_->argv());
command_line.AppendSwitchASCII(kDaemonPipeSwitchName, pipe_handle);
WindowStationAndDesktop handles;
if (!CreateWindowStationAndDesktop(logon_sid.Pass(), &handles)) {
LOG_GETLASTERROR(ERROR)
<< "Failed to create a window station and desktop";
ReportFatalError();
return;
}
ScopedHandle worker_thread;
if (!LaunchProcessWithToken(command_line.GetProgram(),
command_line.GetCommandLineString(),
token,
&process_attributes,
&thread_attributes,
true,
0,
NULL,
&worker_process,
&worker_thread)) {
ReportFatalError();
return;
}
}
channel_ = server.Pass();
ReportProcessLaunched(worker_process.Pass());
}
void UnprivilegedProcessDelegate::Send(IPC::Message* message) {
DCHECK(CalledOnValidThread());
if (channel_) {
channel_->Send(message);
} else {
delete message;
}
}
void UnprivilegedProcessDelegate::CloseChannel() {
DCHECK(CalledOnValidThread());
channel_.reset();
}
void UnprivilegedProcessDelegate::KillProcess() {
DCHECK(CalledOnValidThread());
channel_.reset();
event_handler_ = NULL;
if (worker_process_.IsValid()) {
TerminateProcess(worker_process_, CONTROL_C_EXIT);
worker_process_.Close();
}
}
bool UnprivilegedProcessDelegate::OnMessageReceived(
const IPC::Message& message) {
DCHECK(CalledOnValidThread());
return event_handler_->OnMessageReceived(message);
}
void UnprivilegedProcessDelegate::OnChannelConnected(int32 peer_pid) {
DCHECK(CalledOnValidThread());
DWORD pid = GetProcessId(worker_process_);
if (pid != static_cast<DWORD>(peer_pid)) {
LOG(ERROR) << "The actual client PID " << pid
<< " does not match the one reported by the client: "
<< peer_pid;
ReportFatalError();
return;
}
event_handler_->OnChannelConnected(peer_pid);
}
void UnprivilegedProcessDelegate::OnChannelError() {
DCHECK(CalledOnValidThread());
event_handler_->OnChannelError();
}
void UnprivilegedProcessDelegate::ReportFatalError() {
DCHECK(CalledOnValidThread());
channel_.reset();
WorkerProcessLauncher* event_handler = event_handler_;
event_handler_ = NULL;
event_handler->OnFatalError();
}
void UnprivilegedProcessDelegate::ReportProcessLaunched(
base::win::ScopedHandle worker_process) {
DCHECK(CalledOnValidThread());
DCHECK(!worker_process_.IsValid());
worker_process_ = worker_process.Pass();
DWORD desired_access =
SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION;
HANDLE temp_handle;
if (!DuplicateHandle(GetCurrentProcess(),
worker_process_,
GetCurrentProcess(),
&temp_handle,
desired_access,
FALSE,
0)) {
LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
ReportFatalError();
return;
}
ScopedHandle limited_handle(temp_handle);
event_handler_->OnProcessLaunched(limited_handle.Pass());
}
}