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