xref: /reactos/sdk/lib/rtl/priv.c (revision 49da1cdb)
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