/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/se/priv.c * PURPOSE: Security manager * * PROGRAMMERS: No programmer listed. */ /* INCLUDES ******************************************************************/ #include #define NDEBUG #include #if defined (ALLOC_PRAGMA) #pragma alloc_text(INIT, SepInitPrivileges) #endif /* GLOBALS ********************************************************************/ #define CONST_LUID(x1, x2) {x1, x2} const LUID SeCreateTokenPrivilege = CONST_LUID(SE_CREATE_TOKEN_PRIVILEGE, 0); const LUID SeAssignPrimaryTokenPrivilege = CONST_LUID(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, 0); const LUID SeLockMemoryPrivilege = CONST_LUID(SE_LOCK_MEMORY_PRIVILEGE, 0); const LUID SeIncreaseQuotaPrivilege = CONST_LUID(SE_INCREASE_QUOTA_PRIVILEGE, 0); const LUID SeUnsolicitedInputPrivilege = CONST_LUID(6, 0); const LUID SeTcbPrivilege = CONST_LUID(SE_TCB_PRIVILEGE, 0); const LUID SeSecurityPrivilege = CONST_LUID(SE_SECURITY_PRIVILEGE, 0); const LUID SeTakeOwnershipPrivilege = CONST_LUID(SE_TAKE_OWNERSHIP_PRIVILEGE, 0); const LUID SeLoadDriverPrivilege = CONST_LUID(SE_LOAD_DRIVER_PRIVILEGE, 0); const LUID SeSystemProfilePrivilege = CONST_LUID(SE_SYSTEM_PROFILE_PRIVILEGE, 0); const LUID SeSystemtimePrivilege = CONST_LUID(SE_SYSTEMTIME_PRIVILEGE, 0); const LUID SeProfileSingleProcessPrivilege = CONST_LUID(SE_PROF_SINGLE_PROCESS_PRIVILEGE, 0); const LUID SeIncreaseBasePriorityPrivilege = CONST_LUID(SE_INC_BASE_PRIORITY_PRIVILEGE, 0); const LUID SeCreatePagefilePrivilege = CONST_LUID(SE_CREATE_PAGEFILE_PRIVILEGE, 0); const LUID SeCreatePermanentPrivilege = CONST_LUID(SE_CREATE_PERMANENT_PRIVILEGE, 0); const LUID SeBackupPrivilege = CONST_LUID(SE_BACKUP_PRIVILEGE, 0); const LUID SeRestorePrivilege = CONST_LUID(SE_RESTORE_PRIVILEGE, 0); const LUID SeShutdownPrivilege = CONST_LUID(SE_SHUTDOWN_PRIVILEGE, 0); const LUID SeDebugPrivilege = CONST_LUID(SE_DEBUG_PRIVILEGE, 0); const LUID SeAuditPrivilege = CONST_LUID(SE_AUDIT_PRIVILEGE, 0); const LUID SeSystemEnvironmentPrivilege = CONST_LUID(SE_SYSTEM_ENVIRONMENT_PRIVILEGE, 0); const LUID SeChangeNotifyPrivilege = CONST_LUID(SE_CHANGE_NOTIFY_PRIVILEGE, 0); const LUID SeRemoteShutdownPrivilege = CONST_LUID(SE_REMOTE_SHUTDOWN_PRIVILEGE, 0); const LUID SeUndockPrivilege = CONST_LUID(SE_UNDOCK_PRIVILEGE, 0); const LUID SeSyncAgentPrivilege = CONST_LUID(SE_SYNC_AGENT_PRIVILEGE, 0); const LUID SeEnableDelegationPrivilege = CONST_LUID(SE_ENABLE_DELEGATION_PRIVILEGE, 0); const LUID SeManageVolumePrivilege = CONST_LUID(SE_MANAGE_VOLUME_PRIVILEGE, 0); const LUID SeImpersonatePrivilege = CONST_LUID(SE_IMPERSONATE_PRIVILEGE, 0); const LUID SeCreateGlobalPrivilege = CONST_LUID(SE_CREATE_GLOBAL_PRIVILEGE, 0); const LUID SeTrustedCredmanPrivilege = CONST_LUID(SE_TRUSTED_CREDMAN_ACCESS_PRIVILEGE, 0); const LUID SeRelabelPrivilege = CONST_LUID(SE_RELABEL_PRIVILEGE, 0); const LUID SeIncreaseWorkingSetPrivilege = CONST_LUID(SE_INC_WORKING_SET_PRIVILEGE, 0); const LUID SeTimeZonePrivilege = CONST_LUID(SE_TIME_ZONE_PRIVILEGE, 0); const LUID SeCreateSymbolicLinkPrivilege = CONST_LUID(SE_CREATE_SYMBOLIC_LINK_PRIVILEGE, 0); /* PRIVATE FUNCTIONS **********************************************************/ VOID INIT_FUNCTION NTAPI SepInitPrivileges(VOID) { } BOOLEAN NTAPI SepPrivilegeCheck(PTOKEN Token, PLUID_AND_ATTRIBUTES Privileges, ULONG PrivilegeCount, ULONG PrivilegeControl, KPROCESSOR_MODE PreviousMode) { ULONG i; ULONG j; ULONG Required; DPRINT("SepPrivilegeCheck() called\n"); PAGED_CODE(); if (PreviousMode == KernelMode) return TRUE; /* Get the number of privileges that are required to match */ Required = (PrivilegeControl & PRIVILEGE_SET_ALL_NECESSARY) ? PrivilegeCount : 1; /* Loop all requested privileges until we found the required ones */ for (i = 0; i < PrivilegeCount; i++) { /* Loop the privileges of the token */ for (j = 0; j < Token->PrivilegeCount; j++) { /* Check if the LUIDs match */ if (Token->Privileges[j].Luid.LowPart == Privileges[i].Luid.LowPart && Token->Privileges[j].Luid.HighPart == Privileges[i].Luid.HighPart) { DPRINT("Found privilege. Attributes: %lx\n", Token->Privileges[j].Attributes); /* Check if the privilege is enabled */ if (Token->Privileges[j].Attributes & SE_PRIVILEGE_ENABLED) { Privileges[i].Attributes |= SE_PRIVILEGE_USED_FOR_ACCESS; Required--; /* Check if we have found all privileges */ if (Required == 0) { /* We're done! */ return TRUE; } } /* Leave the inner loop */ break; } } } /* When we reached this point, we did not find all privileges */ NT_ASSERT(Required > 0); return FALSE; } NTSTATUS NTAPI SeCaptureLuidAndAttributesArray(PLUID_AND_ATTRIBUTES Src, ULONG PrivilegeCount, KPROCESSOR_MODE PreviousMode, PLUID_AND_ATTRIBUTES AllocatedMem, ULONG AllocatedLength, POOL_TYPE PoolType, BOOLEAN CaptureIfKernel, PLUID_AND_ATTRIBUTES *Dest, PULONG Length) { ULONG BufferSize; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); if (PrivilegeCount == 0) { *Dest = 0; *Length = 0; return STATUS_SUCCESS; } if (PreviousMode == KernelMode && !CaptureIfKernel) { *Dest = Src; return STATUS_SUCCESS; } /* FIXME - check PrivilegeCount for a valid number so we don't cause an integer overflow or exhaust system resources! */ BufferSize = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); *Length = ROUND_UP(BufferSize, 4); /* round up to a 4 byte alignment */ /* probe the buffer */ if (PreviousMode != KernelMode) { _SEH2_TRY { ProbeForRead(Src, BufferSize, sizeof(ULONG)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } /* allocate enough memory or check if the provided buffer is large enough to hold the array */ if (AllocatedMem != NULL) { if (AllocatedLength < BufferSize) { return STATUS_BUFFER_TOO_SMALL; } *Dest = AllocatedMem; } else { *Dest = ExAllocatePool(PoolType, BufferSize); if (*Dest == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } } /* copy the array to the buffer */ _SEH2_TRY { RtlCopyMemory(*Dest, Src, BufferSize); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; if (!NT_SUCCESS(Status) && AllocatedMem == NULL) { ExFreePool(*Dest); } return Status; } VOID NTAPI SeReleaseLuidAndAttributesArray(PLUID_AND_ATTRIBUTES Privilege, KPROCESSOR_MODE PreviousMode, BOOLEAN CaptureIfKernel) { PAGED_CODE(); if (Privilege != NULL && (PreviousMode != KernelMode || CaptureIfKernel)) { ExFreePool(Privilege); } } /* PUBLIC FUNCTIONS ***********************************************************/ /* * @implemented */ NTSTATUS NTAPI SeAppendPrivileges(IN OUT PACCESS_STATE AccessState, IN PPRIVILEGE_SET Privileges) { PAUX_ACCESS_DATA AuxData; ULONG OldPrivilegeSetSize; ULONG NewPrivilegeSetSize; PPRIVILEGE_SET PrivilegeSet; PAGED_CODE(); /* Get the Auxiliary Data */ AuxData = AccessState->AuxData; /* Calculate the size of the old privilege set */ OldPrivilegeSetSize = sizeof(PRIVILEGE_SET) + (AuxData->PrivilegeSet->PrivilegeCount - 1) * sizeof(LUID_AND_ATTRIBUTES); if (AuxData->PrivilegeSet->PrivilegeCount + Privileges->PrivilegeCount > INITIAL_PRIVILEGE_COUNT) { /* Calculate the size of the new privilege set */ NewPrivilegeSetSize = OldPrivilegeSetSize + Privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); /* Allocate a new privilege set */ PrivilegeSet = ExAllocatePool(PagedPool, NewPrivilegeSetSize); if (PrivilegeSet == NULL) return STATUS_INSUFFICIENT_RESOURCES; /* Copy original privileges from the acess state */ RtlCopyMemory(PrivilegeSet, AuxData->PrivilegeSet, OldPrivilegeSetSize); /* Append privileges from the privilege set*/ RtlCopyMemory((PVOID)((ULONG_PTR)PrivilegeSet + OldPrivilegeSetSize), (PVOID)((ULONG_PTR)Privileges + sizeof(PRIVILEGE_SET) - sizeof(LUID_AND_ATTRIBUTES)), Privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); /* Adjust the number of privileges in the new privilege set */ PrivilegeSet->PrivilegeCount += Privileges->PrivilegeCount; /* Free the old privilege set if it was allocated */ if (AccessState->PrivilegesAllocated == TRUE) ExFreePool(AuxData->PrivilegeSet); /* Now we are using an allocated privilege set */ AccessState->PrivilegesAllocated = TRUE; /* Assign the new privileges to the access state */ AuxData->PrivilegeSet = PrivilegeSet; } else { /* Append privileges */ RtlCopyMemory((PVOID)((ULONG_PTR)AuxData->PrivilegeSet + OldPrivilegeSetSize), (PVOID)((ULONG_PTR)Privileges + sizeof(PRIVILEGE_SET) - sizeof(LUID_AND_ATTRIBUTES)), Privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); /* Adjust the number of privileges in the target privilege set */ AuxData->PrivilegeSet->PrivilegeCount += Privileges->PrivilegeCount; } return STATUS_SUCCESS; } /* * @implemented */ VOID NTAPI SeFreePrivileges(IN PPRIVILEGE_SET Privileges) { PAGED_CODE(); ExFreePool(Privileges); } /* * @implemented */ BOOLEAN NTAPI SePrivilegeCheck(PPRIVILEGE_SET Privileges, PSECURITY_SUBJECT_CONTEXT SubjectContext, KPROCESSOR_MODE PreviousMode) { PACCESS_TOKEN Token = NULL; PAGED_CODE(); if (SubjectContext->ClientToken == NULL) { Token = SubjectContext->PrimaryToken; } else { Token = SubjectContext->ClientToken; if (SubjectContext->ImpersonationLevel < 2) { return FALSE; } } return SepPrivilegeCheck(Token, Privileges->Privilege, Privileges->PrivilegeCount, Privileges->Control, PreviousMode); } /* * @implemented */ BOOLEAN NTAPI SeSinglePrivilegeCheck(IN LUID PrivilegeValue, IN KPROCESSOR_MODE PreviousMode) { SECURITY_SUBJECT_CONTEXT SubjectContext; PRIVILEGE_SET Priv; BOOLEAN Result; PAGED_CODE(); SeCaptureSubjectContext(&SubjectContext); Priv.PrivilegeCount = 1; Priv.Control = PRIVILEGE_SET_ALL_NECESSARY; Priv.Privilege[0].Luid = PrivilegeValue; Priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED; Result = SePrivilegeCheck(&Priv, &SubjectContext, PreviousMode); if (PreviousMode != KernelMode) { #if 0 SePrivilegedServiceAuditAlarm(0, &SubjectContext, &PrivilegeValue); #endif } SeReleaseSubjectContext(&SubjectContext); return Result; } BOOLEAN NTAPI SeCheckPrivilegedObject(IN LUID PrivilegeValue, IN HANDLE ObjectHandle, IN ACCESS_MASK DesiredAccess, IN KPROCESSOR_MODE PreviousMode) { SECURITY_SUBJECT_CONTEXT SubjectContext; PRIVILEGE_SET Priv; BOOLEAN Result; PAGED_CODE(); SeCaptureSubjectContext(&SubjectContext); Priv.PrivilegeCount = 1; Priv.Control = PRIVILEGE_SET_ALL_NECESSARY; Priv.Privilege[0].Luid = PrivilegeValue; Priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED; Result = SePrivilegeCheck(&Priv, &SubjectContext, PreviousMode); if (PreviousMode != KernelMode) { #if 0 SePrivilegeObjectAuditAlarm(ObjectHandle, &SubjectContext, DesiredAccess, &PrivilegeValue, Result, PreviousMode); #endif } SeReleaseSubjectContext(&SubjectContext); return Result; } /* SYSTEM CALLS ***************************************************************/ NTSTATUS NTAPI NtPrivilegeCheck(IN HANDLE ClientToken, IN PPRIVILEGE_SET RequiredPrivileges, OUT PBOOLEAN Result) { PLUID_AND_ATTRIBUTES Privileges; PTOKEN Token; ULONG PrivilegeCount = 0; ULONG PrivilegeControl = 0; ULONG Length; BOOLEAN CheckResult; KPROCESSOR_MODE PreviousMode; NTSTATUS Status; PAGED_CODE(); PreviousMode = KeGetPreviousMode(); /* probe the buffers */ if (PreviousMode != KernelMode) { _SEH2_TRY { ProbeForWrite(RequiredPrivileges, FIELD_OFFSET(PRIVILEGE_SET, Privilege), sizeof(ULONG)); PrivilegeCount = RequiredPrivileges->PrivilegeCount; PrivilegeControl = RequiredPrivileges->Control; /* Check PrivilegeCount to avoid an integer overflow! */ if (FIELD_OFFSET(PRIVILEGE_SET, Privilege[PrivilegeCount]) / sizeof(RequiredPrivileges->Privilege[0]) != PrivilegeCount) { _SEH2_YIELD(return STATUS_INVALID_PARAMETER); } /* probe all of the array */ ProbeForWrite(RequiredPrivileges, FIELD_OFFSET(PRIVILEGE_SET, Privilege[PrivilegeCount]), sizeof(ULONG)); ProbeForWriteBoolean(Result); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } else { PrivilegeCount = RequiredPrivileges->PrivilegeCount; PrivilegeControl = RequiredPrivileges->Control; } /* reference the token and make sure we're not doing an anonymous impersonation */ Status = ObReferenceObjectByHandle(ClientToken, TOKEN_QUERY, SeTokenObjectType, PreviousMode, (PVOID*)&Token, NULL); if (!NT_SUCCESS(Status)) { return Status; } if (Token->TokenType == TokenImpersonation && Token->ImpersonationLevel < SecurityIdentification) { ObDereferenceObject(Token); return STATUS_BAD_IMPERSONATION_LEVEL; } /* capture the privileges */ Status = SeCaptureLuidAndAttributesArray(RequiredPrivileges->Privilege, PrivilegeCount, PreviousMode, NULL, 0, PagedPool, TRUE, &Privileges, &Length); if (!NT_SUCCESS(Status)) { ObDereferenceObject (Token); return Status; } CheckResult = SepPrivilegeCheck(Token, Privileges, PrivilegeCount, PrivilegeControl, PreviousMode); ObDereferenceObject(Token); /* return the array */ _SEH2_TRY { RtlCopyMemory(RequiredPrivileges->Privilege, Privileges, PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); *Result = CheckResult; Status = STATUS_SUCCESS; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; SeReleaseLuidAndAttributesArray(Privileges, PreviousMode, TRUE); return Status; } /* EOF */