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