2289 lines
65 KiB
C
2289 lines
65 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
enum.c
|
||
|
||
Abstract:
|
||
|
||
This file contains the core account enumeration services
|
||
|
||
Author:
|
||
|
||
Jim Kelly (JimK) 4-July-1991
|
||
|
||
Environment:
|
||
|
||
User Mode - Win32
|
||
|
||
Revision History:
|
||
|
||
6-19-96: MURLIS Created.
|
||
|
||
|
||
--*/
|
||
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
/*
|
||
|
||
ENUMERATION ROUTINES IMPLEMENTATION
|
||
|
||
The Entry Points for the core Enumeration routines are
|
||
|
||
SampEnumerateAcountNamesCommon --
|
||
|
||
Called By the Samr RPC routines
|
||
|
||
SampEnumerateAccountNames --
|
||
|
||
Called by the above SampEnumerateAccountNamesCommon
|
||
and internal routines that need enumeration.
|
||
|
||
SampEnumerateAccountNames does the actual work of enumerating account
|
||
names. the transaction domain to be set . SampEnumerateAccountNames
|
||
looks at the current current transaction domain and makes the decision
|
||
wether it is DS or Registry and then Calls either DS or Registry version.
|
||
While the way enumeration is done from the registry is unaltered the
|
||
way it is done from the DS is as follows:
|
||
|
||
Enumerating Accounts in DS uses the DS Search mechanism along with
|
||
the Paged Results extension. The First time the client calls the Enumerate
|
||
accounts routine, the value of EnumerationHandle is set to NULL.
|
||
This results in the code building a DS Filter structure and set up a
|
||
new search. If More entries are turned up the search, than memory
|
||
restrictions will warrant, then the DS will turn return a PagedResults
|
||
Structure. This paged results structure containes a pointer to a restart
|
||
structure. This restart structure represents the state information, the
|
||
DS requires in order to continue the search. The DS expects that this
|
||
structure to be passed back in subsequent searches.
|
||
|
||
There are two cases of the enumeration logic:
|
||
|
||
|
||
|
||
1. Enumeration is called by clients.
|
||
|
||
While the XDS head of the DS passes back the entire restart
|
||
structure SAM needs to pass back a handle in order to maintain
|
||
backwards compatiblity with older releases of NT. Therefore
|
||
SAM copies the restart structure returned by the DS ( the DS
|
||
allocates using its thread alloc scheme, whose scope is limited
|
||
to the current RPC call ) and keeps it around in server memory,
|
||
and returns a handle to the client that identifies this restart
|
||
strucure in server memory. The mechanism of correlating the handle
|
||
to the restart structure is as follows
|
||
|
||
The Handle holds a pointer to an enumeration context structure.
|
||
The value of the pointer is also stored in the Enumeration Context
|
||
Structure. The Domain Context maintains a linked list of Enumeration
|
||
Context's, which represent the Enumerations initiated by this client
|
||
on this domain and which have not yet computed.
|
||
SampValidateEnumerationContext is used to validate an enumeration
|
||
handle passed in by the client. This routine uses the Domain
|
||
Context that is passed in and traverses the List to find a context
|
||
block with a pointer value that matches the passed in Enumeration
|
||
Handle. If it finds such a pointer then it returns STATUS_SUCCESS
|
||
and the value of the handle is cast into the pointer to the
|
||
enumeration context block. Else the routine returns STATUS_INVALID_
|
||
HANDLE. Upon rundown, a SampDeleteContext will be generated on
|
||
the Domain Context block, which will also completely free the linked
|
||
list of enumeration context blocks.
|
||
|
||
|
||
2. Called by code within this DLL.
|
||
|
||
The value of the Enumeration Handle is cast to a pointer to an
|
||
Enumeration Context. The validation involved is only to check the
|
||
pointer value field. It is assumed that bad handles will not be
|
||
passed by the code in this DLL.
|
||
|
||
|
||
*/
|
||
////////////////////////////////////////////////////////////////////////////
|
||
|
||
//
|
||
// Include all those includes
|
||
//
|
||
#include <samsrvp.h>
|
||
#include <mappings.h>
|
||
#include <dslayer.h>
|
||
#include <filtypes.h>
|
||
|
||
//
|
||
//
|
||
// The Maximum Number of Enumerations a Client can simultaneously do. Since
|
||
// we keep around some state in memory per enumeration operation and since
|
||
// we are the security system, we cannot alow a malicious client from running
|
||
// us out of memory. So limit on a per client basis. Our state info is size is
|
||
// qpprox 1K byte.
|
||
//
|
||
|
||
#define SAMP_MAX_CLIENT_ENUMERATIONS 16
|
||
|
||
//
|
||
// DS limits the number of items that a given search can find. While in the
|
||
// SAM API, the approximate amount of memory is specified. This factor is
|
||
// is used in computing the number of entries required fro memory specified
|
||
//
|
||
|
||
#define AVERAGE_MEMORY_PER_ENTRY 32
|
||
|
||
// Prototypes of Private Functions
|
||
//
|
||
|
||
NTSTATUS
|
||
SampEnumerateAccountNamesDs(
|
||
IN SAMP_OBJECT_TYPE ObjectType,
|
||
IN OUT PSAMP_DS_ENUMERATION_CONTEXT *EnumerationContext,
|
||
OUT PSAMPR_ENUMERATION_BUFFER *Buffer,
|
||
IN ULONG PreferedMaximumLength,
|
||
IN ULONG Filter,
|
||
OUT PULONG CountReturned,
|
||
IN BOOLEAN TrustedClient
|
||
);
|
||
|
||
NTSTATUS
|
||
SampBuildDsEnumerationFilter(
|
||
IN SAMP_OBJECT_TYPE ObjectType,
|
||
IN ULONG UserAccountControlFilter,
|
||
OUT FILTER * DsFilter
|
||
);
|
||
|
||
VOID
|
||
SampFreeDsEnumerationFilter(
|
||
FILTER * DsFilter
|
||
);
|
||
|
||
|
||
NTSTATUS
|
||
SampEnumerateAccountNamesRegistry(
|
||
IN SAMP_OBJECT_TYPE ObjectType,
|
||
IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext,
|
||
OUT PSAMPR_ENUMERATION_BUFFER *Buffer,
|
||
IN ULONG PreferedMaximumLength,
|
||
IN ULONG Filter,
|
||
OUT PULONG CountReturned,
|
||
IN BOOLEAN TrustedClient
|
||
);
|
||
|
||
|
||
NTSTATUS
|
||
SampPackDsEnumerationResults(
|
||
SEARCHRES *SearchRes,
|
||
IN SAMP_OBJECT_TYPE ObjectType,
|
||
IN ULONG ExpectedAttrCount,
|
||
IN ULONG Filter,
|
||
ULONG * Count,
|
||
PSAMPR_RID_ENUMERATION *RidEnumerationList
|
||
);
|
||
|
||
NTSTATUS
|
||
SampDoDsSearchContinuation(
|
||
IN SEARCHRES * SearchRes,
|
||
IN OUT PSAMP_DS_ENUMERATION_CONTEXT * EnumerationContext,
|
||
OUT BOOLEAN * MoreEntries
|
||
);
|
||
|
||
NTSTATUS
|
||
SampValidateEnumerationContext(
|
||
IN PSAMP_OBJECT DomainContext,
|
||
IN SAM_ENUMERATE_HANDLE EnumerationHandle,
|
||
IN ULONG MaxEnumerationContexts
|
||
);
|
||
|
||
ULONG
|
||
Ownstrlen(
|
||
CHAR * Sz
|
||
);
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SampCopyRestart(
|
||
IN PRESTART OldRestart,
|
||
OUT PRESTART *NewRestart
|
||
);
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SampEnumerateAccountNamesCommon(
|
||
IN SAMPR_HANDLE DomainHandle,
|
||
IN SAMP_OBJECT_TYPE ObjectType,
|
||
IN OUT PSAM_ENUMERATE_HANDLE EnumerationHandle,
|
||
OUT PSAMPR_ENUMERATION_BUFFER *Buffer,
|
||
IN ULONG PreferedMaximumLength,
|
||
IN ULONG Filter,
|
||
OUT PULONG CountReturned
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine enumerates names of either user, group or alias accounts.
|
||
This routine is intended to directly support
|
||
|
||
SamrEnumerateGroupsInDomain(),
|
||
SamrEnumerateAliasesInDomain() and
|
||
SamrEnumerateUsersInDomain().
|
||
|
||
This routine performs database locking, and context lookup (including
|
||
access validation).
|
||
|
||
|
||
|
||
|
||
All allocation for OUT parameters will be done using MIDL_user_allocate.
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
DomainHandle - The domain handle whose users or groups are to be enumerated.
|
||
|
||
ObjectType - Indicates whether users or groups are to be enumerated.
|
||
|
||
EnumerationHandle - API specific handle to allow multiple calls. The
|
||
caller should return this value in successive calls to retrieve
|
||
additional information.
|
||
|
||
Buffer - Receives a pointer to the buffer containing the
|
||
requested information. The information returned is
|
||
structured as an array of SAM_ENUMERATION_INFORMATION data
|
||
structures. When this information is no longer needed, the
|
||
buffer must be freed 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.
|
||
|
||
Filter - if ObjectType is users, the users can optionally be filtered
|
||
by setting this field with bits from the AccountControlField that
|
||
must match. Otherwise ignored.
|
||
|
||
CountReturned - Receives the number of entries returned.
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - The Service completed successfully, and there
|
||
are no additional entries. Entries may or may not have been
|
||
returned from this call. The CountReturned parameter indicates
|
||
whether any were.
|
||
|
||
STATUS_MORE_ENTRIES - There are more entries which may be obtained
|
||
using successive calls to this API. This is a successful return.
|
||
|
||
STATUS_ACCESS_DENIED - Caller does not have access to request the data.
|
||
|
||
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS NtStatus;
|
||
NTSTATUS IgnoreStatus;
|
||
PSAMP_OBJECT Context;
|
||
SAMP_OBJECT_TYPE FoundType;
|
||
ACCESS_MASK DesiredAccess;
|
||
|
||
SAMTRACE("SampEnumerateAccountNamesCommon");
|
||
|
||
|
||
ASSERT( (ObjectType == SampGroupObjectType) ||
|
||
(ObjectType == SampAliasObjectType) ||
|
||
(ObjectType == SampUserObjectType) );
|
||
|
||
//
|
||
// Make sure we understand what RPC is doing for (to) us.
|
||
//
|
||
|
||
ASSERT (DomainHandle != NULL);
|
||
ASSERT (EnumerationHandle != NULL);
|
||
ASSERT ( Buffer != NULL);
|
||
ASSERT ((*Buffer) == NULL);
|
||
ASSERT (CountReturned != NULL);
|
||
|
||
|
||
//
|
||
// Establish type-specific information
|
||
//
|
||
|
||
DesiredAccess = DOMAIN_LIST_ACCOUNTS;
|
||
|
||
|
||
SampAcquireReadLock();
|
||
|
||
|
||
//
|
||
// Validate type of, and access to object.
|
||
//
|
||
|
||
Context = (PSAMP_OBJECT)DomainHandle;
|
||
NtStatus = SampLookupContext(
|
||
Context,
|
||
DesiredAccess,
|
||
SampDomainObjectType,
|
||
&FoundType
|
||
);
|
||
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
|
||
//
|
||
// If DS Object then Validate the Enumeration
|
||
// Context, as above.
|
||
//
|
||
|
||
NtStatus = SampValidateEnumerationContext(
|
||
Context,
|
||
*EnumerationHandle,
|
||
SAMP_MAX_CLIENT_ENUMERATIONS
|
||
);
|
||
|
||
if ( NT_SUCCESS(NtStatus))
|
||
{
|
||
//
|
||
// If Domain Context was a DS Object
|
||
// Remove the Enumeration Context,
|
||
// given by this handle
|
||
// From the list of enumeration Context's
|
||
// hanging out from the DS Object.
|
||
//
|
||
if ((0!=*EnumerationHandle) && (IsDsObject(Context)))
|
||
RemoveEntryList((LIST_ENTRY *)(*EnumerationHandle));
|
||
|
||
//
|
||
// Call our private worker routine
|
||
//
|
||
|
||
NtStatus = SampEnumerateAccountNames(
|
||
ObjectType,
|
||
EnumerationHandle,
|
||
Buffer,
|
||
PreferedMaximumLength,
|
||
Filter,
|
||
CountReturned,
|
||
Context->TrustedClient
|
||
);
|
||
|
||
//
|
||
// Insert enumeration context blob into list
|
||
//
|
||
if ( // Operation Succeeded
|
||
NT_SUCCESS(NtStatus)
|
||
// Search is not over. More Entries remain to read
|
||
&& *EnumerationHandle
|
||
// We are Talking of DS objects
|
||
&& IsDsObject(Context)
|
||
)
|
||
//
|
||
// Add this EnumerationContext to the list of enumeration
|
||
// context's maintained in the domain context block
|
||
//
|
||
InsertTailList(&(Context->TypeBody.Domain.DsEnumerationContext),
|
||
((LIST_ENTRY *)*EnumerationHandle));
|
||
|
||
|
||
}
|
||
|
||
//
|
||
// De-reference the object, discarding changes
|
||
//
|
||
|
||
IgnoreStatus = SampDeReferenceContext( Context, FALSE );
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
}
|
||
|
||
//
|
||
// Free the read lock
|
||
//
|
||
|
||
SampReleaseReadLock();
|
||
|
||
return(NtStatus);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SampEnumerateAccountNames(
|
||
IN SAMP_OBJECT_TYPE ObjectType,
|
||
IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext,
|
||
OUT PSAMPR_ENUMERATION_BUFFER *Buffer,
|
||
IN ULONG PreferedMaximumLength,
|
||
IN ULONG Filter,
|
||
OUT PULONG CountReturned,
|
||
IN BOOLEAN TrustedClient
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the wrapper around the worker routine used to enumerate user,
|
||
group or alias accounts. This determines wether the domain is in the
|
||
DS or Registry, and then depending upon the outcome calls the
|
||
appropriate flavour of the routine
|
||
|
||
|
||
Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN
|
||
(ESTABLISHED USING SampSetTransactioDomain()). THIS
|
||
SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain()
|
||
AND BEFORE SampReleaseReadLock().
|
||
|
||
|
||
|
||
All allocation for OUT parameters will be done using MIDL_user_allocate.
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
ObjectType - Indicates whether users or groups are to be enumerated.
|
||
|
||
EnumerationContext - API specific handle to allow multiple calls. The
|
||
caller should return this value in successive calls to retrieve
|
||
additional information.
|
||
|
||
Buffer - Receives a pointer to the buffer containing the
|
||
requested information. The information returned is
|
||
structured as an array of SAM_ENUMERATION_INFORMATION data
|
||
structures. When this information is no longer needed, the
|
||
buffer must be freed 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.
|
||
|
||
Filter - if ObjectType is users, the users can optionally be filtered
|
||
by setting this field with bits from the AccountControlField that
|
||
must match. Otherwise ignored.
|
||
|
||
CountReturned - Receives the number of entries returned.
|
||
|
||
TrustedClient - says whether the caller is trusted or not. If so,
|
||
we'll ignore the SAMP_MAXIMUM_MEMORY_TO_USE restriction on data
|
||
returns.
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - The Service completed successfully, and there
|
||
are no additional entries. Entries may or may not have been
|
||
returned from this call. The CountReturned parameter indicates
|
||
whether any were.
|
||
|
||
STATUS_MORE_ENTRIES - There are more entries which may be obtained
|
||
using successive calls to this API. This is a successful return.
|
||
|
||
STATUS_ACCESS_DENIED - Caller does not have access to request the data.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS NtStatus = STATUS_SUCCESS;
|
||
|
||
|
||
if (IsDsObject(SampDefinedDomains[SampTransactionDomainIndex].Context))
|
||
{
|
||
//
|
||
// DS Object - Do the DS thing
|
||
//
|
||
NtStatus = SampEnumerateAccountNamesDs(
|
||
ObjectType,
|
||
(PSAMP_DS_ENUMERATION_CONTEXT *)
|
||
EnumerationContext,
|
||
Buffer,
|
||
PreferedMaximumLength,
|
||
Filter,
|
||
CountReturned,
|
||
TrustedClient
|
||
);
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Registry Object - Do the Registry thing
|
||
//
|
||
NtStatus = SampEnumerateAccountNamesRegistry(
|
||
ObjectType,
|
||
EnumerationContext,
|
||
Buffer,
|
||
PreferedMaximumLength,
|
||
Filter,
|
||
CountReturned,
|
||
TrustedClient
|
||
);
|
||
}
|
||
|
||
return NtStatus;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SampEnumerateAccountNamesDs(
|
||
IN SAMP_OBJECT_TYPE ObjectType,
|
||
IN OUT PSAMP_DS_ENUMERATION_CONTEXT *EnumerationContext,
|
||
OUT PSAMPR_ENUMERATION_BUFFER *Buffer,
|
||
IN ULONG PreferedMaximumLength,
|
||
IN ULONG Filter,
|
||
OUT PULONG CountReturned,
|
||
IN BOOLEAN TrustedClient
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does the work of enumeration for the DS case.
|
||
|
||
|
||
Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN
|
||
(ESTABLISHED USING SampSetTransactioDomain()). THIS
|
||
SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain()
|
||
AND BEFORE SampReleaseReadLock().
|
||
|
||
|
||
|
||
All allocation for OUT parameters will be done using MIDL_user_allocate.
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
ObjectType - Indicates whether users or groups are to be enumerated.
|
||
|
||
EnumerationContext - API specific handle to allow multiple calls. The
|
||
caller should return this value in successive calls to retrieve
|
||
additional information.
|
||
|
||
Buffer - Receives a pointer to the buffer containing the
|
||
requested information. The information returned is
|
||
structured as an array of SAM_ENUMERATION_INFORMATION data
|
||
structures. When this information is no longer needed, the
|
||
buffer must be freed 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.
|
||
|
||
Filter - if ObjectType is users, the users can optionally be filtered
|
||
by setting this field with bits from the AccountControlField that
|
||
must match. Otherwise ignored.
|
||
|
||
CountReturned - Receives the number of entries returned.
|
||
|
||
TrustedClient - says whether the caller is trusted or not. If so,
|
||
we'll ignore the SAMP_MAXIMUM_MEMORY_TO_USE restriction on data
|
||
returns.
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - The Service completed successfully, and there
|
||
are no additional entries. Entries may or may not have been
|
||
returned from this call. The CountReturned parameter indicates
|
||
whether any were.
|
||
|
||
STATUS_MORE_ENTRIES - There are more entries which may be obtained
|
||
using successive calls to this API. This is a successful return.
|
||
|
||
STATUS_ACCESS_DENIED - Caller does not have access to request the data.
|
||
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// Amount of memory that we may use.
|
||
//
|
||
|
||
ULONG MemoryToUse = PreferedMaximumLength;
|
||
|
||
//
|
||
// Specify the attributes that we want to read as part of the search.
|
||
// The Attributes specified in GenericReadAttrTypes are read from the DS,
|
||
// except for user objects ( due to filter on account control bits )
|
||
// account control bits.
|
||
//
|
||
// NOTE
|
||
// The Ordering of the Rid and the Name
|
||
// must be the same for both User and Generic Attr Types.
|
||
// Further they should be the First two attributes.
|
||
//
|
||
|
||
ATTRTYP GenericReadAttrTypes[]=
|
||
{
|
||
SAMP_UNKNOWN_OBJECTRID,
|
||
SAMP_UNKNOWN_OBJECTNAME,
|
||
};
|
||
ATTRVAL GenericReadAttrVals[]=
|
||
{
|
||
{0,NULL},
|
||
{0,NULL}
|
||
};
|
||
|
||
DEFINE_ATTRBLOCK2(
|
||
GenericReadAttrs,
|
||
GenericReadAttrTypes,
|
||
GenericReadAttrVals
|
||
);
|
||
|
||
ATTRTYP UserReadAttrTypes[]=
|
||
{
|
||
SAMP_FIXED_USER_USERID,
|
||
SAMP_USER_ACCOUNT_NAME,
|
||
SAMP_FIXED_USER_ACCOUNT_CONTROL,
|
||
};
|
||
ATTRVAL UserReadAttrVals[]=
|
||
{
|
||
{0,NULL},
|
||
{0,NULL},
|
||
{0,NULL}
|
||
};
|
||
|
||
DEFINE_ATTRBLOCK3(
|
||
UserReadAttrs,
|
||
UserReadAttrTypes,
|
||
UserReadAttrVals
|
||
);
|
||
|
||
//
|
||
// Specify other local variables that we need
|
||
//
|
||
ATTRBLOCK *AttrsToRead;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
DSNAME *DomainObjectName;
|
||
PSAMPR_RID_ENUMERATION RidEnumerationList = NULL;
|
||
SEARCHRES *SearchRes;
|
||
BOOLEAN MoreEntries = FALSE;
|
||
ULONG MaximumNumberOfEntries;
|
||
SAMP_OBJECT_TYPE ObjectTypeForConversion;
|
||
|
||
//
|
||
// Allocate memory to hold the result
|
||
//
|
||
|
||
*Buffer = MIDL_user_allocate(sizeof(SAMPR_ENUMERATION_BUFFER));
|
||
if (NULL==*Buffer)
|
||
{
|
||
Status = STATUS_NO_MEMORY;
|
||
goto Error;
|
||
}
|
||
|
||
//
|
||
// Get The Domain Object Name
|
||
//
|
||
|
||
DomainObjectName =
|
||
SampDefinedDomains[SampTransactionDomainIndex].Context->ObjectNameInDs;
|
||
|
||
//
|
||
// Check for Memory Restrictions
|
||
//
|
||
|
||
if ( (!TrustedClient) &&
|
||
(PreferedMaximumLength > SAMP_MAXIMUM_MEMORY_TO_USE))
|
||
{
|
||
MemoryToUse = SAMP_MAXIMUM_MEMORY_TO_USE;
|
||
}
|
||
|
||
//
|
||
// Compute the maximim number of entries we want based on
|
||
// memory restrictions. Add plus 1 , so that at least 1 entry
|
||
// will be returned.
|
||
//
|
||
|
||
MaximumNumberOfEntries = MemoryToUse/AVERAGE_MEMORY_PER_ENTRY + 1;
|
||
|
||
//
|
||
// Specify the Apropriate Attributes to Read
|
||
//
|
||
|
||
if (ObjectType == SampUserObjectType)
|
||
{
|
||
AttrsToRead = &UserReadAttrs;
|
||
ObjectTypeForConversion = SampUserObjectType;
|
||
}
|
||
else
|
||
{
|
||
AttrsToRead = &GenericReadAttrs;
|
||
ObjectTypeForConversion = SampUnknownObjectType;
|
||
}
|
||
|
||
//
|
||
// Check if New Search. Accordingly Pass Arguments to SampDsDoSearch
|
||
//
|
||
|
||
if (NULL == *EnumerationContext)
|
||
{
|
||
FILTER DsFilter;
|
||
|
||
//
|
||
// Build the correct filter
|
||
//
|
||
|
||
Status = SampBuildDsEnumerationFilter(
|
||
ObjectType,
|
||
Filter,
|
||
&DsFilter
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
goto Error;
|
||
|
||
//
|
||
// Do the DS Search using the Filter, pass in NULL for restart
|
||
//
|
||
|
||
Status = SampDsDoSearch(
|
||
NULL,
|
||
DomainObjectName,
|
||
&DsFilter,
|
||
ObjectTypeForConversion,
|
||
AttrsToRead,
|
||
MaximumNumberOfEntries,
|
||
&SearchRes
|
||
);
|
||
//
|
||
// First Free the Filter Structure , irrespective of any
|
||
// Error returns
|
||
//
|
||
|
||
SampFreeDsEnumerationFilter(&DsFilter);
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
goto Error;
|
||
|
||
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Get the Restart Structure returned from previous search
|
||
// and do the search using it.
|
||
//
|
||
|
||
PRESTART TmpRestart;
|
||
|
||
TmpRestart = (*EnumerationContext)->Restart;
|
||
ASSERT(TmpRestart);
|
||
|
||
Status = SampDsDoSearch(
|
||
TmpRestart,
|
||
DomainObjectName,
|
||
NULL,
|
||
ObjectTypeForConversion,
|
||
AttrsToRead,
|
||
MaximumNumberOfEntries,
|
||
&SearchRes
|
||
);
|
||
if (!NT_SUCCESS(Status))
|
||
goto Error;
|
||
}
|
||
|
||
//
|
||
// Handle any paged results returned by the DS.
|
||
//
|
||
|
||
Status = SampDoDsSearchContinuation(
|
||
SearchRes,
|
||
EnumerationContext,
|
||
&MoreEntries
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
goto Error;
|
||
|
||
|
||
//
|
||
// Search Succeeded. Pack the results into appropriate
|
||
// Rid Enumeration Buffers.
|
||
//
|
||
|
||
Status = SampPackDsEnumerationResults(
|
||
SearchRes,
|
||
ObjectType,
|
||
AttrsToRead->attrCount,
|
||
Filter,
|
||
CountReturned,
|
||
&RidEnumerationList
|
||
);
|
||
|
||
|
||
Error:
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
{
|
||
//
|
||
// Error return, do the cleanup work.
|
||
//
|
||
|
||
if (*EnumerationContext)
|
||
{
|
||
SampFreeRestart((*EnumerationContext)->Restart);
|
||
*EnumerationContext = NULL;
|
||
}
|
||
|
||
if (*Buffer)
|
||
MIDL_user_free(*Buffer);
|
||
|
||
}
|
||
else
|
||
{
|
||
if (MoreEntries)
|
||
Status = STATUS_MORE_ENTRIES;
|
||
(*Buffer)->EntriesRead = *CountReturned;
|
||
(*Buffer)->Buffer = RidEnumerationList;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SampPackDsEnumerationResults(
|
||
IN SEARCHRES *SearchRes,
|
||
IN SAMP_OBJECT_TYPE ObjectType,
|
||
IN ULONG ExpectedAttrCount,
|
||
IN ULONG Filter,
|
||
OUT ULONG * Count,
|
||
OUT PSAMPR_RID_ENUMERATION *RidEnumerationList
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine Packs the complex structures
|
||
returned by the core DS, into the Rid Enumeration
|
||
Structures required by SAM.
|
||
|
||
Arguments:
|
||
|
||
SearchRes SearchRes strucure as obtained from the DS.
|
||
|
||
ExpectedAttrCount -- Passed by the caller. This is the count
|
||
of Attrs which the caller expects from the SearchRes
|
||
on a per search entry basis. Used to validate results
|
||
from the DS.
|
||
|
||
Filter For User Accounts bits of the AccountControlId.
|
||
|
||
Count Returned Count of Structures.
|
||
|
||
RidEnumerationList - Array of structures of type
|
||
SAMP_RID_ENUMERATION passed back in this.
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PSAMPR_RID_ENUMERATION RidEnumerationListToReturn = NULL;
|
||
|
||
//
|
||
// Initialize what we plan to return.
|
||
//
|
||
*RidEnumerationList = NULL;
|
||
*Count = 0;
|
||
|
||
//
|
||
// Look if search turned up any results.
|
||
// If so stuff them in Rid Enumeration Array ( or whatever )
|
||
//
|
||
if (SearchRes->count)
|
||
{
|
||
//
|
||
// Search Did Turn up Results
|
||
//
|
||
|
||
ULONG Index;
|
||
ENTINFLIST * CurrentEntInf = &(SearchRes->FirstEntInf);
|
||
|
||
//
|
||
// Allocate memory for an array of Rid Enumerations
|
||
//
|
||
RidEnumerationListToReturn = MIDL_user_allocate(
|
||
SearchRes->count
|
||
* sizeof(SAMPR_RID_ENUMERATION)
|
||
);
|
||
if (NULL==RidEnumerationListToReturn)
|
||
{
|
||
Status = STATUS_NO_MEMORY;
|
||
goto Error;
|
||
}
|
||
|
||
//
|
||
// Zero Memory just what we alloced. Useful for freeing up stuff
|
||
// in case we error'd out
|
||
//
|
||
RtlZeroMemory(RidEnumerationListToReturn,SearchRes->count
|
||
* sizeof(SAMPR_RID_ENUMERATION)
|
||
);
|
||
|
||
//
|
||
// Walk through the List turned up by the search and
|
||
// build the RidEnumeration Buffer
|
||
//
|
||
for (Index=0;Index<SearchRes->count;Index++)
|
||
{
|
||
|
||
|
||
//
|
||
// Assert the count of Attrs is normal. If Not
|
||
// Fail the Call if the returned count is not the
|
||
// Same as Expected Count
|
||
//
|
||
//
|
||
|
||
ASSERT(CurrentEntInf->Entinf.AttrBlock.attrCount==
|
||
ExpectedAttrCount);
|
||
|
||
if (CurrentEntInf->Entinf.AttrBlock.attrCount!=
|
||
ExpectedAttrCount)
|
||
{
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
goto Error;
|
||
}
|
||
|
||
//
|
||
// Assert that the Rid is in the right place
|
||
//
|
||
|
||
ASSERT(CurrentEntInf->Entinf.AttrBlock.pAttr[0].attrTyp ==
|
||
SampDsAttrFromSamAttr(SampUnknownObjectType,
|
||
SAMP_UNKNOWN_OBJECTRID));
|
||
//
|
||
// Assert that the Name is in the right place
|
||
//
|
||
|
||
ASSERT(CurrentEntInf->Entinf.AttrBlock.pAttr[1].attrTyp ==
|
||
SampDsAttrFromSamAttr(SampUnknownObjectType,
|
||
SAMP_UNKNOWN_OBJECTNAME));
|
||
|
||
if (ObjectType == SampUserObjectType)
|
||
{
|
||
|
||
//
|
||
// For User objects we need to filter based on account-control
|
||
// field
|
||
//
|
||
|
||
ULONG AccountControlValue;
|
||
|
||
//
|
||
// Assert that the Account control is in the right place
|
||
//
|
||
|
||
ASSERT(CurrentEntInf->Entinf.AttrBlock.pAttr[2].attrTyp ==
|
||
SampDsAttrFromSamAttr(SampUserObjectType,
|
||
SAMP_FIXED_USER_ACCOUNT_CONTROL));
|
||
|
||
//
|
||
// Get account control value and skip past if does
|
||
// not match the filter criteria
|
||
//
|
||
AccountControlValue =
|
||
*(CurrentEntInf->Entinf.AttrBlock.
|
||
pAttr[2].AttrVal.pAVal[0].pVal);
|
||
|
||
if ((Filter!=0) &&
|
||
((Filter & AccountControlValue) != Filter))
|
||
//
|
||
// Fails the Filter Test, skip this one
|
||
//
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Stuff this entry in the buffer to be returned.
|
||
//
|
||
|
||
//
|
||
// Copy the RID
|
||
//
|
||
|
||
RtlCopyMemory(
|
||
&(RidEnumerationListToReturn[Index].RelativeId),
|
||
CurrentEntInf->Entinf.AttrBlock.pAttr[0].AttrVal.pAVal[0].pVal,
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
//
|
||
// Copy the Name
|
||
//
|
||
|
||
RidEnumerationListToReturn[Index].Name.Length = (USHORT)
|
||
(CurrentEntInf->Entinf.AttrBlock.pAttr[1].AttrVal.
|
||
pAVal[0].valLen)/2 -1;
|
||
RidEnumerationListToReturn[Index].Name.MaximumLength = (USHORT)
|
||
(CurrentEntInf->Entinf.AttrBlock.pAttr[1].AttrVal.
|
||
pAVal[0].valLen)/2 -1;
|
||
|
||
|
||
RidEnumerationListToReturn[Index].Name.Buffer =
|
||
MIDL_user_allocate(CurrentEntInf->Entinf.AttrBlock.pAttr[1].
|
||
AttrVal.pAVal[0].valLen);
|
||
|
||
if (NULL== (RidEnumerationListToReturn[Index]).Name.Buffer)
|
||
{
|
||
Status = STATUS_NO_MEMORY;
|
||
goto Error;
|
||
}
|
||
|
||
RtlCopyMemory( RidEnumerationListToReturn[Index].Name.Buffer,
|
||
CurrentEntInf->Entinf.AttrBlock.pAttr[1].AttrVal.
|
||
pAVal[0].pVal,
|
||
CurrentEntInf->Entinf.AttrBlock.pAttr[1].AttrVal.
|
||
pAVal[0].valLen
|
||
);
|
||
|
||
//
|
||
// Go to the Next Entry
|
||
//
|
||
|
||
CurrentEntInf = CurrentEntInf->pNextEntInf;
|
||
}
|
||
|
||
//
|
||
// End of For Loop
|
||
//
|
||
|
||
}
|
||
//
|
||
// Fill in the count and return buffer correctly
|
||
//
|
||
|
||
*Count = SearchRes->count;
|
||
*RidEnumerationList = RidEnumerationListToReturn;
|
||
|
||
|
||
Error:
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
{
|
||
//
|
||
// We Errored out, need to free all that we allocated
|
||
//
|
||
|
||
if (NULL!=RidEnumerationListToReturn)
|
||
{
|
||
//
|
||
// We did allocate something
|
||
//
|
||
|
||
ULONG Index;
|
||
|
||
//
|
||
// First free all possible Names that we alloc'ed.
|
||
//
|
||
|
||
for (Index=0;Index<SearchRes->count;Index++)
|
||
{
|
||
if (RidEnumerationListToReturn[Index].Name.Buffer)
|
||
MIDL_user_free(
|
||
RidEnumerationListToReturn[Index].Name.Buffer);
|
||
}
|
||
|
||
//
|
||
// Free the buffer that we alloc'ed
|
||
//
|
||
|
||
MIDL_user_free(RidEnumerationListToReturn);
|
||
RidEnumerationListToReturn = NULL;
|
||
*RidEnumerationList = NULL;
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SampDoDsSearchContinuation(
|
||
IN SEARCHRES * SearchRes,
|
||
IN OUT PSAMP_DS_ENUMERATION_CONTEXT * EnumerationContext,
|
||
OUT BOOLEAN * MoreEntries
|
||
)
|
||
/*++
|
||
Routine Description
|
||
|
||
This routine will look if a PagedResults is present in
|
||
the Search Res argument that is passed in. If so, then it
|
||
will Try creating and EnumerationContext if NULL was passed
|
||
in the handle. Else it will free the old restart structure
|
||
from the Enumeration Context and copy in the new one passed
|
||
by the DS.
|
||
|
||
Arguments:
|
||
SearchRes - Pointer to Search Results structure returned by
|
||
the DS.
|
||
|
||
EnumerationContext - Holds a pointer to the enumeration Context
|
||
Structure
|
||
|
||
MoreEntries - Inidicates that more entries are present.
|
||
|
||
Return Values:
|
||
|
||
STATUS_SUCCESS
|
||
STATUS_NO_MEMORY
|
||
|
||
|
||
-*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PRESTART Restart = NULL;
|
||
|
||
//
|
||
// Initialize this to False
|
||
//
|
||
|
||
*MoreEntries = FALSE;
|
||
|
||
//
|
||
// Now look at the Paged Results part of Search Results
|
||
// And create enumeration contexts as necessary.
|
||
//
|
||
|
||
if ((SearchRes->PagedResult.fPresent)
|
||
&& (SearchRes->PagedResult.pRestart))
|
||
{
|
||
|
||
//
|
||
// Search has more entries to it and therefore retrned
|
||
// a restart structure
|
||
//
|
||
|
||
//
|
||
// If this was new search then we need to allocate a new
|
||
// Enumeration Context Structure
|
||
//
|
||
|
||
if (NULL== *EnumerationContext)
|
||
{
|
||
//
|
||
// New search , allocate a new enumeration context structure
|
||
//
|
||
|
||
PSAMP_DS_ENUMERATION_CONTEXT NewEnumerationContext;
|
||
|
||
//
|
||
// Allocate Memory
|
||
//
|
||
NewEnumerationContext = MIDL_user_allocate(
|
||
sizeof(SAMP_DS_ENUMERATION_CONTEXT)
|
||
);
|
||
if (NULL==NewEnumerationContext)
|
||
{
|
||
Status = STATUS_NO_MEMORY;
|
||
goto Error;
|
||
}
|
||
|
||
//
|
||
// Initialize the List Head fields
|
||
//
|
||
|
||
InitializeListHead((LIST_ENTRY *)(NewEnumerationContext));
|
||
|
||
//
|
||
// Initialize the Handle fields
|
||
//
|
||
|
||
NewEnumerationContext->EnumerateHandle =
|
||
(SAM_ENUMERATE_HANDLE) NewEnumerationContext;
|
||
|
||
//
|
||
// Initialize the Restart Pointer to NULL
|
||
//
|
||
|
||
NewEnumerationContext->Restart = NULL;
|
||
|
||
*EnumerationContext = NewEnumerationContext;
|
||
|
||
}
|
||
|
||
//
|
||
// Copy over the returned Enumeration handle. This Copying over
|
||
// is necessary because the DS allocs using the thread heap. ,
|
||
// while this structure has to stay around for longer as client
|
||
// comes around again and again.
|
||
//
|
||
|
||
Status = SampCopyRestart(SearchRes->PagedResult.pRestart, &Restart);
|
||
if (!NT_SUCCESS(Status))
|
||
goto Error;
|
||
|
||
//
|
||
// Free any old restart Structures, hanging from the enumeration
|
||
// context structures ( No op for NULL Restart Pointers)
|
||
//
|
||
|
||
SampFreeRestart((*EnumerationContext)->Restart);
|
||
|
||
//
|
||
// Keep the restart structure returned by the DS
|
||
// with the enumeration context
|
||
//
|
||
|
||
(*EnumerationContext)->Restart = Restart;
|
||
Restart = NULL;
|
||
*MoreEntries = TRUE;
|
||
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Search is Over, DS did not indicate that we have to come
|
||
// back for more entries. Free any state information that we
|
||
// created for this search
|
||
//
|
||
|
||
if (NULL!= *EnumerationContext)
|
||
{
|
||
//
|
||
// We did allocate State Information for this
|
||
// Search
|
||
//
|
||
|
||
SampFreeRestart((*EnumerationContext)->Restart);
|
||
MIDL_user_free(*EnumerationContext);
|
||
*EnumerationContext = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
Error:
|
||
|
||
//
|
||
// Do all the error cleanup.
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
{
|
||
//
|
||
// Error ocurred, free all state information, NULL
|
||
// out the handle
|
||
//
|
||
|
||
SampFreeRestart(Restart);
|
||
if (NULL!=*EnumerationContext)
|
||
{
|
||
SampFreeRestart((*EnumerationContext)->Restart);
|
||
MIDL_user_free(*EnumerationContext);
|
||
*EnumerationContext = NULL;
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
SampBuildDsEnumerationFilter(
|
||
IN SAMP_OBJECT_TYPE ObjectType,
|
||
IN ULONG UserAccountControlFilter,
|
||
OUT FILTER * DsFilter
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Builds a Filter structure for use in enumeration operations.
|
||
|
||
Arguments:
|
||
|
||
ObjectType - Type of SAM objects we want enumerated
|
||
UserAcountControlFilter - Bitmaks of bits to be set in Account Control field
|
||
when enumerating user objects
|
||
DsFilter -- Filter structure is built in here.
|
||
|
||
NOTE This routine must be kept in sync with
|
||
SampFreeDsEnumerationFilter
|
||
|
||
Return Values
|
||
|
||
STATUS_SUCCESS
|
||
STATUS_NO_MEMORY
|
||
|
||
--*/
|
||
{
|
||
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
// Build the Appropriate Filter
|
||
switch(ObjectType)
|
||
{
|
||
case SampUserObjectType:
|
||
|
||
if (UserAccountControlFilter!=0)
|
||
{
|
||
//
|
||
// Filtering on Account control field is Specified
|
||
//
|
||
|
||
// We need a number which when bitwise anded
|
||
// with the filter gives a non zero result.
|
||
// It can be easily seen that only numbers having
|
||
// a value greater than or equal to the given filter
|
||
// can satisfy this criterion.
|
||
|
||
// We will filter out only on the account control field
|
||
// rather than on the object class field. The assumption is that
|
||
// since this field will exist only for user objects,
|
||
// and we are filtering on objects having a value greater than something
|
||
// automatically we will get only user objects. Since the DS maintains only
|
||
// a limited set of inidices, and walks through the list and see if they match
|
||
// the given filter, a tradeoff between filter complexity and filter accuracy
|
||
// exisits. In case an Index is maintained on the user Account Field, then using
|
||
// this method should be quite O.K
|
||
|
||
DsFilter->choice = FILTER_CHOICE_ITEM;
|
||
DsFilter->FilTypes.Item.choice = FI_CHOICE_EQUALITY;
|
||
DsFilter->FilTypes.
|
||
Item.FilTypes.ava.type = SampDsAttrFromSamAttr(
|
||
SampUserObjectType,
|
||
SAMP_FIXED_USER_ACCOUNT_CONTROL
|
||
);
|
||
|
||
DsFilter->FilTypes.Item.FilTypes.ava.Value.valLen = sizeof(ULONG);
|
||
DsFilter->FilTypes.Item.FilTypes.ava.Value.pVal =
|
||
MIDL_user_allocate(sizeof(ULONG));
|
||
if (NULL==DsFilter->FilTypes.Item.FilTypes.ava.Value.pVal)
|
||
{
|
||
Status = STATUS_NO_MEMORY;
|
||
goto Error;
|
||
}
|
||
*((ULONG *)DsFilter->FilTypes.Item.FilTypes.ava.Value.pVal)
|
||
= UserAccountControlFilter;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// For the non User Account Control filter case we just
|
||
// fall through to the next case.
|
||
//
|
||
|
||
default:
|
||
|
||
|
||
//
|
||
// Build our default Filter.
|
||
//
|
||
|
||
DsFilter->choice = FILTER_CHOICE_ITEM;
|
||
DsFilter->FilTypes.Item.choice = FI_CHOICE_EQUALITY;
|
||
DsFilter->FilTypes.Item.FilTypes.ava.type = SampDsAttrFromSamAttr(
|
||
SampUnknownObjectType,
|
||
SAMP_UNKNOWN_OBJECTCLASS
|
||
);
|
||
|
||
DsFilter->FilTypes.Item.FilTypes.ava.Value.valLen = sizeof(ULONG);
|
||
DsFilter->FilTypes.Item.FilTypes.ava.Value.pVal =
|
||
MIDL_user_allocate(sizeof(ULONG));
|
||
if (NULL==DsFilter->FilTypes.Item.FilTypes.ava.Value.pVal)
|
||
{
|
||
Status = STATUS_NO_MEMORY;
|
||
goto Error;
|
||
}
|
||
*((ULONG *)DsFilter->FilTypes.Item.FilTypes.ava.Value.pVal)=
|
||
SampDsClassFromSamObjectType(ObjectType);
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
Error:
|
||
return Status;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
SampFreeDsEnumerationFilter(
|
||
FILTER * DsFilter
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine frees a DS Filter as built by SampBuildDsEnumerationFilter
|
||
|
||
NOTE: This routine must be kept in sync with SampBuildDsEnumerationFilter
|
||
|
||
Argumements:
|
||
|
||
DsFilter -- Pointer to a DS Filter Structure
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// For Now, Hopefully forever, our filters do not have anything hanging
|
||
// of them
|
||
//
|
||
|
||
MIDL_user_free(DsFilter->FilTypes.Item.FilTypes.ava.Value.pVal);
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SampEnumerateAccountNamesRegistry(
|
||
IN SAMP_OBJECT_TYPE ObjectType,
|
||
IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext,
|
||
OUT PSAMPR_ENUMERATION_BUFFER *Buffer,
|
||
IN ULONG PreferedMaximumLength,
|
||
IN ULONG Filter,
|
||
OUT PULONG CountReturned,
|
||
IN BOOLEAN TrustedClient
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the worker routine used to enumerate user, group or alias accounts
|
||
|
||
|
||
Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN
|
||
(ESTABLISHED USING SampSetTransactioDomain()). THIS
|
||
SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain()
|
||
AND BEFORE SampReleaseReadLock().
|
||
|
||
|
||
|
||
All allocation for OUT parameters will be done using MIDL_user_allocate.
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
ObjectType - Indicates whether users or groups are to be enumerated.
|
||
|
||
EnumerationContext - API specific handle to allow multiple calls. The
|
||
caller should return this value in successive calls to retrieve
|
||
additional information.
|
||
|
||
Buffer - Receives a pointer to the buffer containing the
|
||
requested information. The information returned is
|
||
structured as an array of SAM_ENUMERATION_INFORMATION data
|
||
structures. When this information is no longer needed, the
|
||
buffer must be freed 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.
|
||
|
||
Filter - if ObjectType is users, the users can optionally be filtered
|
||
by setting this field with bits from the AccountControlField that
|
||
must match. Otherwise ignored.
|
||
|
||
CountReturned - Receives the number of entries returned.
|
||
|
||
TrustedClient - says whether the caller is trusted or not. If so,
|
||
we'll ignore the SAMP_MAXIMUM_MEMORY_TO_USE restriction on data
|
||
returns.
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - The Service completed successfully, and there
|
||
are no additional entries. Entries may or may not have been
|
||
returned from this call. The CountReturned parameter indicates
|
||
whether any were.
|
||
|
||
STATUS_MORE_ENTRIES - There are more entries which may be obtained
|
||
using successive calls to this API. This is a successful return.
|
||
|
||
STATUS_ACCESS_DENIED - Caller does not have access to request the data.
|
||
|
||
|
||
--*/
|
||
{
|
||
SAMP_V1_0A_FIXED_LENGTH_USER UserV1aFixed;
|
||
NTSTATUS NtStatus, TmpStatus;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
HANDLE TempHandle = NULL;
|
||
ULONG i, NamesToReturn, MaxMemoryToUse;
|
||
ULONG TotalLength,NewTotalLength;
|
||
PSAMP_OBJECT UserContext = NULL;
|
||
PSAMP_ENUMERATION_ELEMENT SampHead = NULL,
|
||
NextEntry = NULL,
|
||
NewEntry = NULL,
|
||
SampTail = NULL;
|
||
BOOLEAN MoreNames;
|
||
BOOLEAN LengthLimitReached = FALSE;
|
||
BOOLEAN FilteredName;
|
||
PSAMPR_RID_ENUMERATION ArrayBuffer = NULL;
|
||
ULONG ArrayBufferLength;
|
||
LARGE_INTEGER IgnoreLastWriteTime;
|
||
UNICODE_STRING AccountNamesKey;
|
||
SID_NAME_USE IgnoreUse;
|
||
|
||
SAMTRACE("SampEnumerateAccountNames");
|
||
|
||
|
||
//
|
||
// Open the registry key containing the account names
|
||
//
|
||
|
||
NtStatus = SampBuildAccountKeyName(
|
||
ObjectType,
|
||
&AccountNamesKey,
|
||
NULL
|
||
);
|
||
|
||
if ( NT_SUCCESS(NtStatus) ) {
|
||
|
||
//
|
||
// Now try to open this registry key so we can enumerate its
|
||
// sub-keys
|
||
//
|
||
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&AccountNamesKey,
|
||
OBJ_CASE_INSENSITIVE,
|
||
SampKey,
|
||
NULL
|
||
);
|
||
|
||
SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0);
|
||
|
||
NtStatus = RtlpNtOpenKey(
|
||
&TempHandle,
|
||
(KEY_READ),
|
||
&ObjectAttributes,
|
||
0
|
||
);
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
//
|
||
// Read names until we have exceeded the preferred maximum
|
||
// length or we run out of names.
|
||
//
|
||
|
||
NamesToReturn = 0;
|
||
SampHead = NULL;
|
||
SampTail = NULL;
|
||
MoreNames = TRUE;
|
||
|
||
NewTotalLength = 0;
|
||
TotalLength = 0;
|
||
|
||
if ( TrustedClient ) {
|
||
|
||
//
|
||
// We place no restrictions on the amount of memory used
|
||
// by a trusted client. Rely on their
|
||
// PreferedMaximumLength to limit us instead.
|
||
//
|
||
|
||
MaxMemoryToUse = 0xffffffff;
|
||
|
||
} else {
|
||
|
||
MaxMemoryToUse = SAMP_MAXIMUM_MEMORY_TO_USE;
|
||
}
|
||
|
||
while (MoreNames) {
|
||
|
||
UNICODE_STRING SubKeyName;
|
||
USHORT LengthRequired;
|
||
|
||
//
|
||
// Try reading with a DEFAULT length buffer first.
|
||
//
|
||
|
||
LengthRequired = 32;
|
||
|
||
NewTotalLength = TotalLength +
|
||
sizeof(UNICODE_STRING) +
|
||
LengthRequired;
|
||
|
||
//
|
||
// Stop if SAM or user specified length limit reached
|
||
//
|
||
|
||
if ( ( (TotalLength != 0) &&
|
||
(NewTotalLength >= PreferedMaximumLength) ) ||
|
||
( NewTotalLength > MaxMemoryToUse )
|
||
) {
|
||
|
||
NtStatus = STATUS_SUCCESS;
|
||
break; // Out of while loop, MoreNames = TRUE
|
||
}
|
||
|
||
NtStatus = SampInitUnicodeString(&SubKeyName, LengthRequired);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
break; // Out of while loop
|
||
}
|
||
|
||
NtStatus = RtlpNtEnumerateSubKey(
|
||
TempHandle,
|
||
&SubKeyName,
|
||
*EnumerationContext,
|
||
&IgnoreLastWriteTime
|
||
);
|
||
|
||
SampDumpRtlpNtEnumerateSubKey(&SubKeyName,
|
||
EnumerationContext,
|
||
IgnoreLastWriteTime);
|
||
|
||
if (NtStatus == STATUS_BUFFER_OVERFLOW) {
|
||
|
||
//
|
||
// The subkey name is longer than our default size,
|
||
// Free the old buffer.
|
||
// Allocate the correct size buffer and read it again.
|
||
//
|
||
|
||
SampFreeUnicodeString(&SubKeyName);
|
||
|
||
LengthRequired = SubKeyName.Length;
|
||
|
||
NewTotalLength = TotalLength +
|
||
sizeof(UNICODE_STRING) +
|
||
LengthRequired;
|
||
|
||
//
|
||
// Stop if SAM or user specified length limit reached
|
||
//
|
||
|
||
if ( ( (TotalLength != 0) &&
|
||
(NewTotalLength >= PreferedMaximumLength) ) ||
|
||
( NewTotalLength > MaxMemoryToUse )
|
||
) {
|
||
|
||
NtStatus = STATUS_SUCCESS;
|
||
break; // Out of while loop, MoreNames = TRUE
|
||
}
|
||
|
||
//
|
||
// Try reading the name again, we should be successful.
|
||
//
|
||
|
||
NtStatus = SampInitUnicodeString(&SubKeyName, LengthRequired);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
break; // Out of while loop
|
||
}
|
||
|
||
NtStatus = RtlpNtEnumerateSubKey(
|
||
TempHandle,
|
||
&SubKeyName,
|
||
*EnumerationContext,
|
||
&IgnoreLastWriteTime
|
||
);
|
||
|
||
SampDumpRtlpNtEnumerateSubKey(&SubKeyName,
|
||
EnumerationContext,
|
||
IgnoreLastWriteTime);
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Free up our buffer if we failed to read the key data
|
||
//
|
||
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
|
||
SampFreeUnicodeString(&SubKeyName);
|
||
|
||
//
|
||
// Map a no-more-entries status to success
|
||
//
|
||
|
||
if (NtStatus == STATUS_NO_MORE_ENTRIES) {
|
||
|
||
MoreNames = FALSE;
|
||
NtStatus = STATUS_SUCCESS;
|
||
}
|
||
|
||
break; // Out of while loop
|
||
}
|
||
|
||
//
|
||
// We've allocated the subkey and read the data into it
|
||
// Stuff it in an enumeration element.
|
||
//
|
||
|
||
NewEntry = MIDL_user_allocate(sizeof(SAMP_ENUMERATION_ELEMENT));
|
||
if (NewEntry == NULL) {
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
} else {
|
||
|
||
*(PUNICODE_STRING)&NewEntry->Entry.Name = SubKeyName;
|
||
|
||
//
|
||
// Now get the Rid value of this named
|
||
// account. We must be able to get the
|
||
// name or we have an internal database
|
||
// corruption.
|
||
//
|
||
|
||
NtStatus = SampLookupAccountRidRegistry(
|
||
ObjectType,
|
||
(PUNICODE_STRING)&NewEntry->Entry.Name,
|
||
STATUS_INTERNAL_DB_CORRUPTION,
|
||
&NewEntry->Entry.RelativeId,
|
||
&IgnoreUse
|
||
);
|
||
|
||
ASSERT(NtStatus != STATUS_INTERNAL_DB_CORRUPTION);
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
FilteredName = TRUE;
|
||
|
||
if ( ( ObjectType == SampUserObjectType ) &&
|
||
( Filter != 0 ) ) {
|
||
|
||
//
|
||
// We only want to return users with a
|
||
// UserAccountControl field that matches
|
||
// the filter passed in. Check here.
|
||
//
|
||
|
||
NtStatus = SampCreateAccountContext(
|
||
SampUserObjectType,
|
||
NewEntry->Entry.RelativeId,
|
||
TRUE, // Trusted client
|
||
TRUE, // Account exists
|
||
&UserContext
|
||
);
|
||
|
||
if ( NT_SUCCESS( NtStatus ) ) {
|
||
|
||
NtStatus = SampRetrieveUserV1aFixed(
|
||
UserContext,
|
||
&UserV1aFixed
|
||
);
|
||
|
||
if ( NT_SUCCESS( NtStatus ) ) {
|
||
|
||
if ( ( UserV1aFixed.UserAccountControl &
|
||
Filter ) == 0 ) {
|
||
|
||
FilteredName = FALSE;
|
||
SampFreeUnicodeString( &SubKeyName );
|
||
}
|
||
}
|
||
|
||
SampDeleteContext( UserContext );
|
||
}
|
||
}
|
||
|
||
*EnumerationContext += 1;
|
||
|
||
if ( NT_SUCCESS( NtStatus ) && ( FilteredName ) ) {
|
||
|
||
NamesToReturn += 1;
|
||
|
||
TotalLength = TotalLength + (ULONG)
|
||
NewEntry->Entry.Name.MaximumLength;
|
||
|
||
NewEntry->Next = NULL;
|
||
|
||
if( SampHead == NULL ) {
|
||
|
||
ASSERT( SampTail == NULL );
|
||
|
||
SampHead = SampTail = NewEntry;
|
||
}
|
||
else {
|
||
|
||
//
|
||
// add this new entry to the list end.
|
||
//
|
||
|
||
SampTail->Next = NewEntry;
|
||
SampTail = NewEntry;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Entry was filtered out, or error getting
|
||
// filter information.
|
||
//
|
||
|
||
MIDL_user_free( NewEntry );
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Error looking up the RID
|
||
//
|
||
|
||
MIDL_user_free( NewEntry );
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Free up our subkey name
|
||
//
|
||
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
|
||
SampFreeUnicodeString(&SubKeyName);
|
||
break; // Out of whle loop
|
||
}
|
||
|
||
} // while
|
||
|
||
|
||
|
||
TmpStatus = NtClose( TempHandle );
|
||
ASSERT( NT_SUCCESS(TmpStatus) );
|
||
|
||
}
|
||
|
||
|
||
SampFreeUnicodeString( &AccountNamesKey );
|
||
}
|
||
|
||
|
||
|
||
|
||
if ( NT_SUCCESS(NtStatus) ) {
|
||
|
||
|
||
|
||
|
||
//
|
||
// If we are returning the last of the names, then change our
|
||
// enumeration context so that it starts at the beginning again.
|
||
//
|
||
|
||
if (!( (NtStatus == STATUS_SUCCESS) && (MoreNames == FALSE))) {
|
||
|
||
NtStatus = STATUS_MORE_ENTRIES;
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Set the number of names being returned
|
||
//
|
||
|
||
(*CountReturned) = NamesToReturn;
|
||
|
||
|
||
//
|
||
// Build a return buffer containing an array of the
|
||
// SAM_ENUMERATION_INFORMATIONs 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;
|
||
|
||
if (NewEntry->Entry.Name.Buffer != NULL ) MIDL_user_free( NewEntry->Entry.Name.Buffer );
|
||
MIDL_user_free( NewEntry );
|
||
}
|
||
|
||
(*EnumerationContext) = 0;
|
||
(*CountReturned) = 0;
|
||
(*Buffer) = NULL;
|
||
|
||
}
|
||
|
||
return(NtStatus);
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
SampValidateEnumerationContext(
|
||
IN PSAMP_OBJECT DomainContext,
|
||
IN SAM_ENUMERATE_HANDLE EnumerationHandle,
|
||
IN ULONG MaxClientEnumerations
|
||
)
|
||
/*++
|
||
|
||
Routine Description
|
||
|
||
This routine Traverses the linked list of enumeration
|
||
context's that are kept with the Domain Context that is
|
||
passed in and tries to find a enumeration context, whose
|
||
handle value matches the passed in Handle.
|
||
|
||
Arguments:
|
||
DomainContext - The DomainContext pointer
|
||
EnumerationHandle -The enumeration Handle
|
||
MaxClientEnumerations - Max Limit of simultaneous enumerations
|
||
from single non trusted client
|
||
|
||
Return Values:
|
||
|
||
STATUS_SUCCESS -- The Enumeration Handle is safe to be case
|
||
as a pointer and used as a Enumeration Context
|
||
STATUS_ACCESS_DENIED -- A New Search was requested and the number
|
||
of simultaneous enumerations from this client is
|
||
already at the limit.
|
||
STATUS_INVALID_HANDLE -- The walk through the enumeration context
|
||
list did not turn up any matches. Therefore
|
||
the handle must be invalid.
|
||
|
||
--*/
|
||
{
|
||
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
ASSERT(DomainContext->ObjectType == SampDomainObjectType);
|
||
|
||
if (SampDomainObjectType!= DomainContext->ObjectType)
|
||
{
|
||
Status = STATUS_INVALID_HANDLE;
|
||
}
|
||
else if (IsDsObject(DomainContext))
|
||
{
|
||
LIST_ENTRY *HeadOfList=
|
||
&(DomainContext->TypeBody.Domain.DsEnumerationContext);
|
||
LIST_ENTRY *CurrentItem ;
|
||
ULONG Count = 0;
|
||
|
||
CurrentItem = HeadOfList->Flink;
|
||
|
||
//
|
||
// Our Default Return
|
||
//
|
||
|
||
Status = STATUS_INVALID_HANDLE;
|
||
|
||
//
|
||
// Traverse the Circular List for a match.
|
||
// If EnumerationHandle is a NULL, this will just walk the
|
||
// loop taking a count of enumeration's that are proceeding
|
||
// on this client.
|
||
//
|
||
|
||
while (CurrentItem!=HeadOfList)
|
||
{
|
||
|
||
if ((0!=EnumerationHandle)
|
||
&& (EnumerationHandle==((SAMP_DS_ENUMERATION_CONTEXT *)
|
||
CurrentItem)->EnumerateHandle))
|
||
{
|
||
//
|
||
// Existing Search handle Matches
|
||
//
|
||
|
||
|
||
//
|
||
// Assert that the contents of Enumeration Handle is same
|
||
// as the pointer to the context block that matches.
|
||
//
|
||
ASSERT(CurrentItem==(LIST_ENTRY *)
|
||
EnumerationHandle);
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Increment the count of enumeration context's we have so far visited
|
||
//
|
||
|
||
Count++;
|
||
|
||
//
|
||
// Traverse to the Next Element in List
|
||
//
|
||
|
||
CurrentItem = CurrentItem->Flink;
|
||
}
|
||
|
||
//
|
||
// New Search was requested
|
||
// verify that count of enumeration contexts is within limits
|
||
// for non trusted clients. For trusted clients override limit check
|
||
//
|
||
|
||
if ((0==EnumerationHandle)
|
||
&& (!(DomainContext->TrustedClient)
|
||
|| (Count < MaxClientEnumerations)))
|
||
{
|
||
//
|
||
// Either trusted client or count of enumerations is within limits
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
|
||
ULONG
|
||
Ownstrlen(
|
||
CHAR * Sz
|
||
)
|
||
/*++
|
||
|
||
Routine Description
|
||
|
||
String Length function for ASCII Null terminated strings. Own version
|
||
as we are not yet inclined to use C-Runtime
|
||
|
||
Arguments
|
||
|
||
Sz - NULL terminated String Whose lenght we eant to count
|
||
|
||
Return Values
|
||
|
||
Length of String
|
||
|
||
--*/
|
||
{
|
||
ULONG Count = 0;
|
||
|
||
ASSERT(Sz);
|
||
|
||
while (*Sz)
|
||
{
|
||
Sz++;
|
||
Count++;
|
||
}
|
||
|
||
return Count;
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
SampFreeRestart(
|
||
IN PRESTART Restart
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This Routine frees a Restart structure as defined by the DS.
|
||
|
||
Arguments
|
||
|
||
Restart Pointer to the Restart funcion
|
||
|
||
Return values:
|
||
|
||
None
|
||
--*/
|
||
{
|
||
|
||
if (Restart!= NULL)
|
||
{
|
||
if (Restart->szIndexName)
|
||
MIDL_user_free(Restart->szIndexName);
|
||
if (Restart->rgbDBKeyUpper)
|
||
MIDL_user_free(Restart->rgbDBKeyUpper);
|
||
if (Restart->rgbDBKeyCurrent)
|
||
MIDL_user_free(Restart->rgbDBKeyCurrent);
|
||
if (Restart->rgbDBKeyLower)
|
||
MIDL_user_free(Restart->rgbDBKeyLower);
|
||
if (Restart->rgbSubString)
|
||
MIDL_user_free(Restart->rgbSubString);
|
||
|
||
MIDL_user_free(Restart);
|
||
}
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SampCopyRestart(
|
||
IN PRESTART OldRestart,
|
||
OUT PRESTART *NewRestart
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This Routine Copies a Restart Structure
|
||
|
||
Arguments:
|
||
|
||
OldRestart - Old Structure
|
||
NewRestart - New Structure
|
||
|
||
Return Values:
|
||
|
||
STATUS_SUCCESS
|
||
STATUS_NO_MEMORY
|
||
|
||
--*/
|
||
{
|
||
|
||
//////////////////////////////////////////////////////////////
|
||
// Define This Macro so that we may save some typing
|
||
// And also make the code a little more readable
|
||
|
||
#define ALLOC_AND_COPY(ToAlloc,ToCopy,Len)\
|
||
if (ToCopy !=NULL)\
|
||
{\
|
||
ToAlloc = MIDL_user_allocate(Len);\
|
||
if (NULL==ToAlloc)\
|
||
{\
|
||
Status = STATUS_NO_MEMORY;\
|
||
goto Error;\
|
||
}\
|
||
RtlCopyMemory(ToAlloc,ToCopy,Len);\
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////
|
||
|
||
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
*NewRestart = NULL;
|
||
if (OldRestart!=NULL)
|
||
{
|
||
// Alloc memory for 1 restart structure
|
||
*NewRestart = MIDL_user_allocate(sizeof(RESTART));
|
||
if (NULL == *NewRestart)
|
||
{
|
||
Status = STATUS_NO_MEMORY;
|
||
goto Error;
|
||
}
|
||
|
||
// Zero the Memory
|
||
RtlZeroMemory(*NewRestart,sizeof(RESTART));
|
||
|
||
// Alloc and Copy Strlen
|
||
ALLOC_AND_COPY((*NewRestart)->szIndexName,
|
||
OldRestart->szIndexName,
|
||
// Not yet prepared to use C Runtine
|
||
Ownstrlen(OldRestart->szIndexName)+1
|
||
);
|
||
ALLOC_AND_COPY((*NewRestart)->rgbDBKeyLower,
|
||
OldRestart->rgbDBKeyLower,
|
||
OldRestart->cbDBKeyLower
|
||
);
|
||
ALLOC_AND_COPY((*NewRestart)->rgbDBKeyCurrent,
|
||
OldRestart->rgbDBKeyCurrent,
|
||
OldRestart->cbDBKeyCurrent
|
||
);
|
||
ALLOC_AND_COPY((*NewRestart)->rgbDBKeyUpper,
|
||
OldRestart->rgbDBKeyUpper,
|
||
OldRestart->cbDBKeyUpper
|
||
);
|
||
ALLOC_AND_COPY((*NewRestart)->rgbSubString,
|
||
OldRestart->rgbSubString,
|
||
OldRestart->cbSubString
|
||
);
|
||
// Copy the remaining simple Data Types.
|
||
(*NewRestart)->StartDNT = OldRestart->StartDNT;
|
||
(*NewRestart)->cbDBKeyLower = OldRestart->cbDBKeyLower;
|
||
(*NewRestart)->cbDBKeyCurrent = OldRestart->cbDBKeyCurrent;
|
||
(*NewRestart)->cbDBKeyUpper = OldRestart->cbDBKeyUpper;
|
||
(*NewRestart)->ulSearchType = OldRestart->ulSearchType;
|
||
(*NewRestart)->ulSearchRootDnt = OldRestart-> ulSearchRootDnt;
|
||
(*NewRestart)->ulSearchRootDnt = OldRestart-> ulSearchRootPDNT;
|
||
(*NewRestart)->iSearchRoot = OldRestart->iSearchRoot;
|
||
(*NewRestart)->fCursored = OldRestart->fCursored;
|
||
(*NewRestart)->attToReadFromKey = OldRestart->attToReadFromKey;
|
||
(*NewRestart)->ulComparisonToApply = OldRestart->ulComparisonToApply;
|
||
(*NewRestart)->cbSubString = OldRestart->cbSubString;
|
||
}
|
||
Error:
|
||
|
||
// If we were not successful free everything
|
||
if (!NT_SUCCESS(Status))
|
||
{
|
||
SampFreeRestart(*NewRestart);
|
||
*NewRestart = NULL;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|