1 /*
2  * PROJECT:     ReactOS Spooler Router
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Functions related to switching between security contexts
5  * COPYRIGHT:   Copyright 2015 Colin Finck (colin@reactos.org)
6  */
7 
8 #include "precomp.h"
9 
10 /**
11  * @see RevertToPrinterSelf
12  */
13 BOOL WINAPI
14 ImpersonatePrinterClient(HANDLE hToken)
15 {
16     DWORD cbReturned;
17     DWORD dwErrorCode;
18     TOKEN_TYPE Type;
19 
20     // Sanity check
21     if (!hToken)
22     {
23         dwErrorCode = ERROR_INVALID_HANDLE;
24         goto Cleanup;
25     }
26 
27     // Get the type of the supplied token.
28     if (!GetTokenInformation(hToken, TokenType, &Type, sizeof(TOKEN_TYPE), &cbReturned))
29     {
30         dwErrorCode = GetLastError();
31         ERR("GetTokenInformation failed with error %lu!\n", dwErrorCode);
32         goto Cleanup;
33     }
34 
35     // Check if this is an impersonation token and only set it as the thread token in this case.
36     // This is not always an impersonation token, see RevertToPrinterSelf.
37     if (Type == TokenImpersonation)
38     {
39         if (!SetThreadToken(NULL, hToken))
40         {
41             dwErrorCode = GetLastError();
42             ERR("SetThreadToken failed with error %lu!\n", dwErrorCode);
43             goto Cleanup;
44         }
45     }
46 
47 Cleanup:
48     if (hToken)
49         CloseHandle(hToken);
50 
51     SetLastError(dwErrorCode);
52     return (dwErrorCode == ERROR_SUCCESS);
53 }
54 
55 /**
56  * RevertToPrinterSelf reverts the security context from the current user's context back to the process context.
57  * As spoolss.dll is used by spoolsv.exe, this is usually the SYSTEM security context.
58  *
59  * Unlike the traditional ImpersonateClient and then RevertToSelf approach, we do it the other way round here,
60  * because spoolss.dll is delay-loaded by spoolsv.exe in the current user's context. Use RevertToPrinterSelf then to
61  * return to the SYSTEM context for specific tasks.
62  */
63 HANDLE WINAPI
64 RevertToPrinterSelf(VOID)
65 {
66     DWORD dwErrorCode;
67     HANDLE hReturnValue = NULL;
68     HANDLE hToken = NULL;
69 
70     // All spoolss code is usually called after impersonating the client. In this case, we can retrieve our current thread impersonation token using OpenThreadToken.
71     // But in rare occasions, spoolss code is also called from a higher-privileged thread that doesn't impersonate the client. Then we don't get an impersonation token.
72     // Anyway, we can't just return nothing in this case, because this is being treated as failure by the caller. So we return the token of the current process.
73     // This behaviour is verified with Windows!
74     if (OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &hToken))
75     {
76         // Tell the thread to stop impersonating.
77         if (!SetThreadToken(NULL, NULL))
78         {
79             dwErrorCode = GetLastError();
80             ERR("SetThreadToken failed with error %lu!\n", dwErrorCode);
81             goto Cleanup;
82         }
83     }
84     else if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
85     {
86         dwErrorCode = GetLastError();
87         ERR("OpenProcessToken failed with error %lu!\n", dwErrorCode);
88         goto Cleanup;
89     }
90 
91     // We were successful, return a token!
92     dwErrorCode = ERROR_SUCCESS;
93     hReturnValue = hToken;
94 
95     // Don't let the cleanup routine close this.
96     hToken = NULL;
97 
98 Cleanup:
99     if (hToken)
100         CloseHandle(hToken);
101 
102     SetLastError(dwErrorCode);
103     return hReturnValue;
104 }
105