native_process_launcher_win.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
1// Copyright (c) 2012 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 "chrome/browser/extensions/api/messaging/native_process_launcher.h" 6 7#include <windows.h> 8 9#include "base/command_line.h" 10#include "base/logging.h" 11#include "base/process_util.h" 12#include "base/strings/string16.h" 13#include "base/strings/string_number_conversions.h" 14#include "base/strings/stringprintf.h" 15#include "base/strings/utf_string_conversions.h" 16#include "base/win/registry.h" 17#include "base/win/scoped_handle.h" 18#include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h" 19#include "crypto/random.h" 20 21namespace extensions { 22 23const wchar_t kNativeMessagingRegistryKey[] = 24 L"SOFTWARE\\Google\\Chrome\\NativeMessagingHosts"; 25 26namespace { 27 28// Reads path to the native messaging host manifest from the registry. Returns 29// empty string if the path isn't found. 30string16 GetManifestPath(const string16& native_host_name, DWORD flags) { 31 base::win::RegKey key; 32 string16 result; 33 34 if (key.Open(HKEY_LOCAL_MACHINE, kNativeMessagingRegistryKey, 35 KEY_QUERY_VALUE | flags) != ERROR_SUCCESS || 36 key.OpenKey(native_host_name.c_str(), 37 KEY_QUERY_VALUE | flags) != ERROR_SUCCESS || 38 key.ReadValue(NULL, &result) != ERROR_SUCCESS) { 39 return string16(); 40 } 41 42 return result; 43} 44 45} // namespace 46 47// static 48scoped_ptr<NativeMessagingHostManifest> 49NativeProcessLauncher::FindAndLoadManifest( 50 const std::string& native_host_name, 51 std::string* error_message) { 52 string16 native_host_name_wide = UTF8ToUTF16(native_host_name); 53 54 // First check 32-bit registry and then try 64-bit. 55 string16 manifest_path = 56 GetManifestPath(native_host_name_wide, KEY_WOW64_32KEY); 57 if (manifest_path.empty()) 58 manifest_path = GetManifestPath(native_host_name_wide, KEY_WOW64_64KEY); 59 60 if (manifest_path.empty()) { 61 *error_message = "Native messaging host " + native_host_name + 62 " is not registered"; 63 return scoped_ptr<NativeMessagingHostManifest>(); 64 } 65 66 return NativeMessagingHostManifest::Load( 67 base::FilePath(manifest_path), error_message); 68} 69 70// static 71bool NativeProcessLauncher::LaunchNativeProcess( 72 const CommandLine& command_line, 73 base::PlatformFile* read_file, 74 base::PlatformFile* write_file) { 75 // Timeout for the IO pipes. 76 const DWORD kTimeoutMs = 5000; 77 78 // Windows will use default buffer size when 0 is passed to 79 // CreateNamedPipeW(). 80 const DWORD kBufferSize = 0; 81 82 if (!command_line.GetProgram().IsAbsolute()) { 83 LOG(ERROR) << "Native Messaging host path must be absolute."; 84 return false; 85 } 86 87 uint64 pipe_name_token; 88 crypto::RandBytes(&pipe_name_token, sizeof(pipe_name_token)); 89 string16 out_pipe_name = base::StringPrintf( 90 L"\\\\.\\pipe\\chrome.nativeMessaging.out.%llx", pipe_name_token); 91 string16 in_pipe_name = base::StringPrintf( 92 L"\\\\.\\pipe\\chrome.nativeMessaging.in.%llx", pipe_name_token); 93 94 // Create the pipes to read and write from. 95 base::win::ScopedHandle stdout_pipe( 96 CreateNamedPipeW(out_pipe_name.c_str(), 97 PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED | 98 FILE_FLAG_FIRST_PIPE_INSTANCE, 99 PIPE_TYPE_BYTE, 1, kBufferSize, kBufferSize, 100 kTimeoutMs, NULL)); 101 if (!stdout_pipe.IsValid()) { 102 LOG(ERROR) << "Failed to create pipe " << out_pipe_name; 103 return false; 104 } 105 106 base::win::ScopedHandle stdin_pipe( 107 CreateNamedPipeW(in_pipe_name.c_str(), 108 PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED | 109 FILE_FLAG_FIRST_PIPE_INSTANCE, 110 PIPE_TYPE_BYTE, 1, kBufferSize, kBufferSize, 111 kTimeoutMs, NULL)); 112 if (!stdin_pipe.IsValid()) { 113 LOG(ERROR) << "Failed to create pipe " << in_pipe_name; 114 return false; 115 } 116 117 DWORD comspec_length = ::GetEnvironmentVariable(L"COMSPEC", NULL, 0); 118 if (comspec_length == 0) { 119 LOG(ERROR) << "COMSPEC is not set"; 120 return false; 121 } 122 scoped_ptr<wchar_t[]> comspec(new wchar_t[comspec_length]); 123 ::GetEnvironmentVariable(L"COMSPEC", comspec.get(), comspec_length); 124 125 string16 command_line_string = command_line.GetCommandLineString(); 126 127 // 'start' command has a moronic syntax: if first argument is quoted then it 128 // interprets it as a command title. Host path may need to be in quotes, so 129 // we always need to specify the title as the first argument. 130 string16 command = base::StringPrintf( 131 L"%ls /c start \"Chrome Native Messaging Host\" /b " 132 L"%ls < %ls > %ls", 133 comspec.get(), command_line_string.c_str(), 134 in_pipe_name.c_str(), out_pipe_name.c_str()); 135 136 base::LaunchOptions options; 137 options.start_hidden = true; 138 base::ProcessHandle cmd_handle; 139 if (!base::LaunchProcess(command.c_str(), options, &cmd_handle)) { 140 LOG(ERROR) << "Error launching process " 141 << command_line.GetProgram().MaybeAsASCII(); 142 return false; 143 } 144 145 bool stdout_connected = ConnectNamedPipe(stdout_pipe.Get(), NULL) ? 146 TRUE : GetLastError() == ERROR_PIPE_CONNECTED; 147 bool stdin_connected = ConnectNamedPipe(stdin_pipe.Get(), NULL) ? 148 TRUE : GetLastError() == ERROR_PIPE_CONNECTED; 149 if (!stdout_connected || !stdin_connected) { 150 base::KillProcess(cmd_handle, 0, false); 151 base::CloseProcessHandle(cmd_handle); 152 LOG(ERROR) << "Failed to connect IO pipes when starting " 153 << command_line.GetProgram().MaybeAsASCII(); 154 return false; 155 } 156 157 // Check that cmd.exe has completed with 0 exit code to make sure it was 158 // able to connect IO pipes. 159 int error_code; 160 if (!base::WaitForExitCodeWithTimeout( 161 cmd_handle, &error_code, 162 base::TimeDelta::FromMilliseconds(kTimeoutMs)) || 163 error_code != 0) { 164 LOG(ERROR) << "cmd.exe did not exit cleanly"; 165 base::KillProcess(cmd_handle, 0, false); 166 base::CloseProcessHandle(cmd_handle); 167 return false; 168 } 169 170 base::CloseProcessHandle(cmd_handle); 171 172 *read_file = stdout_pipe.Take(); 173 *write_file = stdin_pipe.Take(); 174 175 return true; 176} 177 178} // namespace extensions 179