reactos/ntoskrnl/se/priv.c
2013-06-16 22:01:41 +00:00

596 lines
18 KiB
C

/*
* 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 <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
#if defined (ALLOC_PRAGMA)
#pragma alloc_text(INIT, SepInitPrivileges)
#endif
/* GLOBALS ********************************************************************/
LUID SeCreateTokenPrivilege;
LUID SeAssignPrimaryTokenPrivilege;
LUID SeLockMemoryPrivilege;
LUID SeIncreaseQuotaPrivilege;
LUID SeUnsolicitedInputPrivilege;
LUID SeTcbPrivilege;
LUID SeSecurityPrivilege;
LUID SeTakeOwnershipPrivilege;
LUID SeLoadDriverPrivilege;
LUID SeCreatePagefilePrivilege;
LUID SeIncreaseBasePriorityPrivilege;
LUID SeSystemProfilePrivilege;
LUID SeSystemtimePrivilege;
LUID SeProfileSingleProcessPrivilege;
LUID SeCreatePermanentPrivilege;
LUID SeBackupPrivilege;
LUID SeRestorePrivilege;
LUID SeShutdownPrivilege;
LUID SeDebugPrivilege;
LUID SeAuditPrivilege;
LUID SeSystemEnvironmentPrivilege;
LUID SeChangeNotifyPrivilege;
LUID SeRemoteShutdownPrivilege;
LUID SeUndockPrivilege;
LUID SeSyncAgentPrivilege;
LUID SeEnableDelegationPrivilege;
/* PRIVATE FUNCTIONS **********************************************************/
VOID
INIT_FUNCTION
NTAPI
SepInitPrivileges(VOID)
{
SeCreateTokenPrivilege.LowPart = SE_CREATE_TOKEN_PRIVILEGE;
SeCreateTokenPrivilege.HighPart = 0;
SeAssignPrimaryTokenPrivilege.LowPart = SE_ASSIGNPRIMARYTOKEN_PRIVILEGE;
SeAssignPrimaryTokenPrivilege.HighPart = 0;
SeLockMemoryPrivilege.LowPart = SE_LOCK_MEMORY_PRIVILEGE;
SeLockMemoryPrivilege.HighPart = 0;
SeIncreaseQuotaPrivilege.LowPart = SE_INCREASE_QUOTA_PRIVILEGE;
SeIncreaseQuotaPrivilege.HighPart = 0;
SeUnsolicitedInputPrivilege.LowPart = SE_UNSOLICITED_INPUT_PRIVILEGE;
SeUnsolicitedInputPrivilege.HighPart = 0;
SeTcbPrivilege.LowPart = SE_TCB_PRIVILEGE;
SeTcbPrivilege.HighPart = 0;
SeSecurityPrivilege.LowPart = SE_SECURITY_PRIVILEGE;
SeSecurityPrivilege.HighPart = 0;
SeTakeOwnershipPrivilege.LowPart = SE_TAKE_OWNERSHIP_PRIVILEGE;
SeTakeOwnershipPrivilege.HighPart = 0;
SeLoadDriverPrivilege.LowPart = SE_LOAD_DRIVER_PRIVILEGE;
SeLoadDriverPrivilege.HighPart = 0;
SeSystemProfilePrivilege.LowPart = SE_SYSTEM_PROFILE_PRIVILEGE;
SeSystemProfilePrivilege.HighPart = 0;
SeSystemtimePrivilege.LowPart = SE_SYSTEMTIME_PRIVILEGE;
SeSystemtimePrivilege.HighPart = 0;
SeProfileSingleProcessPrivilege.LowPart = SE_PROF_SINGLE_PROCESS_PRIVILEGE;
SeProfileSingleProcessPrivilege.HighPart = 0;
SeIncreaseBasePriorityPrivilege.LowPart = SE_INC_BASE_PRIORITY_PRIVILEGE;
SeIncreaseBasePriorityPrivilege.HighPart = 0;
SeCreatePagefilePrivilege.LowPart = SE_CREATE_PAGEFILE_PRIVILEGE;
SeCreatePagefilePrivilege.HighPart = 0;
SeCreatePermanentPrivilege.LowPart = SE_CREATE_PERMANENT_PRIVILEGE;
SeCreatePermanentPrivilege.HighPart = 0;
SeBackupPrivilege.LowPart = SE_BACKUP_PRIVILEGE;
SeBackupPrivilege.HighPart = 0;
SeRestorePrivilege.LowPart = SE_RESTORE_PRIVILEGE;
SeRestorePrivilege.HighPart = 0;
SeShutdownPrivilege.LowPart = SE_SHUTDOWN_PRIVILEGE;
SeShutdownPrivilege.HighPart = 0;
SeDebugPrivilege.LowPart = SE_DEBUG_PRIVILEGE;
SeDebugPrivilege.HighPart = 0;
SeAuditPrivilege.LowPart = SE_AUDIT_PRIVILEGE;
SeAuditPrivilege.HighPart = 0;
SeSystemEnvironmentPrivilege.LowPart = SE_SYSTEM_ENVIRONMENT_PRIVILEGE;
SeSystemEnvironmentPrivilege.HighPart = 0;
SeChangeNotifyPrivilege.LowPart = SE_CHANGE_NOTIFY_PRIVILEGE;
SeChangeNotifyPrivilege.HighPart = 0;
SeRemoteShutdownPrivilege.LowPart = SE_REMOTE_SHUTDOWN_PRIVILEGE;
SeRemoteShutdownPrivilege.HighPart = 0;
SeUndockPrivilege.LowPart = SE_UNDOCK_PRIVILEGE;
SeUndockPrivilege.HighPart = 0;
SeSyncAgentPrivilege.LowPart = SE_SYNC_AGENT_PRIVILEGE;
SeSyncAgentPrivilege.HighPart = 0;
SeEnableDelegationPrivilege.LowPart = SE_ENABLE_DELEGATION_PRIVILEGE;
SeEnableDelegationPrivilege.HighPart = 0;
}
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 && Required > 0; 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--;
}
/* Leave the inner loop */
break;
}
}
}
/* Return whether we found all required privileges */
return (Required == 0);
}
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 */