reactos/reactos/ntoskrnl/se/semgr.c

1109 lines
28 KiB
C
Raw Normal View History

/* $Id$
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: ntoskrnl/se/semgr.c
* PURPOSE: Security manager
*
* PROGRAMMERS: No programmer listed.
*/
/* INCLUDES *****************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <internal/debug.h>
/* GLOBALS ******************************************************************/
PSE_EXPORTS SeExports = NULL;
SE_EXPORTS SepExports;
static ERESOURCE SepSubjectContextLock;
/* PROTOTYPES ***************************************************************/
static BOOLEAN SepInitExports(VOID);
/* FUNCTIONS ****************************************************************/
BOOLEAN INIT_FUNCTION
SeInit1(VOID)
{
SepInitLuid();
if (!SepInitSecurityIDs())
return FALSE;
if (!SepInitDACLs())
return FALSE;
if (!SepInitSDs())
return FALSE;
SepInitPrivileges();
if (!SepInitExports())
return FALSE;
/* Initialize the subject context lock */
ExInitializeResource(&SepSubjectContextLock);
return TRUE;
}
BOOLEAN INIT_FUNCTION
SeInit2(VOID)
{
SepInitializeTokenImplementation();
return TRUE;
}
BOOLEAN
SeInitSRM(VOID)
{
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING Name;
HANDLE DirectoryHandle;
HANDLE EventHandle;
NTSTATUS Status;
/* Create '\Security' directory */
RtlInitUnicodeString(&Name,
L"\\Security");
InitializeObjectAttributes(&ObjectAttributes,
&Name,
OBJ_PERMANENT,
0,
NULL);
Status = ZwCreateDirectoryObject(&DirectoryHandle,
DIRECTORY_ALL_ACCESS,
&ObjectAttributes);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to create 'Security' directory!\n");
return FALSE;
}
/* Create 'LSA_AUTHENTICATION_INITALIZED' event */
RtlInitUnicodeString(&Name,
L"\\LSA_AUTHENTICATION_INITALIZED");
InitializeObjectAttributes(&ObjectAttributes,
&Name,
OBJ_PERMANENT,
DirectoryHandle,
SePublicDefaultSd);
Status = ZwCreateEvent(&EventHandle,
EVENT_ALL_ACCESS,
&ObjectAttributes,
SynchronizationEvent,
FALSE);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to create 'LSA_AUTHENTICATION_INITALIZED' event!\n");
NtClose(DirectoryHandle);
return FALSE;
}
ZwClose(EventHandle);
ZwClose(DirectoryHandle);
/* FIXME: Create SRM port and listener thread */
return TRUE;
}
static BOOLEAN INIT_FUNCTION
SepInitExports(VOID)
{
SepExports.SeCreateTokenPrivilege = SeCreateTokenPrivilege;
SepExports.SeAssignPrimaryTokenPrivilege = SeAssignPrimaryTokenPrivilege;
SepExports.SeLockMemoryPrivilege = SeLockMemoryPrivilege;
SepExports.SeIncreaseQuotaPrivilege = SeIncreaseQuotaPrivilege;
SepExports.SeUnsolicitedInputPrivilege = SeUnsolicitedInputPrivilege;
SepExports.SeTcbPrivilege = SeTcbPrivilege;
SepExports.SeSecurityPrivilege = SeSecurityPrivilege;
SepExports.SeTakeOwnershipPrivilege = SeTakeOwnershipPrivilege;
SepExports.SeLoadDriverPrivilege = SeLoadDriverPrivilege;
SepExports.SeCreatePagefilePrivilege = SeCreatePagefilePrivilege;
SepExports.SeIncreaseBasePriorityPrivilege = SeIncreaseBasePriorityPrivilege;
SepExports.SeSystemProfilePrivilege = SeSystemProfilePrivilege;
SepExports.SeSystemtimePrivilege = SeSystemtimePrivilege;
SepExports.SeProfileSingleProcessPrivilege = SeProfileSingleProcessPrivilege;
SepExports.SeCreatePermanentPrivilege = SeCreatePermanentPrivilege;
SepExports.SeBackupPrivilege = SeBackupPrivilege;
SepExports.SeRestorePrivilege = SeRestorePrivilege;
SepExports.SeShutdownPrivilege = SeShutdownPrivilege;
SepExports.SeDebugPrivilege = SeDebugPrivilege;
SepExports.SeAuditPrivilege = SeAuditPrivilege;
SepExports.SeSystemEnvironmentPrivilege = SeSystemEnvironmentPrivilege;
SepExports.SeChangeNotifyPrivilege = SeChangeNotifyPrivilege;
SepExports.SeRemoteShutdownPrivilege = SeRemoteShutdownPrivilege;
SepExports.SeNullSid = SeNullSid;
SepExports.SeWorldSid = SeWorldSid;
SepExports.SeLocalSid = SeLocalSid;
SepExports.SeCreatorOwnerSid = SeCreatorOwnerSid;
SepExports.SeCreatorGroupSid = SeCreatorGroupSid;
SepExports.SeNtAuthoritySid = SeNtAuthoritySid;
SepExports.SeDialupSid = SeDialupSid;
SepExports.SeNetworkSid = SeNetworkSid;
SepExports.SeBatchSid = SeBatchSid;
SepExports.SeInteractiveSid = SeInteractiveSid;
SepExports.SeLocalSystemSid = SeLocalSystemSid;
SepExports.SeAliasAdminsSid = SeAliasAdminsSid;
SepExports.SeAliasUsersSid = SeAliasUsersSid;
SepExports.SeAliasGuestsSid = SeAliasGuestsSid;
SepExports.SeAliasPowerUsersSid = SeAliasPowerUsersSid;
SepExports.SeAliasAccountOpsSid = SeAliasAccountOpsSid;
SepExports.SeAliasSystemOpsSid = SeAliasSystemOpsSid;
SepExports.SeAliasPrintOpsSid = SeAliasPrintOpsSid;
SepExports.SeAliasBackupOpsSid = SeAliasBackupOpsSid;
SepExports.SeAuthenticatedUsersSid = SeAuthenticatedUsersSid;
SepExports.SeRestrictedSid = SeRestrictedSid;
SepExports.SeAnonymousLogonSid = SeAnonymousLogonSid;
SepExports.SeUndockPrivilege = SeUndockPrivilege;
SepExports.SeSyncAgentPrivilege = SeSyncAgentPrivilege;
SepExports.SeEnableDelegationPrivilege = SeEnableDelegationPrivilege;
SeExports = &SepExports;
return TRUE;
}
VOID SepReferenceLogonSession(PLUID AuthenticationId)
{
UNIMPLEMENTED;
}
VOID SepDeReferenceLogonSession(PLUID AuthenticationId)
{
UNIMPLEMENTED;
}
NTSTATUS
STDCALL
SeDefaultObjectMethod(PVOID Object,
SECURITY_OPERATION_CODE OperationType,
SECURITY_INFORMATION SecurityInformation,
PSECURITY_DESCRIPTOR SecurityDescriptor,
PULONG ReturnLength,
PSECURITY_DESCRIPTOR *OldSecurityDescriptor,
POOL_TYPE PoolType,
PGENERIC_MAPPING GenericMapping)
{
PSECURITY_DESCRIPTOR ObjectSd;
PSECURITY_DESCRIPTOR NewSd;
POBJECT_HEADER Header = BODY_TO_HEADER(Object);
PSID Owner = 0;
PSID Group = 0;
PACL Dacl = 0;
PACL Sacl = 0;
ULONG OwnerLength = 0;
ULONG GroupLength = 0;
ULONG DaclLength = 0;
ULONG SaclLength = 0;
ULONG Control = 0;
ULONG_PTR Current;
NTSTATUS Status;
if (OperationType == SetSecurityDescriptor)
{
ObjectSd = Header->SecurityDescriptor;
/* Get owner and owner size */
if (SecurityInformation & OWNER_SECURITY_INFORMATION)
{
if (SecurityDescriptor->Owner != NULL)
{
if( SecurityDescriptor->Control & SE_SELF_RELATIVE )
Owner = (PSID)((ULONG_PTR)SecurityDescriptor->Owner +
(ULONG_PTR)SecurityDescriptor);
else
Owner = (PSID)SecurityDescriptor->Owner;
OwnerLength = ROUND_UP(RtlLengthSid(Owner), 4);
}
Control |= (SecurityDescriptor->Control & SE_OWNER_DEFAULTED);
}
else
{
if (ObjectSd->Owner != NULL)
{
Owner = (PSID)((ULONG_PTR)ObjectSd->Owner + (ULONG_PTR)ObjectSd);
OwnerLength = ROUND_UP(RtlLengthSid(Owner), 4);
}
Control |= (ObjectSd->Control & SE_OWNER_DEFAULTED);
}
/* Get group and group size */
if (SecurityInformation & GROUP_SECURITY_INFORMATION)
{
if (SecurityDescriptor->Group != NULL)
{
if( SecurityDescriptor->Control & SE_SELF_RELATIVE )
Group = (PSID)((ULONG_PTR)SecurityDescriptor->Group +
(ULONG_PTR)SecurityDescriptor);
else
Group = (PSID)SecurityDescriptor->Group;
GroupLength = ROUND_UP(RtlLengthSid(Group), 4);
}
Control |= (SecurityDescriptor->Control & SE_GROUP_DEFAULTED);
}
else
{
if (ObjectSd->Group != NULL)
{
Group = (PSID)((ULONG_PTR)ObjectSd->Group + (ULONG_PTR)ObjectSd);
GroupLength = ROUND_UP(RtlLengthSid(Group), 4);
}
Control |= (ObjectSd->Control & SE_GROUP_DEFAULTED);
}
/* Get DACL and DACL size */
if (SecurityInformation & DACL_SECURITY_INFORMATION)
{
if ((SecurityDescriptor->Control & SE_DACL_PRESENT) &&
(SecurityDescriptor->Dacl != NULL))
{
if( SecurityDescriptor->Control & SE_SELF_RELATIVE )
Dacl = (PACL)((ULONG_PTR)SecurityDescriptor->Dacl +
(ULONG_PTR)SecurityDescriptor);
else
Dacl = (PACL)SecurityDescriptor->Dacl;
DaclLength = ROUND_UP((ULONG)Dacl->AclSize, 4);
}
Control |= (SecurityDescriptor->Control & (SE_DACL_DEFAULTED | SE_DACL_PRESENT));
}
else
{
if ((ObjectSd->Control & SE_DACL_PRESENT) &&
(ObjectSd->Dacl != NULL))
{
Dacl = (PACL)((ULONG_PTR)ObjectSd->Dacl + (ULONG_PTR)ObjectSd);
DaclLength = ROUND_UP((ULONG)Dacl->AclSize, 4);
}
Control |= (ObjectSd->Control & (SE_DACL_DEFAULTED | SE_DACL_PRESENT));
}
/* Get SACL and SACL size */
if (SecurityInformation & SACL_SECURITY_INFORMATION)
{
if ((SecurityDescriptor->Control & SE_SACL_PRESENT) &&
(SecurityDescriptor->Sacl != NULL))
{
if( SecurityDescriptor->Control & SE_SELF_RELATIVE )
Sacl = (PACL)((ULONG_PTR)SecurityDescriptor->Sacl +
(ULONG_PTR)SecurityDescriptor);
else
Sacl = (PACL)SecurityDescriptor->Sacl;
SaclLength = ROUND_UP((ULONG)Sacl->AclSize, 4);
}
Control |= (SecurityDescriptor->Control & (SE_SACL_DEFAULTED | SE_SACL_PRESENT));
}
else
{
if ((ObjectSd->Control & SE_SACL_PRESENT) &&
(ObjectSd->Sacl != NULL))
{
Sacl = (PACL)((ULONG_PTR)ObjectSd->Sacl + (ULONG_PTR)ObjectSd);
SaclLength = ROUND_UP((ULONG)Sacl->AclSize, 4);
}
Control |= (ObjectSd->Control & (SE_SACL_DEFAULTED | SE_SACL_PRESENT));
}
NewSd = ExAllocatePool(NonPagedPool,
sizeof(SECURITY_DESCRIPTOR) + OwnerLength + GroupLength +
DaclLength + SaclLength);
if (NewSd == NULL)
{
ObDereferenceObject(Object);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCreateSecurityDescriptor(NewSd,
SECURITY_DESCRIPTOR_REVISION1);
/* We always build a self-relative descriptor */
NewSd->Control = Control | SE_SELF_RELATIVE;
Current = (ULONG_PTR)NewSd + sizeof(SECURITY_DESCRIPTOR);
if (OwnerLength != 0)
{
RtlCopyMemory((PVOID)Current,
Owner,
OwnerLength);
NewSd->Owner = (PSID)(Current - (ULONG_PTR)NewSd);
Current += OwnerLength;
}
if (GroupLength != 0)
{
RtlCopyMemory((PVOID)Current,
Group,
GroupLength);
NewSd->Group = (PSID)(Current - (ULONG_PTR)NewSd);
Current += GroupLength;
}
if (DaclLength != 0)
{
RtlCopyMemory((PVOID)Current,
Dacl,
DaclLength);
NewSd->Dacl = (PACL)(Current - (ULONG_PTR)NewSd);
Current += DaclLength;
}
if (SaclLength != 0)
{
RtlCopyMemory((PVOID)Current,
Sacl,
SaclLength);
NewSd->Sacl = (PACL)(Current - (ULONG_PTR)NewSd);
Current += SaclLength;
}
/* Add the new SD */
Status = ObpAddSecurityDescriptor(NewSd,
&Header->SecurityDescriptor);
if (NT_SUCCESS(Status))
{
/* Remove the old security descriptor */
ObpRemoveSecurityDescriptor(ObjectSd);
}
else
{
/* Restore the old security descriptor */
Header->SecurityDescriptor = ObjectSd;
}
ExFreePool(NewSd);
}
else if (OperationType == QuerySecurityDescriptor)
{
Status = SeQuerySecurityDescriptorInfo(&SecurityInformation,
SecurityDescriptor,
ReturnLength,
&Header->SecurityDescriptor);
}
else if (OperationType == AssignSecurityDescriptor)
{
/* Assign the security descriptor to the object header */
Status = ObpAddSecurityDescriptor(SecurityDescriptor,
&Header->SecurityDescriptor);
}
return STATUS_SUCCESS;
}
/*
* @implemented
*/
VOID STDCALL
SeCaptureSubjectContext(OUT PSECURITY_SUBJECT_CONTEXT SubjectContext)
{
PETHREAD Thread;
BOOLEAN CopyOnOpen;
BOOLEAN EffectiveOnly;
PAGED_CODE();
Thread = PsGetCurrentThread();
if (Thread == NULL)
{
SubjectContext->ProcessAuditId = 0;
SubjectContext->PrimaryToken = NULL;
SubjectContext->ClientToken = NULL;
SubjectContext->ImpersonationLevel = 0;
}
else
{
SubjectContext->ProcessAuditId = Thread->ThreadsProcess;
SubjectContext->ClientToken =
PsReferenceImpersonationToken(Thread,
&CopyOnOpen,
&EffectiveOnly,
&SubjectContext->ImpersonationLevel);
SubjectContext->PrimaryToken = PsReferencePrimaryToken(Thread->ThreadsProcess);
}
}
/*
* @implemented
*/
VOID STDCALL
SeLockSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext)
{
PAGED_CODE();
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&SepSubjectContextLock, TRUE);
}
/*
* @implemented
*/
VOID STDCALL
SeUnlockSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext)
{
PAGED_CODE();
ExReleaseResourceLite(&SepSubjectContextLock);
KeLeaveCriticalRegion();
}
/*
* @implemented
*/
VOID STDCALL
SeReleaseSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext)
{
PAGED_CODE();
if (SubjectContext->PrimaryToken != NULL)
{
ObDereferenceObject(SubjectContext->PrimaryToken);
}
if (SubjectContext->ClientToken != NULL)
{
ObDereferenceObject(SubjectContext->ClientToken);
}
}
/*
* @implemented
*/
NTSTATUS STDCALL
SeDeassignSecurity(PSECURITY_DESCRIPTOR *SecurityDescriptor)
{
PAGED_CODE();
if (*SecurityDescriptor != NULL)
{
ExFreePool(*SecurityDescriptor);
*SecurityDescriptor = NULL;
}
return STATUS_SUCCESS;
}
/*
* @unimplemented
*/
NTSTATUS STDCALL
SeAssignSecurityEx(IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL,
IN PSECURITY_DESCRIPTOR ExplicitDescriptor OPTIONAL,
OUT PSECURITY_DESCRIPTOR *NewDescriptor,
IN GUID *ObjectType OPTIONAL,
IN BOOLEAN IsDirectoryObject,
IN ULONG AutoInheritFlags,
IN PSECURITY_SUBJECT_CONTEXT SubjectContext,
IN PGENERIC_MAPPING GenericMapping,
IN POOL_TYPE PoolType)
{
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}
/*
* FUNCTION: Creates a security descriptor for a new object.
* ARGUMENTS:
* ParentDescriptor =
* ExplicitDescriptor =
* NewDescriptor =
* IsDirectoryObject =
* SubjectContext =
* GeneralMapping =
* PoolType =
* RETURNS: Status
*
* @implemented
*/
NTSTATUS STDCALL
SeAssignSecurity(PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL,
PSECURITY_DESCRIPTOR ExplicitDescriptor OPTIONAL,
PSECURITY_DESCRIPTOR *NewDescriptor,
BOOLEAN IsDirectoryObject,
PSECURITY_SUBJECT_CONTEXT SubjectContext,
PGENERIC_MAPPING GenericMapping,
POOL_TYPE PoolType)
{
PSECURITY_DESCRIPTOR Descriptor;
PTOKEN Token;
ULONG OwnerLength = 0;
ULONG GroupLength = 0;
ULONG DaclLength = 0;
ULONG SaclLength = 0;
ULONG Length = 0;
ULONG Control = 0;
ULONG_PTR Current;
PSID Owner = NULL;
PSID Group = NULL;
PACL Dacl = NULL;
PACL Sacl = NULL;
PAGED_CODE();
/* Lock subject context */
SeLockSubjectContext(SubjectContext);
if (SubjectContext->ClientToken != NULL)
{
Token = SubjectContext->ClientToken;
}
else
{
Token = SubjectContext->PrimaryToken;
}
/* Inherit the Owner SID */
if (ExplicitDescriptor != NULL && ExplicitDescriptor->Owner != NULL)
{
DPRINT("Use explicit owner sid!\n");
Owner = ExplicitDescriptor->Owner;
Object Manager Patch. This patch continues the work done in the previous patch and makes the following changes in order to support OB 2.0 (it basically temporarily fixes a highly incorrect implementation so that caller code will be ready to work with the OB 2.0 without change): 1) The documented Object Create Information Structure and semantics implemented. All Object Attributes and passed data from user-mode is now probed and saved into this object create structure when ObCreateObject is called. 2) ObCreateObject does NOT PERFORM ANY OTHER OPERATION EXCEPT CREATING THE OBJECT ANYMORE. ObCreateObject will NOT insert the Object into the tree and other operations. These are now done correctly by ObInsertObject. Therefore, the biggest hurdle was changing pieces of code which assumed ObCreateObject would be enough. 3) ObInsertObject uses the captured create info for all operations isntead of the Object Attributes. 4) ObFindObject now uses the captured info as well. 5) The OBject name and directory are now stored in the documented Object Name Information, always allocated and freed from non paged pool. HACKS: 5) Because the registry code is horribly broken and doesn't use ObFindObjectByName, the old ObFindObject had to be temporarily duplicated into CmpFindObject. 7) Win32k used ObInsertObject in CsrInsertObject as a way to create a handle inside csrss. However, OBInsertObject now does more then this. As a temporary hack, ObpCreateHandle is exported from the kernel and called from win32k. A fix needs to be done for this, but I don't know the design of win32k+csrss well enough to find a solution. 8) SEH has been commented out in some places of the new probing code because it breaks smss and explorer. These need to be investigated (seh did not exist in the previous code, so this is not really a hack) 9) Named objects with a parent directory are NOT allowed. However because of bugs in kernel32, the new check has been temporarily disabled. (this check did not exist in the previous code, so this is not really a hack) The next patch will add a proper ObFindObject which will support a more complete Parse Procedure with context and security information. This is needed for proper registry access (requested by Eric Kohl) and for proper functionality of the Desktop/File creation, which should use the Parse routine, and not the Create Handle Routine. This will also make it possible to remove some previous hacks and pave the way for a fixed Iop/IoCreateFile svn path=/trunk/; revision=15395
2005-05-18 19:26:47 +00:00
if (ExplicitDescriptor->Control & SE_SELF_RELATIVE)
{
Owner = (PSID)(((ULONG_PTR)Owner) + (ULONG_PTR)ExplicitDescriptor);
Object Manager Patch. This patch continues the work done in the previous patch and makes the following changes in order to support OB 2.0 (it basically temporarily fixes a highly incorrect implementation so that caller code will be ready to work with the OB 2.0 without change): 1) The documented Object Create Information Structure and semantics implemented. All Object Attributes and passed data from user-mode is now probed and saved into this object create structure when ObCreateObject is called. 2) ObCreateObject does NOT PERFORM ANY OTHER OPERATION EXCEPT CREATING THE OBJECT ANYMORE. ObCreateObject will NOT insert the Object into the tree and other operations. These are now done correctly by ObInsertObject. Therefore, the biggest hurdle was changing pieces of code which assumed ObCreateObject would be enough. 3) ObInsertObject uses the captured create info for all operations isntead of the Object Attributes. 4) ObFindObject now uses the captured info as well. 5) The OBject name and directory are now stored in the documented Object Name Information, always allocated and freed from non paged pool. HACKS: 5) Because the registry code is horribly broken and doesn't use ObFindObjectByName, the old ObFindObject had to be temporarily duplicated into CmpFindObject. 7) Win32k used ObInsertObject in CsrInsertObject as a way to create a handle inside csrss. However, OBInsertObject now does more then this. As a temporary hack, ObpCreateHandle is exported from the kernel and called from win32k. A fix needs to be done for this, but I don't know the design of win32k+csrss well enough to find a solution. 8) SEH has been commented out in some places of the new probing code because it breaks smss and explorer. These need to be investigated (seh did not exist in the previous code, so this is not really a hack) 9) Named objects with a parent directory are NOT allowed. However because of bugs in kernel32, the new check has been temporarily disabled. (this check did not exist in the previous code, so this is not really a hack) The next patch will add a proper ObFindObject which will support a more complete Parse Procedure with context and security information. This is needed for proper registry access (requested by Eric Kohl) and for proper functionality of the Desktop/File creation, which should use the Parse routine, and not the Create Handle Routine. This will also make it possible to remove some previous hacks and pave the way for a fixed Iop/IoCreateFile svn path=/trunk/; revision=15395
2005-05-18 19:26:47 +00:00
}
}
else
{
if (Token != NULL)
{
DPRINT("Use token owner sid!\n");
Owner = Token->UserAndGroups[Token->DefaultOwnerIndex].Sid;
}
else
{
DPRINT("Use default owner sid!\n");
Owner = SeLocalSystemSid;
}
Control |= SE_OWNER_DEFAULTED;
}
OwnerLength = ROUND_UP(RtlLengthSid(Owner), 4);
/* Inherit the Group SID */
if (ExplicitDescriptor != NULL && ExplicitDescriptor->Group != NULL)
{
DPRINT("Use explicit group sid!\n");
Group = ExplicitDescriptor->Group;
if (ExplicitDescriptor->Control & SE_SELF_RELATIVE)
{
Group = (PSID)(((ULONG_PTR)Group) + (ULONG_PTR)ExplicitDescriptor);
}
}
else
{
if (Token != NULL)
{
DPRINT("Use token group sid!\n");
Group = Token->PrimaryGroup;
}
else
{
DPRINT("Use default group sid!\n");
Group = SeLocalSystemSid;
}
Control |= SE_OWNER_DEFAULTED;
}
GroupLength = ROUND_UP(RtlLengthSid(Group), 4);
/* Inherit the DACL */
if (ExplicitDescriptor != NULL &&
(ExplicitDescriptor->Control & SE_DACL_PRESENT) &&
!(ExplicitDescriptor->Control & SE_DACL_DEFAULTED))
{
DPRINT("Use explicit DACL!\n");
Dacl = ExplicitDescriptor->Dacl;
if (Dacl != NULL && (ExplicitDescriptor->Control & SE_SELF_RELATIVE))
{
Dacl = (PACL)(((ULONG_PTR)Dacl) + (ULONG_PTR)ExplicitDescriptor);
}
Control |= SE_DACL_PRESENT;
}
else if (ParentDescriptor != NULL &&
(ParentDescriptor->Control & SE_DACL_PRESENT))
{
DPRINT("Use parent DACL!\n");
/* FIXME: Inherit */
Dacl = ParentDescriptor->Dacl;
if (Dacl != NULL && (ParentDescriptor->Control & SE_SELF_RELATIVE))
{
Dacl = (PACL)(((ULONG_PTR)Dacl) + (ULONG_PTR)ParentDescriptor);
}
Control |= (SE_DACL_PRESENT | SE_DACL_DEFAULTED);
}
else if (Token != NULL && Token->DefaultDacl != NULL)
{
DPRINT("Use token default DACL!\n");
/* FIXME: Inherit */
Dacl = Token->DefaultDacl;
Control |= (SE_DACL_PRESENT | SE_DACL_DEFAULTED);
}
else
{
DPRINT("Use NULL DACL!\n");
Dacl = NULL;
Control |= (SE_DACL_PRESENT | SE_DACL_DEFAULTED);
}
DaclLength = (Dacl != NULL) ? ROUND_UP(Dacl->AclSize, 4) : 0;
/* Inherit the SACL */
if (ExplicitDescriptor != NULL &&
(ExplicitDescriptor->Control & SE_SACL_PRESENT) &&
!(ExplicitDescriptor->Control & SE_SACL_DEFAULTED))
{
DPRINT("Use explicit SACL!\n");
Sacl = ExplicitDescriptor->Sacl;
if (Sacl != NULL && (ExplicitDescriptor->Control & SE_SELF_RELATIVE))
{
Sacl = (PACL)(((ULONG_PTR)Sacl) + (ULONG_PTR)ExplicitDescriptor);
}
Control |= SE_SACL_PRESENT;
}
else if (ParentDescriptor != NULL &&
(ParentDescriptor->Control & SE_SACL_PRESENT))
{
DPRINT("Use parent SACL!\n");
/* FIXME: Inherit */
Sacl = ParentDescriptor->Sacl;
if (Sacl != NULL && (ParentDescriptor->Control & SE_SELF_RELATIVE))
{
Sacl = (PACL)(((ULONG_PTR)Sacl) + (ULONG_PTR)ParentDescriptor);
}
Control |= (SE_SACL_PRESENT | SE_SACL_DEFAULTED);
}
SaclLength = (Sacl != NULL) ? ROUND_UP(Sacl->AclSize, 4) : 0;
/* Allocate and initialize the new security descriptor */
Length = sizeof(SECURITY_DESCRIPTOR) +
OwnerLength + GroupLength + DaclLength + SaclLength;
DPRINT("L: sizeof(SECURITY_DESCRIPTOR) %d OwnerLength %d GroupLength %d DaclLength %d SaclLength %d\n",
sizeof(SECURITY_DESCRIPTOR),
OwnerLength,
GroupLength,
DaclLength,
SaclLength);
Descriptor = ExAllocatePool(PagedPool,
Length);
if (Descriptor == NULL)
{
DPRINT1("ExAlloctePool() failed\n");
/* FIXME: Unlock subject context */
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory( Descriptor, Length );
RtlCreateSecurityDescriptor(Descriptor,
SECURITY_DESCRIPTOR_REVISION);
Descriptor->Control = Control | SE_SELF_RELATIVE;
Current = (ULONG_PTR)Descriptor + sizeof(SECURITY_DESCRIPTOR);
if (SaclLength != 0)
{
RtlCopyMemory((PVOID)Current,
Sacl,
SaclLength);
Descriptor->Sacl = (PACL)((ULONG_PTR)Current - (ULONG_PTR)Descriptor);
Current += SaclLength;
}
if (DaclLength != 0)
{
RtlCopyMemory((PVOID)Current,
Dacl,
DaclLength);
Descriptor->Dacl = (PACL)((ULONG_PTR)Current - (ULONG_PTR)Descriptor);
Current += DaclLength;
}
if (OwnerLength != 0)
{
RtlCopyMemory((PVOID)Current,
Owner,
OwnerLength);
Descriptor->Owner = (PSID)((ULONG_PTR)Current - (ULONG_PTR)Descriptor);
Current += OwnerLength;
DPRINT("Owner of %x at %x\n", Descriptor, Descriptor->Owner);
}
else
DPRINT("Owner of %x is zero length\n", Descriptor);
if (GroupLength != 0)
{
memmove((PVOID)Current,
Group,
GroupLength);
Descriptor->Group = (PSID)((ULONG_PTR)Current - (ULONG_PTR)Descriptor);
}
/* Unlock subject context */
SeUnlockSubjectContext(SubjectContext);
*NewDescriptor = Descriptor;
DPRINT("Descrptor %x\n", Descriptor);
ASSERT(RtlLengthSecurityDescriptor(Descriptor));
return STATUS_SUCCESS;
}
static BOOLEAN
SepSidInToken(PACCESS_TOKEN _Token,
PSID Sid)
{
ULONG i;
PTOKEN Token = (PTOKEN)_Token;
PAGED_CODE();
if (Token->UserAndGroupCount == 0)
{
return FALSE;
}
for (i=0; i<Token->UserAndGroupCount; i++)
{
if (RtlEqualSid(Sid, Token->UserAndGroups[i].Sid))
{
if (Token->UserAndGroups[i].Attributes & SE_GROUP_ENABLED)
{
return TRUE;
}
return FALSE;
}
}
return FALSE;
}
/*
* FUNCTION: Determines whether the requested access rights can be granted
* to an object protected by a security descriptor and an object owner
* ARGUMENTS:
* SecurityDescriptor = Security descriptor protecting the object
* SubjectSecurityContext = Subject's captured security context
* SubjectContextLocked = Indicates the user's subject context is locked
* DesiredAccess = Access rights the caller is trying to acquire
* PreviouslyGrantedAccess = Specified the access rights already granted
* Privileges = ?
* GenericMapping = Generic mapping associated with the object
* AccessMode = Access mode used for the check
* GrantedAccess (OUT) = On return specifies the access granted
* AccessStatus (OUT) = Status indicating why access was denied
* RETURNS: If access was granted, returns TRUE
*
* @implemented
*/
BOOLEAN STDCALL
SeAccessCheck(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
IN BOOLEAN SubjectContextLocked,
IN ACCESS_MASK DesiredAccess,
IN ACCESS_MASK PreviouslyGrantedAccess,
OUT PPRIVILEGE_SET* Privileges,
IN PGENERIC_MAPPING GenericMapping,
IN KPROCESSOR_MODE AccessMode,
OUT PACCESS_MASK GrantedAccess,
OUT PNTSTATUS AccessStatus)
{
LUID_AND_ATTRIBUTES Privilege;
ACCESS_MASK CurrentAccess;
PACCESS_TOKEN Token;
ULONG i;
PACL Dacl;
BOOLEAN Present;
BOOLEAN Defaulted;
PACE CurrentAce;
PSID Sid;
NTSTATUS Status;
PAGED_CODE();
CurrentAccess = PreviouslyGrantedAccess;
if (SubjectContextLocked == FALSE)
{
SeLockSubjectContext(SubjectSecurityContext);
}
Token = SubjectSecurityContext->ClientToken ?
SubjectSecurityContext->ClientToken : SubjectSecurityContext->PrimaryToken;
/* Get the DACL */
Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor,
&Present,
&Dacl,
&Defaulted);
if (!NT_SUCCESS(Status))
{
if (SubjectContextLocked == FALSE)
{
SeUnlockSubjectContext(SubjectSecurityContext);
}
*AccessStatus = Status;
return FALSE;
}
/* RULE 1: Grant desired access if the object is unprotected */
if (Present == TRUE && Dacl == NULL)
{
if (SubjectContextLocked == FALSE)
{
SeUnlockSubjectContext(SubjectSecurityContext);
}
*GrantedAccess = DesiredAccess;
*AccessStatus = STATUS_SUCCESS;
return TRUE;
}
CurrentAccess = PreviouslyGrantedAccess;
/* RULE 2: Check token for 'take ownership' privilege */
Privilege.Luid = SeTakeOwnershipPrivilege;
Privilege.Attributes = SE_PRIVILEGE_ENABLED;
if (SepPrivilegeCheck(Token,
&Privilege,
1,
PRIVILEGE_SET_ALL_NECESSARY,
AccessMode))
{
CurrentAccess |= WRITE_OWNER;
if (DesiredAccess == CurrentAccess)
{
if (SubjectContextLocked == FALSE)
{
SeUnlockSubjectContext(SubjectSecurityContext);
}
*GrantedAccess = CurrentAccess;
*AccessStatus = STATUS_SUCCESS;
return TRUE;
}
}
/* RULE 3: Check whether the token is the owner */
Status = RtlGetOwnerSecurityDescriptor(SecurityDescriptor,
&Sid,
&Defaulted);
if (!NT_SUCCESS(Status))
{
DPRINT1("RtlGetOwnerSecurityDescriptor() failed (Status %lx)\n", Status);
if (SubjectContextLocked == FALSE)
{
SeUnlockSubjectContext(SubjectSecurityContext);
}
*AccessStatus = Status;
return FALSE;
}
if (SepSidInToken(Token, Sid))
{
CurrentAccess |= (READ_CONTROL | WRITE_DAC);
if (DesiredAccess == CurrentAccess)
{
if (SubjectContextLocked == FALSE)
{
SeUnlockSubjectContext(SubjectSecurityContext);
}
*GrantedAccess = CurrentAccess;
*AccessStatus = STATUS_SUCCESS;
return TRUE;
}
}
/* Fail if DACL is absent */
if (Present == FALSE)
{
if (SubjectContextLocked == FALSE)
{
SeUnlockSubjectContext(SubjectSecurityContext);
}
*GrantedAccess = 0;
*AccessStatus = STATUS_ACCESS_DENIED;
return TRUE;
}
/* RULE 4: Grant rights according to the DACL */
CurrentAce = (PACE)(Dacl + 1);
for (i = 0; i < Dacl->AceCount; i++)
{
Sid = (PSID)(CurrentAce + 1);
if (CurrentAce->Header.AceType == ACCESS_DENIED_ACE_TYPE)
{
if (SepSidInToken(Token, Sid))
{
if (SubjectContextLocked == FALSE)
{
SeUnlockSubjectContext(SubjectSecurityContext);
}
*GrantedAccess = 0;
*AccessStatus = STATUS_ACCESS_DENIED;
return TRUE;
}
}
if (CurrentAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
{
if (SepSidInToken(Token, Sid))
{
CurrentAccess |= CurrentAce->AccessMask;
}
}
}
if (SubjectContextLocked == FALSE)
{
SeUnlockSubjectContext(SubjectSecurityContext);
}
DPRINT("CurrentAccess %08lx\n DesiredAccess %08lx\n",
CurrentAccess, DesiredAccess);
*GrantedAccess = CurrentAccess & DesiredAccess;
*AccessStatus =
(*GrantedAccess == DesiredAccess) ? STATUS_SUCCESS : STATUS_ACCESS_DENIED;
return TRUE;
}
NTSTATUS STDCALL
NtAccessCheck(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN HANDLE TokenHandle,
IN ACCESS_MASK DesiredAccess,
IN PGENERIC_MAPPING GenericMapping,
OUT PPRIVILEGE_SET PrivilegeSet,
OUT PULONG ReturnLength,
OUT PACCESS_MASK GrantedAccess,
OUT PNTSTATUS AccessStatus)
{
SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
KPROCESSOR_MODE PreviousMode;
PTOKEN Token;
NTSTATUS Status;
PAGED_CODE();
DPRINT("NtAccessCheck() called\n");
PreviousMode = KeGetPreviousMode();
if (PreviousMode == KernelMode)
{
*GrantedAccess = DesiredAccess;
*AccessStatus = STATUS_SUCCESS;
return STATUS_SUCCESS;
}
Status = ObReferenceObjectByHandle(TokenHandle,
TOKEN_QUERY,
SepTokenObjectType,
PreviousMode,
(PVOID*)&Token,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to reference token (Status %lx)\n", Status);
return Status;
}
/* Check token type */
if (Token->TokenType != TokenImpersonation)
{
DPRINT1("No impersonation token\n");
ObDereferenceObject(Token);
return STATUS_ACCESS_VIOLATION;
}
/* Check impersonation level */
if (Token->ImpersonationLevel < SecurityAnonymous)
{
DPRINT1("Invalid impersonation level\n");
ObDereferenceObject(Token);
return STATUS_ACCESS_VIOLATION;
}
RtlZeroMemory(&SubjectSecurityContext,
sizeof(SECURITY_SUBJECT_CONTEXT));
SubjectSecurityContext.ClientToken = Token;
SubjectSecurityContext.ImpersonationLevel = Token->ImpersonationLevel;
/* Lock subject context */
SeLockSubjectContext(&SubjectSecurityContext);
if (SeAccessCheck(SecurityDescriptor,
&SubjectSecurityContext,
TRUE,
DesiredAccess,
0,
&PrivilegeSet,
GenericMapping,
PreviousMode,
GrantedAccess,
AccessStatus))
{
Status = *AccessStatus;
}
else
{
Status = STATUS_ACCESS_DENIED;
}
/* Unlock subject context */
SeUnlockSubjectContext(&SubjectSecurityContext);
ObDereferenceObject(Token);
DPRINT("NtAccessCheck() done\n");
return Status;
}
/* EOF */