/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
display.c
Abstract:
This file contains services for maintaining the cached display
information.
The information is stored in multiple tables because there are
multiple formats it must be returned in. The tables maintained
include:
AccountsByRid - includes all user and global group accounts
by RID. Aliases may be added to this list at some time
in the future.
NormalUsersByName - Normal user accounts, sorted by name.
MachinesByName - Machine user accounts, sorted by name.
InterDomainByName - Interdomain trust accounts, sorted by
name.
GroupsByName - Global group accounts, sorted by name.
Any time an entry is placed in or removed from one of "ByName"
tables, it is also placed in or removed from the "ByRid" table.
User and machine accounts are added to the display cache in one
operation. So, there is a single boolean flag indicating whether
or not these tables are valid. The groups are maintained in a
separate table, and so there is another flag indicating whether
or not that table is valid.
The Rid table is only valid if both the group table and the
user/machine tables are valid.
Author:
Dave Chalmers (Davidc) 1-April-1992
Environment:
User Mode - Win32
Revision History:
--*/
///////////////////////////////////////////////////////////////////////////////
// //
// Includes //
// //
///////////////////////////////////////////////////////////////////////////////
#include <samsrvp.h>
///////////////////////////////////////////////////////////////////////////////
// //
// private service prototypes //
// //
///////////////////////////////////////////////////////////////////////////////
NTSTATUS
SampCreateDisplayInformation (
DOMAIN_DISPLAY_INFORMATION DisplayType
);
VOID
SampDeleteDisplayInformation (
PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation,
SAMP_OBJECT_TYPE ObjectType
);
NTSTATUS
SampRetrieveDisplayInfoFromDisk(
PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation,
SAMP_OBJECT_TYPE ObjectType
);
NTSTATUS
SampAddDisplayAccount (
PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation,
SAMP_OBJECT_TYPE ObjectType,
PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo
);
NTSTATUS
SampDeleteDisplayAccount (
PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation,
SAMP_OBJECT_TYPE ObjectType,
PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo
);
NTSTATUS
SampUpdateDisplayAccount(
PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation,
SAMP_OBJECT_TYPE ObjectType,
PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo
);
NTSTATUS
SampTallyTableStatistics (
PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation,
SAMP_OBJECT_TYPE ObjectType
);
NTSTATUS
SampEmptyGenericTable2 (
PRTL_GENERIC_TABLE2 Table,
BOOLEAN FreeElements
);
PVOID
SampGenericTable2Allocate (
CLONG BufferSize
);
VOID
SampGenericTable2Free (
PVOID Buffer
);
RTL_GENERIC_COMPARE_RESULTS
SampCompareUserNodeByName (
PVOID Node1,
PVOID Node2
);
RTL_GENERIC_COMPARE_RESULTS
SampCompareMachineNodeByName ( // Also used for Interdomain trust accounts
PVOID Node1,
PVOID Node2
);
RTL_GENERIC_COMPARE_RESULTS
SampCompareGroupNodeByName (
PVOID Node1,
PVOID Node2
);
RTL_GENERIC_COMPARE_RESULTS
SampCompareNodeByRid (
PVOID Node1,
PVOID Node2
);
NTSTATUS
SampInitializeUserInfo(
PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo,
PDOMAIN_DISPLAY_USER *UserInfo,
BOOLEAN CopyData
);
NTSTATUS
SampInitializeMachineInfo( // Also used for Interdomain trust accounts
PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo,
PDOMAIN_DISPLAY_MACHINE *MachineInfo,
BOOLEAN CopyData
);
NTSTATUS
SampInitializeGroupInfo(
PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo,
PDOMAIN_DISPLAY_GROUP *GroupInfo,
BOOLEAN CopyData
);
NTSTATUS
SampDuplicateUserInfo(
PDOMAIN_DISPLAY_USER Destination,
PDOMAIN_DISPLAY_USER Source,
ULONG Index
);
NTSTATUS
SampDuplicateMachineInfo( // Also used for Interdomain trust accounts
PDOMAIN_DISPLAY_MACHINE Destination,
PDOMAIN_DISPLAY_MACHINE Source,
ULONG Index
);
NTSTATUS
SampDuplicateGroupInfo(
PDOMAIN_DISPLAY_GROUP Destination,
PDOMAIN_DISPLAY_GROUP Source,
ULONG Index
);
NTSTATUS
SampDuplicateOemUserInfo(
PDOMAIN_DISPLAY_OEM_USER Destination,
PDOMAIN_DISPLAY_USER Source,
ULONG Index
);
NTSTATUS
SampDuplicateOemGroupInfo(
PDOMAIN_DISPLAY_OEM_GROUP Destination,
PDOMAIN_DISPLAY_GROUP Source,
ULONG Index
);
VOID
SampFreeUserInfo(
PDOMAIN_DISPLAY_USER UserInfo
);
VOID
SampFreeMachineInfo(
PDOMAIN_DISPLAY_MACHINE MachineInfo
);
VOID
SampFreeGroupInfo(
PDOMAIN_DISPLAY_GROUP GroupInfo
);
VOID
SampFreeOemUserInfo(
PDOMAIN_DISPLAY_OEM_USER UserInfo
);
VOID
SampFreeOemGroupInfo(
PDOMAIN_DISPLAY_OEM_GROUP GroupInfo
);
VOID
SampSwapUserInfo(
PDOMAIN_DISPLAY_USER Info1,
PDOMAIN_DISPLAY_USER Info2
);
VOID
SampSwapMachineInfo( // Also used for Interdomain trust accounts
PDOMAIN_DISPLAY_MACHINE Info1,
PDOMAIN_DISPLAY_MACHINE Info2
);
VOID
SampSwapGroupInfo(
PDOMAIN_DISPLAY_GROUP Info1,
PDOMAIN_DISPLAY_GROUP Info2
);
ULONG
SampBytesRequiredUserNode (
PDOMAIN_DISPLAY_USER Node
);
ULONG
SampBytesRequiredMachineNode ( // Also used for Interdomain trust accounts
PDOMAIN_DISPLAY_MACHINE Node
);
ULONG
SampBytesRequiredGroupNode (
PDOMAIN_DISPLAY_GROUP Node
);
ULONG
SampBytesRequiredOemUserNode (
PDOMAIN_DISPLAY_OEM_USER Node
);
ULONG
SampBytesRequiredOemGroupNode (
PDOMAIN_DISPLAY_OEM_GROUP Node
);
VOID
SampDisplayDiagnostic( VOID );
VOID
SampDisplayDiagEnumRids( VOID );
LONG
SampCompareDisplayStrings(
IN PUNICODE_STRING String1,
IN PUNICODE_STRING String2,
IN BOOLEAN IgnoreCase
);
//
// Macros for deciding whether an account is:
//
// A normal user account
//
// A machine account
//
// An Interdomain trust account
//
// Included in the display cache
//
//
#define USER_ACCOUNT(AccountControl) ((AccountControl & \
(USER_NORMAL_ACCOUNT | \
USER_TEMP_DUPLICATE_ACCOUNT)) != 0)
#define MACHINE_ACCOUNT(AccountControl) ((AccountControl & \
(USER_WORKSTATION_TRUST_ACCOUNT | \
USER_SERVER_TRUST_ACCOUNT)) != 0)
#define INTERDOMAIN_ACCOUNT(AccountControl) (((AccountControl) & \
(USER_INTERDOMAIN_TRUST_ACCOUNT)) != 0)
#define DISPLAY_ACCOUNT(AccountControl) (USER_ACCOUNT(AccountControl) || \
MACHINE_ACCOUNT(AccountControl) || \
INTERDOMAIN_ACCOUNT(AccountControl))
//
// Test to see if Rid table is valid
//
// BOOLEAN
// SampRidTableValid( IN ULONG DomainIndex )
//
#define SampRidTableValid(DI) ( \
(SampDefinedDomains[DI].DisplayInformation.UserAndMachineTablesValid) && \
(SampDefinedDomains[DI].DisplayInformation.GroupTableValid) \
)
///////////////////////////////////////////////////////////////////////////////
// //
// Private data types //
// //
///////////////////////////////////////////////////////////////////////////////
//
// All entries in the display cache are expected to start with this
// data structure.
//
typedef struct _SAMP_DISPLAY_ENTRY_HEADER {
//
// The index field plays two roles. Within the generic table,
// it is used to indicate which type of account this is. The
// valid types are: SAM_USER_ACCOUNT, SAM_GLOBAL_GROUP_ACCOUNT,
// or SAM_LOCAL_GROUP_ACCOUNT.
//
// Otherwise, this field is filled in just before being returned
// to query and other client calls.
//
ULONG Index;
//
// The RID of the account
//
ULONG Rid;
} SAMP_DISPLAY_ENTRY_HEADER, *PSAMP_DISPLAY_ENTRY_HEADER;
///////////////////////////////////////////////////////////////////////////////
// //
// Module-wide variables //
// //
///////////////////////////////////////////////////////////////////////////////
LCID SampSystemDefaultLCID;
///////////////////////////////////////////////////////////////////////////////
// //
// RPC exported routines //
// //
///////////////////////////////////////////////////////////////////////////////
NTSTATUS
SamrQueryDisplayInformation (
IN SAMPR_HANDLE DomainHandle,
IN DOMAIN_DISPLAY_INFORMATION DisplayInformation,
IN ULONG Index,
IN ULONG EntriesRequested,
IN ULONG PreferredMaximumLength,
OUT PULONG TotalAvailable,
OUT PULONG TotalReturned,
OUT PSAMPR_DISPLAY_INFO_BUFFER Buffer
)
/*++
Routine Description:
Thin wrapper around SamrQueryDisplayInformation3().
Provided for compatibility with down-level clients.
--*/
{
return( SamrQueryDisplayInformation3(
DomainHandle,
DisplayInformation,
Index,
EntriesRequested,
PreferredMaximumLength,
TotalAvailable,
TotalReturned,
Buffer
) );
}
NTSTATUS
SamrQueryDisplayInformation2 (
IN SAMPR_HANDLE DomainHandle,
IN DOMAIN_DISPLAY_INFORMATION DisplayInformation,
IN ULONG Index,
IN ULONG EntriesRequested,
IN ULONG PreferredMaximumLength,
OUT PULONG TotalAvailable,
OUT PULONG TotalReturned,
OUT PSAMPR_DISPLAY_INFO_BUFFER Buffer
)
/*++
Routine Description:
Thin wrapper around SamrQueryDisplayInformation3().
Provided for compatibility with down-level clients.
--*/
{
return( SamrQueryDisplayInformation3(
DomainHandle,
DisplayInformation,
Index,
EntriesRequested,
PreferredMaximumLength,
TotalAvailable,
TotalReturned,
Buffer
) );
}
NTSTATUS
SamrQueryDisplayInformation3 (
IN SAMPR_HANDLE DomainHandle,
IN DOMAIN_DISPLAY_INFORMATION DisplayInformation,
IN ULONG Index,
IN ULONG EntriesRequested,
IN ULONG PreferredMaximumLength,
OUT PULONG TotalAvailable,
OUT PULONG TotalReturned,
OUT PSAMPR_DISPLAY_INFO_BUFFER Buffer
)
/*++
Routine Description:
This routine provides fast return of information commonly
needed to be displayed in user interfaces.
NT User Interface has a requirement for quick enumeration of SAM
accounts for display in list boxes. (Replication has similar but
broader requirements.)
The netui listboxes all contain similar information. e.g:
o AccountControl, the bits that identify the account type,
eg, HOME, REMOTE, SERVER, WORKSTATION, etc.
o Logon name (machine name for computers)
o Full name (not used for computers)
o Comment (admin comment for users)
SAM maintains this data locally in two sorted indexed cached
lists identified by infolevels.
o DomainDisplayUser: HOME and REMOTE user accounts only
o DomainDisplayMachine: SERVER and WORKSTATION accounts only
Note that trust accounts, groups, and aliases are not in either of
these lists.
Added for NT1.0A -
o Group enumeration has been added in NT1.0A
with the following characteristic:
We did not change the RPC interface ID. This allows
callers to continue to call down-level servers. However,
down-level servers will return an error if they passed
this information level.
o OEM string info levels were added for jimh (Chicago). These
info levels dramatically reduce the memory needed to query
the limited information that Chicago is interested in.
Parameters:
DomainHandle - A handle to an open domain for DOMAIN_LIST_ACCOUNTS.
DisplayInformation - Indicates which information is to be enumerated.
Index - The index of the first entry to be retrieved.
PreferedMaximumLength - A recommended upper limit to the number of
bytes to be returned. The returned information is allocated by
this routine.
TotalAvailable - Total number of bytes availabe in the specified info
class.
TotalReturned - Number of bytes actually returned for this call. Zero
indicates there are no entries with an index as large as that
specified.
ReturnedEntryCount - Number of entries returned by this call. Zero
indicates there are no entries with an index as large as that
specified.
Buffer - Receives a pointer to a buffer containing a (possibly)
sorted list of the requested information. This buffer is
allocated by this routine and contains the following
structure:
DomainDisplayMachine --> An array of ReturnedEntryCount elements
of type DOMAIN_DISPLAY_USER. This is
followed by the bodies of the various
strings pointed to from within the
DOMAIN_DISPLAY_USER structures.
DomainDisplayMachine --> An array of ReturnedEntryCount elements
of type DOMAIN_DISPLAY_MACHINE. This is
followed by the bodies of the various
strings pointed to from within the
DOMAIN_DISPLAY_MACHINE structures.
DomainDisplayGroup --> An array of ReturnedEntryCount elements
of type DOMAIN_DISPLAY_GROUP. This is
followed by the bodies of the various
strings pointed to from within the
DOMAIN_DISPLAY_GROUP structures.
DomainDisplayOemUser --> An array of ReturnedEntryCount elements
of type DOMAIN_DISPLAY_OEM_USER. This is
followed by the bodies of the various
strings pointed to from within the
DOMAIN_DISPLAY_OEM_user structures.
DomainDisplayOemGroup --> An array of ReturnedEntryCount elements
of type DOMAIN_DISPLAY_OEM_GROUP. This is
followed by the bodies of the various
strings pointed to from within the
DOMAIN_DISPLAY_OEM_GROUP structures.
Return Values:
STATUS_SUCCESS - normal, successful completion.
STATUS_ACCESS_DENIED - The specified handle was not opened for
the necessary access.
STATUS_INVALID_HANDLE - The specified handle is not that of an
opened Domain object.
STATUS_INVALID_INFO_CLASS - The requested class of information
is not legitimate for this service.
--*/
{
NTSTATUS
NtStatus,
IgnoreStatus;
PSAMP_OBJECT
DomainContext;
SAMP_OBJECT_TYPE
FoundType;
PSAMP_DEFINED_DOMAINS
Domain;
PSAMPR_DOMAIN_DISPLAY_USER
UserElement;
PSAMPR_DOMAIN_DISPLAY_MACHINE
MachineElement;
PSAMPR_DOMAIN_DISPLAY_GROUP
GroupElement;
ULONG
ReturnedBytes = 0,
ReturnedItems = 0;
PVOID
RestartKey;
//
// Prepare for failure
//
*TotalAvailable = 0;
*TotalReturned = 0;
switch (DisplayInformation) {
case DomainDisplayUser:
Buffer->UserInformation.EntriesRead = 0;
Buffer->UserInformation.Buffer = NULL;
break;
case DomainDisplayMachine:
Buffer->MachineInformation.EntriesRead = 0;
Buffer->MachineInformation.Buffer = NULL;
break;
case DomainDisplayGroup:
Buffer->GroupInformation.EntriesRead = 0;
Buffer->GroupInformation.Buffer = NULL;
break;
case DomainDisplayOemUser:
Buffer->OemUserInformation.EntriesRead = 0;
Buffer->OemUserInformation.Buffer = NULL;
break;
case DomainDisplayOemGroup:
Buffer->OemGroupInformation.EntriesRead = 0;
Buffer->OemGroupInformation.Buffer = NULL;
break;
default:
return(STATUS_INVALID_INFO_CLASS);
}
//
// If they don't want anything, that's what they'll get
//
if (EntriesRequested == 0) {
return(STATUS_SUCCESS);
}
//
// Make sure we don't try to allocate too much memory on
// the user's behalf
//
if (EntriesRequested > 5000) {
EntriesRequested = 5000;
}
//
// Grab the read lock
//
SampAcquireReadLock();
//
// Validate type of, and access to object.
//
DomainContext = (PSAMP_OBJECT)DomainHandle;
NtStatus = SampLookupContext(
DomainContext,
DOMAIN_LIST_ACCOUNTS,
SampDomainObjectType, // ExpectedType
&FoundType
);
if (NT_SUCCESS(NtStatus)) {
Domain = &SampDefinedDomains[ DomainContext->DomainIndex ];
//
// Set up the common loop statistics
//
ReturnedBytes = 0;
ReturnedItems = 0;
switch (DisplayInformation) {
case DomainDisplayUser:
//
// Recreate our cached data if necessary
//
NtStatus = SampCreateDisplayInformation(DomainDisplayUser);
//
// Set the Restart Key from the passed in index
//
UserElement = RtlRestartKeyByIndexGenericTable2(
&Domain->DisplayInformation.UserTable,
Index,
&RestartKey
);
if (UserElement == NULL) {
NtStatus = STATUS_SUCCESS;
Buffer->GroupInformation.EntriesRead = 0;
*TotalReturned = 0;
*TotalAvailable = 0; // Not supported for this info level
break; // out of switch
}
//
// Allocate space for array of elements
//
Buffer->UserInformation.Buffer = MIDL_user_allocate(
EntriesRequested * sizeof(SAMPR_DOMAIN_DISPLAY_USER));
if (Buffer->UserInformation.Buffer == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
break; // out of switch
}
//
// Prepare default return value
//
NtStatus = STATUS_MORE_ENTRIES;
//
// Increment the index value for assignment in our return
// buffer
//
Index++;
do {
NTSTATUS TempStatus;
//
// Store a copy of this element in the return buffer.
//
TempStatus = SampDuplicateUserInfo(
(PDOMAIN_DISPLAY_USER)
&(Buffer->UserInformation.Buffer[ReturnedItems]),
(PDOMAIN_DISPLAY_USER)UserElement,
Index);
Index++;
if (!NT_SUCCESS(TempStatus)) {
//
// Free up everything we've allocated so far
//
while(ReturnedItems > 0) {
ReturnedItems --;
SampFreeUserInfo((PDOMAIN_DISPLAY_USER)
&(Buffer->UserInformation.Buffer[ReturnedItems]));
}
MIDL_user_free(Buffer->UserInformation.Buffer);
Buffer->UserInformation.Buffer = NULL;
NtStatus = TempStatus;
break; // out of do loop
}
//
// Update loop statistics
//
ReturnedBytes += SampBytesRequiredUserNode(
(PDOMAIN_DISPLAY_USER)UserElement);
ReturnedItems ++;
//
// Go find the next element
//
UserElement = RtlEnumerateGenericTable2(
&Domain->DisplayInformation.UserTable,
&RestartKey
);
if (UserElement == NULL) {
NtStatus = STATUS_SUCCESS;
break; // out of do loop
}
} while ( (ReturnedBytes < PreferredMaximumLength) &&
(ReturnedItems < EntriesRequested) );
//
// Update output parameters
//
if (NT_SUCCESS(NtStatus)) {
Buffer->UserInformation.EntriesRead = ReturnedItems;
*TotalReturned = ReturnedBytes;
*TotalAvailable = Domain->DisplayInformation.TotalBytesInUserTable;
}
break; // out of switch
case DomainDisplayMachine:
//
// Recreate our cached data if necessary
//
NtStatus = SampCreateDisplayInformation(DomainDisplayMachine);
//
// Set the Restart Key from the passed in index
//
MachineElement = RtlRestartKeyByIndexGenericTable2(
&Domain->DisplayInformation.MachineTable,
Index,
&RestartKey
);
if (MachineElement == NULL) {
NtStatus = STATUS_SUCCESS;
Buffer->GroupInformation.EntriesRead = 0;
*TotalReturned = 0;
*TotalAvailable = 0; // Not supported for this info level
break; // out of switch
}
//
// Allocate space for array of elements
//
Buffer->MachineInformation.Buffer = MIDL_user_allocate(
EntriesRequested * sizeof(SAMPR_DOMAIN_DISPLAY_MACHINE));
if (Buffer->MachineInformation.Buffer == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
break; // out of switch
}
//
// Prepare default return value
//
NtStatus = STATUS_MORE_ENTRIES;
//
// Increment the index value for assignment in our return
// buffer
//
Index++;
do {
NTSTATUS TempStatus;
//
// Store a copy of this element in the return buffer.
//
TempStatus = SampDuplicateMachineInfo(
(PDOMAIN_DISPLAY_MACHINE)
&(Buffer->MachineInformation.Buffer[ReturnedItems]),
(PDOMAIN_DISPLAY_MACHINE)MachineElement,
Index);
Index++;
if (!NT_SUCCESS(TempStatus)) {
//
// Free up everything we've allocated so far
//
while(ReturnedItems > 0) {
ReturnedItems--;
SampFreeMachineInfo((PDOMAIN_DISPLAY_MACHINE)
&(Buffer->MachineInformation.Buffer[ReturnedItems]));
}
MIDL_user_free(Buffer->MachineInformation.Buffer);
Buffer->MachineInformation.Buffer = NULL;
NtStatus = TempStatus;
break; // out of do loop
}
//
// Update loop statistics
//
ReturnedBytes += SampBytesRequiredMachineNode(
(PDOMAIN_DISPLAY_MACHINE)MachineElement);
ReturnedItems ++;
//
// Go find the next element
//
MachineElement = RtlEnumerateGenericTable2(
&Domain->DisplayInformation.MachineTable,
&RestartKey
);
if (MachineElement == NULL) {
NtStatus = STATUS_SUCCESS;
break; // out of do loop
}
} while ( (ReturnedBytes < PreferredMaximumLength) &&
(ReturnedItems < EntriesRequested) );
//
// Update output parameters
//
if (NT_SUCCESS(NtStatus)) {
Buffer->MachineInformation.EntriesRead = ReturnedItems;
*TotalReturned = ReturnedBytes;
*TotalAvailable = Domain->DisplayInformation.TotalBytesInMachineTable;
}
break; // out of switch
case DomainDisplayGroup:
//
// Recreate our cached data if necessary
//
NtStatus = SampCreateDisplayInformation(DomainDisplayGroup);
//
// Set the Restart Key from the passed in index
//
GroupElement = RtlRestartKeyByIndexGenericTable2(
&Domain->DisplayInformation.GroupTable,
Index,
&RestartKey
);
if (GroupElement == NULL) {
NtStatus = STATUS_SUCCESS;
Buffer->GroupInformation.EntriesRead = 0;
*TotalReturned = 0;
*TotalAvailable = 0; // Not supported for this info level
break; // out of switch
}
//
// Allocate space for array of elements
//
Buffer->GroupInformation.Buffer = MIDL_user_allocate(
EntriesRequested * sizeof(SAMPR_DOMAIN_DISPLAY_GROUP));
if (Buffer->GroupInformation.Buffer == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
break; // out of switch
}
//
// Prepare default return value
//
NtStatus = STATUS_MORE_ENTRIES;
//
// Increment the index value for assignment in our return
// buffer
//
Index++;
do {
NTSTATUS TempStatus;
//
// Store a copy of this element in the return buffer.
//
TempStatus = SampDuplicateGroupInfo(
(PDOMAIN_DISPLAY_GROUP)
&(Buffer->GroupInformation.Buffer[ReturnedItems]),
(PDOMAIN_DISPLAY_GROUP)GroupElement,
Index);
Index++;
if (!NT_SUCCESS(TempStatus)) {
//
// Free up everything we've allocated so far
//
while(ReturnedItems > 0) {
ReturnedItems--;
SampFreeGroupInfo((PDOMAIN_DISPLAY_GROUP)
&(Buffer->GroupInformation.Buffer[ReturnedItems]));
}
MIDL_user_free(Buffer->GroupInformation.Buffer);
Buffer->GroupInformation.Buffer = NULL;
NtStatus = TempStatus;
break; // out of do loop
}
//
// Update loop statistics
//
ReturnedBytes += SampBytesRequiredGroupNode(
(PDOMAIN_DISPLAY_GROUP)GroupElement);
ReturnedItems ++;
//
// Go find the next element
//
GroupElement = RtlEnumerateGenericTable2(
&Domain->DisplayInformation.GroupTable,
&RestartKey
);
if (GroupElement == NULL) {
NtStatus = STATUS_SUCCESS;
break; // out of do loop
}
} while ( (ReturnedBytes < PreferredMaximumLength) &&
(ReturnedItems < EntriesRequested) );
//
// Update output parameters
//
if (NT_SUCCESS(NtStatus)) {
Buffer->GroupInformation.EntriesRead = ReturnedItems;
*TotalReturned = ReturnedBytes;
*TotalAvailable = Domain->DisplayInformation.TotalBytesInGroupTable;
}
break; // out of switch
case DomainDisplayOemUser:
//
// Recreate our cached data if necessary
//
NtStatus = SampCreateDisplayInformation(DomainDisplayUser);
//
// Set the Restart Key from the passed in index
//
UserElement = RtlRestartKeyByIndexGenericTable2(
&Domain->DisplayInformation.UserTable,
Index,
&RestartKey
);
if (UserElement == NULL) {
NtStatus = STATUS_SUCCESS;
Buffer->GroupInformation.EntriesRead = 0;
*TotalReturned = 0;
*TotalAvailable = 0; // Not supported for this info level
break; // out of switch
}
//
// Allocate space for array of elements
//
Buffer->UserInformation.Buffer = MIDL_user_allocate(
EntriesRequested * sizeof(SAMPR_DOMAIN_DISPLAY_OEM_USER));
if (Buffer->OemUserInformation.Buffer == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
break; // out of switch
}
//
// Prepare default return value
//
NtStatus = STATUS_MORE_ENTRIES;
//
// Increment the index value for assignment in our return
// buffer
//
Index++;
do {
NTSTATUS TempStatus;
//
// Store a copy of this element in the return buffer.
//
TempStatus = SampDuplicateOemUserInfo(
(PDOMAIN_DISPLAY_OEM_USER)
&(Buffer->OemUserInformation.Buffer[ReturnedItems]),
(PDOMAIN_DISPLAY_USER)UserElement,
Index);
Index++;
if (!NT_SUCCESS(TempStatus)) {
//
// Free up everything we've allocated so far
//
while(ReturnedItems > 0) {
ReturnedItems --;
SampFreeOemUserInfo((PDOMAIN_DISPLAY_OEM_USER)
&(Buffer->UserInformation.Buffer[ReturnedItems]));
}
MIDL_user_free(Buffer->OemUserInformation.Buffer);
Buffer->OemUserInformation.Buffer = NULL;
NtStatus = TempStatus;
break; // out of do loop
}
//
// Update loop statistics
//
ReturnedBytes +=
SampBytesRequiredOemUserNode(
(PDOMAIN_DISPLAY_OEM_USER)
&(Buffer->OemUserInformation.Buffer[ReturnedItems]));
ReturnedItems ++;
//
// Go find the next element
//
UserElement = RtlEnumerateGenericTable2(
&Domain->DisplayInformation.UserTable,
&RestartKey
);
if (UserElement == NULL) {
NtStatus = STATUS_SUCCESS;
break; // out of do loop
}
} while ( (ReturnedBytes < PreferredMaximumLength) &&
(ReturnedItems < EntriesRequested) );
//
// Update output parameters
//
if (NT_SUCCESS(NtStatus)) {
Buffer->UserInformation.EntriesRead = ReturnedItems;
*TotalReturned = ReturnedBytes;
*TotalAvailable = 0; // Not supported for this info level
}
break; // out of switch
case DomainDisplayOemGroup:
//
// Recreate our cached data if necessary
//
NtStatus = SampCreateDisplayInformation(DomainDisplayGroup);
//
// Set the Restart Key from the passed in index
//
GroupElement = RtlRestartKeyByIndexGenericTable2(
&Domain->DisplayInformation.GroupTable,
Index,
&RestartKey
);
if (GroupElement == NULL) {
NtStatus = STATUS_SUCCESS;
Buffer->GroupInformation.EntriesRead = 0;
*TotalReturned = 0;
*TotalAvailable = 0; // Not supported for this info level
break; // out of switch
}
//
// Allocate space for array of elements
//
Buffer->GroupInformation.Buffer = MIDL_user_allocate(
EntriesRequested * sizeof(SAMPR_DOMAIN_DISPLAY_OEM_GROUP));
if (Buffer->OemGroupInformation.Buffer == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
break; // out of switch
}
//
// Prepare default return value
//
NtStatus = STATUS_MORE_ENTRIES;
//
// Increment the index value for assignment in our return
// buffer
//
Index++;
do {
NTSTATUS TempStatus;
//
// Store a copy of this element in the return buffer.
//
TempStatus = SampDuplicateOemGroupInfo(
(PDOMAIN_DISPLAY_OEM_GROUP)
&(Buffer->OemGroupInformation.Buffer[ReturnedItems]),
(PDOMAIN_DISPLAY_GROUP)GroupElement,
Index);
Index++;
if (!NT_SUCCESS(TempStatus)) {
//
// Free up everything we've allocated so far
//
while(ReturnedItems > 0) {
ReturnedItems --;
SampFreeOemGroupInfo((PDOMAIN_DISPLAY_OEM_GROUP)
&(Buffer->GroupInformation.Buffer[ReturnedItems]));
}
MIDL_user_free(Buffer->OemGroupInformation.Buffer);
Buffer->OemGroupInformation.Buffer = NULL;
NtStatus = TempStatus;
break; // out of do loop
}
//
// Update loop statistics
//
ReturnedBytes +=
SampBytesRequiredOemGroupNode(
(PDOMAIN_DISPLAY_OEM_GROUP)
&(Buffer->OemGroupInformation.Buffer[ReturnedItems]));
ReturnedItems ++;
//
// Go find the next element
//
GroupElement = RtlEnumerateGenericTable2(
&Domain->DisplayInformation.GroupTable,
&RestartKey
);
if (GroupElement == NULL) {
NtStatus = STATUS_SUCCESS;
break; // out of do loop
}
} while ( (ReturnedBytes < PreferredMaximumLength) &&
(ReturnedItems < EntriesRequested) );
//
// Update output parameters
//
if (NT_SUCCESS(NtStatus)) {
Buffer->GroupInformation.EntriesRead = ReturnedItems;
*TotalReturned = ReturnedBytes;
*TotalAvailable = 0; // Not supported for this info level
}
break; // out of switch
}
//
// De-reference the object
//
IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE);
ASSERT(NT_SUCCESS(IgnoreStatus));
}
//
// Free the read lock
//
SampReleaseReadLock();
return(NtStatus);
}
NTSTATUS
SamrGetDisplayEnumerationIndex (
IN SAMPR_HANDLE DomainHandle,
IN DOMAIN_DISPLAY_INFORMATION DisplayInformation,
IN PRPC_UNICODE_STRING Prefix,
OUT PULONG Index
)
/*++
Routine Description:
This wrapper around SamrGetDisplayEnumerationIndex2().
Provided for compatibility with down-level clients.
--*/
{
return(SamrGetDisplayEnumerationIndex2( DomainHandle,
DisplayInformation,
Prefix,
Index
) );
}
NTSTATUS
SamrGetDisplayEnumerationIndex2 (
IN SAMPR_HANDLE DomainHandle,
IN DOMAIN_DISPLAY_INFORMATION DisplayInformation,
IN PRPC_UNICODE_STRING Prefix,
OUT PULONG Index
)
/*++
Routine Description:
This routine returns the index of the entry which alphabetically
immediatly preceeds a specified prefix. If no such entry exists,
then zero is returned as the index.
Parameters:
DomainHandle - A handle to an open domain for DOMAIN_LIST_ACCOUNTS.
DisplayInformation - Indicates which sorted information class is
to be searched.
Prefix - The prefix to compare.
Index - Receives the index of the entry of the information class
with a LogonName (or MachineName) which immediatly preceeds the
provided prefix string. If there are no elements which preceed
the prefix, then zero is returned.
Return Values:
STATUS_SUCCESS - normal, successful completion.
STATUS_ACCESS_DENIED - The specified handle was not opened for
the necessary access.
STATUS_INVALID_HANDLE - The specified handle is not that of an
opened Domain object.
STATUS_NO_MORE_ENTRIES - There are no entries for this information class.
--*/
{
NTSTATUS
NtStatus,
IgnoreStatus;
PSAMP_OBJECT
DomainContext;
SAMP_OBJECT_TYPE
FoundType;
PSAMP_DEFINED_DOMAINS
Domain;
PRTL_GENERIC_TABLE2
Table;
DOMAIN_DISPLAY_USER
UserElement;
DOMAIN_DISPLAY_MACHINE
MachineElement;
DOMAIN_DISPLAY_GROUP
GroupElement;
RTL_GENERIC_COMPARE_RESULTS
CompareResult;
PRTL_GENERIC_2_COMPARE_ROUTINE
CompareRoutine;
PVOID
Element,
NextElement,
RestartKey;
ULONG
CurrentIndex;
//
// Check the information class
//
if ((DisplayInformation != DomainDisplayUser) &&
(DisplayInformation != DomainDisplayMachine) &&
(DisplayInformation != DomainDisplayGroup)
) {
return(STATUS_INVALID_INFO_CLASS);
}
//
// Grab the read lock
//
SampAcquireReadLock();
//
// Validate type of, and access to object.
//
DomainContext = (PSAMP_OBJECT)DomainHandle;
NtStatus = SampLookupContext(
DomainContext,
DOMAIN_LIST_ACCOUNTS,
SampDomainObjectType, // ExpectedType
&FoundType
);
if (NT_SUCCESS(NtStatus)) {
Domain = &SampDefinedDomains[ DomainContext->DomainIndex ];
//
// Set default return value
//
(*Index) = 0;
//
// Recreate our cached data if necessary
//
NtStatus = SampCreateDisplayInformation(DisplayInformation);
if (NT_SUCCESS(NtStatus)) {
//
// Set up
// The table to search,
// The comparison routine to use,
// An appropriate element for the search.
//
switch (DisplayInformation) {
case DomainDisplayUser:
Table = &Domain->DisplayInformation.UserTable;
CompareRoutine = SampCompareUserNodeByName;
Element = (PVOID)&UserElement;
UserElement.LogonName = *(PUNICODE_STRING)Prefix;
break; // out of switch
case DomainDisplayMachine:
Table = &Domain->DisplayInformation.MachineTable;
CompareRoutine = SampCompareMachineNodeByName;
Element = (PVOID)&MachineElement;
MachineElement.Machine = *(PUNICODE_STRING)Prefix;
break; // out of switch
case DomainDisplayGroup:
Table = &Domain->DisplayInformation.GroupTable;
CompareRoutine = SampCompareGroupNodeByName;
Element = (PVOID)&GroupElement;
GroupElement.Group = *(PUNICODE_STRING)Prefix;
break; // out of switch
}
if (RtlIsGenericTable2Empty(Table)) {
NtStatus = STATUS_NO_MORE_ENTRIES;
} else {
//
// Now compare each entry until we find the one asked
// for.
//
CurrentIndex = 0;
RestartKey = NULL;
for (NextElement = RtlEnumerateGenericTable2(Table, &RestartKey);
NextElement != NULL;
NextElement = RtlEnumerateGenericTable2(Table, &RestartKey)) {
//
// Compare with passed in element
//
CompareResult = (*CompareRoutine)( NextElement, Element );
if (CompareResult != GenericLessThan) {
break; // break out of for loop
}
CurrentIndex++;
}
//
// CurrentIndex has the return value in it.
//
ASSERT( CurrentIndex <= RtlNumberElementsGenericTable2(Table) );
(*Index) = CurrentIndex;
NtStatus = STATUS_SUCCESS;
}
}
//
// De-reference the object
//
IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE);
ASSERT(NT_SUCCESS(IgnoreStatus));
}
//
// Free the read lock
//
SampReleaseReadLock();
return(NtStatus);
}
///////////////////////////////////////////////////////////////////////////////
// //
// Routines available to trusted clients in SAM's process //
// //
///////////////////////////////////////////////////////////////////////////////
NTSTATUS
SamIEnumerateAccountRids(
IN SAMPR_HANDLE DomainHandle,
IN ULONG AccountTypesMask,
IN ULONG StartingRid,
IN ULONG PreferedMaximumLength,
OUT PULONG ReturnCount,
OUT PULONG *AccountRids
)
/*++
Routine Description:
Provide a list of account RIDs. The caller may ask for one or
more types of account rids in a single call.
The returned rids are in ascending value order.
WARNING - This routine is only callable by trusted clients.
Therefore, parameter checking is only performed
in checked-build systems.
Parameters:
DomainHandle - handle to the domain whose accounts are to be
enumerated.
AccountTypesMask - Mask indicating which types of accounts
the caller wants enumerated. These included:
SAM_USER_ACCOUNT
SAM_GLOBAL_GROUP_ACCOUNT
SAM_LOCAL_GROUP_ACCOUNT (not yet supported)
StartingRid - A rid that is less than the lowest value rid to be
included in the enumeration.
PreferedMaximumLength - Provides a restriction on how much memory
may be returned in this call. This is not a hard upper limit,
but serves as a guideline.
ReturnCount - Receives a count of the number of rids returned.
AccountRids - Receives a pointer to an array of rids. If
ReturnCount is zero, then this will be returned as NULL.
Otherwise, it will point to an array containing ReturnCount
rids.
Return Values:
STATUS_SUCCESS - The Service completed successfully, and there
are no additional entries.
STATUS_MORE_ENTRIES - There are more entries, so call again.
This is a successful return.
STATUS_INVALID_INFO_CLASS - The specified AccountTypesMask contained
unknown or unsupported account types.
STATUS_NO_MEMORY - Could not allocate pool to complete the call.
--*/
{
NTSTATUS
NtStatus,
IgnoreStatus;
PSAMP_OBJECT
DomainContext;
SAMP_OBJECT_TYPE
FoundType;
PSAMP_DEFINED_DOMAINS
Domain;
PRTL_GENERIC_TABLE2
Table;
ULONG
MaxEntries,
Count,
AccountType;
PVOID
RestartKey;
PSAMP_DISPLAY_ENTRY_HEADER
Element;
SAMP_DISPLAY_ENTRY_HEADER
RestartValue;
//
// Prepare for failure
//
(*ReturnCount) = 0;
(*AccountRids) = NULL;
#if DBG
if ( (AccountTypesMask & ~( SAM_USER_ACCOUNT | SAM_GLOBAL_GROUP_ACCOUNT))
!= 0 ) {
return(STATUS_INVALID_INFO_CLASS);
}
#endif //DBG
//
// Grab the read lock
//
SampAcquireReadLock();
//
// Validate type of, and access to object.
//
DomainContext = (PSAMP_OBJECT)DomainHandle;
NtStatus = SampLookupContext(
DomainContext,
0, // Trusted clients only
SampDomainObjectType, // ExpectedType
&FoundType
);
if (NT_SUCCESS(NtStatus)) {
Domain = &SampDefinedDomains[ DomainContext->DomainIndex ];
Table = &Domain->DisplayInformation.RidTable;
//
// If the RID table isn't valid, force it to be made valid.
//
if (!SampRidTableValid(DomainContext->DomainIndex)) {
NtStatus = SampCreateDisplayInformation ( DomainDisplayUser ); //User and machine
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampCreateDisplayInformation ( DomainDisplayGroup );
}
}
if (NT_SUCCESS(NtStatus)) {
//
// Allocate a return buffer.
// Only allocate as much as we can use.
// This is limited either by PreferedMaximumLength
// or the number of entries in the table.
//
MaxEntries =
( PreferedMaximumLength / sizeof(ULONG) );
if (MaxEntries == 0) {
MaxEntries = 1; // Always return at least one
}
if (MaxEntries > RtlNumberElementsGenericTable2(Table) ) {
MaxEntries = RtlNumberElementsGenericTable2(Table);
}
PreferedMaximumLength = MaxEntries *
sizeof(SAMP_DISPLAY_ENTRY_HEADER);
(*AccountRids) = MIDL_user_allocate( PreferedMaximumLength );
if ((*AccountRids) == NULL) {
STATUS_NO_MEMORY;
}
//
// Get the restart key based upon the passed in RID.
//
Table = &Domain->DisplayInformation.RidTable;
RestartValue.Rid = StartingRid;
Element = RtlRestartKeyByValueGenericTable2(
Table,
&RestartValue,
&RestartKey
);
//
// Now we may loop obtaining entries until we reach
// either MaxEntries or the end of the table.
//
// WARNING - there is one special case that we have to
// take care of. If the returned Element is not null,
// but the RestartKey is null, then the caller has
// asked for an enumeration and passed in the last rid
// defined. If we aren't careful, this will cause an
// enumeration to be started from the beginning of the
// list again. Instead, return status indicating we have
// no more entries.
//
Count = 0;
if (((Element != NULL) && (RestartKey == NULL))) {
Element = NULL; // Used to signify no more entries found
} else {
for (Element = RtlEnumerateGenericTable2(Table, &RestartKey);
( (Element != NULL) && (Count < MaxEntries) );
Element = RtlEnumerateGenericTable2(Table, &RestartKey)) {
//
// Make sure this is an account that was asked for
//
AccountType = Element->Index;
if ((AccountType & AccountTypesMask) != 0) {
(*AccountRids)[Count] = Element->Rid;
Count++;
}
}
}
//
// Now figure out what we have done:
//
// Returned all entries in table => STATUS_SUCCESS
// More entries to return => STATUS_MORE_ENTRIES
//
// Count == 0 => free AccountRid array.
//
if (Element == NULL) {
NtStatus = STATUS_SUCCESS;
} else {
NtStatus = STATUS_MORE_ENTRIES;
}
if (Count == 0) {
MIDL_user_free( (*AccountRids) );
(*AccountRids) = NULL;
}
(*ReturnCount) = Count;
}
//
// De-reference the object
//
IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE);
ASSERT(NT_SUCCESS(IgnoreStatus));
}
//
// Free the read lock
//
SampReleaseReadLock();
return(NtStatus);
}
///////////////////////////////////////////////////////////////////////////////
// //
// Routines available to other SAM modules //
// //
///////////////////////////////////////////////////////////////////////////////
NTSTATUS
SampInitializeDisplayInformation (
ULONG DomainIndex
)
/*++
Routine Description:
This routines initializes the display information structure.
This involves initializing the User, Machine and Group trees (empty),
and setting the Valid flag to FALSE.
If this is the account domain, we also create the display information.
Parameters:
DomainIndex - An index into the DefinedDomains array. This array
contains information about the domain being openned,
including its name.
Return Values:
STATUS_SUCCESS - normal, successful completion.
--*/
{
PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation;
//
// This must be initialized before we use SampCompareDisplayStrings().
//
SampSystemDefaultLCID = GetSystemDefaultLCID();
DisplayInformation = &SampDefinedDomains[DomainIndex].DisplayInformation;
RtlInitializeGenericTable2(
&DisplayInformation->UserTable,
SampCompareUserNodeByName,
SampGenericTable2Allocate,
SampGenericTable2Free);
RtlInitializeGenericTable2(
&DisplayInformation->MachineTable,
SampCompareMachineNodeByName,
SampGenericTable2Allocate,
SampGenericTable2Free);
RtlInitializeGenericTable2(
&DisplayInformation->InterdomainTable,
SampCompareMachineNodeByName,
SampGenericTable2Allocate,
SampGenericTable2Free);
RtlInitializeGenericTable2(
&DisplayInformation->GroupTable,
SampCompareGroupNodeByName,
SampGenericTable2Allocate,
SampGenericTable2Free);
RtlInitializeGenericTable2(
&DisplayInformation->RidTable,
SampCompareNodeByRid,
SampGenericTable2Allocate,
SampGenericTable2Free);
DisplayInformation->UserAndMachineTablesValid = FALSE;
DisplayInformation->GroupTableValid = FALSE;
if ( SampProductType == NtProductLanManNt &&
DomainIndex == SampDefinedDomainsCount - 1 ) {
//
// Grab the read lock and indicate which domain the transaction is in
//
SampAcquireReadLock();
SampSetTransactionDomain( DomainIndex );
//
// Populate the Display Cache
//
(VOID) SampCreateDisplayInformation(DomainDisplayUser);
(VOID) SampCreateDisplayInformation(DomainDisplayGroup);
//
// Free the read lock
//
SampReleaseReadLock();
}
return(STATUS_SUCCESS);
}
VOID
SampDeleteDisplayInformation (
PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation,
SAMP_OBJECT_TYPE ObjectType
)
/*++
Routine Description:
This routines frees up any resources used by the display information.
Note: It use to be that we could selectively invalidate
portions of the display cache (e.g., users, or groups).
With the addition of the RID table, this becomes
problematic. So, now the approach is to flush all tables
for a domain if any the tables in that domain are flushed.
Parameters:
DisplayInformation - The display information structure to delete.
ObjectType - Indicates which table to delete the information from.
Return Values:
STATUS_SUCCESS - normal, successful completion.
--*/
{
NTSTATUS NtStatus;
//
// Empty the user table and check it really is empty
//
NtStatus = SampEmptyGenericTable2(&DisplayInformation->UserTable, FALSE);
ASSERT(NT_SUCCESS(NtStatus));
ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->UserTable));
DisplayInformation->TotalBytesInUserTable = 0;
//
// Empty the machine table and check it really is empty
//
NtStatus = SampEmptyGenericTable2(&DisplayInformation->MachineTable, FALSE);
ASSERT(NT_SUCCESS(NtStatus));
ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->MachineTable));
DisplayInformation->TotalBytesInMachineTable = 0;
//
// Empty the Interdomain table and check it really is empty
//
NtStatus = SampEmptyGenericTable2(&DisplayInformation->InterdomainTable, FALSE);
ASSERT(NT_SUCCESS(NtStatus));
ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->InterdomainTable));
DisplayInformation->TotalBytesInInterdomainTable = 0;
//
// Empty the Group table and check it really is empty
//
NtStatus = SampEmptyGenericTable2(&DisplayInformation->GroupTable, FALSE);
ASSERT(NT_SUCCESS(NtStatus));
ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->GroupTable));
DisplayInformation->TotalBytesInGroupTable = 0;
//
// Empty the Rid table and check it really is empty.
//
NtStatus = SampEmptyGenericTable2(&DisplayInformation->RidTable, FALSE);
ASSERT(NT_SUCCESS(NtStatus));
ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->RidTable));
DisplayInformation->TotalBytesInRidTable = 0;
}
NTSTATUS
SampMarkDisplayInformationInvalid (
SAMP_OBJECT_TYPE ObjectType
)
/*++
Routine Description:
This routine invalidates any cached display information. This
causes it to be recreated the next time a client queries it.
Later we will probably start/restart a thread here and have it
re-create the display information in the background.
Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN
(ESTABLISHED USING SampSetTransactioDomain()). THIS
SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain()
AND BEFORE SampReleaseWriteLock().
Another Note: It use to be that we could selectively invalidate
portions of the display cache (e.g., users, or groups).
With the addition of the RID table, this becomes
problematic. So, now the approach is to flush all tables
for a domain if any the tables in that domain are flushed.
Parameters:
ObjectType - SampUserObjectType or SampGroupObjectType. Only the
appropriate tables will be marked Invalid. For User type, the
user and machine tables will be marked Invalid. For Group type,
the group table will be marked Invalid.
Return Values:
STATUS_SUCCESS - normal, successful completion.
--*/
{
PSAMP_DEFINED_DOMAINS Domain;
ASSERT(SampTransactionWithinDomain == TRUE);
SampDiagPrint(DISPLAY_CACHE,
("SAM: MarkDisplayInformationInvalid : Emptying cache\n"));
//
// Get pointer to the current domain structure
//
Domain = &SampDefinedDomains[SampTransactionDomainIndex];
//
// Delete any cached data
//
SampDeleteDisplayInformation(&Domain->DisplayInformation, ObjectType);
//
// Set the Valid flag to FALSE
//
Domain->DisplayInformation.UserAndMachineTablesValid = FALSE;
Domain->DisplayInformation.GroupTableValid = FALSE;
return(STATUS_SUCCESS);
}
NTSTATUS
SampCreateDisplayInformation (
DOMAIN_DISPLAY_INFORMATION DisplayType
)
/*++
Routine Description:
This routine builds the cached display information for the current
domain.
Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN
(ESTABLISHED USING SampSetTransactioDomain()). THIS
SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain()
AND BEFORE SampReleaseReadLock().
Parameters:
DisplayType - Indicates which type of display information is
being created. This leads us to the appropriate table(s).
Return Values:
STATUS_SUCCESS - normal, successful completion.
--*/
{
NTSTATUS NtStatus;
PSAMP_DEFINED_DOMAINS Domain;
PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation;
ASSERT(SampTransactionWithinDomain == TRUE);
Domain = &SampDefinedDomains[SampTransactionDomainIndex];
DisplayInformation = &Domain->DisplayInformation;
switch (DisplayType) {
case DomainDisplayUser:
case DomainDisplayMachine:
//
// If the cache is valid, nothing to do
//
if (DisplayInformation->UserAndMachineTablesValid) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: CreateDisplayInformation : User/Machine Cache is valid, nothing to do\n"));
return(STATUS_SUCCESS);
};
SampDiagPrint(DISPLAY_CACHE,
("SAM: CreateDisplayInformation : Creating user/machine cache...\n"));
ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->UserTable));
ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->MachineTable));
ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->InterdomainTable));
NtStatus = SampRetrieveDisplayInfoFromDisk( DisplayInformation, SampUserObjectType );
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampTallyTableStatistics(DisplayInformation, SampUserObjectType);
}
//
// Clean up on error
if (!NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: CreateDisplayInformation FAILED: 0x%lx\n", NtStatus));
SampDeleteDisplayInformation(&Domain->DisplayInformation, SampUserObjectType);
} else {
Domain->DisplayInformation.UserAndMachineTablesValid = TRUE;
}
break; // out of switch
case DomainDisplayGroup:
//
// If the cache is valid, nothing to do
//
if (DisplayInformation->GroupTableValid) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: CreateDisplayInformation : Group Cache is valid, nothing to do\n"));
return(STATUS_SUCCESS);
};
SampDiagPrint(DISPLAY_CACHE,
("SAM: CreateDisplayInformation : Creating group cache...\n"));
ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->GroupTable));
NtStatus = SampRetrieveDisplayInfoFromDisk( DisplayInformation, SampGroupObjectType );
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampTallyTableStatistics(DisplayInformation, SampGroupObjectType);
}
//
// Clean up on error
if (!NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: CreateDisplayInformation FAILED: 0x%lx\n", NtStatus));
SampDeleteDisplayInformation(&Domain->DisplayInformation, SampGroupObjectType);
} else {
Domain->DisplayInformation.GroupTableValid = TRUE;
}
break; // out of switch
}
return(NtStatus);
}
NTSTATUS
SampRetrieveDisplayInfoFromDisk(
PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation,
SAMP_OBJECT_TYPE ObjectType
)
{
NTSTATUS NtStatus;
SAM_ENUMERATE_HANDLE EnumerationContext;
PSAMPR_ENUMERATION_BUFFER EnumerationBuffer;
ULONG i;
ULONG CountReturned;
BOOLEAN MoreEntries;
//
// Enumerate the accounts.
// For each account, get the relevant information on it,
// and add to either the UserTable, MachineTable, or GroupTable.
//
EnumerationContext = 0;
do {
NtStatus = SampEnumerateAccountNames(
ObjectType,
&EnumerationContext,
&EnumerationBuffer,
200000, // PreferedMaximumLength
0L, // no filter
&CountReturned,
FALSE // trusted client
);
if (!NT_SUCCESS(NtStatus)) {
SampDiagPrint( DISPLAY_CACHE_ERRORS,
("SAM: Retrieve Info From Disk - "
"Error enumerating account names (0x%lx)\n",
NtStatus) );
break;
}
//
// Make a note if there are more entries
//
MoreEntries = (NtStatus == STATUS_MORE_ENTRIES);
//
// For each account, get the necessary information for it
// and add to the appropriate display information table
//
for (i = 0; i < EnumerationBuffer->EntriesRead; i++) {
ULONG AccountRid =
EnumerationBuffer->Buffer[i].RelativeId;
PUNICODE_STRING AccountName =
(PUNICODE_STRING)&(EnumerationBuffer->Buffer[i].Name);
SAMP_V1_0A_FIXED_LENGTH_USER UserV1aFixed; // Contains account control
SAMP_V1_0A_FIXED_LENGTH_GROUP GroupV1Fixed; // Contains attributes
SAMP_ACCOUNT_DISPLAY_INFO AccountInfo;
PSAMP_OBJECT AccountContext;
//
// Open a context to the account
//
NtStatus = SampCreateAccountContext(
ObjectType,
AccountRid,
TRUE, // Trusted client
TRUE, // Account exists
&AccountContext
);
if (!NT_SUCCESS(NtStatus)) {
SampDiagPrint( DISPLAY_CACHE_ERRORS,
("SAM: Retrieve Info From Disk - "
"Error Creating account context (0x%lx)\n",
NtStatus) );
break; // out of for loop
}
//
// Get the account control information
//
switch (ObjectType) {
case SampUserObjectType:
NtStatus = SampRetrieveUserV1aFixed(AccountContext, &UserV1aFixed);
if (!NT_SUCCESS(NtStatus)) {
SampDeleteContext( AccountContext );
SampDiagPrint( DISPLAY_CACHE_ERRORS,
("SAM: Retrieve USER From Disk - "
"Error getting V1a Fixed (0x%lx)\n",
NtStatus) );
break; // out of for loop
}
//
// If this is not an account we're interested in skip it
//
if (!DISPLAY_ACCOUNT(UserV1aFixed.UserAccountControl)) {
SampDeleteContext( AccountContext );
continue; // next account
}
//
// Get the admin comment
//
NtStatus = SampGetUnicodeStringAttribute(
AccountContext,
SAMP_USER_ADMIN_COMMENT,
FALSE, // Don't make copy
&AccountInfo.Comment
);
if (!NT_SUCCESS(NtStatus)) {
SampDeleteContext( AccountContext );
SampDiagPrint( DISPLAY_CACHE_ERRORS,
("SAM: Retrieve USER From Disk - "
"Error getting admin comment (0x%lx)\n",
NtStatus) );
break; // out of for loop
}
//
// Get the full name
//
NtStatus = SampGetUnicodeStringAttribute(
AccountContext,
SAMP_USER_FULL_NAME,
FALSE, // Don't make copy
&AccountInfo.FullName
);
if (!NT_SUCCESS(NtStatus)) {
SampDeleteContext( AccountContext );
SampDiagPrint( DISPLAY_CACHE_ERRORS,
("SAM: Retrieve USER From Disk - "
"Error getting full name (0x%lx)\n",
NtStatus) );
break; // out of for loop
}
//
// Set the account control
//
AccountInfo.AccountControl = UserV1aFixed.UserAccountControl;
break; // out of switch
case SampGroupObjectType:
NtStatus = SampRetrieveGroupV1Fixed(AccountContext, &GroupV1Fixed);
if (!NT_SUCCESS(NtStatus)) {
SampDeleteContext( AccountContext );
SampDiagPrint( DISPLAY_CACHE_ERRORS,
("SAM: Retrieve GROUP From Disk - "
"Error getting V1 fixed (0x%lx)\n",
NtStatus) );
break; // out of for loop
}
//
// Get the admin comment
//
NtStatus = SampGetUnicodeStringAttribute(
AccountContext,
SAMP_GROUP_ADMIN_COMMENT,
FALSE, // Don't make copy
&AccountInfo.Comment
);
if (!NT_SUCCESS(NtStatus)) {
SampDeleteContext( AccountContext );
SampDiagPrint( DISPLAY_CACHE_ERRORS,
("SAM: Retrieve GROUP From Disk - "
"Error getting admin comment (0x%lx)\n",
NtStatus) );
break; // out of for loop
}
//
// Set the attributes
//
AccountInfo.AccountControl = GroupV1Fixed.Attributes;
break; // out of switch
}
//
// Now add this account to the cached data
//
AccountInfo.Rid = AccountRid;
AccountInfo.Name = *((PUNICODE_STRING)(&EnumerationBuffer->Buffer[i].Name));
NtStatus = SampAddDisplayAccount(DisplayInformation,
ObjectType,
&AccountInfo);
//
// We're finished with the account context
//
SampDeleteContext( AccountContext );
//
// Check the result of adding the account to the cache
//
if (!NT_SUCCESS(NtStatus)) {
break; // out of for loop
}
} // end_for
//
// Free up the enumeration buffer returned
//
SamIFree_SAMPR_ENUMERATION_BUFFER(EnumerationBuffer);
} while ( MoreEntries );
return(NtStatus);
}
NTSTATUS
SampUpdateDisplayInformation (
PSAMP_ACCOUNT_DISPLAY_INFO OldAccountInfo OPTIONAL,
PSAMP_ACCOUNT_DISPLAY_INFO NewAccountInfo OPTIONAL,
SAMP_OBJECT_TYPE ObjectType
)
/*++
Routine Description:
This routines updates the cached display information to reflect
changes to a single account.
If any error occurs, this routine marks the cached information
Invalid so it will get fixed during re-creation.
Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN
(ESTABLISHED USING SampSetTransactioDomain()). THIS
SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain()
AND BEFORE SampReleaseWriteLock().
Parameters:
OldAccountInfo - The old information for this account. If this is NULL
then the account is being added.
The only fields required in the OldAccountInfo are
Name
AccountControl
Rid
NewAccountInfo - The new information for this account. If this is NULL
then the account is being deleted.
ObjectType - Indicates whether the account is a user account or
group account.
Return Values:
STATUS_SUCCESS - normal, successful completion.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PSAMP_DEFINED_DOMAINS Domain;
PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation;
BOOLEAN DoUpdate;
ASSERT( ARGUMENT_PRESENT(OldAccountInfo) ||
ARGUMENT_PRESENT(NewAccountInfo)
);
ASSERT(SampTransactionWithinDomain == TRUE);
Domain = &SampDefinedDomains[SampTransactionDomainIndex];
DisplayInformation = &Domain->DisplayInformation;
IF_SAMP_GLOBAL( DISPLAY_CACHE ) {
if (ARGUMENT_PRESENT(OldAccountInfo) && ARGUMENT_PRESENT(NewAccountInfo)) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: UpdateDisplayInformation : Updating cache for old account <%wZ>, new <%wZ>\n",
&OldAccountInfo->Name, &NewAccountInfo->Name));
}
if (!ARGUMENT_PRESENT(OldAccountInfo) && ARGUMENT_PRESENT(NewAccountInfo)) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: UpdateDisplayInformation : Adding account <%wZ> to cache\n",
&NewAccountInfo->Name));
}
if (ARGUMENT_PRESENT(OldAccountInfo) && !ARGUMENT_PRESENT(NewAccountInfo)) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: UpdateDisplayInformation : Deleting account <%wZ> from cache\n",
&OldAccountInfo->Name));
}
} //end_IF_SAMP_GLOBAL
switch (ObjectType) {
case SampUserObjectType:
//
// If the cache is Invalid there's nothing to do
//
if (!DisplayInformation->UserAndMachineTablesValid) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: UpdateDisplayInformation : User Cache is Invalid, nothing to do\n"));
return(STATUS_SUCCESS);
};
//
// If this is an update to an existing account then try
// to do an inplace update of the cache.
// If this fails because it's too complex etc, then revert to
// the less efficient method of deleting the old, then adding the new.
//
DoUpdate = FALSE;
if (ARGUMENT_PRESENT(OldAccountInfo) && ARGUMENT_PRESENT(NewAccountInfo)) {
//
// We can only do an update if both old and new accounts
// are types that we keep in the display cache.
//
if ( DISPLAY_ACCOUNT(OldAccountInfo->AccountControl) &&
DISPLAY_ACCOUNT(NewAccountInfo->AccountControl) ) {
//
// We can only do an update if the account is still of
// the same type. i.e. it hasn't jumped cache table.
//
if ( (USER_ACCOUNT(OldAccountInfo->AccountControl) ==
USER_ACCOUNT(NewAccountInfo->AccountControl)) &&
(MACHINE_ACCOUNT(OldAccountInfo->AccountControl) ==
MACHINE_ACCOUNT(NewAccountInfo->AccountControl)) ) {
//
// We can only do an update if the account name hasn't changed
//
if (RtlEqualUnicodeString( &OldAccountInfo->Name,
&NewAccountInfo->Name,
FALSE // Case sensitive
)) {
//
// Everything has been checked out - we can do an update
//
DoUpdate = TRUE;
}
}
}
}
break; // out of switch
case SampGroupObjectType:
//
// If the cache is already Invalid there's nothing to do
//
if (!DisplayInformation->GroupTableValid) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: UpdateDisplayInformation : Group Cache is Invalid, nothing to do\n"));
return(STATUS_SUCCESS);
};
//
// If this is an update to an existing account then try
// and do an inplace update of the cache.
// If this fails because it's too complex etc, then revert to
// the less efficient method of deleting the old, then adding the new.
//
DoUpdate = FALSE;
if (ARGUMENT_PRESENT(OldAccountInfo) && ARGUMENT_PRESENT(NewAccountInfo)) {
//
// We can only do an update if the account name hasn't changed
//
if (RtlEqualUnicodeString( &OldAccountInfo->Name,
&NewAccountInfo->Name,
FALSE // Case sensitive
)) {
DoUpdate = TRUE;
}
}
break; // out of switch
}
//
// Do an update if possible, otherwise do delete then insert
//
if (DoUpdate) {
NtStatus = SampUpdateDisplayAccount(DisplayInformation,
ObjectType,
NewAccountInfo);
} else {
NtStatus = STATUS_SUCCESS;
//
// Delete the old account
//
if (ARGUMENT_PRESENT(OldAccountInfo)) {
NtStatus = SampDeleteDisplayAccount(DisplayInformation,
ObjectType,
OldAccountInfo);
}
//
// Add the new account
//
if (NT_SUCCESS(NtStatus) && ARGUMENT_PRESENT(NewAccountInfo)) {
NtStatus = SampAddDisplayAccount(DisplayInformation,
ObjectType,
NewAccountInfo);
}
//
// Re-tally the cache
//
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampTallyTableStatistics(DisplayInformation, ObjectType);
}
}
if (!NT_SUCCESS(NtStatus)) {
//
// Something screwed up.
// Mark the cache Invalid - it will get rebuilt from scratch
// at the next query.
//
KdPrint(("SAM: The display cache is inconsistent, forcing rebuild\n"));
ASSERT(FALSE);
NtStatus = SampMarkDisplayInformationInvalid(ObjectType);
ASSERT(NT_SUCCESS(NtStatus));
NtStatus = STATUS_SUCCESS;
}
return(NtStatus);
}
///////////////////////////////////////////////////////////////////////////////
// //
// Routines available within this module only //
// //
///////////////////////////////////////////////////////////////////////////////
NTSTATUS
SampDeleteDisplayAccount (
PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation,
SAMP_OBJECT_TYPE ObjectType,
PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo
)
/*++
Routine Description:
This routines deletes the specified account from the cached display
information. It is asummed that if this account is a cached type it
will appear in the appropriate cache table.
Parameters:
DisplayInformation - Pointer to cached display information
ObjectType - Indicates which table(s) to look for the account in.
AccountInfo - The account to be deleted.
Return Values:
STATUS_SUCCESS - normal, successful completion.
STATUS_INTERNAL_ERROR - the account is a cached type yet could not be
found in the cached data.
--*/
{
NTSTATUS NtStatus;
ULONG Control = AccountInfo->AccountControl;
BOOLEAN Success;
//
// We expect the cache to be valid
//
#if DBG
switch (ObjectType) {
case SampUserObjectType:
ASSERT(DisplayInformation->UserAndMachineTablesValid);
break; //out of switch
case SampGroupObjectType:
ASSERT(DisplayInformation->GroupTableValid);
break; //out of switch
}
#endif //DBG
SampDiagPrint(DISPLAY_CACHE,
("SAM: DeleteDisplayAccount : Deleting account <%wZ>\n", &AccountInfo->Name));
switch (ObjectType) {
case SampUserObjectType:
if (USER_ACCOUNT(Control)) {
DOMAIN_DISPLAY_USER LocalUserInfo;
PDOMAIN_DISPLAY_USER UserInfo;
SampDiagPrint(DISPLAY_CACHE,
("SAM: DeleteDisplayAccount : Deleting account from user table\n"));
UserInfo = &LocalUserInfo;
NtStatus = SampInitializeUserInfo(AccountInfo, &UserInfo, FALSE);
if (NT_SUCCESS(NtStatus)) {
//
// Delete the account from the user table
//
Success = RtlDeleteElementGenericTable2(
&DisplayInformation->UserTable,
(PVOID)UserInfo);
if (!Success) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: DeleteDisplayAccount : Failed to delete element from user table\n"));
ASSERT(FALSE);
NtStatus = STATUS_INTERNAL_ERROR;
} else {
//
// Now remove it to the RID table
//
(VOID)RtlDeleteElementGenericTable2(
&DisplayInformation->RidTable,
(PVOID)UserInfo);
if (!Success) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: DeleteDisplayAccount : Failed to delete element from RID table\n"));
NtStatus = STATUS_INTERNAL_ERROR;
ASSERT(Success);
}
}
}
} else if (MACHINE_ACCOUNT(Control)) {
DOMAIN_DISPLAY_MACHINE LocalMachineInfo;
PDOMAIN_DISPLAY_MACHINE MachineInfo;
SampDiagPrint(DISPLAY_CACHE,
("SAM: DeleteDisplayAccount : Deleting account from machine table\n"));
MachineInfo = &LocalMachineInfo;
NtStatus = SampInitializeMachineInfo(AccountInfo, &MachineInfo, FALSE);
if (NT_SUCCESS(NtStatus)) {
//
// Delete the account from the machine table
//
Success = RtlDeleteElementGenericTable2(
&DisplayInformation->MachineTable,
(PVOID)MachineInfo);
if (!Success) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: DeleteDisplayAccount : Failed to delete element from machine table\n"));
ASSERT(FALSE);
NtStatus = STATUS_INTERNAL_ERROR;
} else {
//
// Now remove it to the RID table
//
Success = RtlDeleteElementGenericTable2(
&DisplayInformation->RidTable,
(PVOID)MachineInfo);
if (!Success) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: DeleteDisplayAccount : Failed to delete element from RID table\n"));
NtStatus = STATUS_INTERNAL_ERROR;
ASSERT(Success);
}
}
}
} else if (INTERDOMAIN_ACCOUNT(Control)) {
//
// Interdomain account
//
DOMAIN_DISPLAY_MACHINE LocalInterdomainInfo;
PDOMAIN_DISPLAY_MACHINE InterdomainInfo;
SampDiagPrint(DISPLAY_CACHE,
("SAM: DeleteDisplayAccount : Deleting account from Interdomain table\n"));
InterdomainInfo = &LocalInterdomainInfo;
NtStatus = SampInitializeMachineInfo(AccountInfo, &InterdomainInfo, FALSE);
if (NT_SUCCESS(NtStatus)) {
//
// Delete the account from the Interdomain table
//
Success = RtlDeleteElementGenericTable2(
&DisplayInformation->InterdomainTable,
(PVOID)InterdomainInfo);
if (!Success) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: DeleteDisplayAccount : Failed to delete element from Interdomain table\n"));
ASSERT(FALSE);
NtStatus = STATUS_INTERNAL_ERROR;
} else {
//
// Now remove it to the RID table
//
Success = RtlDeleteElementGenericTable2(
&DisplayInformation->RidTable,
(PVOID)InterdomainInfo);
if (!Success) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: DeleteDisplayAccount : Failed to delete element from RID table\n"));
NtStatus = STATUS_INTERNAL_ERROR;
ASSERT(Success);
}
}
}
} else {
//
// This account is not one that we cache - nothing to do
//
NtStatus = STATUS_SUCCESS;
SampDiagPrint(DISPLAY_CACHE,
("SAM: DeleteDisplayAccount : Account is not one that we cache, account control = 0x%lx\n", Control));
}
break; //out of switch
case SampGroupObjectType:
{
DOMAIN_DISPLAY_GROUP LocalGroupInfo;
PDOMAIN_DISPLAY_GROUP GroupInfo;
SampDiagPrint(DISPLAY_CACHE,
("SAM: DeleteDisplayAccount : Deleting account from Group table\n"));
GroupInfo = &LocalGroupInfo;
NtStatus = SampInitializeGroupInfo(AccountInfo, &GroupInfo, FALSE);
if (NT_SUCCESS(NtStatus)) {
//
// Delete the account from the Group table
//
Success = RtlDeleteElementGenericTable2(
&DisplayInformation->GroupTable,
(PVOID)GroupInfo);
if (!Success) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: DeleteDisplayAccount : Failed to delete element from Group table\n"));
ASSERT(FALSE);
NtStatus = STATUS_INTERNAL_ERROR;
} else {
//
// Now remove it to the RID table
//
(VOID)RtlDeleteElementGenericTable2(
&DisplayInformation->RidTable,
(PVOID)GroupInfo);
if (!Success) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: DeleteDisplayAccount : Failed to delete element from RID table\n"));
NtStatus = STATUS_INTERNAL_ERROR;
ASSERT(Success);
}
}
}
break; //out of switch
}
}
return(STATUS_SUCCESS);
}
NTSTATUS
SampAddDisplayAccount (
PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation,
SAMP_OBJECT_TYPE ObjectType,
PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo
)
/*++
Routine Description:
This routines adds the specified account to the cached display
information as appropriate to its type.
Parameters:
DisplayInformation - Pointer to cached display information
ObjectType - SampUserObjectType or SampGroupObjectType. Helps
determine which table it goes into.
AccountInfo - The account to be added.
Return Values:
STATUS_SUCCESS - normal, successful completion.
STATUS_INTERNAL_ERROR - the account already existed in the cache
--*/
{
NTSTATUS
NtStatus;
ULONG
Control = AccountInfo->AccountControl;
BOOLEAN
NewElement;
SampDiagPrint(DISPLAY_CACHE,
("SAM: AddDisplayAccount : Adding account <%wZ>\n", &AccountInfo->Name));
if (ObjectType == SampGroupObjectType) {
PDOMAIN_DISPLAY_GROUP GroupInfo;
SampDiagPrint(DISPLAY_CACHE,
("SAM: AddDisplayAccount : Adding account to group table\n"));
NtStatus = SampInitializeGroupInfo(AccountInfo, &GroupInfo, TRUE);
if (NT_SUCCESS(NtStatus)) {
//
// Add the account to the Group table
//
(VOID)RtlInsertElementGenericTable2(
&DisplayInformation->GroupTable,
GroupInfo,
&NewElement);
if (!NewElement) {
SampDiagPrint(DISPLAY_CACHE_ERRORS,
("SAM: AddDisplayAccount : Account already exists in GROUP table\n"));
ASSERT(FALSE);
SampFreeGroupInfo(GroupInfo);
NtStatus = STATUS_INTERNAL_ERROR;
} else {
//
// Now add it to the RID table
//
(VOID)RtlInsertElementGenericTable2(
&DisplayInformation->RidTable,
GroupInfo,
&NewElement);
if (!NewElement) {
SampDiagPrint(DISPLAY_CACHE_ERRORS,
("SAM: AddDisplayAccount : Account already exists in RID table\n"));
NtStatus = STATUS_INTERNAL_ERROR;
ASSERT(NewElement);
}
}
}
} else {
ASSERT(ObjectType == SampUserObjectType);
if (USER_ACCOUNT(Control)) {
PDOMAIN_DISPLAY_USER UserInfo;
SampDiagPrint(DISPLAY_CACHE,
("SAM: AddDisplayAccount : Adding account to user table\n"));
NtStatus = SampInitializeUserInfo(AccountInfo, &UserInfo, TRUE);
if (NT_SUCCESS(NtStatus)) {
//
// Add the account to the normal user table
//
(VOID)RtlInsertElementGenericTable2(
&DisplayInformation->UserTable,
UserInfo,
&NewElement);
if (!NewElement) {
SampDiagPrint(DISPLAY_CACHE_ERRORS,
("SAM: AddDisplayAccount : Account already exists in USER table\n"));
ASSERT(FALSE);
SampFreeUserInfo(UserInfo);
NtStatus = STATUS_INTERNAL_ERROR;
} else {
//
// Now add it to the RID table
//
(VOID)RtlInsertElementGenericTable2(
&DisplayInformation->RidTable,
UserInfo,
&NewElement);
if (!NewElement) {
SampDiagPrint(DISPLAY_CACHE_ERRORS,
("SAM: AddDisplayAccount : Account already exists in RID table\n"));
ASSERT(NewElement);
NtStatus = STATUS_INTERNAL_ERROR;
}
}
}
} else if (MACHINE_ACCOUNT(Control)) {
PDOMAIN_DISPLAY_MACHINE MachineInfo;
SampDiagPrint(DISPLAY_CACHE,
("SAM: AddDisplayAccount : Adding account to machine table\n"));
NtStatus = SampInitializeMachineInfo(AccountInfo, &MachineInfo, TRUE);
if (NT_SUCCESS(NtStatus)) {
//
// Add the account to the machine table
//
(VOID)RtlInsertElementGenericTable2(
&DisplayInformation->MachineTable,
MachineInfo,
&NewElement);
if (!NewElement) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: AddDisplayAccount : Account already exists in MACHINE table\n"));
ASSERT(FALSE);
SampFreeMachineInfo(MachineInfo);
NtStatus = STATUS_INTERNAL_ERROR;
} else {
//
// Now add it to the RID table
//
(VOID)RtlInsertElementGenericTable2(
&DisplayInformation->RidTable,
MachineInfo,
&NewElement);
if (!NewElement) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: AddDisplayAccount : Account already exists in RID table\n"));
ASSERT(NewElement);
NtStatus = STATUS_INTERNAL_ERROR;
}
}
}
} else if (INTERDOMAIN_ACCOUNT(Control)) {
PDOMAIN_DISPLAY_MACHINE InterdomainInfo;
SampDiagPrint(DISPLAY_CACHE,
("SAM: AddDisplayAccount : Adding account to Interdomain table\n"));
NtStatus = SampInitializeMachineInfo(AccountInfo, &InterdomainInfo, TRUE);
if (NT_SUCCESS(NtStatus)) {
//
// Add the account to the Interdomain table
//
(VOID)RtlInsertElementGenericTable2(
&DisplayInformation->InterdomainTable,
InterdomainInfo,
&NewElement);
if (!NewElement) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: AddDisplayAccount : Account already exists in Interdomain table\n"));
ASSERT(FALSE);
SampFreeMachineInfo(InterdomainInfo);
NtStatus = STATUS_INTERNAL_ERROR;
} else {
//
// Now add it to the RID table
//
(VOID)RtlInsertElementGenericTable2(
&DisplayInformation->RidTable,
InterdomainInfo,
&NewElement);
if (!NewElement) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: AddDisplayAccount : Account already exists in RID table\n"));
ASSERT(NewElement);
NtStatus = STATUS_INTERNAL_ERROR;
}
}
}
} else {
//
// This account is not one that we cache - nothing to do
//
SampDiagPrint(DISPLAY_CACHE,
("SAM: AddDisplayAccount : Account is not one that we cache, account control = 0x%lx\n", Control));
NtStatus = STATUS_SUCCESS;
}
}
return(NtStatus);
}
NTSTATUS
SampUpdateDisplayAccount(
PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation,
SAMP_OBJECT_TYPE ObjectType,
PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo
)
/*++
Routine Description:
This routines attempts to update an account in the display cache.
Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN
(ESTABLISHED USING SampSetTransactioDomain()). THIS
SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain()
AND BEFORE SampReleaseWriteLock().
Parameters:
DisplayInformation - Pointer to cached display information
ObjectType - Indicates whether the account is a user account or
group account.
AccountInfo - The new information for this account.
Return Values:
STATUS_SUCCESS - normal, successful completion.
Notes:
The account must be a cached type (MACHINE/USER/GROUP)
--*/
{
NTSTATUS NtStatus;
SampDiagPrint(DISPLAY_CACHE,
("SAM: UpdateDisplayAccount : Updating cached account <%wZ>\n",
&AccountInfo->Name));
#if SAMP_DIAGNOSTICS
{
UNICODE_STRING
SampDiagAccountName;
RtlInitUnicodeString( &SampDiagAccountName, L"SAMP_DIAG" );
if (RtlEqualUnicodeString(&AccountInfo->Name, &SampDiagAccountName, FALSE)) {
SampDisplayDiagnostic();
}
}
#endif //SAMP_DIAGNOSTICS
//
// We should only be called when the cache is valid.
//
switch (ObjectType) {
case SampUserObjectType:
ASSERT(DisplayInformation->UserAndMachineTablesValid);
//
// The account must be one that we cache
//
ASSERT( DISPLAY_ACCOUNT(AccountInfo->AccountControl) );
//
// Go find the account in the appropriate table and update it's fields.
//
if (USER_ACCOUNT(AccountInfo->AccountControl)) {
PDOMAIN_DISPLAY_USER UserInfo;
//
// Allocate space for and initialize the new data
//
NtStatus = SampInitializeUserInfo(AccountInfo, &UserInfo, TRUE);
if (NT_SUCCESS(NtStatus)) {
PDOMAIN_DISPLAY_USER FoundElement;
//
// Search for the account in the user table
//
FoundElement = RtlLookupElementGenericTable2(
&DisplayInformation->UserTable,
UserInfo);
if (FoundElement == NULL) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: UpdateDisplayAccount : Account <%wZ> not found in user table\n", &AccountInfo->Name));
ASSERT(FALSE);
SampFreeUserInfo(UserInfo);
NtStatus = STATUS_INTERNAL_ERROR;
} else {
//
// We found it. Check the old and new match where we expect.
// Can't change either the logon name or RID by this routine.
//
ASSERT(RtlEqualUnicodeString(&FoundElement->LogonName, &UserInfo->LogonName, FALSE));
ASSERT(FoundElement->Rid == UserInfo->Rid);
//
// Free up the existing data in the account element
// (all the strings) and replace it with the new data.
// Don't worry about the index value. It isn't
// valid in the table.
//
SampSwapUserInfo(FoundElement, UserInfo);
SampFreeUserInfo(UserInfo);
}
}
} else if (MACHINE_ACCOUNT(AccountInfo->AccountControl)) {
PDOMAIN_DISPLAY_MACHINE MachineInfo;
//
// Allocate space for and initialize the new data
//
NtStatus = SampInitializeMachineInfo(AccountInfo, &MachineInfo, TRUE);
if (NT_SUCCESS(NtStatus)) {
PDOMAIN_DISPLAY_MACHINE FoundElement;
//
// Search for the account in the user table
//
FoundElement = RtlLookupElementGenericTable2(
&DisplayInformation->MachineTable,
MachineInfo);
if (FoundElement == NULL) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: UpdateDisplayAccount : Account <%wZ> not found in machine table\n", &AccountInfo->Name));
ASSERT(FALSE);
SampFreeMachineInfo(MachineInfo);
NtStatus = STATUS_INTERNAL_ERROR;
} else {
//
// We found it. Check the old and new match where we expect.
// Can't change either the account name or RID by this routine.
//
ASSERT(RtlEqualUnicodeString(&FoundElement->Machine, &MachineInfo->Machine, FALSE));
ASSERT(FoundElement->Rid == MachineInfo->Rid);
//
// Free up the existing data in the account element
// (all the strings) and replace it with the new data.
// Don't worry about the index value. It isn't
// valid in the table.
//
SampSwapMachineInfo(FoundElement, MachineInfo);
SampFreeMachineInfo(MachineInfo);
}
}
} else if (INTERDOMAIN_ACCOUNT(AccountInfo->AccountControl)) {
PDOMAIN_DISPLAY_MACHINE InterdomainInfo;
//
// Allocate space for and initialize the new data
//
NtStatus = SampInitializeMachineInfo(AccountInfo, &InterdomainInfo, TRUE);
if (NT_SUCCESS(NtStatus)) {
PDOMAIN_DISPLAY_MACHINE FoundElement;
//
// Search for the account in the user table
//
FoundElement = RtlLookupElementGenericTable2(
&DisplayInformation->InterdomainTable,
InterdomainInfo);
if (FoundElement == NULL) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: UpdateDisplayAccount : Account <%wZ> not found in Interdomain table\n", &AccountInfo->Name));
ASSERT(FALSE);
SampFreeMachineInfo(InterdomainInfo);
NtStatus = STATUS_INTERNAL_ERROR;
} else {
//
// We found it. Check the old and new match where we expect.
// Can't change either the account name or RID by this routine.
//
ASSERT(RtlEqualUnicodeString(&FoundElement->Machine, &InterdomainInfo->Machine, FALSE));
ASSERT(FoundElement->Rid == InterdomainInfo->Rid);
//
// Free up the existing data in the account element
// (all the strings) and replace it with the new data.
// Don't worry about the index value. It isn't
// valid in the table.
//
SampSwapMachineInfo(FoundElement, InterdomainInfo);
SampFreeMachineInfo(InterdomainInfo);
}
}
}
break; // out of switch
case SampGroupObjectType:
{
PDOMAIN_DISPLAY_GROUP GroupInfo;
ASSERT(DisplayInformation->GroupTableValid);
//
// Allocate space for and initialize the new data
//
NtStatus = SampInitializeGroupInfo(AccountInfo, &GroupInfo, TRUE);
if (NT_SUCCESS(NtStatus)) {
PDOMAIN_DISPLAY_GROUP FoundElement;
//
// Search for the account in the group table
//
FoundElement = RtlLookupElementGenericTable2(
&DisplayInformation->GroupTable,
GroupInfo);
if (FoundElement == NULL) {
SampDiagPrint(DISPLAY_CACHE,
("SAM: UpdateDisplayAccount : Account <%wZ> not found in group table\n", &AccountInfo->Name));
ASSERT(FALSE);
SampFreeGroupInfo(GroupInfo);
NtStatus = STATUS_INTERNAL_ERROR;
} else {
//
// We found it. Check the old and new match where we expect.
// Can't change either the account name or RID by this routine.
//
ASSERT(RtlEqualUnicodeString(&FoundElement->Group, &GroupInfo->Group, FALSE));
ASSERT(FoundElement->Rid == GroupInfo->Rid);
//
// Free up the existing data in the account element
// (all the strings) and replace it with the new data.
// Don't worry about the index value. It isn't
// valid in the table.
//
SampSwapGroupInfo(FoundElement, GroupInfo);
SampFreeGroupInfo(GroupInfo);
}
}
}
break; // out of switch
} // end_switch
return(NtStatus);
}
NTSTATUS
SampTallyTableStatistics (
PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation,
SAMP_OBJECT_TYPE ObjectType
)
/*++
Routine Description:
This routines runs through the cached data tables and totals
up the number of bytes in all elements of each table and stores
in the displayinfo.
Parameters:
DisplayInformation - The display information structure to tally.
ObjectType - Indicates which table(s) to tally.
Return Values:
STATUS_SUCCESS - normal, successful completion.
--*/
{
PVOID Node;
PVOID RestartKey;
switch (ObjectType) {
case SampUserObjectType:
DisplayInformation->TotalBytesInUserTable = 0;
RestartKey = NULL;
for (Node = RtlEnumerateGenericTable2( &DisplayInformation->UserTable,
&RestartKey);
Node != NULL;
Node = RtlEnumerateGenericTable2( &DisplayInformation->UserTable,
&RestartKey)
) {
DisplayInformation->TotalBytesInUserTable +=
SampBytesRequiredUserNode((PDOMAIN_DISPLAY_USER)Node);
}
DisplayInformation->TotalBytesInMachineTable = 0;
RestartKey = NULL;
for (Node = RtlEnumerateGenericTable2( &DisplayInformation->MachineTable,
&RestartKey);
Node != NULL;
Node = RtlEnumerateGenericTable2( &DisplayInformation->MachineTable,
&RestartKey)
) {
DisplayInformation->TotalBytesInMachineTable +=
SampBytesRequiredMachineNode((PDOMAIN_DISPLAY_MACHINE)Node);
}
break; // out of switch
case SampGroupObjectType:
DisplayInformation->TotalBytesInGroupTable = 0;
RestartKey = NULL;
for (Node = RtlEnumerateGenericTable2( &DisplayInformation->GroupTable,
&RestartKey);
Node != NULL;
Node = RtlEnumerateGenericTable2( &DisplayInformation->GroupTable,
&RestartKey)
) {
DisplayInformation->TotalBytesInGroupTable +=
SampBytesRequiredGroupNode((PDOMAIN_DISPLAY_GROUP)Node);
}
break; // out of switch
} // end_switch
return(STATUS_SUCCESS);
}
NTSTATUS
SampEmptyGenericTable2 (
PRTL_GENERIC_TABLE2 Table,
BOOLEAN FreeElements
)
/*++
Routine Description:
This routines deletes all elements in the specified table.
Parameters:
Table - The table whose elements are to be deleted.
FreeElements - Indicates whether or not the element bodies
should also be freed.
Return Values:
STATUS_SUCCESS - normal, successful completion.
--*/
{
BOOLEAN Deleted;
PVOID Element;
ULONG RestartKey;
RestartKey = 0; // Always get the first element
while ((Element = RtlEnumerateGenericTable2( Table, (PVOID *)&RestartKey)) != NULL) {
Deleted = RtlDeleteElementGenericTable2(Table, Element);
ASSERT(Deleted);
if (FreeElements) {
MIDL_user_free( Element );
}
RestartKey = 0;
}
return(STATUS_SUCCESS);
}
NTSTATUS
SampInitializeUserInfo(
PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo,
PDOMAIN_DISPLAY_USER *UserInfo,
BOOLEAN CopyData
)
/*++
Routine Description:
This routines initializes the passed user info structure from the
AccountInfo parameter.
Parameters:
AccountInfo - The account information
UserInfo - Pointer to the pointer to the user structure to initialize.
If CopyData is TRUE, then a pointer to the user structure will be
returned to this argument.
CopyData - FALSE - the UserInfo structure points to the same data as
the AccountInfo structure
TRUE - space for the data is allocated and all data copied
out of the AccountInfo structure into it.
Return Values:
STATUS_SUCCESS - UserInfo initialized successfully.
STATUS_NO_MEMORY - Heap could not be allocated to copy the data.
--*/
{
NTSTATUS
NtStatus;
PDOMAIN_DISPLAY_USER
UI;
if (CopyData) {
(*UserInfo) = MIDL_user_allocate( sizeof(DOMAIN_DISPLAY_USER) );
if ((*UserInfo) == NULL) {
SampDiagPrint(DISPLAY_CACHE_ERRORS,
("SAM: Init User Info: failed to allocate %d bytes\n",
sizeof(DOMAIN_DISPLAY_USER)) );
return(STATUS_NO_MEMORY);
}
}
UI = (*UserInfo);
UI->Rid = AccountInfo->Rid;
UI->AccountControl = AccountInfo->AccountControl;
if (CopyData) {
//
// Set all strings to NULL initially
//
RtlInitUnicodeString(&UI->LogonName, NULL);
RtlInitUnicodeString(&UI->AdminComment, NULL);
RtlInitUnicodeString(&UI->FullName, NULL);
//
// Copy source data into destination
//
NtStatus = SampDuplicateUnicodeString(&UI->LogonName,
&AccountInfo->Name);
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampDuplicateUnicodeString(&UI->AdminComment,
&AccountInfo->Comment);
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampDuplicateUnicodeString(&UI->FullName,
&AccountInfo->FullName);
}
}
//
// Clean up on failure
//
if (!NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE_ERRORS,
("SAM: SampInitializeUserInfo failed, status = 0x%lx\n", NtStatus));
SampFreeUserInfo(UI);
}
} else {
//
// Refer to source data directly
//
UI->LogonName = AccountInfo->Name;
UI->AdminComment = AccountInfo->Comment;
UI->FullName = AccountInfo->FullName;
NtStatus = STATUS_SUCCESS;
}
//
// In the Generic Table, the Index field is used to tag the type
// of account so we can filter enumerations.
//
UI->Index = SAM_USER_ACCOUNT;
return(NtStatus);
}
NTSTATUS
SampInitializeMachineInfo(
PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo,
PDOMAIN_DISPLAY_MACHINE *MachineInfo,
BOOLEAN CopyData
)
/*++
Routine Description:
This routines initializes the passed machine info structure from the
AccountInfo parameter.
Parameters:
AccountInfo - The account information
MachineInfo - Pointer to the pointer to the Machine structure to initialize.
If CopyData is TRUE, then a pointer to the structure will be
returned to this argument.
CopyData - FALSE - the MachineInfo structure points to the same data as
the AccountInfo structure
TRUE - space for the data is allocated and all data copied
out of the AccountInfo structure into it.
Return Values:
STATUS_SUCCESS - UserInfo initialized successfully.
--*/
{
NTSTATUS
NtStatus;
PDOMAIN_DISPLAY_MACHINE
MI;
if (CopyData) {
(*MachineInfo) = MIDL_user_allocate( sizeof(DOMAIN_DISPLAY_MACHINE) );
if ((*MachineInfo) == NULL) {
SampDiagPrint(DISPLAY_CACHE_ERRORS,
("SAM: Init Mach Info: failed to allocate %d bytes\n",
sizeof(DOMAIN_DISPLAY_MACHINE)) );
return(STATUS_NO_MEMORY);
}
}
MI = (*MachineInfo);
MI->Rid = AccountInfo->Rid;
MI->AccountControl = AccountInfo->AccountControl;
if (CopyData) {
//
// Set all strings to NULL initially
//
RtlInitUnicodeString(&MI->Machine, NULL);
RtlInitUnicodeString(&MI->Comment, NULL);
//
// Copy source data into destination
//
NtStatus = SampDuplicateUnicodeString(&MI->Machine,
&AccountInfo->Name);
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampDuplicateUnicodeString(&MI->Comment,
&AccountInfo->Comment);
}
//
// Clean up on failure
//
if (!NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE_ERRORS,
("SAM: SampInitializeMachineInfo failed, status = 0x%lx\n", NtStatus));
SampFreeMachineInfo(MI);
}
} else {
//
// Refer to source data directly
//
MI->Machine = AccountInfo->Name;
MI->Comment = AccountInfo->Comment;
NtStatus = STATUS_SUCCESS;
}
//
// In the Generic Table, the Index field is used to tag the type
// of account so we can filter enumerations.
//
MI->Index = SAM_USER_ACCOUNT;
return(NtStatus);
}
NTSTATUS
SampInitializeGroupInfo(
PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo,
PDOMAIN_DISPLAY_GROUP *GroupInfo,
BOOLEAN CopyData
)
/*++
Routine Description:
This routines initializes the passed Group info structure from the
AccountInfo parameter.
Parameters:
AccountInfo - The account information
GroupInfo - Pointer to the pointer to the Group structure to initialize.
If CopyData is TRUE, then a pointer to the structure will be
returned to this argument.
CopyData - FALSE - the GroupInfo structure points to the same data as
the AccountInfo structure
TRUE - space for the data is allocated and all data copied
out of the AccountInfo structure into it.
Return Values:
STATUS_SUCCESS - GroupInfo initialized successfully.
--*/
{
NTSTATUS
NtStatus;
PDOMAIN_DISPLAY_GROUP
GI;
if (CopyData) {
(*GroupInfo) = MIDL_user_allocate( sizeof(DOMAIN_DISPLAY_GROUP) );
if ((*GroupInfo) == NULL) {
SampDiagPrint(DISPLAY_CACHE_ERRORS,
("SAM: Init Group Info: failed to allocate %d bytes\n",
sizeof(DOMAIN_DISPLAY_GROUP)) );
return(STATUS_NO_MEMORY);
}
}
GI = (*GroupInfo);
GI->Rid = AccountInfo->Rid;
GI->Attributes = AccountInfo->AccountControl;
if (CopyData) {
//
// Set all strings to NULL initially
//
RtlInitUnicodeString(&GI->Group, NULL);
RtlInitUnicodeString(&GI->Comment, NULL);
//
// Copy source data into destination
//
NtStatus = SampDuplicateUnicodeString(&GI->Group,
&AccountInfo->Name);
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampDuplicateUnicodeString(&GI->Comment,
&AccountInfo->Comment);
}
//
// Clean up on failure
//
if (!NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE_ERRORS,
("SAM: SampInitializeGroupInfo failed, status = 0x%lx\n", NtStatus));
SampFreeGroupInfo(GI);
}
} else {
//
// Refer to source data directly
//
GI->Group = AccountInfo->Name;
GI->Comment = AccountInfo->Comment;
NtStatus = STATUS_SUCCESS;
}
//
// In the Generic Table, the Index field is used to tag the type
// of account so we can filter enumerations.
//
GI->Index = SAM_GLOBAL_GROUP_ACCOUNT;
return(NtStatus);
}
NTSTATUS
SampDuplicateUserInfo(
PDOMAIN_DISPLAY_USER Destination,
PDOMAIN_DISPLAY_USER Source,
ULONG Index
)
/*++
Routine Description:
This routine allocates space in the destination and copies over the
data from the source into it.
Parameters:
Destination - The structure to copy data into
Source - The structure containing the data to copy
Index - This value will be placed in the destination's Index
field.
Return Values:
STATUS_SUCCESS - Destination contains a duplicate of the source data.
--*/
{
NTSTATUS NtStatus;
Destination->Index = Index;
Destination->Rid = Source->Rid;
Destination->AccountControl = Source->AccountControl;
//
// Set all strings to NULL initially
//
RtlInitUnicodeString(&Destination->LogonName, NULL);
RtlInitUnicodeString(&Destination->AdminComment, NULL);
RtlInitUnicodeString(&Destination->FullName, NULL);
//
// Copy source data into destination
//
NtStatus = SampDuplicateUnicodeString(&Destination->LogonName,
&Source->LogonName);
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampDuplicateUnicodeString(&Destination->AdminComment,
&Source->AdminComment);
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampDuplicateUnicodeString(&Destination->FullName,
&Source->FullName);
}
}
//
// Clean up on failure
//
if (!NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE_ERRORS,
("SAM: SampDuplicateUserInfo failed, status = 0x%lx\n", NtStatus));
SampFreeUserInfo(Destination);
}
return(NtStatus);
}
NTSTATUS
SampDuplicateMachineInfo(
PDOMAIN_DISPLAY_MACHINE Destination,
PDOMAIN_DISPLAY_MACHINE Source,
ULONG Index
)
/*++
Routine Description:
This routine allocates space in the destination and copies over the
data from the source into it.
Parameters:
Destination - The structure to copy data into
Source - The structure containing the data to copy
Index - This value will be placed in the destination's Index
field.
Return Values:
STATUS_SUCCESS - Destination contains a duplicate of the source data.
--*/
{
NTSTATUS NtStatus;
Destination->Index = Index;
Destination->Rid = Source->Rid;
Destination->AccountControl = Source->AccountControl;
//
// Set all strings to NULL initially
//
RtlInitUnicodeString(&Destination->Machine, NULL);
RtlInitUnicodeString(&Destination->Comment, NULL);
//
// Copy source data into destination
//
NtStatus = SampDuplicateUnicodeString(&Destination->Machine,
&Source->Machine);
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampDuplicateUnicodeString(&Destination->Comment,
&Source->Comment);
}
//
// Clean up on failure
//
if (!NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE_ERRORS,
("SAM: SampDuplicateMachineInfo failed, status = 0x%lx\n", NtStatus));
SampFreeMachineInfo(Destination);
}
return(NtStatus);
}
NTSTATUS
SampDuplicateGroupInfo(
PDOMAIN_DISPLAY_GROUP Destination,
PDOMAIN_DISPLAY_GROUP Source,
ULONG Index
)
/*++
Routine Description:
This routine allocates space in the destination and copies over the
data from the source into it.
Parameters:
Destination - The structure to copy data into
Source - The structure containing the data to copy
Index - This value will be placed in the destination's Index
field.
Return Values:
STATUS_SUCCESS - Destination contains a duplicate of the source data.
--*/
{
NTSTATUS NtStatus;
Destination->Index = Index;
Destination->Rid = Source->Rid;
Destination->Attributes = Source->Attributes;
//
// Set all strings to NULL initially
//
RtlInitUnicodeString(&Destination->Group, NULL);
RtlInitUnicodeString(&Destination->Comment, NULL);
//
// Copy source data into destination
//
NtStatus = SampDuplicateUnicodeString(&Destination->Group,
&Source->Group);
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampDuplicateUnicodeString(&Destination->Comment,
&Source->Comment);
}
//
// Clean up on failure
//
if (!NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE_ERRORS,
("SAM: SampDuplicateGroupInfo failed, status = 0x%lx\n", NtStatus));
SampFreeGroupInfo(Destination);
}
return(NtStatus);
}
NTSTATUS
SampDuplicateOemUserInfo(
PDOMAIN_DISPLAY_OEM_USER Destination,
PDOMAIN_DISPLAY_USER Source,
ULONG Index
)
/*++
Routine Description:
This routine allocates space in the destination and copies over the
data from the source into it.
Parameters:
Destination - The structure to copy data into
Source - The structure containing the data to copy
Index - This value will be placed in the destination's Index
field.
Return Values:
STATUS_SUCCESS - Destination contains a duplicate of a subset of
the source data.
--*/
{
NTSTATUS NtStatus;
Destination->Index = Index;
//
// Set all strings to NULL initially
//
RtlInitString(&Destination->User, NULL);
//
// Copy source data into destination
//
NtStatus = SampUnicodeToOemString(&Destination->User,
&Source->LogonName);
//
// Clean up on failure
//
if (!NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE_ERRORS,
("SAM: SampDuplicateOemUser failed, status = 0x%lx\n", NtStatus));
RtlInitString(&Destination->User, NULL);
}
return(NtStatus);
}
NTSTATUS
SampDuplicateOemGroupInfo(
PDOMAIN_DISPLAY_OEM_GROUP Destination,
PDOMAIN_DISPLAY_GROUP Source,
ULONG Index
)
/*++
Routine Description:
This routine allocates space in the destination and copies over the
data from the source into it.
Parameters:
Destination - The structure to copy data into
Source - The structure containing the data to copy
Index - This value will be placed in the destination's Index
field.
Return Values:
STATUS_SUCCESS - Destination contains a duplicate of a subset of
the source data.
--*/
{
NTSTATUS NtStatus;
Destination->Index = Index;
//
// Set all strings to NULL initially
//
RtlInitString(&Destination->Group, NULL);
//
// Copy source data into destination
//
NtStatus = SampUnicodeToOemString(&Destination->Group,
&Source->Group);
//
// Clean up on failure
//
if (!NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE_ERRORS,
("SAM: SampDuplicateOemGroup failed, status = 0x%lx\n", NtStatus));
RtlInitString(&Destination->Group, NULL);
}
return(NtStatus);
}
VOID
SampSwapUserInfo(
PDOMAIN_DISPLAY_USER Info1,
PDOMAIN_DISPLAY_USER Info2
)
/*++
Routine Description:
Swap the field contents of Info1 and Info2.
Parameters:
Info1 & Info2 - The structures whose contents are to be swapped.
Return Values:
None
--*/
{
DOMAIN_DISPLAY_USER
Tmp;
Tmp.LogonName = Info1->LogonName;
Tmp.AdminComment = Info1->AdminComment;
Tmp.FullName = Info1->FullName;
Tmp.AccountControl = Info1->AccountControl;
Info1->LogonName = Info2->LogonName;
Info1->AdminComment = Info2->AdminComment;
Info1->FullName = Info2->FullName;
Info1->AccountControl = Info2->AccountControl;
Info2->LogonName = Tmp.LogonName;
Info2->AdminComment = Tmp.AdminComment;
Info2->FullName = Tmp.FullName;
Info2->AccountControl = Tmp.AccountControl;
return;
}
VOID
SampSwapMachineInfo(
PDOMAIN_DISPLAY_MACHINE Info1,
PDOMAIN_DISPLAY_MACHINE Info2
)
/*++
Routine Description:
Swap the field contents of Info1 and Info2.
Parameters:
Info1 & Info2 - The structures whose contents are to be swapped.
Return Values:
None
--*/
{
DOMAIN_DISPLAY_MACHINE
Tmp;
Tmp.Machine = Info1->Machine;
Tmp.Comment = Info1->Comment;
Tmp.AccountControl = Info1->AccountControl;
Info1->Machine = Info2->Machine;
Info1->Comment = Info2->Comment;
Info1->AccountControl = Info2->AccountControl;
Info2->Machine = Tmp.Machine;
Info2->Comment = Tmp.Comment;
Info2->AccountControl = Tmp.AccountControl;
return;
}
VOID
SampSwapGroupInfo(
PDOMAIN_DISPLAY_GROUP Info1,
PDOMAIN_DISPLAY_GROUP Info2
)
/*++
Routine Description:
Swap the field contents of Info1 and Info2.
Parameters:
Info1 & Info2 - The structures whose contents are to be swapped.
Return Values:
None
--*/
{
DOMAIN_DISPLAY_GROUP
Tmp;
Tmp.Group = Info1->Group;
Tmp.Comment = Info1->Comment;
Tmp.Attributes = Info1->Attributes;
Info1->Group = Info2->Group;
Info1->Comment = Info2->Comment;
Info1->Attributes = Info2->Attributes;
Info2->Group = Tmp.Group;
Info2->Comment = Tmp.Comment;
Info2->Attributes = Tmp.Attributes;
return;
}
VOID
SampFreeUserInfo(
PDOMAIN_DISPLAY_USER UserInfo
)
/*++
Routine Description:
Frees data associated with a userinfo structure.
Parameters:
UserInfo - User structure to free
Return Values:
None
--*/
{
SampFreeUnicodeString(&UserInfo->LogonName);
SampFreeUnicodeString(&UserInfo->AdminComment);
SampFreeUnicodeString(&UserInfo->FullName);
MIDL_user_free( UserInfo );
return;
}
VOID
SampFreeMachineInfo(
PDOMAIN_DISPLAY_MACHINE MachineInfo
)
/*++
Routine Description:
Frees data associated with a machineinfo structure.
Parameters:
UserInfo - User structure to free
Return Values:
None
--*/
{
SampFreeUnicodeString(&MachineInfo->Machine);
SampFreeUnicodeString(&MachineInfo->Comment);
MIDL_user_free( MachineInfo );
return;
}
VOID
SampFreeGroupInfo(
PDOMAIN_DISPLAY_GROUP GroupInfo
)
/*++
Routine Description:
Frees data associated with a Groupinfo structure.
Parameters:
UserInfo - User structure to free
Return Values:
None
--*/
{
SampFreeUnicodeString(&GroupInfo->Group);
SampFreeUnicodeString(&GroupInfo->Comment);
MIDL_user_free( GroupInfo );
return;
}
VOID
SampFreeOemUserInfo(
PDOMAIN_DISPLAY_OEM_USER UserInfo
)
/*++
Routine Description:
Frees data associated with a UserInfo structure.
Parameters:
UserInfo - User structure to free
Return Values:
None
--*/
{
SampFreeOemString(&UserInfo->User);
MIDL_user_free( UserInfo );
return;
}
VOID
SampFreeOemGroupInfo(
PDOMAIN_DISPLAY_OEM_GROUP GroupInfo
)
/*++
Routine Description:
Frees data associated with a GroupInfo structure.
Parameters:
GroupInfo - Group structure to free
Return Values:
None
--*/
{
SampFreeOemString(&GroupInfo->Group);
MIDL_user_free( GroupInfo );
return;
}
ULONG
SampBytesRequiredUserNode (
PDOMAIN_DISPLAY_USER Node
)
/*++
Routine Description:
This routines returns the total number of bytes required to store all
the elements of the the specified node.
Parameters:
Node - The node whose size we will return.
Return Values:
Bytes required by node
--*/
{
return( sizeof(*Node) +
Node->LogonName.Length +
Node->AdminComment.Length +
Node->FullName.Length
);
}
ULONG
SampBytesRequiredMachineNode (
PDOMAIN_DISPLAY_MACHINE Node
)
/*++
Routine Description:
This routines returns the total number of bytes required to store all
the elements of the the specified node.
Parameters:
Node - The node whose size we will return.
Return Values:
Bytes required by node
--*/
{
return( sizeof(*Node) +
Node->Machine.Length +
Node->Comment.Length
);
}
ULONG
SampBytesRequiredGroupNode (
PDOMAIN_DISPLAY_GROUP Node
)
/*++
Routine Description:
This routines returns the total number of bytes required to store all
the elements of the the specified node.
Parameters:
Node - The node whose size we will return.
Return Values:
Bytes required by node
--*/
{
return( sizeof(*Node) + Node->Group.Length + Node->Comment.Length );
}
ULONG
SampBytesRequiredOemUserNode (
PDOMAIN_DISPLAY_OEM_USER Node
)
/*++
Routine Description:
This routines returns the total number of bytes required to store all
the elements of the the specified node.
Parameters:
Node - The node whose size we will return.
Return Values:
Bytes required by node
--*/
{
return( sizeof(*Node) + Node->User.Length );
}
ULONG
SampBytesRequiredOemGroupNode (
PDOMAIN_DISPLAY_OEM_GROUP Node
)
/*++
Routine Description:
This routines returns the total number of bytes required to store all
the elements of the the specified node.
Parameters:
Node - The node whose size we will return.
Return Values:
Bytes required by node
--*/
{
return( sizeof(*Node) + Node->Group.Length );
}
PVOID
SampGenericTable2Allocate (
CLONG BufferSize
)
/*++
Routine Description:
This routine is used by the generic table2 package to allocate
memory.
Parameters:
BufferSize - the number of bytes needed.
Return Values:
Pointer to the allocated memory
--*/
{
PVOID
Buffer;
Buffer = MIDL_user_allocate(BufferSize);
#if DBG
if (Buffer == NULL) {
SampDiagPrint( DISPLAY_CACHE_ERRORS,
("SAM: GenTab alloc of %d bytes failed.\n",
BufferSize) );
}
#endif //DBG
return(Buffer);
}
VOID
SampGenericTable2Free (
PVOID Buffer
)
/*++
Routine Description:
This routines frees memory previously allocated using
SampGenericTable2Allocate().
Parameters:
Node - the memory to free.
Return Values:
None.
--*/
{
//
// Free up the base structure
//
MIDL_user_free(Buffer);
return;
}
RTL_GENERIC_COMPARE_RESULTS
SampCompareUserNodeByName (
PVOID Node1,
PVOID Node2
)
/*++
Routine Description:
This routines compares account name fields of two user nodes.
Parameters:
Node1, Node2, the nodes to compare
Return Values:
GenericLessThan - Node1 < Node2
GenericGreaterThan - Node1 > Node2
GenericEqual - Node1 == Node2
--*/
{
PUNICODE_STRING
NodeName1,
NodeName2;
LONG
NameComparison;
NodeName1 = &((PDOMAIN_DISPLAY_USER)Node1)->LogonName;
NodeName2 = &((PDOMAIN_DISPLAY_USER)Node2)->LogonName;
//
// Do a case-insensitive comparison of the node names
//
NameComparison = SampCompareDisplayStrings(NodeName1, NodeName2, TRUE);
if (NameComparison > 0) {
return(GenericGreaterThan);
}
if (NameComparison < 0) {
return(GenericLessThan);
}
return(GenericEqual);
}
RTL_GENERIC_COMPARE_RESULTS
SampCompareMachineNodeByName (
PVOID Node1,
PVOID Node2
)
/*++
Routine Description:
This routines compares account name fields of two machine nodes.
Parameters:
Node1, Node2, the nodes to compare
Return Values:
GenericLessThan - Node1 < Node2
GenericGreaterThan - Node1 > Node2
GenericEqual - Node1 == Node2
--*/
{
PUNICODE_STRING
NodeName1,
NodeName2;
LONG
NameComparison;
NodeName1 = &((PDOMAIN_DISPLAY_MACHINE)Node1)->Machine;
NodeName2 = &((PDOMAIN_DISPLAY_MACHINE)Node2)->Machine;
//
// Do a case-insensitive comparison of the node names
//
NameComparison = SampCompareDisplayStrings(NodeName1, NodeName2, TRUE);
if (NameComparison > 0) {
return(GenericGreaterThan);
}
if (NameComparison < 0) {
return(GenericLessThan);
}
return(GenericEqual);
}
RTL_GENERIC_COMPARE_RESULTS
SampCompareGroupNodeByName (
PVOID Node1,
PVOID Node2
)
/*++
Routine Description:
This routines compares account name fields of two group nodes.
Parameters:
Node1, Node2, the nodes to compare
Return Values:
GenericLessThan - Node1 < Node2
GenericGreaterThan - Node1 > Node2
GenericEqual - Node1 == Node2
--*/
{
PUNICODE_STRING
NodeName1,
NodeName2;
LONG
NameComparison;
NodeName1 = &((PDOMAIN_DISPLAY_GROUP)Node1)->Group;
NodeName2 = &((PDOMAIN_DISPLAY_GROUP)Node2)->Group;
//
// Do a case-insensitive comparison of the node names
//
NameComparison = SampCompareDisplayStrings(NodeName1, NodeName2, TRUE);
if (NameComparison > 0) {
return(GenericGreaterThan);
}
if (NameComparison < 0) {
return(GenericLessThan);
}
return(GenericEqual);
}
RTL_GENERIC_COMPARE_RESULTS
SampCompareNodeByRid (
PVOID Node1,
PVOID Node2
)
/*++
Routine Description:
This routines compares the RID of two nodes.
Parameters:
Node1, Node2, the nodes to compare
Return Values:
GenericLessThan - Node1 < Node2
GenericGreaterThan - Node1 > Node2
GenericEqual - Node1 == Node2
--*/
{
PDOMAIN_DISPLAY_USER
N1,
N2;
//
// This routine assumes that all nodes have RIDs in the same
// place, regardless of node type.
//
ASSERT(FIELD_OFFSET(DOMAIN_DISPLAY_USER, Rid) ==
FIELD_OFFSET(DOMAIN_DISPLAY_MACHINE, Rid));
ASSERT(FIELD_OFFSET(DOMAIN_DISPLAY_USER, Rid) ==
FIELD_OFFSET(DOMAIN_DISPLAY_GROUP, Rid));
N1 = Node1;
N2 = Node2;
if (N1->Rid < N2->Rid) {
return(GenericLessThan);
}
if (N1->Rid > N2->Rid) {
return(GenericGreaterThan);
}
return(GenericEqual);
}
LONG
SampCompareDisplayStrings(
IN PUNICODE_STRING String1,
IN PUNICODE_STRING String2,
IN BOOLEAN IgnoreCase
)
/*++
Routine Description:
This routine is a replacement for RtlCompareUnicodeString().
The difference between RtlCompareUnicodeString() and this routine
is that this routine takes into account various customer selected
sort criteria (like, how is "A-MarilF" sorted in comparison to
"Alfred"). This routine uses CompareStringW() for its comparison
function.
Parameters:
String1 - Points to a unicode string to compare.
String2 - Points to a unicode string to compare.
IgnoreCase - indicates whether the comparison is to be case
sensitive (FALSE) or case insensitive (TRUE).
Return Values:
-1 - String1 is lexically less than string 2. That is, String1
preceeds String2 in an ordered list.
0 - String1 and String2 are lexically equivalent.
-1 - String1 is lexically greater than string 2. That is, String1
follows String2 in an ordered list.
--*/
{
INT
CompareResult;
DWORD
Options = 0;
if (IgnoreCase) {
Options = NORM_IGNORECASE;
}
CompareResult = CompareStringW( SampSystemDefaultLCID,
Options,
String1->Buffer,
(String1->Length / sizeof(WCHAR)),
String2->Buffer,
(String2->Length / sizeof(WCHAR))
);
//
// Note that CompareStringW() returns values 1, 2, and 3 for
// string1 less than, equal, or greater than string2 (respectively)
// So, to obtain the RtlCompareUnicodeString() return values of
// -1, 0, and 1 for the same meaning, we simply have to subtract 2.
//
CompareResult -= 2;
//
// CompareStringW has the property that alternate spellings may
// produce strings that compare identically while the rest of SAM
// treats the strings as different. To get around this, if the
// strings are the same we call RtlCompareUnicodeString to make
// sure the strings really are the same.
//
if (CompareResult == 0) {
CompareResult = RtlCompareUnicodeString(
String1,
String2,
IgnoreCase
);
}
return(CompareResult);
}
#if SAMP_DIAGNOSTICS
///////////////////////////////////////////////////////////////////////////////
// //
// Internal diagnostics //
// //
///////////////////////////////////////////////////////////////////////////////
#define SAMP_DISPLAY_DIAG_ENUM_RIDS (0x00000001)
VOID
SampDisplayDiagnosticSuccess(
IN NTSTATUS s,
IN BOOLEAN Eol
)
/*++
Routine Description:
This routine prints "Success" or "Failure" depending upon the
the passed in status value.
If failure, it also prints the status code.
Parameters:
s - the status value.
Eol - if TRUE, causes an end of line to also be printed.
Return Values:
--*/
{
if (NT_SUCCESS(s)) {
SampDiagPrint(DISPLAY_CACHE, ("Success"));
} else {
SampDiagPrint(DISPLAY_CACHE, ("Failure - Status: 0x%lx", s));
}
if (Eol) {
SampDiagPrint(DISPLAY_CACHE, ("\n"));
}
return;
}
VOID
SampDisplayDiagnostic(
VOID
)
/*++
Routine Description:
This routine provides internal diagnostics and test capabilities.
This routine is called whenever an account called SAMP_DIAG is
modified such that the display cache requires updating.
This routine breakpoints, allowing diagnostic parameters to be set.
Parameters:
None.
Return Values:
--*/
{
ULONG
DiagnosticRunCount = 0,
DiagnosticsToRun = 0;
SampDiagPrint(DISPLAY_CACHE,
("SAM: SampDisplayDiagnostic() called.\n"
" Breakpointing to allow diagnostic parameters to be set.\n"
" Diagnostic Flag Word: 0x%lx\n"
" Diagnostic values: \n"
" SamIEnumerateAccountRids: 0x%lx\n"
"\n",
&DiagnosticsToRun,
SAMP_DISPLAY_DIAG_ENUM_RIDS
) );
DbgBreakPoint();
if ((DiagnosticsToRun & SAMP_DISPLAY_DIAG_ENUM_RIDS) != 0) {
SampDisplayDiagEnumRids();
DiagnosticRunCount++;
}
SampDiagPrint(DISPLAY_CACHE,
("SAM: SampDisplayDiagnostic() - %d diagnostics run.\n",
DiagnosticRunCount) );
return;
}
VOID
SampDisplayDiagEnumRids(
VOID
)
/*++
Routine Description:
This routine tests the RID table enumeration api
(SamIEnumerateAccountRids()).
Parameters:
None.
Return Values:
--*/
{
NTSTATUS
NtStatus;
ULONG
i,
ReturnCount,
LastRid;
PULONG
AccountRids;
SAMPR_HANDLE
Server,
Domain;
SampDiagPrint(DISPLAY_CACHE,
("SAM: Testing SamIEnumerateAccountRids...\n"));
NtStatus = SamIConnect( L"", //ServerName
&Server, //ServerHandle
0, //DesiredAccess
TRUE //TrustedClient
);
ASSERT(NT_SUCCESS(NtStatus));
NtStatus = SamrOpenDomain( Server,
0, //DesiredAccess
SampDefinedDomains[1].Sid, //DomainId
&Domain
);
ASSERT(NT_SUCCESS(NtStatus));
///////////////////////////////////////////////////////////////////
// //
// Enumerate both USERs and GLOBAL GROUPs //
// //
///////////////////////////////////////////////////////////////////
SampDiagPrint(DISPLAY_CACHE,
(" Enumerating first three users and global groups...") );
NtStatus = SamIEnumerateAccountRids( Domain,
SAM_USER_ACCOUNT | SAM_GLOBAL_GROUP_ACCOUNT,
0, //StartingRid
3*sizeof(ULONG), //PreferedMaximumLength
&ReturnCount,
&AccountRids
);
SampDisplayDiagnosticSuccess( NtStatus, TRUE );
if (NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE,
(" %d entries returned.\n", ReturnCount));
if (ReturnCount > 0) {
ASSERT(AccountRids != NULL);
for (i=0; i<ReturnCount; i++) {
SampDiagPrint(DISPLAY_CACHE,
(" 0x%lx\n", AccountRids[i]));
}
LastRid = AccountRids[ReturnCount-1];
MIDL_user_free(AccountRids);
} else {
ASSERT(AccountRids == NULL);
}
}
//
// Now try to continue for another 100 accounts
//
SampDiagPrint(DISPLAY_CACHE,
(" Enumerating next 100 users and global groups...") );
NtStatus = SamIEnumerateAccountRids( Domain,
SAM_USER_ACCOUNT | SAM_GLOBAL_GROUP_ACCOUNT,
LastRid, //StartingRid
100*sizeof(ULONG), //PreferedMaximumLength
&ReturnCount,
&AccountRids
);
SampDisplayDiagnosticSuccess( NtStatus, TRUE );
if (NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE,
(" %d entries returned.\n", ReturnCount));
if (ReturnCount > 0) {
ASSERT(AccountRids != NULL);
for (i=0; i<ReturnCount; i++) {
SampDiagPrint(DISPLAY_CACHE,
(" 0x%lx\n", AccountRids[i]));
}
LastRid = AccountRids[ReturnCount-1];
MIDL_user_free(AccountRids);
} else {
ASSERT(AccountRids == NULL);
}
}
//
// Now try to continue for another 4000 accounts
//
if (NtStatus == STATUS_MORE_ENTRIES) {
SampDiagPrint(DISPLAY_CACHE,
(" Enumerating next 4000 users and global groups...") );
NtStatus = SamIEnumerateAccountRids( Domain,
SAM_USER_ACCOUNT | SAM_GLOBAL_GROUP_ACCOUNT,
LastRid, //StartingRid
400*sizeof(ULONG), //PreferedMaximumLength
&ReturnCount,
&AccountRids
);
SampDisplayDiagnosticSuccess( NtStatus, TRUE );
if (NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE,
(" %d entries returned.\n", ReturnCount));
if (ReturnCount > 0) {
ASSERT(AccountRids != NULL);
i=0;
if (ReturnCount > 8) {
for (i=0; i<ReturnCount-8; i=i+8) {
SampDiagPrint(DISPLAY_CACHE,
(" 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n",
AccountRids[i+0], AccountRids[i+1],
AccountRids[i+2], AccountRids[i+3],
AccountRids[i+4], AccountRids[i+5],
AccountRids[i+6], AccountRids[i+7] ));
}
}
for (i=i; i<ReturnCount; i++) {
SampDiagPrint(DISPLAY_CACHE,
(" 0x%lx ", AccountRids[i]));
}
SampDiagPrint(DISPLAY_CACHE, ("\n"));
LastRid = AccountRids[i];
MIDL_user_free(AccountRids);
} else {
ASSERT(AccountRids == NULL);
}
}
}
///////////////////////////////////////////////////////////////////
// //
// Now try just USER accounts //
// //
///////////////////////////////////////////////////////////////////
SampDiagPrint(DISPLAY_CACHE,
(" Enumerating first three users ...") );
NtStatus = SamIEnumerateAccountRids( Domain,
SAM_USER_ACCOUNT,
0, //StartingRid
3*sizeof(ULONG), //PreferedMaximumLength
&ReturnCount,
&AccountRids
);
SampDisplayDiagnosticSuccess( NtStatus, TRUE );
if (NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE,
(" %d entries returned.\n", ReturnCount));
if (ReturnCount > 0) {
ASSERT(AccountRids != NULL);
for (i=0; i<ReturnCount; i++) {
SampDiagPrint(DISPLAY_CACHE,
(" 0x%lx\n", AccountRids[i]));
}
LastRid = AccountRids[ReturnCount-1];
MIDL_user_free(AccountRids);
} else {
ASSERT(AccountRids == NULL);
}
}
//
// Now try to continue for another 100 accounts
//
SampDiagPrint(DISPLAY_CACHE,
(" Enumerating next 100 users...") );
NtStatus = SamIEnumerateAccountRids( Domain,
SAM_USER_ACCOUNT,
LastRid, //StartingRid
100*sizeof(ULONG), //PreferedMaximumLength
&ReturnCount,
&AccountRids
);
SampDisplayDiagnosticSuccess( NtStatus, TRUE );
if (NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE,
(" %d entries returned.\n", ReturnCount));
if (ReturnCount > 0) {
ASSERT(AccountRids != NULL);
for (i=0; i<ReturnCount; i++) {
SampDiagPrint(DISPLAY_CACHE,
(" 0x%lx\n", AccountRids[i]));
}
LastRid = AccountRids[ReturnCount-1];
MIDL_user_free(AccountRids);
} else {
ASSERT(AccountRids == NULL);
}
}
//
// Now try to continue for another 4000 accounts
//
if (NtStatus == STATUS_MORE_ENTRIES) {
SampDiagPrint(DISPLAY_CACHE,
(" Enumerating next 4000 users...") );
NtStatus = SamIEnumerateAccountRids( Domain,
SAM_USER_ACCOUNT,
LastRid, //StartingRid
400*sizeof(ULONG), //PreferedMaximumLength
&ReturnCount,
&AccountRids
);
SampDisplayDiagnosticSuccess( NtStatus, TRUE );
if (NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE,
(" %d entries returned.\n", ReturnCount));
if (ReturnCount > 0) {
ASSERT(AccountRids != NULL);
i=0;
if (ReturnCount > 8) {
for (i=0; i<ReturnCount-8; i=i+8) {
SampDiagPrint(DISPLAY_CACHE,
(" 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n",
AccountRids[i+0], AccountRids[i+1],
AccountRids[i+2], AccountRids[i+3],
AccountRids[i+4], AccountRids[i+5],
AccountRids[i+6], AccountRids[i+7] ));
}
}
for (i=i; i<ReturnCount; i++) {
SampDiagPrint(DISPLAY_CACHE,
(" 0x%lx ", AccountRids[i]));
}
SampDiagPrint(DISPLAY_CACHE, ("\n"));
LastRid = AccountRids[i];
MIDL_user_free(AccountRids);
} else {
ASSERT(AccountRids == NULL);
}
}
}
///////////////////////////////////////////////////////////////////
// //
// Now just try GLOBAL GROUPs //
// //
///////////////////////////////////////////////////////////////////
SampDiagPrint(DISPLAY_CACHE,
(" Enumerating first three global groups...") );
NtStatus = SamIEnumerateAccountRids( Domain,
SAM_GLOBAL_GROUP_ACCOUNT,
0, //StartingRid
3*sizeof(ULONG), //PreferedMaximumLength
&ReturnCount,
&AccountRids
);
SampDisplayDiagnosticSuccess( NtStatus, TRUE );
if (NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE,
(" %d entries returned.\n", ReturnCount));
if (ReturnCount > 0) {
ASSERT(AccountRids != NULL);
for (i=0; i<ReturnCount; i++) {
SampDiagPrint(DISPLAY_CACHE,
(" 0x%lx\n", AccountRids[i]));
}
LastRid = AccountRids[ReturnCount-1];
MIDL_user_free(AccountRids);
} else {
ASSERT(AccountRids == NULL);
}
}
//
// Now try to continue for another 100 accounts
//
SampDiagPrint(DISPLAY_CACHE,
(" Enumerating next 100 global groups...") );
NtStatus = SamIEnumerateAccountRids( Domain,
SAM_GLOBAL_GROUP_ACCOUNT,
LastRid, //StartingRid
100*sizeof(ULONG), //PreferedMaximumLength
&ReturnCount,
&AccountRids
);
SampDisplayDiagnosticSuccess( NtStatus, TRUE );
if (NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE,
(" %d entries returned.\n", ReturnCount));
if (ReturnCount > 0) {
ASSERT(AccountRids != NULL);
for (i=0; i<ReturnCount; i++) {
SampDiagPrint(DISPLAY_CACHE,
(" 0x%lx\n", AccountRids[i]));
}
LastRid = AccountRids[ReturnCount-1];
MIDL_user_free(AccountRids);
} else {
ASSERT(AccountRids == NULL);
}
}
//
// Now try to continue for another 4000 accounts
//
if (NtStatus == STATUS_MORE_ENTRIES) {
SampDiagPrint(DISPLAY_CACHE,
(" Enumerating next 4000 global groups...") );
NtStatus = SamIEnumerateAccountRids( Domain,
SAM_GLOBAL_GROUP_ACCOUNT,
LastRid, //StartingRid
4000*sizeof(ULONG), //PreferedMaximumLength
&ReturnCount,
&AccountRids
);
SampDisplayDiagnosticSuccess( NtStatus, TRUE );
if (NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE,
(" %d entries returned.\n", ReturnCount));
if (ReturnCount > 0) {
ASSERT(AccountRids != NULL);
i=0;
if (ReturnCount > 8) {
for (i=0; i<ReturnCount-8; i=i+8) {
SampDiagPrint(DISPLAY_CACHE,
(" 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n",
AccountRids[i+0], AccountRids[i+1],
AccountRids[i+2], AccountRids[i+3],
AccountRids[i+4], AccountRids[i+5],
AccountRids[i+6], AccountRids[i+7] ));
}
}
for (i=i; i<ReturnCount; i++) {
SampDiagPrint(DISPLAY_CACHE,
(" 0x%lx ", AccountRids[i]));
}
SampDiagPrint(DISPLAY_CACHE, ("\n"));
LastRid = AccountRids[i];
MIDL_user_free(AccountRids);
} else {
ASSERT(AccountRids == NULL);
}
}
}
//
// Now try to continue an enumeration from the very last RID.
// At one point in time, this use to restart the enumeration
// (which was not correct behaviour). It should indicate that
// there are no more entries.
//
SampDiagPrint(DISPLAY_CACHE,
(" Enumerating next 5 global groups.."
" (should be none to enumerate) ...") );
NtStatus = SamIEnumerateAccountRids( Domain,
SAM_GLOBAL_GROUP_ACCOUNT,
LastRid, //StartingRid
5*sizeof(ULONG), //PreferedMaximumLength
&ReturnCount,
&AccountRids
);
SampDisplayDiagnosticSuccess( NtStatus, TRUE );
if (NT_SUCCESS(NtStatus)) {
SampDiagPrint(DISPLAY_CACHE,
(" %d entries returned.\n", ReturnCount));
if (ReturnCount > 0) {
SampDiagPrint(DISPLAY_CACHE,
(" ERROR - there should be no RIDs returned ! !\n"));
ASSERT(AccountRids != NULL);
i=0;
if (ReturnCount > 8) {
for (i=0; i<ReturnCount-8; i=i+8) {
SampDiagPrint(DISPLAY_CACHE,
(" 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n",
AccountRids[i+0], AccountRids[i+1],
AccountRids[i+2], AccountRids[i+3],
AccountRids[i+4], AccountRids[i+5],
AccountRids[i+6], AccountRids[i+7] ));
}
}
for (i=i; i<ReturnCount; i++) {
SampDiagPrint(DISPLAY_CACHE,
(" 0x%lx ", AccountRids[i]));
}
SampDiagPrint(DISPLAY_CACHE, ("\n"));
LastRid = AccountRids[i];
MIDL_user_free(AccountRids);
} else {
ASSERT(AccountRids == NULL);
}
}
///////////////////////////////////////////////////////////////////
// //
// Hmmm, can't close handles because it will conflict //
// with the rxact state we already have going. Bummer. //
// //
///////////////////////////////////////////////////////////////////
//NtStatus = SamrCloseHandle( &Domain ); ASSERT(NT_SUCCESS(NtStatus));
//NtStatus = SamrCloseHandle( &Server ); ASSERT(NT_SUCCESS(NtStatus));
return;
}
#endif //SAMP_DIAGNOSTICS