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