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