]> mj.ucw.cz Git - pciutils.git/blob - lib/i386-io-windows.h
fd4030d9e7125942c58d4f12fe45ea39b99b0e81
[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(new_dacl);
511       LocalFree(*security_descriptor);
512       LocalFree(owner);
513       CloseHandle(*token);
514       return FALSE;
515     }
516
517   LocalFree(new_dacl);
518   LocalFree(owner);
519   return TRUE;
520 }
521
522 /*
523  * Revert particular granted permissions in specified access token done by
524  * grant_process_token_dacl_permissions() call.
525  */
526 static VOID
527 revert_token_dacl_permissions(HANDLE token, PACL old_dacl, PSECURITY_DESCRIPTOR security_descriptor)
528 {
529   SetSecurityInfoProt MySetSecurityInfo;
530   HMODULE advapi32;
531
532   /*
533    * This source file already uses advapi32.dll library, so it is
534    * linked to executable and automatically loaded when starting
535    * current running process.
536    */
537   advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
538   if (advapi32)
539     {
540       MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
541       MySetSecurityInfo(token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL);
542     }
543
544   LocalFree(security_descriptor);
545   CloseHandle(token);
546 }
547
548 /*
549  * Change error mode of the current thread. If it is not possible then change
550  * error mode of the whole process. Always returns previous error mode.
551  */
552 static UINT
553 change_error_mode(UINT new_mode)
554 {
555   SetThreadErrorModeProt MySetThreadErrorMode = NULL;
556   HMODULE kernel32;
557   DWORD old_mode;
558
559   /*
560    * Function SetThreadErrorMode() was introduced in Windows 7, so use
561    * GetProcAddress() for compatibility with older systems.
562    */
563   kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
564   if (kernel32)
565     MySetThreadErrorMode = (SetThreadErrorModeProt)(LPVOID)GetProcAddress(kernel32, "SetThreadErrorMode");
566
567   if (MySetThreadErrorMode &&
568       MySetThreadErrorMode(new_mode, &old_mode))
569     return old_mode;
570
571   /*
572    * Fallback to function SetErrorMode() which modifies error mode of the
573    * whole process and returns old mode.
574    */
575   return SetErrorMode(new_mode);
576 }
577
578 /*
579  * Open process handle specified by the process id with the query right and
580  * optionally also with vm read right.
581  */
582 static HANDLE
583 open_process_for_query(DWORD pid, BOOL with_vm_read)
584 {
585   BOOL revert_only_privilege;
586   LUID luid_debug_privilege;
587   OSVERSIONINFO version;
588   DWORD process_right;
589   HANDLE revert_token;
590   HANDLE process;
591
592   /*
593    * Some processes on Windows Vista and higher systems can be opened only
594    * with PROCESS_QUERY_LIMITED_INFORMATION right. This right is enough
595    * for accessing primary process token. But this right is not supported
596    * on older pre-Vista systems. When the current thread on these older
597    * systems does not have Debug privilege then OpenProcess() fails with
598    * ERROR_ACCESS_DENIED. If the current thread has Debug privilege then
599    * OpenProcess() success and returns handle to requested process.
600    * Problem is that this handle does not have PROCESS_QUERY_INFORMATION
601    * right and so cannot be used for accessing primary process token
602    * on those older systems. Moreover it has zero rights and therefore
603    * such handle is fully useless. So never try to use open process with
604    * PROCESS_QUERY_LIMITED_INFORMATION right on older systems than
605    * Windows Vista (NT 6.0).
606    */
607   version.dwOSVersionInfoSize = sizeof(version);
608   if (GetVersionEx(&version) &&
609       version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
610       version.dwMajorVersion >= 6)
611     process_right = PROCESS_QUERY_LIMITED_INFORMATION;
612   else
613     process_right = PROCESS_QUERY_INFORMATION;
614
615   if (with_vm_read)
616     process_right |= PROCESS_VM_READ;
617
618   process = OpenProcess(process_right, FALSE, pid);
619   if (process)
620     return process;
621
622   /*
623    * It is possible to open only processes to which owner of the current
624    * thread access token has permissions. For opening other processing it
625    * is required to have Debug privilege enabled. By default local
626    * administrators have this privilege, but it is disabled. So try to
627    * enable it and then try to open process again.
628    */
629
630   if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
631     return NULL;
632
633   if (!enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
634     return NULL;
635
636   process = OpenProcess(process_right, FALSE, pid);
637
638   revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
639
640   return process;
641 }
642
643 /*
644  * Check if process image path name (wide string) matches exe file name
645  * (7-bit ASCII string). Do case-insensitive string comparison. Process
646  * image path name can be in any namespace format (DOS, Win32, UNC, ...).
647  */
648 static BOOL
649 check_process_name(LPCWSTR path, DWORD path_length, LPCSTR exe_file)
650 {
651   DWORD exe_file_length;
652   WCHAR c1;
653   UCHAR c2;
654   DWORD i;
655
656   exe_file_length = 0;
657   while (exe_file[exe_file_length] != '\0')
658     exe_file_length++;
659
660   /* Path must have backslash before exe file name. */
661   if (exe_file_length >= path_length ||
662       path[path_length-exe_file_length-1] != L'\\')
663     return FALSE;
664
665   for (i = 0; i < exe_file_length; i++)
666     {
667       c1 = path[path_length-exe_file_length+i];
668       c2 = exe_file[i];
669       /*
670        * Input string for comparison is 7-bit ASCII and file name part
671        * of path must not contain backslash as it is path separator.
672        */
673       if (c1 >= 0x80 || c2 >= 0x80 || c1 == L'\\')
674         return FALSE;
675       if (c1 >= L'a' && c1 <= L'z')
676         c1 -= L'a' - L'A';
677       if (c2 >= 'a' && c2 <= 'z')
678         c2 -= 'a' - 'A';
679       if (c1 != c2)
680         return FALSE;
681     }
682
683   return TRUE;
684 }
685
686 /* Open process handle with the query right specified by process exe file. */
687 static HANDLE
688 find_and_open_process_for_query(LPCSTR exe_file)
689 {
690   GetProcessImageFileNameWProt MyGetProcessImageFileNameW;
691   GetModuleFileNameExWProt MyGetModuleFileNameExW;
692   EnumProcessesProt MyEnumProcesses;
693   HMODULE kernel32, psapi;
694   UINT prev_error_mode;
695   DWORD partial_retry;
696   BOOL found_process;
697   DWORD size, length;
698   DWORD *processes;
699   HANDLE process;
700   LPWSTR path;
701   DWORD error;
702   DWORD count;
703   DWORD i;
704
705   psapi = NULL;
706   kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
707   if (!kernel32)
708     return NULL;
709
710   /*
711    * On Windows 7 and higher systems these functions are available in
712    * kernel32.dll library with K32 prefix.
713    */
714   MyGetModuleFileNameExW = NULL;
715   MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(kernel32, "K32GetProcessImageFileNameW");
716   MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(kernel32, "K32EnumProcesses");
717   if (!MyGetProcessImageFileNameW || !MyEnumProcesses)
718     {
719       /*
720        * On older NT-based systems these functions are available in
721        * psapi.dll library without K32 prefix.
722        */
723       prev_error_mode = change_error_mode(SEM_FAILCRITICALERRORS);
724       psapi = LoadLibrary(TEXT("psapi.dll"));
725       change_error_mode(prev_error_mode);
726
727       if (!psapi)
728         return NULL;
729
730       /*
731        * Function GetProcessImageFileNameW() is available in
732        * Windows XP and higher systems. On older versions is
733        * available function GetModuleFileNameExW().
734        */
735       MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(psapi, "GetProcessImageFileNameW");
736       MyGetModuleFileNameExW = (GetModuleFileNameExWProt)(LPVOID)GetProcAddress(psapi, "GetModuleFileNameExW");
737       MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(psapi, "EnumProcesses");
738       if ((!MyGetProcessImageFileNameW && !MyGetModuleFileNameExW) || !MyEnumProcesses)
739         {
740           FreeLibrary(psapi);
741           return NULL;
742         }
743     }
744
745   /* Make initial buffer size for 1024 processes. */
746   size = 1024 * sizeof(*processes);
747
748 retry:
749   processes = (DWORD *)LocalAlloc(LPTR, size);
750   if (!processes)
751     {
752       if (psapi)
753         FreeLibrary(psapi);
754       return NULL;
755     }
756
757   if (!MyEnumProcesses(processes, size, &length))
758     {
759       LocalFree(processes);
760       if (psapi)
761         FreeLibrary(psapi);
762       return NULL;
763     }
764   else if (size == length)
765     {
766       /*
767        * There is no indication given when the buffer is too small to
768        * store all process identifiers. Therefore if returned length
769        * is same as buffer size there can be more processes. Call
770        * again with larger buffer.
771        */
772       LocalFree(processes);
773       size *= 2;
774       goto retry;
775     }
776
777   process = NULL;
778   count = length / sizeof(*processes);
779
780   for (i = 0; i < count; i++)
781     {
782       /* Skip System Idle Process. */
783       if (processes[i] == 0)
784         continue;
785
786       /*
787        * Function GetModuleFileNameExW() requires additional
788        * PROCESS_VM_READ right as opposite to function
789        * GetProcessImageFileNameW() which does not need it.
790        */
791       process = open_process_for_query(processes[i], MyGetProcessImageFileNameW ? FALSE : TRUE);
792       if (!process)
793         continue;
794
795       /*
796        * Set initial buffer size to 256 (wide) characters.
797        * Final path length on the modern NT-based systems can be also larger.
798        */
799       size = 256;
800       found_process = FALSE;
801       partial_retry = 0;
802
803 retry_path:
804       path = (LPWSTR)LocalAlloc(LPTR, size * sizeof(*path));
805       if (!path)
806         goto end_path;
807
808       if (MyGetProcessImageFileNameW)
809         length = MyGetProcessImageFileNameW(process, path, size);
810       else
811         length = MyGetModuleFileNameExW(process, NULL, path, size);
812
813       error = GetLastError();
814
815       /*
816        * GetModuleFileNameEx() returns zero and signal error ERROR_PARTIAL_COPY
817        * when remote process is in the middle of updating its module table.
818        * Sleep 10 ms and try again, max 10 attempts.
819        */
820       if (!MyGetProcessImageFileNameW)
821         {
822           if (length == 0 && error == ERROR_PARTIAL_COPY && partial_retry++ < 10)
823             {
824               Sleep(10);
825               goto retry_path;
826             }
827           partial_retry = 0;
828         }
829
830       /*
831        * When buffer is too small then function GetModuleFileNameEx() returns
832        * its size argument on older systems (Windows XP) or its size minus
833        * argument one on new systems (Windows 10) without signalling any error.
834        * Function GetProcessImageFileNameW() on the other hand returns zero
835        * value and signals error ERROR_INSUFFICIENT_BUFFER. So in all these
836        * cases call function again with larger buffer.
837        */
838
839       if (MyGetProcessImageFileNameW && length == 0 && error != ERROR_INSUFFICIENT_BUFFER)
840         goto end_path;
841
842       if ((MyGetProcessImageFileNameW && length == 0) ||
843           (!MyGetProcessImageFileNameW && (length == size || length == size-1)))
844         {
845           LocalFree(path);
846           size *= 2;
847           goto retry_path;
848         }
849
850       if (length && check_process_name(path, length, exe_file))
851         found_process = TRUE;
852
853 end_path:
854       if (path)
855         {
856           LocalFree(path);
857           path = NULL;
858         }
859
860       if (found_process)
861         break;
862
863       CloseHandle(process);
864       process = NULL;
865     }
866
867   LocalFree(processes);
868
869   if (psapi)
870     FreeLibrary(psapi);
871
872   return process;
873 }
874
875 /*
876  * Try to open primary access token of the particular process with specified
877  * rights. Before opening access token try to adjust DACL permissions of the
878  * primary process access token, so following open does not fail on error
879  * related to no open permissions. Revert DACL permissions after open attempt.
880  * As following steps are not atomic, try to execute them more times in case
881  * of possible race conditions caused by other threads or processes.
882  */
883 static HANDLE
884 try_grant_permissions_and_open_process_token(HANDLE process, DWORD rights)
885 {
886   PSECURITY_DESCRIPTOR security_descriptor;
887   HANDLE grant_token;
888   PACL old_dacl;
889   HANDLE token;
890   DWORD retry;
891   DWORD error;
892
893   /*
894    * This code is not atomic. Between grant and open calls can other
895    * thread or process change or revert permissions. So try to execute
896    * it more times.
897    */
898   for (retry = 0; retry < 10; retry++)
899     {
900       if (!grant_process_token_dacl_permissions(process, rights, &grant_token, &old_dacl, &security_descriptor))
901         return NULL;
902       if (!OpenProcessToken(process, rights, &token))
903         {
904           token = NULL;
905           error = GetLastError();
906         }
907       revert_token_dacl_permissions(grant_token, old_dacl, security_descriptor);
908       if (token)
909         return token;
910       else if (error != ERROR_ACCESS_DENIED)
911         return NULL;
912     }
913
914   return NULL;
915 }
916
917 /*
918  * Open primary access token of particular process handle with specified rights.
919  * If permissions for specified rights are missing then try to grant them.
920  */
921 static HANDLE
922 open_process_token_with_rights(HANDLE process, DWORD rights)
923 {
924   HANDLE old_token;
925   HANDLE token;
926
927   /* First try to open primary access token of process handle directly. */
928   if (OpenProcessToken(process, rights, &token))
929     return token;
930
931   /*
932    * If opening failed then it means that owner of the current thread
933    * access token does not have permission for it. Try it again with
934    * primary process access token.
935    */
936   if (change_token_to_primary(&old_token))
937     {
938       if (!OpenProcessToken(process, rights, &token))
939         token = NULL;
940       revert_to_token(old_token);
941       if (token)
942         return token;
943     }
944
945   /*
946    * If opening is still failing then try to grant specified permissions
947    * for the current thread and try to open it again.
948    */
949   token = try_grant_permissions_and_open_process_token(process, rights);
950   if (token)
951     return token;
952
953   /*
954    * And if it is still failing then try it again with granting
955    * permissions for the primary process token of the current process.
956    */
957   if (change_token_to_primary(&old_token))
958     {
959       token = try_grant_permissions_and_open_process_token(process, rights);
960       revert_to_token(old_token);
961       if (token)
962         return token;
963     }
964
965   /*
966    * TODO: Sorry, no other option for now...
967    * It could be possible to use Take Ownership Name privilege to
968    * temporary change token owner of specified process to the owner of
969    * the current thread token, grant permissions for current thread in
970    * that process token, change ownership back to original one, open
971    * that process token and revert granted permissions. But this is
972    * not implemented yet.
973    */
974   return NULL;
975 }
976
977 /*
978  * Set x86 I/O Privilege Level to 3 for the whole current NT process. Do it via
979  * NtSetInformationProcess() call with ProcessUserModeIOPL information class,
980  * which is supported by 32-bit Windows NT kernel versions and requires Tcb
981  * privilege.
982  */
983 static BOOL
984 SetProcessUserModeIOPL(VOID)
985 {
986   NtSetInformationProcessProt MyNtSetInformationProcess;
987
988   LUID luid_tcb_privilege;
989   LUID luid_impersonate_privilege;
990
991   HANDLE revert_token_tcb_privilege;
992   BOOL revert_only_tcb_privilege;
993
994   HANDLE revert_token_impersonate_privilege;
995   BOOL revert_only_impersonate_privilege;
996
997   BOOL impersonate_privilege_enabled;
998
999   BOOL revert_to_old_token;
1000   HANDLE old_token;
1001
1002   HANDLE lsass_process;
1003   HANDLE lsass_token;
1004
1005   UINT prev_error_mode;
1006   NTSTATUS nt_status;
1007   HMODULE ntdll;
1008   BOOL ret;
1009
1010   impersonate_privilege_enabled = FALSE;
1011   revert_to_old_token = FALSE;
1012   lsass_token = NULL;
1013   old_token = NULL;
1014
1015   /* Fast path when ProcessUserModeIOPL was already called. */
1016   if (read_iopl() == 3)
1017     return TRUE;
1018
1019   /*
1020    * Load ntdll.dll library with disabled critical-error-handler message box.
1021    * It means that NT kernel does not show unwanted GUI message box to user
1022    * when LoadLibrary() function fails.
1023    */
1024   prev_error_mode = change_error_mode(SEM_FAILCRITICALERRORS);
1025   ntdll = LoadLibrary(TEXT("ntdll.dll"));
1026   change_error_mode(prev_error_mode);
1027   if (!ntdll)
1028     goto err_not_implemented;
1029
1030   /* Retrieve pointer to NtSetInformationProcess() function. */
1031   MyNtSetInformationProcess = (NtSetInformationProcessProt)(LPVOID)GetProcAddress(ntdll, "NtSetInformationProcess");
1032   if (!MyNtSetInformationProcess)
1033     goto err_not_implemented;
1034
1035   /*
1036    * ProcessUserModeIOPL is syscall for NT kernel to change x86 IOPL
1037    * of the current running process to 3.
1038    *
1039    * Process handle argument for ProcessUserModeIOPL is ignored and
1040    * IOPL is always changed for the current running process. So pass
1041    * GetCurrentProcess() handle for documentation purpose. Process
1042    * information buffer and length are unused for ProcessUserModeIOPL.
1043    *
1044    * ProcessUserModeIOPL may success (return value >= 0) or may fail
1045    * because it is not implemented or because of missing privilege.
1046    * Other errors are not defined, so handle them as unknown.
1047    */
1048   nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1049   if (nt_status >= 0)
1050     goto verify;
1051   else if (nt_status == STATUS_NOT_IMPLEMENTED)
1052     goto err_not_implemented;
1053   else if (nt_status != STATUS_PRIVILEGE_NOT_HELD)
1054     goto err_unknown;
1055
1056   /*
1057    * If ProcessUserModeIOPL call failed with STATUS_PRIVILEGE_NOT_HELD
1058    * error then it means that the current thread token does not have
1059    * Tcb privilege enabled. Try to enable it.
1060    */
1061
1062   if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &luid_tcb_privilege))
1063     goto err_privilege_not_held;
1064
1065   /*
1066    * If the current thread has already Tcb privilege enabled then there
1067    * is some additional unhanded restriction.
1068    */
1069   if (have_privilege(luid_tcb_privilege))
1070     goto err_privilege_not_held;
1071
1072   /* Try to enable Tcb privilege and try ProcessUserModeIOPL call again. */
1073   if (enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
1074     {
1075       nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1076       revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
1077       if (nt_status >= 0)
1078         goto verify;
1079       else if (nt_status == STATUS_NOT_IMPLEMENTED)
1080         goto err_not_implemented;
1081       else if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1082         goto err_privilege_not_held;
1083       else
1084         goto err_unknown;
1085     }
1086
1087   /*
1088    * If enabling of Tcb privilege failed then it means that current thread
1089    * does not this privilege. But current process may have it. So try it
1090    * again with primary process access token.
1091    */
1092
1093   /*
1094    * If system supports Impersonate privilege (Windows 2000 SP4 or higher) then
1095    * all future actions in this function require this Impersonate privilege.
1096    * So try to enable it in case it is currently disabled.
1097    */
1098   if (LookupPrivilegeValue(NULL, SE_IMPERSONATE_NAME, &luid_impersonate_privilege) &&
1099       !have_privilege(luid_impersonate_privilege))
1100     {
1101       /*
1102        * If current thread does not have Impersonate privilege enabled
1103        * then first try to enable it just for the current thread. If
1104        * it is not possible to enable it just for the current thread
1105        * then try it to enable globally for whole process (which
1106        * affects all process threads). Both actions will be reverted
1107        * at the end of this function.
1108        */
1109       if (enable_privilege(luid_impersonate_privilege, &revert_token_impersonate_privilege, &revert_only_impersonate_privilege))
1110         {
1111           impersonate_privilege_enabled = TRUE;
1112         }
1113       else if (enable_privilege(luid_impersonate_privilege, NULL, NULL))
1114         {
1115           impersonate_privilege_enabled = TRUE;
1116           revert_token_impersonate_privilege = NULL;
1117           revert_only_impersonate_privilege = TRUE;
1118         }
1119       else
1120         {
1121           goto err_privilege_not_held;
1122         }
1123
1124       /*
1125        * Now when Impersonate privilege is enabled, try to enable Tcb
1126        * privilege again. Enabling other privileges for the current
1127        * thread requires Impersonate privilege, so enabling Tcb again
1128        * could now pass.
1129        */
1130       if (enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
1131         {
1132           nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1133           revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
1134           if (nt_status >= 0)
1135             goto verify;
1136           else if (nt_status == STATUS_NOT_IMPLEMENTED)
1137             goto err_not_implemented;
1138           else if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1139             goto err_privilege_not_held;
1140           else
1141             goto err_unknown;
1142         }
1143     }
1144
1145   /*
1146    * If enabling Tcb privilege failed then it means that the current
1147    * thread access token does not have this privilege or does not
1148    * have permission to adjust privileges.
1149    *
1150    * Try to use more privileged token from Local Security Authority
1151    * Subsystem Service process (lsass.exe) which has Tcb privilege.
1152    * Retrieving this more privileged token is possible for local
1153    * administrators (unless it was disabled by local administrators).
1154    */
1155
1156   lsass_process = find_and_open_process_for_query("lsass.exe");
1157   if (!lsass_process)
1158     goto err_privilege_not_held;
1159
1160   /*
1161    * Open primary lsass.exe process access token with query and duplicate
1162    * rights. Just these two rights are required for impersonating other
1163    * primary process token (impersonate right is really not required!).
1164    */
1165   lsass_token = open_process_token_with_rights(lsass_process, TOKEN_QUERY | TOKEN_DUPLICATE);
1166
1167   CloseHandle(lsass_process);
1168
1169   if (!lsass_token)
1170     goto err_privilege_not_held;
1171
1172   /*
1173    * After successful open of the primary lsass.exe process access token,
1174    * assign its copy for the current thread.
1175    */
1176   if (!change_token(lsass_token, &old_token))
1177     goto err_privilege_not_held;
1178
1179   revert_to_old_token = TRUE;
1180
1181   nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1182   if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1183     {
1184       /*
1185        * Now current thread is not using primary process token anymore
1186        * but is using custom access token. There is no need to revert
1187        * enabled Tcb privilege as the whole custom access token would
1188        * be reverted. So there is no need to setup revert method for
1189        * enabling privilege.
1190        */
1191       if (have_privilege(luid_tcb_privilege) ||
1192           !enable_privilege(luid_tcb_privilege, NULL, NULL))
1193         goto err_privilege_not_held;
1194       nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1195     }
1196   if (nt_status >= 0)
1197     goto verify;
1198   else if (nt_status == STATUS_NOT_IMPLEMENTED)
1199     goto err_not_implemented;
1200   else if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1201     goto err_privilege_not_held;
1202   else
1203     goto err_unknown;
1204
1205 verify:
1206   /*
1207    * Some Windows NT kernel versions (e.g. Windows 2003 x64) do not
1208    * implement ProcessUserModeIOPL syscall at all but incorrectly
1209    * returns success when it is called by user process. So always
1210    * after this call verify that IOPL is set to 3.
1211    */
1212   if (read_iopl() != 3)
1213     goto err_not_implemented;
1214   ret = TRUE;
1215   goto ret;
1216
1217 err_not_implemented:
1218   SetLastError(ERROR_INVALID_FUNCTION);
1219   ret = FALSE;
1220   goto ret;
1221
1222 err_privilege_not_held:
1223   SetLastError(ERROR_PRIVILEGE_NOT_HELD);
1224   ret = FALSE;
1225   goto ret;
1226
1227 err_unknown:
1228   SetLastError(ERROR_GEN_FAILURE);
1229   ret = FALSE;
1230   goto ret;
1231
1232 ret:
1233   if (revert_to_old_token)
1234     revert_to_token(old_token);
1235
1236   if (impersonate_privilege_enabled)
1237     revert_privilege(luid_impersonate_privilege, revert_token_impersonate_privilege, revert_only_impersonate_privilege);
1238
1239   if (lsass_token)
1240     CloseHandle(lsass_token);
1241
1242   if (ntdll)
1243     FreeLibrary(ntdll);
1244
1245   return ret;
1246 }
1247
1248 static int
1249 intel_setup_io(struct pci_access *a)
1250 {
1251 #ifndef _WIN64
1252   /* 16/32-bit non-NT systems allow applications to access PCI I/O ports without any special setup. */
1253   OSVERSIONINFOA version;
1254   version.dwOSVersionInfoSize = sizeof(version);
1255   if (GetVersionExA(&version) && version.dwPlatformId < VER_PLATFORM_WIN32_NT)
1256     {
1257       a->debug("Detected 16/32-bit non-NT system, skipping NT setup...");
1258       return 1;
1259     }
1260 #endif
1261
1262   /* On NT-based systems issue ProcessUserModeIOPL syscall which changes IOPL to 3. */
1263   if (!SetProcessUserModeIOPL())
1264     {
1265       DWORD error = GetLastError();
1266       a->debug("NT ProcessUserModeIOPL call failed: %s.", error == ERROR_INVALID_FUNCTION ? "Not Implemented" : error == ERROR_PRIVILEGE_NOT_HELD ? "Access Denied" : "Operation Failed");
1267       return 0;
1268     }
1269
1270   a->debug("NT ProcessUserModeIOPL call succeeded...");
1271   return 1;
1272 }
1273
1274 static inline void
1275 intel_cleanup_io(struct pci_access *a UNUSED)
1276 {
1277   /*
1278    * 16/32-bit non-NT systems do not use any special setup and on NT-based
1279    * systems ProcessUserModeIOPL permanently changes IOPL to 3 for the current
1280    * NT process, no revert for current process is possible.
1281    */
1282 }
1283
1284 static inline void intel_io_lock(void)
1285 {
1286 }
1287
1288 static inline void intel_io_unlock(void)
1289 {
1290 }