• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2006-2008 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_util.h"
6 
7 #include <propvarutil.h>
8 #include <sddl.h>
9 
10 #include "base/logging.h"
11 #include "base/registry.h"
12 #include "base/scoped_handle.h"
13 #include "base/scoped_ptr.h"
14 #include "base/string_util.h"
15 
16 namespace win_util {
17 
18 #define SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(struct_name, member) \
19     offsetof(struct_name, member) + \
20     (sizeof static_cast<struct_name*>(NULL)->member)
21 #define NONCLIENTMETRICS_SIZE_PRE_VISTA \
22     SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(NONCLIENTMETRICS, lfMessageFont)
23 
24 const PROPERTYKEY kPKEYAppUserModelID =
25     { { 0x9F4C2855, 0x9F79, 0x4B39,
26     { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3, } }, 5 };
27 
GetNonClientMetrics(NONCLIENTMETRICS * metrics)28 void GetNonClientMetrics(NONCLIENTMETRICS* metrics) {
29   DCHECK(metrics);
30 
31   static const UINT SIZEOF_NONCLIENTMETRICS =
32       (GetWinVersion() >= WINVERSION_VISTA) ?
33       sizeof(NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA;
34   metrics->cbSize = SIZEOF_NONCLIENTMETRICS;
35   const bool success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
36                                               SIZEOF_NONCLIENTMETRICS, metrics,
37                                               0);
38   DCHECK(success);
39 }
40 
GetWinVersion()41 WinVersion GetWinVersion() {
42   static bool checked_version = false;
43   static WinVersion win_version = WINVERSION_PRE_2000;
44   if (!checked_version) {
45     OSVERSIONINFOEX version_info;
46     version_info.dwOSVersionInfoSize = sizeof version_info;
47     GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info));
48     if (version_info.dwMajorVersion == 5) {
49       switch (version_info.dwMinorVersion) {
50         case 0:
51           win_version = WINVERSION_2000;
52           break;
53         case 1:
54           win_version = WINVERSION_XP;
55           break;
56         case 2:
57         default:
58           win_version = WINVERSION_SERVER_2003;
59           break;
60       }
61     } else if (version_info.dwMajorVersion == 6) {
62       if (version_info.wProductType != VER_NT_WORKSTATION) {
63         // 2008 is 6.0, and 2008 R2 is 6.1.
64         win_version = WINVERSION_2008;
65       } else {
66         if (version_info.dwMinorVersion == 0) {
67           win_version = WINVERSION_VISTA;
68         } else {
69           win_version = WINVERSION_WIN7;
70         }
71       }
72     } else if (version_info.dwMajorVersion > 6) {
73       win_version = WINVERSION_WIN7;
74     }
75     checked_version = true;
76   }
77   return win_version;
78 }
79 
GetServicePackLevel(int * major,int * minor)80 void GetServicePackLevel(int* major, int* minor) {
81   DCHECK(major && minor);
82   static bool checked_version = false;
83   static int service_pack_major = -1;
84   static int service_pack_minor = -1;
85   if (!checked_version) {
86     OSVERSIONINFOEX version_info = {0};
87     version_info.dwOSVersionInfoSize = sizeof(version_info);
88     GetVersionEx(reinterpret_cast<OSVERSIONINFOW*>(&version_info));
89     service_pack_major = version_info.wServicePackMajor;
90     service_pack_minor = version_info.wServicePackMinor;
91     checked_version = true;
92   }
93 
94   *major = service_pack_major;
95   *minor = service_pack_minor;
96 }
97 
AddAccessToKernelObject(HANDLE handle,WELL_KNOWN_SID_TYPE known_sid,ACCESS_MASK access)98 bool AddAccessToKernelObject(HANDLE handle, WELL_KNOWN_SID_TYPE known_sid,
99                              ACCESS_MASK access) {
100   PSECURITY_DESCRIPTOR descriptor = NULL;
101   PACL old_dacl = NULL;
102   PACL new_dacl = NULL;
103 
104   if (ERROR_SUCCESS != GetSecurityInfo(handle, SE_KERNEL_OBJECT,
105             DACL_SECURITY_INFORMATION, NULL, NULL, &old_dacl, NULL,
106             &descriptor))
107     return false;
108 
109   BYTE sid[SECURITY_MAX_SID_SIZE] = {0};
110   DWORD size_sid = SECURITY_MAX_SID_SIZE;
111 
112   if (known_sid == WinSelfSid) {
113     // We hijack WinSelfSid when we want to add the current user instead of
114     // a known sid.
115     HANDLE token = NULL;
116     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) {
117       LocalFree(descriptor);
118       return false;
119     }
120 
121     DWORD size = sizeof(TOKEN_USER) + size_sid;
122     scoped_array<BYTE> token_user_bytes(new BYTE[size]);
123     TOKEN_USER* token_user =
124         reinterpret_cast<TOKEN_USER*>(token_user_bytes.get());
125     BOOL ret = GetTokenInformation(token, TokenUser, token_user, size, &size);
126 
127     CloseHandle(token);
128 
129     if (!ret) {
130       LocalFree(descriptor);
131       return false;
132     }
133     memcpy(sid, token_user->User.Sid, size_sid);
134   } else {
135     if (!CreateWellKnownSid(known_sid , NULL, sid, &size_sid)) {
136       LocalFree(descriptor);
137       return false;
138     }
139   }
140 
141   EXPLICIT_ACCESS new_access = {0};
142   new_access.grfAccessMode = GRANT_ACCESS;
143   new_access.grfAccessPermissions = access;
144   new_access.grfInheritance = NO_INHERITANCE;
145 
146   new_access.Trustee.pMultipleTrustee = NULL;
147   new_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
148   new_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
149   new_access.Trustee.ptstrName = reinterpret_cast<LPWSTR>(&sid);
150 
151   if (ERROR_SUCCESS != SetEntriesInAcl(1, &new_access, old_dacl, &new_dacl)) {
152     LocalFree(descriptor);
153     return false;
154   }
155 
156   DWORD result = SetSecurityInfo(handle, SE_KERNEL_OBJECT,
157                                  DACL_SECURITY_INFORMATION, NULL, NULL,
158                                  new_dacl, NULL);
159 
160   LocalFree(new_dacl);
161   LocalFree(descriptor);
162 
163   if (ERROR_SUCCESS != result)
164     return false;
165 
166   return true;
167 }
168 
GetUserSidString(std::wstring * user_sid)169 bool GetUserSidString(std::wstring* user_sid) {
170   // Get the current token.
171   HANDLE token = NULL;
172   if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
173     return false;
174   ScopedHandle token_scoped(token);
175 
176   DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
177   scoped_array<BYTE> user_bytes(new BYTE[size]);
178   TOKEN_USER* user = reinterpret_cast<TOKEN_USER*>(user_bytes.get());
179 
180   if (!::GetTokenInformation(token, TokenUser, user, size, &size))
181     return false;
182 
183   if (!user->User.Sid)
184     return false;
185 
186   // Convert the data to a string.
187   wchar_t* sid_string;
188   if (!::ConvertSidToStringSid(user->User.Sid, &sid_string))
189     return false;
190 
191   *user_sid = sid_string;
192 
193   ::LocalFree(sid_string);
194 
195   return true;
196 }
197 
GetLogonSessionOnlyDACL(SECURITY_DESCRIPTOR ** security_descriptor)198 bool GetLogonSessionOnlyDACL(SECURITY_DESCRIPTOR** security_descriptor) {
199   // Get the current token.
200   HANDLE token = NULL;
201   if (!OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
202     return false;
203   ScopedHandle token_scoped(token);
204 
205   // Get the size of the TokenGroups structure.
206   DWORD size = 0;
207   BOOL result = GetTokenInformation(token, TokenGroups, NULL,  0, &size);
208   if (result != FALSE && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
209     return false;
210 
211   // Get the data.
212   scoped_array<char> token_groups_chars(new char[size]);
213   TOKEN_GROUPS* token_groups =
214       reinterpret_cast<TOKEN_GROUPS*>(token_groups_chars.get());
215 
216   if (!GetTokenInformation(token, TokenGroups, token_groups, size, &size))
217     return false;
218 
219   // Look for the logon sid.
220   SID* logon_sid = NULL;
221   for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) {
222     if ((token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) != 0) {
223         logon_sid = static_cast<SID*>(token_groups->Groups[i].Sid);
224         break;
225     }
226   }
227 
228   if (!logon_sid)
229     return false;
230 
231   // Convert the data to a string.
232   wchar_t* sid_string;
233   if (!ConvertSidToStringSid(logon_sid, &sid_string))
234     return false;
235 
236   static const wchar_t dacl_format[] = L"D:(A;OICI;GA;;;%ls)";
237   wchar_t dacl[SECURITY_MAX_SID_SIZE + arraysize(dacl_format) + 1] = {0};
238   wsprintf(dacl, dacl_format, sid_string);
239 
240   LocalFree(sid_string);
241 
242   // Convert the string to a security descriptor
243   if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
244       dacl,
245       SDDL_REVISION_1,
246       reinterpret_cast<PSECURITY_DESCRIPTOR*>(security_descriptor),
247       NULL)) {
248     return false;
249   }
250 
251   return true;
252 }
253 
254 #pragma warning(push)
255 #pragma warning(disable:4312 4244)
SetWindowProc(HWND hwnd,WNDPROC proc)256 WNDPROC SetWindowProc(HWND hwnd, WNDPROC proc) {
257   // The reason we don't return the SetwindowLongPtr() value is that it returns
258   // the orignal window procedure and not the current one. I don't know if it is
259   // a bug or an intended feature.
260   WNDPROC oldwindow_proc =
261       reinterpret_cast<WNDPROC>(GetWindowLongPtr(hwnd, GWLP_WNDPROC));
262   SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(proc));
263   return oldwindow_proc;
264 }
265 
SetWindowUserData(HWND hwnd,void * user_data)266 void* SetWindowUserData(HWND hwnd, void* user_data) {
267   return
268       reinterpret_cast<void*>(SetWindowLongPtr(hwnd, GWLP_USERDATA,
269           reinterpret_cast<LONG_PTR>(user_data)));
270 }
271 
GetWindowUserData(HWND hwnd)272 void* GetWindowUserData(HWND hwnd) {
273   return reinterpret_cast<void*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
274 }
275 
276 // Maps to the WNDPROC for a window that was active before the subclass was
277 // installed.
278 static const wchar_t* const kHandlerKey = L"__ORIGINAL_MESSAGE_HANDLER__";
279 
IsSubclassed(HWND window,WNDPROC subclass_proc)280 bool IsSubclassed(HWND window, WNDPROC subclass_proc) {
281   WNDPROC original_handler =
282       reinterpret_cast<WNDPROC>(GetWindowLongPtr(window, GWLP_WNDPROC));
283   return original_handler == subclass_proc;
284 }
285 
Subclass(HWND window,WNDPROC subclass_proc)286 bool Subclass(HWND window, WNDPROC subclass_proc) {
287   WNDPROC original_handler =
288       reinterpret_cast<WNDPROC>(GetWindowLongPtr(window, GWLP_WNDPROC));
289   if (original_handler != subclass_proc) {
290     win_util::SetWindowProc(window, subclass_proc);
291     SetProp(window, kHandlerKey, original_handler);
292     return true;
293   }
294   return false;
295 }
296 
Unsubclass(HWND window,WNDPROC subclass_proc)297 bool Unsubclass(HWND window, WNDPROC subclass_proc) {
298   WNDPROC current_handler =
299       reinterpret_cast<WNDPROC>(GetWindowLongPtr(window, GWLP_WNDPROC));
300   if (current_handler == subclass_proc) {
301     HANDLE original_handler = GetProp(window, kHandlerKey);
302     if (original_handler) {
303       RemoveProp(window, kHandlerKey);
304       win_util::SetWindowProc(window,
305                               reinterpret_cast<WNDPROC>(original_handler));
306       return true;
307     }
308   }
309   return false;
310 }
311 
GetSuperclassWNDPROC(HWND window)312 WNDPROC GetSuperclassWNDPROC(HWND window) {
313   return reinterpret_cast<WNDPROC>(GetProp(window, kHandlerKey));
314 }
315 
316 #pragma warning(pop)
317 
IsShiftPressed()318 bool IsShiftPressed() {
319   return (::GetKeyState(VK_SHIFT) & 0x8000) == 0x8000;
320 }
321 
IsCtrlPressed()322 bool IsCtrlPressed() {
323   return (::GetKeyState(VK_CONTROL) & 0x8000) == 0x8000;
324 }
325 
IsAltPressed()326 bool IsAltPressed() {
327   return (::GetKeyState(VK_MENU) & 0x8000) == 0x8000;
328 }
329 
GetClassName(HWND window)330 std::wstring GetClassName(HWND window) {
331   // GetClassNameW will return a truncated result (properly null terminated) if
332   // the given buffer is not large enough.  So, it is not possible to determine
333   // that we got the entire class name if the result is exactly equal to the
334   // size of the buffer minus one.
335   DWORD buffer_size = MAX_PATH;
336   while (true) {
337     std::wstring output;
338     DWORD size_ret =
339         GetClassNameW(window, WriteInto(&output, buffer_size), buffer_size);
340     if (size_ret == 0)
341       break;
342     if (size_ret < (buffer_size - 1)) {
343       output.resize(size_ret);
344       return output;
345     }
346     buffer_size *= 2;
347   }
348   return std::wstring();  // error
349 }
350 
UserAccountControlIsEnabled()351 bool UserAccountControlIsEnabled() {
352   RegKey key(HKEY_LOCAL_MACHINE,
353       L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System");
354   DWORD uac_enabled;
355   if (!key.ReadValueDW(L"EnableLUA", &uac_enabled))
356     return true;
357   // Users can set the EnableLUA value to something arbitrary, like 2, which
358   // Vista will treat as UAC enabled, so we make sure it is not set to 0.
359   return (uac_enabled != 0);
360 }
361 
FormatMessage(unsigned messageid)362 std::wstring FormatMessage(unsigned messageid) {
363   wchar_t* string_buffer = NULL;
364   unsigned string_length = ::FormatMessage(
365       FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
366       FORMAT_MESSAGE_IGNORE_INSERTS, NULL, messageid, 0,
367       reinterpret_cast<wchar_t *>(&string_buffer), 0, NULL);
368 
369   std::wstring formatted_string;
370   if (string_buffer) {
371     formatted_string = string_buffer;
372     LocalFree(reinterpret_cast<HLOCAL>(string_buffer));
373   } else {
374     // The formating failed. simply convert the message value into a string.
375     SStringPrintf(&formatted_string, L"message number %d", messageid);
376   }
377   return formatted_string;
378 }
379 
FormatLastWin32Error()380 std::wstring FormatLastWin32Error() {
381   return FormatMessage(GetLastError());
382 }
383 
KeyboardCodeToWin(base::KeyboardCode keycode)384 WORD KeyboardCodeToWin(base::KeyboardCode keycode) {
385   return static_cast<WORD>(keycode);
386 }
387 
WinToKeyboardCode(WORD keycode)388 base::KeyboardCode WinToKeyboardCode(WORD keycode) {
389   return static_cast<base::KeyboardCode>(keycode);
390 }
391 
SetAppIdForPropertyStore(IPropertyStore * property_store,const wchar_t * app_id)392 bool SetAppIdForPropertyStore(IPropertyStore* property_store,
393                               const wchar_t* app_id) {
394   DCHECK(property_store);
395 
396   // App id should be less than 128 chars and contain no space. And recommended
397   // format is CompanyName.ProductName[.SubProduct.ProductNumber].
398   // See http://msdn.microsoft.com/en-us/library/dd378459%28VS.85%29.aspx
399   DCHECK(lstrlen(app_id) < 128 && wcschr(app_id, L' ') == NULL);
400 
401   PROPVARIANT property_value;
402   if (FAILED(InitPropVariantFromString(app_id, &property_value)))
403     return false;
404 
405   HRESULT result = property_store->SetValue(kPKEYAppUserModelID,
406                                             property_value);
407   if (S_OK == result)
408     result = property_store->Commit();
409 
410   PropVariantClear(&property_value);
411   return SUCCEEDED(result);
412 }
413 
414 }  // namespace win_util
415 
416 #ifdef _MSC_VER
417 //
418 // If the ASSERT below fails, please install Visual Studio 2005 Service Pack 1.
419 //
420 extern char VisualStudio2005ServicePack1Detection[10];
421 COMPILE_ASSERT(sizeof(&VisualStudio2005ServicePack1Detection) == sizeof(void*),
422                VS2005SP1Detect);
423 //
424 // Chrome requires at least Service Pack 1 for Visual Studio 2005.
425 //
426 #endif  // _MSC_VER
427 
428 #ifndef COPY_FILE_COPY_SYMLINK
429 #error You must install the Windows 2008 or Vista Software Development Kit and \
430 set it as your default include path to build this library. You can grab it by \
431 searching for "download windows sdk 2008" in your favorite web search engine.  \
432 Also make sure you register the SDK with Visual Studio, by selecting \
433 "Integrate Windows SDK with Visual Studio 2005" from the Windows SDK \
434 menu (see Start - All Programs - Microsoft Windows SDK - \
435 Visual Studio Registration).
436 #endif
437