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 "base/win_util.h"
31
32 // skip all unless OS_WIN
33 #ifdef OS_WIN
34
35 #include <Aux_ulib.h>
36 #include <Psapi.h>
37 #include <Stringapiset.h>
38 #include <Winternl.h>
39 #include <shellapi.h>
40
41 #define _ATL_NO_AUTOMATIC_NAMESPACE
42 #define _WTL_NO_AUTOMATIC_NAMESPACE
43 #include <atlbase.h>
44
45 #include <clocale>
46 #include <memory>
47
48 #include "base/logging.h"
49 #include "base/mutex.h"
50 #include "base/scoped_handle.h"
51 #include "base/system_util.h"
52 #include "base/util.h"
53
54 using std::unique_ptr;
55
56 namespace mozc {
57 namespace {
58
59 once_t g_aux_lib_initialized = MOZC_ONCE_INIT;
60
CallAuxUlibInitialize()61 void CallAuxUlibInitialize() {
62 ::AuxUlibInitialize();
63 }
64
EqualLuid(const LUID & L1,const LUID & L2)65 bool EqualLuid(const LUID &L1, const LUID &L2) {
66 return (L1.LowPart == L2.LowPart && L1.HighPart == L2.HighPart);
67 }
68
IsProcessSandboxedImpl()69 bool IsProcessSandboxedImpl() {
70 bool is_restricted = false;
71 if (!WinUtil::IsProcessRestricted(::GetCurrentProcess(), &is_restricted)) {
72 return true;
73 }
74 if (is_restricted) {
75 return true;
76 }
77
78 bool in_appcontainer = false;
79 if (!WinUtil::IsProcessInAppContainer(::GetCurrentProcess(),
80 &in_appcontainer)) {
81 return true;
82 }
83
84 return in_appcontainer;
85 }
86
87 } // namespace
88
LoadSystemLibrary(const std::wstring & base_filename)89 HMODULE WinUtil::LoadSystemLibrary(const std::wstring &base_filename) {
90 std::wstring fullpath = SystemUtil::GetSystemDir();
91 fullpath += L"\\";
92 fullpath += base_filename;
93
94 const HMODULE module = ::LoadLibraryExW(fullpath.c_str(),
95 nullptr,
96 LOAD_WITH_ALTERED_SEARCH_PATH);
97 if (nullptr == module) {
98 const int last_error = ::GetLastError();
99 DLOG(WARNING) << "LoadLibraryEx failed."
100 << " fullpath = " << fullpath.c_str()
101 << " error = " << last_error;
102 }
103 return module;
104 }
105
LoadMozcLibrary(const std::wstring & base_filename)106 HMODULE WinUtil::LoadMozcLibrary(const std::wstring &base_filename) {
107 std::wstring fullpath;
108 Util::UTF8ToWide(SystemUtil::GetServerDirectory(), &fullpath);
109 fullpath += L"\\";
110 fullpath += base_filename;
111
112 const HMODULE module = ::LoadLibraryExW(fullpath.c_str(),
113 nullptr,
114 LOAD_WITH_ALTERED_SEARCH_PATH);
115 if (nullptr == module) {
116 const int last_error = ::GetLastError();
117 DLOG(WARNING) << "LoadLibraryEx failed."
118 << " fullpath = " << fullpath.c_str()
119 << " error = " << last_error;
120 }
121 return module;
122 }
123
GetSystemModuleHandle(const std::wstring & base_filename)124 HMODULE WinUtil::GetSystemModuleHandle(const std::wstring &base_filename) {
125 std::wstring fullpath = SystemUtil::GetSystemDir();
126 fullpath += L"\\";
127 fullpath += base_filename;
128
129 HMODULE module = nullptr;
130 if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
131 fullpath.c_str(), &module) == FALSE) {
132 const int last_error = ::GetLastError();
133 DLOG(WARNING) << "GetModuleHandleExW failed."
134 << " fullpath = " << fullpath.c_str()
135 << " error = " << last_error;
136 }
137 return module;
138 }
139
GetSystemModuleHandleAndIncrementRefCount(const std::wstring & base_filename)140 HMODULE WinUtil::GetSystemModuleHandleAndIncrementRefCount(
141 const std::wstring &base_filename) {
142 std::wstring fullpath = SystemUtil::GetSystemDir();
143 fullpath += L"\\";
144 fullpath += base_filename;
145
146 HMODULE module = nullptr;
147 if (GetModuleHandleExW(0, fullpath.c_str(), &module) == FALSE) {
148 const int last_error = ::GetLastError();
149 DLOG(WARNING) << "GetModuleHandleExW failed."
150 << " fullpath = " << fullpath.c_str()
151 << " error = " << last_error;
152 }
153 return module;
154 }
155
IsDLLSynchronizationHeld(bool * lock_status)156 bool WinUtil::IsDLLSynchronizationHeld(bool *lock_status) {
157 mozc::CallOnce(&g_aux_lib_initialized, &CallAuxUlibInitialize);
158
159 if (lock_status == nullptr) {
160 return false;
161 }
162
163 BOOL synchronization_held = FALSE;
164 const BOOL result =
165 ::AuxUlibIsDLLSynchronizationHeld(&synchronization_held);
166 if (!result) {
167 const int error = ::GetLastError();
168 DLOG(ERROR) << "AuxUlibIsDLLSynchronizationHeld failed. error = "
169 << error;
170 return false;
171 }
172 *lock_status = (synchronization_held != FALSE);
173 return true;
174 }
175
EncodeWindowHandle(HWND window_handle)176 uint32 WinUtil::EncodeWindowHandle(HWND window_handle) {
177 return static_cast<uint32>(reinterpret_cast<uintptr_t>(window_handle));
178 }
179
DecodeWindowHandle(uint32 window_handle_value)180 HWND WinUtil::DecodeWindowHandle(uint32 window_handle_value) {
181 return reinterpret_cast<HWND>(static_cast<uintptr_t>(window_handle_value));
182 }
183
SystemEqualString(const std::wstring & lhs,const std::wstring & rhs,bool ignore_case)184 bool WinUtil::SystemEqualString(
185 const std::wstring &lhs, const std::wstring &rhs, bool ignore_case) {
186 // We assume a string instance never contains NUL character in principle.
187 // So we will raise an error to notify the unexpected situation in debug
188 // builds. In production, however, we will admit such an instance and
189 // silently trim it at the first NUL character.
190 const std::wstring::size_type lhs_null_pos = lhs.find_first_of(L'\0');
191 const std::wstring::size_type rhs_null_pos = rhs.find_first_of(L'\0');
192 DCHECK_EQ(lhs.npos, lhs_null_pos)
193 << "|lhs| should not contain NUL character.";
194 DCHECK_EQ(rhs.npos, rhs_null_pos)
195 << "|rhs| should not contain NUL character.";
196 const std::wstring &lhs_null_trimmed = lhs.substr(0, lhs_null_pos);
197 const std::wstring &rhs_null_trimmed = rhs.substr(0, rhs_null_pos);
198
199 const int compare_result = ::CompareStringOrdinal(
200 lhs_null_trimmed.data(), lhs_null_trimmed.size(),
201 rhs_null_trimmed.data(), rhs_null_trimmed.size(),
202 (ignore_case ? TRUE : FALSE));
203
204 return compare_result == CSTR_EQUAL;
205 }
206
IsServiceUser(HANDLE hToken,bool * is_service)207 bool WinUtil::IsServiceUser(HANDLE hToken, bool *is_service) {
208 if (is_service == nullptr) {
209 return false;
210 }
211
212 TOKEN_STATISTICS ts;
213 DWORD dwSize = 0;
214 // Use token logon LUID instead of user SID, for brevity and safety
215 if (!::GetTokenInformation(hToken, TokenStatistics,
216 (LPVOID)&ts, sizeof(ts), &dwSize)) {
217 return false;
218 }
219
220 // Compare LUID
221 const LUID SystemLuid = SYSTEM_LUID;
222 const LUID LocalServiceLuid = LOCALSERVICE_LUID;
223 const LUID NetworkServiceLuid = NETWORKSERVICE_LUID;
224 if (EqualLuid(SystemLuid, ts.AuthenticationId) ||
225 EqualLuid(LocalServiceLuid, ts.AuthenticationId) ||
226 EqualLuid(NetworkServiceLuid, ts.AuthenticationId)) {
227 *is_service = true;
228 return true;
229 }
230
231 // Not a service account
232 *is_service = false;
233 return true;
234 }
235
IsServiceProcess(bool * is_service)236 bool WinUtil::IsServiceProcess(bool *is_service) {
237 if (is_service == nullptr) {
238 return false;
239 }
240
241 // Session 0 is dedicated to services
242 DWORD dwSessionId = 0;
243 if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &dwSessionId) ||
244 (dwSessionId == 0)) {
245 *is_service = true;
246 return true;
247 }
248
249 // Get process token
250 HANDLE hProcessToken = nullptr;
251 if (!::OpenProcessToken(::GetCurrentProcess(),
252 TOKEN_QUERY | TOKEN_QUERY_SOURCE,
253 &hProcessToken)) {
254 return false;
255 }
256
257 ScopedHandle process_token(hProcessToken);
258
259 // Process token is one for a service account.
260 if (!IsServiceUser(process_token.get(), is_service)) {
261 return false;
262 }
263
264 return true;
265 }
266
IsServiceThread(bool * is_service)267 bool WinUtil::IsServiceThread(bool *is_service) {
268 if (is_service == nullptr) {
269 return false;
270 }
271
272 // Get thread token (if any)
273 HANDLE hThreadToken = nullptr;
274 if (!::OpenThreadToken(::GetCurrentThread(),
275 TOKEN_QUERY, TRUE, &hThreadToken) &&
276 ERROR_NO_TOKEN != ::GetLastError()) {
277 return false;
278 }
279
280 if (hThreadToken == nullptr) {
281 // No thread token.
282 *is_service = false;
283 return true;
284 }
285
286 ScopedHandle thread_token(hThreadToken);
287
288 // Check if the thread token (if any) is one for a service account.
289 if (!IsServiceUser(thread_token.get(), is_service)) {
290 return false;
291 }
292 return true;
293 }
294
IsServiceAccount(bool * is_service)295 bool WinUtil::IsServiceAccount(bool *is_service) {
296 if (is_service == nullptr) {
297 return false;
298 }
299
300 bool is_service_process = false;
301 if (!WinUtil::IsServiceProcess(&is_service_process)) {
302 DLOG(ERROR) << "WinUtil::IsServiceProcess failed.";
303 return false;
304 }
305
306 if (is_service_process) {
307 *is_service = true;
308 return true;
309 }
310
311 // Process token is not one for service.
312 // Check thread token just in case.
313 bool is_service_thread = false;
314 if (!WinUtil::IsServiceThread(&is_service_thread)) {
315 DLOG(ERROR) << "WinUtil::IsServiceThread failed.";
316 return false;
317 }
318
319 if (is_service_thread) {
320 *is_service = true;
321 return true;
322 }
323
324 *is_service = false;
325 return true;
326 }
327
IsProcessImmersive(HANDLE process_handle,bool * is_immersive)328 bool WinUtil::IsProcessImmersive(HANDLE process_handle,
329 bool *is_immersive) {
330 if (is_immersive == nullptr) {
331 return false;
332 }
333 *is_immersive = false;
334 // ImmersiveMode is supported only in Windows8 and later.
335 if (!SystemUtil::IsWindows8OrLater()) {
336 return true;
337 }
338
339 const HMODULE module = WinUtil::GetSystemModuleHandle(L"user32.dll");
340 if (module == nullptr) {
341 return false;
342 }
343
344 typedef BOOL (WINAPI* IsImmersiveProcessFunc)(HANDLE process);
345 IsImmersiveProcessFunc is_immersive_process =
346 reinterpret_cast<IsImmersiveProcessFunc>(
347 ::GetProcAddress(module, "IsImmersiveProcess"));
348 if (is_immersive_process == nullptr) {
349 return false;
350 }
351
352 *is_immersive = !!is_immersive_process(process_handle);
353 return true;
354 }
355
IsProcessRestricted(HANDLE process_handle,bool * is_restricted)356 bool WinUtil::IsProcessRestricted(HANDLE process_handle, bool *is_restricted) {
357 if (is_restricted == nullptr) {
358 return false;
359 }
360 *is_restricted = false;
361
362 HANDLE token = nullptr;
363 if (!::OpenProcessToken(process_handle, TOKEN_QUERY, &token)) {
364 return false;
365 }
366
367 ScopedHandle process_token(token);
368 ::SetLastError(NOERROR);
369 if (::IsTokenRestricted(process_token.get()) == FALSE) {
370 const DWORD error = ::GetLastError();
371 if (error != NOERROR) {
372 return false;
373 }
374 } else {
375 *is_restricted = true;
376 }
377 return true;
378 }
379
IsProcessInAppContainer(HANDLE process_handle,bool * in_appcontainer)380 bool WinUtil::IsProcessInAppContainer(HANDLE process_handle,
381 bool *in_appcontainer) {
382 if (in_appcontainer == nullptr) {
383 return false;
384 }
385 *in_appcontainer = false;
386
387 // AppContainer is supported only in Windows8 and later.
388 if (!SystemUtil::IsWindows8OrLater()) {
389 return true;
390 }
391
392 HANDLE token = nullptr;
393 if (!::OpenProcessToken(process_handle, TOKEN_QUERY | TOKEN_QUERY_SOURCE,
394 &token)) {
395 return false;
396 }
397
398 // TokenIsAppContainer is defined only in Windows SDK 8.0 and later.
399 ScopedHandle process_token(token);
400 const TOKEN_INFORMATION_CLASS kTokenIsAppContainer =
401 static_cast<TOKEN_INFORMATION_CLASS>(29); // TokenIsAppContainer
402 #if defined(_WIN32_WINNT_WIN8)
403 static_assert(kTokenIsAppContainer == TokenIsAppContainer,
404 "Checking |kTokenIsAppContainer| has correct value.");
405 #endif // _WIN32_WINNT_WIN8
406 DWORD returned_size = 0;
407 DWORD retval = 0;
408 if (!GetTokenInformation(process_token.get(), kTokenIsAppContainer,
409 &retval, sizeof(retval), &returned_size)) {
410 return false;
411 }
412 if (returned_size != sizeof(retval)) {
413 return false;
414 }
415
416 *in_appcontainer = (retval != 0);
417 return true;
418 }
419
GetFileSystemInfoFromPath(const std::wstring & path,BY_HANDLE_FILE_INFORMATION * info)420 bool WinUtil::GetFileSystemInfoFromPath(
421 const std::wstring &path, BY_HANDLE_FILE_INFORMATION *info) {
422 // no read access is required.
423 ScopedHandle handle(::CreateFileW(
424 path.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
425 nullptr, OPEN_EXISTING,
426 FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL, nullptr));
427
428 // Caveats: handle.get() returns nullptr when it is initialized with
429 // INVALID_HANDLE_VALUE.
430 if (handle.get() == nullptr) {
431 return false;
432 }
433 return !!::GetFileInformationByHandle(handle.get(), info);
434 }
435
AreEqualFileSystemObject(const std::wstring & left_path,const std::wstring & right_path)436 bool WinUtil::AreEqualFileSystemObject(const std::wstring &left_path,
437 const std::wstring &right_path) {
438 BY_HANDLE_FILE_INFORMATION left_info = {};
439 if (!GetFileSystemInfoFromPath(left_path, &left_info)) {
440 return false;
441 }
442 BY_HANDLE_FILE_INFORMATION right_info = {};
443 if (!GetFileSystemInfoFromPath(right_path, &right_info)) {
444 return false;
445 }
446 return (left_info.nFileIndexLow == right_info.nFileIndexLow) &&
447 (left_info.nFileIndexHigh == right_info.nFileIndexHigh);
448 }
449
GetNtPath(const std::wstring & dos_path,std::wstring * nt_path)450 bool WinUtil::GetNtPath(const std::wstring &dos_path, std::wstring *nt_path) {
451 if (nt_path == nullptr) {
452 return false;
453 }
454
455 nt_path->clear();
456
457 ScopedHandle file_handle(::CreateFileW(
458 dos_path.c_str(), 0,
459 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
460 nullptr, OPEN_EXISTING,
461 FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL, nullptr));
462 if (file_handle.get() == nullptr) {
463 // Caveats: |file_handle.get()| becomes nullptr instead of
464 // INVALID_HANDLE_VALUE when failure.
465 return false;
466 }
467
468 const size_t kMaxPath = 4096;
469 unique_ptr<wchar_t[]> ntpath_buffer(
470 new wchar_t[kMaxPath]);
471 const DWORD copied_len_without_null = ::GetFinalPathNameByHandleW(
472 file_handle.get(),
473 ntpath_buffer.get(),
474 kMaxPath,
475 FILE_NAME_NORMALIZED | VOLUME_NAME_NT);
476 if (copied_len_without_null == 0 ||
477 copied_len_without_null > kMaxPath) {
478 const DWORD error = ::GetLastError();
479 VLOG(1) << "GetFinalPathNameByHandleW() failed: " << error;
480 return false;
481 }
482
483 nt_path->assign(ntpath_buffer.get(), copied_len_without_null);
484 return true;
485 }
486
GetProcessInitialNtPath(DWORD pid,std::wstring * nt_path)487 bool WinUtil::GetProcessInitialNtPath(DWORD pid, std::wstring *nt_path) {
488 if (nt_path == nullptr) {
489 return false;
490 }
491 nt_path->clear();
492
493 ScopedHandle process_handle(::OpenProcess(
494 PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid));
495
496 if (process_handle.get() == nullptr) {
497 VLOG(1) << "OpenProcess() failed: " << ::GetLastError();
498 return false;
499 }
500
501 const size_t kMaxPath = 4096;
502 unique_ptr<wchar_t[]> ntpath_buffer(new wchar_t[kMaxPath]);
503 const DWORD copied_len_without_null =
504 ::GetProcessImageFileNameW(process_handle.get(),
505 ntpath_buffer.get(),
506 kMaxPath);
507 if (copied_len_without_null == 0 || copied_len_without_null > kMaxPath) {
508 const DWORD error = ::GetLastError();
509 VLOG(1) << "GetProcessImageFileNameW() failed: " << error;
510 return false;
511 }
512
513 nt_path->assign(ntpath_buffer.get(), copied_len_without_null);
514 return true;
515 }
516
517 // SPI_GETTHREADLOCALINPUTSETTINGS is available on Windows 8 SDK and later.
518 #ifndef SPI_GETTHREADLOCALINPUTSETTINGS
519 #define SPI_GETTHREADLOCALINPUTSETTINGS 0x104E
520 #endif // SPI_GETTHREADLOCALINPUTSETTINGS
521
IsPerUserInputSettingsEnabled()522 bool WinUtil::IsPerUserInputSettingsEnabled() {
523 if (!SystemUtil::IsWindows8OrLater()) {
524 // Windows 7 and below does not support per-user input mode.
525 return false;
526 }
527 BOOL is_thread_local = FALSE;
528 if (::SystemParametersInfo(SPI_GETTHREADLOCALINPUTSETTINGS,
529 0,
530 reinterpret_cast<void *>(&is_thread_local),
531 0) == FALSE) {
532 return false;
533 }
534 return !is_thread_local;
535 }
536
IsProcessSandboxed()537 bool WinUtil::IsProcessSandboxed() {
538 // Thread safety is not required.
539 static bool sandboxed = IsProcessSandboxedImpl();
540 return sandboxed;
541 }
542
ShellExecuteInSystemDir(const wchar_t * verb,const wchar_t * file,const wchar_t * parameters)543 bool WinUtil::ShellExecuteInSystemDir(const wchar_t *verb,
544 const wchar_t *file,
545 const wchar_t *parameters) {
546 const auto result = static_cast<uint32>(reinterpret_cast<uintptr_t>(
547 ::ShellExecuteW(0, verb, file, parameters, SystemUtil::GetSystemDir(),
548 SW_SHOW)));
549 LOG_IF(ERROR, result <= 32)
550 << "ShellExecute failed."
551 << ", error:" << result
552 << ", verb: " << verb
553 << ", file: " << file
554 << ", parameters: " << parameters;
555 return result > 32;
556 }
557
ScopedCOMInitializer()558 ScopedCOMInitializer::ScopedCOMInitializer()
559 : hr_(::CoInitialize(nullptr)) {
560 }
561
~ScopedCOMInitializer()562 ScopedCOMInitializer::~ScopedCOMInitializer() {
563 if (SUCCEEDED(hr_)) {
564 ::CoUninitialize();
565 }
566 }
567
568 } // namespace mozc
569
570 #endif // OS_WIN
571