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 #include "ipc/ipc_path_manager.h"
31
32 #if defined(OS_ANDROID) || defined(OS_NACL)
33 #error "This platform is not supported."
34 #endif // OS_ANDROID || OS_NACL
35
36 #include <errno.h>
37
38 #ifdef OS_WIN
39 #include <windows.h>
40 #include <psapi.h> // GetModuleFileNameExW
41 #else
42 // For stat system call
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <unistd.h>
46 #ifdef OS_MACOSX
47 #include <sys/sysctl.h>
48 #endif // OS_MACOSX
49 #endif // OS_WIN
50
51 #include <cstdlib>
52 #include <map>
53 #ifdef OS_WIN
54 #include <memory> // for std::unique_ptr
55 #endif
56
57 #include "base/const.h"
58 #include "base/file_stream.h"
59 #include "base/file_util.h"
60 #ifdef OS_WIN
61 #include "base/unverified_sha1.h"
62 #endif // OS_WIN
63 #include "base/logging.h"
64 #include "base/mac_util.h"
65 #include "base/mutex.h"
66 #include "base/port.h"
67 #include "base/process_mutex.h"
68 #include "base/scoped_handle.h"
69 #include "base/singleton.h"
70 #include "base/system_util.h"
71 #include "base/util.h"
72 #include "base/version.h"
73 #include "base/win_util.h"
74 #include "ipc/ipc.h"
75 #include "ipc/ipc.pb.h"
76
77 namespace mozc {
78 namespace {
79
80 // size of key
81 const size_t kKeySize = 32;
82
83 // Do not use ConfigFileStream, since client won't link
84 // to the embedded resource files
GetIPCKeyFileName(const string & name)85 string GetIPCKeyFileName(const string &name) {
86 #ifdef OS_WIN
87 string basename;
88 #else
89 string basename = "."; // hidden file
90 #endif
91 basename += name + ".ipc";
92 return FileUtil::JoinPath(SystemUtil::GetUserProfileDirectory(), basename);
93 }
94
IsValidKey(const string & name)95 bool IsValidKey(const string &name) {
96 if (kKeySize != name.size()) {
97 LOG(ERROR) << "IPCKey is invalid length";
98 return false;
99 }
100
101 for (size_t i = 0; i < name.size(); ++i) {
102 if ((name[i] >= '0' && name[i] <= '9') ||
103 (name[i] >= 'a' && name[i] <= 'f')) {
104 // do nothing
105 } else {
106 LOG(ERROR) << "key name is invalid: " << name[i];
107 return false;
108 }
109 }
110
111 return true;
112 }
113
CreateIPCKey()114 string CreateIPCKey() {
115 char buf[16] = {}; // key is 128 bit
116 char value[kKeySize + 1] = {};
117
118 #ifdef OS_WIN
119 const string sid = SystemUtil::GetUserSidAsString();
120 const string sha1 = internal::UnverifiedSHA1::MakeDigest(sid);
121 for (int i = 0; i < sizeof(buf) && i < sha1.size(); ++i) {
122 buf[i] = sha1.at(i);
123 }
124 #else
125 // get 128 bit key: Note that collision will happen.
126 Util::GetRandomSequence(buf, sizeof(buf));
127 #endif
128
129 // escape
130 for (size_t i = 0; i < sizeof(buf); ++i) {
131 const int hi = ((static_cast<int>(buf[i]) & 0xF0) >> 4);
132 const int lo = (static_cast<int>(buf[i]) & 0x0F);
133 value[2 * i] = static_cast<char>(hi >= 10 ? hi - 10 + 'a' : hi + '0');
134 value[2 * i + 1] = static_cast<char>(lo >= 10 ? lo - 10 + 'a' : lo + '0');
135 }
136
137 value[kKeySize] = '\0';
138 return string(value);
139 }
140
141 class IPCPathManagerMap {
142 public:
GetIPCPathManager(const string & name)143 IPCPathManager *GetIPCPathManager(const string &name) {
144 scoped_lock l(&mutex_);
145 std::map<string, IPCPathManager *>::const_iterator it =
146 manager_map_.find(name);
147 if (it != manager_map_.end()) {
148 return it->second;
149 }
150 IPCPathManager *manager = new IPCPathManager(name);
151 manager_map_.insert(std::make_pair(name, manager));
152 return manager;
153 }
154
IPCPathManagerMap()155 IPCPathManagerMap() {}
156
~IPCPathManagerMap()157 ~IPCPathManagerMap() {
158 scoped_lock l(&mutex_);
159 for (std::map<string, IPCPathManager *>::iterator it = manager_map_.begin();
160 it != manager_map_.end(); ++it) {
161 delete it->second;
162 }
163 manager_map_.clear();
164 }
165
166 private:
167 std::map<string, IPCPathManager *> manager_map_;
168 Mutex mutex_;
169 };
170
171 } // namespace
172
IPCPathManager(const string & name)173 IPCPathManager::IPCPathManager(const string &name)
174 : mutex_(new Mutex),
175 ipc_path_info_(new ipc::IPCPathInfo),
176 name_(name),
177 server_pid_(0),
178 last_modified_(-1) {}
179
~IPCPathManager()180 IPCPathManager::~IPCPathManager() {}
181
GetIPCPathManager(const string & name)182 IPCPathManager *IPCPathManager::GetIPCPathManager(const string &name) {
183 IPCPathManagerMap *manager_map = Singleton<IPCPathManagerMap>::get();
184 DCHECK(manager_map != NULL);
185 return manager_map->GetIPCPathManager(name);
186 }
187
CreateNewPathName()188 bool IPCPathManager::CreateNewPathName() {
189 scoped_lock l(mutex_.get());
190 if (ipc_path_info_->key().empty()) {
191 ipc_path_info_->set_key(CreateIPCKey());
192 }
193 return true;
194 }
195
SavePathName()196 bool IPCPathManager::SavePathName() {
197 scoped_lock l(mutex_.get());
198 if (path_mutex_.get() != NULL) {
199 return true;
200 }
201
202 path_mutex_.reset(new ProcessMutex("ipc"));
203
204 path_mutex_->set_lock_filename(GetIPCKeyFileName(name_));
205
206 // a little tricky as CreateNewPathName() tries mutex lock
207 CreateNewPathName();
208
209 // set the server version
210 ipc_path_info_->set_protocol_version(IPC_PROTOCOL_VERSION);
211 ipc_path_info_->set_product_version(Version::GetMozcVersion());
212
213 #ifdef OS_WIN
214 ipc_path_info_->set_process_id(static_cast<uint32>(::GetCurrentProcessId()));
215 ipc_path_info_->set_thread_id(static_cast<uint32>(::GetCurrentThreadId()));
216 #else
217 ipc_path_info_->set_process_id(static_cast<uint32>(getpid()));
218 ipc_path_info_->set_thread_id(0);
219 #endif
220
221 string buf;
222 if (!ipc_path_info_->SerializeToString(&buf)) {
223 LOG(ERROR) << "SerializeToString failed";
224 return false;
225 }
226
227 if (!path_mutex_->LockAndWrite(buf)) {
228 LOG(ERROR) << "ipc key file is already locked";
229 return false;
230 }
231
232 VLOG(1) << "ServerIPCKey: " << ipc_path_info_->key();
233
234 last_modified_ = GetIPCFileTimeStamp();
235 return true;
236 }
237
LoadPathName()238 bool IPCPathManager::LoadPathName() {
239 // On Windows, ShouldReload() always returns false.
240 // On other platform, it returns true when timestamp of the file is different
241 // from that of previous one.
242 const bool should_load = (ShouldReload() || ipc_path_info_->key().empty());
243 if (!should_load) {
244 return true;
245 }
246
247 if (LoadPathNameInternal()) {
248 return true;
249 }
250
251 #if defined(OS_WIN)
252 // Fill the default values as fallback.
253 // Applications conerted by Desktop App Converter (DAC) does not read
254 // a file of ipc session name in the LocalLow directory.
255 // For a workaround, let applications to connect the named pipe directly.
256 // See: b/71338191.
257 CreateNewPathName();
258 DCHECK(!ipc_path_info_->key().empty());
259 ipc_path_info_->set_protocol_version(IPC_PROTOCOL_VERSION);
260 ipc_path_info_->set_product_version(Version::GetMozcVersion());
261 return true;
262 #else
263 LOG(ERROR) << "LoadPathName failed";
264 return false;
265 #endif
266 }
267
GetPathName(string * ipc_name) const268 bool IPCPathManager::GetPathName(string *ipc_name) const {
269 if (ipc_name == NULL) {
270 LOG(ERROR) << "ipc_name is NULL";
271 return false;
272 }
273
274 if (ipc_path_info_->key().empty()) {
275 LOG(ERROR) << "ipc_path_info_ is empty";
276 return false;
277 }
278
279 #ifdef OS_WIN
280 *ipc_name = mozc::kIPCPrefix;
281 #elif defined(OS_MACOSX)
282 ipc_name->assign(MacUtil::GetLabelForSuffix(""));
283 #else // not OS_WIN nor OS_MACOSX
284 // GetUserIPCName("<name>") => "/tmp/.mozc.<key>.<name>"
285 const char kIPCPrefix[] = "/tmp/.mozc.";
286 *ipc_name = kIPCPrefix;
287 #endif // OS_WIN
288
289 #if defined(OS_LINUX) && !defined(OS_DRAGONFLY)
290 // On Linux, use abstract namespace which is independent of the file system.
291 (*ipc_name)[0] = '\0';
292 #endif
293
294 ipc_name->append(ipc_path_info_->key());
295 ipc_name->append(".");
296 ipc_name->append(name_);
297
298 return true;
299 }
300
GetServerProtocolVersion() const301 uint32 IPCPathManager::GetServerProtocolVersion() const {
302 return ipc_path_info_->protocol_version();
303 }
304
GetServerProductVersion() const305 const string &IPCPathManager::GetServerProductVersion() const {
306 return ipc_path_info_->product_version();
307 }
308
GetServerProcessId() const309 uint32 IPCPathManager::GetServerProcessId() const {
310 return ipc_path_info_->process_id();
311 }
312
Clear()313 void IPCPathManager::Clear() {
314 scoped_lock l(mutex_.get());
315 ipc_path_info_->Clear();
316 }
317
IsValidServer(uint32 pid,const string & server_path)318 bool IPCPathManager::IsValidServer(uint32 pid,
319 const string &server_path) {
320 scoped_lock l(mutex_.get());
321 if (pid == 0) {
322 // For backward compatibility.
323 return true;
324 }
325 if (server_path.empty()) {
326 // This means that we do not check the server path.
327 return true;
328 }
329
330 if (pid == static_cast<uint32>(-1)) {
331 VLOG(1) << "pid is -1. so assume that it is an invalid program";
332 return false;
333 }
334
335 // compare path name
336 if (pid == server_pid_) {
337 return (server_path == server_path_);
338 }
339
340 server_pid_ = 0;
341 server_path_.clear();
342
343 #ifdef OS_WIN
344 {
345 std::wstring expected_server_ntpath;
346 const std::map<string, std::wstring>::const_iterator it =
347 expected_server_ntpath_cache_.find(server_path);
348 if (it != expected_server_ntpath_cache_.end()) {
349 expected_server_ntpath = it->second;
350 } else {
351 std::wstring wide_server_path;
352 Util::UTF8ToWide(server_path, &wide_server_path);
353 if (WinUtil::GetNtPath(wide_server_path, &expected_server_ntpath)) {
354 // Caches the relationship from |server_path| to
355 // |expected_server_ntpath| in case |server_path| is renamed later.
356 // (This can happen during the updating).
357 expected_server_ntpath_cache_[server_path] = expected_server_ntpath;
358 }
359 }
360
361 if (expected_server_ntpath.empty()) {
362 return false;
363 }
364
365 std::wstring actual_server_ntpath;
366 if (!WinUtil::GetProcessInitialNtPath(pid, &actual_server_ntpath)) {
367 return false;
368 }
369
370 if (expected_server_ntpath != actual_server_ntpath) {
371 return false;
372 }
373
374 // Here we can safely assume that |server_path| (expected one) should be
375 // the same to |server_path_| (actual one).
376 server_path_ = server_path;
377 server_pid_ = pid;
378 }
379 #endif // OS_WIN
380
381 #ifdef OS_MACOSX
382 int name[] = { CTL_KERN, KERN_PROCARGS, pid };
383 size_t data_len = 0;
384 if (sysctl(name, arraysize(name), NULL,
385 &data_len, NULL, 0) < 0) {
386 LOG(ERROR) << "sysctl KERN_PROCARGS failed";
387 return false;
388 }
389
390 server_path_.resize(data_len);
391 if (sysctl(name, arraysize(name), &server_path_[0],
392 &data_len, NULL, 0) < 0) {
393 LOG(ERROR) << "sysctl KERN_PROCARGS failed";
394 return false;
395 }
396 server_pid_ = pid;
397 #endif // OS_MACOSX
398
399 #ifdef OS_LINUX
400 // load from /proc/<pid>/exe
401 char proc[128];
402 char filename[512];
403 snprintf(proc, sizeof(proc) - 1, "/proc/%u/exe", pid);
404 const ssize_t size = readlink(proc, filename, sizeof(filename) - 1);
405 if (size == -1) {
406 LOG(ERROR) << "readlink failed: " << strerror(errno);
407 return false;
408 }
409 filename[size] = '\0';
410
411 server_path_ = filename;
412 server_pid_ = pid;
413 #endif // OS_LINUX
414
415 VLOG(1) << "server path: " << server_path << " " << server_path_;
416 if (server_path == server_path_) {
417 return true;
418 }
419
420 #ifdef OS_LINUX
421 if ((server_path + " (deleted)") == server_path_) {
422 LOG(WARNING) << server_path << " on disk is modified";
423 // If a user updates the server binary on disk during the server is running,
424 // "readlink /proc/<pid>/exe" returns a path with the " (deleted)" suffix.
425 // We allow the special case.
426 server_path_ = server_path;
427 return true;
428 }
429 #endif // OS_LINUX
430
431 return false;
432 }
433
ShouldReload() const434 bool IPCPathManager::ShouldReload() const {
435 #ifdef OS_WIN
436 // In windows, no reloading mechanism is necessary because IPC files
437 // are automatically removed.
438 return false;
439 #else
440 scoped_lock l(mutex_.get());
441
442 time_t last_modified = GetIPCFileTimeStamp();
443 if (last_modified == last_modified_) {
444 return false;
445 }
446
447 return true;
448 #endif // OS_WIN
449 }
450
GetIPCFileTimeStamp() const451 time_t IPCPathManager::GetIPCFileTimeStamp() const {
452 #ifdef OS_WIN
453 // In windows, we don't need to get the exact file timestamp, so
454 // just returns -1 at this time.
455 return static_cast<time_t>(-1);
456 #else
457 const string filename = GetIPCKeyFileName(name_);
458 struct stat filestat;
459 if (::stat(filename.c_str(), &filestat) == -1) {
460 VLOG(2) << "stat(2) failed. Skipping reload";
461 return static_cast<time_t>(-1);
462 }
463 return filestat.st_mtime;
464 #endif // OS_WIN
465 }
466
LoadPathNameInternal()467 bool IPCPathManager::LoadPathNameInternal() {
468 scoped_lock l(mutex_.get());
469
470 // try the new file name first.
471 const string filename = GetIPCKeyFileName(name_);
472
473 // Special code for Windows,
474 // we want to pass FILE_SHRED_DELETE flag for CreateFile.
475 #ifdef OS_WIN
476 std::wstring wfilename;
477 Util::UTF8ToWide(filename, &wfilename);
478
479 {
480 ScopedHandle handle
481 (::CreateFileW(wfilename.c_str(),
482 GENERIC_READ,
483 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
484 NULL, OPEN_EXISTING, 0, NULL));
485
486 // ScopedHandle does not receive INVALID_HANDLE_VALUE and
487 // NULL check is appropriate here.
488 if (NULL == handle.get()) {
489 LOG(ERROR) << "cannot open: " << filename << " " << ::GetLastError();
490 return false;
491 }
492
493 const DWORD size = ::GetFileSize(handle.get(), NULL);
494 if (-1 == static_cast<int>(size)) {
495 LOG(ERROR) << "GetFileSize failed: " << ::GetLastError();
496 return false;
497 }
498
499 const DWORD kMaxFileSize = 2096;
500 if (size == 0 || size >= kMaxFileSize) {
501 LOG(ERROR) << "Invalid file size: " << kMaxFileSize;
502 return false;
503 }
504
505 std::unique_ptr<char[]> buf(new char[size]);
506
507 DWORD read_size = 0;
508 if (!::ReadFile(handle.get(), buf.get(),
509 size, &read_size, NULL)) {
510 LOG(ERROR) << "ReadFile failed: " << ::GetLastError();
511 return false;
512 }
513
514 if (read_size != size) {
515 LOG(ERROR) << "read_size != size";
516 return false;
517 }
518
519 if (!ipc_path_info_->ParseFromArray(buf.get(), static_cast<int>(size))) {
520 LOG(ERROR) << "ParseFromStream failed";
521 return false;
522 }
523 }
524
525 #else
526
527 InputFileStream is(filename.c_str(), std::ios::binary | std::ios::in);
528 if (!is) {
529 LOG(ERROR) << "cannot open: " << filename;
530 return false;
531 }
532
533 if (!ipc_path_info_->ParseFromIstream(&is)) {
534 LOG(ERROR) << "ParseFromStream failed";
535 return false;
536 }
537 #endif
538
539 if (!IsValidKey(ipc_path_info_->key())) {
540 LOG(ERROR) << "IPCServer::key is invalid";
541 return false;
542 }
543
544 VLOG(1) << "ClientIPCKey: " << ipc_path_info_->key();
545 VLOG(1) << "ProtocolVersion: " << ipc_path_info_->protocol_version();
546
547 last_modified_ = GetIPCFileTimeStamp();
548 return true;
549 }
550 } // namespace mozc
551