]> mj.ucw.cz Git - pciutils.git/blob - lib/i386-io-windows.h
windows: Make msvc __readeflags more readable
[pciutils.git] / lib / i386-io-windows.h
1 /*
2  *      The PCI Library -- Access to i386 I/O ports on Windows
3  *
4  *      Copyright (c) 2004 Alexander Stock <stock.alexander@gmx.de>
5  *      Copyright (c) 2006 Martin Mares <mj@ucw.cz>
6  *      Copyright (c) 2021 Pali Rohár <pali@kernel.org>
7  *
8  *      Can be freely distributed and used under the terms of the GNU GPL v2+
9  *
10  *      SPDX-License-Identifier: GPL-2.0-or-later
11  */
12
13 #include <windows.h>
14 #include <aclapi.h>
15
16 #include "i386-io-access.h"
17
18 /*
19  * Define __readeflags() for MSVC and GCC compilers.
20  * MSVC since version 14.00 included in WDK 6001 and since version 15.00
21  * included in VS 2008 provides __readeflags() intrinsic for both 32 and 64-bit
22  * modes. WDK 6001 defines macro __BUILDMACHINE__ to value WinDDK. VS 2008 does
23  * not define this macro at all. MSVC throws error if name of user defined
24  * function conflicts with some MSVC intrinsic.
25  * MSVC supports inline assembly via __asm keyword in 32-bit mode only.
26  * GCC version 4.9.0 and higher provides __builtin_ia32_readeflags_uXX()
27  * builtin for XX-mode. This builtin is also available as __readeflags()
28  * function indirectly via <x86intrin.h> header file.
29  */
30 #if defined(_MSC_VER) && (_MSC_VER >= 1500 || (_MSC_VER >= 1400 && defined(__BUILDMACHINE__)))
31 #pragma intrinsic(__readeflags)
32 #elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 9) || (__GNUC__ > 4))
33 #include <x86intrin.h>
34 #elif defined(_MSC_VER) && defined(_M_IX86)
35 static unsigned int
36 __readeflags(void)
37 {
38   __asm {
39     pushfd
40     pop eax
41   }
42 }
43 #elif defined(__GNUC__)
44 static inline unsigned
45 #ifdef __x86_64__
46 long long
47 #endif
48 int
49 __readeflags(void)
50 {
51   unsigned
52 #ifdef __x86_64__
53   long long
54 #endif
55   int eflags;
56   asm volatile ("pushf\n\tpop %0\n" : "=r" (eflags));
57   return eflags;
58 }
59 #else
60 #error "Unsupported compiler"
61 #endif
62
63 /* Read IOPL of the current process, IOPL is stored in eflag bits [13:12]. */
64 #define read_iopl() ((__readeflags() >> 12) & 0x3)
65
66 /* Unfortunately i586-mingw32msvc toolchain does not provide this constant. */
67 #ifndef PROCESS_QUERY_LIMITED_INFORMATION
68 #define PROCESS_QUERY_LIMITED_INFORMATION 0x1000
69 #endif
70
71 /* Unfortunately some toolchains do not provide this constant. */
72 #ifndef SE_IMPERSONATE_NAME
73 #define SE_IMPERSONATE_NAME TEXT("SeImpersonatePrivilege")
74 #endif
75
76 /*
77  * These psapi functions are available in kernel32.dll library with K32 prefix
78  * on Windows 7 and higher systems. On older Windows systems these functions are
79  * available in psapi.dll libary without K32 prefix. So resolve pointers to
80  * these functions dynamically at runtime from the available system library.
81  * Function GetProcessImageFileNameW() is not available on Windows 2000 and
82  * older systems.
83  */
84 typedef BOOL (WINAPI *EnumProcessesProt)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded);
85 typedef DWORD (WINAPI *GetProcessImageFileNameWProt)(HANDLE hProcess, LPWSTR lpImageFileName, DWORD nSize);
86 typedef DWORD (WINAPI *GetModuleFileNameExWProt)(HANDLE hProcess, HMODULE hModule, LPWSTR lpImageFileName, DWORD nSize);
87
88 /*
89  * These aclapi functions are available in advapi.dll library on Windows NT 4.0
90  * and higher systems.
91  */
92 typedef DWORD (WINAPI *GetSecurityInfoProt)(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID *ppsidOwner, PSID *ppsidGroup, PACL *ppDacl, PACL *ppSacl, PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
93 typedef DWORD (WINAPI *SetSecurityInfoProt)(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID psidOwner, PSID psidGroup, PACL pDacl, PACL pSacl);
94 typedef DWORD (WINAPI *SetEntriesInAclProt)(ULONG cCountOfExplicitEntries, PEXPLICIT_ACCESS pListOfExplicitEntries, PACL OldAcl, PACL *NewAcl);
95
96 /*
97  * This errhandlingapi function is available in kernel32.dll library on
98  * Windows 7 and higher systems.
99  */
100 typedef BOOL (WINAPI *SetThreadErrorModeProt)(DWORD dwNewMode, LPDWORD lpOldMode);
101
102 /*
103  * Unfortunately NtSetInformationProcess() function, ProcessUserModeIOPL
104  * constant and all other helpers for its usage are not specified in any
105  * standard WinAPI header file. So define all of required constants and types.
106  * Function NtSetInformationProcess() is available in ntdll.dll library on all
107  * Windows systems but marked as it can be removed in some future version.
108  */
109 #ifndef NTSTATUS
110 #define NTSTATUS LONG
111 #endif
112 #ifndef STATUS_NOT_IMPLEMENTED
113 #define STATUS_NOT_IMPLEMENTED (NTSTATUS)0xC0000002
114 #endif
115 #ifndef STATUS_PRIVILEGE_NOT_HELD
116 #define STATUS_PRIVILEGE_NOT_HELD (NTSTATUS)0xC0000061
117 #endif
118 #ifndef PROCESSINFOCLASS
119 #define PROCESSINFOCLASS DWORD
120 #endif
121 #ifndef ProcessUserModeIOPL
122 #define ProcessUserModeIOPL 16
123 #endif
124 typedef NTSTATUS (NTAPI *NtSetInformationProcessProt)(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength);
125
126 /*
127  * Check if the current thread has particular privilege in current active access
128  * token. Case when it not possible to determinate it (e.g. current thread does
129  * not have permission to open its own current active access token) is evaluated
130  * as thread does not have that privilege.
131  */
132 static BOOL
133 have_privilege(LUID luid_privilege)
134 {
135   PRIVILEGE_SET priv;
136   HANDLE token;
137   BOOL ret;
138
139   /*
140    * If the current thread does not have active access token then thread
141    * uses primary process access token for all permission checks.
142    */
143   if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
144       (GetLastError() != ERROR_NO_TOKEN ||
145        !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
146     return FALSE;
147
148   priv.PrivilegeCount = 1;
149   priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
150   priv.Privilege[0].Luid = luid_privilege;
151   priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
152
153   if (!PrivilegeCheck(token, &priv, &ret))
154     return FALSE;
155
156   return ret;
157 }
158
159 /*
160  * Enable or disable particular privilege in specified access token.
161  *
162  * Note that it is not possible to disable privilege in access token with
163  * SE_PRIVILEGE_ENABLED_BY_DEFAULT attribute. This function does not check
164  * this case and incorrectly returns no error even when disabling failed.
165  * Rationale for this decision: Simplification of this function as WinAPI
166  * call AdjustTokenPrivileges() does not signal error in this case too.
167  */
168 static BOOL
169 set_privilege(HANDLE token, LUID luid_privilege, BOOL enable)
170 {
171   TOKEN_PRIVILEGES token_privileges;
172
173   token_privileges.PrivilegeCount = 1;
174   token_privileges.Privileges[0].Luid = luid_privilege;
175   token_privileges.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
176
177   /*
178    * WinAPI function AdjustTokenPrivileges() success also when not all
179    * privileges were enabled. It is always required to check for failure
180    * via GetLastError() call. AdjustTokenPrivileges() always sets error
181    * also when it success, as opposite to other WinAPI functions.
182    */
183   if (!AdjustTokenPrivileges(token, FALSE, &token_privileges, sizeof(token_privileges), NULL, NULL) ||
184       GetLastError() != ERROR_SUCCESS)
185     return FALSE;
186
187   return TRUE;
188 }
189
190 /*
191  * Change access token for the current thread to new specified access token.
192  * Previously active access token is stored in old_token variable and can be
193  * used for reverting to this access token. It is set to NULL if the current
194  * thread previously used primary process access token.
195  */
196 static BOOL
197 change_token(HANDLE new_token, HANDLE *old_token)
198 {
199   HANDLE token;
200
201   if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
202     {
203       if (GetLastError() != ERROR_NO_TOKEN)
204         return FALSE;
205       token = NULL;
206     }
207
208   if (!ImpersonateLoggedOnUser(new_token))
209     {
210       if (token)
211         CloseHandle(token);
212       return FALSE;
213     }
214
215   *old_token = token;
216   return TRUE;
217 }
218
219 /*
220  * Change access token for the current thread to the primary process access
221  * token. This function fails also when the current thread already uses primary
222  * process access token.
223  */
224 static BOOL
225 change_token_to_primary(HANDLE *old_token)
226 {
227   HANDLE token;
228
229   if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
230     return FALSE;
231
232   RevertToSelf();
233
234   *old_token = token;
235   return TRUE;
236 }
237
238 /*
239  * Revert to the specified access token for the current thread. When access
240  * token is specified as NULL then revert to the primary process access token.
241  * Use to revert after change_token() or change_token_to_primary() call.
242  */
243 static VOID
244 revert_to_token(HANDLE token)
245 {
246   /*
247    * If SetThreadToken() call fails then there is no option to revert to
248    * the specified previous thread access token. So in this case revert to
249    * the primary process access token.
250    */
251   if (!token || !SetThreadToken(NULL, token))
252     RevertToSelf();
253   if (token)
254     CloseHandle(token);
255 }
256
257 /*
258  * Enable particular privilege for the current thread. And set method how to
259  * revert this privilege (if to revert whole token or only privilege).
260  */
261 static BOOL
262 enable_privilege(LUID luid_privilege, HANDLE *revert_token, BOOL *revert_only_privilege)
263 {
264   HANDLE thread_token;
265   HANDLE new_token;
266
267   if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &thread_token))
268     {
269       if (set_privilege(thread_token, luid_privilege, TRUE))
270         {
271           /*
272            * Indicate that correct revert method is just to
273            * disable privilege in access token.
274            */
275           if (revert_token && revert_only_privilege)
276             {
277               *revert_token = thread_token;
278               *revert_only_privilege = TRUE;
279             }
280           else
281             {
282               CloseHandle(thread_token);
283             }
284           return TRUE;
285         }
286       CloseHandle(thread_token);
287       /*
288        * If enabling privilege failed then try to enable it via
289        * primary process access token.
290        */
291     }
292
293   /*
294    * If the current thread has already active thread access token then
295    * open it with just impersonate right as it would be used only for
296    * future revert.
297    */
298   if (revert_token && revert_only_privilege)
299     {
300       if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &thread_token))
301         {
302           if (GetLastError() != ERROR_NO_TOKEN)
303             return FALSE;
304           thread_token = NULL;
305         }
306
307       /*
308        * If current thread has no access token (and uses primary
309        * process access token) or it does not have permission to
310        * adjust privileges or it does not have specified privilege
311        * then create a copy of the primary process access token,
312        * assign it for the current thread (= impersonate self)
313        * and then try adjusting privilege again.
314        */
315       if (!ImpersonateSelf(SecurityImpersonation))
316         {
317           if (thread_token)
318             CloseHandle(thread_token);
319           return FALSE;
320         }
321     }
322
323   if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &new_token))
324     {
325       /* thread_token is set only when we were asked for revert method. */
326       if (revert_token && revert_only_privilege)
327         revert_to_token(thread_token);
328       return FALSE;
329     }
330
331   if (!set_privilege(new_token, luid_privilege, TRUE))
332     {
333       CloseHandle(new_token);
334       /* thread_token is set only when we were asked for revert method. */
335       if (revert_token && revert_only_privilege)
336         revert_to_token(thread_token);
337       return FALSE;
338     }
339
340   /*
341    * Indicate that correct revert method is to change to the previous
342    * access token. Either to the primary process access token or to the
343    * previous thread access token.
344    */
345   if (revert_token && revert_only_privilege)
346     {
347       *revert_token = thread_token;
348       *revert_only_privilege = FALSE;
349     }
350   return TRUE;
351 }
352
353 /*
354  * Revert particular privilege for the current thread was previously enabled by
355  * enable_privilege() call. Either disable privilege in specified access token
356  * or revert to previous access token.
357  */
358 static VOID
359 revert_privilege(LUID luid_privilege, HANDLE revert_token, BOOL revert_only_privilege)
360 {
361   if (revert_only_privilege)
362     {
363       set_privilege(revert_token, luid_privilege, FALSE);
364       CloseHandle(revert_token);
365     }
366   else
367     {
368       revert_to_token(revert_token);
369     }
370 }
371
372 /*
373  * Return owner of the access token used by the current thread. Buffer for
374  * returned owner needs to be released by LocalFree() call.
375  */
376 static TOKEN_OWNER *
377 get_current_token_owner(VOID)
378 {
379   HANDLE token;
380   DWORD length;
381   TOKEN_OWNER *owner;
382
383   /*
384    * If the current thread does not have active access token then thread
385    * uses primary process access token for all permission checks.
386    */
387   if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
388       (GetLastError() != ERROR_NO_TOKEN ||
389        !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
390     return NULL;
391
392   if (!GetTokenInformation(token, TokenOwner, NULL, 0, &length) &&
393       GetLastError() != ERROR_INSUFFICIENT_BUFFER)
394     {
395       CloseHandle(token);
396       return NULL;
397     }
398
399 retry:
400   owner = (TOKEN_OWNER *)LocalAlloc(LPTR, length);
401   if (!owner)
402     {
403       CloseHandle(token);
404       return NULL;
405     }
406
407   if (!GetTokenInformation(token, TokenOwner, owner, length, &length))
408     {
409       /*
410        * Length of token owner (SID) buffer between two get calls may
411        * changes (e.g. by another thread of process), so retry.
412        */
413       if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
414         {
415           LocalFree(owner);
416           goto retry;
417         }
418       LocalFree(owner);
419       CloseHandle(token);
420       return NULL;
421     }
422
423   CloseHandle(token);
424   return owner;
425 }
426
427 /*
428  * Grant particular permissions in the primary access token of the specified
429  * process for the owner of current thread token and set old DACL of the
430  * process access token for reverting permissions. Security descriptor is
431  * just memory buffer for old DACL.
432  */
433 static BOOL
434 grant_process_token_dacl_permissions(HANDLE process, DWORD permissions, HANDLE *token, PACL *old_dacl, PSECURITY_DESCRIPTOR *security_descriptor)
435 {
436   GetSecurityInfoProt MyGetSecurityInfo;
437   SetSecurityInfoProt MySetSecurityInfo;
438   SetEntriesInAclProt MySetEntriesInAcl;
439   EXPLICIT_ACCESS explicit_access;
440   TOKEN_OWNER *owner;
441   HMODULE advapi32;
442   PACL new_dacl;
443
444   /*
445    * This source file already uses advapi32.dll library, so it is
446    * linked to executable and automatically loaded when starting
447    * current running process.
448    */
449   advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
450   if (!advapi32)
451     return FALSE;
452
453   /*
454    * It does not matter if SetEntriesInAclA() or SetEntriesInAclW() is
455    * called as no string is passed to SetEntriesInAcl function.
456    */
457   MyGetSecurityInfo = (GetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "GetSecurityInfo");
458   MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
459   MySetEntriesInAcl = (SetEntriesInAclProt)(LPVOID)GetProcAddress(advapi32, "SetEntriesInAclA");
460   if (!MyGetSecurityInfo || !MySetSecurityInfo || !MySetEntriesInAcl)
461     return FALSE;
462
463   owner = get_current_token_owner();
464   if (!owner)
465     return FALSE;
466
467   /*
468    * READ_CONTROL is required for GetSecurityInfo(DACL_SECURITY_INFORMATION)
469    * and WRITE_DAC is required for SetSecurityInfo(DACL_SECURITY_INFORMATION).
470    */
471   if (!OpenProcessToken(process, READ_CONTROL | WRITE_DAC, token))
472     {
473       LocalFree(owner);
474       return FALSE;
475     }
476
477   if (MyGetSecurityInfo(*token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL, security_descriptor) != ERROR_SUCCESS)
478     {
479       LocalFree(owner);
480       CloseHandle(*token);
481       return FALSE;
482     }
483
484   /*
485    * Set new explicit access for the owner of the current thread access
486    * token with non-inherited granting access to specified permissions.
487    */
488   explicit_access.grfAccessPermissions = permissions;
489   explicit_access.grfAccessMode = GRANT_ACCESS;
490   explicit_access.grfInheritance = NO_PROPAGATE_INHERIT_ACE;
491   explicit_access.Trustee.pMultipleTrustee = NULL;
492   explicit_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
493   explicit_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
494   explicit_access.Trustee.TrusteeType = TRUSTEE_IS_USER;
495   /*
496    * Unfortunately i586-mingw32msvc toolchain does not have pSid pointer
497    * member in Trustee union. So assign owner SID to ptstrName pointer
498    * member which aliases with pSid pointer member in the same union.
499    */
500   explicit_access.Trustee.ptstrName = (PVOID)owner->Owner;
501
502   if (MySetEntriesInAcl(1, &explicit_access, *old_dacl, &new_dacl) != ERROR_SUCCESS)
503     {
504       LocalFree(*security_descriptor);
505       LocalFree(owner);
506       CloseHandle(*token);
507       return FALSE;
508     }
509
510   if (MySetSecurityInfo(*token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, new_dacl, NULL) != ERROR_SUCCESS)
511     {
512       LocalFree(new_dacl);
513       LocalFree(*security_descriptor);
514       LocalFree(owner);
515       CloseHandle(*token);
516       return FALSE;
517     }
518
519   LocalFree(new_dacl);
520   LocalFree(owner);
521   return TRUE;
522 }
523
524 /*
525  * Revert particular granted permissions in specified access token done by
526  * grant_process_token_dacl_permissions() call.
527  */
528 static VOID
529 revert_token_dacl_permissions(HANDLE token, PACL old_dacl, PSECURITY_DESCRIPTOR security_descriptor)
530 {
531   SetSecurityInfoProt MySetSecurityInfo;
532   HMODULE advapi32;
533
534   /*
535    * This source file already uses advapi32.dll library, so it is
536    * linked to executable and automatically loaded when starting
537    * current running process.
538    */
539   advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
540   if (advapi32)
541     {
542       MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
543       MySetSecurityInfo(token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL);
544     }
545
546   LocalFree(security_descriptor);
547   CloseHandle(token);
548 }
549
550 /*
551  * Change error mode of the current thread. If it is not possible then change
552  * error mode of the whole process. Always returns previous error mode.
553  */
554 static UINT
555 change_error_mode(UINT new_mode)
556 {
557   SetThreadErrorModeProt MySetThreadErrorMode = NULL;
558   HMODULE kernel32;
559   DWORD old_mode;
560
561   /*
562    * Function SetThreadErrorMode() was introduced in Windows 7, so use
563    * GetProcAddress() for compatibility with older systems.
564    */
565   kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
566   if (kernel32)
567     MySetThreadErrorMode = (SetThreadErrorModeProt)(LPVOID)GetProcAddress(kernel32, "SetThreadErrorMode");
568
569   if (MySetThreadErrorMode &&
570       MySetThreadErrorMode(new_mode, &old_mode))
571     return old_mode;
572
573   /*
574    * Fallback to function SetErrorMode() which modifies error mode of the
575    * whole process and returns old mode.
576    */
577   return SetErrorMode(new_mode);
578 }
579
580 /*
581  * Open process handle specified by the process id with the query right and
582  * optionally also with vm read right.
583  */
584 static HANDLE
585 open_process_for_query(DWORD pid, BOOL with_vm_read)
586 {
587   BOOL revert_only_privilege;
588   LUID luid_debug_privilege;
589   OSVERSIONINFO version;
590   DWORD process_right;
591   HANDLE revert_token;
592   HANDLE process;
593
594   /*
595    * Some processes on Windows Vista and higher systems can be opened only
596    * with PROCESS_QUERY_LIMITED_INFORMATION right. This right is enough
597    * for accessing primary process token. But this right is not supported
598    * on older pre-Vista systems. When the current thread on these older
599    * systems does not have Debug privilege then OpenProcess() fails with
600    * ERROR_ACCESS_DENIED. If the current thread has Debug privilege then
601    * OpenProcess() success and returns handle to requested process.
602    * Problem is that this handle does not have PROCESS_QUERY_INFORMATION
603    * right and so cannot be used for accessing primary process token
604    * on those older systems. Moreover it has zero rights and therefore
605    * such handle is fully useless. So never try to use open process with
606    * PROCESS_QUERY_LIMITED_INFORMATION right on older systems than
607    * Windows Vista (NT 6.0).
608    */
609   version.dwOSVersionInfoSize = sizeof(version);
610   if (GetVersionEx(&version) &&
611       version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
612       version.dwMajorVersion >= 6)
613     process_right = PROCESS_QUERY_LIMITED_INFORMATION;
614   else
615     process_right = PROCESS_QUERY_INFORMATION;
616
617   if (with_vm_read)
618     process_right |= PROCESS_VM_READ;
619
620   process = OpenProcess(process_right, FALSE, pid);
621   if (process)
622     return process;
623
624   /*
625    * It is possible to open only processes to which owner of the current
626    * thread access token has permissions. For opening other processing it
627    * is required to have Debug privilege enabled. By default local
628    * administrators have this privilege, but it is disabled. So try to
629    * enable it and then try to open process again.
630    */
631
632   if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
633     return NULL;
634
635   if (!enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
636     return NULL;
637
638   process = OpenProcess(process_right, FALSE, pid);
639
640   revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
641
642   return process;
643 }
644
645 /*
646  * Check if process image path name (wide string) matches exe file name
647  * (7-bit ASCII string). Do case-insensitive string comparison. Process
648  * image path name can be in any namespace format (DOS, Win32, UNC, ...).
649  */
650 static BOOL
651 check_process_name(LPCWSTR path, DWORD path_length, LPCSTR exe_file)
652 {
653   DWORD exe_file_length;
654   WCHAR c1;
655   UCHAR c2;
656   DWORD i;
657
658   exe_file_length = 0;
659   while (exe_file[exe_file_length] != '\0')
660     exe_file_length++;
661
662   /* Path must have backslash before exe file name. */
663   if (exe_file_length >= path_length ||
664       path[path_length-exe_file_length-1] != L'\\')
665     return FALSE;
666
667   for (i = 0; i < exe_file_length; i++)
668     {
669       c1 = path[path_length-exe_file_length+i];
670       c2 = exe_file[i];
671       /*
672        * Input string for comparison is 7-bit ASCII and file name part
673        * of path must not contain backslash as it is path separator.
674        */
675       if (c1 >= 0x80 || c2 >= 0x80 || c1 == L'\\')
676         return FALSE;
677       if (c1 >= L'a' && c1 <= L'z')
678         c1 -= L'a' - L'A';
679       if (c2 >= 'a' && c2 <= 'z')
680         c2 -= 'a' - 'A';
681       if (c1 != c2)
682         return FALSE;
683     }
684
685   return TRUE;
686 }
687
688 /* Open process handle with the query right specified by process exe file. */
689 static HANDLE
690 find_and_open_process_for_query(LPCSTR exe_file)
691 {
692   GetProcessImageFileNameWProt MyGetProcessImageFileNameW;
693   GetModuleFileNameExWProt MyGetModuleFileNameExW;
694   EnumProcessesProt MyEnumProcesses;
695   HMODULE kernel32, psapi;
696   UINT prev_error_mode;
697   DWORD partial_retry;
698   BOOL found_process;
699   DWORD size, length;
700   DWORD *processes;
701   HANDLE process;
702   LPWSTR path;
703   DWORD error;
704   DWORD count;
705   DWORD i;
706
707   psapi = NULL;
708   kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
709   if (!kernel32)
710     return NULL;
711
712   /*
713    * On Windows 7 and higher systems these functions are available in
714    * kernel32.dll library with K32 prefix.
715    */
716   MyGetModuleFileNameExW = NULL;
717   MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(kernel32, "K32GetProcessImageFileNameW");
718   MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(kernel32, "K32EnumProcesses");
719   if (!MyGetProcessImageFileNameW || !MyEnumProcesses)
720     {
721       /*
722        * On older NT-based systems these functions are available in
723        * psapi.dll library without K32 prefix.
724        */
725       prev_error_mode = change_error_mode(SEM_FAILCRITICALERRORS);
726       psapi = LoadLibrary(TEXT("psapi.dll"));
727       change_error_mode(prev_error_mode);
728
729       if (!psapi)
730         return NULL;
731
732       /*
733        * Function GetProcessImageFileNameW() is available in
734        * Windows XP and higher systems. On older versions is
735        * available function GetModuleFileNameExW().
736        */
737       MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(psapi, "GetProcessImageFileNameW");
738       MyGetModuleFileNameExW = (GetModuleFileNameExWProt)(LPVOID)GetProcAddress(psapi, "GetModuleFileNameExW");
739       MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(psapi, "EnumProcesses");
740       if ((!MyGetProcessImageFileNameW && !MyGetModuleFileNameExW) || !MyEnumProcesses)
741         {
742           FreeLibrary(psapi);
743           return NULL;
744         }
745     }
746
747   /* Make initial buffer size for 1024 processes. */
748   size = 1024 * sizeof(*processes);
749
750 retry:
751   processes = (DWORD *)LocalAlloc(LPTR, size);
752   if (!processes)
753     {
754       if (psapi)
755         FreeLibrary(psapi);
756       return NULL;
757     }
758
759   if (!MyEnumProcesses(processes, size, &length))
760     {
761       LocalFree(processes);
762       if (psapi)
763         FreeLibrary(psapi);
764       return NULL;
765     }
766   else if (size == length)
767     {
768       /*
769        * There is no indication given when the buffer is too small to
770        * store all process identifiers. Therefore if returned length
771        * is same as buffer size there can be more processes. Call
772        * again with larger buffer.
773        */
774       LocalFree(processes);
775       size *= 2;
776       goto retry;
777     }
778
779   process = NULL;
780   count = length / sizeof(*processes);
781
782   for (i = 0; i < count; i++)
783     {
784       /* Skip System Idle Process. */
785       if (processes[i] == 0)
786         continue;
787
788       /*
789        * Function GetModuleFileNameExW() requires additional
790        * PROCESS_VM_READ right as opposite to function
791        * GetProcessImageFileNameW() which does not need it.
792        */
793       process = open_process_for_query(processes[i], MyGetProcessImageFileNameW ? FALSE : TRUE);
794       if (!process)
795         continue;
796
797       /*
798        * Set initial buffer size to 256 (wide) characters.
799        * Final path length on the modern NT-based systems can be also larger.
800        */
801       size = 256;
802       found_process = FALSE;
803       partial_retry = 0;
804
805 retry_path:
806       path = (LPWSTR)LocalAlloc(LPTR, size * sizeof(*path));
807       if (!path)
808         goto end_path;
809
810       if (MyGetProcessImageFileNameW)
811         length = MyGetProcessImageFileNameW(process, path, size);
812       else
813         length = MyGetModuleFileNameExW(process, NULL, path, size);
814
815       error = GetLastError();
816
817       /*
818        * GetModuleFileNameEx() returns zero and signal error ERROR_PARTIAL_COPY
819        * when remote process is in the middle of updating its module table.
820        * Sleep 10 ms and try again, max 10 attempts.
821        */
822       if (!MyGetProcessImageFileNameW)
823         {
824           if (length == 0 && error == ERROR_PARTIAL_COPY && partial_retry++ < 10)
825             {
826               Sleep(10);
827               goto retry_path;
828             }
829           partial_retry = 0;
830         }
831
832       /*
833        * When buffer is too small then function GetModuleFileNameEx() returns
834        * its size argument on older systems (Windows XP) or its size minus
835        * argument one on new systems (Windows 10) without signalling any error.
836        * Function GetProcessImageFileNameW() on the other hand returns zero
837        * value and signals error ERROR_INSUFFICIENT_BUFFER. So in all these
838        * cases call function again with larger buffer.
839        */
840
841       if (MyGetProcessImageFileNameW && length == 0 && error != ERROR_INSUFFICIENT_BUFFER)
842         goto end_path;
843
844       if ((MyGetProcessImageFileNameW && length == 0) ||
845           (!MyGetProcessImageFileNameW && (length == size || length == size-1)))
846         {
847           LocalFree(path);
848           size *= 2;
849           goto retry_path;
850         }
851
852       if (length && check_process_name(path, length, exe_file))
853         found_process = TRUE;
854
855 end_path:
856       if (path)
857         {
858           LocalFree(path);
859           path = NULL;
860         }
861
862       if (found_process)
863         break;
864
865       CloseHandle(process);
866       process = NULL;
867     }
868
869   LocalFree(processes);
870
871   if (psapi)
872     FreeLibrary(psapi);
873
874   return process;
875 }
876
877 /*
878  * Try to open primary access token of the particular process with specified
879  * rights. Before opening access token try to adjust DACL permissions of the
880  * primary process access token, so following open does not fail on error
881  * related to no open permissions. Revert DACL permissions after open attempt.
882  * As following steps are not atomic, try to execute them more times in case
883  * of possible race conditions caused by other threads or processes.
884  */
885 static HANDLE
886 try_grant_permissions_and_open_process_token(HANDLE process, DWORD rights)
887 {
888   PSECURITY_DESCRIPTOR security_descriptor;
889   HANDLE grant_token;
890   PACL old_dacl;
891   HANDLE token;
892   DWORD retry;
893   DWORD error;
894
895   /*
896    * This code is not atomic. Between grant and open calls can other
897    * thread or process change or revert permissions. So try to execute
898    * it more times.
899    */
900   for (retry = 0; retry < 10; retry++)
901     {
902       if (!grant_process_token_dacl_permissions(process, rights, &grant_token, &old_dacl, &security_descriptor))
903         return NULL;
904       if (!OpenProcessToken(process, rights, &token))
905         {
906           token = NULL;
907           error = GetLastError();
908         }
909       revert_token_dacl_permissions(grant_token, old_dacl, security_descriptor);
910       if (token)
911         return token;
912       else if (error != ERROR_ACCESS_DENIED)
913         return NULL;
914     }
915
916   return NULL;
917 }
918
919 /*
920  * Open primary access token of particular process handle with specified rights.
921  * If permissions for specified rights are missing then try to grant them.
922  */
923 static HANDLE
924 open_process_token_with_rights(HANDLE process, DWORD rights)
925 {
926   HANDLE old_token;
927   HANDLE token;
928
929   /* First try to open primary access token of process handle directly. */
930   if (OpenProcessToken(process, rights, &token))
931     return token;
932
933   /*
934    * If opening failed then it means that owner of the current thread
935    * access token does not have permission for it. Try it again with
936    * primary process access token.
937    */
938   if (change_token_to_primary(&old_token))
939     {
940       if (!OpenProcessToken(process, rights, &token))
941         token = NULL;
942       revert_to_token(old_token);
943       if (token)
944         return token;
945     }
946
947   /*
948    * If opening is still failing then try to grant specified permissions
949    * for the current thread and try to open it again.
950    */
951   token = try_grant_permissions_and_open_process_token(process, rights);
952   if (token)
953     return token;
954
955   /*
956    * And if it is still failing then try it again with granting
957    * permissions for the primary process token of the current process.
958    */
959   if (change_token_to_primary(&old_token))
960     {
961       token = try_grant_permissions_and_open_process_token(process, rights);
962       revert_to_token(old_token);
963       if (token)
964         return token;
965     }
966
967   /*
968    * TODO: Sorry, no other option for now...
969    * It could be possible to use Take Ownership Name privilege to
970    * temporary change token owner of specified process to the owner of
971    * the current thread token, grant permissions for current thread in
972    * that process token, change ownership back to original one, open
973    * that process token and revert granted permissions. But this is
974    * not implemented yet.
975    */
976   return NULL;
977 }
978
979 /*
980  * Set x86 I/O Privilege Level to 3 for the whole current NT process. Do it via
981  * NtSetInformationProcess() call with ProcessUserModeIOPL information class,
982  * which is supported by 32-bit Windows NT kernel versions and requires Tcb
983  * privilege.
984  */
985 static BOOL
986 SetProcessUserModeIOPL(VOID)
987 {
988   NtSetInformationProcessProt MyNtSetInformationProcess;
989
990   LUID luid_tcb_privilege;
991   LUID luid_impersonate_privilege;
992
993   HANDLE revert_token_tcb_privilege;
994   BOOL revert_only_tcb_privilege;
995
996   HANDLE revert_token_impersonate_privilege;
997   BOOL revert_only_impersonate_privilege;
998
999   BOOL impersonate_privilege_enabled;
1000
1001   BOOL revert_to_old_token;
1002   HANDLE old_token;
1003
1004   HANDLE lsass_process;
1005   HANDLE lsass_token;
1006
1007   UINT prev_error_mode;
1008   NTSTATUS nt_status;
1009   HMODULE ntdll;
1010   BOOL ret;
1011
1012   impersonate_privilege_enabled = FALSE;
1013   revert_to_old_token = FALSE;
1014   lsass_token = NULL;
1015   old_token = NULL;
1016
1017   /* Fast path when ProcessUserModeIOPL was already called. */
1018   if (read_iopl() == 3)
1019     return TRUE;
1020
1021   /*
1022    * Load ntdll.dll library with disabled critical-error-handler message box.
1023    * It means that NT kernel does not show unwanted GUI message box to user
1024    * when LoadLibrary() function fails.
1025    */
1026   prev_error_mode = change_error_mode(SEM_FAILCRITICALERRORS);
1027   ntdll = LoadLibrary(TEXT("ntdll.dll"));
1028   change_error_mode(prev_error_mode);
1029   if (!ntdll)
1030     goto err_not_implemented;
1031
1032   /* Retrieve pointer to NtSetInformationProcess() function. */
1033   MyNtSetInformationProcess = (NtSetInformationProcessProt)(LPVOID)GetProcAddress(ntdll, "NtSetInformationProcess");
1034   if (!MyNtSetInformationProcess)
1035     goto err_not_implemented;
1036
1037   /*
1038    * ProcessUserModeIOPL is syscall for NT kernel to change x86 IOPL
1039    * of the current running process to 3.
1040    *
1041    * Process handle argument for ProcessUserModeIOPL is ignored and
1042    * IOPL is always changed for the current running process. So pass
1043    * GetCurrentProcess() handle for documentation purpose. Process
1044    * information buffer and length are unused for ProcessUserModeIOPL.
1045    *
1046    * ProcessUserModeIOPL may success (return value >= 0) or may fail
1047    * because it is not implemented or because of missing privilege.
1048    * Other errors are not defined, so handle them as unknown.
1049    */
1050   nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1051   if (nt_status >= 0)
1052     goto verify;
1053   else if (nt_status == STATUS_NOT_IMPLEMENTED)
1054     goto err_not_implemented;
1055   else if (nt_status != STATUS_PRIVILEGE_NOT_HELD)
1056     goto err_unknown;
1057
1058   /*
1059    * If ProcessUserModeIOPL call failed with STATUS_PRIVILEGE_NOT_HELD
1060    * error then it means that the current thread token does not have
1061    * Tcb privilege enabled. Try to enable it.
1062    */
1063
1064   if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &luid_tcb_privilege))
1065     goto err_privilege_not_held;
1066
1067   /*
1068    * If the current thread has already Tcb privilege enabled then there
1069    * is some additional unhanded restriction.
1070    */
1071   if (have_privilege(luid_tcb_privilege))
1072     goto err_privilege_not_held;
1073
1074   /* Try to enable Tcb privilege and try ProcessUserModeIOPL call again. */
1075   if (enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
1076     {
1077       nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1078       revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
1079       if (nt_status >= 0)
1080         goto verify;
1081       else if (nt_status == STATUS_NOT_IMPLEMENTED)
1082         goto err_not_implemented;
1083       else if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1084         goto err_privilege_not_held;
1085       else
1086         goto err_unknown;
1087     }
1088
1089   /*
1090    * If enabling of Tcb privilege failed then it means that current thread
1091    * does not this privilege. But current process may have it. So try it
1092    * again with primary process access token.
1093    */
1094
1095   /*
1096    * If system supports Impersonate privilege (Windows 2000 SP4 or higher) then
1097    * all future actions in this function require this Impersonate privilege.
1098    * So try to enable it in case it is currently disabled.
1099    */
1100   if (LookupPrivilegeValue(NULL, SE_IMPERSONATE_NAME, &luid_impersonate_privilege) &&
1101       !have_privilege(luid_impersonate_privilege))
1102     {
1103       /*
1104        * If current thread does not have Impersonate privilege enabled
1105        * then first try to enable it just for the current thread. If
1106        * it is not possible to enable it just for the current thread
1107        * then try it to enable globally for whole process (which
1108        * affects all process threads). Both actions will be reverted
1109        * at the end of this function.
1110        */
1111       if (enable_privilege(luid_impersonate_privilege, &revert_token_impersonate_privilege, &revert_only_impersonate_privilege))
1112         {
1113           impersonate_privilege_enabled = TRUE;
1114         }
1115       else if (enable_privilege(luid_impersonate_privilege, NULL, NULL))
1116         {
1117           impersonate_privilege_enabled = TRUE;
1118           revert_token_impersonate_privilege = NULL;
1119           revert_only_impersonate_privilege = TRUE;
1120         }
1121       else
1122         {
1123           goto err_privilege_not_held;
1124         }
1125
1126       /*
1127        * Now when Impersonate privilege is enabled, try to enable Tcb
1128        * privilege again. Enabling other privileges for the current
1129        * thread requires Impersonate privilege, so enabling Tcb again
1130        * could now pass.
1131        */
1132       if (enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
1133         {
1134           nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1135           revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
1136           if (nt_status >= 0)
1137             goto verify;
1138           else if (nt_status == STATUS_NOT_IMPLEMENTED)
1139             goto err_not_implemented;
1140           else if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1141             goto err_privilege_not_held;
1142           else
1143             goto err_unknown;
1144         }
1145     }
1146
1147   /*
1148    * If enabling Tcb privilege failed then it means that the current
1149    * thread access token does not have this privilege or does not
1150    * have permission to adjust privileges.
1151    *
1152    * Try to use more privileged token from Local Security Authority
1153    * Subsystem Service process (lsass.exe) which has Tcb privilege.
1154    * Retrieving this more privileged token is possible for local
1155    * administrators (unless it was disabled by local administrators).
1156    */
1157
1158   lsass_process = find_and_open_process_for_query("lsass.exe");
1159   if (!lsass_process)
1160     goto err_privilege_not_held;
1161
1162   /*
1163    * Open primary lsass.exe process access token with query and duplicate
1164    * rights. Just these two rights are required for impersonating other
1165    * primary process token (impersonate right is really not required!).
1166    */
1167   lsass_token = open_process_token_with_rights(lsass_process, TOKEN_QUERY | TOKEN_DUPLICATE);
1168
1169   CloseHandle(lsass_process);
1170
1171   if (!lsass_token)
1172     goto err_privilege_not_held;
1173
1174   /*
1175    * After successful open of the primary lsass.exe process access token,
1176    * assign its copy for the current thread.
1177    */
1178   if (!change_token(lsass_token, &old_token))
1179     goto err_privilege_not_held;
1180
1181   revert_to_old_token = TRUE;
1182
1183   nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1184   if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1185     {
1186       /*
1187        * Now current thread is not using primary process token anymore
1188        * but is using custom access token. There is no need to revert
1189        * enabled Tcb privilege as the whole custom access token would
1190        * be reverted. So there is no need to setup revert method for
1191        * enabling privilege.
1192        */
1193       if (have_privilege(luid_tcb_privilege) ||
1194           !enable_privilege(luid_tcb_privilege, NULL, NULL))
1195         goto err_privilege_not_held;
1196       nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1197     }
1198   if (nt_status >= 0)
1199     goto verify;
1200   else if (nt_status == STATUS_NOT_IMPLEMENTED)
1201     goto err_not_implemented;
1202   else if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1203     goto err_privilege_not_held;
1204   else
1205     goto err_unknown;
1206
1207 verify:
1208   /*
1209    * Some Windows NT kernel versions (e.g. Windows 2003 x64) do not
1210    * implement ProcessUserModeIOPL syscall at all but incorrectly
1211    * returns success when it is called by user process. So always
1212    * after this call verify that IOPL is set to 3.
1213    */
1214   if (read_iopl() != 3)
1215     goto err_not_implemented;
1216   ret = TRUE;
1217   goto ret;
1218
1219 err_not_implemented:
1220   SetLastError(ERROR_INVALID_FUNCTION);
1221   ret = FALSE;
1222   goto ret;
1223
1224 err_privilege_not_held:
1225   SetLastError(ERROR_PRIVILEGE_NOT_HELD);
1226   ret = FALSE;
1227   goto ret;
1228
1229 err_unknown:
1230   SetLastError(ERROR_GEN_FAILURE);
1231   ret = FALSE;
1232   goto ret;
1233
1234 ret:
1235   if (revert_to_old_token)
1236     revert_to_token(old_token);
1237
1238   if (impersonate_privilege_enabled)
1239     revert_privilege(luid_impersonate_privilege, revert_token_impersonate_privilege, revert_only_impersonate_privilege);
1240
1241   if (lsass_token)
1242     CloseHandle(lsass_token);
1243
1244   if (ntdll)
1245     FreeLibrary(ntdll);
1246
1247   return ret;
1248 }
1249
1250 static int
1251 intel_setup_io(struct pci_access *a)
1252 {
1253 #ifndef _WIN64
1254   /* 16/32-bit non-NT systems allow applications to access PCI I/O ports without any special setup. */
1255   OSVERSIONINFOA version;
1256   version.dwOSVersionInfoSize = sizeof(version);
1257   if (GetVersionExA(&version) && version.dwPlatformId < VER_PLATFORM_WIN32_NT)
1258     {
1259       a->debug("Detected 16/32-bit non-NT system, skipping NT setup...");
1260       return 1;
1261     }
1262 #endif
1263
1264   /* On NT-based systems issue ProcessUserModeIOPL syscall which changes IOPL to 3. */
1265   if (!SetProcessUserModeIOPL())
1266     {
1267       DWORD error = GetLastError();
1268       a->debug("NT ProcessUserModeIOPL call failed: %s.", error == ERROR_INVALID_FUNCTION ? "Not Implemented" : error == ERROR_PRIVILEGE_NOT_HELD ? "Access Denied" : "Operation Failed");
1269       return 0;
1270     }
1271
1272   a->debug("NT ProcessUserModeIOPL call succeeded...");
1273   return 1;
1274 }
1275
1276 static inline void
1277 intel_cleanup_io(struct pci_access *a UNUSED)
1278 {
1279   /*
1280    * 16/32-bit non-NT systems do not use any special setup and on NT-based
1281    * systems ProcessUserModeIOPL permanently changes IOPL to 3 for the current
1282    * NT process, no revert for current process is possible.
1283    */
1284 }
1285
1286 static inline void intel_io_lock(void)
1287 {
1288 }
1289
1290 static inline void intel_io_unlock(void)
1291 {
1292 }