• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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"
20 
21 namespace extensions {
22 
23 const wchar_t kNativeMessagingRegistryKey[] =
24     L"SOFTWARE\\Google\\Chrome\\NativeMessagingHosts";
25 
26 namespace {
27 
28 // Reads path to the native messaging host manifest from the registry. Returns
29 // false if the path isn't found.
GetManifestPathWithFlags(HKEY root_key,DWORD flags,const base::string16 & host_name,base::string16 * result)30 bool GetManifestPathWithFlags(HKEY root_key,
31                               DWORD flags,
32                               const base::string16& host_name,
33                               base::string16* result) {
34   base::win::RegKey key;
35 
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) {
41     return false;
42   }
43 
44   return true;
45 }
46 
GetManifestPath(HKEY root_key,const base::string16 & host_name,base::string16 * result)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);
55 }
56 
57 }  // namespace
58 
59 // static
FindManifest(const std::string & host_name,bool allow_user_level_hosts,std::string * error_message)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);
65 
66   // Try to find the key in HKEY_LOCAL_MACHINE and then in HKEY_CURRENT_USER.
67   base::string16 path_str;
68   bool found = false;
69   if (allow_user_level_hosts)
70     found = GetManifestPath(HKEY_CURRENT_USER, host_name_wide, &path_str);
71   if (!found)
72     found = GetManifestPath(HKEY_LOCAL_MACHINE, host_name_wide, &path_str);
73 
74   if (!found) {
75     *error_message =
76         "Native messaging host " + host_name + " is not registered.";
77     return base::FilePath();
78   }
79 
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();
84   }
85 
86   return manifest_path;
87 }
88 
89 // static
LaunchNativeProcess(const CommandLine & command_line,base::ProcessHandle * process_handle,base::File * read_file,base::File * write_file)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;
97 
98   // Windows will use default buffer size when 0 is passed to
99   // CreateNamedPipeW().
100   const DWORD kBufferSize = 0;
101 
102   if (!command_line.GetProgram().IsAbsolute()) {
103     LOG(ERROR) << "Native Messaging host path must be absolute.";
104     return false;
105   }
106 
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);
113 
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,
120                        kTimeoutMs, NULL));
121   if (!stdout_pipe.IsValid()) {
122     LOG(ERROR) << "Failed to create pipe " << out_pipe_name;
123     return false;
124   }
125 
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,
131                        kTimeoutMs, NULL));
132   if (!stdin_pipe.IsValid()) {
133     LOG(ERROR) << "Failed to create pipe " << in_pipe_name;
134     return false;
135   }
136 
137   DWORD comspec_length = ::GetEnvironmentVariable(L"COMSPEC", NULL, 0);
138   if (comspec_length == 0) {
139     LOG(ERROR) << "COMSPEC is not set";
140     return false;
141   }
142   scoped_ptr<wchar_t[]> comspec(new wchar_t[comspec_length]);
143   ::GetEnvironmentVariable(L"COMSPEC", comspec.get(), comspec_length);
144 
145   base::string16 command_line_string = command_line.GetCommandLineString();
146 
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());
151 
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();
158     return false;
159   }
160 
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();
169     return false;
170   }
171 
172   *process_handle = cmd_handle.Take();
173   *read_file = base::File(stdout_pipe.Take());
174   *write_file = base::File(stdin_pipe.Take());
175 
176   return true;
177 }
178 
179 }  // namespace extensions
180