[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 #if 0
116 PRTL_ACQUIRE_STATE State;
117 NTSTATUS Status, IntStatus;
118 ULONG ReturnLength, i, OldSize;
119 SECURITY_QUALITY_OF_SERVICE Sqos;
120 OBJECT_ATTRIBUTES ObjectAttributes;
121 HANDLE ImpersonationToken = 0, ProcessToken;
122
123 DPRINT("RtlAcquirePrivilege(%p, %u, %u, %p)\n", Privilege, NumPriv, Flags, ReturnedState);
124
125 /* Validate flags */
126 if (Flags & ~(RTL_ACQUIRE_PRIVILEGE_PROCESS | RTL_ACQUIRE_PRIVILEGE_IMPERSONATE))
127 {
128 return STATUS_INVALID_PARAMETER;
129 }
130
131 /* If user wants to acquire privileges for the process, we have to impersonate him */
132 if (Flags & RTL_ACQUIRE_PRIVILEGE_PROCESS)
133 {
134 Flags |= RTL_ACQUIRE_PRIVILEGE_IMPERSONATE;
135 }
136
137 /* Allocate enough memory to hold: old privileges (fixed buffer size, might not be enough)
138 * new privileges (big enough, after old privileges memory area)
139 */
140 State = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTL_ACQUIRE_STATE) + sizeof(TOKEN_PRIVILEGES) +
141 NumPriv * sizeof(LUID_AND_ATTRIBUTES));
142 if (!State)
143 {
144 return STATUS_NO_MEMORY;
145 }
146
147 /* Only zero a bit of the memory (will be faster that way) */
148 State->Token = 0;
149 State->OldImpersonationToken = 0;
150 State->Flags = 0;
151 State->OldPrivileges = NULL;
152
153 /* Check whether we have already an active impersonation */
154 if (NtCurrentTeb()->IsImpersonating)
155 {
156 /* Check whether we want to impersonate */
157 if (Flags & RTL_ACQUIRE_PRIVILEGE_IMPERSONATE)
158 {
159 /* That's all fine, just get the token.
160 * We need access for: adjust (obvious...) but also
161 * query, to be able to query old privileges
162 */
163 Status = RtlpOpenThreadToken(TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &State->Token);
164 if (!NT_SUCCESS(Status))
165 {
166 RtlFreeHeap(RtlGetProcessHeap(), 0, State);
167 return Status;
168 }
169 }
170 else
171 {
172 /* Otherwise, we have to temporary disable active impersonation.
173 * Get previous impersonation token to save it
174 */
175 Status = RtlpOpenThreadToken(TOKEN_IMPERSONATE, &State->OldImpersonationToken);
176 if (!NT_SUCCESS(Status))
177 {
178 RtlFreeHeap(RtlGetProcessHeap(), 0, State);
179 return Status;
180 }
181
182 /* Remember the fact we had an active impersonation */
183 State->Flags |= RTL_ACQUIRE_PRIVILEGE_IMPERSONATE;
184
185 /* Revert impersonation (ie, give 0 as handle) */
186 Status = ZwSetInformationThread(NtCurrentThread(),
187 ThreadImpersonationToken,
188 &ImpersonationToken,
189 sizeof(HANDLE));
190 }
191 }
192
193 /* If we have no token yet (which is likely) */
194 if (!State->Token)
195 {
196 /* If we are asked to use process, then do */
197 if (Flags & RTL_ACQUIRE_PRIVILEGE_PROCESS)
198 {
199 Status = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
200 &State->Token);
201 if (!NT_SUCCESS(Status))
202 {
203 goto Cleanup;
204 }
205 }
206 else
207 {
208 /* Otherwise, we have to impersonate.
209 * Open token for duplication
210 */
211 Status = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_DUPLICATE, &ProcessToken);
212
213 InitializeObjectAttributes(&ObjectAttributes,
214 NULL,
215 0,
216 NULL,
217 NULL);
218
219 ObjectAttributes.SecurityQualityOfService = &Sqos;
220 Sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
221 Sqos.ImpersonationLevel = SecurityDelegation;
222 Sqos.ContextTrackingMode = 1;
223 Sqos.EffectiveOnly = FALSE;
224
225 /* Duplicate */
226 Status = ZwDuplicateToken(ProcessToken,
227 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_IMPERSONATE,
228 &ObjectAttributes,
229 FALSE,
230 TokenImpersonation,
231 &ImpersonationToken);
232 if (!NT_SUCCESS(Status))
233 {
234 ZwClose(ProcessToken);
235 goto Cleanup;
236 }
237
238 /* Assign our duplicated token to current thread */
239 Status = ZwSetInformationThread(NtCurrentThread(),
240 ThreadImpersonationToken,
241 &ImpersonationToken,
242 sizeof(HANDLE));
243 if (!NT_SUCCESS(Status))
244 {
245 ZwClose(ImpersonationToken);
246 ZwClose(ProcessToken);
247 goto Cleanup;
248 }
249
250 /* Save said token */
251 State->Token = ImpersonationToken;
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 + 1024);
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 }
318 } while (FALSE);
319
320 DPRINT("RtlAcquirePrivilege succeed!\n");
321
322 return Status;
323
324 Cleanup:
325 /* If we allocated our own buffer for old privileges, release it */
326 if (State->OldPrivileges && (PVOID)State->OldPrivBuffer != (PVOID)State->OldPrivileges)
327 {
328 RtlFreeHeap(RtlGetProcessHeap(), 0, State->OldPrivileges);
329 }
330
331 /* Do we have to restore previously active impersonation? */
332 if (State->Flags & RTL_ACQUIRE_PRIVILEGE_IMPERSONATE)
333 {
334 IntStatus = ZwSetInformationThread(NtCurrentThread(), ThreadImpersonationToken,
335 &State->OldImpersonationToken, sizeof(HANDLE));
336 /* If this ever happens, we're in a really bad situation... */
337 if (!NT_SUCCESS(IntStatus))
338 {
339 RtlRaiseStatus(IntStatus);
340 }
341 }
342
343 /* Release token */
344 if (State->Token)
345 {
346 ZwClose(State->Token);
347 }
348
349 /* And free our state buffer */
350 RtlFreeHeap(RtlGetProcessHeap(), 0, State);
351
352 DPRINT("RtlAcquirePrivilege() failed with status: %lx\n", Status);
353
354 return Status;
355 #else
356 UNIMPLEMENTED;
357 return STATUS_NOT_IMPLEMENTED;
358 #endif
359 }
360
361 /*
362 * @implemented
363 */
364 VOID
365 NTAPI
366 RtlReleasePrivilege(IN PVOID ReturnedState)
367 {
368 #if 0
369 NTSTATUS Status;
370 PRTL_ACQUIRE_STATE State = (PRTL_ACQUIRE_STATE)ReturnedState;
371
372 DPRINT("RtlReleasePrivilege(%p)\n", ReturnedState);
373
374 /* If we had an active impersonation before we acquired privileges */
375 if (State->Flags & RTL_ACQUIRE_PRIVILEGE_IMPERSONATE)
376 {
377 /* Restore it for the current thread */
378 Status = ZwSetInformationThread(NtCurrentThread(), ThreadImpersonationToken,
379 &State->OldImpersonationToken, sizeof(HANDLE));
380 if (!NT_SUCCESS(Status))
381 {
382 RtlRaiseStatus(Status);
383 }
384
385 /* And close the token if needed */
386 if (State->OldImpersonationToken)
387 ZwClose(State->OldImpersonationToken);
388 }
389 else
390 {
391 /* Otherwise, restore old state */
392 ZwAdjustPrivilegesToken(State->Token, FALSE,
393 State->OldPrivileges, 0, NULL, NULL);
394
395 }
396
397 /* If we used a different buffer for old privileges, just free it */
398 if ((PVOID)State->OldPrivBuffer != (PVOID)State->OldPrivileges)
399 {
400 DPRINT("Releasing old privileges: %p\n", State->OldPrivileges);
401 RtlFreeHeap(RtlGetProcessHeap(), 0, State->OldPrivileges);
402 }
403
404 /* Release token and free state */
405 ZwClose(State->Token);
406 RtlFreeHeap(RtlGetProcessHeap(), 0, State);
407 #else
408 UNIMPLEMENTED;
409 #endif
410 }
411
412 /*
413 * @implemented
414 */
415 NTSTATUS
416 NTAPI
417 RtlAdjustPrivilege(IN ULONG Privilege,
418 IN BOOLEAN Enable,
419 IN BOOLEAN CurrentThread,
420 OUT PBOOLEAN Enabled)
421 {
422 TOKEN_PRIVILEGES NewState;
423 TOKEN_PRIVILEGES OldState;
424 ULONG ReturnLength;
425 HANDLE TokenHandle;
426 NTSTATUS Status;
427
428 PAGED_CODE_RTL();
429
430 DPRINT("RtlAdjustPrivilege() called\n");
431
432 if (CurrentThread)
433 {
434 Status = ZwOpenThreadToken(NtCurrentThread(),
435 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
436 FALSE,
437 &TokenHandle);
438 }
439 else
440 {
441 Status = ZwOpenProcessToken(NtCurrentProcess(),
442 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
443 &TokenHandle);
444 }
445
446 if (!NT_SUCCESS (Status))
447 {
448 DPRINT1("Retrieving token handle failed (Status %lx)\n", Status);
449 return Status;
450 }
451
452 OldState.PrivilegeCount = 1;
453
454 NewState.PrivilegeCount = 1;
455 NewState.Privileges[0].Luid.LowPart = Privilege;
456 NewState.Privileges[0].Luid.HighPart = 0;
457 NewState.Privileges[0].Attributes = (Enable) ? SE_PRIVILEGE_ENABLED : 0;
458
459 Status = ZwAdjustPrivilegesToken(TokenHandle,
460 FALSE,
461 &NewState,
462 sizeof(TOKEN_PRIVILEGES),
463 &OldState,
464 &ReturnLength);
465 ZwClose (TokenHandle);
466 if (Status == STATUS_NOT_ALL_ASSIGNED)
467 {
468 DPRINT1("Failed to assign all privileges\n");
469 return STATUS_PRIVILEGE_NOT_HELD;
470 }
471
472 if (!NT_SUCCESS(Status))
473 {
474 DPRINT1("NtAdjustPrivilegesToken() failed (Status %lx)\n", Status);
475 return Status;
476 }
477
478 if (OldState.PrivilegeCount == 0)
479 {
480 *Enabled = Enable;
481 }
482 else
483 {
484 *Enabled = (OldState.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED);
485 }
486
487 DPRINT("RtlAdjustPrivilege() done\n");
488
489 return STATUS_SUCCESS;
490 }