https://git.reactos.org/?p=reactos.git;a=commitdiff;h=f340524ea4d103221a7fd…
commit f340524ea4d103221a7fd2eaca51787ea02461c7
Author: George Bișoc <george.bisoc(a)reactos.org>
AuthorDate: Mon Feb 21 11:12:48 2022 +0100
Commit: George Bișoc <george.bisoc(a)reactos.org>
CommitDate: Fri May 6 10:09:51 2022 +0200
[SERVICES] Grant ReactOS Setup component SYSTEM access
ReactOS Setup is an integral component that is part of the operating system responsible for the installation of ROS during 2nd installation stage. The situation with current master branch is like this -- the Services component always tries to create the process
on behalf of the logged in user with its own security context. That user doesn't have the privileges and access rights like SYSTEM thus the Services component tries to create the process but it fails to do so because of lacking of required access right, TOKEN_DUPLICATE, in order for the calling thread to impersonate as self.
---
base/system/services/database.c | 2 +-
base/system/services/services.c | 24 ++++++++++++++++++++++++
base/system/services/services.h | 1 +
3 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/base/system/services/database.c b/base/system/services/database.c
index d773a44e102..8dbf148d43a 100644
--- a/base/system/services/database.c
+++ b/base/system/services/database.c
@@ -370,7 +370,7 @@ ScmLogonService(
DPRINT("ScmLogonService(%p %p)\n", pService, pImage);
DPRINT("Service %S\n", pService->lpServiceName);
- if (ScmIsLocalSystemAccount(pImage->pszAccountName) || ScmLiveSetup)
+ if (ScmIsLocalSystemAccount(pImage->pszAccountName) || ScmLiveSetup || ScmSetupInProgress)
return ERROR_SUCCESS;
/* Get the user and domain names */
diff --git a/base/system/services/services.c b/base/system/services/services.c
index b45ac07b745..fec54827b87 100644
--- a/base/system/services/services.c
+++ b/base/system/services/services.c
@@ -28,6 +28,7 @@ int WINAPI RegisterServicesProcess(DWORD ServicesProcessId);
BOOL ScmInitialize = FALSE;
BOOL ScmShutdown = FALSE;
BOOL ScmLiveSetup = FALSE;
+BOOL ScmSetupInProgress = FALSE;
static HANDLE hScmShutdownEvent = NULL;
static HANDLE hScmSecurityServicesEvent = NULL;
@@ -55,6 +56,7 @@ CheckForLiveCD(VOID)
WCHAR CommandLine[MAX_PATH];
HKEY hSetupKey;
DWORD dwSetupType;
+ DWORD dwSetupInProgress;
DWORD dwType;
DWORD dwSize;
DWORD dwError;
@@ -107,6 +109,28 @@ CheckForLiveCD(VOID)
ScmLiveSetup = TRUE;
}
+ /* Read the SystemSetupInProgress value */
+ dwSize = sizeof(DWORD);
+ dwError = RegQueryValueExW(hSetupKey,
+ L"SystemSetupInProgress",
+ NULL,
+ &dwType,
+ (LPBYTE)&dwSetupInProgress,
+ &dwSize);
+ if (dwError != ERROR_SUCCESS ||
+ dwType != REG_DWORD ||
+ dwSize != sizeof(DWORD) ||
+ dwSetupType == 0)
+ {
+ goto done;
+ }
+
+ if (dwSetupInProgress == 1)
+ {
+ DPRINT1("ReactOS Setup currently in progress!\n");
+ ScmSetupInProgress = TRUE;
+ }
+
done:
RegCloseKey(hSetupKey);
diff --git a/base/system/services/services.h b/base/system/services/services.h
index cbaa4a93329..204375c17c6 100644
--- a/base/system/services/services.h
+++ b/base/system/services/services.h
@@ -102,6 +102,7 @@ extern LIST_ENTRY ImageListHead;
extern BOOL ScmInitialize;
extern BOOL ScmShutdown;
extern BOOL ScmLiveSetup;
+extern BOOL ScmSetupInProgress;
extern PSECURITY_DESCRIPTOR pPipeSD;
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=d862fa6fc89b8864ce16a…
commit d862fa6fc89b8864ce16a5a44c96fabb15621d09
Author: George Bișoc <george.bisoc(a)reactos.org>
AuthorDate: Sat Mar 26 22:36:03 2022 +0100
Commit: George Bișoc <george.bisoc(a)reactos.org>
CommitDate: Fri May 6 10:09:50 2022 +0200
[ADVAPI32] Implement security descriptor management in CreateProcessAsUserCommon internal function
Currently CreateProcessAsUserCommon doesn't set a default descriptor for the newly duplicated token object for the new process nor it sets any security information for both the process and thread. This is wrong, because when the process is created on behalf of the user's security context,
it still uses the previous security information of the creator that initially gave birth to the process. CreateDefaultProcessSecurityCommon function will serve as a placeholder until CreatePrivateObjectSecurity is implemented.
---
dll/win32/advapi32/misc/logon.c | 433 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 431 insertions(+), 2 deletions(-)
diff --git a/dll/win32/advapi32/misc/logon.c b/dll/win32/advapi32/misc/logon.c
index 6687a9f7e93..66abfc199f7 100644
--- a/dll/win32/advapi32/misc/logon.c
+++ b/dll/win32/advapi32/misc/logon.c
@@ -89,6 +89,346 @@ CloseLogonLsaHandle(VOID)
}
+/**
+ * @brief
+ * Creates a default security descriptor that is going
+ * to be used by both the newly created process and thread
+ * by a call to CreateProcessAsUserA/W. This descriptor also
+ * serves for the newly duplicated token object that is going
+ * to be set for the token which acts as the main user.
+ *
+ * @param[in] TokenHandle
+ * A handle to a token. The function will use this token to
+ * query security details such as the owner and primary group
+ * associated with the security context of this token. The
+ * obtained information will then be assigned to the security
+ * descriptor.
+ *
+ * @param[out] Sd
+ * A pointer to an allocated security descriptor that is given
+ * to the caller.
+ *
+ * @return
+ * Return TRUE if the security descriptor has been successfully
+ * created, FALSE otherwise.
+ *
+ * @remarks
+ * When a process is created on behald of the user's security context
+ * this user will be the owner and responsible for that process. Whatever
+ * objects created or stuff done within the process space is at the
+ * discretion of the user, that is, further objects created are in
+ * charge by the user himself as is the owner of the process.
+ *
+ * !!!NOTE!!! -- On Windows the security descriptor is created by using
+ * CreatePrivateObjectSecurity(Ex) API call. Whilst the way the security
+ * descriptor is created in our end is not wrong per se, this function
+ * serves a placeholder until CreatePrivateObjectSecurity is implemented.
+ */
+static
+BOOL
+CreateDefaultProcessSecurityCommon(
+ _In_ HANDLE TokenHandle,
+ _Out_ PSECURITY_DESCRIPTOR *Sd)
+{
+ NTSTATUS Status;
+ BOOL Success;
+ PACL Dacl;
+ PTOKEN_OWNER OwnerOfToken;
+ PTOKEN_PRIMARY_GROUP PrimaryGroupOfToken;
+ SECURITY_DESCRIPTOR AbsoluteSd;
+ ULONG DaclSize, TokenOwnerSize, PrimaryGroupSize, RelativeSDSize = 0;
+ PSID OwnerSid = NULL, SystemSid = NULL, PrimaryGroupSid = NULL;
+ PSECURITY_DESCRIPTOR RelativeSD = NULL;
+ static SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
+
+ /*
+ * Since we do not know how much space
+ * is needed to allocate the buffer to
+ * hold the token owner, first we must
+ * query the exact size.
+ */
+ Status = NtQueryInformationToken(TokenHandle,
+ TokenOwner,
+ NULL,
+ 0,
+ &TokenOwnerSize);
+ if (Status != STATUS_BUFFER_TOO_SMALL)
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Unexpected status code returned, must be STATUS_BUFFER_TOO_SMALL (Status 0x%08lx)\n", Status);
+ return FALSE;
+ }
+
+ /* We have the required space size, allocate the buffer now */
+ OwnerOfToken = RtlAllocateHeap(RtlGetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ TokenOwnerSize);
+ if (OwnerOfToken == NULL)
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Failed to allocate buffer for token owner!\n");
+ return FALSE;
+ }
+
+ /* Now query the token owner */
+ Status = NtQueryInformationToken(TokenHandle,
+ TokenOwner,
+ OwnerOfToken,
+ TokenOwnerSize,
+ &TokenOwnerSize);
+ if (!NT_SUCCESS(Status))
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Failed to query the token owner (Status 0x%08lx)\n", Status);
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /* Do the same process but for the primary group now */
+ Status = NtQueryInformationToken(TokenHandle,
+ TokenPrimaryGroup,
+ NULL,
+ 0,
+ &PrimaryGroupSize);
+ if (Status != STATUS_BUFFER_TOO_SMALL)
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Unexpected status code returned, must be STATUS_BUFFER_TOO_SMALL (Status 0x%08lx)\n", Status);
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /* Allocate the buffer */
+ PrimaryGroupOfToken = RtlAllocateHeap(RtlGetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ PrimaryGroupSize);
+ if (PrimaryGroupOfToken == NULL)
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Failed to allocate buffer for primary group token!\n");
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /* Query the primary group now */
+ Status = NtQueryInformationToken(TokenHandle,
+ TokenPrimaryGroup,
+ PrimaryGroupOfToken,
+ PrimaryGroupSize,
+ &PrimaryGroupSize);
+ if (!NT_SUCCESS(Status))
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Failed to query the token owner (Status 0x%08lx)\n", Status);
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /* Create the SYSTEM SID */
+ if (!AllocateAndInitializeSid(&NtAuthority,
+ 1,
+ SECURITY_LOCAL_SYSTEM_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &SystemSid))
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Failed to create Local System SID (error code %d)\n", GetLastError());
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /* Cache the token owner and primary group SID */
+ OwnerSid = OwnerOfToken->Owner;
+ PrimaryGroupSid = PrimaryGroupOfToken->PrimaryGroup;
+
+ /* Set up the DACL size */
+ DaclSize = sizeof(ACL) +
+ sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(OwnerSid) +
+ sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(SystemSid);
+
+ /* Allocate buffer for the DACL */
+ Dacl = RtlAllocateHeap(RtlGetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ DaclSize);
+ if (Dacl == NULL)
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Failed to allocate buffer for DACL!\n");
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /* Initialize the DACL */
+ if (!InitializeAcl(Dacl, DaclSize, ACL_REVISION))
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Failed to initialize DACL (error code %d)\n", GetLastError());
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /* Give full powers to the owner */
+ if (!AddAccessAllowedAce(Dacl,
+ ACL_REVISION,
+ GENERIC_ALL,
+ OwnerSid))
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Failed to set up ACE for owner (error code %d)\n", GetLastError());
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /* Give full powers to SYSTEM as well */
+ if (!AddAccessAllowedAce(Dacl,
+ ACL_REVISION,
+ GENERIC_ALL,
+ SystemSid))
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Failed to set up ACE for SYSTEM (error code %d)\n", GetLastError());
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /* Initialize the descriptor in absolute format */
+ if (!InitializeSecurityDescriptor(&AbsoluteSd, SECURITY_DESCRIPTOR_REVISION))
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Failed to initialize absolute security descriptor (error code %d)\n", GetLastError());
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /* Set the DACL to the security descriptor */
+ if (!SetSecurityDescriptorDacl(&AbsoluteSd, TRUE, Dacl, FALSE))
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Failed to set up DACL to absolute security descriptor (error code %d)\n", GetLastError());
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /* Set the owner for this descriptor */
+ if (!SetSecurityDescriptorOwner(&AbsoluteSd, OwnerSid, FALSE))
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Failed to set up owner to absolute security descriptor (error code %d)\n", GetLastError());
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /* Set the primary group for this descriptor */
+ if (!SetSecurityDescriptorGroup(&AbsoluteSd, PrimaryGroupSid, FALSE))
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Failed to set up group to absolute security descriptor (error code %d)\n", GetLastError());
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /*
+ * Determine the exact size space of the absolute
+ * descriptor so that we can allocate a buffer
+ * to hold the descriptor in a converted self
+ * relative format.
+ */
+ if (!MakeSelfRelativeSD(&AbsoluteSd, NULL, &RelativeSDSize) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Unexpected error code (error code %d -- must be ERROR_INSUFFICIENT_BUFFER)\n", GetLastError());
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /* Allocate the buffer */
+ RelativeSD = RtlAllocateHeap(RtlGetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ RelativeSDSize);
+ if (RelativeSD == NULL)
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Failed to allocate buffer for self relative descriptor!\n");
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /* Convert to a self relative format now */
+ if (!MakeSelfRelativeSD(&AbsoluteSd, RelativeSD, &RelativeSDSize))
+ {
+ ERR("CreateDefaultProcessSecurityCommon(): Failed to allocate relative SD, buffer too smal (error code %d)\n", GetLastError());
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /* Success, give the descriptor to the caller */
+ *Sd = RelativeSD;
+ Success = TRUE;
+
+Quit:
+ /* Free all the stuff we have allocated */
+ if (OwnerOfToken != NULL)
+ RtlFreeHeap(RtlGetProcessHeap(), 0, OwnerOfToken);
+
+ if (PrimaryGroupOfToken != NULL)
+ RtlFreeHeap(RtlGetProcessHeap(), 0, PrimaryGroupOfToken);
+
+ if (SystemSid != NULL)
+ FreeSid(SystemSid);
+
+ if (Dacl != NULL)
+ RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl);
+
+ if (Success == FALSE)
+ {
+ if (RelativeSD != NULL)
+ {
+ RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSD);
+ }
+ }
+
+ return Success;
+}
+
+
+/**
+ * @brief
+ * Changes the object security information of a process
+ * and thread that belongs to the process with new security
+ * data, basically by replacing the previous security descriptor
+ * with a new one.
+ *
+ * @param[in] ProcessHandle
+ * A handle to a valid process of which security information is
+ * to be changed by setting up a new security descriptor.
+ *
+ * @param[in] ThreadHandle
+ * A handle to a valid thread of which security information is
+ * to be changed by setting up a new security descriptor.
+ *
+ * @param[in] ProcessSecurity
+ * A pointer to a security descriptor that is for the process.
+ *
+ * @param[in] ThreadSecurity
+ * A pointer to a security descriptor that is for the thread.
+ *
+ * @return
+ * Return TRUE if new security information has been set, FALSE
+ * otherwise.
+ */
+static
+BOOL
+InsertProcessSecurityCommon(
+ _In_ HANDLE ProcessHandle,
+ _In_ HANDLE ThreadHandle,
+ _In_ PSECURITY_DESCRIPTOR ProcessSecurity,
+ _In_ PSECURITY_DESCRIPTOR ThreadSecurity)
+{
+ /* Set new security data for the process */
+ if (!SetKernelObjectSecurity(ProcessHandle,
+ DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION,
+ ProcessSecurity))
+ {
+ ERR("InsertProcessSecurityCommon(): Failed to set security for process (error code %d)\n", GetLastError());
+ return FALSE;
+ }
+
+ /* Set new security data for the thread */
+ if (!SetKernelObjectSecurity(ThreadHandle,
+ DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION,
+ ThreadSecurity))
+ {
+ ERR("InsertProcessSecurityCommon(): Failed to set security for thread (error code %d)\n", GetLastError());
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
/**
* @brief
* Sets a primary token to the newly created process.
@@ -267,6 +607,20 @@ InsertTokenToProcessCommon(
* if the process wasn't created in a suspended way
* and if not the function will resume the main thread.
*
+ * @param[in] lpProcessAttributes
+ * A pointer to process attributes. This function uses
+ * this parameter to gather the security descriptor,
+ * if ever present. If it is, this descriptor takes
+ * precedence over the default one when setting
+ * new security information to the process.
+ *
+ * @param[in] lpThreadAttributes
+ * A pointer to thread attributes. This function uses
+ * this parameter to gather the security descriptor,
+ * if ever present. If it is, this descriptor takes
+ * precedence over the default one when setting
+ * new security information to the thread.
+ *
* @param[in,out] lpProcessInformation
* A pointer to a structure that contains process creation
* information data. Such pointer contains the process
@@ -291,6 +645,8 @@ BOOL
CreateProcessAsUserCommon(
_In_opt_ HANDLE hToken,
_In_ DWORD dwCreationFlags,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_Inout_ LPPROCESS_INFORMATION lpProcessInformation)
{
NTSTATUS Status = STATUS_SUCCESS, StatusOnExit;
@@ -298,6 +654,7 @@ CreateProcessAsUserCommon(
TOKEN_TYPE Type;
ULONG ReturnLength;
OBJECT_ATTRIBUTES ObjectAttributes;
+ PSECURITY_DESCRIPTOR DefaultSd = NULL, ProcessSd, ThreadSd;
HANDLE hTokenDup = NULL;
HANDLE OriginalImpersonationToken = NULL;
HANDLE NullToken = NULL;
@@ -367,12 +724,27 @@ CreateProcessAsUserCommon(
}
}
- /* Duplicate the token for this new process */
+ /*
+ * Create a security descriptor that will be common for the
+ * newly created process on behalf of the context user.
+ */
+ if (!CreateDefaultProcessSecurityCommon(hToken, &DefaultSd))
+ {
+ ERR("Failed to create common security descriptor for the token for new process!\n");
+ Success = FALSE;
+ goto Quit;
+ }
+
+ /*
+ * Duplicate the token for this new process. This token
+ * object will get a default security descriptor that we
+ * have created ourselves in ADVAPI32.
+ */
InitializeObjectAttributes(&ObjectAttributes,
NULL,
0,
NULL,
- NULL); // FIXME: Use a valid SecurityDescriptor!
+ DefaultSd);
Status = NtDuplicateToken(hToken,
0,
&ObjectAttributes,
@@ -452,6 +824,53 @@ CreateProcessAsUserCommon(
* this user.
*/
+ /*
+ * As we have successfully set the token into the process now
+ * it is time that we set up new security information for both
+ * the process and its thread as well, that is, these securable
+ * objects will grant a security descriptor. The security descriptors
+ * provided by the caller take precedence so we should use theirs
+ * if possible in this case. Otherwise both the process and thread
+ * will receive the default security descriptor that we have created
+ * ourselves.
+ *
+ * BEAR IN MIND!!! AT THE MOMENT when these securable objects get new
+ * security information, the process (and the thread) can't be opened
+ * by the creator anymore as the new owner will take in charge of
+ * the process and future objects that are going to be created within
+ * the process. For further information in regard of the documentation
+ * see https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-pro….
+ */
+ if (lpProcessAttributes && lpProcessAttributes->lpSecurityDescriptor)
+ {
+ ProcessSd = lpProcessAttributes->lpSecurityDescriptor;
+ }
+ else
+ {
+ ProcessSd = DefaultSd;
+ }
+
+ if (lpThreadAttributes && lpThreadAttributes->lpSecurityDescriptor)
+ {
+ ThreadSd = lpThreadAttributes->lpSecurityDescriptor;
+ }
+ else
+ {
+ ThreadSd = DefaultSd;
+ }
+
+ /* Set new security info to the process and thread now */
+ if (!InsertProcessSecurityCommon(lpProcessInformation->hProcess,
+ lpProcessInformation->hThread,
+ ProcessSd,
+ ThreadSd))
+ {
+ ERR("Failed to set new security information for process and thread!\n");
+ NtClose(hTokenDup);
+ Success = FALSE;
+ goto Quit;
+ }
+
/* Close the duplicated token */
NtClose(hTokenDup);
Success = TRUE;
@@ -507,6 +926,12 @@ Quit:
ResumeThread(lpProcessInformation->hThread);
}
+ /* Free the security descriptor from memory */
+ if (DefaultSd != NULL)
+ {
+ RtlFreeHeap(RtlGetProcessHeap(), 0, DefaultSd);
+ }
+
return Success;
}
@@ -553,6 +978,8 @@ CreateProcessAsUserA(
/* Call the helper function */
return CreateProcessAsUserCommon(hToken,
dwCreationFlags,
+ lpProcessAttributes,
+ lpThreadAttributes,
lpProcessInformation);
}
@@ -599,6 +1026,8 @@ CreateProcessAsUserW(
/* Call the helper function */
return CreateProcessAsUserCommon(hToken,
dwCreationFlags,
+ lpProcessAttributes,
+ lpThreadAttributes,
lpProcessInformation);
}
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=e90e918039e60228d1c3e…
commit e90e918039e60228d1c3e3a7e8bc016915dd49ac
Author: George Bișoc <george.bisoc(a)reactos.org>
AuthorDate: Thu Mar 10 12:03:31 2022 +0100
Commit: George Bișoc <george.bisoc(a)reactos.org>
CommitDate: Fri May 6 10:09:50 2022 +0200
[ADVAPI32] Soft rewrite of CreateProcessAsUserCommon
Refactor the function in such a way that it can jump to a single exit but most importantly, implement a "rinse and repeat" mechanism where we assign a primary token to process by disabling impersonation first and retry with impersonation later.
More info can be found in the documention within the code.
---
dll/win32/advapi32/misc/logon.c | 419 +++++++++++++++++++++++++++++++++-------
1 file changed, 352 insertions(+), 67 deletions(-)
diff --git a/dll/win32/advapi32/misc/logon.c b/dll/win32/advapi32/misc/logon.c
index 1262f1b652d..6687a9f7e93 100644
--- a/dll/win32/advapi32/misc/logon.c
+++ b/dll/win32/advapi32/misc/logon.c
@@ -89,24 +89,221 @@ CloseLogonLsaHandle(VOID)
}
+/**
+ * @brief
+ * Sets a primary token to the newly created process.
+ * The primary token that gets assigned to is a token
+ * whose security context is associated with the logged
+ * in user. For futher documentation information, see
+ * Remarks.
+ *
+ * @param[in] ImpersonateAsSelf
+ * If set to TRUE, the function will act on behalf of
+ * the calling process by impersonating its security context.
+ * Generally the caller will disable impersonation and attempt
+ * to act on behalf of the said main process as a first tentative
+ * to acquire the needed privilege in order to assign a token
+ * to the process. If set to FALSE, the function won't act on behalf
+ * of the calling process.
+ *
+ * @param[in] ProcessHandle
+ * A handle to the newly created process. The function will use it
+ * as a mean to assign the primary token to this process.
+ *
+ * @param[in] ThreadHandle
+ * A handle to the newly and primary created thread associated with
+ * the process.
+ *
+ * @param[in] DuplicatedTokenHandle
+ * A handle to a duplicated access token. This token represents as a primary
+ * one, initially duplicated in form as a primary type from an impersonation
+ * type.
+ *
+ * @return
+ * STATUS_SUCCESS is returned if token assignment to process succeeded, otherwise
+ * a failure NTSTATUS code is returned. A potential failure status code is
+ * STATUS_ACCESS_DENIED which means the caller doesn't have enough rights
+ * to grant access for primary token assignment to process.
+ *
+ * @remarks
+ * This function acts like an internal helper for CreateProcessAsUserCommon (and as
+ * such for CreateProcessAsUserW/A as well) as once a process is created, the
+ * function is tasked to assign the security context of the logged in user to
+ * that process. However, the rate of success of inserting the token into the
+ * process ultimately depends on the caller.
+ *
+ * The caller will either succeed or fail at acquiring SE_ASSIGNPRIMARYTOKEN_PRIVILEGE
+ * privilege depending on the security context of the user. If it's allowed, the caller
+ * would generally acquire such privilege immediately but if not, the caller will attempt
+ * to do a second try.
+ */
+static
+NTSTATUS
+InsertTokenToProcessCommon(
+ _In_ BOOL ImpersonateAsSelf,
+ _In_ HANDLE ProcessHandle,
+ _In_ HANDLE ThreadHandle,
+ _In_ HANDLE DuplicatedTokenHandle)
+{
+ NTSTATUS Status;
+ PROCESS_ACCESS_TOKEN AccessToken;
+ BOOLEAN PrivilegeSet;
+ BOOLEAN HavePrivilege;
+
+ /*
+ * Assume the SE_ASSIGNPRIMARYTOKEN_PRIVILEGE
+ * privilege hasn't been set.
+ */
+ PrivilegeSet = FALSE;
+
+ /*
+ * The caller asked that we must impersonate as
+ * ourselves, that is, we'll be going to impersonate
+ * the security context of the calling process. If
+ * self impersonation fails then the caller has
+ * to do a "rinse and repeat" approach.
+ */
+ if (ImpersonateAsSelf)
+ {
+ Status = RtlImpersonateSelf(SecurityImpersonation);
+ if (!NT_SUCCESS(Status))
+ {
+ ERR("RtlImpersonateSelf(SecurityImpersonation) failed, Status 0x%08x\n", Status);
+ return Status;
+ }
+ }
+
+ /*
+ * Attempt to acquire the process primary token assignment privilege
+ * in case we actually need it.
+ * The call will either succeed or fail when the caller has (or has not)
+ * enough rights.
+ * The last situation may not be dramatic for us. Indeed it may happen
+ * that the user-provided token is a restricted version of the caller's
+ * primary token (aka. a "child" token), or both tokens inherit (i.e. are
+ * children, and are together "siblings") from a common parent token.
+ * In this case the NT kernel allows us to assign the token to the child
+ * process without the need for the assignment privilege, which is fine.
+ * On the contrary, if the user-provided token is completely arbitrary,
+ * then the NT kernel will enforce the presence of the assignment privilege:
+ * because we failed (by assumption) to assign the privilege, the process
+ * token assignment will fail as required. It is then the job of the
+ * caller to manually acquire the necessary privileges.
+ */
+ Status = RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE,
+ TRUE, TRUE, &PrivilegeSet);
+ HavePrivilege = NT_SUCCESS(Status);
+ if (!HavePrivilege)
+ {
+ ERR("RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE) failed, Status 0x%08lx, "
+ "attempting to continue without it...\n", Status);
+ }
+
+ /*
+ * Assign the duplicated token and thread
+ * handle to the structure so that we'll
+ * use it to assign the primary token
+ * to process.
+ */
+ AccessToken.Token = DuplicatedTokenHandle;
+ AccessToken.Thread = ThreadHandle;
+
+ /* Set the new process token */
+ Status = NtSetInformationProcess(ProcessHandle,
+ ProcessAccessToken,
+ (PVOID)&AccessToken,
+ sizeof(AccessToken));
+
+ /* Restore the privilege */
+ if (HavePrivilege)
+ {
+ RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE,
+ PrivilegeSet, TRUE, &PrivilegeSet);
+ }
+
+ /*
+ * Check again if the caller wanted to impersonate
+ * as self. If that is the case we must revert this
+ * impersonation back.
+ */
+ if (ImpersonateAsSelf)
+ {
+ RevertToSelf();
+ }
+
+ /*
+ * Finally, check if we actually succeeded on assigning
+ * a primary token to the process. If we failed, oh well,
+ * asta la vista baby e arrivederci. The caller has to do
+ * a rinse and repeat approach.
+ */
+ if (!NT_SUCCESS(Status))
+ {
+ ERR("Failed to assign primary token to the process (Status 0x%08lx)\n", Status);
+ return Status;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+/**
+ * @brief
+ * Internal function that serves as a helper for
+ * CreateProcessAsUserW/A routines on creating
+ * a process within the context of the logged in
+ * user.
+ *
+ * @param[in] hToken
+ * A handle to an access token that is associated
+ * with the logged in user. If the caller does not
+ * submit a token, the helper will immediately quit
+ * and return success, and the newly created process
+ * will be created upon using the default security
+ * context.
+ *
+ * @param[in] dwCreationFlags
+ * Bit masks containing the creation process flags.
+ * The function uses this parameter to determine
+ * if the process wasn't created in a suspended way
+ * and if not the function will resume the main thread.
+ *
+ * @param[in,out] lpProcessInformation
+ * A pointer to a structure that contains process creation
+ * information data. Such pointer contains the process
+ * and thread handles and whatnot.
+ *
+ * @return
+ * Returns TRUE if the helper has successfully assigned
+ * the newly created process the user's security context
+ * to that process, otherwise FALSE is returned.
+ *
+ * @remarks
+ * In order for the helper function to assign the primary
+ * token to the process, it has to do a "rinse and repeat"
+ * approach. That is, the helper will stop the impersonation
+ * and attempt to assign the token to process by acting
+ * on behalf of the main process' security context. If that
+ * fails, the function will do a second attempt by doing this
+ * but with impersonation enabled instead.
+ */
static
BOOL
CreateProcessAsUserCommon(
_In_opt_ HANDLE hToken,
_In_ DWORD dwCreationFlags,
- _Out_ LPPROCESS_INFORMATION lpProcessInformation)
+ _Inout_ LPPROCESS_INFORMATION lpProcessInformation)
{
- NTSTATUS Status;
- PROCESS_ACCESS_TOKEN AccessToken;
+ NTSTATUS Status = STATUS_SUCCESS, StatusOnExit;
+ BOOL Success;
+ TOKEN_TYPE Type;
+ ULONG ReturnLength;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE hTokenDup = NULL;
+ HANDLE OriginalImpersonationToken = NULL;
+ HANDLE NullToken = NULL;
if (hToken != NULL)
{
- TOKEN_TYPE Type;
- ULONG ReturnLength;
- OBJECT_ATTRIBUTES ObjectAttributes;
- HANDLE hTokenDup;
- BOOLEAN PrivilegeSet = FALSE, HavePrivilege;
-
/* Check whether the user-provided token is a primary token */
// GetTokenInformation();
Status = NtQueryInformationToken(hToken,
@@ -117,15 +314,59 @@ CreateProcessAsUserCommon(
if (!NT_SUCCESS(Status))
{
ERR("NtQueryInformationToken() failed, Status 0x%08x\n", Status);
+ Success = FALSE;
goto Quit;
}
+
if (Type != TokenPrimary)
{
ERR("Wrong token type for token 0x%p, expected TokenPrimary, got %ld\n", hToken, Type);
Status = STATUS_BAD_TOKEN_TYPE;
+ Success = FALSE;
goto Quit;
}
+ /*
+ * Open the original token of the calling thread
+ * and halt the impersonation for the moment
+ * being. The opened thread token will be cached
+ * so that we will restore it back when we're done.
+ */
+ Status = NtOpenThreadToken(NtCurrentThread(),
+ TOKEN_QUERY | TOKEN_IMPERSONATE,
+ TRUE,
+ &OriginalImpersonationToken);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We failed? Does this thread have a token at least? */
+ OriginalImpersonationToken = NULL;
+ if (Status != STATUS_NO_TOKEN)
+ {
+ /*
+ * OK so this thread has a token but we
+ * could not open it for whatever reason.
+ * Bail out then.
+ */
+ ERR("Failed to open thread token with 0x%08lx\n", Status);
+ Success = FALSE;
+ goto Quit;
+ }
+ }
+ else
+ {
+ /* We succeeded, stop the impersonation for now */
+ Status = NtSetInformationThread(NtCurrentThread(),
+ ThreadImpersonationToken,
+ &NullToken,
+ sizeof(NullToken));
+ if (!NT_SUCCESS(Status))
+ {
+ ERR("Failed to stop impersonation with 0x%08lx\n", Status);
+ Success = FALSE;
+ goto Quit;
+ }
+ }
+
/* Duplicate the token for this new process */
InitializeObjectAttributes(&ObjectAttributes,
NULL,
@@ -141,79 +382,123 @@ CreateProcessAsUserCommon(
if (!NT_SUCCESS(Status))
{
ERR("NtDuplicateToken() failed, Status 0x%08x\n", Status);
- goto Quit;
- }
-
- // FIXME: Do we always need SecurityImpersonation?
- Status = RtlImpersonateSelf(SecurityImpersonation);
- if (!NT_SUCCESS(Status))
- {
- ERR("RtlImpersonateSelf(SecurityImpersonation) failed, Status 0x%08x\n", Status);
- NtClose(hTokenDup);
+ Success = FALSE;
goto Quit;
}
/*
- * Attempt to acquire the process primary token assignment privilege
- * in case we actually need it.
- * The call will either succeed or fail when the caller has (or has not)
- * enough rights.
- * The last situation may not be dramatic for us. Indeed it may happen
- * that the user-provided token is a restricted version of the caller's
- * primary token (aka. a "child" token), or both tokens inherit (i.e. are
- * children, and are together "siblings") from a common parent token.
- * In this case the NT kernel allows us to assign the token to the child
- * process without the need for the assignment privilege, which is fine.
- * On the contrary, if the user-provided token is completely arbitrary,
- * then the NT kernel will enforce the presence of the assignment privilege:
- * because we failed (by assumption) to assign the privilege, the process
- * token assignment will fail as required. It is then the job of the
- * caller to manually acquire the necessary privileges.
+ * Now it's time to set the primary token into
+ * the process. On the first try, do it by
+ * impersonating the security context of the
+ * calling process (impersonate as self).
*/
- Status = RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE,
- TRUE, TRUE, &PrivilegeSet);
- HavePrivilege = NT_SUCCESS(Status);
- if (!HavePrivilege)
+ Status = InsertTokenToProcessCommon(TRUE,
+ lpProcessInformation->hProcess,
+ lpProcessInformation->hThread,
+ hTokenDup);
+ if (!NT_SUCCESS(Status))
{
- ERR("RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE) failed, Status 0x%08lx, "
- "attempting to continue without it...\n", Status);
- }
-
- AccessToken.Token = hTokenDup;
- AccessToken.Thread = lpProcessInformation->hThread;
+ /*
+ * OK, we failed. Our second (and last try) is to not
+ * impersonate as self but instead we will try by setting
+ * the original impersonation (thread) token and set the
+ * primary token to the process through this way. This is
+ * what we call -- the "rinse and repeat" approach.
+ */
+ Status = NtSetInformationThread(NtCurrentThread(),
+ ThreadImpersonationToken,
+ &OriginalImpersonationToken,
+ sizeof(OriginalImpersonationToken));
+ if (!NT_SUCCESS(Status))
+ {
+ ERR("Failed to restore impersonation token for setting process token, Status 0x%08lx\n", Status);
+ NtClose(hTokenDup);
+ Success = FALSE;
+ goto Quit;
+ }
- /* Set the new process token */
- Status = NtSetInformationProcess(lpProcessInformation->hProcess,
- ProcessAccessToken,
- (PVOID)&AccessToken,
- sizeof(AccessToken));
+ /* Retry again */
+ Status = InsertTokenToProcessCommon(FALSE,
+ lpProcessInformation->hProcess,
+ lpProcessInformation->hThread,
+ hTokenDup);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Even the second try failed, bail out... */
+ ERR("Failed to insert the primary token into process, Status 0x%08lx\n", Status);
+ NtClose(hTokenDup);
+ Success = FALSE;
+ goto Quit;
+ }
- /* Restore the privilege */
- if (HavePrivilege)
- {
- RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE,
- PrivilegeSet, TRUE, &PrivilegeSet);
+ /* All good, now stop impersonation */
+ Status = NtSetInformationThread(NtCurrentThread(),
+ ThreadImpersonationToken,
+ &NullToken,
+ sizeof(NullToken));
+ if (!NT_SUCCESS(Status))
+ {
+ ERR("Failed to unset impersonationg token after setting process token, Status 0x%08lx\n", Status);
+ NtClose(hTokenDup);
+ Success = FALSE;
+ goto Quit;
+ }
}
- RevertToSelf();
+ /*
+ * FIXME: As we have successfully set up a primary token to
+ * the newly created process, we must set up as well a definite
+ * limit of quota charges for this process on the context of
+ * this user.
+ */
/* Close the duplicated token */
NtClose(hTokenDup);
+ Success = TRUE;
+ }
- /* Check whether NtSetInformationProcess() failed */
- if (!NT_SUCCESS(Status))
- {
- ERR("NtSetInformationProcess() failed, Status 0x%08x\n", Status);
- goto Quit;
- }
+ /*
+ * If the caller did not supply a token then just declare
+ * ourselves as job done. The newly created process will use
+ * the default security context at this point anyway.
+ */
+ TRACE("No token supplied, the process will use default security context!\n");
+ Success = TRUE;
- if (!NT_SUCCESS(Status))
- {
Quit:
- TerminateProcess(lpProcessInformation->hProcess, Status);
- SetLastError(RtlNtStatusToDosError(Status));
- return FALSE;
- }
+ /*
+ * If we successfully opened the thread token before
+ * and stopped the impersonation then we have to assign
+ * its original token back and close that token we have
+ * referenced it.
+ */
+ if (OriginalImpersonationToken != NULL)
+ {
+ StatusOnExit = NtSetInformationThread(NtCurrentThread(),
+ ThreadImpersonationToken,
+ &OriginalImpersonationToken,
+ sizeof(OriginalImpersonationToken));
+
+ /*
+ * We really must assert ourselves that we successfully
+ * set the original token back, otherwise if we fail
+ * then something is seriously going wrong....
+ * The status code is cached in a separate status
+ * variable because we would not want to tamper
+ * with the original status code that could have been
+ * returned by someone else above in this function code.
+ */
+ ASSERT(NT_SUCCESS(StatusOnExit));
+
+ /* De-reference it */
+ NtClose(OriginalImpersonationToken);
+ }
+
+ /* Terminate the process and set the last error status */
+ if (!NT_SUCCESS(Status))
+ {
+ TerminateProcess(lpProcessInformation->hProcess, Status);
+ SetLastError(RtlNtStatusToDosError(Status));
}
/* Resume the main thread */
@@ -222,7 +507,7 @@ Quit:
ResumeThread(lpProcessInformation->hThread);
}
- return TRUE;
+ return Success;
}
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=bf40c7a310bfd48a5e9a0…
commit bf40c7a310bfd48a5e9a0b822ab19b0d9443b788
Author: George Bișoc <george.bisoc(a)reactos.org>
AuthorDate: Mon Apr 11 20:24:41 2022 +0200
Commit: George Bișoc <george.bisoc(a)reactos.org>
CommitDate: Fri May 6 10:09:49 2022 +0200
[KERNEL32] Let KERNEL32 assign security to NLS section names
Currently Kernel32 doesn't make any server call to Basesrv in order to create NLS section names, instead it's Kernel32 itself that handles the job of NLS section names. With that said, let Kernel32 assign a security descriptor to NLS section names. See the FIXME comment on code for further dtails
---
dll/win32/kernel32/winnls/string/nls.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/dll/win32/kernel32/winnls/string/nls.c b/dll/win32/kernel32/winnls/string/nls.c
index 3a096884992..d810c429069 100644
--- a/dll/win32/kernel32/winnls/string/nls.c
+++ b/dll/win32/kernel32/winnls/string/nls.c
@@ -59,6 +59,9 @@ GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize);
NTSTATUS
CreateNlsDirectorySecurity(_Out_ PSECURITY_DESCRIPTOR *NlsSecurityDescriptor);
+NTSTATUS WINAPI
+CreateNlsSecurityDescriptor(_Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor, _In_ SIZE_T DescriptorSize, _In_ ULONG AccessMask);
+
/* PRIVATE FUNCTIONS **********************************************************/
/**
@@ -219,6 +222,7 @@ IntGetCodePageEntry(UINT CodePage)
WCHAR FileName[MAX_PATH + 1];
UINT FileNamePos;
PCODEPAGE_ENTRY CodePageEntry;
+ PSECURITY_DESCRIPTOR NlsSd;
if (CodePage == CP_ACP)
{
return &AnsiCodePage;
@@ -281,7 +285,23 @@ IntGetCodePageEntry(UINT CodePage)
RtlInitAnsiString(&AnsiName, SectionName);
RtlAnsiStringToUnicodeString(&UnicodeName, &AnsiName, TRUE);
- InitializeObjectAttributes(&ObjectAttributes, &UnicodeName, 0, NULL, NULL);
+ /*
+ * FIXME: IntGetCodePageEntry should not create any security
+ * descriptor here but instead this responsibility should be
+ * assigned to Base Server API (aka basesrv.dll). That is,
+ * kernel32 must instruct basesrv.dll on creating NLS section
+ * names that do not exist through API message communication.
+ * However since we do not do that, let the kernel32 do the job
+ * by assigning security to NLS section names for the time being...
+ */
+ Status = CreateNlsSecurityDescriptor(&NlsSd, sizeof(SECURITY_DESCRIPTOR), SECTION_MAP_READ);
+ if (!NT_SUCCESS(Status))
+ {
+ RtlLeaveCriticalSection(&CodePageListLock);
+ return NULL;
+ }
+
+ InitializeObjectAttributes(&ObjectAttributes, &UnicodeName, 0, NULL, NlsSd);
/* Try to open the section first */
Status = NtOpenSection(&SectionHandle, SECTION_MAP_READ, &ObjectAttributes);
@@ -329,6 +349,7 @@ IntGetCodePageEntry(UINT CodePage)
}
}
RtlFreeUnicodeString(&UnicodeName);
+ HeapFree(GetProcessHeap(), 0, NlsSd);
if (!NT_SUCCESS(Status))
{
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=2092dc06bb3069155adf4…
commit 2092dc06bb3069155adf40bd7a3616fee8ec68bb
Author: George Bișoc <george.bisoc(a)reactos.org>
AuthorDate: Sat Apr 9 21:17:58 2022 +0200
Commit: George Bișoc <george.bisoc(a)reactos.org>
CommitDate: Fri May 6 10:09:49 2022 +0200
[WINLOGON][HACK] Allow network services access to default window station
HHHHHHHHHHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCCCCCCKKKKKKKKKKKKKKKKKK!!!
There are two problems concerning with network services. First, a window station should be created for every network service process that gets started although this doesn't happen. Instead, network services like RPCSS and DNS service host process (svchost.exe) attempt to access the default window station (Winsta0).
This is because the access token of these two network service processes have an authentication ID that is uniquely generated. This is incorrect, because NetworkService is a special account with its own designed authentication ID for it. As a matter of fact, no window station is created for a network service and as such
both RPCSS and DNS svchost.exe attempt to access Winsta0 which they cannot.
The second problem, albeit not quite relevant to the first one but still worth mentioning nevertheless, is that network services have an access token that is primary which it should be an impersonation token. These problems all come from LSASS as LSA infrastructure is responsible for creating access tokens with security
context for objects.
For the moment being, add a hack on Winlogon that gives allow access to the default window station to network services. When LSASS and involved components are fixed, this hack must be removed.
---
base/system/winlogon/security.c | 191 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 183 insertions(+), 8 deletions(-)
diff --git a/base/system/winlogon/security.c b/base/system/winlogon/security.c
index 26d3935fe2e..4045496abb2 100644
--- a/base/system/winlogon/security.c
+++ b/base/system/winlogon/security.c
@@ -115,7 +115,7 @@ CreateWinstaSecurity(
BOOL Success = FALSE;
SECURITY_DESCRIPTOR AbsoluteSd;
PSECURITY_DESCRIPTOR RelativeSd = NULL;
- PSID WinlogonSid = NULL, AdminsSid = NULL;
+ PSID WinlogonSid = NULL, AdminsSid = NULL, NetworkServiceSid = NULL; /* NetworkServiceSid is a HACK, see the comment below for information */
DWORD DaclSize;
PACL Dacl;
@@ -142,18 +142,62 @@ CreateWinstaSecurity(
goto Quit;
}
+ /* HACK: Create the network service SID */
+ if (!AllocateAndInitializeSid(&NtAuthority,
+ 1,
+ SECURITY_NETWORK_SERVICE_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &NetworkServiceSid))
+ {
+ ERR("CreateWinstaSecurity(): Failed to create the network service SID (error code %lu)\n", GetLastError());
+ goto Quit;
+ }
+
/*
* Build up the DACL size. This includes a number
* of four ACEs of two different SIDs. The first two
* ACEs give both window station and generic access
* to Winlogon, the last two give limited window station
* and desktop access to admins.
+ *
+ * ===================== !!!MUST READ!!! =====================
+ *
+ * HACK -- Include in the DACL two more ACEs for network
+ * service SID. Network services will be granted full
+ * access to the default window station. Whilst technically
+ * services that are either network or local ones are part
+ * and act on behalf of the system, what we are doing here
+ * is a hack because of two reasons:
+ *
+ * 1) Winlogon does not allow default window station (Winsta0)
+ * access to network services on Windows. As a matter of fact,
+ * network services must access their own service window station
+ * (aka Service-0x0-3e4$) which never gets created. Why it never
+ * gets created is explained on the second point.
+ *
+ * 2) Our LSASS terribly lacks in code that handles special logon
+ * service types, NetworkService and LocalService. For this reason
+ * whenever an access token is created for a network service process
+ * for example, its authentication ID (aka LogonId represented as a LUID)
+ * is a uniquely generated ID by LSASS for this process. This is wrong
+ * on so many levels, partly because a network service is not a regular
+ * service and network services have their own special authentication logon
+ * ID (with its respective LUID as {0x3e4, 0x0}). On top of that, a network
+ * service process must have an impersonation token but for whatever reason
+ * we are creating a primary access token instead.
+ *
+ * FOR ANYONE WHO'S INTERESTED ON FIXING THIS, DO NOT FORGET TO REMOVE THIS
+ * HACK!!!
+ *
+ * =========================== !!!END!!! ================================
*/
DaclSize = sizeof(ACL) +
sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(WinlogonSid) +
sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(WinlogonSid) +
sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(AdminsSid) +
- sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(AdminsSid);
+ sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(AdminsSid) +
+ sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(NetworkServiceSid) +
+ sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(NetworkServiceSid);
/* Allocate the DACL now */
Dacl = RtlAllocateHeap(RtlGetProcessHeap(),
@@ -216,6 +260,28 @@ CreateWinstaSecurity(
goto Quit;
}
+ /* HACK: Fifth ACE -- give full access to network services */
+ if (!AddAccessAllowedAceEx(Dacl,
+ ACL_REVISION,
+ NO_PROPAGATE_INHERIT_ACE,
+ WINSTA_ALL,
+ NetworkServiceSid))
+ {
+ ERR("CreateWinstaSecurity(): Failed to set ACE for network service (error code %lu)\n", GetLastError());
+ goto Quit;
+ }
+
+ /* HACK: Sixth ACE -- give full generic access to network services */
+ if (!AddAccessAllowedAceEx(Dacl,
+ ACL_REVISION,
+ INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE,
+ GENERIC_ACCESS,
+ NetworkServiceSid))
+ {
+ ERR("CreateWinstaSecurity(): Failed to set ACE for network service (error code %lu)\n", GetLastError());
+ goto Quit;
+ }
+
/* Initialize the security descriptor */
if (!InitializeSecurityDescriptor(&AbsoluteSd, SECURITY_DESCRIPTOR_REVISION))
{
@@ -253,6 +319,13 @@ Quit:
FreeSid(AdminsSid);
}
+ /* HACK */
+ if (NetworkServiceSid != NULL)
+ {
+ FreeSid(NetworkServiceSid);
+ }
+ /* END HACK */
+
if (Dacl != NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl);
@@ -289,7 +362,7 @@ CreateApplicationDesktopSecurity(
BOOL Success = FALSE;
SECURITY_DESCRIPTOR AbsoluteSd;
PSECURITY_DESCRIPTOR RelativeSd = NULL;
- PSID WinlogonSid = NULL, AdminsSid = NULL;
+ PSID WinlogonSid = NULL, AdminsSid = NULL, NetworkServiceSid = NULL; /* NetworkServiceSid is a HACK, see the comment in CreateWinstaSecurity for information */
DWORD DaclSize;
PACL Dacl;
@@ -316,6 +389,17 @@ CreateApplicationDesktopSecurity(
goto Quit;
}
+ /* HACK: Create the network service SID */
+ if (!AllocateAndInitializeSid(&NtAuthority,
+ 1,
+ SECURITY_NETWORK_SERVICE_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &NetworkServiceSid))
+ {
+ ERR("CreateApplicationDesktopSecurity(): Failed to create the network service SID (error code %lu)\n", GetLastError());
+ goto Quit;
+ }
+
/*
* Build up the DACL size. This includes a number
* of two ACEs of two different SIDs. The first ACE
@@ -324,7 +408,8 @@ CreateApplicationDesktopSecurity(
*/
DaclSize = sizeof(ACL) +
sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(WinlogonSid) +
- sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(AdminsSid);
+ sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(AdminsSid) +
+ sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(NetworkServiceSid); /* HACK */
/* Allocate the DACL now */
Dacl = RtlAllocateHeap(RtlGetProcessHeap(),
@@ -365,6 +450,17 @@ CreateApplicationDesktopSecurity(
goto Quit;
}
+ /* HACK: Third ACE -- Give full desktop power to network services */
+ if (!AddAccessAllowedAceEx(Dacl,
+ ACL_REVISION,
+ 0,
+ DESKTOP_ALL,
+ NetworkServiceSid))
+ {
+ ERR("CreateApplicationDesktopSecurity(): Failed to set ACE for network services (error code %lu)\n", GetLastError());
+ goto Quit;
+ }
+
/* Initialize the security descriptor */
if (!InitializeSecurityDescriptor(&AbsoluteSd, SECURITY_DESCRIPTOR_REVISION))
{
@@ -402,6 +498,13 @@ Quit:
FreeSid(AdminsSid);
}
+ /* HACK */
+ if (NetworkServiceSid != NULL)
+ {
+ FreeSid(NetworkServiceSid);
+ }
+ /* END HACK */
+
if (Dacl != NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl);
@@ -775,7 +878,7 @@ AllowWinstaAccessToUser(
BOOL Success = FALSE;
SECURITY_DESCRIPTOR AbsoluteSd;
PSECURITY_DESCRIPTOR RelativeSd = NULL;
- PSID WinlogonSid = NULL, AdminsSid = NULL, InteractiveSid = NULL;
+ PSID WinlogonSid = NULL, AdminsSid = NULL, InteractiveSid = NULL, NetworkServiceSid = NULL; /* NetworkServiceSid is a HACK, see the comment in CreateWinstaSecurity for information */
SECURITY_INFORMATION SecurityInformation;
DWORD DaclSize;
PACL Dacl;
@@ -814,6 +917,17 @@ AllowWinstaAccessToUser(
goto Quit;
}
+ /* HACK: Create the network service SID */
+ if (!AllocateAndInitializeSid(&NtAuthority,
+ 1,
+ SECURITY_NETWORK_SERVICE_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &NetworkServiceSid))
+ {
+ ERR("AllowWinstaAccessToUser(): Failed to create the network service SID (error code %lu)\n", GetLastError());
+ goto Quit;
+ }
+
/*
* Build up the DACL size. This includes a number
* of eight ACEs of four different SIDs. The first ACE
@@ -830,7 +944,9 @@ AllowWinstaAccessToUser(
sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(InteractiveSid) +
sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(InteractiveSid) +
sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(LogonSid) +
- sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(LogonSid);
+ sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(LogonSid) +
+ sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(NetworkServiceSid) + /* HACK */
+ sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(NetworkServiceSid);
/* Allocate the DACL now */
Dacl = RtlAllocateHeap(RtlGetProcessHeap(),
@@ -937,6 +1053,28 @@ AllowWinstaAccessToUser(
goto Quit;
}
+ /* HACK : Ninenth ACE -- Give full winsta access to network services */
+ if (!AddAccessAllowedAceEx(Dacl,
+ ACL_REVISION,
+ NO_PROPAGATE_INHERIT_ACE,
+ WINSTA_ALL,
+ NetworkServiceSid))
+ {
+ ERR("AllowWinstaAccessToUser(): Failed to set ACE for logon network service SID (error code %lu)\n", GetLastError());
+ goto Quit;
+ }
+
+ /* HACK: Tenth ACE -- Give generic access to network services */
+ if (!AddAccessAllowedAceEx(Dacl,
+ ACL_REVISION,
+ INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE,
+ GENERIC_ACCESS,
+ NetworkServiceSid))
+ {
+ ERR("AllowWinstaAccessToUser(): Failed to set ACE for network service SID (error code %lu)\n", GetLastError());
+ goto Quit;
+ }
+
/* Initialize the security descriptor */
if (!InitializeSecurityDescriptor(&AbsoluteSd, SECURITY_DESCRIPTOR_REVISION))
{
@@ -985,6 +1123,13 @@ Quit:
FreeSid(InteractiveSid);
}
+ /* HACK */
+ if (NetworkServiceSid != NULL)
+ {
+ FreeSid(NetworkServiceSid);
+ }
+ /* END HACK */
+
if (Dacl != NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl);
@@ -1024,7 +1169,7 @@ AllowDesktopAccessToUser(
BOOL Success = FALSE;
SECURITY_DESCRIPTOR AbsoluteSd;
PSECURITY_DESCRIPTOR RelativeSd = NULL;
- PSID WinlogonSid = NULL, AdminsSid = NULL, InteractiveSid = NULL;
+ PSID WinlogonSid = NULL, AdminsSid = NULL, InteractiveSid = NULL, NetworkServiceSid = NULL; /* NetworkServiceSid is a HACK, see the comment in CreateWinstaSecurity for information */
SECURITY_INFORMATION SecurityInformation;
DWORD DaclSize;
PACL Dacl;
@@ -1063,6 +1208,17 @@ AllowDesktopAccessToUser(
goto Quit;
}
+ /* HACK: Create the network service SID */
+ if (!AllocateAndInitializeSid(&NtAuthority,
+ 1,
+ SECURITY_NETWORK_SERVICE_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &NetworkServiceSid))
+ {
+ ERR("AllowDesktopAccessToUser(): Failed to create the network service SID (error code %lu)\n", GetLastError());
+ goto Quit;
+ }
+
/*
* Build up the DACL size. This includes a number
* of four ACEs of four different SIDs. The first ACE
@@ -1075,7 +1231,8 @@ AllowDesktopAccessToUser(
sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(WinlogonSid) +
sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(AdminsSid) +
sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(InteractiveSid) +
- sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(LogonSid);
+ sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(LogonSid) +
+ sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(NetworkServiceSid); /* HACK */
/* Allocate the DACL now */
Dacl = RtlAllocateHeap(RtlGetProcessHeap(),
@@ -1138,6 +1295,17 @@ AllowDesktopAccessToUser(
goto Quit;
}
+ /* HACK: Fifth ACE -- Give full desktop to network services */
+ if (!AddAccessAllowedAceEx(Dacl,
+ ACL_REVISION,
+ 0,
+ DESKTOP_ALL,
+ NetworkServiceSid))
+ {
+ ERR("AllowDesktopAccessToUser(): Failed to set ACE for network service SID (error code %lu)\n", GetLastError());
+ goto Quit;
+ }
+
/* Initialize the security descriptor */
if (!InitializeSecurityDescriptor(&AbsoluteSd, SECURITY_DESCRIPTOR_REVISION))
{
@@ -1186,6 +1354,13 @@ Quit:
FreeSid(InteractiveSid);
}
+ /* HACK */
+ if (NetworkServiceSid != NULL)
+ {
+ FreeSid(NetworkServiceSid);
+ }
+ /* END HACK */
+
if (Dacl != NULL)
{
RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl);