946 lines
24 KiB
C
946 lines
24 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
server.c
|
||
|
||
Abstract:
|
||
|
||
This file contains services related to the SAM "server" object.
|
||
|
||
|
||
Author:
|
||
|
||
Jim Kelly (JimK) 4-July-1991
|
||
|
||
Environment:
|
||
|
||
User Mode - Win32
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Includes //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
#include <samsrvp.h>
|
||
|
||
|
||
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// private service prototypes //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Routines //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SamrConnect2(
|
||
IN PSAMPR_SERVER_NAME ServerName,
|
||
OUT SAMPR_HANDLE * ServerHandle,
|
||
IN ACCESS_MASK DesiredAccess
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This service is the dispatch routine for SamConnect. It performs
|
||
an access validation to determine whether the caller may connect
|
||
to SAM for the access specified. If so, a context block is established.
|
||
This is different from the SamConnect call in that the entire server
|
||
name is passed instead of just the first character.
|
||
|
||
|
||
Arguments:
|
||
|
||
ServerName - Name of the node this SAM reside on. Ignored by this
|
||
routine.
|
||
|
||
ServerHandle - If the connection is successful, the value returned
|
||
via this parameter serves as a context handle to the openned
|
||
SERVER object.
|
||
|
||
DesiredAccess - Specifies the accesses desired to the SERVER object.
|
||
|
||
|
||
Return Value:
|
||
|
||
Status values returned by SamIConnect().
|
||
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN TrustedClient;
|
||
|
||
SAMTRACE("SamrConnect2");
|
||
|
||
//
|
||
// If we ever want to support trusted remote clients, then the test
|
||
// for whether or not the client is trusted can be made here and
|
||
// TrustedClient set appropriately. For now, all remote clients are
|
||
// considered untrusted.
|
||
|
||
// All clients are considered trusted clients for user mode SAM. This
|
||
// is because the NtAccessCheckAndAuditAlarm returns
|
||
// STATUS_PRIVILEGE_NOT_HELD while running from a user account as opposed
|
||
// to being part of the system
|
||
|
||
#ifdef USER_MODE_SAM
|
||
TrustedClient = TRUE;
|
||
#else
|
||
TrustedClient = FALSE;
|
||
#endif
|
||
|
||
return SamIConnect(ServerName, ServerHandle, DesiredAccess, TrustedClient );
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SamrConnect(
|
||
IN PSAMPR_SERVER_NAME ServerName,
|
||
OUT SAMPR_HANDLE * ServerHandle,
|
||
IN ACCESS_MASK DesiredAccess
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This service is the dispatch routine for SamConnect. It performs
|
||
an access validation to determine whether the caller may connect
|
||
to SAM for the access specified. If so, a context block is established
|
||
|
||
|
||
Arguments:
|
||
|
||
ServerName - Name of the node this SAM reside on. Ignored by this
|
||
routine. The name contains only a single character.
|
||
|
||
ServerHandle - If the connection is successful, the value returned
|
||
via this parameter serves as a context handle to the openned
|
||
SERVER object.
|
||
|
||
DesiredAccess - Specifies the accesses desired to the SERVER object.
|
||
|
||
|
||
Return Value:
|
||
|
||
Status values returned by SamIConnect().
|
||
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN TrustedClient;
|
||
|
||
SAMTRACE("SamrConnect");
|
||
|
||
|
||
//
|
||
// If we ever want to support trusted remote clients, then the test
|
||
// for whether or not the client is trusted can be made here and
|
||
// TrustedClient set appropriately. For now, all remote clients are
|
||
// considered untrusted.
|
||
|
||
// All clients are considered trusted clients for user mode SAM. This
|
||
// is because the NT call for checking access will return
|
||
// STATUS_PRIVILEGE_NOT_HELD while running from a user account as opposed
|
||
// to being part of the system
|
||
|
||
#ifdef USER_MODE_SAM
|
||
TrustedClient = TRUE;
|
||
#else
|
||
TrustedClient = FALSE;
|
||
#endif
|
||
|
||
return SamIConnect(NULL, ServerHandle, DesiredAccess, TrustedClient );
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SamIConnect(
|
||
IN PSAMPR_SERVER_NAME ServerName,
|
||
OUT SAMPR_HANDLE * ServerHandle,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN BOOLEAN TrustedClient
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This service is the dispatch routine for SamConnect. It performs
|
||
an access validation to determine whether the caller may connect
|
||
to SAM for the access specified. If so, a context block is established
|
||
|
||
|
||
NOTE: If the caller is trusted, then the DesiredAccess parameter may
|
||
NOT contain any Generic access types or MaximumAllowed. All
|
||
mapping must be done by the caller.
|
||
|
||
Arguments:
|
||
|
||
ServerName - Name of the node this SAM reside on. Ignored by this
|
||
routine.
|
||
|
||
ServerHandle - If the connection is successful, the value returned
|
||
via this parameter serves as a context handle to the openned
|
||
SERVER object.
|
||
|
||
DesiredAccess - Specifies the accesses desired to the SERVER object.
|
||
|
||
TrustedClient - Indicates whether the client is known to be part of
|
||
the trusted computer base (TCB). If so (TRUE), no access validation
|
||
is performed and all requested accesses are granted. If not
|
||
(FALSE), then the client is impersonated and access validation
|
||
performed against the SecurityDescriptor on the SERVER object.
|
||
|
||
Return Value:
|
||
|
||
|
||
STATUS_SUCCESS - The SERVER object has been successfully openned.
|
||
|
||
STATUS_INSUFFICIENT_RESOURCES - The SAM server processes doesn't
|
||
have sufficient resources to process or accept another connection
|
||
at this time.
|
||
|
||
Other values as may be returned from:
|
||
|
||
NtAccessCheckAndAuditAlarm()
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS NtStatus;
|
||
PSAMP_OBJECT Context;
|
||
|
||
UNREFERENCED_PARAMETER( ServerName ); //Ignored by this routine
|
||
|
||
SAMTRACE("SamIConnect");
|
||
|
||
//
|
||
// If the SAM server is not initialized, reject the connection.
|
||
//
|
||
|
||
if (SampServiceState != SampServiceEnabled) {
|
||
|
||
return(STATUS_INVALID_SERVER_STATE);
|
||
}
|
||
|
||
SampAcquireReadLock();
|
||
|
||
|
||
Context = SampCreateContext( SampServerObjectType, TrustedClient );
|
||
|
||
if (Context != NULL) {
|
||
|
||
//
|
||
// The RootKey for a SERVER object is the root of the SAM database.
|
||
// This key should not be closed when the context is deleted.
|
||
//
|
||
|
||
Context->RootKey = SampKey;
|
||
|
||
//
|
||
// The rootkeyname has been initialized to NULL inside CreateContext.
|
||
//
|
||
|
||
//
|
||
// Perform access validation ...
|
||
//
|
||
|
||
NtStatus = SampValidateObjectAccess(
|
||
Context, //Context
|
||
DesiredAccess, //DesiredAccess
|
||
FALSE //ObjectCreation
|
||
);
|
||
|
||
|
||
|
||
//
|
||
// if we didn't pass the access test, then free up the context block
|
||
// and return the error status returned from the access validation
|
||
// routine. Otherwise, return the context handle value.
|
||
//
|
||
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
SampDeleteContext( Context );
|
||
} else {
|
||
(*ServerHandle) = Context;
|
||
}
|
||
|
||
} else {
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Free the read lock
|
||
//
|
||
|
||
SampReleaseReadLock();
|
||
|
||
return(NtStatus);
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SamrShutdownSamServer(
|
||
IN SAMPR_HANDLE ServerHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This service shuts down the SAM server.
|
||
|
||
In the long run, this routine will perform an orderly shutdown.
|
||
In the short term, it is useful for debug purposes to shutdown
|
||
in a brute force un-orderly fashion.
|
||
|
||
Arguments:
|
||
|
||
ServerHandle - Received from a previous call to SamIConnect().
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - The services completed successfully.
|
||
|
||
|
||
STATUS_ACCESS_DENIED - The caller doesn't have the appropriate access
|
||
to perform the requested operation.
|
||
|
||
|
||
--*/
|
||
{
|
||
|
||
NTSTATUS NtStatus, IgnoreStatus;
|
||
PSAMP_OBJECT ServerContext;
|
||
SAMP_OBJECT_TYPE FoundType;
|
||
|
||
SAMTRACE("SamrShutdownSamServer");
|
||
|
||
|
||
|
||
NtStatus = SampAcquireWriteLock();
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
return(NtStatus);
|
||
}
|
||
|
||
//
|
||
// Validate type of, and access to server object.
|
||
//
|
||
|
||
ServerContext = (PSAMP_OBJECT)ServerHandle;
|
||
NtStatus = SampLookupContext(
|
||
ServerContext,
|
||
SAM_SERVER_SHUTDOWN, // DesiredAccess
|
||
SampServerObjectType, // ExpectedType
|
||
&FoundType
|
||
);
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
|
||
//
|
||
// Signal the event that will cut loose the main thread.
|
||
// The main thread will then exit - causing the walls to
|
||
// come tumbling down.
|
||
//
|
||
|
||
IgnoreStatus = RpcMgmtStopServerListening(0);
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
|
||
|
||
|
||
//
|
||
// De-reference the server object
|
||
//
|
||
|
||
IgnoreStatus = SampDeReferenceContext( ServerContext, FALSE );
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
}
|
||
|
||
//
|
||
// Free the write lock and roll-back the transaction
|
||
//
|
||
|
||
IgnoreStatus = SampReleaseWriteLock( FALSE );
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
|
||
return(NtStatus);
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SamrLookupDomainInSamServer(
|
||
IN SAMPR_HANDLE ServerHandle,
|
||
IN PRPC_UNICODE_STRING Name,
|
||
OUT PRPC_SID *DomainId
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This service
|
||
|
||
Arguments:
|
||
|
||
ServerHandle - A context handle returned by a previous call
|
||
to SamConnect().
|
||
|
||
Name - contains the name of the domain to look up.
|
||
|
||
DomainSid - Receives a pointer to a buffer containing the SID of
|
||
the domain. The buffer pointed to must be deallocated by the
|
||
caller using MIDL_user_free() when no longer needed.
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
STATUS_SUCCESS - The services completed successfully.
|
||
|
||
STATUS_ACCESS_DENIED - The caller doesn't have the appropriate access
|
||
to perform the requested operation.
|
||
|
||
STATUS_NO_SUCH_DOMAIN - The specified domain does not exist at this
|
||
server.
|
||
|
||
|
||
STATUS_INVALID_SERVER_STATE - Indicates the SAM server is currently
|
||
disabled.
|
||
|
||
|
||
|
||
|
||
--*/
|
||
{
|
||
|
||
NTSTATUS NtStatus, IgnoreStatus;
|
||
PSAMP_OBJECT ServerContext;
|
||
SAMP_OBJECT_TYPE FoundType;
|
||
ULONG i, SidLength;
|
||
BOOLEAN DomainFound;
|
||
PSID FoundSid;
|
||
|
||
SAMTRACE("SamrLookupDomainInSamServer");
|
||
|
||
|
||
//
|
||
// Make sure we understand what RPC is doing for (to) us.
|
||
//
|
||
|
||
ASSERT (DomainId != NULL);
|
||
ASSERT ((*DomainId) == NULL);
|
||
|
||
|
||
|
||
|
||
|
||
|
||
SampAcquireReadLock();
|
||
|
||
|
||
//
|
||
// Validate type of, and access to object.
|
||
//
|
||
|
||
ServerContext = (PSAMP_OBJECT)ServerHandle;
|
||
NtStatus = SampLookupContext(
|
||
ServerContext,
|
||
SAM_SERVER_LOOKUP_DOMAIN,
|
||
SampServerObjectType, // ExpectedType
|
||
&FoundType
|
||
);
|
||
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
|
||
|
||
//
|
||
// Set our default completion status
|
||
//
|
||
|
||
NtStatus = STATUS_NO_SUCH_DOMAIN;
|
||
|
||
|
||
//
|
||
// Search the list of defined domains for a match.
|
||
//
|
||
|
||
DomainFound = FALSE;
|
||
for (i = 0;
|
||
(i<SampDefinedDomainsCount && (!DomainFound));
|
||
i++ ) {
|
||
|
||
if (RtlEqualDomainName(&SampDefinedDomains[i].ExternalName, (PUNICODE_STRING)Name) ) {
|
||
|
||
|
||
DomainFound = TRUE;
|
||
|
||
|
||
//
|
||
// Allocate and fill in the return buffer
|
||
//
|
||
|
||
SidLength = RtlLengthSid( SampDefinedDomains[i].Sid );
|
||
FoundSid = MIDL_user_allocate( SidLength );
|
||
if (FoundSid != NULL) {
|
||
NtStatus =
|
||
RtlCopySid( SidLength, FoundSid, SampDefinedDomains[i].Sid );
|
||
|
||
if (!NT_SUCCESS(NtStatus) ) {
|
||
MIDL_user_free( FoundSid );
|
||
NtStatus = STATUS_INTERNAL_ERROR;
|
||
}
|
||
|
||
(*DomainId) = FoundSid;
|
||
}
|
||
|
||
|
||
NtStatus = STATUS_SUCCESS;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// De-reference the object
|
||
//
|
||
|
||
if ( NT_SUCCESS( NtStatus ) ) {
|
||
|
||
NtStatus = SampDeReferenceContext( ServerContext, FALSE );
|
||
|
||
} else {
|
||
|
||
IgnoreStatus = SampDeReferenceContext( ServerContext, FALSE );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Free the read lock
|
||
//
|
||
|
||
SampReleaseReadLock();
|
||
|
||
|
||
|
||
|
||
return(NtStatus);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SamrEnumerateDomainsInSamServer(
|
||
IN SAMPR_HANDLE ServerHandle,
|
||
IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext,
|
||
OUT PSAMPR_ENUMERATION_BUFFER *Buffer,
|
||
IN ULONG PreferedMaximumLength,
|
||
OUT PULONG CountReturned
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This API lists all the domains defined in the account database.
|
||
Since there may be more domains than can fit into a buffer, the
|
||
caller is provided with a handle that can be used across calls to
|
||
the API. On the initial call, EnumerationContext should point to a
|
||
SAM_ENUMERATE_HANDLE variable that is set to 0.
|
||
|
||
If the API returns STATUS_MORE_ENTRIES, then the API should be
|
||
called again with EnumerationContext. When the API returns
|
||
STATUS_SUCCESS or any error return, the handle becomes invalid for
|
||
future use.
|
||
|
||
This API requires SAM_SERVER_ENUMERATE_DOMAINS access to the
|
||
SamServer object.
|
||
|
||
Arguments:
|
||
|
||
ConnectHandle - Handle obtained from a previous SamConnect call.
|
||
|
||
EnumerationContext - API specific handle to allow multiple calls
|
||
(see below). This is a zero based index.
|
||
|
||
Buffer - Receives a pointer to the buffer where the information
|
||
is placed. The information returned is contiguous
|
||
SAM_RID_ENUMERATION data structures. However, the
|
||
RelativeId field of each of these structures is not valid.
|
||
This buffer must be freed when no longer needed using
|
||
SamFreeMemory().
|
||
|
||
PreferedMaximumLength - Prefered maximum length of returned data
|
||
(in 8-bit bytes). This is not a hard upper limit, but serves
|
||
as a guide to the server. Due to data conversion between
|
||
systems with different natural data sizes, the actual amount
|
||
of data returned may be greater than this value.
|
||
|
||
CountReturned - Number of entries returned.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - The Service completed successfully, and there
|
||
are no addition entries.
|
||
|
||
STATUS_MORE_ENTRIES - There are more entries, so call again.
|
||
This is a successful return.
|
||
|
||
STATUS_ACCESS_DENIED - Caller does not have the access required
|
||
to enumerate the domains.
|
||
|
||
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
||
|
||
STATUS_INVALID_SERVER_STATE - Indicates the SAM server is
|
||
currently disabled.
|
||
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS NtStatus, IgnoreStatus;
|
||
ULONG i;
|
||
PSAMP_OBJECT Context;
|
||
SAMP_OBJECT_TYPE FoundType;
|
||
ULONG TotalLength = 0;
|
||
ULONG NewTotalLength;
|
||
PSAMP_ENUMERATION_ELEMENT SampHead, NextEntry, NewEntry;
|
||
BOOLEAN LengthLimitReached = FALSE;
|
||
PSAMPR_RID_ENUMERATION ArrayBuffer;
|
||
ULONG ArrayBufferLength;
|
||
|
||
SAMTRACE("SamrEnumerateDomainsInSamServer");
|
||
|
||
|
||
//
|
||
// Make sure we understand what RPC is doing for (to) us.
|
||
//
|
||
|
||
ASSERT (ServerHandle != NULL);
|
||
ASSERT (EnumerationContext != NULL);
|
||
ASSERT ( Buffer != NULL);
|
||
ASSERT ((*Buffer) == NULL);
|
||
ASSERT (CountReturned != NULL);
|
||
|
||
|
||
//
|
||
// Initialize the list of names being returned.
|
||
// This is a singly linked list.
|
||
//
|
||
|
||
SampHead = NULL;
|
||
|
||
|
||
//
|
||
// Initialize the count returned
|
||
//
|
||
|
||
(*CountReturned) = 0;
|
||
|
||
|
||
|
||
|
||
|
||
|
||
SampAcquireReadLock();
|
||
|
||
|
||
//
|
||
// Validate type of, and access to object.
|
||
//
|
||
|
||
Context = (PSAMP_OBJECT)ServerHandle;
|
||
NtStatus = SampLookupContext(
|
||
Context,
|
||
SAM_SERVER_ENUMERATE_DOMAINS,
|
||
SampServerObjectType, // ExpectedType
|
||
&FoundType
|
||
);
|
||
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
|
||
//
|
||
// Enumerating domains is easy. We keep a list in memory.
|
||
// All we have to do is use the enumeration context as an
|
||
// index into the defined domains array.
|
||
//
|
||
|
||
|
||
|
||
//
|
||
// Set our default completion status
|
||
// Note that this is a SUCCESS status code.
|
||
// That is NT_SUCCESS(STATUS_MORE_ENTRIES) will return TRUE.
|
||
|
||
//
|
||
|
||
NtStatus = STATUS_MORE_ENTRIES;
|
||
|
||
|
||
|
||
//
|
||
// Search the list of defined domains for a match.
|
||
//
|
||
|
||
for ( i = (ULONG)(*EnumerationContext);
|
||
( (i < SampDefinedDomainsCount) &&
|
||
(NT_SUCCESS(NtStatus)) &&
|
||
(!LengthLimitReached) );
|
||
i++ ) {
|
||
|
||
|
||
//
|
||
// See if there is room for the next name. If TotalLength
|
||
// is still zero then we haven't yet even gotten one name.
|
||
// We have to return at least one name even if it exceeds
|
||
// the length request.
|
||
//
|
||
|
||
|
||
NewTotalLength = TotalLength +
|
||
sizeof(UNICODE_STRING) +
|
||
(ULONG)SampDefinedDomains[i].ExternalName.Length +
|
||
sizeof(UNICODE_NULL);
|
||
|
||
if ( (NewTotalLength < PreferedMaximumLength) ||
|
||
(TotalLength == 0) ) {
|
||
|
||
if (NewTotalLength > SAMP_MAXIMUM_MEMORY_TO_USE) {
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
} else {
|
||
|
||
|
||
TotalLength = NewTotalLength;
|
||
(*CountReturned) += 1;
|
||
|
||
//
|
||
// Room for this name as well.
|
||
// Allocate a new return list entry, and a buffer for the
|
||
// name.
|
||
//
|
||
|
||
NewEntry = MIDL_user_allocate(sizeof(SAMP_ENUMERATION_ELEMENT));
|
||
if (NewEntry == NULL) {
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
} else {
|
||
|
||
NewEntry->Entry.Name.Buffer =
|
||
MIDL_user_allocate(
|
||
(ULONG)SampDefinedDomains[i].ExternalName.Length +
|
||
sizeof(UNICODE_NULL)
|
||
);
|
||
|
||
if (NewEntry->Entry.Name.Buffer == NULL) {
|
||
MIDL_user_free(NewEntry);
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
} else {
|
||
|
||
//
|
||
// Copy the name into the return buffer
|
||
//
|
||
|
||
RtlCopyMemory( NewEntry->Entry.Name.Buffer,
|
||
SampDefinedDomains[i].ExternalName.Buffer,
|
||
SampDefinedDomains[i].ExternalName.Length
|
||
);
|
||
NewEntry->Entry.Name.Length = SampDefinedDomains[i].ExternalName.Length;
|
||
NewEntry->Entry.Name.MaximumLength = NewEntry->Entry.Name.Length + (USHORT)sizeof(UNICODE_NULL);
|
||
UnicodeTerminate((PUNICODE_STRING)(&NewEntry->Entry.Name));
|
||
|
||
|
||
//
|
||
// The Rid field of the ENUMERATION_INFORMATION is not
|
||
// filled in for domains.
|
||
// Just for good measure, set it to zero.
|
||
//
|
||
|
||
NewEntry->Entry.RelativeId = 0;
|
||
|
||
|
||
|
||
//
|
||
// Now add this to the list of names to be returned.
|
||
//
|
||
|
||
NewEntry->Next = (PSAMP_ENUMERATION_ELEMENT)SampHead;
|
||
SampHead = NewEntry;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
LengthLimitReached = TRUE;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
if ( NT_SUCCESS(NtStatus) ) {
|
||
|
||
//
|
||
// Set the enumeration context
|
||
//
|
||
|
||
(*EnumerationContext) = (*EnumerationContext) + (*CountReturned);
|
||
|
||
|
||
|
||
//
|
||
// If we are returning the last of the names, then change our
|
||
// status code to indicate this condition.
|
||
//
|
||
|
||
if ( ((*EnumerationContext) >= SampDefinedDomainsCount) ) {
|
||
|
||
NtStatus = STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
|
||
//
|
||
// Build a return buffer containing an array of the
|
||
// SAM_RID_ENUMERATIONs pointed to by another
|
||
// buffer containing the number of elements in that
|
||
// array.
|
||
//
|
||
|
||
(*Buffer) = MIDL_user_allocate( sizeof(SAMPR_ENUMERATION_BUFFER) );
|
||
|
||
if ( (*Buffer) == NULL) {
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
} else {
|
||
|
||
(*Buffer)->EntriesRead = (*CountReturned);
|
||
|
||
ArrayBufferLength = sizeof( SAM_RID_ENUMERATION ) *
|
||
(*CountReturned);
|
||
ArrayBuffer = MIDL_user_allocate( ArrayBufferLength );
|
||
(*Buffer)->Buffer = ArrayBuffer;
|
||
|
||
if ( ArrayBuffer == NULL) {
|
||
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
MIDL_user_free( (*Buffer) );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Walk the list of return entries, copying
|
||
// them into the return buffer
|
||
//
|
||
|
||
NextEntry = SampHead;
|
||
i = 0;
|
||
while (NextEntry != NULL) {
|
||
|
||
NewEntry = NextEntry;
|
||
NextEntry = NewEntry->Next;
|
||
|
||
ArrayBuffer[i] = NewEntry->Entry;
|
||
i += 1;
|
||
|
||
MIDL_user_free( NewEntry );
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
if ( !NT_SUCCESS(NtStatus) ) {
|
||
|
||
//
|
||
// Free the memory we've allocated
|
||
//
|
||
|
||
NextEntry = SampHead;
|
||
while (NextEntry != NULL) {
|
||
|
||
NewEntry = NextEntry;
|
||
NextEntry = NewEntry->Next;
|
||
|
||
MIDL_user_free( NewEntry->Entry.Name.Buffer );
|
||
MIDL_user_free( NewEntry );
|
||
}
|
||
|
||
(*EnumerationContext) = 0;
|
||
(*CountReturned) = 0;
|
||
(*Buffer) = NULL;
|
||
|
||
}
|
||
|
||
//
|
||
// De-reference the object
|
||
// Note that NtStatus could be STATUS_MORE_ENTRIES, which is a
|
||
// successful return code - we want to make sure we return that,
|
||
// without wiping it out here.
|
||
//
|
||
|
||
if ( NtStatus == STATUS_SUCCESS ) {
|
||
|
||
NtStatus = SampDeReferenceContext( Context, FALSE );
|
||
|
||
} else {
|
||
|
||
IgnoreStatus = SampDeReferenceContext( Context, FALSE );
|
||
}
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Free the read lock
|
||
//
|
||
|
||
SampReleaseReadLock();
|
||
|
||
|
||
|
||
return(NtStatus);
|
||
|
||
}
|