fix returning the new token handle in DuplicateTokenEx
[reactos.git] / reactos / lib / advapi32 / token / token.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: lib/advapi32/token/token.c
5 * PURPOSE: Token functions
6 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
7 * UPDATE HISTORY:
8 * Created 01/11/98
9 */
10
11 #include <advapi32.h>
12
13 #define NDEBUG
14 #include <debug.h>
15
16 /*
17 * @implemented
18 */
19 BOOL STDCALL
20 AdjustTokenGroups (HANDLE TokenHandle,
21 BOOL ResetToDefault,
22 PTOKEN_GROUPS NewState,
23 DWORD BufferLength,
24 PTOKEN_GROUPS PreviousState,
25 PDWORD ReturnLength)
26 {
27 NTSTATUS Status;
28
29 Status = NtAdjustGroupsToken (TokenHandle,
30 ResetToDefault,
31 NewState,
32 BufferLength,
33 PreviousState,
34 (PULONG)ReturnLength);
35 if (!NT_SUCCESS (Status))
36 {
37 SetLastError (RtlNtStatusToDosError (Status));
38 return FALSE;
39 }
40
41 return TRUE;
42 }
43
44
45 /*
46 * @implemented
47 */
48 BOOL STDCALL
49 AdjustTokenPrivileges (HANDLE TokenHandle,
50 BOOL DisableAllPrivileges,
51 PTOKEN_PRIVILEGES NewState,
52 DWORD BufferLength,
53 PTOKEN_PRIVILEGES PreviousState,
54 PDWORD ReturnLength)
55 {
56 NTSTATUS Status;
57
58 Status = NtAdjustPrivilegesToken (TokenHandle,
59 DisableAllPrivileges,
60 NewState,
61 BufferLength,
62 PreviousState,
63 (PULONG)ReturnLength);
64 if (STATUS_NOT_ALL_ASSIGNED == Status)
65 {
66 SetLastError(ERROR_NOT_ALL_ASSIGNED);
67 return TRUE;
68 }
69 if (! NT_SUCCESS(Status))
70 {
71 SetLastError(RtlNtStatusToDosError(Status));
72 return FALSE;
73 }
74
75 SetLastError(ERROR_SUCCESS); /* AdjustTokenPrivileges is documented to do this */
76 return TRUE;
77 }
78
79
80 /*
81 * @implemented
82 */
83 BOOL STDCALL
84 GetTokenInformation (HANDLE TokenHandle,
85 TOKEN_INFORMATION_CLASS TokenInformationClass,
86 LPVOID TokenInformation,
87 DWORD TokenInformationLength,
88 PDWORD ReturnLength)
89 {
90 NTSTATUS Status;
91
92 Status = NtQueryInformationToken (TokenHandle,
93 TokenInformationClass,
94 TokenInformation,
95 TokenInformationLength,
96 (PULONG)ReturnLength);
97 if (!NT_SUCCESS (Status))
98 {
99 SetLastError (RtlNtStatusToDosError (Status));
100 return FALSE;
101 }
102
103 return TRUE;
104 }
105
106
107 /*
108 * @implemented
109 */
110 BOOL STDCALL
111 SetTokenInformation (HANDLE TokenHandle,
112 TOKEN_INFORMATION_CLASS TokenInformationClass,
113 LPVOID TokenInformation,
114 DWORD TokenInformationLength)
115 {
116 NTSTATUS Status;
117
118 Status = NtSetInformationToken (TokenHandle,
119 TokenInformationClass,
120 TokenInformation,
121 TokenInformationLength);
122 if (!NT_SUCCESS (Status))
123 {
124 SetLastError (RtlNtStatusToDosError (Status));
125 return FALSE;
126 }
127
128 return TRUE;
129 }
130
131
132 /*
133 * @implemented
134 */
135 BOOL STDCALL
136 AccessCheck (PSECURITY_DESCRIPTOR pSecurityDescriptor,
137 HANDLE ClientToken,
138 DWORD DesiredAccess,
139 PGENERIC_MAPPING GenericMapping,
140 PPRIVILEGE_SET PrivilegeSet,
141 LPDWORD PrivilegeSetLength,
142 LPDWORD GrantedAccess,
143 LPBOOL AccessStatus)
144 {
145 NTSTATUS Status;
146 NTSTATUS AccessStat;
147
148 Status = NtAccessCheck (pSecurityDescriptor,
149 ClientToken,
150 DesiredAccess,
151 GenericMapping,
152 PrivilegeSet,
153 (PULONG)PrivilegeSetLength,
154 (PACCESS_MASK)GrantedAccess,
155 &AccessStat);
156 if (!NT_SUCCESS (Status))
157 {
158 SetLastError (RtlNtStatusToDosError (Status));
159 return FALSE;
160 }
161
162 if (!NT_SUCCESS (AccessStat))
163 {
164 SetLastError (RtlNtStatusToDosError (Status));
165 *AccessStatus = FALSE;
166 return TRUE;
167 }
168
169 *AccessStatus = TRUE;
170
171 return TRUE;
172 }
173
174
175 /*
176 * @implemented
177 */
178 BOOL STDCALL
179 OpenProcessToken (HANDLE ProcessHandle,
180 DWORD DesiredAccess,
181 PHANDLE TokenHandle)
182 {
183 NTSTATUS Status;
184
185 Status = NtOpenProcessToken (ProcessHandle,
186 DesiredAccess,
187 TokenHandle);
188 if (!NT_SUCCESS (Status))
189 {
190 SetLastError (RtlNtStatusToDosError (Status));
191 return FALSE;
192 }
193
194 return TRUE;
195 }
196
197
198 /*
199 * @implemented
200 */
201 BOOL STDCALL
202 OpenThreadToken (HANDLE ThreadHandle,
203 DWORD DesiredAccess,
204 BOOL OpenAsSelf,
205 PHANDLE TokenHandle)
206 {
207 NTSTATUS Status;
208
209 Status = NtOpenThreadToken (ThreadHandle,
210 DesiredAccess,
211 OpenAsSelf,
212 TokenHandle);
213 if (!NT_SUCCESS(Status))
214 {
215 SetLastError (RtlNtStatusToDosError (Status));
216 return FALSE;
217 }
218
219 return TRUE;
220 }
221
222
223 /*
224 * @implemented
225 */
226 BOOL STDCALL
227 SetThreadToken (IN PHANDLE ThreadHandle OPTIONAL,
228 IN HANDLE TokenHandle)
229 {
230 NTSTATUS Status;
231 HANDLE hThread;
232
233 hThread = ((ThreadHandle != NULL) ? *ThreadHandle : NtCurrentThread());
234
235 Status = NtSetInformationThread (hThread,
236 ThreadImpersonationToken,
237 &TokenHandle,
238 sizeof(HANDLE));
239 if (!NT_SUCCESS(Status))
240 {
241 SetLastError (RtlNtStatusToDosError (Status));
242 return FALSE;
243 }
244
245 return TRUE;
246 }
247
248
249 /*
250 * @implemented
251 */
252 BOOL STDCALL
253 DuplicateTokenEx (IN HANDLE ExistingTokenHandle,
254 IN DWORD dwDesiredAccess,
255 IN LPSECURITY_ATTRIBUTES lpTokenAttributes OPTIONAL,
256 IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
257 IN TOKEN_TYPE TokenType,
258 OUT PHANDLE DuplicateTokenHandle)
259 {
260 OBJECT_ATTRIBUTES ObjectAttributes;
261 NTSTATUS Status;
262 SECURITY_QUALITY_OF_SERVICE Sqos;
263
264 Sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
265 Sqos.ImpersonationLevel = ImpersonationLevel;
266 Sqos.ContextTrackingMode = 0;
267 Sqos.EffectiveOnly = FALSE;
268
269 if (lpTokenAttributes != NULL)
270 {
271 InitializeObjectAttributes(&ObjectAttributes,
272 NULL,
273 lpTokenAttributes->bInheritHandle ? OBJ_INHERIT : 0,
274 NULL,
275 lpTokenAttributes->lpSecurityDescriptor);
276 }
277 else
278 {
279 InitializeObjectAttributes(&ObjectAttributes,
280 NULL,
281 0,
282 NULL,
283 NULL);
284 }
285
286 ObjectAttributes.SecurityQualityOfService = &Sqos;
287
288 Status = NtDuplicateToken (ExistingTokenHandle,
289 dwDesiredAccess,
290 &ObjectAttributes,
291 FALSE,
292 TokenType,
293 DuplicateTokenHandle);
294 if (!NT_SUCCESS(Status))
295 {
296 SetLastError(RtlNtStatusToDosError(Status));
297 return FALSE;
298 }
299
300 return TRUE;
301 }
302
303
304 /*
305 * @implemented
306 */
307 BOOL STDCALL
308 DuplicateToken (IN HANDLE ExistingTokenHandle,
309 IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
310 OUT PHANDLE DuplicateTokenHandle)
311 {
312 return DuplicateTokenEx (ExistingTokenHandle,
313 TOKEN_IMPERSONATE | TOKEN_QUERY,
314 NULL,
315 ImpersonationLevel,
316 TokenImpersonation,
317 DuplicateTokenHandle);
318 }
319
320
321 /*
322 * @implemented
323 */
324 BOOL STDCALL
325 CheckTokenMembership(IN HANDLE ExistingTokenHandle,
326 IN PSID SidToCheck,
327 OUT PBOOL IsMember)
328 {
329 PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
330 ACCESS_MASK GrantedAccess;
331 struct
332 {
333 PRIVILEGE_SET PrivilegeSet;
334 LUID_AND_ATTRIBUTES Privileges[4];
335 } PrivBuffer;
336 ULONG PrivBufferSize = sizeof(PrivBuffer);
337 GENERIC_MAPPING GenericMapping =
338 {
339 STANDARD_RIGHTS_READ,
340 STANDARD_RIGHTS_WRITE,
341 STANDARD_RIGHTS_EXECUTE,
342 STANDARD_RIGHTS_ALL
343 };
344 PACL Dacl;
345 ULONG SidLen;
346 HANDLE hToken = NULL;
347 NTSTATUS Status, AccessStatus;
348
349 /* doesn't return gracefully if IsMember is NULL! */
350 *IsMember = FALSE;
351
352 SidLen = RtlLengthSid(SidToCheck);
353
354 if (ExistingTokenHandle == NULL)
355 {
356 Status = NtOpenThreadToken(NtCurrentThread(),
357 TOKEN_QUERY,
358 FALSE,
359 &hToken);
360
361 if (Status == STATUS_NO_TOKEN)
362 {
363 /* we're not impersonating, open the primary token */
364 Status = NtOpenProcessToken(NtCurrentProcess(),
365 TOKEN_QUERY | TOKEN_DUPLICATE,
366 &hToken);
367 if (NT_SUCCESS(Status))
368 {
369 HANDLE hNewToken = FALSE;
370 BOOL DupRet;
371
372 /* duplicate the primary token to create an impersonation token */
373 DupRet = DuplicateTokenEx(hToken,
374 TOKEN_QUERY | TOKEN_IMPERSONATE,
375 NULL,
376 SecurityImpersonation,
377 TokenImpersonation,
378 &hNewToken);
379
380 NtClose(hToken);
381
382 if (!DupRet)
383 {
384 DPRINT1("Failed to duplicate the primary token!\n");
385 return FALSE;
386 }
387
388 hToken = hNewToken;
389 }
390 }
391
392 if (!NT_SUCCESS(Status))
393 {
394 goto Cleanup;
395 }
396 }
397 else
398 {
399 hToken = ExistingTokenHandle;
400 }
401
402 /* create a security descriptor */
403 SecurityDescriptor = RtlAllocateHeap(RtlGetProcessHeap(),
404 0,
405 sizeof(SECURITY_DESCRIPTOR) +
406 sizeof(ACL) + SidLen +
407 sizeof(ACCESS_ALLOWED_ACE));
408 if (SecurityDescriptor == NULL)
409 {
410 Status = STATUS_INSUFFICIENT_RESOURCES;
411 goto Cleanup;
412 }
413
414 Status = RtlCreateSecurityDescriptor(SecurityDescriptor,
415 SECURITY_DESCRIPTOR_REVISION);
416 if (!NT_SUCCESS(Status))
417 {
418 goto Cleanup;
419 }
420
421 /* set the owner and group */
422 Status = RtlSetOwnerSecurityDescriptor(SecurityDescriptor,
423 SidToCheck,
424 FALSE);
425 if (!NT_SUCCESS(Status))
426 {
427 goto Cleanup;
428 }
429
430 Status = RtlSetGroupSecurityDescriptor(SecurityDescriptor,
431 SidToCheck,
432 FALSE);
433 if (!NT_SUCCESS(Status))
434 {
435 goto Cleanup;
436 }
437
438 /* create the DACL */
439 Dacl = (PACL)(SecurityDescriptor + 1);
440 Status = RtlCreateAcl(Dacl,
441 sizeof(ACL) + SidLen + sizeof(ACCESS_ALLOWED_ACE),
442 ACL_REVISION);
443 if (!NT_SUCCESS(Status))
444 {
445 goto Cleanup;
446 }
447
448 Status = RtlAddAccessAllowedAce(Dacl,
449 ACL_REVISION,
450 0x1,
451 SidToCheck);
452 if (!NT_SUCCESS(Status))
453 {
454 goto Cleanup;
455 }
456
457 /* assign the DACL to the security descriptor */
458 Status = RtlSetDaclSecurityDescriptor(SecurityDescriptor,
459 TRUE,
460 Dacl,
461 FALSE);
462 if (!NT_SUCCESS(Status))
463 {
464 goto Cleanup;
465 }
466
467 /* it's time to perform the access check. Just use _some_ desired access right
468 (same as for the ACE) and see if we're getting it granted. This indicates
469 our SID is a member of the token. We however can't use a generic access
470 right as those aren't mapped and return an error (STATUS_GENERIC_NOT_MAPPED). */
471 Status = NtAccessCheck(SecurityDescriptor,
472 hToken,
473 0x1,
474 &GenericMapping,
475 &PrivBuffer.PrivilegeSet,
476 &PrivBufferSize,
477 &GrantedAccess,
478 &AccessStatus);
479
480 if (NT_SUCCESS(Status) && NT_SUCCESS(AccessStatus) && (GrantedAccess == 0x1))
481 {
482 *IsMember = TRUE;
483 }
484
485 Cleanup:
486 if (hToken != NULL && hToken != ExistingTokenHandle)
487 {
488 NtClose(hToken);
489 }
490
491 if (SecurityDescriptor != NULL)
492 {
493 RtlFreeHeap(RtlGetProcessHeap(),
494 0,
495 SecurityDescriptor);
496 }
497
498 if (!NT_SUCCESS(Status))
499 {
500 SetLastError(RtlNtStatusToDosError(Status));
501 return FALSE;
502 }
503
504 return TRUE;
505 }
506
507
508 /*
509 * @implemented
510 */
511 BOOL STDCALL
512 IsTokenRestricted(HANDLE TokenHandle)
513 {
514 ULONG RetLength;
515 PTOKEN_GROUPS lpGroups;
516 NTSTATUS Status;
517 BOOL Ret = FALSE;
518
519 /* determine the required buffer size and allocate enough memory to read the
520 list of restricted SIDs */
521
522 Status = NtQueryInformationToken(TokenHandle,
523 TokenRestrictedSids,
524 NULL,
525 0,
526 &RetLength);
527 if (Status != STATUS_BUFFER_TOO_SMALL)
528 {
529 SetLastError(RtlNtStatusToDosError(Status));
530 return FALSE;
531 }
532
533 AllocAndReadRestrictedSids:
534 lpGroups = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(),
535 0,
536 RetLength);
537 if (lpGroups == NULL)
538 {
539 SetLastError(ERROR_OUTOFMEMORY);
540 return FALSE;
541 }
542
543 /* actually read the list of the restricted SIDs */
544
545 Status = NtQueryInformationToken(TokenHandle,
546 TokenRestrictedSids,
547 lpGroups,
548 RetLength,
549 &RetLength);
550 if (NT_SUCCESS(Status))
551 {
552 Ret = (lpGroups->GroupCount != 0);
553 }
554 else if (Status == STATUS_BUFFER_TOO_SMALL)
555 {
556 /* looks like the token was modified in the meanwhile, let's just try again */
557
558 HeapFree(GetProcessHeap(),
559 0,
560 lpGroups);
561
562 goto AllocAndReadRestrictedSids;
563 }
564 else
565 {
566 SetLastError(RtlNtStatusToDosError(Status));
567 }
568
569 /* free allocated memory */
570
571 HeapFree(GetProcessHeap(),
572 0,
573 lpGroups);
574
575 return Ret;
576 }
577
578 /* EOF */