File: launch_native_messaging_host_process.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (183 lines) | stat: -rw-r--r-- 6,233 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/host/win/launch_native_messaging_host_process.h"

#include <windows.h>

#include <shellapi.h>

#include <cstdint>
#include <string>

#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/win_util.h"
#include "ipc/ipc_channel.h"
#include "remoting/host/base/switches.h"
#include "remoting/host/win/security_descriptor.h"

namespace {

// Windows will use default buffer size when 0 is passed to CreateNamedPipeW().
const uint32_t kBufferSize = 0;
const int kTimeOutMilliseconds = 2000;
const char kChromePipeNamePrefix[] = "\\\\.\\pipe\\chrome_remote_desktop.";

uint32_t CreateNamedPipe(const std::string& pipe_name,
                         remoting::ScopedSd& security_descriptor,
                         uint32_t open_mode,
                         base::win::ScopedHandle* file_handle) {
  DCHECK(file_handle);

  SECURITY_ATTRIBUTES security_attributes = {
      .nLength = sizeof(security_attributes),
      .lpSecurityDescriptor = security_descriptor.get(),
      .bInheritHandle = FALSE,
  };

  base::win::ScopedHandle temp_handle(::CreateNamedPipe(
      base::ASCIIToWide(pipe_name).c_str(), open_mode,
      PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS, 1,
      kBufferSize, kBufferSize, kTimeOutMilliseconds, &security_attributes));

  if (!temp_handle.IsValid()) {
    uint32_t error = GetLastError();
    PLOG(ERROR) << "Failed to create named pipe '" << pipe_name << "'";
    return error;
  }

  file_handle->Set(temp_handle.Take());
  return 0;
}

}  // namespace

namespace remoting {

ProcessLaunchResult LaunchNativeMessagingHostProcess(
    const base::FilePath& binary_path,
    intptr_t parent_window_handle,
    bool elevate_process,
    base::win::ScopedHandle* read_handle,
    base::win::ScopedHandle* write_handle) {
  DCHECK(read_handle);
  DCHECK(write_handle);

  if (!base::PathExists(binary_path)) {
    LOG(ERROR) << "Cannot find binary: " << binary_path.value();
    return PROCESS_LAUNCH_RESULT_FAILED;
  }

  // presubmit: allow wstring
  std::wstring user_sid;
  if (!base::win::GetUserSidString(&user_sid)) {
    LOG(ERROR) << "Failed to query the current user SID.";
    return PROCESS_LAUNCH_RESULT_FAILED;
  }

  // Create a security descriptor that gives full access to the caller and
  // BUILTIN_ADMINISTRATORS and denies access by anyone else.
  // Local admins need access because the privileged host process will run
  // as a local admin which may not be the same user as the current user.
  std::string security_descriptor =
      base::StringPrintf("O:%lsG:%lsD:(A;;GA;;;%ls)(A;;GA;;;BA)",
                         user_sid.c_str(), user_sid.c_str(), user_sid.c_str());

  ScopedSd sd = ConvertSddlToSd(security_descriptor);
  if (!sd) {
    PLOG(ERROR) << "Failed to create a security descriptor.";
    return PROCESS_LAUNCH_RESULT_FAILED;
  }

  std::string input_pipe_name(kChromePipeNamePrefix);
  input_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
  base::win::ScopedHandle temp_write_handle;
  CreateNamedPipe(input_pipe_name, sd, PIPE_ACCESS_OUTBOUND,
                  &temp_write_handle);
  if (!temp_write_handle.IsValid()) {
    return PROCESS_LAUNCH_RESULT_FAILED;
  }

  std::string output_pipe_name(kChromePipeNamePrefix);
  output_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
  base::win::ScopedHandle temp_read_handle;
  CreateNamedPipe(output_pipe_name, sd, PIPE_ACCESS_INBOUND, &temp_read_handle);
  if (!temp_read_handle.IsValid()) {
    return PROCESS_LAUNCH_RESULT_FAILED;
  }

  const base::CommandLine* current_command_line =
      base::CommandLine::ForCurrentProcess();

  // Create the child process command line by copying switches from the current
  // command line.
  base::CommandLine command_line(base::CommandLine::NO_PROGRAM);

  DCHECK(!current_command_line->HasSwitch(kElevateSwitchName));
  // Always add the elevation switch when launched via this function.
  command_line.AppendSwitch(kElevateSwitchName);
  command_line.AppendSwitchASCII(kInputSwitchName, input_pipe_name);
  command_line.AppendSwitchASCII(kOutputSwitchName, output_pipe_name);

  for (const auto& switch_data : current_command_line->GetSwitches()) {
    command_line.AppendSwitchNative(switch_data.first, switch_data.second);
  }
  for (const auto& arg : current_command_line->GetArgs()) {
    command_line.AppendArgNative(arg);
  }

  // Get the parameters for the binary to launch.
  base::CommandLine::StringType params = command_line.GetCommandLineString();

  // Launch the child process, requesting elevation if needed.
  SHELLEXECUTEINFO info;
  UNSAFE_TODO(memset(&info, 0, sizeof(info)));
  info.cbSize = sizeof(info);
  info.hwnd = reinterpret_cast<HWND>(parent_window_handle);
  info.lpFile = binary_path.value().c_str();
  info.lpParameters = params.c_str();
  info.nShow = SW_HIDE;

  if (elevate_process) {
    info.lpVerb = L"runas";
  }

  if (!ShellExecuteEx(&info)) {
    uint32_t error = GetLastError();
    PLOG(ERROR) << "Unable to launch '" << binary_path.value() << "'";
    if (error == ERROR_CANCELLED) {
      return PROCESS_LAUNCH_RESULT_CANCELLED;
    } else {
      return PROCESS_LAUNCH_RESULT_FAILED;
    }
  }

  if (!ConnectNamedPipe(temp_write_handle.Get(), nullptr)) {
    uint32_t error = GetLastError();
    if (error != ERROR_PIPE_CONNECTED) {
      PLOG(ERROR) << "Unable to connect '" << output_pipe_name << "'";
      return PROCESS_LAUNCH_RESULT_FAILED;
    }
  }

  if (!ConnectNamedPipe(temp_read_handle.Get(), nullptr)) {
    uint32_t error = GetLastError();
    if (error != ERROR_PIPE_CONNECTED) {
      PLOG(ERROR) << "Unable to connect '" << input_pipe_name << "'";
      return PROCESS_LAUNCH_RESULT_FAILED;
    }
  }

  read_handle->Set(temp_read_handle.Take());
  write_handle->Set(temp_write_handle.Take());
  return PROCESS_LAUNCH_RESULT_SUCCESS;
}

}  // namespace remoting