[ADVAPI32] Improve a bit CreateProcessAsUser().
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Thu, 27 Sep 2018 22:36:59 +0000 (00:36 +0200)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Thu, 27 Sep 2018 22:45:04 +0000 (00:45 +0200)
- Check whether the user-provided token is a primary token.

- Do not fail when the RtlAdjustPrivilege() call fails (see the code
  comment for an explanation). TL;DR is: that call may indeed fail but
  the privilege may also not be necessary because the user-provided
  token is a restricted version of the caller's primary token.
  And this is situation is perfectly fine.

This fixes Java 7 installation, CORE-14874.

dll/win32/advapi32/misc/logon.c

index f4e3f85..0b9d603 100644 (file)
@@ -145,9 +145,30 @@ CreateProcessAsUserCommon(
 
     if (hToken != NULL)
     {
+        TOKEN_TYPE Type;
+        ULONG ReturnLength;
         OBJECT_ATTRIBUTES ObjectAttributes;
         HANDLE hTokenDup;
-        BOOLEAN PrivilegeSet = FALSE;
+        BOOLEAN PrivilegeSet = FALSE, HavePrivilege;
+
+        /* Check whether the user-provided token is a primary token */
+        // GetTokenInformation();
+        Status = NtQueryInformationToken(hToken,
+                                         TokenType,
+                                         &Type,
+                                         sizeof(Type),
+                                         &ReturnLength);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("NtQueryInformationToken() failed, Status 0x%08x\n", Status);
+            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;
+            goto Quit;
+        }
 
         /* Duplicate the token for this new process */
         InitializeObjectAttributes(&ObjectAttributes,
@@ -163,32 +184,43 @@ CreateProcessAsUserCommon(
                                   &hTokenDup);
         if (!NT_SUCCESS(Status))
         {
-            ERR("NtDuplicateToken failed, Status 0x%08x\n", Status);
-            TerminateProcess(lpProcessInformation->hProcess, Status);
-            SetLastError(RtlNtStatusToDosError(Status));
-            return FALSE;
+            ERR("NtDuplicateToken() failed, Status 0x%08x\n", Status);
+            goto Quit;
         }
 
         // FIXME: Do we always need SecurityImpersonation?
-        if (!ImpersonateSelf(SecurityImpersonation))
+        Status = RtlImpersonateSelf(SecurityImpersonation);
+        if (!NT_SUCCESS(Status))
         {
-            ERR("ImpersonateSelf(SecurityImpersonation) failed, last error: %d\n", GetLastError());
+            ERR("RtlImpersonateSelf(SecurityImpersonation) failed, Status 0x%08x\n", Status);
             NtClose(hTokenDup);
-            TerminateProcess(lpProcessInformation->hProcess, RtlGetLastNtStatus());
-            // SetLastError(RtlNtStatusToDosError(Status));
-            return FALSE;
+            goto Quit;
         }
 
-        /* Acquire the process primary token assignment privilege */
-        Status = RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, TRUE, &PrivilegeSet);
-        if (!NT_SUCCESS(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\n", Status);
-            RevertToSelf();
-            NtClose(hTokenDup);
-            TerminateProcess(lpProcessInformation->hProcess, Status);
-            SetLastError(RtlNtStatusToDosError(Status));
-            return FALSE;
+            ERR("RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE) failed, Status 0x%08lx, "
+                "attempting to continue without it...\n", Status);
         }
 
         AccessToken.Token  = hTokenDup;
@@ -200,8 +232,12 @@ CreateProcessAsUserCommon(
                                          (PVOID)&AccessToken,
                                          sizeof(AccessToken));
 
-        /* Restore the privileges */
-        RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, PrivilegeSet, TRUE, &PrivilegeSet);
+        /* Restore the privilege */
+        if (HavePrivilege)
+        {
+            RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE,
+                               PrivilegeSet, TRUE, &PrivilegeSet);
+        }
 
         RevertToSelf();
 
@@ -211,7 +247,13 @@ CreateProcessAsUserCommon(
         /* Check whether NtSetInformationProcess() failed */
         if (!NT_SUCCESS(Status))
         {
-            ERR("NtSetInformationProcess failed, Status 0x%08x\n", Status);
+            ERR("NtSetInformationProcess() failed, Status 0x%08x\n", Status);
+            goto Quit;
+        }
+
+        if (!NT_SUCCESS(Status))
+        {
+Quit:
             TerminateProcess(lpProcessInformation->hProcess, Status);
             SetLastError(RtlNtStatusToDosError(Status));
             return FALSE;