xref: /reactos/ntoskrnl/se/client.c (revision 8b75dce4)
1 /*
2  * PROJECT:     ReactOS Kernel
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Security client support routines
5  * COPYRIGHT:   Copyright Alex Ionescu <alex@relsoft.net>
6  */
7 
8 /* INCLUDES *******************************************************************/
9 
10 #include <ntoskrnl.h>
11 #define NDEBUG
12 #include <debug.h>
13 
14 /* PRIVATE FUNCTIONS **********************************************************/
15 
16 /**
17  * @brief
18  * Creates a client security context based upon an access token.
19  *
20  * @param[in] Token
21  * A valid token object.
22  *
23  * @param[in] ClientSecurityQos
24  * The Quality of Service (QoS) of a client security context.
25  *
26  * @param[in] ServerIsRemote
27  * If the client is a remote server (TRUE), the function will retrieve the
28  * control information of an access token, that is, we're doing delegation
29  * and that the server isn't local.
30  *
31  * @param[in] TokenType
32  * Type of token.
33  *
34  * @param[in] ThreadEffectiveOnly
35  * If set to TRUE, the client wants that the current thread wants to modify
36  * (enable or disable) privileges and groups.
37  *
38  * @param[in] ImpersonationLevel
39  * Security impersonation level filled in the QoS context.
40  *
41  * @param[out] ClientContext
42  * The returned security client context.
43  *
44  * @return
45  * Returns STATUS_SUCCESS if client security creation has completed successfully.
46  * STATUS_INVALID_PARAMETER is returned if one or more of the parameters are bogus.
47  * STATUS_BAD_IMPERSONATION_LEVEL is returned if the current impersonation level
48  * within QoS context doesn't meet with the conditions required. A failure
49  * NTSTATUS code is returned otherwise.
50  */
51 NTSTATUS
52 NTAPI
SepCreateClientSecurity(_In_ PACCESS_TOKEN Token,_In_ PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,_In_ BOOLEAN ServerIsRemote,_In_ TOKEN_TYPE TokenType,_In_ BOOLEAN ThreadEffectiveOnly,_In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,_Out_ PSECURITY_CLIENT_CONTEXT ClientContext)53 SepCreateClientSecurity(
54     _In_ PACCESS_TOKEN Token,
55     _In_ PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,
56     _In_ BOOLEAN ServerIsRemote,
57     _In_ TOKEN_TYPE TokenType,
58     _In_ BOOLEAN ThreadEffectiveOnly,
59     _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
60     _Out_ PSECURITY_CLIENT_CONTEXT ClientContext)
61 {
62     NTSTATUS Status;
63     PACCESS_TOKEN NewToken;
64     PAGED_CODE();
65 
66     /* Check for bogus impersonation level */
67     if (!VALID_IMPERSONATION_LEVEL(ClientSecurityQos->ImpersonationLevel))
68     {
69         /* Fail the call */
70         return STATUS_INVALID_PARAMETER;
71     }
72 
73     /* Check what kind of token this is */
74     if (TokenType != TokenImpersonation)
75     {
76         /* On a primary token, if we do direct access, copy the flag from the QOS */
77         ClientContext->DirectAccessEffectiveOnly = ClientSecurityQos->EffectiveOnly;
78     }
79     else
80     {
81         /* This is an impersonation token, is the level ok? */
82         if (ClientSecurityQos->ImpersonationLevel > ImpersonationLevel)
83         {
84             /* Nope, fail */
85             return STATUS_BAD_IMPERSONATION_LEVEL;
86         }
87 
88         /* Is the level too low, or are we doing something other than delegation remotely */
89         if ((ImpersonationLevel == SecurityAnonymous) ||
90             (ImpersonationLevel == SecurityIdentification) ||
91             ((ServerIsRemote) && (ImpersonationLevel != SecurityDelegation)))
92         {
93             /* Fail the call */
94             return STATUS_BAD_IMPERSONATION_LEVEL;
95         }
96 
97         /* Pick either the thread setting or the QOS setting */
98         ClientContext->DirectAccessEffectiveOnly =
99             ((ThreadEffectiveOnly) || (ClientSecurityQos->EffectiveOnly)) ? TRUE : FALSE;
100     }
101 
102     /* Is this static tracking */
103     if (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING)
104     {
105         /* Do not use direct access and make a copy */
106         ClientContext->DirectlyAccessClientToken = FALSE;
107         Status = SeCopyClientToken(Token,
108                                    ClientSecurityQos->ImpersonationLevel,
109                                    KernelMode,
110                                    &NewToken);
111         if (!NT_SUCCESS(Status))
112             return Status;
113     }
114     else
115     {
116         /* Use direct access and check if this is local */
117         ClientContext->DirectlyAccessClientToken = TRUE;
118         if (ServerIsRemote)
119         {
120             /* We are doing delegation, so make a copy of the control data */
121             SeGetTokenControlInformation(Token,
122                                          &ClientContext->ClientTokenControl);
123         }
124 
125         /* Keep the same token */
126         NewToken = Token;
127     }
128 
129     /* Fill out the context and return success */
130     ClientContext->SecurityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
131     ClientContext->SecurityQos.ImpersonationLevel = ClientSecurityQos->ImpersonationLevel;
132     ClientContext->SecurityQos.ContextTrackingMode = ClientSecurityQos->ContextTrackingMode;
133     ClientContext->SecurityQos.EffectiveOnly = ClientSecurityQos->EffectiveOnly;
134     ClientContext->ServerIsRemote = ServerIsRemote;
135     ClientContext->ClientToken = NewToken;
136     return STATUS_SUCCESS;
137 }
138 
139 /* PUBLIC FUNCTIONS ***********************************************************/
140 
141 /**
142  * @brief
143  * Creates a client security context.
144  *
145  * @param[in] Thread
146  * Thread object of the client where impersonation has to begin.
147  *
148  * @param[in] Qos
149  * Quality of service to specify what kind of impersonation to be done.
150  *
151  * @param[in] RemoteClient
152  * If set to TRUE, the client that we're going to impersonate is remote.
153  *
154  * @param[out] ClientContext
155  * The returned security client context.
156  *
157  * @return
158  * See SepCreateClientSecurity.
159  */
160 NTSTATUS
161 NTAPI
SeCreateClientSecurity(_In_ PETHREAD Thread,_In_ PSECURITY_QUALITY_OF_SERVICE Qos,_In_ BOOLEAN RemoteClient,_Out_ PSECURITY_CLIENT_CONTEXT ClientContext)162 SeCreateClientSecurity(
163     _In_ PETHREAD Thread,
164     _In_ PSECURITY_QUALITY_OF_SERVICE Qos,
165     _In_ BOOLEAN RemoteClient,
166     _Out_ PSECURITY_CLIENT_CONTEXT ClientContext)
167 {
168     TOKEN_TYPE TokenType;
169     BOOLEAN ThreadEffectiveOnly;
170     SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
171     PACCESS_TOKEN Token;
172     NTSTATUS Status;
173     PAGED_CODE();
174 
175     /* Reference the correct token */
176     Token = PsReferenceEffectiveToken(Thread,
177                                       &TokenType,
178                                       &ThreadEffectiveOnly,
179                                       &ImpersonationLevel);
180 
181     /* Create client security from it */
182     Status = SepCreateClientSecurity(Token,
183                                      Qos,
184                                      RemoteClient,
185                                      TokenType,
186                                      ThreadEffectiveOnly,
187                                      ImpersonationLevel,
188                                      ClientContext);
189 
190     /* Check if we failed or static tracking was used */
191     if (!(NT_SUCCESS(Status)) || (Qos->ContextTrackingMode == SECURITY_STATIC_TRACKING))
192     {
193         /* Dereference our copy since it's not being used */
194         ObDereferenceObject(Token);
195     }
196 
197     /* Return status */
198     return Status;
199 }
200 
201 /**
202  * @brief
203  * Creates a client security context based upon the captured security
204  * subject context.
205  *
206  * @param[in] SubjectContext
207  * The captured subject context where client security is to be created
208  * from.
209  *
210  * @param[in] ClientSecurityQos
211  * Quality of service to specify what kind of impersonation to be done.
212  *
213  * @param[in] ServerIsRemote
214  * If set to TRUE, the client that we're going to impersonate is remote.
215  *
216  * @param[out] ClientContext
217  * The returned security client context.
218  *
219  * @return
220  * See SepCreateClientSecurity.
221  */
222 NTSTATUS
223 NTAPI
SeCreateClientSecurityFromSubjectContext(_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,_In_ PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,_In_ BOOLEAN ServerIsRemote,_Out_ PSECURITY_CLIENT_CONTEXT ClientContext)224 SeCreateClientSecurityFromSubjectContext(
225     _In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
226     _In_ PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,
227     _In_ BOOLEAN ServerIsRemote,
228     _Out_ PSECURITY_CLIENT_CONTEXT ClientContext)
229 {
230     PACCESS_TOKEN Token;
231     NTSTATUS Status;
232     PAGED_CODE();
233 
234     /* Get the right token and reference it */
235     Token = SeQuerySubjectContextToken(SubjectContext);
236     ObReferenceObject(Token);
237 
238     /* Create the context */
239     Status = SepCreateClientSecurity(Token,
240                                      ClientSecurityQos,
241                                      ServerIsRemote,
242                                      SubjectContext->ClientToken ?
243                                      TokenImpersonation : TokenPrimary,
244                                      FALSE,
245                                      SubjectContext->ImpersonationLevel,
246                                      ClientContext);
247 
248     /* Check if we failed or static tracking was used */
249     if (!(NT_SUCCESS(Status)) ||
250         (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING))
251     {
252         /* Dereference our copy since it's not being used */
253         ObDereferenceObject(Token);
254     }
255 
256     /* Return status */
257     return Status;
258 }
259 
260 /**
261  * @brief
262  * Extended function that impersonates a client.
263  *
264  * @param[in] ClientContext
265  * A valid client context.
266  *
267  * @param[in] ServerThread
268  * The thread where impersonation is to be done.
269  *
270  * @return
271  * STATUS_SUCCESS is returned if the calling thread successfully impersonates
272  * the client. A failure NTSTATUS code is returned otherwise.
273  */
274 NTSTATUS
275 NTAPI
SeImpersonateClientEx(_In_ PSECURITY_CLIENT_CONTEXT ClientContext,_In_opt_ PETHREAD ServerThread)276 SeImpersonateClientEx(
277     _In_ PSECURITY_CLIENT_CONTEXT ClientContext,
278     _In_opt_ PETHREAD ServerThread)
279 {
280     BOOLEAN EffectiveOnly;
281     PAGED_CODE();
282 
283     /* Check if direct access is requested */
284     if (!ClientContext->DirectlyAccessClientToken)
285     {
286         /* No, so get the flag from QOS */
287         EffectiveOnly = ClientContext->SecurityQos.EffectiveOnly;
288     }
289     else
290     {
291         /* Yes, so see if direct access should be effective only */
292         EffectiveOnly = ClientContext->DirectAccessEffectiveOnly;
293     }
294 
295     /* Use the current thread if one was not passed */
296     if (!ServerThread) ServerThread = PsGetCurrentThread();
297 
298     /* Call the lower layer routine */
299     return PsImpersonateClient(ServerThread,
300                                ClientContext->ClientToken,
301                                TRUE,
302                                EffectiveOnly,
303                                ClientContext->SecurityQos.ImpersonationLevel);
304 }
305 
306 /**
307  * @brief
308  * Impersonates a client user.
309  *
310  * @param[in] ClientContext
311  * A valid client context.
312  *
313  * @param[in] ServerThread
314  * The thread where impersonation is to be done.
315  * *
316  * @return
317  * Nothing.
318  */
319 VOID
320 NTAPI
SeImpersonateClient(_In_ PSECURITY_CLIENT_CONTEXT ClientContext,_In_opt_ PETHREAD ServerThread)321 SeImpersonateClient(
322     _In_ PSECURITY_CLIENT_CONTEXT ClientContext,
323     _In_opt_ PETHREAD ServerThread)
324 {
325     PAGED_CODE();
326 
327     /* Call the new API */
328     SeImpersonateClientEx(ClientContext, ServerThread);
329 }
330 
331 /* EOF */
332