/*++ 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 /////////////////////////////////////////////////////////////////////////////// // // // 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 0) { ASSERT(AccountRids != NULL); for (i=0; i 0) { ASSERT(AccountRids != NULL); i=0; if (ReturnCount > 8) { for (i=0; i 0) { ASSERT(AccountRids != NULL); for (i=0; i 0) { ASSERT(AccountRids != NULL); for (i=0; i 0) { ASSERT(AccountRids != NULL); i=0; if (ReturnCount > 8) { for (i=0; i 0) { ASSERT(AccountRids != NULL); for (i=0; i 0) { ASSERT(AccountRids != NULL); for (i=0; i 0) { ASSERT(AccountRids != NULL); i=0; if (ReturnCount > 8) { for (i=0; i 0) { SampDiagPrint(DISPLAY_CACHE, (" ERROR - there should be no RIDs returned ! !\n")); ASSERT(AccountRids != NULL); i=0; if (ReturnCount > 8) { for (i=0; i