1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "remoting/host/win/launch_native_messaging_host_process.h"
6 
7 #include <windows.h>
8 #include <shellapi.h>
9 
10 #include <cstdint>
11 #include <string>
12 
13 #include "base/command_line.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/logging.h"
17 #include "base/macros.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/win/win_util.h"
21 #include "ipc/ipc_channel.h"
22 #include "remoting/host/switches.h"
23 #include "remoting/host/win/security_descriptor.h"
24 
25 namespace {
26 
27 // Windows will use default buffer size when 0 is passed to CreateNamedPipeW().
28 const uint32_t kBufferSize = 0;
29 const int kTimeOutMilliseconds = 2000;
30 const char kChromePipeNamePrefix[] = "\\\\.\\pipe\\chrome_remote_desktop.";
31 
CreateNamedPipe(const std::string & pipe_name,const remoting::ScopedSd & security_descriptor,uint32_t open_mode,base::win::ScopedHandle * file_handle)32 uint32_t CreateNamedPipe(const std::string& pipe_name,
33                          const remoting::ScopedSd& security_descriptor,
34                          uint32_t open_mode,
35                          base::win::ScopedHandle* file_handle) {
36   DCHECK(file_handle);
37 
38   SECURITY_ATTRIBUTES security_attributes = {0};
39   security_attributes.nLength = sizeof(security_attributes);
40   security_attributes.lpSecurityDescriptor = security_descriptor.get();
41   security_attributes.bInheritHandle = FALSE;
42 
43   base::win::ScopedHandle temp_handle(::CreateNamedPipe(
44       base::ASCIIToUTF16(pipe_name).c_str(), open_mode,
45       PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS, 1,
46       kBufferSize, kBufferSize, kTimeOutMilliseconds, &security_attributes));
47 
48   if (!temp_handle.IsValid()) {
49     uint32_t error = GetLastError();
50     PLOG(ERROR) << "Failed to create named pipe '" << pipe_name << "'";
51     return error;
52   }
53 
54   file_handle->Set(temp_handle.Take());
55   return 0;
56 }
57 
58 }  // namespace
59 
60 namespace remoting {
61 
LaunchNativeMessagingHostProcess(const base::FilePath & binary_path,intptr_t parent_window_handle,bool elevate_process,base::win::ScopedHandle * read_handle,base::win::ScopedHandle * write_handle)62 ProcessLaunchResult LaunchNativeMessagingHostProcess(
63     const base::FilePath& binary_path,
64     intptr_t parent_window_handle,
65     bool elevate_process,
66     base::win::ScopedHandle* read_handle,
67     base::win::ScopedHandle* write_handle) {
68   DCHECK(read_handle);
69   DCHECK(write_handle);
70 
71   if (!base::PathExists(binary_path)) {
72     LOG(ERROR) << "Cannot find binary: " << binary_path.value();
73     return PROCESS_LAUNCH_RESULT_FAILED;
74   }
75 
76   // presubmit: allow wstring
77   std::wstring user_sid;
78   if (!base::win::GetUserSidString(&user_sid)) {
79     LOG(ERROR) << "Failed to query the current user SID.";
80     return PROCESS_LAUNCH_RESULT_FAILED;
81   }
82 
83   // Create a security descriptor that gives full access to the caller and
84   // BUILTIN_ADMINISTRATORS and denies access by anyone else.
85   // Local admins need access because the privileged host process will run
86   // as a local admin which may not be the same user as the current user.
87   std::string user_sid_ascii = base::UTF16ToASCII(user_sid);
88   std::string security_descriptor = base::StringPrintf(
89       "O:%sG:%sD:(A;;GA;;;%s)(A;;GA;;;BA)", user_sid_ascii.c_str(),
90       user_sid_ascii.c_str(), user_sid_ascii.c_str());
91 
92   ScopedSd sd = ConvertSddlToSd(security_descriptor);
93   if (!sd) {
94     PLOG(ERROR) << "Failed to create a security descriptor.";
95     return PROCESS_LAUNCH_RESULT_FAILED;
96   }
97 
98   uint32_t result;
99   std::string input_pipe_name(kChromePipeNamePrefix);
100   input_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
101   base::win::ScopedHandle temp_write_handle;
102   result = CreateNamedPipe(input_pipe_name, sd, PIPE_ACCESS_OUTBOUND,
103                            &temp_write_handle);
104   if (!temp_write_handle.IsValid()) {
105     return PROCESS_LAUNCH_RESULT_FAILED;
106   }
107 
108   std::string output_pipe_name(kChromePipeNamePrefix);
109   output_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
110   base::win::ScopedHandle temp_read_handle;
111   result = CreateNamedPipe(output_pipe_name, sd, PIPE_ACCESS_INBOUND,
112                            &temp_read_handle);
113   if (!temp_read_handle.IsValid()) {
114     return PROCESS_LAUNCH_RESULT_FAILED;
115   }
116 
117   const base::CommandLine* current_command_line =
118       base::CommandLine::ForCurrentProcess();
119 
120   // Create the child process command line by copying switches from the current
121   // command line.
122   base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
123 
124   DCHECK(!current_command_line->HasSwitch(kElevateSwitchName));
125   // Always add the elevation switch when launched via this function.
126   command_line.AppendSwitch(kElevateSwitchName);
127   command_line.AppendSwitchASCII(kInputSwitchName, input_pipe_name);
128   command_line.AppendSwitchASCII(kOutputSwitchName, output_pipe_name);
129 
130   for (const auto& switch_data : current_command_line->GetSwitches()) {
131     command_line.AppendSwitchNative(switch_data.first, switch_data.second);
132   }
133   for (const auto& arg : current_command_line->GetArgs()) {
134     command_line.AppendArgNative(arg);
135   }
136 
137   // Get the parameters for the binary to launch.
138   base::CommandLine::StringType params = command_line.GetCommandLineString();
139 
140   // Launch the child process, requesting elevation if needed.
141   SHELLEXECUTEINFO info;
142   memset(&info, 0, sizeof(info));
143   info.cbSize = sizeof(info);
144   info.hwnd = reinterpret_cast<HWND>(parent_window_handle);
145   info.lpFile = binary_path.value().c_str();
146   info.lpParameters = params.c_str();
147   info.nShow = SW_HIDE;
148 
149   if (elevate_process) {
150     info.lpVerb = L"runas";
151   }
152 
153   if (!ShellExecuteEx(&info)) {
154     uint32_t error = GetLastError();
155     PLOG(ERROR) << "Unable to launch '" << binary_path.value() << "'";
156     if (error == ERROR_CANCELLED) {
157       return PROCESS_LAUNCH_RESULT_CANCELLED;
158     } else {
159       return PROCESS_LAUNCH_RESULT_FAILED;
160     }
161   }
162 
163   if (!ConnectNamedPipe(temp_write_handle.Get(), nullptr)) {
164     uint32_t error = GetLastError();
165     if (error != ERROR_PIPE_CONNECTED) {
166       PLOG(ERROR) << "Unable to connect '" << output_pipe_name << "'";
167       return PROCESS_LAUNCH_RESULT_FAILED;
168     }
169   }
170 
171   if (!ConnectNamedPipe(temp_read_handle.Get(), nullptr)) {
172     uint32_t error = GetLastError();
173     if (error != ERROR_PIPE_CONNECTED) {
174       PLOG(ERROR) << "Unable to connect '" << input_pipe_name << "'";
175       return PROCESS_LAUNCH_RESULT_FAILED;
176     }
177   }
178 
179   read_handle->Set(temp_read_handle.Take());
180   write_handle->Set(temp_write_handle.Take());
181   return PROCESS_LAUNCH_RESULT_SUCCESS;
182 }
183 
184 }  // namespace remoting
185