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