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