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
|
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
#include <windows.h>
#include <stdint.h>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "base/process/process.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include "base/win/scoped_handle.h"
#include "crypto/random.h"
namespace extensions {
const wchar_t kNativeMessagingRegistryKey[] =
L"SOFTWARE\\Google\\Chrome\\NativeMessagingHosts";
namespace {
// Reads path to the native messaging host manifest from the registry. Returns
// false if the path isn't found.
bool GetManifestPathWithFlags(HKEY root_key,
DWORD flags,
const base::string16& host_name,
base::string16* result) {
base::win::RegKey key;
if (key.Open(root_key, kNativeMessagingRegistryKey,
KEY_QUERY_VALUE | flags) != ERROR_SUCCESS ||
key.OpenKey(host_name.c_str(),
KEY_QUERY_VALUE | flags) != ERROR_SUCCESS ||
key.ReadValue(NULL, result) != ERROR_SUCCESS) {
return false;
}
return true;
}
bool GetManifestPath(HKEY root_key,
const base::string16& host_name,
base::string16* result) {
// First check 32-bit registry and then try 64-bit.
return GetManifestPathWithFlags(
root_key, KEY_WOW64_32KEY, host_name, result) ||
GetManifestPathWithFlags(
root_key, KEY_WOW64_64KEY, host_name, result);
}
} // namespace
// static
base::FilePath NativeProcessLauncher::FindManifest(
const std::string& host_name,
bool allow_user_level_hosts,
std::string* error_message) {
base::string16 host_name_wide = base::UTF8ToUTF16(host_name);
// Try to find the key in HKEY_LOCAL_MACHINE and then in HKEY_CURRENT_USER.
base::string16 path_str;
bool found = false;
if (allow_user_level_hosts)
found = GetManifestPath(HKEY_CURRENT_USER, host_name_wide, &path_str);
if (!found)
found = GetManifestPath(HKEY_LOCAL_MACHINE, host_name_wide, &path_str);
if (!found) {
*error_message =
"Native messaging host " + host_name + " is not registered.";
return base::FilePath();
}
base::FilePath manifest_path(path_str);
if (!manifest_path.IsAbsolute()) {
*error_message = "Path to native messaging host manifest must be absolute.";
return base::FilePath();
}
return manifest_path;
}
// static
bool NativeProcessLauncher::LaunchNativeProcess(
const base::CommandLine& command_line,
base::Process* process,
base::File* read_file,
base::File* write_file) {
// Timeout for the IO pipes.
const DWORD kTimeoutMs = 5000;
// Windows will use default buffer size when 0 is passed to
// CreateNamedPipeW().
const DWORD kBufferSize = 0;
if (!command_line.GetProgram().IsAbsolute()) {
LOG(ERROR) << "Native Messaging host path must be absolute.";
return false;
}
uint64_t pipe_name_token;
crypto::RandBytes(&pipe_name_token, sizeof(pipe_name_token));
base::string16 out_pipe_name = base::StringPrintf(
L"\\\\.\\pipe\\chrome.nativeMessaging.out.%llx", pipe_name_token);
base::string16 in_pipe_name = base::StringPrintf(
L"\\\\.\\pipe\\chrome.nativeMessaging.in.%llx", pipe_name_token);
// Create the pipes to read and write from.
base::win::ScopedHandle stdout_pipe(
CreateNamedPipeW(out_pipe_name.c_str(),
PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED |
FILE_FLAG_FIRST_PIPE_INSTANCE,
PIPE_TYPE_BYTE, 1, kBufferSize, kBufferSize,
kTimeoutMs, NULL));
if (!stdout_pipe.IsValid()) {
LOG(ERROR) << "Failed to create pipe " << out_pipe_name;
return false;
}
base::win::ScopedHandle stdin_pipe(
CreateNamedPipeW(in_pipe_name.c_str(),
PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED |
FILE_FLAG_FIRST_PIPE_INSTANCE,
PIPE_TYPE_BYTE, 1, kBufferSize, kBufferSize,
kTimeoutMs, NULL));
if (!stdin_pipe.IsValid()) {
LOG(ERROR) << "Failed to create pipe " << in_pipe_name;
return false;
}
DWORD comspec_length = ::GetEnvironmentVariable(L"COMSPEC", NULL, 0);
if (comspec_length == 0) {
LOG(ERROR) << "COMSPEC is not set";
return false;
}
std::unique_ptr<wchar_t[]> comspec(new wchar_t[comspec_length]);
::GetEnvironmentVariable(L"COMSPEC", comspec.get(), comspec_length);
base::string16 command_line_string = command_line.GetCommandLineString();
base::string16 command = base::StringPrintf(
L"%ls /c %ls < %ls > %ls", comspec.get(), command_line_string.c_str(),
in_pipe_name.c_str(), out_pipe_name.c_str());
base::LaunchOptions options;
options.start_hidden = true;
options.current_directory = command_line.GetProgram().DirName();
base::Process cmd_process = base::LaunchProcess(command.c_str(), options);
if (!cmd_process.IsValid()) {
LOG(ERROR) << "Error launching process "
<< command_line.GetProgram().MaybeAsASCII();
return false;
}
bool stdout_connected = ConnectNamedPipe(stdout_pipe.Get(), NULL) ?
TRUE : GetLastError() == ERROR_PIPE_CONNECTED;
bool stdin_connected = ConnectNamedPipe(stdin_pipe.Get(), NULL) ?
TRUE : GetLastError() == ERROR_PIPE_CONNECTED;
if (!stdout_connected || !stdin_connected) {
cmd_process.Terminate(0, false);
LOG(ERROR) << "Failed to connect IO pipes when starting "
<< command_line.GetProgram().MaybeAsASCII();
return false;
}
*process = std::move(cmd_process);
*read_file = base::File::CreateForAsyncHandle(stdout_pipe.Take());
*write_file = base::File::CreateForAsyncHandle(stdin_pipe.Take());
return true;
}
} // namespace extensions
|