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.
5 #include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "base/process/kill.h"
12 #include "base/process/launch.h"
13 #include "base/strings/string16.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/win/registry.h"
18 #include "base/win/scoped_handle.h"
19 #include "crypto/random.h"
21 namespace extensions
{
23 const wchar_t kNativeMessagingRegistryKey
[] =
24 L
"SOFTWARE\\Google\\Chrome\\NativeMessagingHosts";
28 // Reads path to the native messaging host manifest from the registry. Returns
29 // false if the path isn't found.
30 bool GetManifestPathWithFlags(HKEY root_key
,
32 const base::string16
& host_name
,
33 base::string16
* result
) {
34 base::win::RegKey key
;
36 if (key
.Open(root_key
, kNativeMessagingRegistryKey
,
37 KEY_QUERY_VALUE
| flags
) != ERROR_SUCCESS
||
38 key
.OpenKey(host_name
.c_str(),
39 KEY_QUERY_VALUE
| flags
) != ERROR_SUCCESS
||
40 key
.ReadValue(NULL
, result
) != ERROR_SUCCESS
) {
47 bool GetManifestPath(HKEY root_key
,
48 const base::string16
& host_name
,
49 base::string16
* result
) {
50 // First check 32-bit registry and then try 64-bit.
51 return GetManifestPathWithFlags(
52 root_key
, KEY_WOW64_32KEY
, host_name
, result
) ||
53 GetManifestPathWithFlags(
54 root_key
, KEY_WOW64_64KEY
, host_name
, result
);
60 base::FilePath
NativeProcessLauncher::FindManifest(
61 const std::string
& host_name
,
62 bool allow_user_level_hosts
,
63 std::string
* error_message
) {
64 base::string16 host_name_wide
= base::UTF8ToUTF16(host_name
);
66 // Try to find the key in HKEY_LOCAL_MACHINE and then in HKEY_CURRENT_USER.
67 base::string16 path_str
;
69 if (allow_user_level_hosts
)
70 found
= GetManifestPath(HKEY_CURRENT_USER
, host_name_wide
, &path_str
);
72 found
= GetManifestPath(HKEY_LOCAL_MACHINE
, host_name_wide
, &path_str
);
76 "Native messaging host " + host_name
+ " is not registered.";
77 return base::FilePath();
80 base::FilePath
manifest_path(path_str
);
81 if (!manifest_path
.IsAbsolute()) {
82 *error_message
= "Path to native messaging host manifest must be absolute.";
83 return base::FilePath();
90 bool NativeProcessLauncher::LaunchNativeProcess(
91 const CommandLine
& command_line
,
92 base::ProcessHandle
* process_handle
,
93 base::File
* read_file
,
94 base::File
* write_file
) {
95 // Timeout for the IO pipes.
96 const DWORD kTimeoutMs
= 5000;
98 // Windows will use default buffer size when 0 is passed to
99 // CreateNamedPipeW().
100 const DWORD kBufferSize
= 0;
102 if (!command_line
.GetProgram().IsAbsolute()) {
103 LOG(ERROR
) << "Native Messaging host path must be absolute.";
107 uint64 pipe_name_token
;
108 crypto::RandBytes(&pipe_name_token
, sizeof(pipe_name_token
));
109 base::string16 out_pipe_name
= base::StringPrintf(
110 L
"\\\\.\\pipe\\chrome.nativeMessaging.out.%llx", pipe_name_token
);
111 base::string16 in_pipe_name
= base::StringPrintf(
112 L
"\\\\.\\pipe\\chrome.nativeMessaging.in.%llx", pipe_name_token
);
114 // Create the pipes to read and write from.
115 base::win::ScopedHandle
stdout_pipe(
116 CreateNamedPipeW(out_pipe_name
.c_str(),
117 PIPE_ACCESS_INBOUND
| FILE_FLAG_OVERLAPPED
|
118 FILE_FLAG_FIRST_PIPE_INSTANCE
,
119 PIPE_TYPE_BYTE
, 1, kBufferSize
, kBufferSize
,
121 if (!stdout_pipe
.IsValid()) {
122 LOG(ERROR
) << "Failed to create pipe " << out_pipe_name
;
126 base::win::ScopedHandle
stdin_pipe(
127 CreateNamedPipeW(in_pipe_name
.c_str(),
128 PIPE_ACCESS_OUTBOUND
| FILE_FLAG_OVERLAPPED
|
129 FILE_FLAG_FIRST_PIPE_INSTANCE
,
130 PIPE_TYPE_BYTE
, 1, kBufferSize
, kBufferSize
,
132 if (!stdin_pipe
.IsValid()) {
133 LOG(ERROR
) << "Failed to create pipe " << in_pipe_name
;
137 DWORD comspec_length
= ::GetEnvironmentVariable(L
"COMSPEC", NULL
, 0);
138 if (comspec_length
== 0) {
139 LOG(ERROR
) << "COMSPEC is not set";
142 scoped_ptr
<wchar_t[]> comspec(new wchar_t[comspec_length
]);
143 ::GetEnvironmentVariable(L
"COMSPEC", comspec
.get(), comspec_length
);
145 base::string16 command_line_string
= command_line
.GetCommandLineString();
147 base::string16 command
= base::StringPrintf(
148 L
"%ls /c %ls < %ls > %ls",
149 comspec
.get(), command_line_string
.c_str(),
150 in_pipe_name
.c_str(), out_pipe_name
.c_str());
152 base::LaunchOptions options
;
153 options
.start_hidden
= true;
154 base::win::ScopedHandle cmd_handle
;
155 if (!base::LaunchProcess(command
.c_str(), options
, &cmd_handle
)) {
156 LOG(ERROR
) << "Error launching process "
157 << command_line
.GetProgram().MaybeAsASCII();
161 bool stdout_connected
= ConnectNamedPipe(stdout_pipe
.Get(), NULL
) ?
162 TRUE
: GetLastError() == ERROR_PIPE_CONNECTED
;
163 bool stdin_connected
= ConnectNamedPipe(stdin_pipe
.Get(), NULL
) ?
164 TRUE
: GetLastError() == ERROR_PIPE_CONNECTED
;
165 if (!stdout_connected
|| !stdin_connected
) {
166 base::KillProcess(cmd_handle
.Get(), 0, false);
167 LOG(ERROR
) << "Failed to connect IO pipes when starting "
168 << command_line
.GetProgram().MaybeAsASCII();
172 *process_handle
= cmd_handle
.Take();
173 *read_file
= base::File(stdout_pipe
.Take());
174 *write_file
= base::File(stdin_pipe
.Take());
179 } // namespace extensions