xref: /pciutils/lib/win32-helpers.c (revision 8eee6d97)
1 /*
2  *      The PCI Library -- Win32 helper functions
3  *
4  *      Copyright (c) 2023 Pali Rohár <[email protected]>
5  *
6  *      Can be freely distributed and used under the terms of the GNU GPL v2+
7  *
8  *      SPDX-License-Identifier: GPL-2.0-or-later
9  */
10 
11 #include <windows.h>
12 
13 #include <stdio.h> /* for sprintf() */
14 
15 #include "win32-helpers.h"
16 
17 /* Unfortunately i586-mingw32msvc toolchain does not provide this constant. */
18 #ifndef PROCESS_QUERY_LIMITED_INFORMATION
19 #define PROCESS_QUERY_LIMITED_INFORMATION 0x1000
20 #endif
21 
22 #ifndef IMAGE_FILE_MACHINE_ARMNT
23 #define IMAGE_FILE_MACHINE_ARMNT 0x01c4
24 #endif
25 #ifndef IMAGE_FILE_MACHINE_IA64
26 #define IMAGE_FILE_MACHINE_IA64 0x0200
27 #endif
28 #ifndef IMAGE_FILE_MACHINE_AMD64
29 #define IMAGE_FILE_MACHINE_AMD64 0x8664
30 #endif
31 #ifndef IMAGE_FILE_MACHINE_ARM64
32 #define IMAGE_FILE_MACHINE_ARM64 0xaa64
33 #endif
34 
35 #ifndef PROCESSOR_ARCHITECTURE_INTEL
36 #define PROCESSOR_ARCHITECTURE_INTEL 0
37 #endif
38 #ifndef PROCESSOR_ARCHITECTURE_ARM
39 #define PROCESSOR_ARCHITECTURE_ARM 5
40 #endif
41 #ifndef PROCESSOR_ARCHITECTURE_IA64
42 #define PROCESSOR_ARCHITECTURE_IA64 6
43 #endif
44 #ifndef PROCESSOR_ARCHITECTURE_AMD64
45 #define PROCESSOR_ARCHITECTURE_AMD64 9
46 #endif
47 #ifndef PROCESSOR_ARCHITECTURE_ARM64
48 #define PROCESSOR_ARCHITECTURE_ARM64 12
49 #endif
50 #ifndef PROCESSOR_ARCHITECTURE_UNKNOWN
51 #define PROCESSOR_ARCHITECTURE_UNKNOWN 0xffff
52 #endif
53 
54 #if WINVER < 0x0400
55 #define wProcessorArchitecture dwOemId
56 #endif
57 
58 /* Unfortunately some toolchains do not provide this constant. */
59 #ifndef SE_IMPERSONATE_NAME
60 #define SE_IMPERSONATE_NAME TEXT("SeImpersonatePrivilege")
61 #endif
62 
63 /* Unfortunately some toolchains do not provide these constants. */
64 #ifndef SE_DACL_AUTO_INHERIT_REQ
65 #define SE_DACL_AUTO_INHERIT_REQ 0x0100
66 #endif
67 #ifndef SE_SACL_AUTO_INHERIT_REQ
68 #define SE_SACL_AUTO_INHERIT_REQ 0x0200
69 #endif
70 #ifndef SE_DACL_AUTO_INHERITED
71 #define SE_DACL_AUTO_INHERITED 0x0400
72 #endif
73 #ifndef SE_SACL_AUTO_INHERITED
74 #define SE_SACL_AUTO_INHERITED 0x0800
75 #endif
76 
77 /* Older SDK versions do not provide NtCurrentTeb symbol for X86, header files have only function declaration. */
78 #if defined(_MSC_VER) && defined(_M_IX86) && !defined(PcTeb)
79 #define PcTeb 0x18
80 #if _MSC_VER >= 1400
81 #pragma intrinsic(__readfsdword)
NtCurrentTeb(void)82 __inline struct _TEB *NtCurrentTeb(void) { return (struct _TEB *)__readfsdword(PcTeb); }
83 #else
NtCurrentTeb(void)84 __inline struct _TEB *NtCurrentTeb(void) { __asm mov eax, fs:[PcTeb] }
85 #endif
86 #endif
87 
88 /* Offset to ULONG HardErrorMode field in TEB structure, it is architecture specific. */
89 #if defined(_M_IX86) || defined(__i386__)
90 #define TEB_HARD_ERROR_MODE_OFFSET 0x0F28
91 #elif defined(_M_AMD64) || defined(__x86_64__)
92 #define TEB_HARD_ERROR_MODE_OFFSET 0x16B0
93 #endif
94 
95 /*
96  * These psapi functions are available in kernel32.dll library with K32 prefix
97  * on Windows 7 and higher systems. On older Windows systems these functions are
98  * available in psapi.dll libary without K32 prefix. So resolve pointers to
99  * these functions dynamically at runtime from the available system library.
100  * Function GetProcessImageFileNameW() is not available on Windows 2000 and
101  * older systems.
102  */
103 typedef BOOL (WINAPI *EnumProcessesProt)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded);
104 typedef DWORD (WINAPI *GetProcessImageFileNameWProt)(HANDLE hProcess, LPWSTR lpImageFileName, DWORD nSize);
105 typedef DWORD (WINAPI *GetModuleFileNameExWProt)(HANDLE hProcess, HMODULE hModule, LPWSTR lpImageFileName, DWORD nSize);
106 
107 /*
108  * These aclapi function is available in advapi.dll library on Windows 2000
109  * and higher systems.
110  */
111 typedef BOOL (WINAPI *SetSecurityDescriptorControlProt)(PSECURITY_DESCRIPTOR pSecurityDescriptor, SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet);
112 
113 /*
114  * This errhandlingapi function is available in kernel32.dll library on
115  * Windows 7 and higher systems.
116  */
117 typedef BOOL (WINAPI *SetThreadErrorModeProt)(DWORD dwNewMode, LPDWORD lpOldMode);
118 
119 
120 static DWORD
format_message_from_system(DWORD win32_error_id,DWORD lang_id,LPSTR buffer,DWORD size)121 format_message_from_system(DWORD win32_error_id, DWORD lang_id, LPSTR buffer, DWORD size)
122 {
123   return FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, win32_error_id, lang_id, buffer, size, NULL);
124 }
125 
126 const char *
win32_strerror(DWORD win32_error_id)127 win32_strerror(DWORD win32_error_id)
128 {
129   /*
130    * Use static buffer which is large enough.
131    * Hopefully no Win32 API error message string is longer than 4 kB.
132    */
133   static char buffer[4096];
134   DWORD len;
135 
136   /*
137    * If it is possible show error messages in US English language.
138    * International Windows editions do not have to provide error
139    * messages in English language, so fallback to the language
140    * which system provides (neutral).
141    */
142   len = format_message_from_system(win32_error_id, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), buffer, sizeof(buffer));
143   if (!len)
144     len = format_message_from_system(win32_error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer));
145 
146   /* FormatMessage() automatically appends ".\r\n" to the error message. */
147   if (len && buffer[len-1] == '\n')
148     buffer[--len] = '\0';
149   if (len && buffer[len-1] == '\r')
150     buffer[--len] = '\0';
151   if (len && buffer[len-1] == '.')
152     buffer[--len] = '\0';
153 
154   if (!len)
155     sprintf(buffer, "Unknown Win32 error %lu", win32_error_id);
156 
157   return buffer;
158 }
159 
160 USHORT
win32_get_process_machine(void)161 win32_get_process_machine(void)
162 {
163   IMAGE_DOS_HEADER *dos_header;
164   IMAGE_NT_HEADERS *nt_header;
165 
166   dos_header = (IMAGE_DOS_HEADER *)GetModuleHandle(NULL);
167   if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
168     return IMAGE_FILE_MACHINE_UNKNOWN;
169 
170   nt_header = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew);
171   if (nt_header->Signature != IMAGE_NT_SIGNATURE)
172     return IMAGE_FILE_MACHINE_UNKNOWN;
173 
174   return nt_header->FileHeader.Machine;
175 }
176 
177 BOOL
win32_is_non_nt_system(void)178 win32_is_non_nt_system(void)
179 {
180   OSVERSIONINFOA version;
181   version.dwOSVersionInfoSize = sizeof(version);
182   return GetVersionExA(&version) && version.dwPlatformId < VER_PLATFORM_WIN32_NT;
183 }
184 
185 BOOL
win32_is_32bit_on_64bit_system(void)186 win32_is_32bit_on_64bit_system(void)
187 {
188 #ifdef _WIN64
189   return FALSE;
190 #else
191   BOOL (WINAPI *MyIsWow64Process)(HANDLE, PBOOL);
192   HMODULE kernel32;
193   BOOL is_wow64;
194 
195   /*
196    * 32-bit process running on 64-bit system is called Wow64 process.
197    * So AMD64 process running on ARM64 system is not Wow64 process.
198    * Check for Wow64 process via IsWow64Process() function exported
199    * from 32-bit kernel32.dll library available on the 64-bit systems.
200    * Resolve pointer to this function at runtime as this code path is
201    * primary running on 32-bit systems where are not available 64-bit
202    * functions.
203    */
204 
205   kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
206   if (!kernel32)
207     return FALSE;
208 
209   MyIsWow64Process = (void *)GetProcAddress(kernel32, "IsWow64Process");
210   if (!MyIsWow64Process)
211     return FALSE;
212 
213   if (!MyIsWow64Process(GetCurrentProcess(), &is_wow64))
214     return FALSE;
215 
216   return is_wow64;
217 #endif
218 }
219 
220 BOOL
win32_is_32bit_on_win8_64bit_system(void)221 win32_is_32bit_on_win8_64bit_system(void)
222 {
223 #ifdef _WIN64
224   return FALSE;
225 #else
226   OSVERSIONINFOA version;
227 
228   /* Check for Windows 8 (NT 6.2). */
229   version.dwOSVersionInfoSize = sizeof(version);
230   if (!GetVersionExA(&version) ||
231       version.dwPlatformId != VER_PLATFORM_WIN32_NT ||
232       version.dwMajorVersion < 6 ||
233       (version.dwMajorVersion == 6 && version.dwMinorVersion < 2))
234     return FALSE;
235 
236   return win32_is_32bit_on_64bit_system();
237 #endif
238 }
239 
240 BOOL
win32_is_not_native_process(USHORT * native_machine_ptr)241 win32_is_not_native_process(USHORT *native_machine_ptr)
242 {
243   BOOL (WINAPI *MyIsWow64Process2)(HANDLE, PUSHORT, PUSHORT);
244   void (WINAPI *MyGetNativeSystemInfo)(LPSYSTEM_INFO);
245   SYSTEM_INFO system_info;
246   USHORT process_machine;
247   USHORT native_machine;
248   HMODULE kernel32;
249 
250   /*
251    * Process is not native if the process architecture does not match the
252    * native machine architecture. Every Wow64 process is not native (which
253    * means 32-bit process on 64-bit system) but there are also non-Wow64
254    * processes which are not native (e.g. AMD64 process on ARM64 system).
255    */
256 
257   kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
258   if (!kernel32)
259     return FALSE;
260 
261   /*
262    * First try to use IsWow64Process2() function to determinate if the process
263    * is native or not. This function is available since Windows 10.
264    */
265   MyIsWow64Process2 = (void *)GetProcAddress(kernel32, "IsWow64Process2");
266   if (MyIsWow64Process2 && MyIsWow64Process2(GetCurrentProcess(), &process_machine, &native_machine))
267     {
268       /*
269        * Return value from IsWow64Process2() does not indicate if the process
270        * is Wow64, but rather it indicates if the function succeed or not and
271        * filled process_machine and native_machine values.
272        * Process is Wow64 if process_machine is not IMAGE_FILE_MACHINE_UNKNOWN.
273        * For non-Wow64 processes this function does not provide information
274        * about process architecture, so it cannot be used for detecting if the
275        * non-Wow64 process is native or not.
276        */
277       if (process_machine != IMAGE_FILE_MACHINE_UNKNOWN)
278         {
279           if (native_machine_ptr)
280             *native_machine_ptr = native_machine;
281           return TRUE;
282         }
283 
284       /*
285        * For non-Wow64 processes retrieve process architecture and compare it
286        * with native machine architecture. This will distinguish between native
287        * and non-native non-Wow64 processes.
288        */
289       process_machine = win32_get_process_machine();
290       if (process_machine != native_machine)
291         {
292           if (native_machine_ptr)
293             *native_machine_ptr = native_machine;
294           return TRUE;
295         }
296       return FALSE;
297     }
298 
299   /*
300    * If function IsWow64Process2() is not available or failed then fallback to
301    * IsWow64Process() via win32_is_32bit_on_64bit_system() wrapper. For Wow64
302    * processes is function GetNativeSystemInfo() returning the correct native
303    * machine architecture. For non-Wow64 it is same as GetSystemInfo() and
304    * therefore does NOT return native system information, despite the name
305    * (this happens for example for AMD64 process on ARM64 system).
306    */
307   if (win32_is_32bit_on_64bit_system())
308     {
309       system_info.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
310       MyGetNativeSystemInfo = (void *)GetProcAddress(kernel32, "GetNativeSystemInfo");
311       if (MyGetNativeSystemInfo)
312         MyGetNativeSystemInfo(&system_info);
313       switch (system_info.wProcessorArchitecture)
314         {
315         case PROCESSOR_ARCHITECTURE_INTEL:
316           *native_machine_ptr = IMAGE_FILE_MACHINE_I386;
317           break;
318         case PROCESSOR_ARCHITECTURE_IA64:
319           *native_machine_ptr = IMAGE_FILE_MACHINE_IA64;
320           break;
321         case PROCESSOR_ARCHITECTURE_ARM:
322           *native_machine_ptr = IMAGE_FILE_MACHINE_ARMNT;
323           break;
324         case PROCESSOR_ARCHITECTURE_AMD64:
325           *native_machine_ptr = IMAGE_FILE_MACHINE_AMD64;
326           break;
327         case PROCESSOR_ARCHITECTURE_ARM64:
328           *native_machine_ptr = IMAGE_FILE_MACHINE_ARM64;
329           break;
330         default:
331           *native_machine_ptr = IMAGE_FILE_MACHINE_UNKNOWN;
332         }
333       return TRUE;
334     }
335 
336   /*
337    * It looks like that IsWow64Process2() is currently the only function which
338    * can determinate if the non-Wow64 process is native or not. So if the
339    * IsWow64Process2() function is not available (or failed) and process in not
340    * Wow64 then expects that it is native process.
341    */
342   return FALSE;
343 }
344 
345 /*
346  * Change error mode of the current thread. If it is not possible then change
347  * error mode of the whole process. Always returns previous error mode.
348  */
349 UINT
win32_change_error_mode(UINT new_mode)350 win32_change_error_mode(UINT new_mode)
351 {
352   SetThreadErrorModeProt MySetThreadErrorMode = NULL;
353   OSVERSIONINFOA version;
354   HMODULE kernel32;
355   HMODULE ntdll;
356   DWORD old_mode;
357 
358   /*
359    * Function SetThreadErrorMode() was introduced in Windows 7, so use
360    * GetProcAddress() for compatibility with older systems.
361    */
362   kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
363   if (kernel32)
364     MySetThreadErrorMode = (SetThreadErrorModeProt)(void(*)(void))GetProcAddress(kernel32, "SetThreadErrorMode");
365 
366   /*
367    * Function RtlSetThreadErrorMode() was introduced in Windows XP x64
368    * and Windows Server 2003. Use GetProcAddress() as it is in ntdll.dll.
369    */
370   if (!MySetThreadErrorMode)
371     {
372       ntdll = GetModuleHandle(TEXT("ntdll.dll"));
373       if (ntdll)
374         MySetThreadErrorMode = (SetThreadErrorModeProt)(void(*)(void))GetProcAddress(ntdll, "RtlSetThreadErrorMode");
375     }
376 
377   if (MySetThreadErrorMode &&
378       MySetThreadErrorMode(new_mode, &old_mode))
379     return old_mode;
380 
381 #ifdef TEB_HARD_ERROR_MODE_OFFSET
382   /*
383    * On Windows NT 4.0+ systems fallback to thread HardErrorMode API.
384    * It depends on architecture specific offset for HardErrorMode field in TEB.
385    */
386   version.dwOSVersionInfoSize = sizeof(version);
387   if (GetVersionExA(&version) &&
388       version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
389       version.dwMajorVersion >= 4)
390     {
391       ULONG *hard_error_mode_ptr = (ULONG *)((BYTE *)NtCurrentTeb() + TEB_HARD_ERROR_MODE_OFFSET);
392       old_mode = *hard_error_mode_ptr;
393       *hard_error_mode_ptr = new_mode;
394       return old_mode;
395     }
396 #endif
397 
398   /*
399    * Fallback to function SetErrorMode() which modifies error mode of the
400    * whole process and returns old mode.
401    */
402   return SetErrorMode(new_mode);
403 }
404 
405 /*
406  * Check if the current thread has particular privilege in current active access
407  * token. Case when it not possible to determinate it (e.g. current thread does
408  * not have permission to open its own current active access token) is evaluated
409  * as thread does not have that privilege.
410  */
411 BOOL
win32_have_privilege(LUID luid_privilege)412 win32_have_privilege(LUID luid_privilege)
413 {
414   PRIVILEGE_SET priv;
415   HANDLE token;
416   BOOL ret;
417 
418   /*
419    * If the current thread does not have active access token then thread
420    * uses primary process access token for all permission checks.
421    */
422   if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
423       (GetLastError() != ERROR_NO_TOKEN ||
424        !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
425     return FALSE;
426 
427   priv.PrivilegeCount = 1;
428   priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
429   priv.Privilege[0].Luid = luid_privilege;
430   priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
431 
432   if (!PrivilegeCheck(token, &priv, &ret))
433     return FALSE;
434 
435   return ret;
436 }
437 
438 /*
439  * Enable or disable particular privilege in specified access token.
440  *
441  * Note that it is not possible to disable privilege in access token with
442  * SE_PRIVILEGE_ENABLED_BY_DEFAULT attribute. This function does not check
443  * this case and incorrectly returns no error even when disabling failed.
444  * Rationale for this decision: Simplification of this function as WinAPI
445  * call AdjustTokenPrivileges() does not signal error in this case too.
446  */
447 static BOOL
set_privilege(HANDLE token,LUID luid_privilege,BOOL enable)448 set_privilege(HANDLE token, LUID luid_privilege, BOOL enable)
449 {
450   TOKEN_PRIVILEGES token_privileges;
451 
452   token_privileges.PrivilegeCount = 1;
453   token_privileges.Privileges[0].Luid = luid_privilege;
454   token_privileges.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
455 
456   /*
457    * WinAPI function AdjustTokenPrivileges() success also when not all
458    * privileges were enabled. It is always required to check for failure
459    * via GetLastError() call. AdjustTokenPrivileges() always sets error
460    * also when it success, as opposite to other WinAPI functions.
461    */
462   if (!AdjustTokenPrivileges(token, FALSE, &token_privileges, sizeof(token_privileges), NULL, NULL) ||
463       GetLastError() != ERROR_SUCCESS)
464     return FALSE;
465 
466   return TRUE;
467 }
468 
469 /*
470  * Change access token for the current thread to new specified access token.
471  * Previously active access token is stored in old_token variable and can be
472  * used for reverting to this access token. It is set to NULL if the current
473  * thread previously used primary process access token.
474  */
475 BOOL
win32_change_token(HANDLE new_token,HANDLE * old_token)476 win32_change_token(HANDLE new_token, HANDLE *old_token)
477 {
478   HANDLE token;
479 
480   if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
481     {
482       if (GetLastError() != ERROR_NO_TOKEN)
483         return FALSE;
484       token = NULL;
485     }
486 
487   if (!ImpersonateLoggedOnUser(new_token))
488     {
489       if (token)
490         CloseHandle(token);
491       return FALSE;
492     }
493 
494   *old_token = token;
495   return TRUE;
496 }
497 
498 /*
499  * Change access token for the current thread to the primary process access
500  * token. This function fails also when the current thread already uses primary
501  * process access token.
502  */
503 static BOOL
change_token_to_primary(HANDLE * old_token)504 change_token_to_primary(HANDLE *old_token)
505 {
506   HANDLE token;
507 
508   if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
509     return FALSE;
510 
511   RevertToSelf();
512 
513   *old_token = token;
514   return TRUE;
515 }
516 
517 /*
518  * Revert to the specified access token for the current thread. When access
519  * token is specified as NULL then revert to the primary process access token.
520  * Use to revert after win32_change_token() or change_token_to_primary() call.
521  */
522 VOID
win32_revert_to_token(HANDLE token)523 win32_revert_to_token(HANDLE token)
524 {
525   /*
526    * If SetThreadToken() call fails then there is no option to revert to
527    * the specified previous thread access token. So in this case revert to
528    * the primary process access token.
529    */
530   if (!token || !SetThreadToken(NULL, token))
531     RevertToSelf();
532   if (token)
533     CloseHandle(token);
534 }
535 
536 /*
537  * Enable particular privilege for the current thread. And set method how to
538  * revert this privilege (if to revert whole token or only privilege).
539  */
540 BOOL
win32_enable_privilege(LUID luid_privilege,HANDLE * revert_token,BOOL * revert_only_privilege)541 win32_enable_privilege(LUID luid_privilege, HANDLE *revert_token, BOOL *revert_only_privilege)
542 {
543   HANDLE thread_token;
544   HANDLE new_token;
545 
546   if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &thread_token))
547     {
548       if (set_privilege(thread_token, luid_privilege, TRUE))
549         {
550           /*
551            * Indicate that correct revert method is just to
552            * disable privilege in access token.
553            */
554           if (revert_token && revert_only_privilege)
555             {
556               *revert_token = thread_token;
557               *revert_only_privilege = TRUE;
558             }
559           else
560             {
561               CloseHandle(thread_token);
562             }
563           return TRUE;
564         }
565       CloseHandle(thread_token);
566       /*
567        * If enabling privilege failed then try to enable it via
568        * primary process access token.
569        */
570     }
571 
572   /*
573    * If the current thread has already active thread access token then
574    * open it with just impersonate right as it would be used only for
575    * future revert.
576    */
577   if (revert_token && revert_only_privilege)
578     {
579       if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &thread_token))
580         {
581           if (GetLastError() != ERROR_NO_TOKEN)
582             return FALSE;
583           thread_token = NULL;
584         }
585 
586       /*
587        * If current thread has no access token (and uses primary
588        * process access token) or it does not have permission to
589        * adjust privileges or it does not have specified privilege
590        * then create a copy of the primary process access token,
591        * assign it for the current thread (= impersonate self)
592        * and then try adjusting privilege again.
593        */
594       if (!ImpersonateSelf(SecurityImpersonation))
595         {
596           if (thread_token)
597             CloseHandle(thread_token);
598           return FALSE;
599         }
600     }
601 
602   if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &new_token))
603     {
604       /* thread_token is set only when we were asked for revert method. */
605       if (revert_token && revert_only_privilege)
606         win32_revert_to_token(thread_token);
607       return FALSE;
608     }
609 
610   if (!set_privilege(new_token, luid_privilege, TRUE))
611     {
612       CloseHandle(new_token);
613       /* thread_token is set only when we were asked for revert method. */
614       if (revert_token && revert_only_privilege)
615         win32_revert_to_token(thread_token);
616       return FALSE;
617     }
618 
619   /*
620    * Indicate that correct revert method is to change to the previous
621    * access token. Either to the primary process access token or to the
622    * previous thread access token.
623    */
624   if (revert_token && revert_only_privilege)
625     {
626       *revert_token = thread_token;
627       *revert_only_privilege = FALSE;
628     }
629   return TRUE;
630 }
631 
632 /*
633  * Revert particular privilege for the current thread was previously enabled by
634  * win32_enable_privilege() call. Either disable privilege in specified access token
635  * or revert to previous access token.
636  */
637 VOID
win32_revert_privilege(LUID luid_privilege,HANDLE revert_token,BOOL revert_only_privilege)638 win32_revert_privilege(LUID luid_privilege, HANDLE revert_token, BOOL revert_only_privilege)
639 {
640   if (revert_only_privilege)
641     {
642       set_privilege(revert_token, luid_privilege, FALSE);
643       CloseHandle(revert_token);
644     }
645   else
646     {
647       win32_revert_to_token(revert_token);
648     }
649 }
650 
651 /*
652  * Return owner of the access token used by the current thread. Buffer for
653  * returned owner needs to be released by LocalFree() call.
654  */
655 static TOKEN_OWNER *
get_current_token_owner(VOID)656 get_current_token_owner(VOID)
657 {
658   HANDLE token;
659   DWORD length;
660   TOKEN_OWNER *owner;
661 
662   /*
663    * If the current thread does not have active access token then thread
664    * uses primary process access token for all permission checks.
665    */
666   if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
667       (GetLastError() != ERROR_NO_TOKEN ||
668        !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
669     return NULL;
670 
671   if (!GetTokenInformation(token, TokenOwner, NULL, 0, &length) &&
672       GetLastError() != ERROR_INSUFFICIENT_BUFFER)
673     {
674       CloseHandle(token);
675       return NULL;
676     }
677 
678 retry:
679   owner = (TOKEN_OWNER *)LocalAlloc(LPTR, length);
680   if (!owner)
681     {
682       CloseHandle(token);
683       return NULL;
684     }
685 
686   if (!GetTokenInformation(token, TokenOwner, owner, length, &length))
687     {
688       /*
689        * Length of token owner (SID) buffer between two get calls may
690        * changes (e.g. by another thread of process), so retry.
691        */
692       if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
693         {
694           LocalFree(owner);
695           goto retry;
696         }
697       LocalFree(owner);
698       CloseHandle(token);
699       return NULL;
700     }
701 
702   CloseHandle(token);
703   return owner;
704 }
705 
706 /*
707  * Create a new security descriptor in absolute form from relative form.
708  * Newly created security descriptor in absolute form is stored in linear buffer.
709  */
710 static PSECURITY_DESCRIPTOR
create_relsd_from_abssd(PSECURITY_DESCRIPTOR rel_security_descriptor)711 create_relsd_from_abssd(PSECURITY_DESCRIPTOR rel_security_descriptor)
712 {
713   PBYTE abs_security_descriptor_buffer;
714   DWORD abs_security_descriptor_size=0, abs_dacl_size=0, abs_sacl_size=0, abs_owner_size=0, abs_primary_group_size=0;
715 
716   if (!MakeAbsoluteSD(rel_security_descriptor,
717         NULL, &abs_security_descriptor_size,
718         NULL, &abs_dacl_size,
719         NULL, &abs_sacl_size,
720         NULL, &abs_owner_size,
721         NULL, &abs_primary_group_size) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
722     return NULL;
723 
724   abs_security_descriptor_buffer = (PBYTE)LocalAlloc(LPTR, abs_security_descriptor_size+abs_dacl_size+abs_sacl_size+abs_owner_size+abs_primary_group_size);
725   if (!abs_security_descriptor_buffer)
726     return NULL;
727 
728   if (!MakeAbsoluteSD(rel_security_descriptor,
729         (PSECURITY_DESCRIPTOR)abs_security_descriptor_buffer, &abs_security_descriptor_size,
730         (PACL)(abs_security_descriptor_buffer+abs_security_descriptor_size), &abs_dacl_size,
731         (PACL)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size), &abs_sacl_size,
732         (PSID)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size+abs_sacl_size), &abs_owner_size,
733         (PSID)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size+abs_sacl_size+abs_owner_size), &abs_primary_group_size))
734     return NULL;
735 
736   return (PSECURITY_DESCRIPTOR)abs_security_descriptor_buffer;
737 }
738 
739 /*
740  * Prepare security descriptor obtained by GetKernelObjectSecurity() so it can be
741  * passed to SetKernelObjectSecurity() as identity operation. It modifies control
742  * flags of security descriptor, which is needed for Windows 2000 and new.
743  */
744 static BOOL
prepare_security_descriptor_for_set_operation(PSECURITY_DESCRIPTOR security_descriptor)745 prepare_security_descriptor_for_set_operation(PSECURITY_DESCRIPTOR security_descriptor)
746 {
747   SetSecurityDescriptorControlProt MySetSecurityDescriptorControl;
748   SECURITY_DESCRIPTOR_CONTROL bits_mask;
749   SECURITY_DESCRIPTOR_CONTROL bits_set;
750   SECURITY_DESCRIPTOR_CONTROL control;
751   OSVERSIONINFO version;
752   HMODULE advapi32;
753   DWORD revision;
754 
755   /*
756    * SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED are flags introduced in
757    * Windows 2000 to control client-side automatic inheritance (client - user
758    * process - is responsible for propagating inherited ACEs to subobjects).
759    * To prevent applications which do not understand client-side automatic
760    * inheritance (applications created prior Windows 2000 or which use low
761    * level API like SetKernelObjectSecurity()) to unintentionally set those
762    * SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED control flags when
763    * coping them from other security descriptor.
764    *
765    * As we are not modifying existing ACEs, we are compatible with Windows 2000
766    * client-side automatic inheritance model and therefore prepare security
767    * descriptor for SetKernelObjectSecurity() to not clear existing automatic
768    * inheritance control flags.
769    *
770    * Control flags SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED are set
771    * into security object only when they are set together with set-only flags
772    * SE_DACL_AUTO_INHERIT_REQ and SE_SACL_AUTO_INHERIT_REQ. Those flags are
773    * never received by GetKernelObjectSecurity() and are just commands for
774    * SetKernelObjectSecurity() how to interpret SE_DACL_AUTO_INHERITED and
775    * SE_SACL_AUTO_INHERITED flags.
776    *
777    * Function symbol SetSecurityDescriptorControl is not available in the
778    * older versions of advapi32.dll library, so resolve it at runtime.
779    */
780 
781   version.dwOSVersionInfoSize = sizeof(version);
782   if (!GetVersionEx(&version) ||
783       version.dwPlatformId != VER_PLATFORM_WIN32_NT ||
784       version.dwMajorVersion < 5)
785     return TRUE;
786 
787   if (!GetSecurityDescriptorControl(security_descriptor, &control, &revision))
788     return FALSE;
789 
790   bits_mask = 0;
791   bits_set = 0;
792 
793   if (control & SE_DACL_AUTO_INHERITED)
794     {
795       bits_mask |= SE_DACL_AUTO_INHERIT_REQ;
796       bits_set |= SE_DACL_AUTO_INHERIT_REQ;
797     }
798 
799   if (control & SE_SACL_AUTO_INHERITED)
800     {
801       bits_mask |= SE_SACL_AUTO_INHERIT_REQ;
802       bits_set |= SE_SACL_AUTO_INHERIT_REQ;
803     }
804 
805   if (!bits_mask)
806     return TRUE;
807 
808   advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
809   if (!advapi32)
810     return FALSE;
811 
812   MySetSecurityDescriptorControl = (SetSecurityDescriptorControlProt)(void(*)(void))GetProcAddress(advapi32, "SetSecurityDescriptorControl");
813   if (!MySetSecurityDescriptorControl)
814     return FALSE;
815 
816   if (!MySetSecurityDescriptorControl(security_descriptor, bits_mask, bits_set))
817     return FALSE;
818 
819   return TRUE;
820 }
821 
822 /*
823  * Grant particular permissions in the primary access token of the specified
824  * process for the owner of current thread token and set old DACL of the
825  * process access token for reverting permissions. Security descriptor is
826  * just memory buffer for old DACL.
827  */
828 static BOOL
grant_process_token_dacl_permissions(HANDLE process,DWORD permissions,HANDLE * token,PSECURITY_DESCRIPTOR * old_security_descriptor)829 grant_process_token_dacl_permissions(HANDLE process, DWORD permissions, HANDLE *token, PSECURITY_DESCRIPTOR *old_security_descriptor)
830 {
831   TOKEN_OWNER *owner;
832   PACL old_dacl;
833   BOOL old_dacl_present;
834   BOOL old_dacl_defaulted;
835   PACL new_dacl;
836   WORD new_dacl_size;
837   PSECURITY_DESCRIPTOR new_security_descriptor;
838   DWORD length;
839 
840   owner = get_current_token_owner();
841   if (!owner)
842     return FALSE;
843 
844   /*
845    * READ_CONTROL is required for GetSecurityInfo(DACL_SECURITY_INFORMATION)
846    * and WRITE_DAC is required for SetSecurityInfo(DACL_SECURITY_INFORMATION).
847    */
848   if (!OpenProcessToken(process, READ_CONTROL | WRITE_DAC, token))
849     {
850       LocalFree(owner);
851       return FALSE;
852     }
853 
854   if (!GetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, NULL, 0, &length) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
855     {
856       LocalFree(owner);
857       CloseHandle(*token);
858       return FALSE;
859     }
860 
861 retry:
862   *old_security_descriptor = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, length);
863   if (!*old_security_descriptor)
864     {
865       LocalFree(owner);
866       CloseHandle(*token);
867       return FALSE;
868     }
869 
870   if (!GetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, *old_security_descriptor, length, &length))
871     {
872       /*
873        * Length of the security descriptor between two get calls
874        * may changes (e.g. by another thread of process), so retry.
875        */
876       if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
877         {
878           LocalFree(*old_security_descriptor);
879           goto retry;
880         }
881       LocalFree(*old_security_descriptor);
882       LocalFree(owner);
883       CloseHandle(*token);
884       return FALSE;
885     }
886 
887   if (!prepare_security_descriptor_for_set_operation(*old_security_descriptor))
888     {
889       LocalFree(*old_security_descriptor);
890       LocalFree(owner);
891       CloseHandle(*token);
892       return FALSE;
893     }
894 
895   /* Retrieve the current DACL from security descriptor including present and defaulted properties. */
896   if (!GetSecurityDescriptorDacl(*old_security_descriptor, &old_dacl_present, &old_dacl, &old_dacl_defaulted))
897     {
898       LocalFree(*old_security_descriptor);
899       LocalFree(owner);
900       CloseHandle(*token);
901       return FALSE;
902     }
903 
904   /*
905    * If DACL is not present then system grants full access to everyone. It this
906    * case do not modify DACL as it just adds one ACL allow rule for us, which
907    * automatically disallow access to anybody else which had access before.
908    */
909   if (!old_dacl_present || !old_dacl)
910     {
911       LocalFree(*old_security_descriptor);
912       LocalFree(owner);
913       *old_security_descriptor = NULL;
914       return TRUE;
915     }
916 
917   /* Create new DACL which would be copy of the current old one. */
918   new_dacl_size = old_dacl->AclSize + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(owner->Owner) - sizeof(DWORD);
919   new_dacl = (PACL)LocalAlloc(LPTR, new_dacl_size);
920   if (!new_dacl)
921     {
922       LocalFree(*old_security_descriptor);
923       LocalFree(owner);
924       CloseHandle(*token);
925       return FALSE;
926     }
927 
928   /*
929    * Initialize new DACL structure to the same format as was the old one.
930    * Set new explicit access for the owner of the current thread access
931    * token with non-inherited granting access to specified permissions.
932    * This permission is added in the first ACE, so has the highest priority.
933    */
934   if (!InitializeAcl(new_dacl, new_dacl_size, old_dacl->AclRevision) ||
935       !AddAccessAllowedAce(new_dacl, ACL_REVISION2, permissions, owner->Owner))
936     {
937       LocalFree(new_dacl);
938       LocalFree(*old_security_descriptor);
939       LocalFree(owner);
940       CloseHandle(*token);
941       return FALSE;
942     }
943 
944   /*
945    * Now (after setting our new permissions) append all ACE entries from the
946    * old DACL to the new DACL, which preserve all other existing permissions.
947    */
948   if (old_dacl->AceCount > 0)
949     {
950       WORD ace_index;
951       LPVOID ace;
952 
953       for (ace_index = 0; ace_index < old_dacl->AceCount; ace_index++)
954         {
955           if (!GetAce(old_dacl, ace_index, &ace) ||
956               !AddAce(new_dacl, old_dacl->AclRevision, MAXDWORD, ace, ((PACE_HEADER)ace)->AceSize))
957             {
958               LocalFree(new_dacl);
959               LocalFree(*old_security_descriptor);
960               LocalFree(owner);
961               CloseHandle(*token);
962               return FALSE;
963             }
964         }
965     }
966 
967   /*
968    * Create copy of the old security descriptor, so we can modify its DACL.
969    * Function SetSecurityDescriptorDacl() works only with security descriptors
970    * in absolute format. So use our helper function create_relsd_from_abssd()
971    * for converting security descriptor from relative format (which is returned
972    * by GetKernelObjectSecurity() function) to the absolute format.
973    */
974   new_security_descriptor = create_relsd_from_abssd(*old_security_descriptor);
975   if (!new_security_descriptor)
976     {
977       LocalFree(new_dacl);
978       LocalFree(*old_security_descriptor);
979       LocalFree(owner);
980       CloseHandle(*token);
981       return FALSE;
982     }
983 
984   /*
985    * In the new security descriptor replace old DACL by the new DACL (which has
986    * new permissions) and then set this new security descriptor to the token,
987    * so token would have new access permissions.
988    */
989   if (!SetSecurityDescriptorDacl(new_security_descriptor, TRUE, new_dacl, FALSE) ||
990       !SetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, new_security_descriptor))
991     {
992       LocalFree(new_security_descriptor);
993       LocalFree(new_dacl);
994       LocalFree(*old_security_descriptor);
995       LocalFree(owner);
996       CloseHandle(*token);
997       return FALSE;
998     }
999 
1000   LocalFree(new_security_descriptor);
1001   LocalFree(new_dacl);
1002   LocalFree(owner);
1003   return TRUE;
1004 }
1005 
1006 /*
1007  * Revert particular granted permissions in specified access token done by
1008  * grant_process_token_dacl_permissions() call.
1009  */
1010 static VOID
revert_token_dacl_permissions(HANDLE token,PSECURITY_DESCRIPTOR old_security_descriptor)1011 revert_token_dacl_permissions(HANDLE token, PSECURITY_DESCRIPTOR old_security_descriptor)
1012 {
1013   SetKernelObjectSecurity(token, DACL_SECURITY_INFORMATION, old_security_descriptor);
1014   LocalFree(old_security_descriptor);
1015   CloseHandle(token);
1016 }
1017 
1018 /*
1019  * Open process handle specified by the process id with the query right and
1020  * optionally also with vm read right.
1021  */
1022 static HANDLE
open_process_for_query(DWORD pid,BOOL with_vm_read)1023 open_process_for_query(DWORD pid, BOOL with_vm_read)
1024 {
1025   BOOL revert_only_privilege;
1026   LUID luid_debug_privilege;
1027   OSVERSIONINFO version;
1028   DWORD process_right;
1029   HANDLE revert_token;
1030   HANDLE process;
1031 
1032   /*
1033    * Some processes on Windows Vista and higher systems can be opened only
1034    * with PROCESS_QUERY_LIMITED_INFORMATION right. This right is enough
1035    * for accessing primary process token. But this right is not supported
1036    * on older pre-Vista systems. When the current thread on these older
1037    * systems does not have Debug privilege then OpenProcess() fails with
1038    * ERROR_ACCESS_DENIED. If the current thread has Debug privilege then
1039    * OpenProcess() success and returns handle to requested process.
1040    * Problem is that this handle does not have PROCESS_QUERY_INFORMATION
1041    * right and so cannot be used for accessing primary process token
1042    * on those older systems. Moreover it has zero rights and therefore
1043    * such handle is fully useless. So never try to use open process with
1044    * PROCESS_QUERY_LIMITED_INFORMATION right on older systems than
1045    * Windows Vista (NT 6.0).
1046    */
1047   version.dwOSVersionInfoSize = sizeof(version);
1048   if (GetVersionEx(&version) &&
1049       version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
1050       version.dwMajorVersion >= 6)
1051     process_right = PROCESS_QUERY_LIMITED_INFORMATION;
1052   else
1053     process_right = PROCESS_QUERY_INFORMATION;
1054 
1055   if (with_vm_read)
1056     process_right |= PROCESS_VM_READ;
1057 
1058   process = OpenProcess(process_right, FALSE, pid);
1059   if (process)
1060     return process;
1061 
1062   /*
1063    * It is possible to open only processes to which owner of the current
1064    * thread access token has permissions. For opening other processing it
1065    * is required to have Debug privilege enabled. By default local
1066    * administrators have this privilege, but it is disabled. So try to
1067    * enable it and then try to open process again.
1068    */
1069 
1070   if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
1071     return NULL;
1072 
1073   if (!win32_enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
1074     return NULL;
1075 
1076   process = OpenProcess(process_right, FALSE, pid);
1077 
1078   win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
1079 
1080   return process;
1081 }
1082 
1083 /*
1084  * Check if process image path name (wide string) matches exe file name
1085  * (7-bit ASCII string). Do case-insensitive string comparison. Process
1086  * image path name can be in any namespace format (DOS, Win32, UNC, ...).
1087  */
1088 static BOOL
check_process_name(LPCWSTR path,DWORD path_length,LPCSTR exe_file)1089 check_process_name(LPCWSTR path, DWORD path_length, LPCSTR exe_file)
1090 {
1091   DWORD exe_file_length;
1092   WCHAR c1;
1093   UCHAR c2;
1094   DWORD i;
1095 
1096   exe_file_length = 0;
1097   while (exe_file[exe_file_length] != '\0')
1098     exe_file_length++;
1099 
1100   /* Path must have backslash before exe file name. */
1101   if (exe_file_length >= path_length ||
1102       path[path_length-exe_file_length-1] != L'\\')
1103     return FALSE;
1104 
1105   for (i = 0; i < exe_file_length; i++)
1106     {
1107       c1 = path[path_length-exe_file_length+i];
1108       c2 = exe_file[i];
1109       /*
1110        * Input string for comparison is 7-bit ASCII and file name part
1111        * of path must not contain backslash as it is path separator.
1112        */
1113       if (c1 >= 0x80 || c2 >= 0x80 || c1 == L'\\')
1114         return FALSE;
1115       if (c1 >= L'a' && c1 <= L'z')
1116         c1 -= L'a' - L'A';
1117       if (c2 >= 'a' && c2 <= 'z')
1118         c2 -= 'a' - 'A';
1119       if (c1 != c2)
1120         return FALSE;
1121     }
1122 
1123   return TRUE;
1124 }
1125 
1126 /* Open process handle with the query right specified by process exe file. */
1127 HANDLE
win32_find_and_open_process_for_query(LPCSTR exe_file)1128 win32_find_and_open_process_for_query(LPCSTR exe_file)
1129 {
1130   GetProcessImageFileNameWProt MyGetProcessImageFileNameW;
1131   GetModuleFileNameExWProt MyGetModuleFileNameExW;
1132   EnumProcessesProt MyEnumProcesses;
1133   HMODULE kernel32, psapi;
1134   UINT prev_error_mode;
1135   DWORD partial_retry;
1136   BOOL found_process;
1137   DWORD size, length;
1138   DWORD *processes;
1139   HANDLE process;
1140   LPWSTR path;
1141   DWORD error;
1142   DWORD count;
1143   DWORD i;
1144 
1145   psapi = NULL;
1146   kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
1147   if (!kernel32)
1148     return NULL;
1149 
1150   /*
1151    * On Windows 7 and higher systems these functions are available in
1152    * kernel32.dll library with K32 prefix.
1153    */
1154   MyGetModuleFileNameExW = NULL;
1155   MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(void(*)(void))GetProcAddress(kernel32, "K32GetProcessImageFileNameW");
1156   MyEnumProcesses = (EnumProcessesProt)(void(*)(void))GetProcAddress(kernel32, "K32EnumProcesses");
1157   if (!MyGetProcessImageFileNameW || !MyEnumProcesses)
1158     {
1159       /*
1160        * On older NT-based systems these functions are available in
1161        * psapi.dll library without K32 prefix.
1162        */
1163       prev_error_mode = win32_change_error_mode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
1164       psapi = LoadLibrary(TEXT("psapi.dll"));
1165       win32_change_error_mode(prev_error_mode);
1166 
1167       if (!psapi)
1168         return NULL;
1169 
1170       /*
1171        * Function GetProcessImageFileNameW() is available in
1172        * Windows XP and higher systems. On older versions is
1173        * available function GetModuleFileNameExW().
1174        */
1175       MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(void(*)(void))GetProcAddress(psapi, "GetProcessImageFileNameW");
1176       MyGetModuleFileNameExW = (GetModuleFileNameExWProt)(void(*)(void))GetProcAddress(psapi, "GetModuleFileNameExW");
1177       MyEnumProcesses = (EnumProcessesProt)(void(*)(void))GetProcAddress(psapi, "EnumProcesses");
1178       if ((!MyGetProcessImageFileNameW && !MyGetModuleFileNameExW) || !MyEnumProcesses)
1179         {
1180           FreeLibrary(psapi);
1181           return NULL;
1182         }
1183     }
1184 
1185   /* Make initial buffer size for 1024 processes. */
1186   size = 1024 * sizeof(*processes);
1187 
1188 retry:
1189   processes = (DWORD *)LocalAlloc(LPTR, size);
1190   if (!processes)
1191     {
1192       if (psapi)
1193         FreeLibrary(psapi);
1194       return NULL;
1195     }
1196 
1197   if (!MyEnumProcesses(processes, size, &length))
1198     {
1199       LocalFree(processes);
1200       if (psapi)
1201         FreeLibrary(psapi);
1202       return NULL;
1203     }
1204   else if (size == length)
1205     {
1206       /*
1207        * There is no indication given when the buffer is too small to
1208        * store all process identifiers. Therefore if returned length
1209        * is same as buffer size there can be more processes. Call
1210        * again with larger buffer.
1211        */
1212       LocalFree(processes);
1213       size *= 2;
1214       goto retry;
1215     }
1216 
1217   process = NULL;
1218   count = length / sizeof(*processes);
1219 
1220   for (i = 0; i < count; i++)
1221     {
1222       /* Skip System Idle Process. */
1223       if (processes[i] == 0)
1224         continue;
1225 
1226       /*
1227        * Function GetModuleFileNameExW() requires additional
1228        * PROCESS_VM_READ right as opposite to function
1229        * GetProcessImageFileNameW() which does not need it.
1230        */
1231       process = open_process_for_query(processes[i], MyGetProcessImageFileNameW ? FALSE : TRUE);
1232       if (!process)
1233         continue;
1234 
1235       /*
1236        * Set initial buffer size to 256 (wide) characters.
1237        * Final path length on the modern NT-based systems can be also larger.
1238        */
1239       size = 256;
1240       found_process = FALSE;
1241       partial_retry = 0;
1242 
1243 retry_path:
1244       path = (LPWSTR)LocalAlloc(LPTR, size * sizeof(*path));
1245       if (!path)
1246         goto end_path;
1247 
1248       if (MyGetProcessImageFileNameW)
1249         length = MyGetProcessImageFileNameW(process, path, size);
1250       else
1251         length = MyGetModuleFileNameExW(process, NULL, path, size);
1252 
1253       error = GetLastError();
1254 
1255       /*
1256        * GetModuleFileNameEx() returns zero and signal error ERROR_PARTIAL_COPY
1257        * when remote process is in the middle of updating its module table.
1258        * Sleep 10 ms and try again, max 10 attempts.
1259        */
1260       if (!MyGetProcessImageFileNameW)
1261         {
1262           if (length == 0 && error == ERROR_PARTIAL_COPY && partial_retry++ < 10)
1263             {
1264               Sleep(10);
1265               goto retry_path;
1266             }
1267           partial_retry = 0;
1268         }
1269 
1270       /*
1271        * When buffer is too small then function GetModuleFileNameEx() returns
1272        * its size argument on older systems (Windows XP) or its size minus
1273        * argument one on new systems (Windows 10) without signalling any error.
1274        * Function GetProcessImageFileNameW() on the other hand returns zero
1275        * value and signals error ERROR_INSUFFICIENT_BUFFER. So in all these
1276        * cases call function again with larger buffer.
1277        */
1278 
1279       if (MyGetProcessImageFileNameW && length == 0 && error != ERROR_INSUFFICIENT_BUFFER)
1280         goto end_path;
1281 
1282       if ((MyGetProcessImageFileNameW && length == 0) ||
1283           (!MyGetProcessImageFileNameW && (length == size || length == size-1)))
1284         {
1285           LocalFree(path);
1286           size *= 2;
1287           goto retry_path;
1288         }
1289 
1290       if (length && check_process_name(path, length, exe_file))
1291         found_process = TRUE;
1292 
1293 end_path:
1294       if (path)
1295         {
1296           LocalFree(path);
1297           path = NULL;
1298         }
1299 
1300       if (found_process)
1301         break;
1302 
1303       CloseHandle(process);
1304       process = NULL;
1305     }
1306 
1307   LocalFree(processes);
1308 
1309   if (psapi)
1310     FreeLibrary(psapi);
1311 
1312   return process;
1313 }
1314 
1315 /*
1316  * Try to open primary access token of the particular process with specified
1317  * rights. Before opening access token try to adjust DACL permissions of the
1318  * primary process access token, so following open does not fail on error
1319  * related to no open permissions. Revert DACL permissions after open attempt.
1320  * As following steps are not atomic, try to execute them more times in case
1321  * of possible race conditions caused by other threads or processes.
1322  */
1323 static HANDLE
try_grant_permissions_and_open_process_token(HANDLE process,DWORD rights)1324 try_grant_permissions_and_open_process_token(HANDLE process, DWORD rights)
1325 {
1326   PSECURITY_DESCRIPTOR old_security_descriptor;
1327   HANDLE grant_token;
1328   HANDLE token;
1329   DWORD retry;
1330   DWORD error;
1331 
1332   /*
1333    * This code is not atomic. Between grant and open calls can other
1334    * thread or process change or revert permissions. So try to execute
1335    * it more times.
1336    */
1337   for (retry = 0; retry < 10; retry++)
1338     {
1339       if (!grant_process_token_dacl_permissions(process, rights, &grant_token, &old_security_descriptor))
1340         return NULL;
1341       if (!OpenProcessToken(process, rights, &token))
1342         {
1343           token = NULL;
1344           error = GetLastError();
1345         }
1346       if (old_security_descriptor)
1347         revert_token_dacl_permissions(grant_token, old_security_descriptor);
1348       if (token)
1349         return token;
1350       else if (error != ERROR_ACCESS_DENIED)
1351         return NULL;
1352     }
1353 
1354   return NULL;
1355 }
1356 
1357 /*
1358  * Open primary access token of particular process handle with specified rights.
1359  * If permissions for specified rights are missing then try to grant them.
1360  */
1361 HANDLE
win32_open_process_token_with_rights(HANDLE process,DWORD rights)1362 win32_open_process_token_with_rights(HANDLE process, DWORD rights)
1363 {
1364   HANDLE old_token;
1365   HANDLE token;
1366 
1367   /* First try to open primary access token of process handle directly. */
1368   if (OpenProcessToken(process, rights, &token))
1369     return token;
1370 
1371   /*
1372    * If opening failed then it means that owner of the current thread
1373    * access token does not have permission for it. Try it again with
1374    * primary process access token.
1375    */
1376   if (change_token_to_primary(&old_token))
1377     {
1378       if (!OpenProcessToken(process, rights, &token))
1379         token = NULL;
1380       win32_revert_to_token(old_token);
1381       if (token)
1382         return token;
1383     }
1384 
1385   /*
1386    * If opening is still failing then try to grant specified permissions
1387    * for the current thread and try to open it again.
1388    */
1389   token = try_grant_permissions_and_open_process_token(process, rights);
1390   if (token)
1391     return token;
1392 
1393   /*
1394    * And if it is still failing then try it again with granting
1395    * permissions for the primary process token of the current process.
1396    */
1397   if (change_token_to_primary(&old_token))
1398     {
1399       token = try_grant_permissions_and_open_process_token(process, rights);
1400       win32_revert_to_token(old_token);
1401       if (token)
1402         return token;
1403     }
1404 
1405   /*
1406    * TODO: Sorry, no other option for now...
1407    * It could be possible to use Take Ownership Name privilege to
1408    * temporary change token owner of specified process to the owner of
1409    * the current thread token, grant permissions for current thread in
1410    * that process token, change ownership back to original one, open
1411    * that process token and revert granted permissions. But this is
1412    * not implemented yet.
1413    */
1414   return NULL;
1415 }
1416 
1417 /*
1418  * Call supplied function with its argument and if it fails with
1419  * ERROR_PRIVILEGE_NOT_HELD then try to enable Tcb privilege and
1420  * call function with its argument again.
1421  */
1422 BOOL
win32_call_func_with_tcb_privilege(BOOL (* function)(LPVOID),LPVOID argument)1423 win32_call_func_with_tcb_privilege(BOOL (*function)(LPVOID), LPVOID argument)
1424 {
1425   LUID luid_tcb_privilege;
1426   LUID luid_impersonate_privilege;
1427 
1428   HANDLE revert_token_tcb_privilege;
1429   BOOL revert_only_tcb_privilege;
1430 
1431   HANDLE revert_token_impersonate_privilege;
1432   BOOL revert_only_impersonate_privilege;
1433 
1434   BOOL impersonate_privilege_enabled;
1435 
1436   BOOL revert_to_old_token;
1437   HANDLE old_token;
1438 
1439   HANDLE lsass_process;
1440   HANDLE lsass_token;
1441 
1442   DWORD error;
1443   BOOL ret;
1444 
1445   impersonate_privilege_enabled = FALSE;
1446   revert_to_old_token = FALSE;
1447   lsass_token = NULL;
1448   old_token = NULL;
1449 
1450   /* Call supplied function. */
1451   ret = function(argument);
1452   if (ret || GetLastError() != ERROR_PRIVILEGE_NOT_HELD)
1453     goto ret;
1454 
1455   /*
1456    * If function call failed with ERROR_PRIVILEGE_NOT_HELD
1457    * error then it means that the current thread token does not have
1458    * Tcb privilege enabled. Try to enable it.
1459    */
1460 
1461   if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &luid_tcb_privilege))
1462     goto err_privilege_not_held;
1463 
1464   /*
1465    * If the current thread has already Tcb privilege enabled then there
1466    * is some additional unhanded restriction.
1467    */
1468   if (win32_have_privilege(luid_tcb_privilege))
1469     goto err_privilege_not_held;
1470 
1471   /* Try to enable Tcb privilege and try function call again. */
1472   if (win32_enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
1473     {
1474       ret = function(argument);
1475       win32_revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
1476       goto ret;
1477     }
1478 
1479   /*
1480    * If enabling of Tcb privilege failed then it means that current thread
1481    * does not have this privilege. But current process may have it. So try it
1482    * again with primary process access token.
1483    */
1484 
1485   /*
1486    * If system supports Impersonate privilege (Windows 2000 SP4 or higher) then
1487    * all future actions in this function require this Impersonate privilege.
1488    * So try to enable it in case it is currently disabled.
1489    */
1490   if (LookupPrivilegeValue(NULL, SE_IMPERSONATE_NAME, &luid_impersonate_privilege) &&
1491       !win32_have_privilege(luid_impersonate_privilege))
1492     {
1493       /*
1494        * If current thread does not have Impersonate privilege enabled
1495        * then first try to enable it just for the current thread. If
1496        * it is not possible to enable it just for the current thread
1497        * then try it to enable globally for whole process (which
1498        * affects all process threads). Both actions will be reverted
1499        * at the end of this function.
1500        */
1501       if (win32_enable_privilege(luid_impersonate_privilege, &revert_token_impersonate_privilege, &revert_only_impersonate_privilege))
1502         {
1503           impersonate_privilege_enabled = TRUE;
1504         }
1505       else if (win32_enable_privilege(luid_impersonate_privilege, NULL, NULL))
1506         {
1507           impersonate_privilege_enabled = TRUE;
1508           revert_token_impersonate_privilege = NULL;
1509           revert_only_impersonate_privilege = TRUE;
1510         }
1511       else
1512         {
1513           goto err_privilege_not_held;
1514         }
1515 
1516       /*
1517        * Now when Impersonate privilege is enabled, try to enable Tcb
1518        * privilege again. Enabling other privileges for the current
1519        * thread requires Impersonate privilege, so enabling Tcb again
1520        * could now pass.
1521        */
1522       if (win32_enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
1523         {
1524           ret = function(argument);
1525           win32_revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
1526           goto ret;
1527         }
1528     }
1529 
1530   /*
1531    * If enabling Tcb privilege failed then it means that the current
1532    * thread access token does not have this privilege or does not
1533    * have permission to adjust privileges.
1534    *
1535    * Try to use more privileged token from Local Security Authority
1536    * Subsystem Service process (lsass.exe) which has Tcb privilege.
1537    * Retrieving this more privileged token is possible for local
1538    * administrators (unless it was disabled by local administrators).
1539    */
1540 
1541   lsass_process = win32_find_and_open_process_for_query("lsass.exe");
1542   if (!lsass_process)
1543     goto err_privilege_not_held;
1544 
1545   /*
1546    * Open primary lsass.exe process access token with query and duplicate
1547    * rights. Just these two rights are required for impersonating other
1548    * primary process token (impersonate right is really not required!).
1549    */
1550   lsass_token = win32_open_process_token_with_rights(lsass_process, TOKEN_QUERY | TOKEN_DUPLICATE);
1551 
1552   CloseHandle(lsass_process);
1553 
1554   if (!lsass_token)
1555     goto err_privilege_not_held;
1556 
1557   /*
1558    * After successful open of the primary lsass.exe process access token,
1559    * assign its copy for the current thread.
1560    */
1561   if (!win32_change_token(lsass_token, &old_token))
1562     goto err_privilege_not_held;
1563 
1564   revert_to_old_token = TRUE;
1565 
1566   ret = function(argument);
1567   if (ret || GetLastError() != ERROR_PRIVILEGE_NOT_HELD)
1568     goto ret;
1569 
1570   /*
1571    * Now current thread is not using primary process token anymore
1572    * but is using custom access token. There is no need to revert
1573    * enabled Tcb privilege as the whole custom access token would
1574    * be reverted. So there is no need to setup revert method for
1575    * enabling privilege.
1576    */
1577   if (win32_have_privilege(luid_tcb_privilege) ||
1578       !win32_enable_privilege(luid_tcb_privilege, NULL, NULL))
1579     goto err_privilege_not_held;
1580 
1581   ret = function(argument);
1582   goto ret;
1583 
1584 err_privilege_not_held:
1585   SetLastError(ERROR_PRIVILEGE_NOT_HELD);
1586   ret = FALSE;
1587   goto ret;
1588 
1589 ret:
1590   error = GetLastError();
1591 
1592   if (revert_to_old_token)
1593     win32_revert_to_token(old_token);
1594 
1595   if (impersonate_privilege_enabled)
1596     win32_revert_privilege(luid_impersonate_privilege, revert_token_impersonate_privilege, revert_only_impersonate_privilege);
1597 
1598   if (lsass_token)
1599     CloseHandle(lsass_token);
1600 
1601   SetLastError(error);
1602 
1603   return ret;
1604 }
1605