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