[RTL]
[reactos.git] / reactos / lib / rtl / priv.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: lib/rtl/priv.c
5 * PURPOSE: Security related functions and Security Objects
6 * PROGRAMMER: Eric Kohl
7 * Pierre Schweitzer (pierre@reactos.org)
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <rtl.h>
13
14 #define NDEBUG
15 #include <debug.h>
16
17 /* FUNCTIONS ***************************************************************/
18
19 /*
20 * @implemented
21 */
22 NTSTATUS
23 NTAPI
24 RtlpOpenThreadToken(IN ACCESS_MASK DesiredAccess,
25 OUT PHANDLE TokenHandle)
26 {
27 NTSTATUS Status;
28
29 Status = ZwOpenThreadToken(NtCurrentThread(), DesiredAccess,
30 TRUE, TokenHandle);
31 if (!NT_SUCCESS(Status))
32 {
33 Status = ZwOpenThreadToken(NtCurrentThread(), DesiredAccess,
34 FALSE, TokenHandle);
35 }
36
37 return Status;
38 }
39
40 /*
41 * @implemented
42 */
43 NTSTATUS
44 NTAPI
45 RtlImpersonateSelf(IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
46 {
47 HANDLE ProcessToken;
48 HANDLE ImpersonationToken;
49 NTSTATUS Status;
50 OBJECT_ATTRIBUTES ObjAttr;
51 SECURITY_QUALITY_OF_SERVICE Sqos;
52
53 PAGED_CODE_RTL();
54
55 Status = ZwOpenProcessToken(NtCurrentProcess(),
56 TOKEN_DUPLICATE,
57 &ProcessToken);
58 if (!NT_SUCCESS(Status))
59 {
60 DPRINT1("NtOpenProcessToken() failed (Status %lx)\n", Status);
61 return Status;
62 }
63
64 Sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
65 Sqos.ImpersonationLevel = ImpersonationLevel;
66 Sqos.ContextTrackingMode = 0;
67 Sqos.EffectiveOnly = FALSE;
68
69 InitializeObjectAttributes(&ObjAttr,
70 NULL,
71 0,
72 NULL,
73 NULL);
74
75 ObjAttr.SecurityQualityOfService = &Sqos;
76
77 Status = ZwDuplicateToken(ProcessToken,
78 TOKEN_IMPERSONATE,
79 &ObjAttr,
80 Sqos.EffectiveOnly, /* why both here _and_ in Sqos? */
81 TokenImpersonation,
82 &ImpersonationToken);
83 if (!NT_SUCCESS(Status))
84 {
85 DPRINT1("NtDuplicateToken() failed (Status %lx)\n", Status);
86 NtClose(ProcessToken);
87 return Status;
88 }
89
90 Status = ZwSetInformationThread(NtCurrentThread(),
91 ThreadImpersonationToken,
92 &ImpersonationToken,
93 sizeof(HANDLE));
94 if (!NT_SUCCESS(Status))
95 {
96 DPRINT1("NtSetInformationThread() failed (Status %lx)\n", Status);
97 }
98
99 ZwClose(ImpersonationToken);
100 ZwClose(ProcessToken);
101
102 return Status;
103 }
104
105 /*
106 * @implemented
107 */
108 NTSTATUS
109 NTAPI
110 RtlAcquirePrivilege(IN PULONG Privilege,
111 IN ULONG NumPriv,
112 IN ULONG Flags,
113 OUT PVOID *ReturnedState)
114 {
115 PRTL_ACQUIRE_STATE State;
116 NTSTATUS Status, IntStatus;
117 ULONG ReturnLength, i, OldSize;
118 SECURITY_QUALITY_OF_SERVICE Sqos;
119 OBJECT_ATTRIBUTES ObjectAttributes;
120 HANDLE ImpersonationToken = 0, ProcessToken;
121
122 DPRINT("RtlAcquirePrivilege(%p, %u, %u, %p)\n", Privilege, NumPriv, Flags, ReturnedState);
123
124 /* Validate flags */
125 if (Flags & ~(RTL_ACQUIRE_PRIVILEGE_PROCESS | RTL_ACQUIRE_PRIVILEGE_IMPERSONATE))
126 {
127 return STATUS_INVALID_PARAMETER;
128 }
129
130 /* If user wants to acquire privileges for the process, we have to impersonate him */
131 if (Flags & RTL_ACQUIRE_PRIVILEGE_PROCESS)
132 {
133 Flags |= RTL_ACQUIRE_PRIVILEGE_IMPERSONATE;
134 }
135
136 /* Allocate enough memory to hold: old privileges (fixed buffer size, might not be enough)
137 * new privileges (big enough, after old privileges memory area)
138 */
139 State = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTL_ACQUIRE_STATE) + sizeof(TOKEN_PRIVILEGES) +
140 (NumPriv - ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES));
141 if (!State)
142 {
143 return STATUS_NO_MEMORY;
144 }
145
146 /* Only zero a bit of the memory (will be faster that way) */
147 State->Token = 0;
148 State->OldImpersonationToken = 0;
149 State->Flags = 0;
150 State->OldPrivileges = NULL;
151
152 /* Check whether we have already an active impersonation */
153 if (NtCurrentTeb()->IsImpersonating)
154 {
155 /* Check whether we want to impersonate */
156 if (Flags & RTL_ACQUIRE_PRIVILEGE_IMPERSONATE)
157 {
158 /* That's all fine, just get the token.
159 * We need access for: adjust (obvious...) but also
160 * query, to be able to query old privileges
161 */
162 Status = RtlpOpenThreadToken(TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &State->Token);
163 if (!NT_SUCCESS(Status))
164 {
165 RtlFreeHeap(RtlGetProcessHeap(), 0, State);
166 return Status;
167 }
168 }
169 else
170 {
171 /* Otherwise, we have to temporary disable active impersonation.
172 * Get previous impersonation token to save it
173 */
174 Status = RtlpOpenThreadToken(TOKEN_IMPERSONATE, &State->OldImpersonationToken);
175 if (!NT_SUCCESS(Status))
176 {
177 RtlFreeHeap(RtlGetProcessHeap(), 0, State);
178 return Status;
179 }
180
181 /* Remember the fact we had an active impersonation */
182 State->Flags |= RTL_ACQUIRE_PRIVILEGE_IMPERSONATE;
183
184 /* Revert impersonation (ie, give 0 as handle) */
185 Status = ZwSetInformationThread(NtCurrentThread(),
186 ThreadImpersonationToken,
187 &ImpersonationToken,
188 sizeof(HANDLE));
189 }
190 }
191
192 /* If we have no token yet (which is likely) */
193 if (!State->Token)
194 {
195 /* If we are asked to use process, then do */
196 if (Flags & RTL_ACQUIRE_PRIVILEGE_PROCESS)
197 {
198 Status = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
199 &State->Token);
200 if (!NT_SUCCESS(Status))
201 {
202 goto Cleanup;
203 }
204 }
205 else
206 {
207 /* Otherwise, we have to impersonate.
208 * Open token for duplication
209 */
210 Status = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_DUPLICATE, &ProcessToken);
211
212 InitializeObjectAttributes(&ObjectAttributes,
213 NULL,
214 0,
215 NULL,
216 NULL);
217
218 ObjectAttributes.SecurityQualityOfService = &Sqos;
219 Sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
220 Sqos.ImpersonationLevel = SecurityDelegation;
221 Sqos.ContextTrackingMode = 1;
222 Sqos.EffectiveOnly = FALSE;
223
224 /* Duplicate */
225 Status = ZwDuplicateToken(ProcessToken,
226 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_IMPERSONATE,
227 &ObjectAttributes,
228 FALSE,
229 TokenImpersonation,
230 &ImpersonationToken);
231 if (!NT_SUCCESS(Status))
232 {
233 ZwClose(ProcessToken);
234 goto Cleanup;
235 }
236
237 /* Assign our duplicated token to current thread */
238 Status = ZwSetInformationThread(NtCurrentThread(),
239 ThreadImpersonationToken,
240 &ImpersonationToken,
241 sizeof(HANDLE));
242 if (!NT_SUCCESS(Status))
243 {
244 ZwClose(ImpersonationToken);
245 ZwClose(ProcessToken);
246 goto Cleanup;
247 }
248
249 /* Save said token and the fact we have impersonated */
250 State->Token = ImpersonationToken;
251 State->Flags |= RTL_ACQUIRE_PRIVILEGE_IMPERSONATE;
252
253 ZwClose(ProcessToken);
254 }
255 }
256
257 /* Properly set the privileges pointers:
258 * OldPrivileges points to the static memory in struct (= OldPrivBuffer)
259 * NewPrivileges points to the dynamic memory after OldPrivBuffer
260 * There's NO overflow risks (OldPrivileges is always used with its size)
261 */
262 State->OldPrivileges = (PTOKEN_PRIVILEGES)State->OldPrivBuffer;
263 State->NewPrivileges = (PTOKEN_PRIVILEGES)(State->OldPrivBuffer + (sizeof(State->OldPrivBuffer) / sizeof(State->OldPrivBuffer[0])));
264
265 /* Assign all the privileges to be acquired */
266 State->NewPrivileges->PrivilegeCount = NumPriv;
267 for (i = 0; i < NumPriv; ++i)
268 {
269 State->NewPrivileges->Privileges[i].Luid.LowPart = Privilege[i];
270 State->NewPrivileges->Privileges[i].Luid.HighPart = 0;
271 State->NewPrivileges->Privileges[i].Attributes = SE_PRIVILEGE_ENABLED;
272 }
273
274 /* Start privileges adjustements */
275 OldSize = sizeof(State->OldPrivBuffer);
276 do
277 {
278 ReturnLength = sizeof(State->OldPrivBuffer);
279 Status = ZwAdjustPrivilegesToken(State->Token, FALSE, State->NewPrivileges,
280 OldSize, State->OldPrivileges, &ReturnLength);
281 /* This is returned when OldPrivileges buffer is too small */
282 if (Status == STATUS_BUFFER_TOO_SMALL)
283 {
284 /* Try to allocate a new one, big enough to hold data */
285 State->OldPrivileges = RtlAllocateHeap(RtlGetProcessHeap(), 0, ReturnLength);
286 if (State->OldPrivileges)
287 {
288 DPRINT("Allocated old privileges: %p\n", State->OldPrivileges);
289 OldSize = ReturnLength;
290 continue;
291 }
292 else
293 {
294 /* If we failed, properly set status: we failed because of the lack of memory */
295 Status = STATUS_NO_MEMORY;
296 }
297 }
298
299 /* If we failed to assign at least one privilege */
300 if (Status == STATUS_NOT_ALL_ASSIGNED)
301 {
302 /* If there was actually only one privilege to acquire, use more accurate status */
303 if (NumPriv == 1)
304 {
305 Status = STATUS_PRIVILEGE_NOT_HELD;
306 }
307 }
308
309 /* Fail if needed, otherwise return our state to caller */
310 if (!NT_SUCCESS(Status))
311 {
312 goto Cleanup;
313 }
314 else
315 {
316 *ReturnedState = State;
317 break;
318 }
319 } while (TRUE);
320
321 DPRINT("RtlAcquirePrivilege succeed!\n");
322
323 return Status;
324
325 Cleanup:
326 /* If we allocated our own buffer for old privileges, release it */
327 if (State->OldPrivileges && (PVOID)State->OldPrivBuffer != (PVOID)State->OldPrivileges)
328 {
329 RtlFreeHeap(RtlGetProcessHeap(), 0, State->OldPrivileges);
330 }
331
332 /* Do we have to restore previously active impersonation? */
333 if (State->Flags & RTL_ACQUIRE_PRIVILEGE_IMPERSONATE)
334 {
335 IntStatus = ZwSetInformationThread(NtCurrentThread(), ThreadImpersonationToken,
336 &State->OldImpersonationToken, sizeof(HANDLE));
337 /* If this ever happens, we're in a really bad situation... */
338 if (!NT_SUCCESS(IntStatus))
339 {
340 RtlRaiseStatus(IntStatus);
341 }
342 }
343
344 /* Release token */
345 if (State->Token)
346 {
347 ZwClose(State->Token);
348 }
349
350 /* And free our state buffer */
351 RtlFreeHeap(RtlGetProcessHeap(), 0, State);
352
353 DPRINT("RtlAcquirePrivilege() failed with status: %lx\n", Status);
354
355 return Status;
356 }
357
358 /*
359 * @implemented
360 */
361 VOID
362 NTAPI
363 RtlReleasePrivilege(IN PVOID ReturnedState)
364 {
365 NTSTATUS Status;
366 PRTL_ACQUIRE_STATE State = (PRTL_ACQUIRE_STATE)ReturnedState;
367
368 DPRINT("RtlReleasePrivilege(%p)\n", ReturnedState);
369
370 /* If we had an active impersonation before we acquired privileges
371 * Or if we have impersonated, quit it
372 */
373 if (State->Flags & RTL_ACQUIRE_PRIVILEGE_IMPERSONATE)
374 {
375 /* Restore it for the current thread */
376 Status = ZwSetInformationThread(NtCurrentThread(), ThreadImpersonationToken,
377 &State->OldImpersonationToken, sizeof(HANDLE));
378 if (!NT_SUCCESS(Status))
379 {
380 RtlRaiseStatus(Status);
381 }
382
383 /* And close the token if needed */
384 if (State->OldImpersonationToken)
385 ZwClose(State->OldImpersonationToken);
386 }
387 else
388 {
389 /* Otherwise, restore old state */
390 ZwAdjustPrivilegesToken(State->Token, FALSE,
391 State->OldPrivileges, 0, NULL, NULL);
392
393 }
394
395 /* If we used a different buffer for old privileges, just free it */
396 if ((PVOID)State->OldPrivBuffer != (PVOID)State->OldPrivileges)
397 {
398 DPRINT("Releasing old privileges: %p\n", State->OldPrivileges);
399 RtlFreeHeap(RtlGetProcessHeap(), 0, State->OldPrivileges);
400 }
401
402 /* Release token and free state */
403 ZwClose(State->Token);
404 RtlFreeHeap(RtlGetProcessHeap(), 0, State);
405 }
406
407 /*
408 * @implemented
409 */
410 NTSTATUS
411 NTAPI
412 RtlAdjustPrivilege(IN ULONG Privilege,
413 IN BOOLEAN Enable,
414 IN BOOLEAN CurrentThread,
415 OUT PBOOLEAN Enabled)
416 {
417 TOKEN_PRIVILEGES NewState;
418 TOKEN_PRIVILEGES OldState;
419 ULONG ReturnLength;
420 HANDLE TokenHandle;
421 NTSTATUS Status;
422
423 PAGED_CODE_RTL();
424
425 DPRINT("RtlAdjustPrivilege() called\n");
426
427 if (CurrentThread)
428 {
429 Status = ZwOpenThreadToken(NtCurrentThread(),
430 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
431 FALSE,
432 &TokenHandle);
433 }
434 else
435 {
436 Status = ZwOpenProcessToken(NtCurrentProcess(),
437 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
438 &TokenHandle);
439 }
440
441 if (!NT_SUCCESS (Status))
442 {
443 DPRINT1("Retrieving token handle failed (Status %lx)\n", Status);
444 return Status;
445 }
446
447 OldState.PrivilegeCount = 1;
448
449 NewState.PrivilegeCount = 1;
450 NewState.Privileges[0].Luid.LowPart = Privilege;
451 NewState.Privileges[0].Luid.HighPart = 0;
452 NewState.Privileges[0].Attributes = (Enable) ? SE_PRIVILEGE_ENABLED : 0;
453
454 Status = ZwAdjustPrivilegesToken(TokenHandle,
455 FALSE,
456 &NewState,
457 sizeof(TOKEN_PRIVILEGES),
458 &OldState,
459 &ReturnLength);
460 ZwClose (TokenHandle);
461 if (Status == STATUS_NOT_ALL_ASSIGNED)
462 {
463 DPRINT1("Failed to assign all privileges\n");
464 return STATUS_PRIVILEGE_NOT_HELD;
465 }
466
467 if (!NT_SUCCESS(Status))
468 {
469 DPRINT1("NtAdjustPrivilegesToken() failed (Status %lx)\n", Status);
470 return Status;
471 }
472
473 if (OldState.PrivilegeCount == 0)
474 {
475 *Enabled = Enable;
476 }
477 else
478 {
479 *Enabled = (OldState.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED);
480 }
481
482 DPRINT("RtlAdjustPrivilege() done\n");
483
484 return STATUS_SUCCESS;
485 }