xref: /reactos/sdk/lib/rtl/priv.c (revision 5100859e)
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 }
486