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 
32 #include "server/cache_service_manager.h"
33 
34 #include <windows.h>
35 #include <wincrypt.h>
36 
37 #include <memory>
38 #include <string>
39 
40 #include "base/const.h"
41 #include "base/file_util.h"
42 #include "base/logging.h"
43 #include "base/scoped_handle.h"
44 #include "base/system_util.h"
45 #include "base/util.h"
46 #include "server/win32_service_state.pb.h"
47 
48 using std::unique_ptr;
49 
50 namespace mozc {
51 namespace {
52 
53 const uint64 kMinimumRequiredMemorySizeForInstall = 384 * 1024 * 1024;
54 
55 class ScopedSCHandle {
56  public:
ScopedSCHandle()57   ScopedSCHandle()
58       : handle_(NULL) {}
ScopedSCHandle(SC_HANDLE handle)59   explicit ScopedSCHandle(SC_HANDLE handle)
60       : handle_(handle) {}
61 
~ScopedSCHandle()62   ~ScopedSCHandle() {
63     if (NULL != handle_) {
64       ::CloseServiceHandle(handle_);
65     }
66   }
67 
get() const68   SC_HANDLE get() const { return handle_; }
69 
swap(ScopedSCHandle & other)70   void swap(ScopedSCHandle & other) {
71     SC_HANDLE tmp = handle_;
72     handle_ = other.handle_;
73     other.handle_ = tmp;
74   }
75 
76  private:
77   SC_HANDLE handle_;
78 };
79 
80 // This function serializes a given message (any type of protobuf message)
81 // as a std::wstring encoded by base64.
82 // Returns true if succeeds.
SerializeToBase64WString(const::google::protobuf::Message & message,std::wstring * dest)83 bool SerializeToBase64WString(const ::google::protobuf::Message &message,
84                               std::wstring *dest) {
85   if (dest == NULL) {
86     return false;
87   }
88 
89   const int serialized_len = message.ByteSize();
90   unique_ptr<BYTE[]> serialized(new BYTE[serialized_len]);
91   if (!message.SerializeToArray(serialized.get(), serialized_len)) {
92     LOG(ERROR) << "SerializeAsArray failed";
93     return false;
94   }
95 
96   DWORD base64_string_len = 0;
97   BOOL result = ::CryptBinaryToString(serialized.get(), serialized_len,
98                                       CRYPT_STRING_BASE64, NULL,
99                                       &base64_string_len);
100   if (result == FALSE) {
101     return false;
102   }
103   unique_ptr<wchar_t[]> base64_string(new wchar_t[base64_string_len]);
104   result = ::CryptBinaryToString(serialized.get(), serialized_len,
105                                  CRYPT_STRING_BASE64, base64_string.get(),
106                                  &base64_string_len);
107   if (result == FALSE) {
108     return false;
109   }
110   dest->assign(base64_string.get(), base64_string_len);
111   return true;
112 }
113 
114 // This function deserializes a message (any type of protobuf message)
115 // from the given std::wstring.  This wstring should be generated by
116 // SerializeToBase64WString.
117 // Returns true if succeeds
DeserializeFromBase64WString(const std::wstring & src,::google::protobuf::Message * message)118 bool DeserializeFromBase64WString(const std::wstring &src,
119                                   ::google::protobuf::Message *message) {
120   if (message == NULL) {
121     return false;
122   }
123 
124   DWORD buffer_len = 0;
125   BOOL result = ::CryptStringToBinary(src.c_str(),
126                                       src.size(),
127                                       CRYPT_STRING_BASE64,
128                                       NULL,
129                                       &buffer_len,
130                                       NULL,
131                                       NULL);
132   if (result == FALSE) {
133     return false;
134   }
135   unique_ptr<BYTE[]> buffer(new BYTE[buffer_len]);
136   result = ::CryptStringToBinary(src.c_str(),
137                                  src.size(),
138                                  CRYPT_STRING_BASE64,
139                                  buffer.get(),
140                                  &buffer_len,
141                                  NULL,
142                                  NULL);
143   if (result == FALSE) {
144     return false;
145   }
146   if (!message->ParseFromArray(buffer.get(), buffer_len)) {
147     LOG(ERROR) << "ParseFromArray failed";
148     return false;
149   }
150   return true;
151 }
152 
153 // A helper function to retrieve a service handle of the cache service
154 // with specified access rights.
155 // Returns true if one of the following conditions is satisfied.
156 //  - A valid service handle is retrieved.  In this case, |handle| owns the
157 //    retrieved handle.
158 //  - It turns out w/o any error that the cache service is not installed.
159 //    In this case, |handle| owns a NULL handle.
160 // Otherwise, returns false.
GetCacheService(DWORD service_controler_rights,DWORD service_rights,ScopedSCHandle * handle)161 bool GetCacheService(DWORD service_controler_rights, DWORD service_rights,
162                      ScopedSCHandle *handle) {
163   if (NULL == handle) {
164     return false;
165   }
166 
167   ScopedSCHandle sc_handle(::OpenSCManager(NULL, NULL,
168                                            service_controler_rights));
169   if (NULL == sc_handle.get()) {
170     LOG(ERROR) << "OpenSCManager failed: " << ::GetLastError();
171     return false;
172   }
173 
174   ScopedSCHandle service_handle(::OpenService(
175       sc_handle.get(), CacheServiceManager::GetServiceName(), service_rights));
176   const int error = ::GetLastError();
177 
178   if (NULL == service_handle.get() && ERROR_SERVICE_DOES_NOT_EXIST != error) {
179     LOG(ERROR) << "OpenService failed: " << error;
180     return false;
181   }
182 
183   // |service_handle| is null if and only if the cache service is not
184   // installed.
185   service_handle.swap(*handle);
186   return true;
187 }
188 
IsServiceRunning(const ScopedSCHandle & service_handle)189 bool IsServiceRunning(const ScopedSCHandle &service_handle) {
190   if (NULL == service_handle.get()) {
191     return false;
192   }
193 
194   SERVICE_STATUS service_status = {};
195   if (!::QueryServiceStatus(service_handle.get(), &service_status)) {
196     LOG(ERROR) << "QueryServiceStatus failed: " << ::GetLastError();
197     return false;
198   }
199 
200   return service_status.dwCurrentState == SERVICE_RUNNING;
201 }
202 
StartServiceInternal(const ScopedSCHandle & service_handle,const std::vector<std::wstring> & arguments)203 bool StartServiceInternal(const ScopedSCHandle &service_handle,
204                           const std::vector<std::wstring> &arguments) {
205   if (arguments.size() <= 0) {
206     if (!::StartService(service_handle.get(), 0, NULL)) {
207       LOG(ERROR) << "StartService failed: " << ::GetLastError();
208       return false;
209     }
210     return true;
211   }
212 
213   unique_ptr<const wchar_t*[]> args(new const wchar_t*[arguments.size()]);
214   for (size_t i = 0; i < arguments.size(); ++i) {
215     args[i] = arguments[i].c_str();
216   }
217   if (!::StartService(service_handle.get(), arguments.size(), args.get())) {
218     LOG(ERROR) << "StartService failed: " << ::GetLastError();
219     return false;
220   }
221 
222   return true;
223 }
224 
StopService(const ScopedSCHandle & service_handle)225 bool StopService(const ScopedSCHandle &service_handle) {
226   SERVICE_STATUS status;
227   if (!::ControlService(service_handle.get(), SERVICE_CONTROL_STOP, &status)) {
228     LOG(ERROR) << "ControlService failed: " << ::GetLastError();
229     return false;
230   }
231   return true;
232 }
233 
234 // Set *nice-to-have* features for the cache service.
235 // We do not care about any failure because it is not critical for the
236 // functionality of the cache service itself.
SetAdvancedConfig(const ScopedSCHandle & service_handle)237 void SetAdvancedConfig(const ScopedSCHandle &service_handle) {
238   // Windows Vista or later has some nice features as described in the
239   // following documents.
240   // http://msdn.microsoft.com/en-us/magazine/cc164252.aspx
241 
242   // Enable "Delayed Auto-start".
243   {
244     SERVICE_DELAYED_AUTO_START_INFO info = {};
245     info.fDelayedAutostart = TRUE;
246 
247     if (!::ChangeServiceConfig2(service_handle.get(),
248                                 SERVICE_CONFIG_DELAYED_AUTO_START_INFO,
249                                 &info)) {
250       LOG(ERROR) << "ChangeServiceConfig2 failed: " << ::GetLastError();
251     }
252   }
253 
254   // http://blogs.technet.com/richard_macdonald/archive/2007/06/27/1375523.aspx
255   // See also http://b/2470180
256 
257   // Only SE_INC_BASE_PRIORITY_NAME and SE_CHANGE_NOTIFY are needed.
258   // According to MSDN Library, we need not explicitly specify the later.
259   // See http://msdn.microsoft.com/en-us/library/ms685976.aspx for details.
260   {
261     SERVICE_REQUIRED_PRIVILEGES_INFO privileges_info = {};
262     // |SERVICE_REQUIRED_PRIVILEGES_INFO::pmszRequiredPrivileges| needs to be
263     // terminated with two L'\0's.
264     std::wstring required_privileges(SE_INC_BASE_PRIORITY_NAME);
265     required_privileges.push_back(L'\0');
266     required_privileges.push_back(L'\0');
267     // |SERVICE_REQUIRED_PRIVILEGES_INFO::pmszRequiredPrivileges| needs to be
268     // writtable for some reasons.
269     std::unique_ptr<wchar_t[]> buffer(new wchar_t[required_privileges.size()]);
270     required_privileges.copy(buffer.get(), required_privileges.size());
271     privileges_info.pmszRequiredPrivileges = buffer.get();
272     if (!::ChangeServiceConfig2(service_handle.get(),
273                                 SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO,
274                                 &privileges_info)) {
275       LOG(ERROR) << "ChangeServiceConfig2 failed: " << ::GetLastError();
276     }
277   }
278 
279   // Remove write privileges from the cache service.
280   // See http://msdn.microsoft.com/en-us/library/ms685987.aspx for details.
281   // This may restrict glog functions such as LOG(ERROR).
282   // TODO(yukawa): Output logging messages as debug strings, or output them
283   // to the Win32 event log.
284   {
285     SERVICE_SID_INFO sid_info = {};
286     sid_info.dwServiceSidType = SERVICE_SID_TYPE_RESTRICTED;
287     if (!::ChangeServiceConfig2(service_handle.get(),
288                                 SERVICE_CONFIG_SERVICE_SID_INFO,
289                                 &sid_info)) {
290       LOG(ERROR) << "ChangeServiceConfig2 failed: " << ::GetLastError();
291     }
292   }
293 }
294 
295 // This function updates the following settings of the cache service as
296 // specified.
297 //  - Display name
298 //  - Desctiption
299 //  - Load type (regardless of the amount of the system memory)
300 // This function also starts or stops the cache service.
301 // Win32ServiceState::IsInstalled() will be ignored.  You cannot use this
302 // function to install nor uninstall the cache service.
RestoreStateInternal(const cache_service::Win32ServiceState & state)303 bool RestoreStateInternal(const cache_service::Win32ServiceState &state) {
304   ScopedSCHandle service_handle;
305   const DWORD kSCRights = SC_MANAGER_CONNECT;
306   const DWORD kServiceRights =
307       GENERIC_READ | GENERIC_WRITE | SERVICE_START | SERVICE_STOP;
308   if (!GetCacheService(kSCRights, kServiceRights, &service_handle)
309       || (NULL == service_handle.get())) {
310     return false;
311   }
312 
313   if (!::ChangeServiceConfig(service_handle.get(), SERVICE_NO_CHANGE,
314                              static_cast<DWORD>(state.load_type()),
315                              SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL,
316                              NULL, NULL)) {
317     LOG(ERROR) << "ChangeServiceConfig failed: " << ::GetLastError();
318     return false;
319   }
320 
321   // We ignore any failure of advanced configurations because they are
322   // *nice-to-have* features.
323   SetAdvancedConfig(service_handle);
324 
325   const bool now_running = IsServiceRunning(service_handle);
326 
327   // If the service was runnning and is not running now, start it unless the
328   // load type is not DISABLED.
329   if (state.running() && !now_running &&
330       (state.load_type() != cache_service::Win32ServiceState::DISABLED)) {
331     std::vector<std::wstring> arguments(state.arguments_size());
332     arguments.resize(state.arguments_size());
333     for (size_t i = 0; i < state.arguments_size(); ++i) {
334       if (Util::UTF8ToWide(state.arguments(i), &arguments[i]) <= 0) {
335         return false;
336       }
337     }
338     return StartServiceInternal(service_handle, arguments);
339   // If the service is now runnning and was not running, stop it.
340   } else if (!state.running() && now_running) {
341     return StopService(service_handle);
342   // Nothing to do.
343   } else {
344     return true;
345   }
346 
347   assert(0);
348   return false;
349 }
350 
351 // Returns true if a byte array which contains valid QUERY_SERVICE_CONFIG
352 // is successfully retrieved.  Some members of QUERY_SERVICE_CONFIG points
353 // memory addresses inside the retrieved byte array.
GetServiceConfig(const ScopedSCHandle & service_handle,unique_ptr<char[]> * result)354 bool GetServiceConfig(const ScopedSCHandle &service_handle,
355                       unique_ptr<char[]> *result) {
356   if (NULL == result) {
357     return false;
358   }
359 
360   if (NULL == service_handle.get()) {
361     return false;
362   }
363 
364   DWORD size = 0;
365   if (!::QueryServiceConfig(service_handle.get(), NULL, 0, &size) &&
366       ::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
367     LOG(ERROR) << "QueryServiceConfig failed: " << ::GetLastError();
368     return false;
369   }
370 
371   if (size == 0) {
372     LOG(ERROR) << "buffer size is 0";
373     return false;
374   }
375 
376   unique_ptr<char[]> buf(new char[size]);
377   LPQUERY_SERVICE_CONFIG service_config =
378       reinterpret_cast<LPQUERY_SERVICE_CONFIG>(buf.get());
379 
380   if (!::QueryServiceConfig(service_handle.get(),
381                             service_config, size, &size)) {
382     LOG(ERROR) << "QueryServiceConfig failed: " << ::GetLastError();
383     return false;
384   }
385 
386   buf.swap(*result);
387   return true;
388 }
389 }  // namespace
390 
IsInstalled()391 bool CacheServiceManager::IsInstalled() {
392   ScopedSCHandle service_handle;
393   if (!GetCacheService(SC_MANAGER_CONNECT | GENERIC_READ, SERVICE_QUERY_STATUS,
394                        &service_handle)
395       || (NULL == service_handle.get())) {
396     return false;
397   }
398   return true;
399 }
400 
IsRunning()401 bool CacheServiceManager::IsRunning() {
402   ScopedSCHandle service_handle;
403   if (!GetCacheService(SC_MANAGER_CONNECT | GENERIC_READ, SERVICE_QUERY_STATUS,
404                        &service_handle)
405       || (NULL == service_handle.get())) {
406     return false;
407   }
408   return IsServiceRunning(service_handle);
409 }
410 
IsEnabled()411 bool CacheServiceManager::IsEnabled() {
412   ScopedSCHandle service_handle;
413   if (!GetCacheService(SC_MANAGER_CONNECT | GENERIC_READ, SERVICE_QUERY_CONFIG,
414                        &service_handle)
415       || (NULL == service_handle.get())) {
416     return false;
417   }
418 
419   // This byte array will contain both QUERY_SERVICE_CONFIG and some
420   // string buffers which are reffered by corresponding members of
421   // QUERY_SERVICE_CONFIG.  We have to keep the array until we complete
422   // all tasks which use the contents of QUERY_SERVICE_CONFIG.
423   unique_ptr<char[]> buffer;
424   if (!GetServiceConfig(service_handle, &buffer)) {
425     return false;
426   }
427   const LPQUERY_SERVICE_CONFIG service_config =
428       reinterpret_cast<const LPQUERY_SERVICE_CONFIG>(buffer.get());
429   return service_config->dwStartType == SERVICE_AUTO_START;
430 }
431 
GetServiceName()432 const wchar_t *CacheServiceManager::GetServiceName() {
433   return kMozcCacheServiceName;
434 }
435 
GetUnquotedServicePath()436 std::wstring CacheServiceManager::GetUnquotedServicePath() {
437   const string lock_service_path =
438       FileUtil::JoinPath(SystemUtil::GetServerDirectory(),
439                          kMozcCacheServiceExeName);
440   std::wstring wlock_service_path;
441   if (Util::UTF8ToWide(lock_service_path, &wlock_service_path) <= 0) {
442     return L"";
443   }
444   return wlock_service_path;
445 }
446 
GetQuotedServicePath()447 std::wstring CacheServiceManager::GetQuotedServicePath() {
448   return L"\"" + GetUnquotedServicePath() + L"\"";
449 }
450 
EnableAutostart()451 bool CacheServiceManager::EnableAutostart() {
452   if (!IsInstalled()) {
453     return false;
454   }
455 
456   const bool enough = CacheServiceManager::HasEnoughMemory();
457 
458   // Fortunately, the expected service settings can be descried by
459   // Win32ServiceState so we can use RestoreStateInternal to change the
460   // settings and start the service (if needed).
461   // If the system does not have enough memory, disable the service
462   // to reduce the performance impact on the boot-time.
463   cache_service::Win32ServiceState state;
464   state.set_version(1);
465   state.set_installed(true);
466   state.set_load_type(enough ? cache_service::Win32ServiceState::AUTO_START
467                              : cache_service::Win32ServiceState::DISABLED);
468   state.set_running(enough);
469   const bool result = RestoreStateInternal(state);
470   return enough ? result : false;
471 }
472 
DisableService()473 bool CacheServiceManager::DisableService() {
474   if (!IsInstalled()) {
475     return false;
476   }
477 
478   // Fortunately, the expected service settings can be descried by
479   // Win32ServiceState so we can use RestoreStateInternal to change the
480   // settings and stop the service (if needed).
481   cache_service::Win32ServiceState state;
482   state.set_version(1);
483   state.set_installed(true);
484   state.set_load_type(cache_service::Win32ServiceState::DISABLED);
485   state.set_running(false);
486   return RestoreStateInternal(state);
487 }
488 
RestartService()489 bool CacheServiceManager::RestartService() {
490   ScopedSCHandle service_handle;
491   if (!GetCacheService(SC_MANAGER_CONNECT,
492                        SERVICE_START | SERVICE_STOP | SERVICE_QUERY_STATUS,
493                        &service_handle)
494       || (NULL == service_handle.get())) {
495     return false;
496   }
497 
498   SERVICE_STATUS status;
499   if (!::ControlService(service_handle.get(), SERVICE_CONTROL_STOP, &status)) {
500     LOG(ERROR) << "ControlService failed: " << ::GetLastError();
501   }
502 
503   const int kNumTrial = 10;
504   for (int i = 0; i < kNumTrial; ++i) {
505     SERVICE_STATUS service_status = {};
506     if (!::QueryServiceStatus(service_handle.get(), &service_status)) {
507       LOG(ERROR) << "QueryServiceStatus failed: " << ::GetLastError();
508       return false;
509     }
510     if (service_status.dwCurrentState != SERVICE_RUNNING) {
511       break;
512     }
513     ::Sleep(200);  // wait for 0.2sec
514   }
515 
516   return StartServiceInternal(service_handle, std::vector<std::wstring>());
517 }
518 
HasEnoughMemory()519 bool CacheServiceManager::HasEnoughMemory() {
520   return SystemUtil::GetTotalPhysicalMemory() >=
521       kMinimumRequiredMemorySizeForInstall;
522 }
523 
BackupStateAsString(std::wstring * result)524 bool CacheServiceManager::BackupStateAsString(std::wstring *result) {
525   if (result == NULL) {
526     return false;
527   }
528   result->clear();
529 
530   cache_service::Win32ServiceState state;
531   state.set_version(1);
532 
533   ScopedSCHandle service_handle;
534   if (!GetCacheService(SC_MANAGER_CONNECT | GENERIC_READ,
535                        SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG,
536                        &service_handle)) {
537     // If it turns out w/o any error that the cache service is not installed,
538     // |GetCacheService| returns true.  We can conclude any other error
539     // occurred.
540     return false;
541   }
542 
543   // If the cachse service is actually installed, |service_handle| should have
544   // non-null value.
545   state.set_installed(NULL != service_handle.get());
546   if (!state.installed()) {
547     // The cache service is not installed.
548     // Use default settings with setting |installed| flag to false.
549     state.set_load_type(cache_service::Win32ServiceState::AUTO_START);
550     state.set_running(true);
551   } else {
552     // This byte array will contain both QUERY_SERVICE_CONFIG and some
553     // string buffers which are reffered by corresponding members of
554     // QUERY_SERVICE_CONFIG.  We have to keep the array until we complete
555     // all tasks which use the contents of QUERY_SERVICE_CONFIG.
556     unique_ptr<char[]> buffer;
557     if (!GetServiceConfig(service_handle, &buffer)) {
558       return false;
559     }
560     const LPQUERY_SERVICE_CONFIG service_config =
561         reinterpret_cast<const LPQUERY_SERVICE_CONFIG>(buffer.get());
562     // retrieves the server load type.
563     state.set_load_type(
564         static_cast<cache_service::Win32ServiceState::LoadType>(
565             service_config->dwStartType));
566     // retrieves the server running status.
567     state.set_running(IsServiceRunning(service_handle));
568   }
569 
570   if (!SerializeToBase64WString(state, result)) {
571     return false;
572   }
573 
574   return true;
575 }
576 
RestoreStateFromString(const std::wstring & serialized)577 bool CacheServiceManager::RestoreStateFromString(
578     const std::wstring &serialized) {
579   cache_service::Win32ServiceState state;
580   if (!DeserializeFromBase64WString(serialized, &state)) {
581     return false;
582   }
583 
584   return RestoreStateInternal(state);
585 }
586 
EnsureServiceStopped()587 bool CacheServiceManager::EnsureServiceStopped() {
588   ScopedSCHandle service_handle;
589   const DWORD kSCRights = SC_MANAGER_CONNECT;
590   const DWORD kServiceRights = GENERIC_READ | SERVICE_STOP;
591   if (!GetCacheService(kSCRights, kServiceRights, &service_handle)) {
592     return false;
593   }
594 
595   if (NULL == service_handle.get()) {
596     return true;
597   }
598 
599   if (!IsServiceRunning(service_handle)) {
600     return true;
601   }
602 
603   if (!StopService(service_handle)) {
604     return false;
605   }
606 
607   return IsServiceRunning(service_handle);
608 }
609 
610 }  // namespace mozc
611 #endif  // OS_WIN
612