1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #ifdef OS_WIN
31 #include <string.h>
32 #include <windows.h>
33 #include <sddl.h>
34 #include <shlobj.h>
35 #else
36 #include <unistd.h>
37 #endif  // OS_WIN
38 
39 #include <cstring>
40 #include <string>
41 
42 #include "base/const.h"
43 #include "base/file_stream.h"
44 #include "base/file_util.h"
45 #include "base/logging.h"
46 #include "base/mac_util.h"
47 #include "base/port.h"
48 #include "base/process.h"
49 #include "base/run_level.h"
50 #include "base/system_util.h"
51 #include "base/util.h"
52 #include "client/client.h"
53 #include "client/client_interface.h"
54 #include "ipc/ipc.h"
55 #include "ipc/named_event.h"
56 
57 #ifdef OS_WIN
58 #include "base/win_sandbox.h"
59 #endif  // OS_WIN
60 
61 namespace mozc {
62 namespace client {
63 namespace {
64 const char kServerName[] = "session";
65 
66 // Wait at most kServerWaitTimeout msec until server gets ready
67 const uint32 kServerWaitTimeout = 20000;  // 20 sec
68 
69 // for every 1000m sec, check server
70 const uint32 kRetryIntervalForServer = 1000;
71 
72 // Try 20 times to check mozc_server is running
73 const uint32 kTrial = 20;
74 
75 #ifdef DEBUG
76 // Load special flags for server.
77 // This should be enabled on debug build
LoadServerFlags()78 const string LoadServerFlags() {
79   const char kServerFlagsFile[] = "mozc_server_flags.txt";
80   const string filename = FileUtil::JoinPath(
81       SystemUtil::GetUserProfileDirectory(), kServerFlagsFile);
82   string flags;
83   InputFileStream ifs(filename.c_str());
84   if (ifs) {
85     getline(ifs, flags);
86   }
87   VLOG(1) << "New server flag: " << flags;
88   return flags;
89 }
90 #endif  // DEBUG
91 }  // namespace
92 
93 // initialize default path
ServerLauncher()94 ServerLauncher::ServerLauncher()
95     : server_program_(SystemUtil::GetServerPath()),
96       restricted_(false),
97       suppress_error_dialog_(false) {}
98 
~ServerLauncher()99 ServerLauncher::~ServerLauncher() {}
100 
StartServer(ClientInterface * client)101 bool ServerLauncher::StartServer(ClientInterface *client) {
102   if (server_program().empty()) {
103     LOG(ERROR) << "Server path is empty";
104     return false;
105   }
106 
107   // ping first
108   if (client->PingServer()) {
109     return true;
110   }
111 
112   string arg;
113 
114 #ifdef OS_WIN
115   // When mozc is not used as a default IME and some applications (like notepad)
116   // are registered in "Start up", mozc_server may not be launched successfully.
117   // This is because the Explorer launches start-up processes inside a group job
118   // and the process inside a job cannot make our sandboxed child processes.
119   // The group job is unregistered after 60 secs (default).
120   //
121   // Here we relax the sandbox restriction if process is in a job.
122   // In order to keep security, the mozc_server is launched
123   // with restricted mode.
124 
125   const bool process_in_job = RunLevel::IsProcessInJob();
126   if (process_in_job || restricted_) {
127     LOG(WARNING)
128         << "Parent process is in job. start with restricted mode";
129     arg += "--restricted";
130   }
131 #endif
132 
133 #ifdef DEBUG
134   // In oreder to test the Session treatment (timeout/size constratins),
135   // Server flags can be configurable on DEBUG build
136   if (!arg.empty()) {
137     arg += " ";
138   }
139   arg += LoadServerFlags();
140 #endif  // DEBUG
141 
142   NamedEventListener listener(kServerName);
143   const bool listener_is_available = listener.IsAvailable();
144 
145   size_t pid = 0;
146 #ifdef OS_WIN
147   mozc::WinSandbox::SecurityInfo info;
148   // You cannot use WinSandbox::USER_INTERACTIVE here because restricted token
149   // seems to prevent WinHTTP from using SSL. b/5502343
150   info.primary_level = WinSandbox::USER_NON_ADMIN;
151   info.impersonation_level = WinSandbox::USER_RESTRICTED_SAME_ACCESS;
152   info.integrity_level = WinSandbox::INTEGRITY_LEVEL_LOW;
153   // If the current process is in a job, you cannot use
154   // CREATE_BREAKAWAY_FROM_JOB. b/1571395
155   info.use_locked_down_job = !process_in_job;
156   info.allow_ui_operation = false;
157   info.in_system_dir = true;  // use system dir not to lock current directory
158   info.creation_flags = CREATE_DEFAULT_ERROR_MODE | CREATE_NO_WINDOW;
159 
160   DWORD tmp_pid = 0;
161   const bool result = mozc::WinSandbox::SpawnSandboxedProcess(
162       server_program(), arg, info, &tmp_pid);
163   pid = static_cast<size_t>(tmp_pid);
164 
165   if (!result) {
166     LOG(ERROR) << "Can't start process: " << ::GetLastError();
167     return false;
168   }
169 #elif defined(OS_MACOSX)
170   // Use launchd API instead of spawning process.  It doesn't use
171   // server_program() at all.
172   const bool result = MacUtil::StartLaunchdService(
173       "Converter", reinterpret_cast<pid_t *>(&pid));
174   if (!result) {
175       LOG(ERROR) << "Can't start process";
176       return false;
177     }
178 #else
179   const bool result = mozc::Process::SpawnProcess(server_program(),
180                                                   arg,
181                                                   &pid);
182   if (!result) {
183     LOG(ERROR) << "Can't start process: " << strerror(result);
184     return false;
185   }
186 #endif  // OS_WIN
187 
188   // maybe another process will launch mozc_server at the same time.
189   if (client->PingServer()) {
190     VLOG(1) << "Another process has launched the server";
191     return true;
192   }
193 
194   // Common part:
195   // Wait until mozc_server becomes ready to process requests
196   if (listener_is_available) {
197     const int ret = listener.WaitEventOrProcess(kServerWaitTimeout, pid);
198     switch (ret) {
199       case NamedEventListener::TIMEOUT:
200         LOG(WARNING) << "seems that " << kProductNameInEnglish << " is not "
201                      << "ready within " << kServerWaitTimeout << " msec";
202         break;
203       case NamedEventListener::EVENT_SIGNALED:
204         VLOG(1) << kProductNameInEnglish << " is launched successfully "
205                 << "within " << kServerWaitTimeout << " msec";
206         break;
207       case NamedEventListener::PROCESS_SIGNALED:
208         LOG(ERROR) << "Mozc server is terminated";
209         // Mozc may be terminated because another client launches mozc_server
210         if (client->PingServer()) {
211           return true;
212         }
213         return false;
214     }
215   } else {
216     // maybe another process is trying to launch mozc_server.
217     LOG(ERROR) << "cannot make NamedEventListener ";
218     Util::Sleep(kRetryIntervalForServer);
219   }
220 
221   // Try to connect mozc_server just in case.
222   for (int trial = 0; trial < kTrial; ++trial) {
223     if (client->PingServer()) {
224       return true;
225     }
226     Util::Sleep(kRetryIntervalForServer);
227   }
228 
229   LOG(ERROR) << kProductNameInEnglish << " cannot be launched";
230 
231   return false;
232 }
233 
ForceTerminateServer(const string & name)234 bool ServerLauncher::ForceTerminateServer(const string &name) {
235   return IPCClient::TerminateServer(name);
236 }
237 
WaitServer(uint32 pid)238 bool ServerLauncher::WaitServer(uint32 pid) {
239   const int kTimeout = 10000;
240   return Process::WaitProcess(static_cast<size_t>(pid), kTimeout);
241 }
242 
OnFatal(ServerLauncherInterface::ServerErrorType type)243 void ServerLauncher::OnFatal(
244     ServerLauncherInterface::ServerErrorType type) {
245   LOG(ERROR) << "OnFatal is called: " << static_cast<int>(type);
246 
247   string error_type;
248   switch (type) {
249     case ServerLauncherInterface::SERVER_TIMEOUT:
250       error_type = "server_timeout";
251       break;
252     case ServerLauncherInterface::SERVER_BROKEN_MESSAGE:
253       error_type = "server_broken_message";
254       break;
255     case ServerLauncherInterface::SERVER_VERSION_MISMATCH:
256       error_type = "server_version_mismatch";
257       break;
258     case ServerLauncherInterface::SERVER_SHUTDOWN:
259       error_type = "server_shutdown";
260       break;
261     case ServerLauncherInterface::SERVER_FATAL:
262       error_type = "server_fatal";
263       break;
264     default:
265       LOG(ERROR) << "Unknown error: " << type;
266       return;
267   }
268 
269   if (!suppress_error_dialog_) {
270     Process::LaunchErrorMessageDialog(error_type);
271   }
272 }
273 }  // namespace client
274 }  // namespace mozc
275