NT4/private/newsam2/server/context.c
2020-09-30 17:12:29 +02:00

1937 lines
48 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
context.c
Abstract:
This file contains services for operating on internal context blocks.
Author:
Jim Kelly (JimK) 4-July-1991
Environment:
User Mode - Win32
Revision History:
31 - May 1996 Murlis
Ported to use the NT5 DS.
ChrisMay 10-Jun-96
Added initialization of Context->ObjectNameInDs and IsDsObject data
members. Note that this causes context objects to be created with a
default storage of type registry (instead of DS).
6-16-96
Moved DS object decision to SampCreateContext for account objects.
--*/
///////////////////////////////////////////////////////////////////////////////
// //
// Includes //
// //
///////////////////////////////////////////////////////////////////////////////
#include <samsrvp.h>
#include <dslayer.h>
///////////////////////////////////////////////////////////////////////////////
// //
// private service prototypes //
// //
///////////////////////////////////////////////////////////////////////////////
//
// Context validation services.
// The service to invalidate a context is visible outside this file and so
// its prototype is in samsrvp.h.
//
VOID
SampAddNewValidContextAddress(
IN PSAMP_OBJECT NewContext
);
NTSTATUS
SampValidateContextAddress(
IN PSAMP_OBJECT Context
);
NTSTATUS
SampCheckIfObjectExists(
IN PSAMP_OBJECT Context
);
BOOLEAN
SampIsObjectLocated(
IN PSAMP_OBJECT Context
);
NTSTATUS
SampLocateObject(
IN PSAMP_OBJECT Context,
IN ULONG Rid
);
///////////////////////////////////////////////////////////////////////////////
// //
// Routines //
// //
///////////////////////////////////////////////////////////////////////////////
PSAMP_OBJECT
SampCreateContext(
IN SAMP_OBJECT_TYPE Type,
IN BOOLEAN TrustedClient
)
/*++
Routine Description:
This service creates a new object context block of the specified type.
If the context block is for either a user or group object type, then
it will be added to the list of contexts for the transaction domain.
THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS.
Upon return:
- The ObjectType field will be set to the passed value.
- The Reference count field will be set to 1,
- The GrantedAccess field will be zero.
- The TrustedClient field will be set according to the passed
value.
- The Valid flag will be TRUE.
All other fields must be filled in by the creator.
Arguments:
Type - Specifies the type of context block being created.
TrustedClient - Indicates whether the client is a trusted component
of the operating syste. If so, than all access checks are
circumvented.
Return Value:
Non-Null - Pointer to a context block.
NULL - Insufficient resources. No context block allocated.
--*/
{
PSAMP_OBJECT Context;
SAMTRACE("SampCreateContext");
if (!TrustedClient) {
if (SampActiveContextCount >= SAMP_MAXIMUM_ACTIVE_CONTEXTS) {
return(NULL);
}
SampActiveContextCount += 1;
}
Context = MIDL_user_allocate( sizeof(SAMP_OBJECT) );
if (Context != NULL) {
#if SAMP_DIAGNOSTICS
IF_SAMP_GLOBAL( CONTEXT_TRACKING ) {
SampDiagPrint( CONTEXT_TRACKING, ("Creating ") );
if (Type == SampServerObjectType) SampDiagPrint(CONTEXT_TRACKING, ("Server "));
if (Type == SampDomainObjectType) SampDiagPrint(CONTEXT_TRACKING, (" Domain "));
if (Type == SampGroupObjectType) SampDiagPrint(CONTEXT_TRACKING, (" Group "));
if (Type == SampAliasObjectType) SampDiagPrint(CONTEXT_TRACKING, (" Alias "));
if (Type == SampUserObjectType) SampDiagPrint(CONTEXT_TRACKING, (" User "));
SampDiagPrint(CONTEXT_TRACKING, ("context : 0x%lx\n", Context ));
}
#endif //SAMP_DIAGNOSTICS
Context->ObjectType = Type;
Context->ReferenceCount = 1; // Represents RPCs held context handle value
Context->GrantedAccess = 0;
Context->RootKey = INVALID_HANDLE_VALUE;
RtlInitUnicodeString(&Context->RootName, NULL);
Context->TrustedClient = TrustedClient;
Context->MarkedForDelete = FALSE;
Context->AuditOnClose = FALSE;
Context->OnDisk = NULL;
Context->OnDiskAllocated = 0;
Context->FixedValid = FALSE;
Context->VariableValid = FALSE;
//
// The following are meaningless at this point because of the
// values of the variables above, but we'll set them just to be
// neat.
//
Context->FixedDirty = FALSE;
Context->VariableDirty = FALSE;
Context->OnDiskUsed = 0;
Context->OnDiskFree = 0;
//
// Inititialize the IsDSObject to Registry Object and
// Object Name in DS to NULL. Later we will find out where
// the Object should actually exist
//
SetRegistryObject(Context);
Context->ObjectNameInDs = NULL;
//
// Add this new context to the set of valid contexts ...
//
SampAddNewValidContextAddress( Context );
//
// User and group context blocks are kept on linked lists
// from the domain's in-memory structure. Insert in the
// Appropriate List and then additionally for Account Objects
// Make the DS/Registry decision by looking at the TransactionDomain
//
Context->DomainIndex = SampTransactionDomainIndex;
switch (Type) {
case SampDomainObjectType:
InitializeListHead(
&(Context->TypeBody.Domain.DsEnumerationContext));
//////////////////////////////////////////////////////
// //
// Warning This case falls into the next one //
// //
//////////////////////////////////////////////////////
case SampServerObjectType:
InsertTailList(
&SampContextListHead,
&Context->ContextListEntry
);
break;
case SampUserObjectType:
// We must have set the Transaction Domain
ASSERT(SampTransactionWithinDomain);
// Set the DS or Registry Object Part
if (IsDsObject(SampDefinedDomains[SampTransactionDomainIndex].Context))
SetDsObject(Context);
// Insert into List
InsertTailList(
&SampDefinedDomains[SampTransactionDomainIndex].UserContextHead,
&Context->ContextListEntry
);
break;
case SampGroupObjectType:
// We must have set the Transaction Domain
ASSERT(SampTransactionWithinDomain);
// Set the DS or Registry Object Part
if (IsDsObject(SampDefinedDomains[SampTransactionDomainIndex].Context))
SetDsObject(Context);
// Insert into List
InsertTailList(
&SampDefinedDomains[SampTransactionDomainIndex].GroupContextHead,
&Context->ContextListEntry
);
break;
case SampAliasObjectType:
// We must have set the Transaction Domain
ASSERT(SampTransactionWithinDomain);
// Set the DS or Registry Object Part
if (IsDsObject(SampDefinedDomains[SampTransactionDomainIndex].Context))
SetDsObject(Context);
// Insert into List
InsertTailList(
&SampDefinedDomains[SampTransactionDomainIndex].AliasContextHead,
&Context->ContextListEntry
);
break;
}
}
return(Context);
}
VOID
SampDeleteContext(
IN PSAMP_OBJECT Context
)
/*++
Routine Description:
This service marks a context object for delete and dereferences it.
If this causes the reference count to go to zero, then the context
block will be immediately deleted (deallocated). Otherwise, the
context block will be deleted when the reference count finally does
go to zero.
THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS.
Arguments:
Context - Pointer to the context block to delete.
Return Value:
None.
--*/
{
NTSTATUS IgnoreStatus;
SAMTRACE("SampDeleteContext");
Context->MarkedForDelete = TRUE;
//
// Audit the close of this context.
//
(VOID) NtCloseObjectAuditAlarm (
&SampSamSubsystem,
(PVOID)Context,
Context->AuditOnClose
);
//
// Remove this context from the valid context set.
// Note that the context may have already been removed. This is
// not an error.
//
SampInvalidateContextAddress( Context );
//
// User and group context blocks are kept on linked lists
// from the domain's in-memory structure. Domain and
// server context blocks are kept on a global in-memory list.
// They are removed when they are marked for delete.
//
RemoveEntryList(&Context->ContextListEntry);
//
// We have to call dereference to counter the initial count of 1
// put on by create.
//
IgnoreStatus = SampDeReferenceContext( Context, FALSE );
return;
}
NTSTATUS
SampLookupContext(
IN PSAMP_OBJECT Context,
IN ACCESS_MASK DesiredAccess,
IN SAMP_OBJECT_TYPE ExpectedType,
OUT PSAMP_OBJECT_TYPE FoundType
)
/*++
Routine Description:
This service:
- Checks to make sure the Service state is one in which an
object can be looked up (i.e., not Initializing or Terminating).
- Makes sure the Service state is compatible with the lookup.
Non-trusted clients can only perform lookups when the Service
state is Enabled. If the client isn't trusted and the context
is for a group or user, then the state of that object's domain
must also be enabled
- Checks to make sure the context block represents the
type of object expected, and, if so:
- Checks to see that the caller has the requested (desired)
access, and, if so:
- Makes sure the object still exists, and opens it if it
does. Servers and domains can't be deleted, and so
their handle is left open.
- References the context block
Note that if the block is marked as TrustedClient, then access will
always be granted unless service state prevents it.
Also, if the ExpectedType is specified to be unknown, then any type
of context will be accepted.
If the type of object is found to be , Domain, Group or User, then the
this service will set the transaction domain.
THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS.
Arguments:
Context - Pointer to the context block to look-up.
DesiredAccess - The type of access the client is requesting to this
object. A zero-valued access mask may be specified. In this case,
the calling routine must do access validation.
ExpectedType - The type of object expected. This may be unknown. In
this case, the DesiredAccess should only include access types that
apply to any type of object (e.g., Delete, WriteDacl, et cetera).
FoundType - Receives the type of context actually found.
Return Value:
STATUS_SUCCESS - The context was found to be the type expected (or any
type if ExpectedType was unknown) and the DesiredAccesses are all
granted.
STATUS_OBJECT_TYPE_MISMATCH - Indicates the context was not the expected
type.
STATUS_ACCESS_DENIED - The desired access is not granted by this context.
--*/
{
NTSTATUS NtStatus;
SAMTRACE("SampLookupContext");
//
// Make sure we are in a legitimate state to at least access
// a context block. If we are initializing we have somehow allowed
// a connect through. This should never happen.
// If we are terminating, clients may still have handles (since we
// have no way to tell RPC they are no longer valid without the client
// calling us, Argh!). However, since we are terminating, the blocks
// are being cleaned up and may no longer be allocated.
//
ASSERT( SampServiceState != SampServiceInitializing );
if ( SampServiceState == SampServiceTerminating ) {
return(STATUS_INVALID_SERVER_STATE);
}
//
// Make sure the passed context address is (still) valid.
//
NtStatus = SampValidateContextAddress( Context );
if ( !NT_SUCCESS(NtStatus) ) {
return(NtStatus);
}
//
// Check type
//
(*FoundType) = Context->ObjectType;
if (ExpectedType != SampUnknownObjectType) {
if (ExpectedType != (*FoundType)) {
return(STATUS_OBJECT_TYPE_MISMATCH);
}
}
//
// if the object is either user or group, then we need to set the
// transaction domain.
if ((Context->ObjectType == SampDomainObjectType) ||
(Context->ObjectType == SampGroupObjectType) ||
(Context->ObjectType == SampAliasObjectType) ||
(Context->ObjectType == SampUserObjectType) ) {
SampSetTransactionDomain( Context->DomainIndex );
}
//
// If the client isn't trusted, then there are a number of things
// that will prevent them from continuing...
//
// If the service isn't enabled, we allow trusted clients to continue,
// but reject non-trusted client lookups.
//
if ( !Context->TrustedClient ) {
//
// The SAM service must be enabled
//
if (SampServiceState != SampServiceEnabled) {
return(STATUS_INVALID_SERVER_STATE);
}
//
// If the access is to a USER or GROUP and the client isn't trusted
// then the domain must be enabled or the operation is rejected.
//
if ( (Context->ObjectType == SampUserObjectType) ||
(Context->ObjectType == SampAliasObjectType) ||
(Context->ObjectType == SampGroupObjectType) ) {
if (SampDefinedDomains[Context->DomainIndex].CurrentFixed.ServerState
!= DomainServerEnabled) {
return(STATUS_INVALID_DOMAIN_STATE);
}
}
}
//
// Check the desired access ...
//
// There are several special cases:
//
// 1) The client is trusted. This is granted with no access check
// or role consistency check.
//
// 2) The caller specified 0 for desired access. This is used
// to close handles and is granted with no access check.
//
// 3) The role of the domain (for domain object operations) is
// inconsistent with the desired access.
//
//
if ( (!Context->TrustedClient) ) {
if (DesiredAccess != 0) {
if (!RtlAreAllAccessesGranted( Context->GrantedAccess, DesiredAccess)) {
return(STATUS_ACCESS_DENIED);
}
}
if ( (Context->ObjectType == SampDomainObjectType) ||
(Context->ObjectType == SampGroupObjectType) ||
(Context->ObjectType == SampAliasObjectType) ||
(Context->ObjectType == SampUserObjectType)
) {
//
// The state of the domain may have changed while the caller had
// the object open. In this case, the granted access mask may
// provide a write operation, but the role of the domain no longer
// allows un-trusted clients to perform write operations.
//
// Yuch.
//
if (SampDefinedDomains[Context->DomainIndex].CurrentFixed.ServerRole
!= DomainServerRolePrimary) {
if (RtlAreAnyAccessesGranted(
SampObjectInformation[ Context->ObjectType ].WriteOperations,
DesiredAccess) ) {
return(STATUS_INVALID_DOMAIN_ROLE);
}
}
}
}
//
// Make sure the object is still around (that is, somebody didn't delete
// it right out from under us).
//
NtStatus = SampCheckIfObjectExists(Context);
if (NT_SUCCESS(NtStatus)) {
//
// Reference the context
//
Context->ReferenceCount ++;
}
return(NtStatus);
}
VOID
SampReferenceContext(
IN PSAMP_OBJECT Context
)
/*++
Routine Description:
This service increments a context block's reference count.
THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS.
Arguments:
Context - Pointer to the context block to dreference.
Return Value:
None.
--*/
{
SAMTRACE("SampReferenceContext");
Context->ReferenceCount++;
return;
}
NTSTATUS
SampDeReferenceContext(
IN PSAMP_OBJECT Context,
IN BOOLEAN Commit
)
/*++
Routine Description:
This service decrements a context block's reference count.
If the reference count drops to zero, then the MarkedForDelete
flag is checked. If it is true, then the context block is
deallocated.
The attribute buffers are always deleted.
THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS.
Arguments:
Context - Pointer to the context block to de-reference.
Commit - if TRUE, the attribute buffers will be added to the RXACT.
Otherwise, they will just be ignored.
Return Value:
STATUS_SUCCESS - The service completed successfully.
Errors may be returned from SampStoreObjectAttributes().
--*/
{
NTSTATUS NtStatus, IgnoreStatus;
BOOLEAN TrustedClient;
SAMTRACE("SampDeReferenceContext");
ASSERT( Context->ReferenceCount != 0 );
Context->ReferenceCount --;
TrustedClient = Context->TrustedClient;
NtStatus = STATUS_SUCCESS;
if ( Context->OnDisk != NULL ) {
//
// There are attribute buffers for this context. Flush them if
// asked to do so.
// Use existing open keys
//
if ( Commit ) {
NtStatus = SampStoreObjectAttributes(Context, TRUE);
#if DBG
//
// SampFreeAttributeBuffer will assert if we try to free dirty
// buffers. This is generally useful, but if we're aborting
// (which is this case, Commit = FALSE) we don't want the assert
// so avoid it by pretending the buffers aren't dirty.
//
} else {
Context->FixedDirty = FALSE;
Context->VariableDirty = FALSE;
#endif
}
//
// Free the buffer that was being used to hold attributes.
//
SampFreeAttributeBuffer( Context );
}
if (Context->ReferenceCount == 0) {
//
// ReferenceCount has dropped to 0, see if we should delete this
// context.
//
if (Context->MarkedForDelete == TRUE) {
//
// Close the context block's root key.
// Domain and server contexts contain root key
// handles that are shared - so don't clean-up these
// if they match the ones in memory.
//
switch (Context->ObjectType) {
case SampServerObjectType:
if ((Context->RootKey != SampKey) &&
(Context->RootKey != INVALID_HANDLE_VALUE)) {
IgnoreStatus = NtClose( Context->RootKey );
ASSERT(NT_SUCCESS(IgnoreStatus));
}
break;
case SampDomainObjectType:
if (IsDsObject(Context))
{
// Free Any enumeration Context's
LIST_ENTRY * EnumerationList= (LIST_ENTRY *)
&(Context->TypeBody.Domain.DsEnumerationContext);
LIST_ENTRY * CurrentItem = EnumerationList->Flink ;
while(!IsListEmpty(EnumerationList))
{
LIST_ENTRY * ListItem;
ListItem = RemoveTailList(EnumerationList);
SampFreeRestart(((PSAMP_DS_ENUMERATION_CONTEXT)
ListItem)->Restart);
MIDL_user_free(ListItem);
}
// Free the DsName
MIDL_user_free(Context->ObjectNameInDs);
Context->ObjectNameInDs = NULL;
}
else
{
// Free all the Key Stuff
if ((Context->RootKey != SampDefinedDomains[Context->DomainIndex].Context->RootKey) &&
(Context->RootKey != INVALID_HANDLE_VALUE))
{
IgnoreStatus = NtClose( Context->RootKey );
ASSERT(NT_SUCCESS(IgnoreStatus));
}
}
break;
default:
if (IsDsObject(Context))
{
// Free the DSName
MIDL_user_free(Context->ObjectNameInDs);
Context->ObjectNameInDs = NULL;
}
else
{
//
// Close the root key handle
//
if (Context->RootKey != INVALID_HANDLE_VALUE)
{
IgnoreStatus = NtClose( Context->RootKey );
ASSERT(NT_SUCCESS(IgnoreStatus));
}
//
// Free the root key name
//
SampFreeUnicodeString( &(Context->RootName) );
}
}
#if SAMP_DIAGNOSTICS
IF_SAMP_GLOBAL( CONTEXT_TRACKING ) {
SampDiagPrint( CONTEXT_TRACKING, ("Deallocating ") );
if (Context->ObjectType == SampServerObjectType) SampDiagPrint(CONTEXT_TRACKING, ("Server "));
if (Context->ObjectType == SampDomainObjectType) SampDiagPrint(CONTEXT_TRACKING, (" Domain "));
if (Context->ObjectType == SampGroupObjectType) SampDiagPrint(CONTEXT_TRACKING, (" Group "));
if (Context->ObjectType == SampAliasObjectType) SampDiagPrint(CONTEXT_TRACKING, (" Alias "));
if (Context->ObjectType == SampUserObjectType) SampDiagPrint(CONTEXT_TRACKING, (" User "));
SampDiagPrint(CONTEXT_TRACKING, ("context : 0x%lx\n", Context ));
}
#endif //SAMP_DIAGNOSTICS
MIDL_user_free( Context );
//
// Decrement the number of active opens
//
if (!TrustedClient) {
ASSERT( SampActiveContextCount >= 1 );
SampActiveContextCount -= 1;
}
}
}
#if DBG
//
// Make sure a commit worked.
//
if (Commit) {
if (!NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_STORAGE_FAIL,
("SAM: Commit failure, status: 0x%lx\n",
NtStatus) );
IF_SAMP_GLOBAL( BREAK_ON_STORAGE_FAIL ) {
ASSERT(NT_SUCCESS(NtStatus));
}
}
}
#endif //DBG
return( NtStatus );
}
VOID
SampInvalidateContextAddress(
IN PSAMP_OBJECT Context
)
/*++
Routine Description:
This service removes a context from the set of valid contexts.
Note that we may have already removed the context. This is not an
error is expected to happen in the case where an object (like a user
or group) is deleted out from under an open handle.
THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS.
Arguments:
Context - Pointer to the context block to be removed from the set
of valid contexts. The ObjectType field of this context must
be valid.
Return Value:
None.
--*/
{
SAMTRACE("SampInvalidateContextAddress");
ASSERT( (Context->ObjectType == SampUserObjectType) ||
(Context->ObjectType == SampGroupObjectType) ||
(Context->ObjectType == SampAliasObjectType) ||
(Context->ObjectType == SampDomainObjectType) ||
(Context->ObjectType == SampServerObjectType)
);
Context->Valid = FALSE;
}
VOID
SampInvalidateGroupContexts(
IN ULONG Rid
)
/*++
Routine Description:
This service marks all group contexts open to the specified
group as being invalid. This is typically done because the
object has been deleted while there were open handles. All
registry keys related to this context are closed.
This is done by walking the list of group contexts hung off
the permanent in-memory domain structure.
THIS IS AN IRRIVERSIBLE OPERATION.
THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS.
Arguments:
Rid - The RID of the group being invalidated.
Return Value:
None.
--*/
{
NTSTATUS IgnoreStatus;
PLIST_ENTRY Head, NextEntry;
PSAMP_OBJECT NextContext;
SAMTRACE("SampInvalidateGroupContexts");
Head = &SampDefinedDomains[SampTransactionDomainIndex].GroupContextHead;
//
// Walk the list of active contexts checking for RID matches
//
NextEntry = Head->Flink;
while (NextEntry != Head) {
NextContext = CONTAINING_RECORD(
NextEntry,
SAMP_OBJECT,
ContextListEntry
);
if ( (Rid == NextContext->TypeBody.Group.Rid) &&
(NextContext->Valid == TRUE)
) {
NextContext->Valid = FALSE;
SampDiagPrint( CONTEXT_TRACKING, ("SAM: Invalidating group context 0x%lx : <%wZ>, handle = 0x%lx\n", NextContext, &NextContext->RootName, NextContext->RootKey));
if (NextContext->RootKey != INVALID_HANDLE_VALUE) {
SampDiagPrint( CONTEXT_TRACKING, ("SAM: Closing handle 0x%lx\n", NextContext->RootKey));
IgnoreStatus = NtClose(NextContext->RootKey);
ASSERT(NT_SUCCESS(IgnoreStatus));
NextContext->RootKey = INVALID_HANDLE_VALUE;
}
}
NextEntry = NextEntry->Flink;
}
return;
}
VOID
SampInvalidateAliasContexts(
IN ULONG Rid
)
/*++
Routine Description:
This service marks all alias contexts open to the specified
alias as being invalid. This is typically done because the
object has been deleted while there were open handles. All
registry keys related to this context are closed.
This is done by walking the list of alias contexts hung off
the permanent in-memory domain structure.
THIS IS AN IRRIVERSIBLE OPERATION.
THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS.
Arguments:
Rid - The RID of the alias being invalidated.
Return Value:
None.
--*/
{
NTSTATUS IgnoreStatus;
PLIST_ENTRY Head, NextEntry;
PSAMP_OBJECT NextContext;
SAMTRACE("SampInvalidateAliasContexts");
Head = &SampDefinedDomains[SampTransactionDomainIndex].AliasContextHead;
//
// Walk the list of active contexts checking for RID matches
//
NextEntry = Head->Flink;
while (NextEntry != Head) {
NextContext = CONTAINING_RECORD(
NextEntry,
SAMP_OBJECT,
ContextListEntry
);
if ( (Rid == NextContext->TypeBody.Alias.Rid) &&
(NextContext->Valid == TRUE)
) {
NextContext->Valid = FALSE;
SampDiagPrint( CONTEXT_TRACKING, ("SAM: Invalidating alias context 0x%lx : <%wZ>, handle = 0x%lx\n", NextContext, &NextContext->RootName, NextContext->RootKey));
if (NextContext->RootKey != INVALID_HANDLE_VALUE) {
SampDiagPrint( CONTEXT_TRACKING, ("SAM: Closing handle 0x%lx\n", NextContext->RootKey));
IgnoreStatus = NtClose(NextContext->RootKey);
ASSERT(NT_SUCCESS(IgnoreStatus));
NextContext->RootKey = INVALID_HANDLE_VALUE;
}
}
NextEntry = NextEntry->Flink;
}
return;
}
VOID
SampInvalidateUserContexts(
IN ULONG Rid
)
/*++
Routine Description:
This service marks all group contexts open to the specified
group as being invalid. This is typically done because the
object has been deleted while there were open handles. All
registry keys related to this context are closed.
This is done by walking the list of group contexts hung off
the permanent in-memory domain structure.
THIS IS AN IRRIVERSIBLE OPERATION.
THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS.
Arguments:
Rid - The RID of the group being invalidated.
Return Value:
None.
--*/
{
NTSTATUS IgnoreStatus;
PLIST_ENTRY Head, NextEntry;
PSAMP_OBJECT NextContext;
SAMTRACE("SampInvalidateUserContexts");
Head = &SampDefinedDomains[SampTransactionDomainIndex].UserContextHead;
//
// Walk the list of active contexts checking for RID matches
//
NextEntry = Head->Flink;
while (NextEntry != Head) {
NextContext = CONTAINING_RECORD(
NextEntry,
SAMP_OBJECT,
ContextListEntry
);
if ( (Rid == NextContext->TypeBody.User.Rid) &&
(NextContext->Valid == TRUE)
) {
NextContext->Valid = FALSE;
SampDiagPrint( CONTEXT_TRACKING, ("SAM: Invalidating user context 0x%lx : <%wZ>, handle = 0x%lx\n", NextContext, &NextContext->RootName, NextContext->RootKey));
if (NextContext->RootKey != INVALID_HANDLE_VALUE) {
SampDiagPrint( CONTEXT_TRACKING, ("SAM: Closing handle 0x%lx\n", NextContext->RootKey));
IgnoreStatus = NtClose(NextContext->RootKey);
ASSERT(NT_SUCCESS(IgnoreStatus));
NextContext->RootKey = INVALID_HANDLE_VALUE;
}
}
NextEntry = NextEntry->Flink;
}
}
VOID
SampInvalidateContextListKeys(
IN PLIST_ENTRY Head,
IN BOOLEAN Close
)
/*++
Routine Description:
Marks all registry handles invalid in the contexts in the passed list.
Used after a registry hive refresh.
Arguments:
Close : If TRUE the registry handles are closed before invalidation
Return Value:
None.
--*/
{
NTSTATUS IgnoreStatus;
PLIST_ENTRY NextEntry;
SAMTRACE("SampInvalidateContextListKeys");
NextEntry = Head->Flink;
while (NextEntry != Head) {
PSAMP_OBJECT NextContext;
NextContext = CONTAINING_RECORD(
NextEntry,
SAMP_OBJECT,
ContextListEntry
);
SampDiagPrint( CONTEXT_TRACKING, ("SAM: Invalidating key for context 0x%lx : <%wZ>, handle = 0x%lx\n", NextContext, &NextContext->RootName, NextContext->RootKey));
if (Close && (NextContext->RootKey != INVALID_HANDLE_VALUE)) {
SampDiagPrint( CONTEXT_TRACKING, ("SAM: Closing handle 0x%lx\n", NextContext->RootKey));
IgnoreStatus = NtClose( NextContext->RootKey );
ASSERT(NT_SUCCESS(IgnoreStatus));
}
NextContext->RootKey = INVALID_HANDLE_VALUE;
NextEntry = NextEntry->Flink;
}
}
#ifdef SAMP_DIAGNOSTICS
VOID
SampDumpContext(
IN PSAMP_OBJECT Context
)
/*++
Routine Description:
This service prints out info on a context to debugger
Arguments:
Context - a context
Return Value:
None.
--*/
{
PSTR Type;
switch (Context->ObjectType) {
case SampServerObjectType:
Type = "S";
break;
case SampDomainObjectType:
if (Context == SampDefinedDomains[Context->DomainIndex].Context) {
Type = "d";
} else {
Type = "D";
}
break;
case SampUserObjectType:
Type = "U";
break;
case SampAliasObjectType:
Type = "A";
break;
case SampGroupObjectType:
Type = "G";
break;
}
KdPrint(("%s 0x%8x %2d 0x%8x %s %s %s %wZ\n",
Type,
Context,
Context->ReferenceCount,
Context->RootKey,
Context->MarkedForDelete ? "D": " ",
Context->Valid ? " ": "NV",
Context->TrustedClient ? "TC": " ",
&Context->RootName
));
}
VOID
SampDumpContexts(
VOID
)
/*++
Routine Description:
Prints out info on all contexts
Arguments:
Return Value:
None.
--*/
{
PLIST_ENTRY NextEntry;
PLIST_ENTRY Head;
ULONG Servers;
ULONG Domains;
ULONG Domain0Users;
ULONG Domain0Aliases;
ULONG Domain0Groups;
ULONG Domain1Users;
ULONG Domain1Aliases;
ULONG Domain1Groups;
Domains = 0;
Servers = 0;
Head = &SampContextListHead;
NextEntry = Head->Flink;
while (NextEntry != Head) {
PSAMP_OBJECT NextContext;
NextContext = CONTAINING_RECORD(
NextEntry,
SAMP_OBJECT,
ContextListEntry
);
switch (NextContext->ObjectType) {
case SampServerObjectType:
(Servers)++;
break;
case SampDomainObjectType:
(Domains)++;
break;
default:
ASSERT(FALSE);
break;
}
SampDumpContext(NextContext);
NextEntry = NextEntry->Flink;
}
Domain0Users = 0;
Head = &SampDefinedDomains[0].UserContextHead;
NextEntry = Head->Flink;
while (NextEntry != Head) {
PSAMP_OBJECT NextContext;
(Domain0Users) ++;
NextContext = CONTAINING_RECORD(
NextEntry,
SAMP_OBJECT,
ContextListEntry
);
ASSERT (NextContext->ObjectType == SampUserObjectType);
SampDumpContext(NextContext);
NextEntry = NextEntry->Flink;
}
Domain1Users = 0;
Head = &SampDefinedDomains[1].UserContextHead;
NextEntry = Head->Flink;
while (NextEntry != Head) {
PSAMP_OBJECT NextContext;
(Domain1Users) ++;
NextContext = CONTAINING_RECORD(
NextEntry,
SAMP_OBJECT,
ContextListEntry
);
ASSERT (NextContext->ObjectType == SampUserObjectType);
SampDumpContext(NextContext);
NextEntry = NextEntry->Flink;
}
Domain0Groups = 0;
Head = &SampDefinedDomains[0].GroupContextHead;
NextEntry = Head->Flink;
while (NextEntry != Head) {
PSAMP_OBJECT NextContext;
(Domain0Groups) ++;
NextContext = CONTAINING_RECORD(
NextEntry,
SAMP_OBJECT,
ContextListEntry
);
ASSERT (NextContext->ObjectType == SampGroupObjectType);
SampDumpContext(NextContext);
NextEntry = NextEntry->Flink;
}
Domain1Groups = 0;
Head = &SampDefinedDomains[1].GroupContextHead;
NextEntry = Head->Flink;
while (NextEntry != Head) {
PSAMP_OBJECT NextContext;
(Domain1Groups) ++;
NextContext = CONTAINING_RECORD(
NextEntry,
SAMP_OBJECT,
ContextListEntry
);
ASSERT (NextContext->ObjectType == SampGroupObjectType);
SampDumpContext(NextContext);
NextEntry = NextEntry->Flink;
}
Domain0Aliases = 0;
Head = &SampDefinedDomains[0].AliasContextHead;
NextEntry = Head->Flink;
while (NextEntry != Head) {
PSAMP_OBJECT NextContext;
(Domain0Aliases) ++;
NextContext = CONTAINING_RECORD(
NextEntry,
SAMP_OBJECT,
ContextListEntry
);
ASSERT (NextContext->ObjectType == SampAliasObjectType);
SampDumpContext(NextContext);
NextEntry = NextEntry->Flink;
}
Domain1Aliases = 0;
Head = &SampDefinedDomains[1].AliasContextHead;
NextEntry = Head->Flink;
while (NextEntry != Head) {
PSAMP_OBJECT NextContext;
(Domain1Aliases) ++;
NextContext = CONTAINING_RECORD(
NextEntry,
SAMP_OBJECT,
ContextListEntry
);
ASSERT (NextContext->ObjectType == SampAliasObjectType);
SampDumpContext(NextContext);
NextEntry = NextEntry->Flink;
}
KdPrint(("SAM: Active untrusted contexts = %d\n", SampActiveContextCount));
KdPrint((" Server = %4d Domain = %4d\n", Servers, Domains));
KdPrint((" Users0 = %4d Groups0 = %4d Aliases0 = %4d\n", Domain0Users, Domain0Aliases, Domain0Groups));
KdPrint((" Users1 = %4d Groups1 = %4d Aliases1 = %4d\n", Domain1Users, Domain1Aliases, Domain1Groups));
}
#endif //SAMP_DIAGNOSTICS
///////////////////////////////////////////////////////////////////////////////
// //
// private service Implementations //
// //
///////////////////////////////////////////////////////////////////////////////
VOID
SampAddNewValidContextAddress(
IN PSAMP_OBJECT NewContext
)
/*++
Routine Description:
This service adds the new context to the set of valid contexts.
THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS.
Arguments:
NewContext - Pointer to the context block to be added to the set
of valid contexts. The ObjectType field of this context must
be set.
Return Value:
None.
--*/
{
SAMTRACE("SampAddNewValidContextAddress");
ASSERT( (NewContext->ObjectType == SampUserObjectType) ||
(NewContext->ObjectType == SampGroupObjectType) ||
(NewContext->ObjectType == SampAliasObjectType) ||
(NewContext->ObjectType == SampDomainObjectType) ||
(NewContext->ObjectType == SampServerObjectType)
);
NewContext->Valid = TRUE;
}
NTSTATUS
SampValidateContextAddress(
IN PSAMP_OBJECT Context
)
/*++
Routine Description:
This service checks to make sure a context is still valid.
Note that even though RPC still thinks we have a context related
to a SAM_HANDLE, we may, in fact, have deleted it out from under
the user. Since there is no way to inform RPC of this, we must
suffer, and wait until RPC calls us (either with a call by the client
or to rundown the context handle). This sucks, but there apparently
isn't any other way around it.
WARNING - IT IS ASSUMED THE CONTEXT WAS ONCE VALID. IT MAY HAVE
BEEN INVALIDATED, BUT IF YOU ARE CALLING THIS ROUTINE
IT BETTER STILL HAVE A NON-ZERO REFERENCE COUNT. THIS
COULD BE CHANGED IN THE FUTURE, BUT IT WOULD REQUIRE
KEEPING A LIST OF VALID DOMAINS AND PERFORMING THE BULK
OF THIS ROUTINE INSIDE A TRY-EXCEPT CLAUSE. YOU COULD
LOCATE THE CONTEXT'S DOMAIN (WHICH MIGHT ACCESS VIOLATE)
AND THEN MAKE SURE THAT DOMAIN IS VALID. THEN WALK THAT
DOMAIN'S LIST TO ENSURE THE USER OR GROUP IS VALID.
THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS.
Arguments:
Context - Pointer to the context block to be validated as still being
a valid context. The ObjectType field of this context must
be valid.
Return Value:
STATUS_SUCCESS - The context is still valid.
STATUS_INVALID_HANDLE - The context is no longer valid and the handle
that caused the reference should be invalidated as well. When the
handle is invalidated, the context should be closed (deleted).
STATUS_NO_SUCH_CONTEXT - This value is not yet returned by this routine.
It may be added in the future to distinguish between an attempt to
use a context that has been invalidated and an attempt to use a
context that doesn't exist. The prior being a legitimate condition,
the later representing a bug-check condition.
--*/
{
SAMTRACE("SampValidateContextAddress");
ASSERT( (Context->ObjectType == SampUserObjectType) ||
(Context->ObjectType == SampGroupObjectType) ||
(Context->ObjectType == SampAliasObjectType) ||
(Context->ObjectType == SampDomainObjectType) ||
(Context->ObjectType == SampServerObjectType)
);
if (Context->Valid != TRUE) {
return(STATUS_INVALID_HANDLE);
}
return(STATUS_SUCCESS);
}
NTSTATUS
SampCheckIfObjectExists(
IN PSAMP_OBJECT Context
)
/*++
Routine Description:
Checks to see if the object exists in the DS/ Registry.
Will fill out the following information
1. If DS object its DSNAME, which must exist
2. If Registry object, Open its Root Key and fill out
the handle of the Root Key in the registry.
IMPORTANT NOTE:
In the Registry Case of SAM once the key is open nobody can
delete the object. However in the DS case this is not so. Currently
we have only the DS Name of the Object in Hand, and we have no way
of Locking the Object, for access. Since this is the case
Arguments:
Context -- Pointer to a Context block desribing the Object
Rid -- Rid of the desired Object
Return Values:
STATUS_SUCCESS if everything succeeds
Error codes from Registry Manipulation / DsLayer.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
ULONG Rid;
//
// Check wether the Object is located or not.
// Location is the process of finding out
// 1. DSNAME of the object if it is in the DS. An object with this
// DSNAME must exist.
//
// 2. The Root Key HANDLE of the Object if it is in the Registry
//
if (!SampIsObjectLocated(Context)) {
//
// No we first need to locate the Object.
// This is done by using the Rid for Account Objects
// For Domain Objects we already cache all the defined domains, so fill out
// From the Cache
// BUG: For Server Objects Don't know what to do.
//
switch (Context->ObjectType) {
case SampGroupObjectType:
SampDiagPrint( CONTEXT_TRACKING, ("SAM: Reopened group handle <%wZ>,", &Context->RootName));
Rid = Context->TypeBody.Group.Rid;
break;
case SampAliasObjectType:
SampDiagPrint( CONTEXT_TRACKING, ("SAM: Reopened alias handle <%wZ>,", &Context->RootName));
Rid = Context->TypeBody.Alias.Rid;
break;
case SampUserObjectType:
SampDiagPrint( CONTEXT_TRACKING, ("SAM: Reopened user handle <%wZ>,", &Context->RootName));
Rid = Context->TypeBody.User.Rid;
break;
case SampDomainObjectType:
//
// Domain objects share the root key and the object name in the DS that
// we keep around in the in memory domain context for each domain
//
ASSERT(Context != SampDefinedDomains[Context->DomainIndex].Context);
Context->RootKey = SampDefinedDomains[Context->DomainIndex].Context->RootKey;
Context->ObjectNameInDs = SampDefinedDomains[Context->DomainIndex].Context->ObjectNameInDs;
Context->ObjectNameInDs = SampDefinedDomains[Context->DomainIndex].Context->ObjectNameInDs;
ASSERT(SampIsObjectLocated(Context));
SampDiagPrint( CONTEXT_TRACKING, ("SAM: Recopied domain context handle <%wZ>, 0x%lx\n", &Context->RootName, Context->RootKey));
goto ObjectLocated;
case SampServerObjectType:
//
// Server objects share our global root key
//
Context->RootKey = SampKey;
ASSERT(SampIsObjectLocated(Context));
SampDiagPrint( CONTEXT_TRACKING, ("SAM: Recopied server context handle <%wZ>, 0x%lx\n", &Context->RootName, Context->RootKey));
goto ObjectLocated;
}
//
// Go open the appropriate account key/ or find object Name from RID
//
NtStatus = SampLocateObject(Context, Rid);
ObjectLocated:
;;
}
return NtStatus;
}
BOOLEAN
SampIsObjectLocated(
IN PSAMP_OBJECT Context
)
/*++
Description:
Checks if an object has been located in the DS or in the registry
An Object being Located implies the Following
1. For a DS Object we have the DS Name
2. For a Registry Object we have a Valid Open Registry Key for
the Object.
Arguments:
Context -- Pointer to a Context block desribing the Object
Return Values:
TRUE -- If Conditions above are satisfied
FALSE -- If Conditions above are not satisfied
--*/
{
if (IsDsObject(Context))
return (Context->ObjectNameInDs != NULL);
else
return (Context->RootKey != INVALID_HANDLE_VALUE);
}
NTSTATUS
SampLocateObject(
IN PSAMP_OBJECT Context,
IN ULONG Rid
)
/*++
Description:
Uses the Rid to find the Object in either the DS or
the Registry.
NOTE:
This routine is meaningful for Context's that represent
Account Objects Only.
Arguments:
Context -- Pointer to a Context block desribing the Object
Rid -- Rid of the desired Object
Return Values:
STATUS_SUCCESS if everything succeeds
Error codes from Registry Manipulation / DsLayer.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PSAMP_OBJECT DomainContext = NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
//
// This routine can be called only for Account Objects
//
ASSERT((Context->ObjectType == SampGroupObjectType)
|| (Context->ObjectType == SampAliasObjectType)
|| (Context->ObjectType == SampUserObjectType)
);
//
// Get the Domain Object, as we will need this to find out
// to find out in which domain we look for the Rid.
//
DomainContext = SampDefinedDomains[Context->DomainIndex].Context;
// Now Make the Decision
if (IsDsObject(Context))
{
//
// Object is in the DS
//
// Look it up using the Rid
Status = SampDsLookupObjectByRid(DomainContext->ObjectNameInDs, Rid, &Context->ObjectNameInDs);
if (!NT_SUCCESS(Status))
{
Context->ObjectNameInDs = NULL;
}
}
else
{
// Object Should be in Registry
SetRegistryObject(Context);
InitializeObjectAttributes(
&ObjectAttributes,
&Context->RootName,
OBJ_CASE_INSENSITIVE,
SampKey,
NULL
);
SampDumpNtOpenKey((KEY_READ | KEY_WRITE), &ObjectAttributes, 0);
// Try opening the Key
Status = RtlpNtOpenKey(
&Context->RootKey,
(KEY_READ | KEY_WRITE),
&ObjectAttributes,
0
);
if (!NT_SUCCESS(Status))
{
Context->RootKey = INVALID_HANDLE_VALUE;
}
}
return Status;
}