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