1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/win/win_util.h"
6
7#include <aclapi.h>
8#include <lm.h>
9#include <powrprof.h>
10#include <shellapi.h>
11#include <shlobj.h>
12#include <shobjidl.h>  // Must be before propkey.
13#include <initguid.h>
14#include <propkey.h>
15#include <propvarutil.h>
16#include <sddl.h>
17#include <signal.h>
18#include <stdlib.h>
19
20#include "base/lazy_instance.h"
21#include "base/logging.h"
22#include "base/memory/scoped_ptr.h"
23#include "base/strings/string_util.h"
24#include "base/strings/stringprintf.h"
25#include "base/threading/thread_restrictions.h"
26#include "base/win/metro.h"
27#include "base/win/registry.h"
28#include "base/win/scoped_co_mem.h"
29#include "base/win/scoped_handle.h"
30#include "base/win/scoped_propvariant.h"
31#include "base/win/windows_version.h"
32
33namespace {
34
35// Sets the value of |property_key| to |property_value| in |property_store|.
36bool SetPropVariantValueForPropertyStore(
37    IPropertyStore* property_store,
38    const PROPERTYKEY& property_key,
39    const base::win::ScopedPropVariant& property_value) {
40  DCHECK(property_store);
41
42  HRESULT result = property_store->SetValue(property_key, property_value.get());
43  if (result == S_OK)
44    result = property_store->Commit();
45  return SUCCEEDED(result);
46}
47
48void __cdecl ForceCrashOnSigAbort(int) {
49  *((int*)0) = 0x1337;
50}
51
52const wchar_t kWindows8OSKRegPath[] =
53    L"Software\\Classes\\CLSID\\{054AAE20-4BEA-4347-8A35-64A533254A9D}"
54    L"\\LocalServer32";
55
56}  // namespace
57
58namespace base {
59namespace win {
60
61static bool g_crash_on_process_detach = false;
62
63#define NONCLIENTMETRICS_SIZE_PRE_VISTA \
64    SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(NONCLIENTMETRICS, lfMessageFont)
65
66void GetNonClientMetrics(NONCLIENTMETRICS* metrics) {
67  DCHECK(metrics);
68
69  static const UINT SIZEOF_NONCLIENTMETRICS =
70      (base::win::GetVersion() >= base::win::VERSION_VISTA) ?
71      sizeof(NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA;
72  metrics->cbSize = SIZEOF_NONCLIENTMETRICS;
73  const bool success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
74                                              SIZEOF_NONCLIENTMETRICS, metrics,
75                                              0);
76  DCHECK(success);
77}
78
79bool GetUserSidString(std::wstring* user_sid) {
80  // Get the current token.
81  HANDLE token = NULL;
82  if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
83    return false;
84  base::win::ScopedHandle token_scoped(token);
85
86  DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
87  scoped_ptr<BYTE[]> user_bytes(new BYTE[size]);
88  TOKEN_USER* user = reinterpret_cast<TOKEN_USER*>(user_bytes.get());
89
90  if (!::GetTokenInformation(token, TokenUser, user, size, &size))
91    return false;
92
93  if (!user->User.Sid)
94    return false;
95
96  // Convert the data to a string.
97  wchar_t* sid_string;
98  if (!::ConvertSidToStringSid(user->User.Sid, &sid_string))
99    return false;
100
101  *user_sid = sid_string;
102
103  ::LocalFree(sid_string);
104
105  return true;
106}
107
108bool IsShiftPressed() {
109  return (::GetKeyState(VK_SHIFT) & 0x8000) == 0x8000;
110}
111
112bool IsCtrlPressed() {
113  return (::GetKeyState(VK_CONTROL) & 0x8000) == 0x8000;
114}
115
116bool IsAltPressed() {
117  return (::GetKeyState(VK_MENU) & 0x8000) == 0x8000;
118}
119
120bool IsAltGrPressed() {
121  return (::GetKeyState(VK_MENU) & 0x8000) == 0x8000 &&
122      (::GetKeyState(VK_CONTROL) & 0x8000) == 0x8000;
123}
124
125bool UserAccountControlIsEnabled() {
126  // This can be slow if Windows ends up going to disk.  Should watch this key
127  // for changes and only read it once, preferably on the file thread.
128  //   http://code.google.com/p/chromium/issues/detail?id=61644
129  base::ThreadRestrictions::ScopedAllowIO allow_io;
130
131  base::win::RegKey key(HKEY_LOCAL_MACHINE,
132      L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
133      KEY_READ);
134  DWORD uac_enabled;
135  if (key.ReadValueDW(L"EnableLUA", &uac_enabled) != ERROR_SUCCESS)
136    return true;
137  // Users can set the EnableLUA value to something arbitrary, like 2, which
138  // Vista will treat as UAC enabled, so we make sure it is not set to 0.
139  return (uac_enabled != 0);
140}
141
142bool SetBooleanValueForPropertyStore(IPropertyStore* property_store,
143                                     const PROPERTYKEY& property_key,
144                                     bool property_bool_value) {
145  ScopedPropVariant property_value;
146  if (FAILED(InitPropVariantFromBoolean(property_bool_value,
147                                        property_value.Receive()))) {
148    return false;
149  }
150
151  return SetPropVariantValueForPropertyStore(property_store,
152                                             property_key,
153                                             property_value);
154}
155
156bool SetStringValueForPropertyStore(IPropertyStore* property_store,
157                                    const PROPERTYKEY& property_key,
158                                    const wchar_t* property_string_value) {
159  ScopedPropVariant property_value;
160  if (FAILED(InitPropVariantFromString(property_string_value,
161                                       property_value.Receive()))) {
162    return false;
163  }
164
165  return SetPropVariantValueForPropertyStore(property_store,
166                                             property_key,
167                                             property_value);
168}
169
170bool SetAppIdForPropertyStore(IPropertyStore* property_store,
171                              const wchar_t* app_id) {
172  // App id should be less than 64 chars and contain no space. And recommended
173  // format is CompanyName.ProductName[.SubProduct.ProductNumber].
174  // See http://msdn.microsoft.com/en-us/library/dd378459%28VS.85%29.aspx
175  DCHECK(lstrlen(app_id) < 64 && wcschr(app_id, L' ') == NULL);
176
177  return SetStringValueForPropertyStore(property_store,
178                                        PKEY_AppUserModel_ID,
179                                        app_id);
180}
181
182static const char16 kAutoRunKeyPath[] =
183    L"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
184
185bool AddCommandToAutoRun(HKEY root_key, const string16& name,
186                         const string16& command) {
187  base::win::RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
188  return (autorun_key.WriteValue(name.c_str(), command.c_str()) ==
189      ERROR_SUCCESS);
190}
191
192bool RemoveCommandFromAutoRun(HKEY root_key, const string16& name) {
193  base::win::RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
194  return (autorun_key.DeleteValue(name.c_str()) == ERROR_SUCCESS);
195}
196
197bool ReadCommandFromAutoRun(HKEY root_key,
198                            const string16& name,
199                            string16* command) {
200  base::win::RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_QUERY_VALUE);
201  return (autorun_key.ReadValue(name.c_str(), command) == ERROR_SUCCESS);
202}
203
204void SetShouldCrashOnProcessDetach(bool crash) {
205  g_crash_on_process_detach = crash;
206}
207
208bool ShouldCrashOnProcessDetach() {
209  return g_crash_on_process_detach;
210}
211
212void SetAbortBehaviorForCrashReporting() {
213  // Prevent CRT's abort code from prompting a dialog or trying to "report" it.
214  // Disabling the _CALL_REPORTFAULT behavior is important since otherwise it
215  // has the sideffect of clearing our exception filter, which means we
216  // don't get any crash.
217  _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
218
219  // Set a SIGABRT handler for good measure. We will crash even if the default
220  // is left in place, however this allows us to crash earlier. And it also
221  // lets us crash in response to code which might directly call raise(SIGABRT)
222  signal(SIGABRT, ForceCrashOnSigAbort);
223}
224
225bool IsTabletDevice() {
226  if (GetSystemMetrics(SM_MAXIMUMTOUCHES) == 0)
227    return false;
228
229  base::win::Version version = base::win::GetVersion();
230  if (version == base::win::VERSION_XP)
231    return (GetSystemMetrics(SM_TABLETPC) != 0);
232
233  // If the device is docked, the user is treating the device as a PC.
234  if (GetSystemMetrics(SM_SYSTEMDOCKED) != 0)
235    return false;
236
237  // PlatformRoleSlate was only added in Windows 8, but prior to Win8 it is
238  // still possible to check for a mobile power profile.
239  POWER_PLATFORM_ROLE role = PowerDeterminePlatformRole();
240  bool mobile_power_profile = (role == PlatformRoleMobile);
241  bool slate_power_profile = false;
242  if (version >= base::win::VERSION_WIN8)
243    slate_power_profile = (role == PlatformRoleSlate);
244
245  if (mobile_power_profile || slate_power_profile)
246    return (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0);
247
248  return false;
249}
250
251bool DisplayVirtualKeyboard() {
252  if (base::win::GetVersion() < base::win::VERSION_WIN8)
253    return false;
254
255  static base::LazyInstance<string16>::Leaky osk_path =
256      LAZY_INSTANCE_INITIALIZER;
257
258  if (osk_path.Get().empty()) {
259    // We need to launch TabTip.exe from the location specified under the
260    // LocalServer32 key for the {{054AAE20-4BEA-4347-8A35-64A533254A9D}}
261    // CLSID.
262    // TabTip.exe is typically found at
263    // c:\program files\common files\microsoft shared\ink on English Windows.
264    // We don't want to launch TabTip.exe from
265    // c:\program files (x86)\common files\microsoft shared\ink. This path is
266    // normally found on 64 bit Windows.
267    base::win::RegKey key(HKEY_LOCAL_MACHINE,
268                          kWindows8OSKRegPath,
269                          KEY_READ | KEY_WOW64_64KEY);
270    DWORD osk_path_length = 1024;
271    if (key.ReadValue(NULL,
272                      WriteInto(&osk_path.Get(), osk_path_length),
273                      &osk_path_length,
274                      NULL) != ERROR_SUCCESS) {
275      DLOG(WARNING) << "Failed to read on screen keyboard path from registry";
276      return false;
277    }
278    size_t common_program_files_offset =
279        osk_path.Get().find(L"%CommonProgramFiles%");
280    // Typically the path to TabTip.exe read from the registry will start with
281    // %CommonProgramFiles% which needs to be replaced with the corrsponding
282    // expanded string.
283    // If the path does not begin with %CommonProgramFiles% we use it as is.
284    if (common_program_files_offset != string16::npos) {
285      // Preserve the beginning quote in the path.
286      osk_path.Get().erase(common_program_files_offset,
287                           wcslen(L"%CommonProgramFiles%"));
288      // The path read from the registry contains the %CommonProgramFiles%
289      // environment variable prefix. On 64 bit Windows the SHGetKnownFolderPath
290      // function returns the common program files path with the X86 suffix for
291      // the FOLDERID_ProgramFilesCommon value.
292      // To get the correct path to TabTip.exe we first read the environment
293      // variable CommonProgramW6432 which points to the desired common
294      // files path. Failing that we fallback to the SHGetKnownFolderPath API.
295
296      // We then replace the %CommonProgramFiles% value with the actual common
297      // files path found in the process.
298      string16 common_program_files_path;
299      scoped_ptr<wchar_t[]> common_program_files_wow6432;
300      DWORD buffer_size =
301          GetEnvironmentVariable(L"CommonProgramW6432", NULL, 0);
302      if (buffer_size) {
303        common_program_files_wow6432.reset(new wchar_t[buffer_size]);
304        GetEnvironmentVariable(L"CommonProgramW6432",
305                               common_program_files_wow6432.get(),
306                               buffer_size);
307        common_program_files_path = common_program_files_wow6432.get();
308        DCHECK(!common_program_files_path.empty());
309      } else {
310        base::win::ScopedCoMem<wchar_t> common_program_files;
311        if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, 0, NULL,
312                                        &common_program_files))) {
313          return false;
314        }
315        common_program_files_path = common_program_files;
316      }
317
318      osk_path.Get().insert(1, common_program_files_path);
319    }
320  }
321
322  HINSTANCE ret = ::ShellExecuteW(NULL,
323                                  L"",
324                                  osk_path.Get().c_str(),
325                                  NULL,
326                                  NULL,
327                                  SW_SHOW);
328  return reinterpret_cast<int>(ret) > 32;
329}
330
331bool DismissVirtualKeyboard() {
332  if (base::win::GetVersion() < base::win::VERSION_WIN8)
333    return false;
334
335  // We dismiss the virtual keyboard by generating the ESC keystroke
336  // programmatically.
337  const wchar_t kOSKClassName[] = L"IPTip_Main_Window";
338  HWND osk = ::FindWindow(kOSKClassName, NULL);
339  if (::IsWindow(osk) && ::IsWindowEnabled(osk)) {
340    PostMessage(osk, WM_SYSCOMMAND, SC_CLOSE, 0);
341    return true;
342  }
343  return false;
344}
345
346typedef HWND (*MetroRootWindow) ();
347
348enum DomainEnrollementState {UNKNOWN = -1, NOT_ENROLLED, ENROLLED};
349static volatile long int g_domain_state = UNKNOWN;
350
351bool IsEnrolledToDomain() {
352  // Doesn't make any sense to retry inside a user session because joining a
353  // domain will only kick in on a restart.
354  if (g_domain_state == UNKNOWN) {
355    LPWSTR domain;
356    NETSETUP_JOIN_STATUS join_status;
357    if(::NetGetJoinInformation(NULL, &domain, &join_status) != NERR_Success)
358      return false;
359    ::NetApiBufferFree(domain);
360    ::InterlockedCompareExchange(&g_domain_state,
361                                 join_status == ::NetSetupDomainName ?
362                                     ENROLLED : NOT_ENROLLED,
363                                 UNKNOWN);
364  }
365
366  return g_domain_state == ENROLLED;
367}
368
369void SetDomainStateForTesting(bool state) {
370  g_domain_state = state ? ENROLLED : NOT_ENROLLED;
371}
372
373bool MaybeHasSHA256Support() {
374  const base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
375
376  if (os_info->version() == base::win::VERSION_PRE_XP)
377    return false;  // Too old to have it and this OS is not supported anyway.
378
379  if (os_info->version() == base::win::VERSION_XP)
380    return os_info->service_pack().major >= 3;  // Windows XP SP3 has it.
381
382  // Assume it is missing in this case, although it may not be. This category
383  // includes Windows XP x64, and Windows Server, where a hotfix could be
384  // deployed.
385  if (os_info->version() == base::win::VERSION_SERVER_2003)
386    return false;
387
388  DCHECK(os_info->version() >= base::win::VERSION_VISTA);
389  return true;  // New enough to have SHA-256 support.
390}
391
392}  // namespace win
393}  // namespace base
394
395#ifdef _MSC_VER
396
397// There are optimizer bugs in x86 VS2012 pre-Update 1.
398#if _MSC_VER == 1700 && defined _M_IX86 && _MSC_FULL_VER < 170051106
399
400#pragma message("Relevant defines:")
401#define __STR2__(x) #x
402#define __STR1__(x) __STR2__(x)
403#define __PPOUT__(x) "#define " #x " " __STR1__(x)
404#if defined(_M_IX86)
405  #pragma message(__PPOUT__(_M_IX86))
406#endif
407#if defined(_M_X64)
408  #pragma message(__PPOUT__(_M_X64))
409#endif
410#if defined(_MSC_FULL_VER)
411  #pragma message(__PPOUT__(_MSC_FULL_VER))
412#endif
413
414#pragma message("Visual Studio 2012 x86 must be updated to at least Update 1")
415#error Must install Update 1 to Visual Studio 2012.
416#endif
417
418#endif  // _MSC_VER
419
420