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