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