*/
#include <windows.h>
-#include <aclapi.h>
+
#include <stdio.h> /* for sprintf() */
#include "win32-helpers.h"
#define PROCESS_QUERY_LIMITED_INFORMATION 0x1000
#endif
+/* Unfortunately some toolchains do not provide this constant. */
+#ifndef SE_IMPERSONATE_NAME
+#define SE_IMPERSONATE_NAME TEXT("SeImpersonatePrivilege")
+#endif
+
+/* Unfortunately some toolchains do not provide these constants. */
+#ifndef SE_DACL_AUTO_INHERIT_REQ
+#define SE_DACL_AUTO_INHERIT_REQ 0x0100
+#endif
+#ifndef SE_SACL_AUTO_INHERIT_REQ
+#define SE_SACL_AUTO_INHERIT_REQ 0x0200
+#endif
+#ifndef SE_DACL_AUTO_INHERITED
+#define SE_DACL_AUTO_INHERITED 0x0400
+#endif
+#ifndef SE_SACL_AUTO_INHERITED
+#define SE_SACL_AUTO_INHERITED 0x0800
+#endif
+
/*
* These psapi functions are available in kernel32.dll library with K32 prefix
* on Windows 7 and higher systems. On older Windows systems these functions are
typedef DWORD (WINAPI *GetModuleFileNameExWProt)(HANDLE hProcess, HMODULE hModule, LPWSTR lpImageFileName, DWORD nSize);
/*
- * These aclapi functions are available in advapi.dll library on Windows NT 4.0
+ * These aclapi function is available in advapi.dll library on Windows 2000
* and higher systems.
*/
-typedef DWORD (WINAPI *GetSecurityInfoProt)(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID *ppsidOwner, PSID *ppsidGroup, PACL *ppDacl, PACL *ppSacl, PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
-typedef DWORD (WINAPI *SetSecurityInfoProt)(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID psidOwner, PSID psidGroup, PACL pDacl, PACL pSacl);
-typedef DWORD (WINAPI *SetEntriesInAclProt)(ULONG cCountOfExplicitEntries, PEXPLICIT_ACCESS pListOfExplicitEntries, PACL OldAcl, PACL *NewAcl);
+typedef BOOL (WINAPI *SetSecurityDescriptorControlProt)(PSECURITY_DESCRIPTOR pSecurityDescriptor, SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet);
/*
* This errhandlingapi function is available in kernel32.dll library on
typedef BOOL (WINAPI *SetThreadErrorModeProt)(DWORD dwNewMode, LPDWORD lpOldMode);
+static DWORD
+format_message_from_system(DWORD win32_error_id, DWORD lang_id, LPSTR buffer, DWORD size)
+{
+ return FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, win32_error_id, lang_id, buffer, size, NULL);
+}
+
const char *
win32_strerror(DWORD win32_error_id)
{
static char buffer[4096];
DWORD len;
- len = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, win32_error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL);
+ /*
+ * If it is possible show error messages in US English language.
+ * International Windows editions do not have to provide error
+ * messages in English language, so fallback to the language
+ * which system provides (neutral).
+ */
+ len = format_message_from_system(win32_error_id, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), buffer, sizeof(buffer));
+ if (!len)
+ len = format_message_from_system(win32_error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer));
/* FormatMessage() automatically appends ".\r\n" to the error message. */
if (len && buffer[len-1] == '\n')
}
/*
- * Grant particular permissions in the primary access token of the specified
- * process for the owner of current thread token and set old DACL of the
- * process access token for reverting permissions. Security descriptor is
- * just memory buffer for old DACL.
+ * Create a new security descriptor in absolute form from relative form.
+ * Newly created security descriptor in absolute form is stored in linear buffer.
+ */
+static PSECURITY_DESCRIPTOR
+create_relsd_from_abssd(PSECURITY_DESCRIPTOR rel_security_descriptor)
+{
+ PBYTE abs_security_descriptor_buffer;
+ DWORD abs_security_descriptor_size=0, abs_dacl_size=0, abs_sacl_size=0, abs_owner_size=0, abs_primary_group_size=0;
+
+ if (!MakeAbsoluteSD(rel_security_descriptor,
+ NULL, &abs_security_descriptor_size,
+ NULL, &abs_dacl_size,
+ NULL, &abs_sacl_size,
+ NULL, &abs_owner_size,
+ NULL, &abs_primary_group_size) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ return NULL;
+
+ abs_security_descriptor_buffer = (PBYTE)LocalAlloc(LPTR, abs_security_descriptor_size+abs_dacl_size+abs_sacl_size+abs_owner_size+abs_primary_group_size);
+ if (!abs_security_descriptor_buffer)
+ return NULL;
+
+ if (!MakeAbsoluteSD(rel_security_descriptor,
+ (PSECURITY_DESCRIPTOR)abs_security_descriptor_buffer, &abs_security_descriptor_size,
+ (PACL)(abs_security_descriptor_buffer+abs_security_descriptor_size), &abs_dacl_size,
+ (PACL)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size), &abs_sacl_size,
+ (PSID)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size+abs_sacl_size), &abs_owner_size,
+ (PSID)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size+abs_sacl_size+abs_owner_size), &abs_primary_group_size))
+ return NULL;
+
+ return (PSECURITY_DESCRIPTOR)abs_security_descriptor_buffer;
+}
+
+/*
+ * Prepare security descriptor obtained by GetKernelObjectSecurity() so it can be
+ * passed to SetKernelObjectSecurity() as identity operation. It modifies control
+ * flags of security descriptor, which is needed for Windows 2000 and new.
*/
static BOOL
-grant_process_token_dacl_permissions(HANDLE process, DWORD permissions, HANDLE *token, PACL *old_dacl, PSECURITY_DESCRIPTOR *security_descriptor)
+prepare_security_descriptor_for_set_operation(PSECURITY_DESCRIPTOR security_descriptor)
{
- GetSecurityInfoProt MyGetSecurityInfo;
- SetSecurityInfoProt MySetSecurityInfo;
- SetEntriesInAclProt MySetEntriesInAcl;
- EXPLICIT_ACCESS explicit_access;
- TOKEN_OWNER *owner;
+ SetSecurityDescriptorControlProt MySetSecurityDescriptorControl;
+ SECURITY_DESCRIPTOR_CONTROL bits_mask;
+ SECURITY_DESCRIPTOR_CONTROL bits_set;
+ SECURITY_DESCRIPTOR_CONTROL control;
+ OSVERSIONINFO version;
HMODULE advapi32;
- PACL new_dacl;
+ DWORD revision;
/*
- * This source file already uses advapi32.dll library, so it is
- * linked to executable and automatically loaded when starting
- * current running process.
+ * SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED are flags introduced in
+ * Windows 2000 to control client-side automatic inheritance (client - user
+ * process - is responsible for propagating inherited ACEs to subobjects).
+ * To prevent applications which do not understand client-side automatic
+ * inheritance (applications created prior Windows 2000 or which use low
+ * level API like SetKernelObjectSecurity()) to unintentionally set those
+ * SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED control flags when
+ * coping them from other security descriptor.
+ *
+ * As we are not modifying existing ACEs, we are compatible with Windows 2000
+ * client-side automatic inheritance model and therefore prepare security
+ * descriptor for SetKernelObjectSecurity() to not clear existing automatic
+ * inheritance control flags.
+ *
+ * Control flags SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED are set
+ * into security object only when they are set together with set-only flags
+ * SE_DACL_AUTO_INHERIT_REQ and SE_SACL_AUTO_INHERIT_REQ. Those flags are
+ * never received by GetKernelObjectSecurity() and are just commands for
+ * SetKernelObjectSecurity() how to interpret SE_DACL_AUTO_INHERITED and
+ * SE_SACL_AUTO_INHERITED flags.
+ *
+ * Function symbol SetSecurityDescriptorControl is not available in the
+ * older versions of advapi32.dll library, so resolve it at runtime.
*/
+
+ version.dwOSVersionInfoSize = sizeof(version);
+ if (!GetVersionEx(&version) ||
+ version.dwPlatformId != VER_PLATFORM_WIN32_NT ||
+ version.dwMajorVersion < 5)
+ return TRUE;
+
+ if (!GetSecurityDescriptorControl(security_descriptor, &control, &revision))
+ return FALSE;
+
+ bits_mask = 0;
+ bits_set = 0;
+
+ if (control & SE_DACL_AUTO_INHERITED)
+ {
+ bits_mask |= SE_DACL_AUTO_INHERIT_REQ;
+ bits_set |= SE_DACL_AUTO_INHERIT_REQ;
+ }
+
+ if (control & SE_SACL_AUTO_INHERITED)
+ {
+ bits_mask |= SE_SACL_AUTO_INHERIT_REQ;
+ bits_set |= SE_SACL_AUTO_INHERIT_REQ;
+ }
+
+ if (!bits_mask)
+ return TRUE;
+
advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
if (!advapi32)
return FALSE;
- /*
- * It does not matter if SetEntriesInAclA() or SetEntriesInAclW() is
- * called as no string is passed to SetEntriesInAcl function.
- */
- MyGetSecurityInfo = (GetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "GetSecurityInfo");
- MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
- MySetEntriesInAcl = (SetEntriesInAclProt)(LPVOID)GetProcAddress(advapi32, "SetEntriesInAclA");
- if (!MyGetSecurityInfo || !MySetSecurityInfo || !MySetEntriesInAcl)
+ MySetSecurityDescriptorControl = (SetSecurityDescriptorControlProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityDescriptorControl");
+ if (!MySetSecurityDescriptorControl)
+ return FALSE;
+
+ if (!MySetSecurityDescriptorControl(security_descriptor, bits_mask, bits_set))
return FALSE;
+ return TRUE;
+}
+
+/*
+ * Grant particular permissions in the primary access token of the specified
+ * process for the owner of current thread token and set old DACL of the
+ * process access token for reverting permissions. Security descriptor is
+ * just memory buffer for old DACL.
+ */
+static BOOL
+grant_process_token_dacl_permissions(HANDLE process, DWORD permissions, HANDLE *token, PSECURITY_DESCRIPTOR *old_security_descriptor)
+{
+ TOKEN_OWNER *owner;
+ PACL old_dacl;
+ BOOL old_dacl_present;
+ BOOL old_dacl_defaulted;
+ PACL new_dacl;
+ WORD new_dacl_size;
+ PSECURITY_DESCRIPTOR new_security_descriptor;
+ DWORD length;
+
owner = get_current_token_owner();
if (!owner)
return FALSE;
return FALSE;
}
- if (MyGetSecurityInfo(*token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL, security_descriptor) != ERROR_SUCCESS)
+ if (!GetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, NULL, 0, &length) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ {
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+retry:
+ *old_security_descriptor = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, length);
+ if (!*old_security_descriptor)
+ {
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+ if (!GetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, *old_security_descriptor, length, &length))
+ {
+ /*
+ * Length of the security descriptor between two get calls
+ * may changes (e.g. by another thread of process), so retry.
+ */
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ LocalFree(*old_security_descriptor);
+ goto retry;
+ }
+ LocalFree(*old_security_descriptor);
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+ if (!prepare_security_descriptor_for_set_operation(*old_security_descriptor))
+ {
+ LocalFree(*old_security_descriptor);
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+ /* Retrieve the current DACL from security descriptor including present and defaulted properties. */
+ if (!GetSecurityDescriptorDacl(*old_security_descriptor, &old_dacl_present, &old_dacl, &old_dacl_defaulted))
{
+ LocalFree(*old_security_descriptor);
LocalFree(owner);
CloseHandle(*token);
return FALSE;
}
/*
+ * If DACL is not present then system grants full access to everyone. It this
+ * case do not modify DACL as it just adds one ACL allow rule for us, which
+ * automatically disallow access to anybody else which had access before.
+ */
+ if (!old_dacl_present || !old_dacl)
+ {
+ LocalFree(*old_security_descriptor);
+ LocalFree(owner);
+ *old_security_descriptor = NULL;
+ return TRUE;
+ }
+
+ /* Create new DACL which would be copy of the current old one. */
+ new_dacl_size = old_dacl->AclSize + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(owner->Owner) - sizeof(DWORD);
+ new_dacl = (PACL)LocalAlloc(LPTR, new_dacl_size);
+ if (!new_dacl)
+ {
+ LocalFree(*old_security_descriptor);
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
+ /*
+ * Initialize new DACL structure to the same format as was the old one.
* Set new explicit access for the owner of the current thread access
* token with non-inherited granting access to specified permissions.
+ * This permission is added in the first ACE, so has the highest priority.
*/
- explicit_access.grfAccessPermissions = permissions;
- explicit_access.grfAccessMode = GRANT_ACCESS;
- explicit_access.grfInheritance = NO_PROPAGATE_INHERIT_ACE;
- explicit_access.Trustee.pMultipleTrustee = NULL;
- explicit_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
- explicit_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
- explicit_access.Trustee.TrusteeType = TRUSTEE_IS_USER;
+ if (!InitializeAcl(new_dacl, new_dacl_size, old_dacl->AclRevision) ||
+ !AddAccessAllowedAce(new_dacl, ACL_REVISION2, permissions, owner->Owner))
+ {
+ LocalFree(new_dacl);
+ LocalFree(*old_security_descriptor);
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+
/*
- * Unfortunately i586-mingw32msvc toolchain does not have pSid pointer
- * member in Trustee union. So assign owner SID to ptstrName pointer
- * member which aliases with pSid pointer member in the same union.
+ * Now (after setting our new permissions) append all ACE entries from the
+ * old DACL to the new DACL, which preserve all other existing permissions.
*/
- explicit_access.Trustee.ptstrName = (PVOID)owner->Owner;
+ if (old_dacl->AceCount > 0)
+ {
+ WORD ace_index;
+ LPVOID ace;
+
+ for (ace_index = 0; ace_index < old_dacl->AceCount; ace_index++)
+ {
+ if (!GetAce(old_dacl, ace_index, &ace) ||
+ !AddAce(new_dacl, old_dacl->AclRevision, MAXDWORD, ace, ((PACE_HEADER)ace)->AceSize))
+ {
+ LocalFree(new_dacl);
+ LocalFree(*old_security_descriptor);
+ LocalFree(owner);
+ CloseHandle(*token);
+ return FALSE;
+ }
+ }
+ }
- if (MySetEntriesInAcl(1, &explicit_access, *old_dacl, &new_dacl) != ERROR_SUCCESS)
+ /*
+ * Create copy of the old security descriptor, so we can modify its DACL.
+ * Function SetSecurityDescriptorDacl() works only with security descriptors
+ * in absolute format. So use our helper function create_relsd_from_abssd()
+ * for converting security descriptor from relative format (which is returned
+ * by GetKernelObjectSecurity() function) to the absolute format.
+ */
+ new_security_descriptor = create_relsd_from_abssd(*old_security_descriptor);
+ if (!new_security_descriptor)
{
- LocalFree(*security_descriptor);
+ LocalFree(new_dacl);
+ LocalFree(*old_security_descriptor);
LocalFree(owner);
CloseHandle(*token);
return FALSE;
}
- if (MySetSecurityInfo(*token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, new_dacl, NULL) != ERROR_SUCCESS)
+ /*
+ * In the new security descriptor replace old DACL by the new DACL (which has
+ * new permissions) and then set this new security descriptor to the token,
+ * so token would have new access permissions.
+ */
+ if (!SetSecurityDescriptorDacl(new_security_descriptor, TRUE, new_dacl, FALSE) ||
+ !SetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, new_security_descriptor))
{
+ LocalFree(new_security_descriptor);
LocalFree(new_dacl);
- LocalFree(*security_descriptor);
+ LocalFree(*old_security_descriptor);
LocalFree(owner);
CloseHandle(*token);
return FALSE;
}
+ LocalFree(new_security_descriptor);
LocalFree(new_dacl);
LocalFree(owner);
return TRUE;
* grant_process_token_dacl_permissions() call.
*/
static VOID
-revert_token_dacl_permissions(HANDLE token, PACL old_dacl, PSECURITY_DESCRIPTOR security_descriptor)
+revert_token_dacl_permissions(HANDLE token, PSECURITY_DESCRIPTOR old_security_descriptor)
{
- SetSecurityInfoProt MySetSecurityInfo;
- HMODULE advapi32;
-
- /*
- * This source file already uses advapi32.dll library, so it is
- * linked to executable and automatically loaded when starting
- * current running process.
- */
- advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
- if (advapi32)
- {
- MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
- MySetSecurityInfo(token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL);
- }
-
- LocalFree(security_descriptor);
+ SetKernelObjectSecurity(token, DACL_SECURITY_INFORMATION, old_security_descriptor);
+ LocalFree(old_security_descriptor);
CloseHandle(token);
}
static HANDLE
try_grant_permissions_and_open_process_token(HANDLE process, DWORD rights)
{
- PSECURITY_DESCRIPTOR security_descriptor;
+ PSECURITY_DESCRIPTOR old_security_descriptor;
HANDLE grant_token;
- PACL old_dacl;
HANDLE token;
DWORD retry;
DWORD error;
*/
for (retry = 0; retry < 10; retry++)
{
- if (!grant_process_token_dacl_permissions(process, rights, &grant_token, &old_dacl, &security_descriptor))
+ if (!grant_process_token_dacl_permissions(process, rights, &grant_token, &old_security_descriptor))
return NULL;
if (!OpenProcessToken(process, rights, &token))
{
token = NULL;
error = GetLastError();
}
- revert_token_dacl_permissions(grant_token, old_dacl, security_descriptor);
+ if (old_security_descriptor)
+ revert_token_dacl_permissions(grant_token, old_security_descriptor);
if (token)
return token;
else if (error != ERROR_ACCESS_DENIED)
*/
return NULL;
}
+
+/*
+ * Call supplied function with its argument and if it fails with
+ * ERROR_PRIVILEGE_NOT_HELD then try to enable Tcb privilege and
+ * call function with its argument again.
+ */
+BOOL
+win32_call_func_with_tcb_privilege(BOOL (*function)(LPVOID), LPVOID argument)
+{
+ LUID luid_tcb_privilege;
+ LUID luid_impersonate_privilege;
+
+ HANDLE revert_token_tcb_privilege;
+ BOOL revert_only_tcb_privilege;
+
+ HANDLE revert_token_impersonate_privilege;
+ BOOL revert_only_impersonate_privilege;
+
+ BOOL impersonate_privilege_enabled;
+
+ BOOL revert_to_old_token;
+ HANDLE old_token;
+
+ HANDLE lsass_process;
+ HANDLE lsass_token;
+
+ BOOL ret;
+
+ impersonate_privilege_enabled = FALSE;
+ revert_to_old_token = FALSE;
+ lsass_token = NULL;
+ old_token = NULL;
+
+ /* Call supplied function. */
+ ret = function(argument);
+ if (ret || GetLastError() != ERROR_PRIVILEGE_NOT_HELD)
+ goto ret;
+
+ /*
+ * If function call failed with ERROR_PRIVILEGE_NOT_HELD
+ * error then it means that the current thread token does not have
+ * Tcb privilege enabled. Try to enable it.
+ */
+
+ if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &luid_tcb_privilege))
+ goto err_privilege_not_held;
+
+ /*
+ * If the current thread has already Tcb privilege enabled then there
+ * is some additional unhanded restriction.
+ */
+ if (win32_have_privilege(luid_tcb_privilege))
+ goto err_privilege_not_held;
+
+ /* Try to enable Tcb privilege and try function call again. */
+ if (win32_enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
+ {
+ ret = function(argument);
+ win32_revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
+ goto ret;
+ }
+
+ /*
+ * If enabling of Tcb privilege failed then it means that current thread
+ * does not have this privilege. But current process may have it. So try it
+ * again with primary process access token.
+ */
+
+ /*
+ * If system supports Impersonate privilege (Windows 2000 SP4 or higher) then
+ * all future actions in this function require this Impersonate privilege.
+ * So try to enable it in case it is currently disabled.
+ */
+ if (LookupPrivilegeValue(NULL, SE_IMPERSONATE_NAME, &luid_impersonate_privilege) &&
+ !win32_have_privilege(luid_impersonate_privilege))
+ {
+ /*
+ * If current thread does not have Impersonate privilege enabled
+ * then first try to enable it just for the current thread. If
+ * it is not possible to enable it just for the current thread
+ * then try it to enable globally for whole process (which
+ * affects all process threads). Both actions will be reverted
+ * at the end of this function.
+ */
+ if (win32_enable_privilege(luid_impersonate_privilege, &revert_token_impersonate_privilege, &revert_only_impersonate_privilege))
+ {
+ impersonate_privilege_enabled = TRUE;
+ }
+ else if (win32_enable_privilege(luid_impersonate_privilege, NULL, NULL))
+ {
+ impersonate_privilege_enabled = TRUE;
+ revert_token_impersonate_privilege = NULL;
+ revert_only_impersonate_privilege = TRUE;
+ }
+ else
+ {
+ goto err_privilege_not_held;
+ }
+
+ /*
+ * Now when Impersonate privilege is enabled, try to enable Tcb
+ * privilege again. Enabling other privileges for the current
+ * thread requires Impersonate privilege, so enabling Tcb again
+ * could now pass.
+ */
+ if (win32_enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
+ {
+ ret = function(argument);
+ win32_revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
+ goto ret;
+ }
+ }
+
+ /*
+ * If enabling Tcb privilege failed then it means that the current
+ * thread access token does not have this privilege or does not
+ * have permission to adjust privileges.
+ *
+ * Try to use more privileged token from Local Security Authority
+ * Subsystem Service process (lsass.exe) which has Tcb privilege.
+ * Retrieving this more privileged token is possible for local
+ * administrators (unless it was disabled by local administrators).
+ */
+
+ lsass_process = win32_find_and_open_process_for_query("lsass.exe");
+ if (!lsass_process)
+ goto err_privilege_not_held;
+
+ /*
+ * Open primary lsass.exe process access token with query and duplicate
+ * rights. Just these two rights are required for impersonating other
+ * primary process token (impersonate right is really not required!).
+ */
+ lsass_token = win32_open_process_token_with_rights(lsass_process, TOKEN_QUERY | TOKEN_DUPLICATE);
+
+ CloseHandle(lsass_process);
+
+ if (!lsass_token)
+ goto err_privilege_not_held;
+
+ /*
+ * After successful open of the primary lsass.exe process access token,
+ * assign its copy for the current thread.
+ */
+ if (!win32_change_token(lsass_token, &old_token))
+ goto err_privilege_not_held;
+
+ revert_to_old_token = TRUE;
+
+ ret = function(argument);
+ if (ret || GetLastError() != ERROR_PRIVILEGE_NOT_HELD)
+ goto ret;
+
+ /*
+ * Now current thread is not using primary process token anymore
+ * but is using custom access token. There is no need to revert
+ * enabled Tcb privilege as the whole custom access token would
+ * be reverted. So there is no need to setup revert method for
+ * enabling privilege.
+ */
+ if (win32_have_privilege(luid_tcb_privilege) ||
+ !win32_enable_privilege(luid_tcb_privilege, NULL, NULL))
+ goto err_privilege_not_held;
+
+ ret = function(argument);
+ goto ret;
+
+err_privilege_not_held:
+ SetLastError(ERROR_PRIVILEGE_NOT_HELD);
+ ret = FALSE;
+ goto ret;
+
+ret:
+ if (revert_to_old_token)
+ win32_revert_to_token(old_token);
+
+ if (impersonate_privilege_enabled)
+ win32_revert_privilege(luid_impersonate_privilege, revert_token_impersonate_privilege, revert_only_impersonate_privilege);
+
+ if (lsass_token)
+ CloseHandle(lsass_token);
+
+ return ret;
+}