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
RtlpOpenThreadToken(IN ACCESS_MASK DesiredAccess,OUT PHANDLE TokenHandle)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
RtlImpersonateSelf(IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)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
RtlAcquirePrivilege(IN PULONG Privilege,IN ULONG NumPriv,IN ULONG Flags,OUT PVOID * ReturnedState)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
RtlReleasePrivilege(IN PVOID ReturnedState)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 Status = ZwAdjustPrivilegesToken(State->Token, FALSE,
391 State->OldPrivileges, 0, NULL, NULL);
392 if (!NT_SUCCESS(Status))
393 {
394 RtlRaiseStatus(Status);
395 }
396 }
397
398 /* If we used a different buffer for old privileges, just free it */
399 if ((PVOID)State->OldPrivBuffer != (PVOID)State->OldPrivileges)
400 {
401 DPRINT("Releasing old privileges: %p\n", State->OldPrivileges);
402 RtlFreeHeap(RtlGetProcessHeap(), 0, State->OldPrivileges);
403 }
404
405 /* Release token and free state */
406 ZwClose(State->Token);
407 RtlFreeHeap(RtlGetProcessHeap(), 0, State);
408 }
409
410 /*
411 * @implemented
412 */
413 NTSTATUS
414 NTAPI
RtlAdjustPrivilege(IN ULONG Privilege,IN BOOLEAN Enable,IN BOOLEAN CurrentThread,OUT PBOOLEAN Enabled)415 RtlAdjustPrivilege(IN ULONG Privilege,
416 IN BOOLEAN Enable,
417 IN BOOLEAN CurrentThread,
418 OUT PBOOLEAN Enabled)
419 {
420 TOKEN_PRIVILEGES NewState;
421 TOKEN_PRIVILEGES OldState;
422 ULONG ReturnLength;
423 HANDLE TokenHandle;
424 NTSTATUS Status;
425
426 PAGED_CODE_RTL();
427
428 DPRINT("RtlAdjustPrivilege() called\n");
429
430 if (CurrentThread)
431 {
432 Status = ZwOpenThreadToken(NtCurrentThread(),
433 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
434 FALSE,
435 &TokenHandle);
436 }
437 else
438 {
439 Status = ZwOpenProcessToken(NtCurrentProcess(),
440 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
441 &TokenHandle);
442 }
443
444 if (!NT_SUCCESS (Status))
445 {
446 DPRINT1("Retrieving token handle failed (Status %lx)\n", Status);
447 return Status;
448 }
449
450 OldState.PrivilegeCount = 1;
451
452 NewState.PrivilegeCount = 1;
453 NewState.Privileges[0].Luid.LowPart = Privilege;
454 NewState.Privileges[0].Luid.HighPart = 0;
455 NewState.Privileges[0].Attributes = (Enable) ? SE_PRIVILEGE_ENABLED : 0;
456
457 Status = ZwAdjustPrivilegesToken(TokenHandle,
458 FALSE,
459 &NewState,
460 sizeof(TOKEN_PRIVILEGES),
461 &OldState,
462 &ReturnLength);
463 ZwClose (TokenHandle);
464 if (Status == STATUS_NOT_ALL_ASSIGNED)
465 {
466 DPRINT1("Failed to assign all privileges\n");
467 return STATUS_PRIVILEGE_NOT_HELD;
468 }
469
470 if (!NT_SUCCESS(Status))
471 {
472 DPRINT1("NtAdjustPrivilegesToken() failed (Status %lx)\n", Status);
473 return Status;
474 }
475
476 if (OldState.PrivilegeCount == 0)
477 {
478 *Enabled = Enable;
479 }
480 else
481 {
482 *Enabled = (OldState.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED);
483 }
484
485 DPRINT("RtlAdjustPrivilege() done\n");
486
487 return STATUS_SUCCESS;
488 }
489
490 #if (NTDDI_VERSION >= NTDDI_VISTA) || (DLL_EXPORT_VERSION >= _WIN32_WINNT_VISTA)
491
492 /**
493 * @brief
494 * Removes all privileges in the specified access token.
495 *
496 * @param[in] TokenHandle
497 * A handle to the access token that contains the privileges to be removed.
498 *
499 * @param[in] PrivilegesToKeep
500 * A pointer to an array of privilege values (defined as SE_XXX_PRIVILEGE) that specify
501 * the privileges to keep in the token.
502 *
503 * @param[in] PrivilegeCount
504 * Specifies the number of entries in the PrivilegesToKeep array.
505 *
506 * @return
507 * Returns STATUS_SUCCESS if privileges removed successfully.
508 * STATUS_INVALID_PARAMETER is returned if input privilege value greater than
509 * SE_MAX_WELL_KNOWN_PRIVILEGE. STATUS_NOT_ALL_ASSIGNED is returned if The token does
510 * not have one or more of the privileges specified in the PrivilegesToKeep parameter,
511 * and no privileges were removed. A failure NTSTATUS code is returned otherwise.
512 */
513 NTSTATUS
514 NTAPI
515 RtlRemovePrivileges(
516 _In_ HANDLE TokenHandle,
517 _In_reads_opt_(PrivilegeCount) _When_(PrivilegeCount != 0, _Notnull_)
518 PULONG PrivilegesToKeep,
519 _In_ ULONG PrivilegeCount)
520 {
521 NTSTATUS Status;
522 UINT64 PrivilegesToKeepBitmap;
523 ULONG i, ReturnLength;
524 UCHAR Buffer[sizeof(TOKEN_PRIVILEGES) +
525 sizeof(LUID_AND_ATTRIBUTES) * (SE_MAX_WELL_KNOWN_PRIVILEGE - SE_MIN_WELL_KNOWN_PRIVILEGE)];
526 PTOKEN_PRIVILEGES Privileges;
527
528 C_ASSERT(SE_MAX_WELL_KNOWN_PRIVILEGE < 64);
529
530 DPRINT("RtlRemovePrivileges(%p, %p, %u)\n", TokenHandle, PrivilegesToKeep, PrivilegeCount);
531
532 /* Save privileges that should be keep */
533 PrivilegesToKeepBitmap = 0;
534 if (PrivilegeCount)
535 {
536 for (i = 0; i < PrivilegeCount; i++)
537 {
538 if (PrivilegesToKeep[i] > SE_MAX_WELL_KNOWN_PRIVILEGE)
539 {
540 return STATUS_INVALID_PARAMETER;
541 }
542 PrivilegesToKeepBitmap |= (1ULL << PrivilegesToKeep[i]);
543 }
544 }
545
546 /* Get token privileges information */
547 Status = ZwQueryInformationToken(TokenHandle,
548 TokenPrivileges,
549 Buffer,
550 sizeof(Buffer),
551 &ReturnLength);
552 if (!NT_SUCCESS(Status))
553 {
554 return Status;
555 }
556
557 /* Remove all privileges that we don't need to keep */
558 Privileges = (PTOKEN_PRIVILEGES)Buffer;
559 for (i = 0; i < Privileges->PrivilegeCount; i++)
560 {
561 LARGE_INTEGER Privilege = *(LARGE_INTEGER*)&Privileges->Privileges[i].Luid;
562 ASSERT(Privilege.QuadPart <= SE_MAX_WELL_KNOWN_PRIVILEGE);
563 if (PrivilegesToKeepBitmap & (1ULL << Privilege.QuadPart))
564 {
565 PrivilegesToKeepBitmap &= ~(1ULL << Privilege.QuadPart);
566 }
567 else
568 {
569 Privileges->Privileges[i].Attributes = SE_PRIVILEGE_REMOVED;
570 }
571 }
572
573 if (PrivilegesToKeepBitmap)
574 {
575 Status = STATUS_NOT_ALL_ASSIGNED;
576 }
577 else
578 {
579 Status = ZwAdjustPrivilegesToken(TokenHandle,
580 FALSE,
581 (PTOKEN_PRIVILEGES)Buffer,
582 sizeof(Buffer),
583 NULL,
584 NULL);
585 }
586
587 return Status;
588 }
589
590 #endif /* (NTDDI_VERSION >= NTDDI_VISTA) */
591