/*++ Copyright (c) 1990 Microsoft Corporation Module Name: almember.c Abstract: This file contains utilities related to membership of aliases. Alternative design Author: Scott Birrell 01-Apr-1993 Environment: User Mode - Win32 Revision History: --*/ /////////////////////////////////////////////////////////////////////////////// // // // Includes // // // /////////////////////////////////////////////////////////////////////////////// #include #define SAMP_AL_FREE_OLD_LIST ((ULONG) 0x00000001L) #define SAMP_AL_ERROR_IF_MEMBER ((ULONG) 0x00000002L) #define SAMP_AL_ERROR_IF_NOT_MEMBER ((ULONG) 0x00000004L) #define SAMP_AL_ASSIGN_NEW_REFERENCES ((ULONG) 0x00000008L) #define SAMP_AL_LOOKUP_BY_SID ((ULONG) 0x00000010L) #define SAMP_AL_LOOKUP_BY_REFERENCE ((ULONG) 0x00000020L) #define SAMP_AL_VERIFY_NO_ALIASES_IN_ACCOUNT ((ULONG) 0x00000040L) #define SAMP_AL_VERIFY_ALL_ALIASES_IN_ACCOUNT ((ULONG) 0x00000080L) #define SAMP_AL_VERIFY_NO_MEMBERS_IN_ALIAS ((ULONG) 0x00000100L) #define SAMP_AL_VERIFY_ALL_MEMBERS_IN_ALIAS ((ULONG) 0x00000200L) #define SAMP_UNKNOWN_INDEX ((ULONG) 0xffffffffL) #define SAMP_AL_ALIAS_LIST_DELTA ((ULONG) 0x00000100L) #define SAMP_AL_ALIAS_DELTA ((ULONG) 0x00000040L) #define SAMP_AL_REFERENCED_DOMAIN_LIST_DELTA ((ULONG) 0x00000100L) #define SAMP_AL_INITIAL_MEMBER_ALIAS_LIST_LENGTH ((ULONG) 0x00001000L) #define SAMP_AL_MEMBER_ALIAS_LIST_DELTA ((ULONG) 0x00001000L) #define SAMP_AL_INITIAL_REFERENCED_DOMAIN_LIST_LENGTH ((ULONG) 0x00000400L) #define SAMP_AL_INITIAL_MEMBER_DOMAIN_LENGTH ((ULONG) 0x00000040L) #define SAMP_AL_INITIAL_MEMBER_ACCOUNT_ALIAS_CAPACITY ((ULONG) 0x00000004L) #define SAMP_AL_ENUM_PREFERRED_LENGTH ((ULONG) 0x00001000L) #define SAMP_AL_INITIAL_MEMBERSHIP_COUNT ((ULONG) 0x0000000aL) #define SAMP_AL_MEMBERSHIP_COUNT_DELTA ((ULONG) 0x0000000aL) #define SAMP_AL_MEMBER_ALIAS_LIST_SIGNATURE ((ULONG) 0x53494c41) #define SAMP_AL_MEMBER_DOMAIN_SIGNATURE ((ULONG) 0x4d4f444d) #define SAMP_AL_MEMBER_ACCOUNT_SIGNATURE ((ULONG) 0x4343414d) #define SAMP_AL_DR_ALIAS_LIST_KEY_NAME L"Aliases\\Members\\AliasList" #define SAMP_AL_DR_REFERENCED_DOMAIN_LIST_KEY_NAME \ L"Aliases\\Members\\ReferencedDomainList" ///////////////////////////////////////////////////////////////////////////// // // // Private macro functions // // // ///////////////////////////////////////////////////////////////////////////// #define SampAlFirstMemberDomain( MemberAliasList ) \ (MemberAliasList->MemberDomains) #define SampAlOffsetFirstMemberDomain( MemberAliasList ) \ (((PUCHAR) SampAlFirstMemberDomain(MemberAliasList)) - ((PUCHAR) MemberAliasList)) #define SampAlFirstMemberAccount( MemberDomain ) \ ((PSAMP_AL_MEMBER_ACCOUNT) \ (((PUCHAR) &((MemberDomain)->DomainSid)) + RtlLengthSid(&((MemberDomain)->DomainSid)))) #define SampAlOffsetFirstMemberAccount( MemberDomain ) \ (((PUCHAR) SampAlFirstMemberAccount(MemberDomain)) - ((PUCHAR) MemberDomain)) #define SampAlNextMemberAccount( MemberAccount ) \ ((PSAMP_AL_MEMBER_ACCOUNT)(((PUCHAR) MemberAccount) + (MemberAccount)->MaximumLength)) #define SampAlOffsetFirstAlias( OutputMemberAccount ) \ ((ULONG) FIELD_OFFSET(SAMP_AL_MEMBER_ACCOUNT, AliasRids)) #define SampAlNextMemberDomain( MemberDomain ) \ ((PSAMP_AL_MEMBER_DOMAIN)(((PUCHAR) MemberDomain) + (MemberDomain)->MaximumLength)) #define SampAlNextNewAliasInMemberAccount( MemberAccount ) \ ((PULONG)(((PUCHAR) MemberAccount) + (MemberAccount)->UsedLength)) #define SampAlNextNewMemberAccount( MemberDomain ) \ ((PSAMP_AL_MEMBER_ACCOUNT)(((PUCHAR) MemberDomain) + (MemberDomain)->UsedLength)) #define SampAlNextNewMemberDomain( MemberAliasList ) \ ((PSAMP_AL_MEMBER_DOMAIN)(((PUCHAR) MemberAliasList) + (MemberAliasList)->UsedLength)) #define SampAlInfoIsValid(DomainIndex) \ ((SampDefinedDomains[DomainIndex].AliasInformation.Valid) || \ (SampServiceState == SampServiceInitializing )) #define SampAlInfoMakeValid(DomainIndex) \ (SampDefinedDomains[DomainIndex].AliasInformation.Valid = TRUE) #define SampAlInfoMakeInvalid(DomainIndex) \ (SampDefinedDomains[DomainIndex].AliasInformation.Valid = FALSE) #define SampAlDomainIndexToMemberAliasList( DomainIndex ) \ ((PSAMP_AL_MEMBER_ALIAS_LIST) \ SampDefinedDomains[ DomainIndex].AliasInformation.MemberAliasList) #define SampAlDomainHandleToMemberAliasList( DomainHandle ) \ (SampAlDomainIndexToMemberAliasList(((PSAMP_OBJECT) DomainHandle)->DomainIndex)) #define SampAlAliasHandleToMemberAliasList( AliasHandle ) \ (SampAlDomainIndexToMemberAliasList(((PSAMP_OBJECT) AliasHandle)->DomainIndex)) #define SampAlMemberDomainToOffset( MemberAliasList, MemberDomain) \ (((PUCHAR) MemberDomain) - ((PUCHAR) MemberAliasList)) #define SampAlMemberDomainFromOffset( MemberDomain, MemberDomainOffset) \ ((PSAMP_AL_MEMBER_DOMAIN)(((PUCHAR) MemberDomain) + MemberDomainOffset)) #define SampAlMemberAccountToOffset( MemberDomain, MemberAccount) \ (((PUCHAR) MemberAccount) - ((PUCHAR) MemberDomain)) #define SampAlMemberAccountFromOffset( MemberDomain, MemberAccountOffset) \ ((PSAMP_AL_MEMBER_ACCOUNT)(((PUCHAR) MemberDomain) + MemberAccountOffset)) #define SampAlLengthRequiredMemberAccount( AliasCapacity ) \ (sizeof(SAMP_AL_MEMBER_ACCOUNT) + ((AliasCapacity - 1) * sizeof(ULONG))) #define SampAlUpdateMemberAliasList( AliasHandle, MemberAliasList ) \ { \ PSAMP_OBJECT InternalAliasHandle = (PSAMP_OBJECT) AliasHandle; \ SampDefinedDomains[InternalAliasHandle->DomainIndex].AliasInformation.MemberAliasList \ = MemberAliasList; \ } ///////////////////////////////////////////////////////////////////////////// // // // Private Datatypes // // // ///////////////////////////////////////////////////////////////////////////// // This datatype is not currently used. It may be used if Alias information // is every stored to Registry Keys. // typedef enum _SAMP_AL_LIST_TYPE { SampAlMemberAliasList = 1 } SAMP_AL_LIST_TYPE, *PSAMP_AL_LIST_TYPE; ///////////////////////////////////////////////////////////////////////////// // // // Private Static Data // // // ///////////////////////////////////////////////////////////////////////////// UNICODE_STRING SampAlDrMemberAliasListKeyName; BOOLEAN SampAlEnableBuildingOfList[SAMP_DEFINED_DOMAINS_COUNT] = { TRUE, TRUE }; ///////////////////////////////////////////////////////////////////////////// // // // Prototypes of functions private to this module // // // ///////////////////////////////////////////////////////////////////////////// NTSTATUS SampAlCreateMemberAliasList( IN LONG DomainIndex, IN ULONG InitialMemberAliasListLength, OUT OPTIONAL PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList ); NTSTATUS SampAlGrowMemberAliasList( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN ULONG ExtraSpaceRequired ); NTSTATUS SampAlBuildMemberAliasList( IN LONG DomainIndex ); NTSTATUS SampAlCreateMemberDomain( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSID DomainSid, OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain ); NTSTATUS SampAlAllocateMemberDomain( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN ULONG MaximumLengthMemberDomain, OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain ); NTSTATUS SampAlGrowMemberDomain( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, IN ULONG ExtraSpaceRequired ); NTSTATUS SampAlDeleteMemberDomain( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSAMP_AL_MEMBER_DOMAIN MemberDomain ); NTSTATUS SampAlLookupMemberDomain( IN PSAMP_AL_MEMBER_ALIAS_LIST MemberAliasList, IN PSID DomainSid, OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain ); NTSTATUS SampAlCreateMemberAccount( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, IN ULONG Rid, IN ULONG AliasCapacity, OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount ); NTSTATUS SampAlAllocateMemberAccount( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, IN ULONG MaximumLengthMemberAccount, OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount ); NTSTATUS SampAlGrowMemberAccount( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, IN OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount, IN ULONG ExtraSpaceRequired ); NTSTATUS SampAlDeleteMemberAccount( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, IN OUT PSAMP_AL_MEMBER_ACCOUNT MemberAccount, OUT PBOOLEAN MemberDomainDeleted ); NTSTATUS SampAlLookupMemberAccount( IN PSAMP_AL_MEMBER_DOMAIN MemberDomain, IN ULONG MemberRid, OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount ); NTSTATUS SampAlAddAliasesToMemberAccount( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, IN OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount, IN ULONG Options, IN PSAMPR_ULONG_ARRAY AliasRids ); NTSTATUS SampAlRemoveAliasesFromMemberAccount( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, IN OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount, IN ULONG Options, IN PSAMPR_ULONG_ARRAY AliasRids, OUT PBOOLEAN MemberDomainDeleted, OUT PBOOLEAN MemberAccountDeleted ); NTSTATUS SampAlLookupAliasesInMemberAccount( IN PSAMP_AL_MEMBER_ACCOUNT MemberAccount, IN PSAMPR_ULONG_ARRAY AliasRids, OUT PULONG ExistingAliasCount ); NTSTATUS SampAlSplitMemberSids( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN ULONG Options, IN PSAMPR_PSID_ARRAY MemberSids, OUT PSAMP_AL_SPLIT_MEMBER_SID_LIST SplitMemberSids ); BOOLEAN SampAlInfoIsValidForDomain( IN SAMPR_HANDLE DomainHandle ); BOOLEAN SampAlInfoIsValidForAlias( IN SAMPR_HANDLE AliasHandle ); ////////////////////////////////////////////////////////////////////////////// // // // Code of Exported Routines // // // ////////////////////////////////////////////////////////////////////////////// NTSTATUS SamrGetAliasMembership( IN SAMPR_HANDLE DomainHandle, IN PSAMPR_PSID_ARRAY SidArray, OUT PSAMPR_ULONG_ARRAY Membership ) /*++ Routine Description: This API searches the set of aliases in the specified domain to see which aliases, if any, the passed SIDs are members of. Any aliases that any of the SIDs are found to be members of are returned. Note that any particular alias will appear only once in the returned list. Parameters: DomainHandle - Handle from a SamOpenDomain call. PassedCount - Specifies the number of Sids being passed. Sids - Pointer to an arrray of Count pointers to Sids whose alias memberships are to be looked up. Membership - receives the array of rids rerpresenting the aliases in this domain that any of the sid(s) are members of. Return Values: STATUS_SUCCESS - The combined alias membership is in Membership STATUS_INVALID_SID - One of the passed sids was invalid --*/ { NTSTATUS NtStatus, IgnoreStatus; PSAMP_OBJECT DomainContext; SAMP_OBJECT_TYPE FoundType; ULONG i; ULONG SidCount; PSID *Sids; BOOLEAN ObjectReferenced = FALSE; ASSERT(Membership != NULL); ASSERT(Membership->Element == NULL); SidCount = SidArray->Count; Sids = (PSID *)(SidArray->Sids); // // Grab the lock // SampAcquireReadLock(); // // Validate type of, and access to object. // DomainContext = (PSAMP_OBJECT)DomainHandle; NtStatus = SampLookupContext( DomainContext, DOMAIN_LOOKUP, SampDomainObjectType, &FoundType ); if (!NT_SUCCESS(NtStatus)) { goto GetAliasMembershipError; } ObjectReferenced = TRUE; // // Validate the Sids. if any are invalid, return an error. // for (i=0; i < SidCount; i++) { // // Check for valid sid // if ( (Sids[i] == NULL) || !RtlValidSid(Sids[i]) ) { NtStatus = STATUS_INVALID_SID; break; } } if (!NT_SUCCESS(NtStatus)) { goto GetAliasMembershipError; } // // If the in-memory Alias Membership information for this domain is valid, // use it to retrieve the Alias members. // if (SampAlInfoIsValidForDomain(DomainHandle)) { NtStatus = SampAlQueryAliasMembership( DomainHandle, SidArray, Membership ); } else { NtStatus = SampAlSlowQueryAliasMembership( DomainHandle, SidArray, Membership ); } GetAliasMembershipFinish: // // If necessary, dereference the SAM server object. // if (ObjectReferenced) { IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); } // // Free the read lock // SampReleaseReadLock(); return(NtStatus); GetAliasMembershipError: goto GetAliasMembershipFinish; } NTSTATUS SampAlQueryAliasMembership( IN SAMPR_HANDLE DomainHandle, IN PSAMPR_PSID_ARRAY SidArray, OUT PSAMPR_ULONG_ARRAY Membership ) /*++ Routine Description: This function is one of two worker routines for the SamrGetAliasMembership API. This worker uses the Member Alias List to determine which aliases, if any, the passed SIDs are members of. Any aliases that any of the SIDs are found to be members of are returned. Note that any particular alias will appear only once in the returned list. See also SampAlSlowQueryAliasMembership() WARNING: The SAM Read Lock must be held while this function executes. Parameters: DomainHandle - Handle from a SamrOpenDomain call. SidArray - Pointer to a counted array of pointers to Sids whose alias memberships are to be looked up. Membership - Receives the array of rids rerpresenting the aliases in this domain that any of the sid(s) are members of. Return Values: STATUS_SUCCESS - The combined alias membership is in Membership STATUS_INVALID_SID - One of the passed sids was invalid --*/ { NTSTATUS Status = STATUS_SUCCESS; PSAMP_AL_MEMBER_ALIAS_LIST MemberAliasList = NULL; PSAMP_AL_MEMBER_DOMAIN MemberDomain = NULL; ULONG Rid, AliasRid; ULONG AliasIndex, SidIndex; PSAMP_AL_MEMBER_ACCOUNT MemberAccount = NULL; BOOLEAN AliasAlreadyFound; ULONG AliasFoundIndex, MembershipMaximumCount; PSID DomainSid = NULL; PSID Sid = NULL; PULONG NewMembership = NULL; Membership->Count = 0; Membership->Element = NULL; // // Obtain pointer to Alias Member List. // MemberAliasList = SampAlDomainHandleToMemberAliasList( DomainHandle ); // // If there are no Member Domains in this Member Alias List, then just // finish. // if (MemberAliasList->DomainCount == 0) { goto QueryAliasMembershipFinish; } // // Allocate Scratch Sid buffer. We will use this same buffer for splitting // each Sid. // DomainSid = MIDL_user_allocate( RtlLengthRequiredSid( 256 )); Status = STATUS_NO_MEMORY; if (DomainSid == NULL) { goto QueryAliasMembershipError; } Status = STATUS_SUCCESS; // // Allocate output array with a nominal initial size. Reallocate it // as necessary // MembershipMaximumCount = SAMP_AL_INITIAL_MEMBERSHIP_COUNT; Membership->Element = MIDL_user_allocate( MembershipMaximumCount * sizeof(ULONG)); Status = STATUS_NO_MEMORY; if (Membership->Element == NULL) { goto QueryAliasMembershipError; } Status = STATUS_SUCCESS; // // Now query the membership of the array of split Sids. For each // Sid, we skip the Sid if it has an unknown MemberDomain, because // it does not belong to any aliases. For each surviving Sid, we scan the // Alias List, skipping entries for aliases we've already entered in the // output list. We search for the Rid only in the section of the // Alias List pertinent to the Sid's domain. // for (SidIndex = 0; SidIndex < SidArray->Count; SidIndex++) { Sid = SidArray->Sids[ SidIndex ].SidPointer; // // Split this Sid into a DomainSid and a Rid. Note that we re-use // the buffer containing the Domain Sid for the next Sid. // Status = SampSplitSid( Sid, &DomainSid, &Rid); if (!NT_SUCCESS(Status)) { break; } // // Search the Member Alias List for the Sid's Member Domain // (if any). // Status = SampAlLookupMemberDomain( MemberAliasList, DomainSid, &MemberDomain ); if (!NT_SUCCESS(Status)) { // // The only expected error is STATUS_NO_SUCH_DOMAIN. If we // don't get this error, fail the request. Otherwise, the // Sid is not a member of any aliases in the SAM local domain, so // just skip to the next Sid. // if (Status != STATUS_NO_SUCH_DOMAIN) { break; } Status = STATUS_SUCCESS; continue; } // // We've found the Member Domain. Now find the Member Account. // Status = SampAlLookupMemberAccount( MemberDomain, Rid, &MemberAccount ); if (!NT_SUCCESS(Status)) { // // The only expected error is STATUS_NO_SUCH_MEMBER. If we // don't get this error, fail the request. Otherwise, the // Sid is not a member of any aliases in the domain, so just // skip to the next Sid. // if (Status != STATUS_NO_SUCH_MEMBER) { break; } Status = STATUS_SUCCESS; continue; } // // We've found the Member Account. For each of the aliases our Sid // belongs to, add the alias to the output list if not already there. // for (AliasIndex = 0; AliasIndex < MemberAccount->AliasCount; AliasIndex++) { AliasRid = MemberAccount->AliasRids[AliasIndex]; AliasAlreadyFound = FALSE; for (AliasFoundIndex = 0; AliasFoundIndex < Membership->Count; AliasFoundIndex++) { if (AliasRid == Membership->Element[AliasFoundIndex]) { AliasAlreadyFound = TRUE; break; } } if (!AliasAlreadyFound) { // // If there isn't enough room in the output Membership // array, reallocate it. // if (Membership->Count == MembershipMaximumCount) { MembershipMaximumCount += SAMP_AL_MEMBERSHIP_COUNT_DELTA; NewMembership = MIDL_user_allocate( MembershipMaximumCount * sizeof(ULONG) ); Status = STATUS_NO_MEMORY; if (NewMembership == NULL) { break; } Status = STATUS_SUCCESS; RtlMoveMemory( NewMembership, Membership->Element, Membership->Count * sizeof(ULONG) ); MIDL_user_free( Membership->Element); Membership->Element = NewMembership; } Membership->Element[Membership->Count] = AliasRid; Membership->Count++; } } } // // If the buffer we've allocated turns out to be way overboard, allocate // a smaller one for the output. // // TBS QueryAliasMembershipFinish: // // If we got as far as allocating a buffer for the DomainSids, free it. // if (DomainSid != NULL) { MIDL_user_free(DomainSid); DomainSid = NULL; } return(Status); QueryAliasMembershipError: // // If necessary, free the output membership array. // if (Membership->Element != NULL) { MIDL_user_free( Membership->Element); Membership->Element = NULL; } goto QueryAliasMembershipFinish; } NTSTATUS SampAlSlowQueryAliasMembership( IN SAMPR_HANDLE DomainHandle, IN PSAMPR_PSID_ARRAY SidArray, OUT PSAMPR_ULONG_ARRAY Membership ) /*++ Routine Description: This function is the slow version of the worker routine for the SamrGetAliasMembership API searches. It is called when the in-memory Alias Information is no longer valid. WARNING! The caller of this function must hold the SAM Database Read Lock. Parameters: DomainHandle - Handle from a SamOpenDomain call. SidArray - Pointer to a counted array of pointers to Sids whose alias memberships are to be looked up. Membership - Receives the array of rids rerpresenting the aliases in this domain that any of the sid(s) are members of. Return Values: STATUS_SUCCESS - The combined alias membership is in Membership STATUS_INVALID_SID - One of the passed sids was invalid --*/ { NTSTATUS NtStatus; ULONG i; ULONG MembershipCount; ULONG TotalMembershipCount; ULONG MembershipIndex; ULONG BufferSize; ULONG TotalBufferSize; ULONG SidCount = SidArray->Count; PSID *Sids = (PSID *) &SidArray->Sids->SidPointer; // // For each Sid, retrieve its membership and size up how many membership // entries we'll have in total. // TotalMembershipCount = 0; TotalBufferSize = 0; for (i=0; i < SidCount; i++) { // // Get the membership count for this account // BufferSize = 0; NtStatus = SampRetrieveAliasMembership( Sids[i], &MembershipCount, &BufferSize, NULL ); if (NT_SUCCESS(NtStatus) || (NtStatus == STATUS_BUFFER_OVERFLOW)) { ASSERT(BufferSize == (MembershipCount * sizeof(ULONG))); TotalMembershipCount += MembershipCount; TotalBufferSize += BufferSize; NtStatus = STATUS_SUCCESS; } else { break; } } // // Allocate and fill in the membership array // if (NT_SUCCESS(NtStatus)) { Membership->Element = MIDL_user_allocate(TotalBufferSize); if (Membership->Element == NULL) { NtStatus = STATUS_INSUFFICIENT_RESOURCES; } else { // // Fill in the allocated membership list // MembershipIndex = 0; for (i=0; i < SidCount; i++) { // // Get the membership list for this account // BufferSize = TotalBufferSize; NtStatus = SampRetrieveAliasMembership( Sids[i], &MembershipCount, &BufferSize, &(Membership->Element[MembershipIndex]) ); if (!NT_SUCCESS(NtStatus)) { break; } ASSERT(BufferSize == (MembershipCount * sizeof(*(Membership->Element)))); // // Remove duplicate aliases // if (MembershipCount > 0) { ULONG ExistingIndex, NewIndex; for (ExistingIndex = 0; ExistingIndex < MembershipIndex; ExistingIndex ++) { for (NewIndex = MembershipIndex; NewIndex < MembershipIndex + MembershipCount; NewIndex ++) { if (Membership->Element[ExistingIndex] == Membership->Element[NewIndex]) { // // This alias is already in the list - forget it // if (NewIndex < MembershipIndex + MembershipCount - 1) { // // Remove the duplicate alias // Membership->Element[NewIndex] = Membership->Element[MembershipIndex + MembershipCount - 1]; NewIndex --; // So we come back to this alias again } MembershipCount --; TotalMembershipCount --; } } } } MembershipIndex += MembershipCount; ASSERT(MembershipIndex <= TotalMembershipCount); ASSERT(TotalBufferSize >= BufferSize); TotalBufferSize -= BufferSize; } if (!NT_SUCCESS(NtStatus)) { MIDL_user_free(Membership->Element); Membership->Element = NULL; } else { Membership->Count = TotalMembershipCount; } } } return NtStatus; } NTSTATUS SampAlQueryMembersOfAlias( IN SAMPR_HANDLE AliasHandle, OUT PSAMPR_PSID_ARRAY MemberSids ) /*++ Routine Description: This function returns an array of Sids of accounts that are members of a specified alias. Arguments: AliasHandle - Handle to an Alias object MemberSids - Receives an array of Sids that belong to the Alias Return Value: --*/ { NTSTATUS Status; PSAMP_AL_ALIAS_MEMBER_LIST AliasMemberList = NULL; PSID *Members = NULL; ULONG AliasMemberCount; Status = SampRetrieveAliasMembers( AliasHandle, &AliasMemberCount, &Members ); if (!NT_SUCCESS(Status)) { goto QueryMembersOfAliasError; } QueryMembersOfAliasFinish: MemberSids->Count = AliasMemberCount; MemberSids->Sids = (PSAMPR_SID_INFORMATION) Members; return(Status); QueryMembersOfAliasError: AliasMemberCount = 0; Members = NULL; goto QueryMembersOfAliasFinish; } NTSTATUS SampAlAddMembersToAlias( IN SAMPR_HANDLE AliasHandle, IN ULONG Options, IN PSAMPR_PSID_ARRAY MemberSids ) /*++ Routine Description: This function adds one or more member to an alias. Any failure results in the in-memory Alias Information being discarded. WARNING: The calling function must perform all parameter validation and the SAM Database Write Lock must be held. Parameters: AliasHandle - The handle of an opened alias to operate on. Options - Specifies optional actions to be taken SAMP_AL_VERIFY_NO_MEMBERS_IN_ALIAS - Verify that none of the Members are already present in the Alias. MemberSids - Array of member Sids to be added. Return Values: STATUS_SUCCESS - The Service completed successfully. STATUS_MEMBER_IN_ALIAS - The member already belongs to the alias. --*/ { NTSTATUS Status; PSAMP_AL_MEMBER_ALIAS_LIST MemberAliasList = NULL; PSAMP_AL_MEMBER_ALIAS_LIST OldMemberAliasList = NULL; PSAMP_AL_MEMBER_DOMAIN MemberDomain = NULL; PSAMP_AL_MEMBER_ACCOUNT MemberAccount = NULL; ULONG AliasRid = ((PSAMP_OBJECT) AliasHandle)->TypeBody.Alias.Rid; ULONG MemberRid, SidIndex, MembershipCount; PSID DomainSid = NULL; PSID MemberSid = NULL; SAMPR_ULONG_ARRAY AliasRids; AliasRids.Count = 0; AliasRids.Element = NULL; // // Verify that the cached Alias Membership information is valid. // if (!SampAlInfoIsValidForAlias(AliasHandle)) { goto AddMembersToAliasFinish; } // // If requested, verify that none of members already belong to the alias // if (Options & SAMP_AL_VERIFY_NO_MEMBERS_IN_ALIAS) { Status = SampAlLookupMembersInAlias( AliasHandle, AliasRid, MemberSids, &MembershipCount ); if (!NT_SUCCESS(Status)) { goto AddMembersToAliasError; } Status = STATUS_MEMBER_NOT_IN_ALIAS; if (MembershipCount > 0) { goto AddMembersToAliasError; } Status = STATUS_SUCCESS; } // // Allocate Scratch Sid buffer. We will use this same buffer for splitting // each Sid. // DomainSid = MIDL_user_allocate( RtlLengthRequiredSid( 256 )); Status = STATUS_NO_MEMORY; if (DomainSid == NULL) { goto AddMembersToAliasError; } Status = STATUS_SUCCESS; // // Obtain pointer to Member Alias List. // MemberAliasList = SampAlAliasHandleToMemberAliasList( AliasHandle ); OldMemberAliasList = MemberAliasList; // // For each Sid, obtain its DomainSid and Rid. Then lookup its // DomainSid to obtain the MemberDomain, creating one if necessary. // Then lookup its Rid to obtain its MemberAccount, creating one // if necessary. Then add the Alias to the MemebrAccount. // for (SidIndex = 0; SidIndex < MemberSids->Count; SidIndex++ ) { MemberSid = MemberSids->Sids[ SidIndex ].SidPointer; Status = SampSplitSid( MemberSid, &DomainSid, &MemberRid ); if (!NT_SUCCESS(Status)) { break; } // // Lookup the Member Domain for this DomainSid in the Member Alias // List. // Status = SampAlLookupMemberDomain( MemberAliasList, DomainSid, &MemberDomain ); if (!NT_SUCCESS(Status)) { if (Status != STATUS_NO_SUCH_DOMAIN) { break; } Status = STATUS_SUCCESS; // // The Member Domain was not found. Create a new Member Domain // Status = SampAlCreateMemberDomain( &MemberAliasList, DomainSid, &MemberDomain ); if (!NT_SUCCESS(Status)) { break; } // // Create a Member Account entry. // Status = SampAlCreateMemberAccount( &MemberAliasList, &MemberDomain, MemberRid, SAMP_AL_INITIAL_MEMBER_ACCOUNT_ALIAS_CAPACITY, &MemberAccount ); if (!NT_SUCCESS(Status)) { break; } } else { // // We found the domain. This means that we have to lookup // each Member Account. If a Member Account does not exist, // we'll create one. Note that we may already have one due // to this account being a member of another Alias. // Status = SampAlLookupMemberAccount( MemberDomain, MemberRid, &MemberAccount ); if (!NT_SUCCESS(Status)) { if (Status != STATUS_NO_SUCH_MEMBER) { break; } // // Create a Member Account for this Rid, // Status = SampAlCreateMemberAccount( &MemberAliasList, &MemberDomain, MemberRid, SAMP_AL_INITIAL_MEMBER_ACCOUNT_ALIAS_CAPACITY, &MemberAccount ); if (!NT_SUCCESS(Status)) { break; } } } // // We now have a MemberAccount. Now add the Alias to it. // AliasRids.Count = 1; AliasRids.Element = &AliasRid; Status = SampAlAddAliasesToMemberAccount( &MemberAliasList, &MemberDomain, &MemberAccount, 0, &AliasRids ); if (!NT_SUCCESS(Status)) { break; } // // Deal with next Member Sid for the Alias. // } if (!NT_SUCCESS(Status)) { goto AddMembersToAliasError; } // // If the Member Alias List has been reallocated, store its new address. // if (MemberAliasList != OldMemberAliasList) { SampAlUpdateMemberAliasList( AliasHandle, MemberAliasList ); } AddMembersToAliasFinish: // // If necessary, free the DomainSid. // if (DomainSid != NULL) { MIDL_user_free( DomainSid ); DomainSid = NULL; } return(Status); AddMembersToAliasError: goto AddMembersToAliasFinish; } NTSTATUS SampAlRemoveMembersFromAlias( IN SAMPR_HANDLE AliasHandle, IN ULONG Options, IN PSAMPR_PSID_ARRAY MemberSids ) /*++ Routine Description: This function removes a list of members from an Alias. Arguments: AliasHandle - The handle of an opened alias to operate on. Options - Specifies optional actions to be taken SAMP_AL_VERIFY_ALL_MEMBERS_IN_ALIAS - Verify that all of the Members belong to the Alias. MemberSids - Array of member Sids to be removed. Return Values: STATUS_SUCCESS - The Service completed successfully. --*/ { NTSTATUS Status; PSAMP_AL_MEMBER_ALIAS_LIST MemberAliasList = NULL; PSAMP_AL_MEMBER_ALIAS_LIST OldMemberAliasList = NULL; PSAMP_AL_MEMBER_DOMAIN MemberDomain = NULL; PSAMP_AL_MEMBER_ACCOUNT MemberAccount = NULL; BOOLEAN MemberDomainDeleted; BOOLEAN MemberAccountDeleted; ULONG AliasRid = ((PSAMP_OBJECT) AliasHandle)->TypeBody.Alias.Rid; ULONG MemberRid, SidIndex, MembershipCount; PSID DomainSid = NULL; PSID MemberSid = NULL; SAMPR_ULONG_ARRAY AliasRids; AliasRids.Count = 0; AliasRids.Element = NULL; // // If requested, verify that all of members already belong to the alias // if (Options & SAMP_AL_VERIFY_ALL_MEMBERS_IN_ALIAS) { Status = SampAlLookupMembersInAlias( AliasHandle, AliasRid, MemberSids, &MembershipCount ); if (!NT_SUCCESS(Status)) { goto RemoveMembersFromAliasError; } Status = STATUS_MEMBER_NOT_IN_ALIAS; if (MembershipCount < MemberSids->Count) { goto RemoveMembersFromAliasError; } Status = STATUS_SUCCESS; } // // Obtain pointer to Member Alias List. // MemberAliasList = SampAlAliasHandleToMemberAliasList( AliasHandle ); OldMemberAliasList = MemberAliasList; if (!NT_SUCCESS(Status)) { goto RemoveMembersFromAliasError; } // // For each Sid, obtain its DomainSid and Rid. Then lookup its // DomainSid to obtain the MemberDomain. Then lookup its Rid to obtain // its MemberAccount. Then remove the Alias from the MemberAccount. // for (SidIndex = 0; SidIndex < MemberSids->Count; SidIndex++ ) { MemberSid = MemberSids->Sids[ SidIndex ].SidPointer; SampSplitSid( MemberSid, &DomainSid, &MemberRid ); // // Lookup the Member Domain for this DomainSid in the Member Alias // List. // Status = SampAlLookupMemberDomain( MemberAliasList, DomainSid, &MemberDomain ); if (!NT_SUCCESS(Status)) { break; } if (!NT_SUCCESS(Status)) { if (Status != STATUS_MEMBER_NOT_IN_ALIAS) { break; } if (Options & SAMP_AL_VERIFY_ALL_MEMBERS_IN_ALIAS) { ASSERT( FALSE ); } Status = STATUS_SUCCESS; continue; } // // We found the domain. This means that we have to lookup // each Member Account. If a Member Account does not exist, // we'll just skip this account unless we already checked existence. // If we checked existence and we can't find it now, its an // internal error. // Status = SampAlLookupMemberAccount( MemberDomain, MemberRid, &MemberAccount ); if (!NT_SUCCESS(Status)) { if (Status != STATUS_MEMBER_NOT_IN_ALIAS) { break; } if (Options & SAMP_AL_VERIFY_ALL_MEMBERS_IN_ALIAS) { ASSERT( FALSE); } Status = STATUS_SUCCESS; continue; } // // We now have the MemberAccount. Now remove the Alias from it. // AliasRids.Count = 1; AliasRids.Element = &AliasRid; Status = SampAlRemoveAliasesFromMemberAccount( &MemberAliasList, &MemberDomain, &MemberAccount, 0, &AliasRids, &MemberDomainDeleted, &MemberAccountDeleted ); if (!NT_SUCCESS(Status)) { break; } // // Deal with next Member Sid for the Alias. // MIDL_user_free( DomainSid ); DomainSid = NULL; } if (!NT_SUCCESS(Status)) { goto RemoveMembersFromAliasError; } // // If the Member Alias List has been reallocated, store its new address. // if (MemberAliasList != OldMemberAliasList) { SampAlUpdateMemberAliasList( AliasHandle, MemberAliasList ); } RemoveMembersFromAliasFinish: return(Status); RemoveMembersFromAliasError: goto RemoveMembersFromAliasFinish; } NTSTATUS SampAlLookupMembersInAlias( IN SAMPR_HANDLE AliasHandle, IN ULONG AliasRid, IN PSAMPR_PSID_ARRAY MemberSids, OUT PULONG MembershipCount ) /*++ Routine Description: This function checks how many of a given list of Member Sids belong to an Alias. It is called prior to updating Alias Memberships. Arguments: AliasHandle - Handle to Alias Object AliasRid - Specifies the Rid of the Alias MemberSids - Pointer to counted array of pointers to Member Sids MembershipCount - Receives count of member Sids in the given set that belong to the alias. --*/ { NTSTATUS Status = STATUS_SUCCESS; SAMPR_PSID_ARRAY AliasMemberSids; ULONG OutputMembershipCount = 0; ULONG SidIndex, AliasMemberSidIndex; PSID Sid = NULL; PSID AliasMemberSid = NULL; // // First, query the members of the Alias. // Status = SampAlQueryMembersOfAlias( AliasHandle, &AliasMemberSids ); if (!NT_SUCCESS(Status)) { goto LookupMembersInAliasError; } // // Now scan each of the given Member Sids and count it if it is a member // of the Alias. // for (SidIndex = 0; SidIndex < MemberSids->Count; SidIndex++) { Sid = MemberSids->Sids[ SidIndex].SidPointer; for (AliasMemberSidIndex = 0; AliasMemberSidIndex = AliasMemberSids.Count; AliasMemberSidIndex++) { AliasMemberSid = AliasMemberSids.Sids[ AliasMemberSidIndex].SidPointer; if (RtlEqualSid( Sid, AliasMemberSid)) { OutputMembershipCount++; } } } *MembershipCount = OutputMembershipCount; LookupMembersInAliasFinish: return(Status); LookupMembersInAliasError: *MembershipCount =0; goto LookupMembersInAliasFinish; } NTSTATUS SampAlDeleteAlias( IN SAMPR_HANDLE *AliasHandle ) /*++ Routine Description: This function deletes an alias. Arguments: AliasHandle - Pointer to Handle to Alias Return Values: NTSTATUS - Standard Nt Result Code --*/ { NTSTATUS Status = STATUS_SUCCESS; PSAMP_AL_MEMBER_ALIAS_LIST MemberAliasList = NULL; PSAMP_AL_MEMBER_DOMAIN MemberDomain = NULL; PSAMP_AL_MEMBER_ACCOUNT MemberAccount = NULL; BOOLEAN MemberDomainDeleted; BOOLEAN MemberAccountDeleted; ULONG AliasRid = ((PSAMP_OBJECT) *AliasHandle)->TypeBody.Alias.Rid; LONG DomainIndex; ULONG RidCount; LONG DomainCount; ULONG AccountIndex; SAMPR_ULONG_ARRAY AliasRids; AliasRids.Count = 1; AliasRids.Element = &AliasRid; // // Obtain pointer to Member Alias List. // MemberAliasList = SampAlAliasHandleToMemberAliasList( *AliasHandle ); if (!NT_SUCCESS(Status)) { goto DeleteAliasError; } // // Traverse the Member Alias List. Look in every Member Account for the // Alias and remove it if present. This is rather slow if there is a // large number of alias relationships for diverse domains. // DomainCount = (LONG) MemberAliasList->DomainCount; for (DomainIndex = 0, MemberDomain = SampAlFirstMemberDomain( MemberAliasList ); DomainIndex < DomainCount; DomainIndex++ ) { RidCount = MemberDomain->RidCount; for (AccountIndex = 0, MemberAccount = SampAlFirstMemberAccount( MemberDomain ); AccountIndex < RidCount; AccountIndex++ ) { ASSERT(MemberAccount->Signature == SAMP_AL_MEMBER_ACCOUNT_SIGNATURE); // // We now have the MemberAccount. Now remove the Alias from it. // Status = SampAlRemoveAliasesFromMemberAccount( &MemberAliasList, &MemberDomain, &MemberAccount, 0, &AliasRids, &MemberDomainDeleted, &MemberAccountDeleted ); if (!NT_SUCCESS(Status)) { if (Status == STATUS_MEMBER_NOT_IN_ALIAS) { Status = STATUS_SUCCESS; continue; } break; } // // Move the the next member account unless the one we were pointing // to was deleted (in which case the next one moved to us). // if (!MemberAccountDeleted) { MemberAccount = SampAlNextMemberAccount( MemberAccount ); } // // If the member domain was deleted, then the count of members // is off as is the member account pointer. // if (MemberDomainDeleted) { break; } } if (!NT_SUCCESS(Status)) { break; } // // Move the the next member domain unless the one we were pointing // to was deleted (in which case the next one moved to us). // if (!MemberDomainDeleted) { MemberDomain = SampAlNextMemberDomain( MemberDomain ); } } if (!NT_SUCCESS(Status)) { goto DeleteAliasError; } DeleteAliasFinish: return(Status); DeleteAliasError: goto DeleteAliasFinish; } NTSTATUS SampAlRemoveAccountFromAllAliases( IN PSID AccountSid, IN BOOLEAN CheckAccess, IN SAMPR_HANDLE DomainHandle OPTIONAL, IN PULONG MembershipCount OPTIONAL, IN PULONG *Membership OPTIONAL ) /*++ Routine Description: This routine removes the specified account from the member list of all aliases in this domain. Arguments: AccountSid - The SID of the account being Removed. CheckAccess - if TRUE, this routine will make sure that the caller is allowed REMOVE_ALIAS_MEMBER access to this alias. If FALSE, the caller is already known to have proper access. DomainHandle - if CheckAccess is TRUE, this handle must be provided to allow access to be checked. MembershipCount - if CheckAccess is TRUE, this pointer must be provided to receive the number of aliases the account was deleted from. Membership - if CheckAccess is TRUE, this pointer must be provided to point to a list of aliases the account was removed from. The caller must free this list with MIDL_user_free(). Return Value: STATUS_SUCCESS - The user has been Removed from all aliases. --*/ { NTSTATUS Status = STATUS_SUCCESS; PSAMP_AL_MEMBER_ALIAS_LIST MemberAliasList = NULL; PSAMP_AL_MEMBER_DOMAIN MemberDomain = NULL; PSAMP_AL_MEMBER_ACCOUNT MemberAccount = NULL; BOOLEAN MemberDomainDeleted; PSID DomainSid = NULL; LONG DomainIndex; ULONG MemberRid, AliasRid; SAMPR_ULONG_ARRAY AliasRids; AliasRids.Count = 1; AliasRids.Element = &AliasRid; // // Obtain pointer to Member Alias List for the Current Transaction Domain. // DomainIndex = SampTransactionDomainIndex; MemberAliasList = SampAlDomainIndexToMemberAliasList( DomainIndex ); // // We remove the Account from all aliases by locating its Member Account // structure and deleting it. First, find the Member Domain. // SampSplitSid( AccountSid, &DomainSid, &MemberRid ); // // Lookup the Member Domain for this DomainSid in the Member Alias // List. // Status = SampAlLookupMemberDomain( MemberAliasList, DomainSid, &MemberDomain ); if (!NT_SUCCESS(Status)) { if (Status != STATUS_NO_SUCH_DOMAIN) { goto RemoveAccountFromAllAliasesError; } // // There is no member Domain object for this account. This means // the account does not belong to any aliases. // Status = STATUS_SUCCESS; goto RemoveAccountFromAllAliasesFinish; } // // We found the Member Domain. Now find the Member Account. // Status = SampAlLookupMemberAccount( MemberDomain, MemberRid, &MemberAccount ); if (!NT_SUCCESS(Status)) { if (Status != STATUS_NO_SUCH_MEMBER) { goto RemoveAccountFromAllAliasesError; } Status = STATUS_SUCCESS; goto RemoveAccountFromAllAliasesFinish; } // // If CheckAccess = TRUE, return a list of Aliases that the account was // a member of. // if (CheckAccess) { *Membership = MIDL_user_allocate( MemberAccount->AliasCount * sizeof(ULONG)); *MembershipCount = MemberAccount->AliasCount; Status = STATUS_NO_MEMORY; if (*Membership == NULL) { goto RemoveAccountFromAllAliasesError; } Status = STATUS_SUCCESS; } // // We now have the MemberAccount. Now delete it, thereby removing the // account from all Aliases. // Status = SampAlDeleteMemberAccount( &MemberAliasList, &MemberDomain, MemberAccount, &MemberDomainDeleted ); if (!NT_SUCCESS(Status)) { goto RemoveAccountFromAllAliasesError; } RemoveAccountFromAllAliasesFinish: // // Free the Domain Sid buffer (if any) // if (DomainSid != NULL) { MIDL_user_free( DomainSid ); DomainSid = NULL; } return(Status); RemoveAccountFromAllAliasesError: if (CheckAccess) { *Membership = NULL; *MembershipCount = 0; } goto RemoveAccountFromAllAliasesFinish; } NTSTATUS SampAlBuildAliasInformation( ) /*++ Routine Description: This function builds the Alias Information for each of the SAM Local Domains. For each Domain, this information consists of the Member Alias List. Arguments: None. Return Values: --*/ { NTSTATUS Status = STATUS_SUCCESS; LONG DomainIndex; for (DomainIndex = 0; DomainIndex < (LONG) SampDefinedDomainsCount; DomainIndex++) { if (SampAlEnableBuildingOfList[ DomainIndex]) { Status = SampAlBuildMemberAliasList( DomainIndex); if (!NT_SUCCESS(Status)) { break; } } } if (!NT_SUCCESS(Status)) { goto BuildAliasInformationError; } BuildAliasInformationFinish: return(Status); BuildAliasInformationError: goto BuildAliasInformationFinish; } //////////////////////////////////////////////////////////////////////////// // // // Private functions // // // //////////////////////////////////////////////////////////////////////////// NTSTATUS SampAlCreateMemberAccount( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, IN ULONG Rid, IN ULONG AliasCapacity, OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount ) /*++ Routine Description: This function creates an empty Member Account in the specified Member Domain for the specified Member Rid. There must not already be al account for this Rid. The Member Account is appended to the end of any existing ones in the Member Domain. Arguments: MemberAliasList - Pointer to pointer to Member Alias List. MemberDomain - Pointer to Member Domain in which the Member Account is to be created. The Member Domain must already exist. Rid - Specifies the Account Rid. AliasCapacity - Specifies the initial number of Alias Rids that the MemberAccount can hold. MemberAccount - Receives pointer to the newly created Member Account. --*/ { NTSTATUS Status; ULONG MaximumLengthMemberAccount; PSAMP_AL_MEMBER_ACCOUNT OutputMemberAccount = NULL; // // Calculate the length of data needed for the new member Account entry. // MaximumLengthMemberAccount = SampAlLengthRequiredMemberAccount( AliasCapacity ); // // Allocate space for the Member Account. // Status = SampAlAllocateMemberAccount( MemberAliasList, MemberDomain, MaximumLengthMemberAccount, &OutputMemberAccount ); if (!NT_SUCCESS(Status)) { goto CreateMemberAccountError; } // // Scratch the new Member Account // OutputMemberAccount->Signature = SAMP_AL_MEMBER_ACCOUNT_SIGNATURE; OutputMemberAccount->MaximumLength = MaximumLengthMemberAccount; OutputMemberAccount->UsedLength = SampAlOffsetFirstAlias( OutputMemberAccount ); ASSERT(OutputMemberAccount->MaximumLength >= OutputMemberAccount->UsedLength); OutputMemberAccount->Rid = Rid; OutputMemberAccount->AliasCount = 0; ((*MemberDomain)->RidCount)++; *MemberAccount = OutputMemberAccount; CreateMemberAccountFinish: return(Status); CreateMemberAccountError: *MemberAccount = NULL; SampAlInfoMakeInvalid( (*MemberAliasList)->DomainIndex ); goto CreateMemberAccountFinish; } NTSTATUS SampAlAllocateMemberAccount( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, IN ULONG MaximumLengthMemberAccount, OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount ) /*++ Routine Description: This function allocates the space for a new Member Account in a Member Domain. If necessary, the Mmeber Domain and its associated Member Alias List will be grown. Arguments: MemberAliasList - Pointer to pointer to the Member Alias List. MemberDomain - Pointer to pointer to the Member Domain MaximumLengthMemberAccount - Initial Maximum Length required for the Member Account MemberAccount - receives pointer to the newly allocated Member Account --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG SpaceAvailable; // // Calculate the space available in the Member Domain // SpaceAvailable = (*MemberDomain)->MaximumLength - (*MemberDomain)->UsedLength; if (MaximumLengthMemberAccount > SpaceAvailable) { Status = SampAlGrowMemberDomain( MemberAliasList, MemberDomain, MaximumLengthMemberAccount - SpaceAvailable ); if (!NT_SUCCESS(Status)) { goto AllocateMemberAccountError; } } // // The Member Domain is now guaranteed to be large enough. Reserve the // space for the new Member Account. // *MemberAccount = SampAlNextNewMemberAccount(*MemberDomain); (*MemberDomain)->UsedLength += MaximumLengthMemberAccount; ASSERT((*MemberDomain)->MaximumLength >= (*MemberDomain)->UsedLength); AllocateMemberAccountFinish: return(Status); AllocateMemberAccountError: SampAlInfoMakeInvalid( (*MemberAliasList)->DomainIndex ); *MemberAccount = NULL; goto AllocateMemberAccountFinish; } NTSTATUS SampAlGrowMemberAccount( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, IN OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount, IN ULONG ExtraSpaceRequired ) /*++ Routine Description: This function grows a Member Account by at least the requested amount. If necessary, the containing Member Domain and Member Alias List will also be grown. Arguments: MemberAliasList - Pointer to pointer to the Member Alias List. MemberDomain - Pointer to pointer to the Member Domain MemberAccount - Pointer to Pointer to the Member Account. ExtraSpaceRequired - Extra space needed in the Member Account. Return Values: --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG SpaceAvailable, MemberAccountOffset, CopyLength; PUCHAR Destination = NULL; PUCHAR Source = NULL; // // Calculate the space available in the Member Domain // SpaceAvailable = (*MemberDomain)->MaximumLength - (*MemberDomain)->UsedLength; if (ExtraSpaceRequired > SpaceAvailable) { // // We need to grow the Member Domain. Calculate the offset of the // Member Account in the old Member Domain, grow the Member Domain // and then calculate the new address of the Member Account. // MemberAccountOffset = SampAlMemberAccountToOffset( *MemberDomain, *MemberAccount ); Status = SampAlGrowMemberDomain( MemberAliasList, MemberDomain, ExtraSpaceRequired - SpaceAvailable ); if (!NT_SUCCESS(Status)) { goto GrowMemberAccountError; } *MemberAccount = SampAlMemberAccountFromOffset( *MemberDomain, MemberAccountOffset ); } // // The Member Domain is now guaranteed to be large enough. // Now shift any Member Accounts that follow the one being grown // up to make room for the expanded Member Account. The source address // for the move is the address of the next Member Account (if any) based // on the existing size of the Member Account. The destination address // of the move is the address of the next Member Account (if any) based // on the new size of the Member Account. // Source = (PUCHAR) SampAlNextMemberAccount( *MemberAccount ); (*MemberAccount)->MaximumLength += ExtraSpaceRequired; Destination = (PUCHAR) SampAlNextMemberAccount( *MemberAccount ); CopyLength = (((PUCHAR)(SampAlNextNewMemberAccount(*MemberDomain))) - Source); // // Reserve the space in the Member Domain. If all's well, the // end of the destination buffer should match the updated end of the // used area of the Member Domain. // (*MemberDomain)->UsedLength += ExtraSpaceRequired; ASSERT((*MemberDomain)->MaximumLength >= (*MemberDomain)->UsedLength); ASSERT( Destination + CopyLength == (PUCHAR) SampAlNextNewMemberAccount( *MemberDomain )); ASSERT( Destination + CopyLength <= (PUCHAR)(*MemberAliasList) + (*MemberAliasList)->MaximumLength ); ASSERT( Destination + CopyLength <= (PUCHAR)(*MemberDomain) + (*MemberDomain)->MaximumLength ); if (CopyLength > 0) { RtlMoveMemory( Destination, Source, CopyLength ); } GrowMemberAccountFinish: return(Status); GrowMemberAccountError: SampAlInfoMakeInvalid( (*MemberAliasList)->DomainIndex ); goto GrowMemberAccountFinish; } NTSTATUS SampAlLookupMemberAccount( IN PSAMP_AL_MEMBER_DOMAIN MemberDomain, IN ULONG MemberRid, OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount ) /*++ Routine Description: This function looks up an Account Rid in a Member Domain to see if there is a Member Account structure for it. Arguments: MemberDomain - Pointer to Member Domain MemberRid - Specifies the Account Rid MemberAccount - Receives pointer to Member Account if found. --*/ { NTSTATUS Status; PSAMP_AL_MEMBER_ACCOUNT NextMemberAccount = NULL; ULONG RidIndex; BOOLEAN AccountFound = FALSE; for (RidIndex = 0, NextMemberAccount = SampAlFirstMemberAccount( MemberDomain ); RidIndex < MemberDomain->RidCount; RidIndex++, NextMemberAccount = SampAlNextMemberAccount( NextMemberAccount)) { if (MemberRid == NextMemberAccount->Rid) { AccountFound = TRUE; break; } } Status = STATUS_NO_SUCH_MEMBER; if (!AccountFound) { goto LookupMemberAccountError; } *MemberAccount = NextMemberAccount; Status = STATUS_SUCCESS; LookupMemberAccountFinish: return(Status); LookupMemberAccountError: goto LookupMemberAccountFinish; } NTSTATUS SampAlAddAliasesToMemberAccount( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, IN OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount, IN ULONG Options, IN PSAMPR_ULONG_ARRAY AliasRids ) /*++ Routine Description: This function adds an array of aliases to a Member Account. An error will be returned if any of the aliases exist in the Member Account. If necessary, the containing Member Account, Member Domain and Member Alias List will also be grown. Arguments: MemberAliasList - Pointer to pointer to the Member Alias List. MemberDomain - Pointer to pointer to the Member Domain MemberAccount - Pointer to Pointer to the Member Account. Options - Specifies optional actions to be taken SAMP_AL_VERIFY_NO_ALIASES_IN_ACCOUNT - Verify that none of the Aliases presented belong to the various Member Accounts. AliasRids - Pointer to counted array of Alias Rids. Return Values: --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG SpaceRequired, SpaceAvailable, CopyLength; PUCHAR Source = NULL; PUCHAR Destination = NULL; ULONG ExistingAliasCount; // // If requested, verify that none of the Aliases are already // in the Member Account // if (Options & SAMP_AL_VERIFY_NO_ALIASES_IN_ACCOUNT) { Status = SampAlLookupAliasesInMemberAccount( *MemberAccount, AliasRids, &ExistingAliasCount ); if (!NT_SUCCESS(Status)) { goto AddAliasesToMemberAccountError; } Status = STATUS_MEMBER_IN_ALIAS; if (ExistingAliasCount > 0) { goto AddAliasesToMemberAccountError; } Status = STATUS_SUCCESS; } // // Calculate the space required for the new Aliases. // SpaceRequired = AliasRids->Count * sizeof( ULONG ); // // If there is not enough space available in the Member Account, // grow it. // SpaceAvailable = (*MemberAccount)->MaximumLength - (*MemberAccount)->UsedLength; if (SpaceRequired > SpaceAvailable) { Status = SampAlGrowMemberAccount( MemberAliasList, MemberDomain, MemberAccount, SpaceRequired - SpaceAvailable ); if (!NT_SUCCESS(Status)) { goto AddAliasesToMemberAccountError; } } // // The Member Account is now large enough. Copy in the aliases. // Destination = (PUCHAR) SampAlNextNewAliasInMemberAccount( *MemberAccount ); Source = (PUCHAR) AliasRids->Element; CopyLength = SpaceRequired; (*MemberAccount)->UsedLength += SpaceRequired; ASSERT((*MemberAccount)->MaximumLength >= (*MemberAccount)->UsedLength); RtlMoveMemory( Destination, Source, CopyLength ); // // Update the count of Aliases both in this Member Account and in the // Member Alias List. // (*MemberAccount)->AliasCount += AliasRids->Count; AddAliasesToMemberAccountFinish: return(Status); AddAliasesToMemberAccountError: goto AddAliasesToMemberAccountFinish; } NTSTATUS SampAlLookupAliasesInMemberAccount( IN PSAMP_AL_MEMBER_ACCOUNT MemberAccount, IN PSAMPR_ULONG_ARRAY AliasRids, OUT PULONG ExistingAliasCount ) /*++ Routine Description: This function checks a set of Alias Rids to see if any are present in a Member Account. Arguments: MemberAccount - Pointer to Member Account AliasRids - Specifies counted array of Alias Rids. ExistingAliasCount - Receives a count of the Alias Rids presented that are already in the Member Account. --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG AliasIndex, AliasInMemberAccountIndex; // // Scan the Alias Rids, looking each one up. // for (AliasIndex = 0; AliasIndex < AliasRids->Count; AliasRids++ ) { for (AliasInMemberAccountIndex = 0; AliasInMemberAccountIndex < MemberAccount->AliasCount; AliasInMemberAccountIndex++) { if (AliasRids->Element[ AliasIndex ] == MemberAccount->AliasRids[ AliasInMemberAccountIndex ] ) { (*ExistingAliasCount)++; } } } return(Status); } NTSTATUS SampAlRemoveAliasesFromMemberAccount( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, IN OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount, IN ULONG Options, IN PSAMPR_ULONG_ARRAY AliasRids, OUT PBOOLEAN MemberDomainDeleted, OUT PBOOLEAN MemberAccountDeleted ) /*++ Routine Description: This function removes aliases from a Member Account. The Aliases need not already exist unless an option to check that they do exist is specified. No down sizing of the Member Account occurs, but an empty one will be deleted. NOTE: I don't know why ScottBi made MemberAliasList, MemberDomain, and MemberAccount parameters pointers to pointers. He never updates the pointers so he could have passed them in directly. JK Arguments: MemberAliasList - Pointer to pointer to the Member Alias List. MemberDomain - Pointer to pointer to the Member Domain MemberAccount - Pointer to Pointer to the Member Account. Options - Specifies optional actions to be taken SAMP_AL_VERIFY_ALL_ALIASES_IN_ACCOUNT - Verify that none of the Aliases presented belong to the Member Account. MemberDomainDeleted - Will be set to TRUE if the member domain pointed to by MemberDomain was deleted. Otherwise FALSE is returned. MemberAccountDeleted - Will be set to TRUE if the member account pointed to by MemberAccount was deleted. Otherwise FALSE is returned. AliasRids - Pointer to counted array of Alias Rids. --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG ExistingAliasIndex, LastAliasIndex, RemoveAliasIndex, ExistingAlias; ULONG ExistingAliasCount; (*MemberDomainDeleted) = FALSE; (*MemberAccountDeleted) = FALSE; // // If requested, verify that all of the Aliases are already // in the Member Account // if (Options & SAMP_AL_VERIFY_ALL_ALIASES_IN_ACCOUNT) { Status = SampAlLookupAliasesInMemberAccount( *MemberAccount, AliasRids, &ExistingAliasCount ); if (!NT_SUCCESS(Status)) { goto RemoveAliasesFromMemberAccountError; } Status = STATUS_MEMBER_IN_ALIAS; if (ExistingAliasCount < AliasRids->Count) { goto RemoveAliasesFromMemberAccountError; } Status = STATUS_SUCCESS; } // // If the Member Account is empty, then somebody forgot to delete it // ASSERT((*MemberAccount)->AliasCount != 0); LastAliasIndex = (*MemberAccount)->AliasCount - 1; for (ExistingAliasIndex = 0; ExistingAliasIndex < (*MemberAccount)->AliasCount; ExistingAliasIndex++) { ExistingAlias = (*MemberAccount)->AliasRids[ ExistingAliasIndex ]; for (RemoveAliasIndex = 0; RemoveAliasIndex < AliasRids->Count; RemoveAliasIndex++) { if (ExistingAlias == AliasRids->Element[ RemoveAliasIndex ]) { // // We're to delete this Alias. If this Alias Rid is not at the // end of the list contained in the Member Account, overwrite // it with the one at the end of the list. // if (ExistingAliasIndex < LastAliasIndex) { (*MemberAccount)->AliasRids[ ExistingAliasIndex] = (*MemberAccount)->AliasRids[ LastAliasIndex]; } (*MemberAccount)->AliasCount--; (*MemberAccount)->UsedLength -= sizeof(ULONG); ASSERT((*MemberAccount)->MaximumLength >= (*MemberAccount)->UsedLength); // // If the Member Account is now empty, quit. // if ((*MemberAccount)->AliasCount == 0) { break; } LastAliasIndex--; } } // // If the Member Account is now empty, quit. // if ((*MemberAccount)->AliasCount == 0) { break; } } // // If the Member Account is now empty, delete it. // if ((*MemberAccount)->AliasCount == 0) { Status = SampAlDeleteMemberAccount( MemberAliasList, MemberDomain, *MemberAccount, MemberDomainDeleted ); if (NT_SUCCESS(Status)) { (*MemberAccountDeleted) = TRUE; } } RemoveAliasesFromMemberAccountFinish: return(Status); RemoveAliasesFromMemberAccountError: goto RemoveAliasesFromMemberAccountFinish; } NTSTATUS SampAlDeleteMemberAccount( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, IN OUT PSAMP_AL_MEMBER_ACCOUNT MemberAccount, OUT PBOOLEAN MemberDomainDeleted ) /*++ Routine Description: This function deletes a Member Account. Currently, the containing Member Domain and Member Alias List are not shrunk, but the containing Member Domain will be deleted if empty. Arguments: MemberAliasList - Pointer to pointer to the Member Alias List. MemberDomain - Pointer to pointer to the Member Domain MemberAccount - Pointer to the Member Account. MemberDomainDeleted - Will be set to TRUE if the member domain pointed to by MemberDomain was deleted. Otherwise FALSE is returned. Return Values: --*/ { NTSTATUS Status = STATUS_SUCCESS; PUCHAR Source = NULL; PUCHAR Destination = NULL; ULONG CopyLength; (*MemberDomainDeleted) = FALSE; // // Calculate pointers for moving the residual portion of the Member // Domain down to close the gap left by the extant Member Account. // unused space. The start of the residual portion is the end of the // Member Account being deleted. The length of the residual portion is // the distance from the start to the end of the used portion of the // Member Domain. // Source = (PUCHAR) SampAlNextMemberAccount( MemberAccount ); Destination = (PUCHAR) MemberAccount; CopyLength = (PUCHAR) SampAlNextNewMemberAccount( *MemberDomain ) - Source; (*MemberDomain)->UsedLength -= MemberAccount->MaximumLength; ASSERT((*MemberDomain)->MaximumLength >= (*MemberDomain)->UsedLength); (*MemberDomain)->RidCount--; if (CopyLength > 0) { RtlMoveMemory( Destination, Source, CopyLength ); #if DBG { PSAMP_AL_MEMBER_ACCOUNT Member = (PSAMP_AL_MEMBER_ACCOUNT) Destination; ASSERT(Member->Signature == SAMP_AL_MEMBER_ACCOUNT_SIGNATURE); } #endif } // // If the Member Domain now has no Member Accounts, delete it. // if ((*MemberDomain)->RidCount == 0) { Status = SampAlDeleteMemberDomain( MemberAliasList, *MemberDomain ); if (!NT_SUCCESS(Status)) { goto DeleteMemberAccountError; } (*MemberDomainDeleted) = TRUE; } DeleteMemberAccountFinish: return(Status); DeleteMemberAccountError: goto DeleteMemberAccountFinish; } NTSTATUS SampAlCreateMemberDomain( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN PSID DomainSid, OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain ) /*++ Routine Description: This function creates a new Member Domain in the specified Alias Member List. Arguments: MemberAliasList - Pointer to pointer to Alias Member List. DomainSid - Pointer to Sid of Domain to which this MemberDomain relates. MemberDomain - Receives pointer to the newly created Member Domain. Return Values: --*/ { NTSTATUS Status; PSAMP_AL_MEMBER_DOMAIN OutputMemberDomain = NULL; PSAMP_AL_MEMBER_ACCOUNT OutputMemberAccount = NULL; ULONG MaximumLengthMemberDomain; ULONG DomainSidLength = RtlLengthSid(DomainSid); ULONG AlternativeLength; // // Allocate the Member Domain. // MaximumLengthMemberDomain = SAMP_AL_INITIAL_MEMBER_DOMAIN_LENGTH; AlternativeLength = FIELD_OFFSET(SAMP_AL_MEMBER_DOMAIN, DomainSid) + DomainSidLength; if (MaximumLengthMemberDomain < AlternativeLength) { MaximumLengthMemberDomain = AlternativeLength; } Status = SampAlAllocateMemberDomain( MemberAliasList, MaximumLengthMemberDomain, &OutputMemberDomain ); if (!NT_SUCCESS(Status)) { goto CreateMemberDomainError; } // // Setup the new Member Domain entry. // OutputMemberDomain->MaximumLength = MaximumLengthMemberDomain; OutputMemberDomain->RidCount = 0; OutputMemberDomain->Signature = SAMP_AL_MEMBER_DOMAIN_SIGNATURE; RtlCopySid( DomainSidLength, &OutputMemberDomain->DomainSid, DomainSid ); OutputMemberDomain->UsedLength = SampAlOffsetFirstMemberAccount( OutputMemberDomain ); ASSERT(OutputMemberDomain->MaximumLength >= OutputMemberDomain->UsedLength); ((*MemberAliasList)->DomainCount)++; *MemberDomain = OutputMemberDomain; CreateMemberDomainFinish: return(Status); CreateMemberDomainError: *MemberDomain = NULL; goto CreateMemberDomainFinish; } NTSTATUS SampAlAllocateMemberDomain( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN ULONG MaximumLengthMemberDomain, OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain ) /*++ Routine Description: This function allocates the space for a new Member Domain in a Member Alias List. If necessary, the Member Alias List will be grown. Arguments: MemberAliasList - Pointer to pointer to the Member Alias List. MaximumLengthMemberDomain - Initial Maximum Length required for the Member Domain MemberDomain - Receives pointer to the Member Domain Return Values: --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG SpaceAvailable; // // Calculate the space available in the Member Alias List // SpaceAvailable = (*MemberAliasList)->MaximumLength - (*MemberAliasList)->UsedLength; if (MaximumLengthMemberDomain > SpaceAvailable) { Status = SampAlGrowMemberAliasList( MemberAliasList, MaximumLengthMemberDomain - SpaceAvailable ); if (!NT_SUCCESS(Status)) { goto AllocateMemberDomainError; } } // // The Member Alias List is now guaranteed to be large enough. Reserve the // space for the new Member Domain. // *MemberDomain = SampAlNextNewMemberDomain(*MemberAliasList); (*MemberAliasList)->UsedLength += MaximumLengthMemberDomain; ASSERT((*MemberAliasList)->MaximumLength >= (*MemberAliasList)->UsedLength); AllocateMemberDomainFinish: return(Status); AllocateMemberDomainError: SampAlInfoMakeInvalid( (*MemberAliasList)->DomainIndex ); *MemberDomain = NULL; goto AllocateMemberDomainFinish; } NTSTATUS SampAlGrowMemberDomain( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, IN ULONG ExtraSpaceRequired ) /*++ Routine Description: This function grows a Member Domain by at least the requested amount. If necessary, the Member Alias List will also be grown. Arguments: MemberAliasList - Pointer to pointer to the Member Alias List. MemberDomain - Pointer to pointer to the Member Domain ExtraSpaceRequired - Extra space needed in the Member Domain. Return Values: --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG SpaceAvailable, MemberDomainOffset, CopyLength; PUCHAR Destination = NULL; PUCHAR Source = NULL; // // Calculate the space available in the Member Alias List // SpaceAvailable = (*MemberAliasList)->MaximumLength - (*MemberAliasList)->UsedLength; if (ExtraSpaceRequired > SpaceAvailable) { // // We need to grow the Member Alias List. Calculate the offset of the // Member Domain in the old Member Alias List, grow the Member Alias // List and then calculate the new address of the Member Domain. // MemberDomainOffset = SampAlMemberDomainToOffset( *MemberAliasList, *MemberDomain ); Status = SampAlGrowMemberAliasList( MemberAliasList, ExtraSpaceRequired - SpaceAvailable ); if (!NT_SUCCESS(Status)) { goto GrowMemberDomainError; } // // Calculate the new address of the Member Domain // *MemberDomain = SampAlMemberDomainFromOffset( *MemberAliasList, MemberDomainOffset ); } // // The Member Alias List is now guaranteed to be large enough. // Now shift any Member Domains that follow the one being grown // up to make room for the expanded Member Domain. The source address // for the move is the address of the next Member Domain (if any) based // on the existing size of the Member Domain. The destination address // of the move is the address of the next Member Domain (if any) based // on the new size of the Member Domain. // Source = (PUCHAR) SampAlNextMemberDomain( *MemberDomain ); (*MemberDomain)->MaximumLength += ExtraSpaceRequired; Destination = (PUCHAR) SampAlNextMemberDomain( *MemberDomain ); CopyLength = (((PUCHAR)(SampAlNextNewMemberDomain(*MemberAliasList))) - Source); // // Reserve the space in the Member Alias List. If all's well, the // end of the destination buffer should match the updated end of the // used area of the member Alias List. // (*MemberAliasList)->UsedLength += ExtraSpaceRequired; ASSERT((*MemberAliasList)->MaximumLength >= (*MemberAliasList)->UsedLength); ASSERT( Destination + CopyLength == (PUCHAR) SampAlNextNewMemberDomain( *MemberAliasList )); ASSERT( Destination + CopyLength <= (PUCHAR)(*MemberAliasList) + (*MemberAliasList)->MaximumLength ); if (CopyLength > 0) { RtlMoveMemory( Destination, Source, CopyLength ); } GrowMemberDomainFinish: return(Status); GrowMemberDomainError: SampAlInfoMakeInvalid( (*MemberAliasList)->DomainIndex ); goto GrowMemberDomainFinish; } NTSTATUS SampAlLookupMemberDomain( IN PSAMP_AL_MEMBER_ALIAS_LIST MemberAliasList, IN PSID DomainSid, OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain ) /*++ Routine Description: This function looks up a Domain Sid in a Member Alias List to find its Member Domain structure (if any). Arguments: MemberAliasList - Pointer to pointer to Member Alias List DomainSid - Domain Sid whose Member Domain is to be found. --*/ { NTSTATUS Status; PSAMP_AL_MEMBER_DOMAIN NextMemberDomain = NULL; LONG DomainIndex; BOOLEAN DomainFound = FALSE; for (DomainIndex = 0, NextMemberDomain = SampAlFirstMemberDomain( MemberAliasList ); DomainIndex < (LONG) MemberAliasList->DomainCount; DomainIndex++, NextMemberDomain = SampAlNextMemberDomain( NextMemberDomain ) ) { if (RtlEqualSid( DomainSid, &NextMemberDomain->DomainSid)) { DomainFound = TRUE; break; } } Status = STATUS_NO_SUCH_DOMAIN; if (!DomainFound) { goto LookupMemberDomainError; } *MemberDomain = NextMemberDomain; Status = STATUS_SUCCESS; LookupMemberDomainFinish: return(Status); LookupMemberDomainError: *MemberDomain = NULL; goto LookupMemberDomainFinish; } NTSTATUS SampAlDeleteMemberDomain( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN OUT PSAMP_AL_MEMBER_DOMAIN MemberDomain ) /*++ Routine Description: This function deletes a Member Domain. The Member Domain may contain zero or more Member Accounts. The containing Member Alias List is shrunk. Arguments: MemberAliasList - Pointer to pointer to the Member Alias List. MemberDomain - Pointer to the Member Domain Return Values: --*/ { NTSTATUS Status = STATUS_SUCCESS; PUCHAR Source = NULL; PUCHAR Destination = NULL; ULONG CopyLength; // // Calculate pointers for moving the residual portion of the // Member Alias List down to close the gap left by the extant Member // Domain. The start of the residual portion is the next Member Domain. // The size of the portion is the distance between the start and the // used portion of the Member Alias List. // Source = (PUCHAR) SampAlNextMemberDomain( MemberDomain ); Destination = (PUCHAR) MemberDomain; CopyLength = ((PUCHAR) SampAlNextNewMemberDomain( *MemberAliasList )) - Source; (*MemberAliasList)->UsedLength -= MemberDomain->MaximumLength; ASSERT((*MemberAliasList)->MaximumLength >= (*MemberAliasList)->UsedLength); (*MemberAliasList)->DomainCount--; if (CopyLength > 0) { RtlMoveMemory( Destination, Source, CopyLength ); } return(Status); } NTSTATUS SampAlCreateMemberAliasList( IN LONG DomainIndex, IN ULONG InitialMemberAliasListLength, OUT OPTIONAL PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList ) /*++ Routine Description: This function creates an empty Member Alias List for the specified SAM Local Domain. The Member Alias List will be marked invalid. Arguments: DomainIndex - Specifies the Local SAM Domain InitialMemberAliasListLength - Specifies the initial maximum length of the Member Alias List in bytes MemberAliasList - Optional pointer to location in which a pointer to the Member Alias List will be returned. Note that the pointer can always be retrieved given the DomainIndex. Return Values: --*/ { NTSTATUS Status; PSAMP_AL_MEMBER_ALIAS_LIST OutputMemberAliasList = NULL; PSAMP_AL_ALIAS_INFORMATION AliasInformation = NULL; // // Allocate memory for the list. // OutputMemberAliasList = MIDL_user_allocate( InitialMemberAliasListLength ); Status = STATUS_NO_MEMORY; if (OutputMemberAliasList == NULL) { goto CreateMemberAliasListError; } Status = STATUS_SUCCESS; // // Scratch the List header // OutputMemberAliasList->Signature = SAMP_AL_MEMBER_ALIAS_LIST_SIGNATURE; OutputMemberAliasList->MaximumLength = InitialMemberAliasListLength; OutputMemberAliasList->UsedLength = SampAlOffsetFirstMemberDomain( OutputMemberAliasList ); ASSERT(OutputMemberAliasList->MaximumLength >= OutputMemberAliasList->UsedLength); OutputMemberAliasList->DomainIndex = DomainIndex; OutputMemberAliasList->DomainCount = 0; // // Link the Member Alias List to the SAM Local Domain info // AliasInformation = &(SampDefinedDomains[ DomainIndex].AliasInformation); AliasInformation->MemberAliasList = OutputMemberAliasList; *MemberAliasList = OutputMemberAliasList; CreateMemberAliasListFinish: return(Status); CreateMemberAliasListError: *MemberAliasList = NULL; goto CreateMemberAliasListFinish; } NTSTATUS SampAlGrowMemberAliasList( IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, IN ULONG ExtraSpaceRequired ) /*++ Routine Description: This function grows a Member Alias List by at least the requested amount. Arguments: MemberAliasList - Pointer to pointer to the Member Alias List. ExtraSpaceRequired - Extra space needed in the Member Alias List. Return Values: --*/ { NTSTATUS Status; ULONG NewMaximumLengthMemberAliasList; PSAMP_AL_MEMBER_ALIAS_LIST OutputMemberAliasList = NULL; // // Calculate the new size of the Member Alias List needed. Round up to // a multiple of the granularity. // NewMaximumLengthMemberAliasList = (*MemberAliasList)->MaximumLength + ExtraSpaceRequired; NewMaximumLengthMemberAliasList += (SAMP_AL_MEMBER_ALIAS_LIST_DELTA - (ULONG) 1); NewMaximumLengthMemberAliasList &= ((ULONG)(~(SAMP_AL_MEMBER_ALIAS_LIST_DELTA - (ULONG) 1))); // // Allocate memory for the grown Member Alias List. // OutputMemberAliasList = MIDL_user_allocate( NewMaximumLengthMemberAliasList ); Status = STATUS_NO_MEMORY; if (OutputMemberAliasList == NULL) { goto GrowMemberAliasListError; } Status = STATUS_SUCCESS; // // Copy the old list to the new list and the the new maximum length. // Return pointer to new list. // RtlMoveMemory( OutputMemberAliasList, *MemberAliasList, (*MemberAliasList)->UsedLength ); OutputMemberAliasList->MaximumLength = NewMaximumLengthMemberAliasList; ASSERT(OutputMemberAliasList->MaximumLength >= OutputMemberAliasList->UsedLength); *MemberAliasList = OutputMemberAliasList; GrowMemberAliasListFinish: return(Status); GrowMemberAliasListError: SampAlInfoMakeInvalid( (*MemberAliasList)->DomainIndex ); goto GrowMemberAliasListFinish; } NTSTATUS SampAlBuildMemberAliasList( IN LONG DomainIndex ) /*++ Routine Description: This function builds the Member Alias List for the specified SAM Local Domain. For each Alias, its list of member Sids is read from backing storage and MemberDomain and MemberAccount blocks are created. Arguments: DomainIndex - Specifies the SAM Local Domain --*/ { NTSTATUS Status, EnumerationStatus; PSAMP_AL_MEMBER_ALIAS_LIST OutputMemberAliasList = NULL; PSAMP_AL_MEMBER_DOMAIN MemberDomain = NULL; PSAMP_AL_MEMBER_ACCOUNT MemberAccount = NULL; SAMPR_ULONG_ARRAY AliasRids; ULONG Rids[1], EnumerationContext, AliasCount, AliasRid; ULONG AliasIndex; PSAMP_OBJECT AccountContext = NULL; PSAMP_OBJECT AliasContext = NULL; SAMPR_PSID_ARRAY MemberSids; ULONG DomainSidMaximumLength = RtlLengthRequiredSid( 256 ); PSAMPR_ENUMERATION_BUFFER EnumerationBuffer = NULL; PSID DomainSid = NULL; PSID MemberSid = NULL; AliasRids.Element = Rids; // // Mark the Member Alias List invalid // SampAlInfoMakeInvalid( DomainIndex ); // // Allocate a scratch Domain Sid for splitting Sids. This has length // equal to maximum possible Sid length. // Status = STATUS_NO_MEMORY; DomainSid = MIDL_user_allocate( DomainSidMaximumLength ); if (DomainSid == NULL) { goto BuildMemberAliasListError; } Status = STATUS_SUCCESS; // // Create an empty Member Alias List and connect it to the // local SAM Domain. // Status = SampAlCreateMemberAliasList( DomainIndex, SAMP_AL_INITIAL_MEMBER_ALIAS_LIST_LENGTH, &OutputMemberAliasList ); if (!NT_SUCCESS(Status)) { goto BuildMemberAliasListError; } // // For each Alias in the SAM local domain, add its members to the // Alias List // EnumerationContext = 0; EnumerationStatus = STATUS_MORE_ENTRIES; // // It is currently necessary to set the Transaction Domain before // calling SampEnumerateAccountNames even though we're not modifying // anything. The is because called routine SampBuildAccountKeyName() // uses this information. // SampTransactionWithinDomain = FALSE; SampSetTransactionDomain( DomainIndex ); while (EnumerationStatus == STATUS_MORE_ENTRIES) { Status = SampEnumerateAccountNames( SampAliasObjectType, &EnumerationContext, &EnumerationBuffer, SAMP_AL_ENUM_PREFERRED_LENGTH, 0, &AliasCount, TRUE ); if (!NT_SUCCESS(Status)) { break; } EnumerationStatus = Status; for (AliasIndex = 0; AliasIndex < AliasCount; AliasIndex++) { AliasRid = EnumerationBuffer->Buffer[ AliasIndex ].RelativeId; // // Create a context for the account. // Status = SampCreateAccountContext( SampAliasObjectType, AliasRid, TRUE, TRUE, &AliasContext ); if (NT_SUCCESS(Status)) { // // There is a rather ugly feature of the way the DomainIndex // field is used in context handles while initializing. This // value is set to the count of SAM Local Domains! So, I am // setting it to the DomainIndex for the SAM Local Domain we're // initializing, since this AliasContext is used only by me. // AliasContext->DomainIndex = DomainIndex; Status = SampAlQueryMembersOfAlias( AliasContext, &MemberSids ); if (NT_SUCCESS(Status)) { // // Add these members to the Alias. No need to verify that // they are already present since we're loading the Member Alias // List from scratch. // Status = SampAlAddMembersToAlias( AliasContext, 0, &MemberSids ); } SampDeleteContext( AliasContext ); } if (!NT_SUCCESS(Status)) { break; } } // // Enumerate next set of Aliases // if (!NT_SUCCESS(Status)) { break; } // // Dispose of the Enumeration Buffer returned by SampEnumerateAccountNames // SamIFree_SAMPR_ENUMERATION_BUFFER( EnumerationBuffer ); EnumerationBuffer = NULL; } if (!NT_SUCCESS(Status)) { goto BuildMemberAliasListError; } // // Mark the Member Alias List valid // SampAlInfoMakeValid( DomainIndex ); BuildMemberAliasListFinish: SampTransactionWithinDomain = FALSE; return(Status); BuildMemberAliasListError: goto BuildMemberAliasListFinish; } BOOLEAN SampAlInfoIsValidForDomain( IN SAMPR_HANDLE DomainHandle ) /*++ Routine Description: This function checks whether Alias Information is valid for a specific SAM Local Domain Arguments: DomainHandle - Handle to SAM Local Domain Return Values: BOOLEAN - TRUE if Alias Information is valid. The Alias Information may be used in place of the backing storage to determine Alias membership FALSE if the Alias Information is not valid. The Alias Information does not exist, or is not reliable. --*/ { LONG DomainIndex; // // Get the Domain Index for the SAM Local Domain specified by DomainHandle. DomainIndex = ((PSAMP_OBJECT) DomainHandle)->DomainIndex; return(SampAlInfoIsValid( DomainIndex )); } BOOLEAN SampAlInfoIsValidForAlias( IN SAMPR_HANDLE AliasHandle ) /*++ Routine Description: This function checks whether Alias Information is valid for a specific Alias. The information is valid if it is valid for the SAM Local Domain containing the Alias. Arguments: AliasHandle - Handle to SAM Alias Return Values: BOOLEAN - TRUE if Alias Information is valid. The Alias Information may be used in place of the backing storage to determine Alias membership FALSE if the Alias Information is not valid. The Alias Information does not exist, or is not reliable. --*/ { LONG DomainIndex; // // Get the Domain Index for the SAM Local Domain specified by DomainHandle. DomainIndex = ((PSAMP_OBJECT) AliasHandle)->DomainIndex; return(SampAlInfoIsValid( DomainIndex )); }