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