1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Security privileges support
5 * COPYRIGHT: Copyright Alex Ionescu <alex@relsoft.net>
6 * Copyright Timo Kreuzer <timo.kreuzer@reactos.org>
7 * Copyright Eric Kohl
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS ********************************************************************/
17
18 #define SE_MAXIMUM_PRIVILEGE_LIMIT 0x3C
19
20 #define CONST_LUID(x1, x2) {x1, x2}
21 const LUID SeCreateTokenPrivilege = CONST_LUID(SE_CREATE_TOKEN_PRIVILEGE, 0);
22 const LUID SeAssignPrimaryTokenPrivilege = CONST_LUID(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, 0);
23 const LUID SeLockMemoryPrivilege = CONST_LUID(SE_LOCK_MEMORY_PRIVILEGE, 0);
24 const LUID SeIncreaseQuotaPrivilege = CONST_LUID(SE_INCREASE_QUOTA_PRIVILEGE, 0);
25 const LUID SeUnsolicitedInputPrivilege = CONST_LUID(6, 0);
26 const LUID SeTcbPrivilege = CONST_LUID(SE_TCB_PRIVILEGE, 0);
27 const LUID SeSecurityPrivilege = CONST_LUID(SE_SECURITY_PRIVILEGE, 0);
28 const LUID SeTakeOwnershipPrivilege = CONST_LUID(SE_TAKE_OWNERSHIP_PRIVILEGE, 0);
29 const LUID SeLoadDriverPrivilege = CONST_LUID(SE_LOAD_DRIVER_PRIVILEGE, 0);
30 const LUID SeSystemProfilePrivilege = CONST_LUID(SE_SYSTEM_PROFILE_PRIVILEGE, 0);
31 const LUID SeSystemtimePrivilege = CONST_LUID(SE_SYSTEMTIME_PRIVILEGE, 0);
32 const LUID SeProfileSingleProcessPrivilege = CONST_LUID(SE_PROF_SINGLE_PROCESS_PRIVILEGE, 0);
33 const LUID SeIncreaseBasePriorityPrivilege = CONST_LUID(SE_INC_BASE_PRIORITY_PRIVILEGE, 0);
34 const LUID SeCreatePagefilePrivilege = CONST_LUID(SE_CREATE_PAGEFILE_PRIVILEGE, 0);
35 const LUID SeCreatePermanentPrivilege = CONST_LUID(SE_CREATE_PERMANENT_PRIVILEGE, 0);
36 const LUID SeBackupPrivilege = CONST_LUID(SE_BACKUP_PRIVILEGE, 0);
37 const LUID SeRestorePrivilege = CONST_LUID(SE_RESTORE_PRIVILEGE, 0);
38 const LUID SeShutdownPrivilege = CONST_LUID(SE_SHUTDOWN_PRIVILEGE, 0);
39 const LUID SeDebugPrivilege = CONST_LUID(SE_DEBUG_PRIVILEGE, 0);
40 const LUID SeAuditPrivilege = CONST_LUID(SE_AUDIT_PRIVILEGE, 0);
41 const LUID SeSystemEnvironmentPrivilege = CONST_LUID(SE_SYSTEM_ENVIRONMENT_PRIVILEGE, 0);
42 const LUID SeChangeNotifyPrivilege = CONST_LUID(SE_CHANGE_NOTIFY_PRIVILEGE, 0);
43 const LUID SeRemoteShutdownPrivilege = CONST_LUID(SE_REMOTE_SHUTDOWN_PRIVILEGE, 0);
44 const LUID SeUndockPrivilege = CONST_LUID(SE_UNDOCK_PRIVILEGE, 0);
45 const LUID SeSyncAgentPrivilege = CONST_LUID(SE_SYNC_AGENT_PRIVILEGE, 0);
46 const LUID SeEnableDelegationPrivilege = CONST_LUID(SE_ENABLE_DELEGATION_PRIVILEGE, 0);
47 const LUID SeManageVolumePrivilege = CONST_LUID(SE_MANAGE_VOLUME_PRIVILEGE, 0);
48 const LUID SeImpersonatePrivilege = CONST_LUID(SE_IMPERSONATE_PRIVILEGE, 0);
49 const LUID SeCreateGlobalPrivilege = CONST_LUID(SE_CREATE_GLOBAL_PRIVILEGE, 0);
50 const LUID SeTrustedCredmanPrivilege = CONST_LUID(SE_TRUSTED_CREDMAN_ACCESS_PRIVILEGE, 0);
51 const LUID SeRelabelPrivilege = CONST_LUID(SE_RELABEL_PRIVILEGE, 0);
52 const LUID SeIncreaseWorkingSetPrivilege = CONST_LUID(SE_INC_WORKING_SET_PRIVILEGE, 0);
53 const LUID SeTimeZonePrivilege = CONST_LUID(SE_TIME_ZONE_PRIVILEGE, 0);
54 const LUID SeCreateSymbolicLinkPrivilege = CONST_LUID(SE_CREATE_SYMBOLIC_LINK_PRIVILEGE, 0);
55
56
57 /* PRIVATE FUNCTIONS **********************************************************/
58
59 /**
60 * @brief
61 * Initializes the privileges during the startup phase of the security
62 * manager module. This function serves as a placeholder as it currently
63 * does nothing.
64 *
65 * @return
66 * Nothing.
67 */
68 CODE_SEG("INIT")
69 VOID
70 NTAPI
SepInitPrivileges(VOID)71 SepInitPrivileges(VOID)
72 {
73
74 }
75
76 /**
77 * @brief
78 * Checks the privileges pointed by Privileges array argument if they exist and
79 * match with the privileges from an access token.
80 *
81 * @param[in] Token
82 * An access token where privileges are to be checked.
83 *
84 * @param[in] Privileges
85 * An array of privileges with attributes used as checking indicator for
86 * the function.
87 *
88 * @param[in] PrivilegeCount
89 * The total number count of privileges in the array.
90 *
91 * @param[in] PrivilegeControl
92 * Privilege control bit mask to determine if we should check all the
93 * privileges based on the number count of privileges or not.
94 *
95 * @param[in] PreviousMode
96 * Processor level access mode.
97 *
98 * @return
99 * Returns TRUE if the required privileges exist and that they do match.
100 * Otherwise the functions returns FALSE.
101 */
102 BOOLEAN
103 NTAPI
SepPrivilegeCheck(_In_ PTOKEN Token,_In_ PLUID_AND_ATTRIBUTES Privileges,_In_ ULONG PrivilegeCount,_In_ ULONG PrivilegeControl,_In_ KPROCESSOR_MODE PreviousMode)104 SepPrivilegeCheck(
105 _In_ PTOKEN Token,
106 _In_ PLUID_AND_ATTRIBUTES Privileges,
107 _In_ ULONG PrivilegeCount,
108 _In_ ULONG PrivilegeControl,
109 _In_ KPROCESSOR_MODE PreviousMode)
110 {
111 ULONG i;
112 ULONG j;
113 ULONG Required;
114
115 DPRINT("SepPrivilegeCheck() called\n");
116
117 PAGED_CODE();
118
119 if (PreviousMode == KernelMode)
120 return TRUE;
121
122 /* Get the number of privileges that are required to match */
123 Required = (PrivilegeControl & PRIVILEGE_SET_ALL_NECESSARY) ? PrivilegeCount : 1;
124
125 /* Acquire a shared token lock */
126 SepAcquireTokenLockShared(Token);
127
128 /* Loop all requested privileges until we found the required ones */
129 for (i = 0; i < PrivilegeCount; i++)
130 {
131 /* Loop the privileges of the token */
132 for (j = 0; j < Token->PrivilegeCount; j++)
133 {
134 /* Check if the LUIDs match */
135 if (RtlEqualLuid(&Token->Privileges[j].Luid, &Privileges[i].Luid))
136 {
137 DPRINT("Found privilege. Attributes: %lx\n",
138 Token->Privileges[j].Attributes);
139
140 /* Check if the privilege is enabled */
141 if (Token->Privileges[j].Attributes & SE_PRIVILEGE_ENABLED)
142 {
143 Privileges[i].Attributes |= SE_PRIVILEGE_USED_FOR_ACCESS;
144 Required--;
145
146 /* Check if we have found all privileges */
147 if (Required == 0)
148 {
149 /* We're done! */
150 SepReleaseTokenLock(Token);
151 return TRUE;
152 }
153 }
154
155 /* Leave the inner loop */
156 break;
157 }
158 }
159 }
160
161 /* Release the token lock */
162 SepReleaseTokenLock(Token);
163
164 /* When we reached this point, we did not find all privileges */
165 ASSERT(Required > 0);
166 return FALSE;
167 }
168
169 /**
170 * @brief
171 * Checks only single privilege based upon the privilege pointed by a LUID and
172 * if it matches with the one from an access token.
173 *
174 * @param[in] PrivilegeValue
175 * The privilege to be checked.
176 *
177 * @param[in] Token
178 * An access token where its privilege is to be checked against the one
179 * provided by the caller.
180 *
181 * @param[in] PreviousMode
182 * Processor level access mode.
183 *
184 * @return
185 * Returns TRUE if the required privilege exists and that it matches
186 * with the one from the access token, FALSE otherwise.
187 */
188 BOOLEAN
189 NTAPI
SepSinglePrivilegeCheck(_In_ LUID PrivilegeValue,_In_ PTOKEN Token,_In_ KPROCESSOR_MODE PreviousMode)190 SepSinglePrivilegeCheck(
191 _In_ LUID PrivilegeValue,
192 _In_ PTOKEN Token,
193 _In_ KPROCESSOR_MODE PreviousMode)
194 {
195 LUID_AND_ATTRIBUTES Privilege;
196 PAGED_CODE();
197 ASSERT(!RtlEqualLuid(&PrivilegeValue, &SeTcbPrivilege));
198
199 Privilege.Luid = PrivilegeValue;
200 Privilege.Attributes = SE_PRIVILEGE_ENABLED;
201 return SepPrivilegeCheck(Token,
202 &Privilege,
203 1,
204 PRIVILEGE_SET_ALL_NECESSARY,
205 PreviousMode);
206 }
207
208 /**
209 * @brief
210 * Checks the security policy and returns a set of privileges
211 * based upon the said security policy context.
212 *
213 * @param[in,out] DesiredAccess
214 * The desired access right mask.
215 *
216 * @param[in,out] GrantedAccess
217 * The granted access rights masks. The rights are granted depending
218 * on the desired access rights requested by the calling thread.
219 *
220 * @param[in] SubjectContext
221 * Security subject context. If the caller supplies one, the access token
222 * supplied by the caller will be assigned to one of client or primary tokens
223 * of the subject context in question.
224 *
225 * @param[in] Token
226 * An access token.
227 *
228 * @param[out] OutPrivilegeSet
229 * An array set of privileges to be reported to the caller, if the actual
230 * calling thread wants such set of privileges in the first place.
231 *
232 * @param[in] PreviousMode
233 * Processor level access mode.
234 *
235 * @return
236 * Returns STATUS_PRIVILEGE_NOT_HELD if the respective operations have succeeded
237 * without problems. STATUS_PRIVILEGE_NOT_HELD is returned if the access token
238 * doesn't have SeSecurityPrivilege privilege to warrant ACCESS_SYSTEM_SECURITY
239 * access right. STATUS_INSUFFICIENT_RESOURCES is returned if we failed
240 * to allocate block of memory pool for the array set of privileges.
241 */
242 NTSTATUS
243 NTAPI
SePrivilegePolicyCheck(_Inout_ PACCESS_MASK DesiredAccess,_Inout_ PACCESS_MASK GrantedAccess,_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,_In_ PTOKEN Token,_Out_opt_ PPRIVILEGE_SET * OutPrivilegeSet,_In_ KPROCESSOR_MODE PreviousMode)244 SePrivilegePolicyCheck(
245 _Inout_ PACCESS_MASK DesiredAccess,
246 _Inout_ PACCESS_MASK GrantedAccess,
247 _In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
248 _In_ PTOKEN Token,
249 _Out_opt_ PPRIVILEGE_SET *OutPrivilegeSet,
250 _In_ KPROCESSOR_MODE PreviousMode)
251 {
252 SIZE_T PrivilegeSize;
253 PPRIVILEGE_SET PrivilegeSet;
254 ULONG PrivilegeCount = 0, Index = 0;
255 ACCESS_MASK AccessMask = 0;
256 PAGED_CODE();
257
258 /* Check if we have a security subject context */
259 if (SubjectContext != NULL)
260 {
261 /* Check if there is a client impersonation token */
262 if (SubjectContext->ClientToken != NULL)
263 Token = SubjectContext->ClientToken;
264 else
265 Token = SubjectContext->PrimaryToken;
266 }
267
268 /* Check if the caller wants ACCESS_SYSTEM_SECURITY access */
269 if (*DesiredAccess & ACCESS_SYSTEM_SECURITY)
270 {
271 /* Do the privilege check */
272 if (SepSinglePrivilegeCheck(SeSecurityPrivilege, Token, PreviousMode))
273 {
274 /* Remember this access flag */
275 AccessMask |= ACCESS_SYSTEM_SECURITY;
276 PrivilegeCount++;
277 }
278 else
279 {
280 return STATUS_PRIVILEGE_NOT_HELD;
281 }
282 }
283
284 /* Check if the caller wants WRITE_OWNER access */
285 if (*DesiredAccess & WRITE_OWNER)
286 {
287 /* Do the privilege check */
288 if (SepSinglePrivilegeCheck(SeTakeOwnershipPrivilege, Token, PreviousMode))
289 {
290 /* Remember this access flag */
291 AccessMask |= WRITE_OWNER;
292 PrivilegeCount++;
293 }
294 }
295
296 /* Update the access masks */
297 *GrantedAccess |= AccessMask;
298 *DesiredAccess &= ~AccessMask;
299
300 /* Does the caller want a privilege set? */
301 if (OutPrivilegeSet != NULL)
302 {
303 /* Do we have any privileges to report? */
304 if (PrivilegeCount > 0)
305 {
306 /* Calculate size and allocate the structure */
307 PrivilegeSize = FIELD_OFFSET(PRIVILEGE_SET, Privilege[PrivilegeCount]);
308 PrivilegeSet = ExAllocatePoolWithTag(PagedPool, PrivilegeSize, TAG_PRIVILEGE_SET);
309 *OutPrivilegeSet = PrivilegeSet;
310 if (PrivilegeSet == NULL)
311 {
312 return STATUS_INSUFFICIENT_RESOURCES;
313 }
314
315 PrivilegeSet->PrivilegeCount = PrivilegeCount;
316 PrivilegeSet->Control = 0;
317
318 if (AccessMask & WRITE_OWNER)
319 {
320 PrivilegeSet->Privilege[Index].Luid = SeTakeOwnershipPrivilege;
321 PrivilegeSet->Privilege[Index].Attributes = SE_PRIVILEGE_USED_FOR_ACCESS;
322 Index++;
323 }
324
325 if (AccessMask & ACCESS_SYSTEM_SECURITY)
326 {
327 PrivilegeSet->Privilege[Index].Luid = SeSecurityPrivilege;
328 PrivilegeSet->Privilege[Index].Attributes = SE_PRIVILEGE_USED_FOR_ACCESS;
329 }
330 }
331 else
332 {
333 /* No privileges, no structure */
334 *OutPrivilegeSet = NULL;
335 }
336 }
337
338 return STATUS_SUCCESS;
339 }
340
341 /**
342 * @brief
343 * Checks a single privilege and performs an audit
344 * against a privileged service based on a security subject
345 * context.
346 *
347 * @param[in] DesiredAccess
348 * Security subject context used for privileged service
349 * auditing.
350 *
351 * @param[in] PreviousMode
352 * Processor level access mode.
353 *
354 * @return
355 * Returns TRUE if service auditing and privilege checking
356 * tests have succeeded, FALSE otherwise.
357 */
358 BOOLEAN
359 NTAPI
SeCheckAuditPrivilege(_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,_In_ KPROCESSOR_MODE PreviousMode)360 SeCheckAuditPrivilege(
361 _In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
362 _In_ KPROCESSOR_MODE PreviousMode)
363 {
364 PRIVILEGE_SET PrivilegeSet;
365 BOOLEAN Result;
366 PAGED_CODE();
367
368 /* Initialize the privilege set with the single privilege */
369 PrivilegeSet.PrivilegeCount = 1;
370 PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
371 PrivilegeSet.Privilege[0].Luid = SeAuditPrivilege;
372 PrivilegeSet.Privilege[0].Attributes = 0;
373
374 /* Check against the primary token! */
375 Result = SepPrivilegeCheck(SubjectContext->PrimaryToken,
376 &PrivilegeSet.Privilege[0],
377 1,
378 PRIVILEGE_SET_ALL_NECESSARY,
379 PreviousMode);
380
381 if (PreviousMode != KernelMode)
382 {
383 SePrivilegedServiceAuditAlarm(NULL,
384 SubjectContext,
385 &PrivilegeSet,
386 Result);
387 }
388
389 return Result;
390 }
391
392 /**
393 * @brief
394 * Captures a LUID with attributes structure. This function is mainly
395 * tied in the context of privileges.
396 *
397 * @param[in] Src
398 * Source of a valid LUID with attributes structure.
399 *
400 * @param[in] PrivilegeCount
401 * Count number of privileges to be captured.
402 *
403 * @param[in] PreviousMode
404 * Processor level access mode.
405 *
406 * @param[in] AllocatedMem
407 * If specified, the function will use this allocated block memory
408 * buffer for the captured LUID and attributes structure. Otherwise
409 * the function will automatically allocate some buffer for it.
410 *
411 * @param[in] AllocatedLength
412 * The length of the buffer, pointed by AllocatedMem.
413 *
414 * @param[in] PoolType
415 * Pool type of the memory allocation.
416 *
417 * @param[in] CaptureIfKernel
418 * If set to TRUE, the capturing is done in the kernel itself.
419 * FALSE if the capturing is done in a kernel mode driver instead.
420 *
421 * @param[out] Dest
422 * The captured LUID with attributes buffer.
423 *
424 * @param[in,out] Length
425 * The length of the captured privileges count.
426 *
427 * @return
428 * Returns STATUS_SUCCESS if the LUID and attributes array
429 * has been captured successfully. STATUS_INSUFFICIENT_RESOURCES is returned
430 * if memory pool allocation for the captured buffer has failed.
431 * STATUS_BUFFER_TOO_SMALL is returned if the buffer size is less than the
432 * required size. STATUS_INVALID_PARAMETER is returned if the caller has
433 * submitted a privilege count that exceeds that maximum threshold the
434 * kernel can permit, for the purpose to avoid an integer overflow.
435 */
436 NTSTATUS
437 NTAPI
SeCaptureLuidAndAttributesArray(_In_ PLUID_AND_ATTRIBUTES Src,_In_ ULONG PrivilegeCount,_In_ KPROCESSOR_MODE PreviousMode,_In_opt_ PLUID_AND_ATTRIBUTES AllocatedMem,_In_opt_ ULONG AllocatedLength,_In_ POOL_TYPE PoolType,_In_ BOOLEAN CaptureIfKernel,_Out_ PLUID_AND_ATTRIBUTES * Dest,_Inout_ PULONG Length)438 SeCaptureLuidAndAttributesArray(
439 _In_ PLUID_AND_ATTRIBUTES Src,
440 _In_ ULONG PrivilegeCount,
441 _In_ KPROCESSOR_MODE PreviousMode,
442 _In_opt_ PLUID_AND_ATTRIBUTES AllocatedMem,
443 _In_opt_ ULONG AllocatedLength,
444 _In_ POOL_TYPE PoolType,
445 _In_ BOOLEAN CaptureIfKernel,
446 _Out_ PLUID_AND_ATTRIBUTES *Dest,
447 _Inout_ PULONG Length)
448 {
449 ULONG BufferSize;
450 NTSTATUS Status = STATUS_SUCCESS;
451
452 PAGED_CODE();
453
454 if (PrivilegeCount == 0)
455 {
456 *Dest = 0;
457 *Length = 0;
458 return STATUS_SUCCESS;
459 }
460
461 if (PrivilegeCount > SE_MAXIMUM_PRIVILEGE_LIMIT)
462 {
463 return STATUS_INVALID_PARAMETER;
464 }
465
466 if (PreviousMode == KernelMode && !CaptureIfKernel)
467 {
468 *Dest = Src;
469 return STATUS_SUCCESS;
470 }
471
472 BufferSize = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
473 *Length = ROUND_UP(BufferSize, 4); /* round up to a 4 byte alignment */
474
475 /* probe the buffer */
476 if (PreviousMode != KernelMode)
477 {
478 _SEH2_TRY
479 {
480 ProbeForRead(Src,
481 BufferSize,
482 sizeof(ULONG));
483 }
484 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
485 {
486 /* Return the exception code */
487 _SEH2_YIELD(return _SEH2_GetExceptionCode());
488 }
489 _SEH2_END;
490 }
491
492 /* allocate enough memory or check if the provided buffer is
493 large enough to hold the array */
494 if (AllocatedMem != NULL)
495 {
496 if (AllocatedLength < BufferSize)
497 {
498 return STATUS_BUFFER_TOO_SMALL;
499 }
500
501 *Dest = AllocatedMem;
502 }
503 else
504 {
505 *Dest = ExAllocatePoolWithTag(PoolType,
506 BufferSize,
507 TAG_LUID);
508 if (*Dest == NULL)
509 {
510 return STATUS_INSUFFICIENT_RESOURCES;
511 }
512 }
513
514 /* copy the array to the buffer */
515 _SEH2_TRY
516 {
517 RtlCopyMemory(*Dest,
518 Src,
519 BufferSize);
520 }
521 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
522 {
523 Status = _SEH2_GetExceptionCode();
524 }
525 _SEH2_END;
526
527 if (!NT_SUCCESS(Status) && AllocatedMem == NULL)
528 {
529 ExFreePoolWithTag(*Dest, TAG_LUID);
530 }
531
532 return Status;
533 }
534
535 /**
536 * @brief
537 * Releases a LUID with attributes structure.
538 *
539 * @param[in] Privilege
540 * Array of a LUID and attributes that represents a privilege.
541 *
542 * @param[in] PreviousMode
543 * Processor level access mode.
544 *
545 * @param[in] CaptureIfKernel
546 * If set to TRUE, the releasing is done in the kernel itself.
547 * FALSE if the releasing is done in a kernel mode driver instead.
548 *
549 * @return
550 * Nothing.
551 */
552 VOID
553 NTAPI
SeReleaseLuidAndAttributesArray(_In_ PLUID_AND_ATTRIBUTES Privilege,_In_ KPROCESSOR_MODE PreviousMode,_In_ BOOLEAN CaptureIfKernel)554 SeReleaseLuidAndAttributesArray(
555 _In_ PLUID_AND_ATTRIBUTES Privilege,
556 _In_ KPROCESSOR_MODE PreviousMode,
557 _In_ BOOLEAN CaptureIfKernel)
558 {
559 PAGED_CODE();
560
561 if (Privilege != NULL &&
562 (PreviousMode != KernelMode || CaptureIfKernel))
563 {
564 ExFreePoolWithTag(Privilege, TAG_LUID);
565 }
566 }
567
568 /* PUBLIC FUNCTIONS ***********************************************************/
569
570 /**
571 * @brief
572 * Appends additional privileges.
573 *
574 * @param[in] AccessState
575 * Access request to append.
576 *
577 * @param[in] Privileges
578 * Set of new privileges to append.
579 *
580 * @return
581 * Returns STATUS_SUCCESS if the privileges have been successfully
582 * appended. Otherwise STATUS_INSUFFICIENT_RESOURCES is returned,
583 * indicating that pool allocation has failed for the buffer to hold
584 * the new set of privileges.
585 */
586 NTSTATUS
587 NTAPI
SeAppendPrivileges(_Inout_ PACCESS_STATE AccessState,_In_ PPRIVILEGE_SET Privileges)588 SeAppendPrivileges(
589 _Inout_ PACCESS_STATE AccessState,
590 _In_ PPRIVILEGE_SET Privileges)
591 {
592 PAUX_ACCESS_DATA AuxData;
593 ULONG OldPrivilegeSetSize;
594 ULONG NewPrivilegeSetSize;
595 PPRIVILEGE_SET PrivilegeSet;
596
597 PAGED_CODE();
598
599 /* Get the Auxiliary Data */
600 AuxData = AccessState->AuxData;
601
602 /* Calculate the size of the old privilege set */
603 OldPrivilegeSetSize = sizeof(PRIVILEGE_SET) +
604 (AuxData->PrivilegesUsed->PrivilegeCount - 1) * sizeof(LUID_AND_ATTRIBUTES);
605
606 if (AuxData->PrivilegesUsed->PrivilegeCount +
607 Privileges->PrivilegeCount > INITIAL_PRIVILEGE_COUNT)
608 {
609 /* Calculate the size of the new privilege set */
610 NewPrivilegeSetSize = OldPrivilegeSetSize +
611 Privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
612
613 /* Allocate a new privilege set */
614 PrivilegeSet = ExAllocatePoolWithTag(PagedPool,
615 NewPrivilegeSetSize,
616 TAG_PRIVILEGE_SET);
617 if (PrivilegeSet == NULL)
618 return STATUS_INSUFFICIENT_RESOURCES;
619
620 /* Copy original privileges from the acess state */
621 RtlCopyMemory(PrivilegeSet,
622 AuxData->PrivilegesUsed,
623 OldPrivilegeSetSize);
624
625 /* Append privileges from the privilege set*/
626 RtlCopyMemory((PVOID)((ULONG_PTR)PrivilegeSet + OldPrivilegeSetSize),
627 (PVOID)((ULONG_PTR)Privileges + sizeof(PRIVILEGE_SET) - sizeof(LUID_AND_ATTRIBUTES)),
628 Privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
629
630 /* Adjust the number of privileges in the new privilege set */
631 PrivilegeSet->PrivilegeCount += Privileges->PrivilegeCount;
632
633 /* Free the old privilege set if it was allocated */
634 if (AccessState->PrivilegesAllocated != FALSE)
635 ExFreePoolWithTag(AuxData->PrivilegesUsed, TAG_PRIVILEGE_SET);
636
637 /* Now we are using an allocated privilege set */
638 AccessState->PrivilegesAllocated = TRUE;
639
640 /* Assign the new privileges to the access state */
641 AuxData->PrivilegesUsed = PrivilegeSet;
642 }
643 else
644 {
645 /* Append privileges */
646 RtlCopyMemory((PVOID)((ULONG_PTR)AuxData->PrivilegesUsed + OldPrivilegeSetSize),
647 (PVOID)((ULONG_PTR)Privileges + sizeof(PRIVILEGE_SET) - sizeof(LUID_AND_ATTRIBUTES)),
648 Privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
649
650 /* Adjust the number of privileges in the target privilege set */
651 AuxData->PrivilegesUsed->PrivilegeCount += Privileges->PrivilegeCount;
652 }
653
654 return STATUS_SUCCESS;
655 }
656
657 /**
658 * @brief
659 * Frees a set of privileges.
660 *
661 * @param[in] Privileges
662 * Set of privileges array to be freed.
663 *
664 * @return
665 * Nothing.
666 */
667 VOID
668 NTAPI
SeFreePrivileges(_In_ PPRIVILEGE_SET Privileges)669 SeFreePrivileges(
670 _In_ PPRIVILEGE_SET Privileges)
671 {
672 PAGED_CODE();
673 ExFreePoolWithTag(Privileges, TAG_PRIVILEGE_SET);
674 }
675
676 /**
677 * @brief
678 * Checks if a set of privileges exist and match within a
679 * security subject context.
680 *
681 * @param[in] Privileges
682 * A set of privileges where the check must be performed
683 * against the subject context.
684 *
685 * @param[in] SubjectContext
686 * A subject security context.
687 *
688 * @param[in] PreviousMode
689 * Processor level access mode.
690 *
691 * @return
692 * Returns TRUE if all the privileges do exist and match
693 * with the ones specified by the caller and subject
694 * context, FALSE otherwise.
695 */
696 BOOLEAN
697 NTAPI
SePrivilegeCheck(_In_ PPRIVILEGE_SET Privileges,_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,_In_ KPROCESSOR_MODE PreviousMode)698 SePrivilegeCheck(
699 _In_ PPRIVILEGE_SET Privileges,
700 _In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
701 _In_ KPROCESSOR_MODE PreviousMode)
702 {
703 PACCESS_TOKEN Token = NULL;
704
705 PAGED_CODE();
706
707 if (SubjectContext->ClientToken == NULL)
708 {
709 Token = SubjectContext->PrimaryToken;
710 }
711 else
712 {
713 Token = SubjectContext->ClientToken;
714 if (SubjectContext->ImpersonationLevel < 2)
715 {
716 return FALSE;
717 }
718 }
719
720 return SepPrivilegeCheck(Token,
721 Privileges->Privilege,
722 Privileges->PrivilegeCount,
723 Privileges->Control,
724 PreviousMode);
725 }
726
727 /**
728 * @brief
729 * Checks if a single privilege is present in the context
730 * of the calling thread.
731 *
732 * @param[in] PrivilegeValue
733 * The specific privilege to be checked.
734 *
735 * @param[in] PreviousMode
736 * Processor level access mode.
737 *
738 * @return
739 * Returns TRUE if the privilege is present, FALSE
740 * otherwise.
741 */
742 BOOLEAN
743 NTAPI
SeSinglePrivilegeCheck(_In_ LUID PrivilegeValue,_In_ KPROCESSOR_MODE PreviousMode)744 SeSinglePrivilegeCheck(
745 _In_ LUID PrivilegeValue,
746 _In_ KPROCESSOR_MODE PreviousMode)
747 {
748 SECURITY_SUBJECT_CONTEXT SubjectContext;
749 PRIVILEGE_SET Priv;
750 BOOLEAN Result;
751
752 PAGED_CODE();
753
754 SeCaptureSubjectContext(&SubjectContext);
755
756 Priv.PrivilegeCount = 1;
757 Priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
758 Priv.Privilege[0].Luid = PrivilegeValue;
759 Priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
760
761 Result = SePrivilegeCheck(&Priv,
762 &SubjectContext,
763 PreviousMode);
764
765 if (PreviousMode != KernelMode)
766 {
767 SePrivilegedServiceAuditAlarm(NULL,
768 &SubjectContext,
769 &Priv,
770 Result);
771
772 }
773
774 SeReleaseSubjectContext(&SubjectContext);
775
776 return Result;
777 }
778
779 /**
780 * @brief
781 * Checks a privileged object if such object has
782 * the specific privilege submitted by the caller.
783 *
784 * @param[in] PrivilegeValue
785 * A privilege to be checked against the one from
786 * the object.
787 *
788 * @param[in] ObjectHandle
789 * A handle to any kind of object.
790 *
791 * @param[in] DesiredAccess
792 * Desired access right mask requested by the caller.
793 *
794 * @param[in] PreviousMode
795 * Processor level access mode.
796 *
797 * @return
798 * Returns TRUE if the privilege is present, FALSE
799 * otherwise.
800 */
801 BOOLEAN
802 NTAPI
SeCheckPrivilegedObject(_In_ LUID PrivilegeValue,_In_ HANDLE ObjectHandle,_In_ ACCESS_MASK DesiredAccess,_In_ KPROCESSOR_MODE PreviousMode)803 SeCheckPrivilegedObject(
804 _In_ LUID PrivilegeValue,
805 _In_ HANDLE ObjectHandle,
806 _In_ ACCESS_MASK DesiredAccess,
807 _In_ KPROCESSOR_MODE PreviousMode)
808 {
809 SECURITY_SUBJECT_CONTEXT SubjectContext;
810 PRIVILEGE_SET Priv;
811 BOOLEAN Result;
812
813 PAGED_CODE();
814
815 SeCaptureSubjectContext(&SubjectContext);
816
817 Priv.PrivilegeCount = 1;
818 Priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
819 Priv.Privilege[0].Luid = PrivilegeValue;
820 Priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
821
822 Result = SePrivilegeCheck(&Priv, &SubjectContext, PreviousMode);
823 if (PreviousMode != KernelMode)
824 {
825 #if 0
826 SePrivilegeObjectAuditAlarm(ObjectHandle,
827 &SubjectContext,
828 DesiredAccess,
829 &PrivilegeValue,
830 Result,
831 PreviousMode);
832 #endif
833 }
834
835 SeReleaseSubjectContext(&SubjectContext);
836
837 return Result;
838 }
839
840 /* SYSTEM CALLS ***************************************************************/
841
842 /**
843 * @brief
844 * Checks a client access token if it has the required set of
845 * privileges.
846 *
847 * @param[in] ClientToken
848 * A handle to an access client token.
849 *
850 * @param[in] RequiredPrivileges
851 * A set of required privileges to be checked against the privileges
852 * of the access token.
853 *
854 * @param[out] Result
855 * The result, as a boolean value. If TRUE, the token has all the required
856 * privileges, FALSE otherwise.
857 *
858 * @return
859 * Returns STATUS_SUCCESS if the function has completed successfully.
860 * STATUS_INVALID_PARAMETER is returned if the set array of required
861 * privileges has a bogus number of privileges, that is, the array
862 * has a count of privileges that exceeds the maximum threshold
863 * (or in other words, an integer overflow). A failure NTSTATUS code
864 * is returned otherwise.
865 */
866 NTSTATUS
867 NTAPI
NtPrivilegeCheck(_In_ HANDLE ClientToken,_In_ PPRIVILEGE_SET RequiredPrivileges,_Out_ PBOOLEAN Result)868 NtPrivilegeCheck(
869 _In_ HANDLE ClientToken,
870 _In_ PPRIVILEGE_SET RequiredPrivileges,
871 _Out_ PBOOLEAN Result)
872 {
873 PLUID_AND_ATTRIBUTES Privileges;
874 PTOKEN Token;
875 ULONG PrivilegeCount = 0;
876 ULONG PrivilegeControl = 0;
877 ULONG Length;
878 BOOLEAN CheckResult;
879 KPROCESSOR_MODE PreviousMode;
880 NTSTATUS Status;
881
882 PAGED_CODE();
883
884 PreviousMode = KeGetPreviousMode();
885
886 /* probe the buffers */
887 if (PreviousMode != KernelMode)
888 {
889 _SEH2_TRY
890 {
891 ProbeForWrite(RequiredPrivileges,
892 FIELD_OFFSET(PRIVILEGE_SET,
893 Privilege),
894 sizeof(ULONG));
895
896 PrivilegeCount = RequiredPrivileges->PrivilegeCount;
897 PrivilegeControl = RequiredPrivileges->Control;
898
899 /* Check PrivilegeCount to avoid an integer overflow! */
900 if (FIELD_OFFSET(PRIVILEGE_SET,
901 Privilege[PrivilegeCount]) /
902 sizeof(RequiredPrivileges->Privilege[0]) != PrivilegeCount)
903 {
904 _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
905 }
906
907 /* probe all of the array */
908 ProbeForWrite(RequiredPrivileges,
909 FIELD_OFFSET(PRIVILEGE_SET,
910 Privilege[PrivilegeCount]),
911 sizeof(ULONG));
912
913 ProbeForWriteBoolean(Result);
914 }
915 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
916 {
917 /* Return the exception code */
918 _SEH2_YIELD(return _SEH2_GetExceptionCode());
919 }
920 _SEH2_END;
921 }
922 else
923 {
924 PrivilegeCount = RequiredPrivileges->PrivilegeCount;
925 PrivilegeControl = RequiredPrivileges->Control;
926 }
927
928 /* reference the token and make sure we're
929 not doing an anonymous impersonation */
930 Status = ObReferenceObjectByHandle(ClientToken,
931 TOKEN_QUERY,
932 SeTokenObjectType,
933 PreviousMode,
934 (PVOID*)&Token,
935 NULL);
936 if (!NT_SUCCESS(Status))
937 {
938 return Status;
939 }
940
941 if (Token->TokenType == TokenImpersonation &&
942 Token->ImpersonationLevel < SecurityIdentification)
943 {
944 ObDereferenceObject(Token);
945 return STATUS_BAD_IMPERSONATION_LEVEL;
946 }
947
948 /* capture the privileges */
949 Status = SeCaptureLuidAndAttributesArray(RequiredPrivileges->Privilege,
950 PrivilegeCount,
951 PreviousMode,
952 NULL,
953 0,
954 PagedPool,
955 TRUE,
956 &Privileges,
957 &Length);
958 if (!NT_SUCCESS(Status))
959 {
960 ObDereferenceObject (Token);
961 return Status;
962 }
963
964 CheckResult = SepPrivilegeCheck(Token,
965 Privileges,
966 PrivilegeCount,
967 PrivilegeControl,
968 PreviousMode);
969
970 ObDereferenceObject(Token);
971
972 /* return the array */
973 _SEH2_TRY
974 {
975 RtlCopyMemory(RequiredPrivileges->Privilege,
976 Privileges,
977 PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
978 *Result = CheckResult;
979 Status = STATUS_SUCCESS;
980 }
981 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
982 {
983 Status = _SEH2_GetExceptionCode();
984 }
985 _SEH2_END;
986
987 SeReleaseLuidAndAttributesArray(Privileges,
988 PreviousMode,
989 TRUE);
990
991 return Status;
992 }
993
994 /* EOF */
995