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