]> mj.ucw.cz Git - pciutils.git/blob - lib/win32-helpers.c
windows: Move common non-I/O port code from i386-io-windows.h to win32-helpers.c
[pciutils.git] / lib / win32-helpers.c
1 /*
2  *      The PCI Library -- Win32 helper functions
3  *
4  *      Copyright (c) 2023 Pali Rohár <pali@kernel.org>
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 #include <aclapi.h>
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 /*
23  * These psapi functions are available in kernel32.dll library with K32 prefix
24  * on Windows 7 and higher systems. On older Windows systems these functions are
25  * available in psapi.dll libary without K32 prefix. So resolve pointers to
26  * these functions dynamically at runtime from the available system library.
27  * Function GetProcessImageFileNameW() is not available on Windows 2000 and
28  * older systems.
29  */
30 typedef BOOL (WINAPI *EnumProcessesProt)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded);
31 typedef DWORD (WINAPI *GetProcessImageFileNameWProt)(HANDLE hProcess, LPWSTR lpImageFileName, DWORD nSize);
32 typedef DWORD (WINAPI *GetModuleFileNameExWProt)(HANDLE hProcess, HMODULE hModule, LPWSTR lpImageFileName, DWORD nSize);
33
34 /*
35  * These aclapi functions are available in advapi.dll library on Windows NT 4.0
36  * and higher systems.
37  */
38 typedef DWORD (WINAPI *GetSecurityInfoProt)(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID *ppsidOwner, PSID *ppsidGroup, PACL *ppDacl, PACL *ppSacl, PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
39 typedef DWORD (WINAPI *SetSecurityInfoProt)(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID psidOwner, PSID psidGroup, PACL pDacl, PACL pSacl);
40 typedef DWORD (WINAPI *SetEntriesInAclProt)(ULONG cCountOfExplicitEntries, PEXPLICIT_ACCESS pListOfExplicitEntries, PACL OldAcl, PACL *NewAcl);
41
42 /*
43  * This errhandlingapi function is available in kernel32.dll library on
44  * Windows 7 and higher systems.
45  */
46 typedef BOOL (WINAPI *SetThreadErrorModeProt)(DWORD dwNewMode, LPDWORD lpOldMode);
47
48
49 const char *
50 win32_strerror(DWORD win32_error_id)
51 {
52   /*
53    * Use static buffer which is large enough.
54    * Hopefully no Win32 API error message string is longer than 4 kB.
55    */
56   static char buffer[4096];
57   DWORD len;
58
59   len = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, win32_error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL);
60
61   /* FormatMessage() automatically appends ".\r\n" to the error message. */
62   if (len && buffer[len-1] == '\n')
63     buffer[--len] = '\0';
64   if (len && buffer[len-1] == '\r')
65     buffer[--len] = '\0';
66   if (len && buffer[len-1] == '.')
67     buffer[--len] = '\0';
68
69   if (!len)
70     sprintf(buffer, "Unknown Win32 error %lu", win32_error_id);
71
72   return buffer;
73 }
74
75 BOOL
76 win32_is_non_nt_system(void)
77 {
78   OSVERSIONINFOA version;
79   version.dwOSVersionInfoSize = sizeof(version);
80   return GetVersionExA(&version) && version.dwPlatformId < VER_PLATFORM_WIN32_NT;
81 }
82
83 BOOL
84 win32_is_32bit_on_64bit_system(void)
85 {
86   BOOL (WINAPI *MyIsWow64Process)(HANDLE, PBOOL);
87   HMODULE kernel32;
88   BOOL is_wow64;
89
90   /*
91    * Check for 64-bit system via IsWow64Process() function exported
92    * from 32-bit kernel32.dll library available on the 64-bit systems.
93    * Resolve pointer to this function at runtime as this code path is
94    * primary running on 32-bit systems where are not available 64-bit
95    * functions.
96    */
97
98   kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
99   if (!kernel32)
100     return FALSE;
101
102   MyIsWow64Process = (void *)GetProcAddress(kernel32, "IsWow64Process");
103   if (!MyIsWow64Process)
104     return FALSE;
105
106   if (!MyIsWow64Process(GetCurrentProcess(), &is_wow64))
107     return FALSE;
108
109   return is_wow64;
110 }
111
112 BOOL
113 win32_is_32bit_on_win8_64bit_system(void)
114 {
115 #ifdef _WIN64
116   return FALSE;
117 #else
118   OSVERSIONINFOA version;
119
120   /* Check for Windows 8 (NT 6.2). */
121   version.dwOSVersionInfoSize = sizeof(version);
122   if (!GetVersionExA(&version) ||
123       version.dwPlatformId != VER_PLATFORM_WIN32_NT ||
124       version.dwMajorVersion < 6 ||
125       (version.dwMajorVersion == 6 && version.dwMinorVersion < 2))
126     return FALSE;
127
128   return win32_is_32bit_on_64bit_system();
129 #endif
130 }
131
132 /*
133  * Change error mode of the current thread. If it is not possible then change
134  * error mode of the whole process. Always returns previous error mode.
135  */
136 UINT
137 win32_change_error_mode(UINT new_mode)
138 {
139   SetThreadErrorModeProt MySetThreadErrorMode = NULL;
140   HMODULE kernel32;
141   DWORD old_mode;
142
143   /*
144    * Function SetThreadErrorMode() was introduced in Windows 7, so use
145    * GetProcAddress() for compatibility with older systems.
146    */
147   kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
148   if (kernel32)
149     MySetThreadErrorMode = (SetThreadErrorModeProt)(LPVOID)GetProcAddress(kernel32, "SetThreadErrorMode");
150
151   if (MySetThreadErrorMode &&
152       MySetThreadErrorMode(new_mode, &old_mode))
153     return old_mode;
154
155   /*
156    * Fallback to function SetErrorMode() which modifies error mode of the
157    * whole process and returns old mode.
158    */
159   return SetErrorMode(new_mode);
160 }
161
162 /*
163  * Check if the current thread has particular privilege in current active access
164  * token. Case when it not possible to determinate it (e.g. current thread does
165  * not have permission to open its own current active access token) is evaluated
166  * as thread does not have that privilege.
167  */
168 BOOL
169 win32_have_privilege(LUID luid_privilege)
170 {
171   PRIVILEGE_SET priv;
172   HANDLE token;
173   BOOL ret;
174
175   /*
176    * If the current thread does not have active access token then thread
177    * uses primary process access token for all permission checks.
178    */
179   if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
180       (GetLastError() != ERROR_NO_TOKEN ||
181        !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
182     return FALSE;
183
184   priv.PrivilegeCount = 1;
185   priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
186   priv.Privilege[0].Luid = luid_privilege;
187   priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
188
189   if (!PrivilegeCheck(token, &priv, &ret))
190     return FALSE;
191
192   return ret;
193 }
194
195 /*
196  * Enable or disable particular privilege in specified access token.
197  *
198  * Note that it is not possible to disable privilege in access token with
199  * SE_PRIVILEGE_ENABLED_BY_DEFAULT attribute. This function does not check
200  * this case and incorrectly returns no error even when disabling failed.
201  * Rationale for this decision: Simplification of this function as WinAPI
202  * call AdjustTokenPrivileges() does not signal error in this case too.
203  */
204 static BOOL
205 set_privilege(HANDLE token, LUID luid_privilege, BOOL enable)
206 {
207   TOKEN_PRIVILEGES token_privileges;
208
209   token_privileges.PrivilegeCount = 1;
210   token_privileges.Privileges[0].Luid = luid_privilege;
211   token_privileges.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
212
213   /*
214    * WinAPI function AdjustTokenPrivileges() success also when not all
215    * privileges were enabled. It is always required to check for failure
216    * via GetLastError() call. AdjustTokenPrivileges() always sets error
217    * also when it success, as opposite to other WinAPI functions.
218    */
219   if (!AdjustTokenPrivileges(token, FALSE, &token_privileges, sizeof(token_privileges), NULL, NULL) ||
220       GetLastError() != ERROR_SUCCESS)
221     return FALSE;
222
223   return TRUE;
224 }
225
226 /*
227  * Change access token for the current thread to new specified access token.
228  * Previously active access token is stored in old_token variable and can be
229  * used for reverting to this access token. It is set to NULL if the current
230  * thread previously used primary process access token.
231  */
232 BOOL
233 win32_change_token(HANDLE new_token, HANDLE *old_token)
234 {
235   HANDLE token;
236
237   if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
238     {
239       if (GetLastError() != ERROR_NO_TOKEN)
240         return FALSE;
241       token = NULL;
242     }
243
244   if (!ImpersonateLoggedOnUser(new_token))
245     {
246       if (token)
247         CloseHandle(token);
248       return FALSE;
249     }
250
251   *old_token = token;
252   return TRUE;
253 }
254
255 /*
256  * Change access token for the current thread to the primary process access
257  * token. This function fails also when the current thread already uses primary
258  * process access token.
259  */
260 static BOOL
261 change_token_to_primary(HANDLE *old_token)
262 {
263   HANDLE token;
264
265   if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
266     return FALSE;
267
268   RevertToSelf();
269
270   *old_token = token;
271   return TRUE;
272 }
273
274 /*
275  * Revert to the specified access token for the current thread. When access
276  * token is specified as NULL then revert to the primary process access token.
277  * Use to revert after win32_change_token() or change_token_to_primary() call.
278  */
279 VOID
280 win32_revert_to_token(HANDLE token)
281 {
282   /*
283    * If SetThreadToken() call fails then there is no option to revert to
284    * the specified previous thread access token. So in this case revert to
285    * the primary process access token.
286    */
287   if (!token || !SetThreadToken(NULL, token))
288     RevertToSelf();
289   if (token)
290     CloseHandle(token);
291 }
292
293 /*
294  * Enable particular privilege for the current thread. And set method how to
295  * revert this privilege (if to revert whole token or only privilege).
296  */
297 BOOL
298 win32_enable_privilege(LUID luid_privilege, HANDLE *revert_token, BOOL *revert_only_privilege)
299 {
300   HANDLE thread_token;
301   HANDLE new_token;
302
303   if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &thread_token))
304     {
305       if (set_privilege(thread_token, luid_privilege, TRUE))
306         {
307           /*
308            * Indicate that correct revert method is just to
309            * disable privilege in access token.
310            */
311           if (revert_token && revert_only_privilege)
312             {
313               *revert_token = thread_token;
314               *revert_only_privilege = TRUE;
315             }
316           else
317             {
318               CloseHandle(thread_token);
319             }
320           return TRUE;
321         }
322       CloseHandle(thread_token);
323       /*
324        * If enabling privilege failed then try to enable it via
325        * primary process access token.
326        */
327     }
328
329   /*
330    * If the current thread has already active thread access token then
331    * open it with just impersonate right as it would be used only for
332    * future revert.
333    */
334   if (revert_token && revert_only_privilege)
335     {
336       if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &thread_token))
337         {
338           if (GetLastError() != ERROR_NO_TOKEN)
339             return FALSE;
340           thread_token = NULL;
341         }
342
343       /*
344        * If current thread has no access token (and uses primary
345        * process access token) or it does not have permission to
346        * adjust privileges or it does not have specified privilege
347        * then create a copy of the primary process access token,
348        * assign it for the current thread (= impersonate self)
349        * and then try adjusting privilege again.
350        */
351       if (!ImpersonateSelf(SecurityImpersonation))
352         {
353           if (thread_token)
354             CloseHandle(thread_token);
355           return FALSE;
356         }
357     }
358
359   if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &new_token))
360     {
361       /* thread_token is set only when we were asked for revert method. */
362       if (revert_token && revert_only_privilege)
363         win32_revert_to_token(thread_token);
364       return FALSE;
365     }
366
367   if (!set_privilege(new_token, luid_privilege, TRUE))
368     {
369       CloseHandle(new_token);
370       /* thread_token is set only when we were asked for revert method. */
371       if (revert_token && revert_only_privilege)
372         win32_revert_to_token(thread_token);
373       return FALSE;
374     }
375
376   /*
377    * Indicate that correct revert method is to change to the previous
378    * access token. Either to the primary process access token or to the
379    * previous thread access token.
380    */
381   if (revert_token && revert_only_privilege)
382     {
383       *revert_token = thread_token;
384       *revert_only_privilege = FALSE;
385     }
386   return TRUE;
387 }
388
389 /*
390  * Revert particular privilege for the current thread was previously enabled by
391  * win32_enable_privilege() call. Either disable privilege in specified access token
392  * or revert to previous access token.
393  */
394 VOID
395 win32_revert_privilege(LUID luid_privilege, HANDLE revert_token, BOOL revert_only_privilege)
396 {
397   if (revert_only_privilege)
398     {
399       set_privilege(revert_token, luid_privilege, FALSE);
400       CloseHandle(revert_token);
401     }
402   else
403     {
404       win32_revert_to_token(revert_token);
405     }
406 }
407
408 /*
409  * Return owner of the access token used by the current thread. Buffer for
410  * returned owner needs to be released by LocalFree() call.
411  */
412 static TOKEN_OWNER *
413 get_current_token_owner(VOID)
414 {
415   HANDLE token;
416   DWORD length;
417   TOKEN_OWNER *owner;
418
419   /*
420    * If the current thread does not have active access token then thread
421    * uses primary process access token for all permission checks.
422    */
423   if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
424       (GetLastError() != ERROR_NO_TOKEN ||
425        !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
426     return NULL;
427
428   if (!GetTokenInformation(token, TokenOwner, NULL, 0, &length) &&
429       GetLastError() != ERROR_INSUFFICIENT_BUFFER)
430     {
431       CloseHandle(token);
432       return NULL;
433     }
434
435 retry:
436   owner = (TOKEN_OWNER *)LocalAlloc(LPTR, length);
437   if (!owner)
438     {
439       CloseHandle(token);
440       return NULL;
441     }
442
443   if (!GetTokenInformation(token, TokenOwner, owner, length, &length))
444     {
445       /*
446        * Length of token owner (SID) buffer between two get calls may
447        * changes (e.g. by another thread of process), so retry.
448        */
449       if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
450         {
451           LocalFree(owner);
452           goto retry;
453         }
454       LocalFree(owner);
455       CloseHandle(token);
456       return NULL;
457     }
458
459   CloseHandle(token);
460   return owner;
461 }
462
463 /*
464  * Grant particular permissions in the primary access token of the specified
465  * process for the owner of current thread token and set old DACL of the
466  * process access token for reverting permissions. Security descriptor is
467  * just memory buffer for old DACL.
468  */
469 static BOOL
470 grant_process_token_dacl_permissions(HANDLE process, DWORD permissions, HANDLE *token, PACL *old_dacl, PSECURITY_DESCRIPTOR *security_descriptor)
471 {
472   GetSecurityInfoProt MyGetSecurityInfo;
473   SetSecurityInfoProt MySetSecurityInfo;
474   SetEntriesInAclProt MySetEntriesInAcl;
475   EXPLICIT_ACCESS explicit_access;
476   TOKEN_OWNER *owner;
477   HMODULE advapi32;
478   PACL new_dacl;
479
480   /*
481    * This source file already uses advapi32.dll library, so it is
482    * linked to executable and automatically loaded when starting
483    * current running process.
484    */
485   advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
486   if (!advapi32)
487     return FALSE;
488
489   /*
490    * It does not matter if SetEntriesInAclA() or SetEntriesInAclW() is
491    * called as no string is passed to SetEntriesInAcl function.
492    */
493   MyGetSecurityInfo = (GetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "GetSecurityInfo");
494   MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
495   MySetEntriesInAcl = (SetEntriesInAclProt)(LPVOID)GetProcAddress(advapi32, "SetEntriesInAclA");
496   if (!MyGetSecurityInfo || !MySetSecurityInfo || !MySetEntriesInAcl)
497     return FALSE;
498
499   owner = get_current_token_owner();
500   if (!owner)
501     return FALSE;
502
503   /*
504    * READ_CONTROL is required for GetSecurityInfo(DACL_SECURITY_INFORMATION)
505    * and WRITE_DAC is required for SetSecurityInfo(DACL_SECURITY_INFORMATION).
506    */
507   if (!OpenProcessToken(process, READ_CONTROL | WRITE_DAC, token))
508     {
509       LocalFree(owner);
510       return FALSE;
511     }
512
513   if (MyGetSecurityInfo(*token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL, security_descriptor) != ERROR_SUCCESS)
514     {
515       LocalFree(owner);
516       CloseHandle(*token);
517       return FALSE;
518     }
519
520   /*
521    * Set new explicit access for the owner of the current thread access
522    * token with non-inherited granting access to specified permissions.
523    */
524   explicit_access.grfAccessPermissions = permissions;
525   explicit_access.grfAccessMode = GRANT_ACCESS;
526   explicit_access.grfInheritance = NO_PROPAGATE_INHERIT_ACE;
527   explicit_access.Trustee.pMultipleTrustee = NULL;
528   explicit_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
529   explicit_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
530   explicit_access.Trustee.TrusteeType = TRUSTEE_IS_USER;
531   /*
532    * Unfortunately i586-mingw32msvc toolchain does not have pSid pointer
533    * member in Trustee union. So assign owner SID to ptstrName pointer
534    * member which aliases with pSid pointer member in the same union.
535    */
536   explicit_access.Trustee.ptstrName = (PVOID)owner->Owner;
537
538   if (MySetEntriesInAcl(1, &explicit_access, *old_dacl, &new_dacl) != ERROR_SUCCESS)
539     {
540       LocalFree(*security_descriptor);
541       LocalFree(owner);
542       CloseHandle(*token);
543       return FALSE;
544     }
545
546   if (MySetSecurityInfo(*token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, new_dacl, NULL) != ERROR_SUCCESS)
547     {
548       LocalFree(new_dacl);
549       LocalFree(*security_descriptor);
550       LocalFree(owner);
551       CloseHandle(*token);
552       return FALSE;
553     }
554
555   LocalFree(new_dacl);
556   LocalFree(owner);
557   return TRUE;
558 }
559
560 /*
561  * Revert particular granted permissions in specified access token done by
562  * grant_process_token_dacl_permissions() call.
563  */
564 static VOID
565 revert_token_dacl_permissions(HANDLE token, PACL old_dacl, PSECURITY_DESCRIPTOR security_descriptor)
566 {
567   SetSecurityInfoProt MySetSecurityInfo;
568   HMODULE advapi32;
569
570   /*
571    * This source file already uses advapi32.dll library, so it is
572    * linked to executable and automatically loaded when starting
573    * current running process.
574    */
575   advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
576   if (advapi32)
577     {
578       MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
579       MySetSecurityInfo(token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL);
580     }
581
582   LocalFree(security_descriptor);
583   CloseHandle(token);
584 }
585
586 /*
587  * Open process handle specified by the process id with the query right and
588  * optionally also with vm read right.
589  */
590 static HANDLE
591 open_process_for_query(DWORD pid, BOOL with_vm_read)
592 {
593   BOOL revert_only_privilege;
594   LUID luid_debug_privilege;
595   OSVERSIONINFO version;
596   DWORD process_right;
597   HANDLE revert_token;
598   HANDLE process;
599
600   /*
601    * Some processes on Windows Vista and higher systems can be opened only
602    * with PROCESS_QUERY_LIMITED_INFORMATION right. This right is enough
603    * for accessing primary process token. But this right is not supported
604    * on older pre-Vista systems. When the current thread on these older
605    * systems does not have Debug privilege then OpenProcess() fails with
606    * ERROR_ACCESS_DENIED. If the current thread has Debug privilege then
607    * OpenProcess() success and returns handle to requested process.
608    * Problem is that this handle does not have PROCESS_QUERY_INFORMATION
609    * right and so cannot be used for accessing primary process token
610    * on those older systems. Moreover it has zero rights and therefore
611    * such handle is fully useless. So never try to use open process with
612    * PROCESS_QUERY_LIMITED_INFORMATION right on older systems than
613    * Windows Vista (NT 6.0).
614    */
615   version.dwOSVersionInfoSize = sizeof(version);
616   if (GetVersionEx(&version) &&
617       version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
618       version.dwMajorVersion >= 6)
619     process_right = PROCESS_QUERY_LIMITED_INFORMATION;
620   else
621     process_right = PROCESS_QUERY_INFORMATION;
622
623   if (with_vm_read)
624     process_right |= PROCESS_VM_READ;
625
626   process = OpenProcess(process_right, FALSE, pid);
627   if (process)
628     return process;
629
630   /*
631    * It is possible to open only processes to which owner of the current
632    * thread access token has permissions. For opening other processing it
633    * is required to have Debug privilege enabled. By default local
634    * administrators have this privilege, but it is disabled. So try to
635    * enable it and then try to open process again.
636    */
637
638   if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
639     return NULL;
640
641   if (!win32_enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
642     return NULL;
643
644   process = OpenProcess(process_right, FALSE, pid);
645
646   win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
647
648   return process;
649 }
650
651 /*
652  * Check if process image path name (wide string) matches exe file name
653  * (7-bit ASCII string). Do case-insensitive string comparison. Process
654  * image path name can be in any namespace format (DOS, Win32, UNC, ...).
655  */
656 static BOOL
657 check_process_name(LPCWSTR path, DWORD path_length, LPCSTR exe_file)
658 {
659   DWORD exe_file_length;
660   WCHAR c1;
661   UCHAR c2;
662   DWORD i;
663
664   exe_file_length = 0;
665   while (exe_file[exe_file_length] != '\0')
666     exe_file_length++;
667
668   /* Path must have backslash before exe file name. */
669   if (exe_file_length >= path_length ||
670       path[path_length-exe_file_length-1] != L'\\')
671     return FALSE;
672
673   for (i = 0; i < exe_file_length; i++)
674     {
675       c1 = path[path_length-exe_file_length+i];
676       c2 = exe_file[i];
677       /*
678        * Input string for comparison is 7-bit ASCII and file name part
679        * of path must not contain backslash as it is path separator.
680        */
681       if (c1 >= 0x80 || c2 >= 0x80 || c1 == L'\\')
682         return FALSE;
683       if (c1 >= L'a' && c1 <= L'z')
684         c1 -= L'a' - L'A';
685       if (c2 >= 'a' && c2 <= 'z')
686         c2 -= 'a' - 'A';
687       if (c1 != c2)
688         return FALSE;
689     }
690
691   return TRUE;
692 }
693
694 /* Open process handle with the query right specified by process exe file. */
695 HANDLE
696 win32_find_and_open_process_for_query(LPCSTR exe_file)
697 {
698   GetProcessImageFileNameWProt MyGetProcessImageFileNameW;
699   GetModuleFileNameExWProt MyGetModuleFileNameExW;
700   EnumProcessesProt MyEnumProcesses;
701   HMODULE kernel32, psapi;
702   UINT prev_error_mode;
703   DWORD partial_retry;
704   BOOL found_process;
705   DWORD size, length;
706   DWORD *processes;
707   HANDLE process;
708   LPWSTR path;
709   DWORD error;
710   DWORD count;
711   DWORD i;
712
713   psapi = NULL;
714   kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
715   if (!kernel32)
716     return NULL;
717
718   /*
719    * On Windows 7 and higher systems these functions are available in
720    * kernel32.dll library with K32 prefix.
721    */
722   MyGetModuleFileNameExW = NULL;
723   MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(kernel32, "K32GetProcessImageFileNameW");
724   MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(kernel32, "K32EnumProcesses");
725   if (!MyGetProcessImageFileNameW || !MyEnumProcesses)
726     {
727       /*
728        * On older NT-based systems these functions are available in
729        * psapi.dll library without K32 prefix.
730        */
731       prev_error_mode = win32_change_error_mode(SEM_FAILCRITICALERRORS);
732       psapi = LoadLibrary(TEXT("psapi.dll"));
733       win32_change_error_mode(prev_error_mode);
734
735       if (!psapi)
736         return NULL;
737
738       /*
739        * Function GetProcessImageFileNameW() is available in
740        * Windows XP and higher systems. On older versions is
741        * available function GetModuleFileNameExW().
742        */
743       MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(psapi, "GetProcessImageFileNameW");
744       MyGetModuleFileNameExW = (GetModuleFileNameExWProt)(LPVOID)GetProcAddress(psapi, "GetModuleFileNameExW");
745       MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(psapi, "EnumProcesses");
746       if ((!MyGetProcessImageFileNameW && !MyGetModuleFileNameExW) || !MyEnumProcesses)
747         {
748           FreeLibrary(psapi);
749           return NULL;
750         }
751     }
752
753   /* Make initial buffer size for 1024 processes. */
754   size = 1024 * sizeof(*processes);
755
756 retry:
757   processes = (DWORD *)LocalAlloc(LPTR, size);
758   if (!processes)
759     {
760       if (psapi)
761         FreeLibrary(psapi);
762       return NULL;
763     }
764
765   if (!MyEnumProcesses(processes, size, &length))
766     {
767       LocalFree(processes);
768       if (psapi)
769         FreeLibrary(psapi);
770       return NULL;
771     }
772   else if (size == length)
773     {
774       /*
775        * There is no indication given when the buffer is too small to
776        * store all process identifiers. Therefore if returned length
777        * is same as buffer size there can be more processes. Call
778        * again with larger buffer.
779        */
780       LocalFree(processes);
781       size *= 2;
782       goto retry;
783     }
784
785   process = NULL;
786   count = length / sizeof(*processes);
787
788   for (i = 0; i < count; i++)
789     {
790       /* Skip System Idle Process. */
791       if (processes[i] == 0)
792         continue;
793
794       /*
795        * Function GetModuleFileNameExW() requires additional
796        * PROCESS_VM_READ right as opposite to function
797        * GetProcessImageFileNameW() which does not need it.
798        */
799       process = open_process_for_query(processes[i], MyGetProcessImageFileNameW ? FALSE : TRUE);
800       if (!process)
801         continue;
802
803       /*
804        * Set initial buffer size to 256 (wide) characters.
805        * Final path length on the modern NT-based systems can be also larger.
806        */
807       size = 256;
808       found_process = FALSE;
809       partial_retry = 0;
810
811 retry_path:
812       path = (LPWSTR)LocalAlloc(LPTR, size * sizeof(*path));
813       if (!path)
814         goto end_path;
815
816       if (MyGetProcessImageFileNameW)
817         length = MyGetProcessImageFileNameW(process, path, size);
818       else
819         length = MyGetModuleFileNameExW(process, NULL, path, size);
820
821       error = GetLastError();
822
823       /*
824        * GetModuleFileNameEx() returns zero and signal error ERROR_PARTIAL_COPY
825        * when remote process is in the middle of updating its module table.
826        * Sleep 10 ms and try again, max 10 attempts.
827        */
828       if (!MyGetProcessImageFileNameW)
829         {
830           if (length == 0 && error == ERROR_PARTIAL_COPY && partial_retry++ < 10)
831             {
832               Sleep(10);
833               goto retry_path;
834             }
835           partial_retry = 0;
836         }
837
838       /*
839        * When buffer is too small then function GetModuleFileNameEx() returns
840        * its size argument on older systems (Windows XP) or its size minus
841        * argument one on new systems (Windows 10) without signalling any error.
842        * Function GetProcessImageFileNameW() on the other hand returns zero
843        * value and signals error ERROR_INSUFFICIENT_BUFFER. So in all these
844        * cases call function again with larger buffer.
845        */
846
847       if (MyGetProcessImageFileNameW && length == 0 && error != ERROR_INSUFFICIENT_BUFFER)
848         goto end_path;
849
850       if ((MyGetProcessImageFileNameW && length == 0) ||
851           (!MyGetProcessImageFileNameW && (length == size || length == size-1)))
852         {
853           LocalFree(path);
854           size *= 2;
855           goto retry_path;
856         }
857
858       if (length && check_process_name(path, length, exe_file))
859         found_process = TRUE;
860
861 end_path:
862       if (path)
863         {
864           LocalFree(path);
865           path = NULL;
866         }
867
868       if (found_process)
869         break;
870
871       CloseHandle(process);
872       process = NULL;
873     }
874
875   LocalFree(processes);
876
877   if (psapi)
878     FreeLibrary(psapi);
879
880   return process;
881 }
882
883 /*
884  * Try to open primary access token of the particular process with specified
885  * rights. Before opening access token try to adjust DACL permissions of the
886  * primary process access token, so following open does not fail on error
887  * related to no open permissions. Revert DACL permissions after open attempt.
888  * As following steps are not atomic, try to execute them more times in case
889  * of possible race conditions caused by other threads or processes.
890  */
891 static HANDLE
892 try_grant_permissions_and_open_process_token(HANDLE process, DWORD rights)
893 {
894   PSECURITY_DESCRIPTOR security_descriptor;
895   HANDLE grant_token;
896   PACL old_dacl;
897   HANDLE token;
898   DWORD retry;
899   DWORD error;
900
901   /*
902    * This code is not atomic. Between grant and open calls can other
903    * thread or process change or revert permissions. So try to execute
904    * it more times.
905    */
906   for (retry = 0; retry < 10; retry++)
907     {
908       if (!grant_process_token_dacl_permissions(process, rights, &grant_token, &old_dacl, &security_descriptor))
909         return NULL;
910       if (!OpenProcessToken(process, rights, &token))
911         {
912           token = NULL;
913           error = GetLastError();
914         }
915       revert_token_dacl_permissions(grant_token, old_dacl, security_descriptor);
916       if (token)
917         return token;
918       else if (error != ERROR_ACCESS_DENIED)
919         return NULL;
920     }
921
922   return NULL;
923 }
924
925 /*
926  * Open primary access token of particular process handle with specified rights.
927  * If permissions for specified rights are missing then try to grant them.
928  */
929 HANDLE
930 win32_open_process_token_with_rights(HANDLE process, DWORD rights)
931 {
932   HANDLE old_token;
933   HANDLE token;
934
935   /* First try to open primary access token of process handle directly. */
936   if (OpenProcessToken(process, rights, &token))
937     return token;
938
939   /*
940    * If opening failed then it means that owner of the current thread
941    * access token does not have permission for it. Try it again with
942    * primary process access token.
943    */
944   if (change_token_to_primary(&old_token))
945     {
946       if (!OpenProcessToken(process, rights, &token))
947         token = NULL;
948       win32_revert_to_token(old_token);
949       if (token)
950         return token;
951     }
952
953   /*
954    * If opening is still failing then try to grant specified permissions
955    * for the current thread and try to open it again.
956    */
957   token = try_grant_permissions_and_open_process_token(process, rights);
958   if (token)
959     return token;
960
961   /*
962    * And if it is still failing then try it again with granting
963    * permissions for the primary process token of the current process.
964    */
965   if (change_token_to_primary(&old_token))
966     {
967       token = try_grant_permissions_and_open_process_token(process, rights);
968       win32_revert_to_token(old_token);
969       if (token)
970         return token;
971     }
972
973   /*
974    * TODO: Sorry, no other option for now...
975    * It could be possible to use Take Ownership Name privilege to
976    * temporary change token owner of specified process to the owner of
977    * the current thread token, grant permissions for current thread in
978    * that process token, change ownership back to original one, open
979    * that process token and revert granted permissions. But this is
980    * not implemented yet.
981    */
982   return NULL;
983 }