15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <windows.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/command_line.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
11bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch#include "base/process/kill.h"
12bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch#include "base/process/launch.h"
13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string16.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/stringprintf.h"
16868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/win/registry.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/win/scoped_handle.h"
19868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "crypto/random.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace extensions {
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kNativeMessagingRegistryKey[] =
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    L"SOFTWARE\\Google\\Chrome\\NativeMessagingHosts";
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
26eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochnamespace {
27eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
28eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Reads path to the native messaging host manifest from the registry. Returns
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// false if the path isn't found.
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool GetManifestPathWithFlags(HKEY root_key,
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                              DWORD flags,
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                              const base::string16& host_name,
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                              base::string16* result) {
34eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::win::RegKey key;
35eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (key.Open(root_key, kNativeMessagingRegistryKey,
37eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch               KEY_QUERY_VALUE | flags) != ERROR_SUCCESS ||
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      key.OpenKey(host_name.c_str(),
39eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                  KEY_QUERY_VALUE | flags) != ERROR_SUCCESS ||
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      key.ReadValue(NULL, result) != ERROR_SUCCESS) {
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
42eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return true;
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool GetManifestPath(HKEY root_key,
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                     const base::string16& host_name,
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                     base::string16* result) {
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // First check 32-bit registry and then try 64-bit.
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return GetManifestPathWithFlags(
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)             root_key, KEY_WOW64_32KEY, host_name, result) ||
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)         GetManifestPathWithFlags(
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)             root_key, KEY_WOW64_64KEY, host_name, result);
55eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
56eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
57eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}  // namespace
58eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
60a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)base::FilePath NativeProcessLauncher::FindManifest(
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const std::string& host_name,
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    bool allow_user_level_hosts,
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string* error_message) {
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 host_name_wide = base::UTF8ToUTF16(host_name);
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Try to find the key in HKEY_LOCAL_MACHINE and then in HKEY_CURRENT_USER.
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 path_str;
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  bool found = false;
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (allow_user_level_hosts)
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    found = GetManifestPath(HKEY_CURRENT_USER, host_name_wide, &path_str);
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!found)
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    found = GetManifestPath(HKEY_LOCAL_MACHINE, host_name_wide, &path_str);
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!found) {
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    *error_message =
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        "Native messaging host " + host_name + " is not registered.";
77a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    return base::FilePath();
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::FilePath manifest_path(path_str);
81a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  if (!manifest_path.IsAbsolute()) {
82a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    *error_message = "Path to native messaging host manifest must be absolute.";
83a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    return base::FilePath();
84a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
85a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
86a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  return manifest_path;
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool NativeProcessLauncher::LaunchNativeProcess(
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const CommandLine& command_line,
924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    base::ProcessHandle* process_handle,
9323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    base::File* read_file,
9423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    base::File* write_file) {
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Timeout for the IO pipes.
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const DWORD kTimeoutMs = 5000;
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Windows will use default buffer size when 0 is passed to
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // CreateNamedPipeW().
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const DWORD kBufferSize = 0;
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!command_line.GetProgram().IsAbsolute()) {
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    LOG(ERROR) << "Native Messaging host path must be absolute.";
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  uint64 pipe_name_token;
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  crypto::RandBytes(&pipe_name_token, sizeof(pipe_name_token));
109a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 out_pipe_name = base::StringPrintf(
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      L"\\\\.\\pipe\\chrome.nativeMessaging.out.%llx", pipe_name_token);
111a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 in_pipe_name = base::StringPrintf(
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      L"\\\\.\\pipe\\chrome.nativeMessaging.in.%llx", pipe_name_token);
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Create the pipes to read and write from.
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::win::ScopedHandle stdout_pipe(
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CreateNamedPipeW(out_pipe_name.c_str(),
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED |
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           FILE_FLAG_FIRST_PIPE_INSTANCE,
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       PIPE_TYPE_BYTE, 1, kBufferSize, kBufferSize,
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       kTimeoutMs, NULL));
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!stdout_pipe.IsValid()) {
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    LOG(ERROR) << "Failed to create pipe " << out_pipe_name;
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::win::ScopedHandle stdin_pipe(
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CreateNamedPipeW(in_pipe_name.c_str(),
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED |
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           FILE_FLAG_FIRST_PIPE_INSTANCE,
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       PIPE_TYPE_BYTE, 1, kBufferSize, kBufferSize,
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       kTimeoutMs, NULL));
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!stdin_pipe.IsValid()) {
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    LOG(ERROR) << "Failed to create pipe " << in_pipe_name;
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DWORD comspec_length = ::GetEnvironmentVariable(L"COMSPEC", NULL, 0);
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (comspec_length == 0) {
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    LOG(ERROR) << "COMSPEC is not set";
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<wchar_t[]> comspec(new wchar_t[comspec_length]);
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ::GetEnvironmentVariable(L"COMSPEC", comspec.get(), comspec_length);
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
145a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 command_line_string = command_line.GetCommandLineString();
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
147a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 command = base::StringPrintf(
14868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      L"%ls /c %ls < %ls > %ls",
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      comspec.get(), command_line_string.c_str(),
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      in_pipe_name.c_str(), out_pipe_name.c_str());
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::LaunchOptions options;
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  options.start_hidden = true;
154f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  base::win::ScopedHandle cmd_handle;
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!base::LaunchProcess(command.c_str(), options, &cmd_handle)) {
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    LOG(ERROR) << "Error launching process "
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)               << command_line.GetProgram().MaybeAsASCII();
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool stdout_connected = ConnectNamedPipe(stdout_pipe.Get(), NULL) ?
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      TRUE : GetLastError() == ERROR_PIPE_CONNECTED;
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool stdin_connected = ConnectNamedPipe(stdin_pipe.Get(), NULL) ?
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      TRUE : GetLastError() == ERROR_PIPE_CONNECTED;
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!stdout_connected || !stdin_connected) {
166f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    base::KillProcess(cmd_handle.Get(), 0, false);
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    LOG(ERROR) << "Failed to connect IO pipes when starting "
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)               << command_line.GetProgram().MaybeAsASCII();
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
172f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  *process_handle = cmd_handle.Take();
17323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  *read_file = base::File(stdout_pipe.Take());
17423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  *write_file = base::File(stdin_pipe.Take());
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return true;
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace extensions
180