File: start_host_main.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 (506 lines) | stat: -rw-r--r-- 17,988 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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/390223051): Remove C-library calls to fix the errors.
#pragma allow_unsafe_libc_calls
#endif

#include "remoting/host/setup/start_host_main.h"

#include <stddef.h>
#include <stdio.h>

#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/message_loop/message_pump_type.h"
#include "base/notreached.h"
#include "base/run_loop.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/task/single_thread_task_executor.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "mojo/core/embedder/embedder.h"
#include "net/ssl/client_cert_store.h"
#include "net/url_request/url_request_context_getter.h"
#include "remoting/base/certificate_helpers.h"
#include "remoting/base/logging.h"
#include "remoting/base/url_request_context_getter.h"
#include "remoting/host/setup/cloud_host_starter.h"
#include "remoting/host/setup/corp_host_starter.h"
#include "remoting/host/setup/host_starter.h"
#include "remoting/host/setup/oauth_host_starter.h"
#include "remoting/host/setup/pin_validator.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/transitional_url_loader_factory_owner.h"

#if BUILDFLAG(IS_POSIX)
#include <termios.h>
#include <unistd.h>
#endif  // BUILDFLAG(IS_POSIX)

#if BUILDFLAG(IS_LINUX)
#include "remoting/base/crash/crash_reporting_crashpad.h"
#include "remoting/host/setup/daemon_controller_delegate_linux.h"
#include "remoting/host/setup/start_host_as_root.h"
#endif  // BUILDFLAG(IS_LINUX)

#if BUILDFLAG(IS_WIN)
#include <windows.h>

#include "base/process/process_info.h"
#include "remoting/base/crash/crash_reporting_breakpad.h"
#endif  // BUILDFLAG(IS_WIN)

namespace remoting {

namespace {

// Flags for registering the host and generating the host config.
constexpr char kPinSwitchName[] = "pin";
constexpr char kAuthCodeSwitchName[] = "code";
constexpr char kRedirectUrlSwitchName[] = "redirect-url";
// If set, this flag is used to compare against the email address of the user
// who generated the OAuth authorization code.
constexpr char kHostOwnerSwitchName[] = "host-owner";

// Specifies the username for the account to associate with this instance when
// using the Corp registration process.
constexpr char kCorpUserSwitchName[] = "corp-user";

// Specifies the account email to be used when configuring a machine using the
// Cloud registration process.
constexpr char kCloudUserSwitchName[] = "cloud-user";
// Specifies the API_KEY to use when registering the cloud host instance.
constexpr char kCloudApiKeySwitchName[] = "cloud-api-key";

// TODO: joedow - switch to using `display-name` for consistency. Remove `name`
// after we no longer need to support start_host for Pre-M125 packages.
constexpr char kNameSwitchName[] = "name";
constexpr char kDisplayNameSwitchName[] = "display-name";

// Used to disable crash reporting.
constexpr char kDisableCrashReportingSwitchName[] = "disable-crash-reporting";

constexpr char kInvalidPinErrorMessage[] =
    "Please provide a numeric PIN consisting of at least six digits.\n";

// True if the host was started successfully.
bool g_started = false;

base::SingleThreadTaskExecutor* g_main_thread_task_executor = nullptr;

// The active RunLoop.
base::RunLoop* g_active_run_loop = nullptr;

void PrintDefaultHelpMessage(const char* process_name) {
  // Optional args are shown first as the most common issue is needing to
  // generate the auth-code again and this ordering makes it easy to fix the
  // command line to rerun the tool.
  fprintf(stderr,
          "Please visit https://remotedesktop.google.com/headless for "
          "instructions on running this tool and help generating the command "
          "line arguments.\n"
          "\n"
          "Example usage:\n%s --%s=<auth code> --%s=<redirect url> "
          "[--%s=<host display name>] [--%s=<6+ digit PIN>] [--%s]\n",
          process_name, kAuthCodeSwitchName, kRedirectUrlSwitchName,
          kDisplayNameSwitchName, kPinSwitchName,
          kDisableCrashReportingSwitchName);
}

void PrintCorpUserHelpMessage(const char* process_name) {
  fprintf(stdout,
          "Setting up a machine for a corp user requires the username of that "
          "user and an optional display name.\n\nExample usage:\n"
          "%s --%s=<username> [--%s=corp-machine-name]\n",
          process_name, kCorpUserSwitchName, kDisplayNameSwitchName);
}

void PrintCloudUserHelpMessage(const char* process_name) {
  // TODO: joedow - Add a link to public documentation and/or samples when they
  // are available.
  fprintf(stdout,
          "Setting up a Compute Engine Instance requires the email address of "
          "the user.\n\nAn optional API_KEY, created for the project the "
          "Compute Engine Instance is in, can be provided. Otherwise an access "
          "token will be retrieved for the default service account.\n\nAn "
          "optional display name can also be provided, otherwise the hostname, "
          "or FQDN, of the instance will be used.\n\n"
          "Example usage:\n%s --%s=<user_email_address> [--%s=<API_KEY>] "
          "[--%s=cloud-instance-display-name] [--%s]\n",
          process_name, kCloudUserSwitchName, kCloudApiKeySwitchName,
          kDisplayNameSwitchName, kDisableCrashReportingSwitchName);
}

// Lets us hide the PIN that a user types.
void SetEcho(bool echo) {
#if BUILDFLAG(IS_WIN)
  DWORD mode;
  HANDLE console_handle = GetStdHandle(STD_INPUT_HANDLE);
  if (!GetConsoleMode(console_handle, &mode)) {
    LOG(ERROR) << "GetConsoleMode failed";
    return;
  }
  SetConsoleMode(console_handle,
                 (mode & ~ENABLE_ECHO_INPUT) | (echo ? ENABLE_ECHO_INPUT : 0));
#else
  termios term;
  tcgetattr(STDIN_FILENO, &term);
  if (echo) {
    term.c_lflag |= ECHO;
  } else {
    term.c_lflag &= ~ECHO;
  }
  tcsetattr(STDIN_FILENO, TCSANOW, &term);
#endif  // !BUILDFLAG(IS_WIN)
}

// Reads a newline-terminated string from stdin.
std::string ReadString(bool no_echo) {
  if (no_echo) {
    SetEcho(false);
  }
  const int kMaxLen = 1024;
  std::string str(kMaxLen, 0);
  char* result = fgets(&str[0], kMaxLen, stdin);
  if (no_echo) {
    printf("\n");
    SetEcho(true);
  }
  if (!result) {
    return std::string();
  }
  size_t newline_index = str.find('\n');
  if (newline_index != std::string::npos) {
    str[newline_index] = '\0';
  }
  str.resize(strlen(&str[0]));
  return str;
}

// Called when the HostStarter has finished.
void OnDone(HostStarter::Result result) {
  if (!g_main_thread_task_executor->task_runner()->BelongsToCurrentThread()) {
    g_main_thread_task_executor->task_runner()->PostTask(
        FROM_HERE, base::BindOnce(&OnDone, result));
    return;
  }
  switch (result) {
    case HostStarter::START_COMPLETE:
      g_started = true;
      printf("Host started successfully.\n");
      break;
    case HostStarter::NETWORK_ERROR:
      fprintf(stderr, "Couldn't start host: network error.\n");
      break;
    case HostStarter::OAUTH_ERROR:
      fprintf(stderr, "Couldn't start host: OAuth error.\n");
      break;
    case HostStarter::PERMISSION_DENIED:
      fprintf(stderr, "Couldn't start host: Permission denied.\n");
      break;
    case HostStarter::REGISTRATION_ERROR:
      fprintf(stderr, "Couldn't start host: Registration error.\n");
      break;
    case HostStarter::START_ERROR:
      fprintf(stderr, "Couldn't start host.\n");
      break;
  }

  g_active_run_loop->Quit();
}

bool InitializeParamsForOAuthFlow(HostStarter::Params& params,
                                  const base::CommandLine* command_line) {
  if (command_line->HasSwitch("host-id")) {
    // This is an undocumented parameter as it was added for a scenario that is
    // partially supported but infrequently used.
    // TODO: joedow - Remove this param after switching to the Corp workflow.
    params.id = command_line->GetSwitchValueASCII("host-id");
  }
  params.name = command_line->GetSwitchValueASCII(kDisplayNameSwitchName);
  if (params.name.empty()) {
    // Fallback to the 'name' switch if it was provided instead. We want to
    // support this as some folks have documented the usage of this tool and
    // refer to the old flag name.
    params.name = command_line->GetSwitchValueASCII(kNameSwitchName);
  }
  params.pin = command_line->GetSwitchValueASCII(kPinSwitchName);
  params.auth_code = command_line->GetSwitchValueASCII(kAuthCodeSwitchName);
  params.redirect_url =
      command_line->GetSwitchValueASCII(kRedirectUrlSwitchName);
  params.owner_email = base::ToLowerASCII(
      command_line->GetSwitchValueASCII(kHostOwnerSwitchName));
  params.enable_crash_reporting =
      !command_line->HasSwitch(kDisableCrashReportingSwitchName);

  if (params.auth_code.empty() || params.redirect_url.empty()) {
    return false;
  }

  if (params.name.empty()) {
    fprintf(stdout, "Enter a name for this computer: ");
    fflush(stdout);
    params.name = ReadString(false);
  }

  if (params.pin.empty()) {
    while (true) {
      fprintf(stdout, "Enter a PIN of at least six digits: ");
      fflush(stdout);
      params.pin = ReadString(true);
      if (!remoting::IsPinValid(params.pin)) {
        fprintf(stdout, kInvalidPinErrorMessage);
        fflush(stdout);
        continue;
      }
      std::string pin_confirmation;
      fprintf(stdout, "Enter the same PIN again: ");
      fflush(stdout);
      pin_confirmation = ReadString(true);
      if (params.pin != pin_confirmation) {
        fprintf(stdout, "You entered different PINs.\n");
        fflush(stdout);
        continue;
      }
      break;
    }
  } else {
    if (!remoting::IsPinValid(params.pin)) {
      fprintf(stderr, kInvalidPinErrorMessage);
      return false;
    }
  }

  return true;
}

bool InitializeCorpMachineParams(HostStarter::Params& params,
                                 const base::CommandLine* command_line) {
  // Crash reporting is always enabled for this flow.
  params.enable_crash_reporting = true;

  // Count the number of args provided so we can show a helpful error message
  // if the user provides an unexpected value.
  size_t corp_arg_count = 1;

  // Some legacy scripts may still provide an email domain for this parameter
  // however the username is the preferred value when calling the Directory
  // service. If we are given an email, strip the domain and treat it like a
  // username.
  std::string corp_user_value = base::ToLowerASCII(
      command_line->GetSwitchValueASCII(kCorpUserSwitchName));
  auto parts = base::SplitStringOnce(corp_user_value, '@');
  if (!parts) {
    params.username = std::move(corp_user_value);
  } else {
    params.username = std::move(parts->first);
  }

  // Allow user to specify a display name.
  if (command_line->HasSwitch(kDisplayNameSwitchName)) {
    corp_arg_count++;
    params.name = command_line->GetSwitchValueASCII(kDisplayNameSwitchName);
  }

  // Allow debugging switches.
  if (command_line->HasSwitch("v")) {
    corp_arg_count++;
  }
  if (command_line->HasSwitch("vmodule")) {
    corp_arg_count++;
  }

  if (command_line->GetSwitches().size() > corp_arg_count) {
    return false;
  }

  return true;
}

bool InitializeCloudMachineParams(HostStarter::Params& params,
                                  const base::CommandLine* command_line) {
  // Count the number of args provided so we can show a helpful error message
  // if the user provides an unexpected value.
  size_t cloud_arg_count = 1;
  params.owner_email = base::ToLowerASCII(
      command_line->GetSwitchValueASCII(kCloudUserSwitchName));

  // Allow user to specify a display name.
  if (command_line->HasSwitch(kDisplayNameSwitchName)) {
    cloud_arg_count++;
    params.name = command_line->GetSwitchValueASCII(kDisplayNameSwitchName);
  }

  if (command_line->HasSwitch(kCloudApiKeySwitchName)) {
    // Using a cloud API_KEY means start-host will not attempt to retrieve an
    // access token for the default service-account.
    params.api_key = command_line->GetSwitchValueASCII(kCloudApiKeySwitchName);
    cloud_arg_count++;
  }

  bool has_disable_crash_reporting_switch =
      command_line->HasSwitch(kDisableCrashReportingSwitchName);
  params.enable_crash_reporting = !has_disable_crash_reporting_switch;
  if (has_disable_crash_reporting_switch) {
    cloud_arg_count++;
  }

  // Allow debugging switches.
  if (command_line->HasSwitch("v")) {
    cloud_arg_count++;
  }
  if (command_line->HasSwitch("vmodule")) {
    cloud_arg_count++;
  }

  if (command_line->GetSwitches().size() > cloud_arg_count) {
    return false;
  }

  return true;
}

}  // namespace

int StartHostMain(int argc, char** argv) {
#if BUILDFLAG(IS_LINUX)
  // Minimize the amount of code that runs as root on Posix systems.
  if (getuid() == 0) {
    return remoting::StartHostAsRoot(argc, argv);
  }
#endif  // BUILDFLAG(IS_LINUX)

  // google_apis::GetOAuth2ClientID/Secret need a static CommandLine.
  base::CommandLine::Init(argc, argv);
  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();

  // This object instance is required by Chrome code (for example,
  // FilePath, LazyInstance, MessageLoop).
  base::AtExitManager exit_manager;

  logging::LoggingSettings settings;
  settings.logging_dest =
      logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;
  logging::InitLogging(settings);

  base::ThreadPoolInstance::CreateAndStartWithDefaultParams(
      "RemotingHostSetup");

  mojo::core::Init();

#if BUILDFLAG(IS_LINUX)
  if (command_line->HasSwitch("no-start")) {
    // On Linux, registering the host with systemd and starting it is the only
    // reason start_host requires root. The --no-start options skips that final
    // step, allowing it to be run non-interactively if the parent process has
    // root and can do complete the setup itself. Since this functionality is
    // Linux-specific, it isn't plumbed through the platform-independent daemon
    // controller code, and must be configured on the Linux delegate explicitly.
    DaemonControllerDelegateLinux::set_start_host_after_setup(false);
    // Remove the switch from the command line to simplify arg count checks.
    command_line->RemoveSwitch("no-start");
  }
#endif  // BUILDFLAG(IS_LINUX)
#if BUILDFLAG(IS_WIN)
  // The tool must be run elevated on Windows so the host has access to the
  // directories used to store the configuration JSON files.
  if (!base::IsCurrentProcessElevated()) {
    fprintf(stderr, "Error: %s must be run as an elevated process.", argv[0]);
    return 1;
  }
#endif  // BUILDFLAG(IS_WIN)

  if (command_line->HasSwitch("help") || command_line->HasSwitch("h") ||
      command_line->HasSwitch("?") || !command_line->GetArgs().empty()) {
    PrintDefaultHelpMessage(argv[0]);
    return 1;
  }

  HostStarter::Params params;
  bool use_corp_machine_flow = command_line->HasSwitch(kCorpUserSwitchName);
  bool use_cloud_machine_flow = command_line->HasSwitch(kCloudUserSwitchName);
  if (use_corp_machine_flow) {
    if (!InitializeCorpMachineParams(params, command_line)) {
      PrintCorpUserHelpMessage(argv[0]);
      return 1;
    }
  } else if (use_cloud_machine_flow) {
    if (!InitializeCloudMachineParams(params, command_line)) {
      PrintCloudUserHelpMessage(argv[0]);
      return 1;
    }
  } else if (!InitializeParamsForOAuthFlow(params, command_line)) {
    PrintDefaultHelpMessage(argv[0]);
    return 1;
  }

#if defined(REMOTING_ENABLE_CRASH_REPORTING)
  // We don't have a config file yet so we can't use IsUsageStatsAllowed(),
  // instead we can just check the command line parameter.
  if (params.enable_crash_reporting) {
#if BUILDFLAG(IS_LINUX)
    InitializeCrashpadReporting();
#elif BUILDFLAG(IS_WIN)
    InitializeBreakpadReporting();
#endif  // BUILDFLAG(IS_LINUX)
  }
#endif  // defined(REMOTING_ENABLE_CRASH_REPORTING)

  // Provide SingleThreadTaskExecutor and threads for the
  // URLRequestContextGetter.
  base::SingleThreadTaskExecutor main_thread_task_executor;
  g_main_thread_task_executor = &main_thread_task_executor;
  base::Thread::Options io_thread_options(base::MessagePumpType::IO, 0);
  base::Thread io_thread("IO thread");
  io_thread.StartWithOptions(std::move(io_thread_options));

  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter(
      new remoting::URLRequestContextGetter(io_thread.task_runner()));
  network::TransitionalURLLoaderFactoryOwner url_loader_factory_owner(
      url_request_context_getter, /*is_trusted=*/use_corp_machine_flow);

  // Start the host.
  std::unique_ptr<HostStarter> host_starter;
  if (use_corp_machine_flow) {
    host_starter =
        ProvisionCorpMachine(url_loader_factory_owner.GetURLLoaderFactory(),
                             CreateClientCertStoreInstance());
  } else if (use_cloud_machine_flow) {
    fprintf(stdout,
            "*** Warning: This workflow is experimental and not fully "
            "supported at this time ***\n");
    host_starter =
        ProvisionCloudInstance(url_loader_factory_owner.GetURLLoaderFactory());
  } else {
    host_starter =
        CreateOAuthHostStarter(url_loader_factory_owner.GetURLLoaderFactory());
  }

  host_starter->StartHost(std::move(params), base::BindOnce(&OnDone));

  // Run the task executor until the StartHost completion callback.
  base::RunLoop run_loop;
  g_active_run_loop = &run_loop;
  run_loop.Run();

  g_main_thread_task_executor = nullptr;
  g_active_run_loop = nullptr;

  // Destroy the HostStarter and URLRequestContextGetter before stopping the
  // IO thread.
  host_starter.reset();
  url_request_context_getter = nullptr;

  io_thread.Stop();

  return g_started ? 0 : 1;
}

}  // namespace remoting