/*++ Copyright (c) 1991 Microsoft Corporation Module Name: groupp.c Abstract: Private functions for supporting NetGroup API Author: Cliff Van Dyke (cliffv) 06-Mar-1991 Environment: User mode only. Contains NT-specific code. Requires ANSI C extensions: slash-slash comments, long external names. Revision History: 17-Apr-1991 (cliffv) Incorporated review comments. 20-Jan-1992 (madana) Sundry API changes --*/ #include #include #include #undef DOMAIN_ALL_ACCESS // defined in both ntsam.h and ntwinapi.h #include #include #define NOMINMAX // Avoid redefinition of min and max in stdlib.h #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include NET_API_STATUS GrouppChangeMember( IN LPCWSTR ServerName OPTIONAL, IN LPCWSTR GroupName, IN LPCWSTR UserName, IN BOOL AddMember ) /*++ Routine Description: Common routine to add or remove a member from a group Arguments: ServerName - A pointer to a string containing the name of the remote server on which the function is to execute. A NULL pointer or string specifies the local machine. GroupName - Name of the group to change membership of. UserName - Name of the user to change membership of. AddMember - True to ADD the user to the group. Return Value: Error code for the operation. --*/ { NET_API_STATUS NetStatus; NTSTATUS Status; SAM_HANDLE SamServerHandle = NULL; SAM_HANDLE DomainHandle = NULL; SAM_HANDLE GroupHandle = NULL; // // Variables for converting names to relative IDs // UNICODE_STRING NameString; PULONG RelativeId = NULL; PSID_NAME_USE NameUse = NULL; // // Connect to the SAM server // NetStatus = UaspOpenSam( ServerName, FALSE, // Don't try null session &SamServerHandle ); if ( NetStatus != NERR_Success ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppChangeMember: Cannot UaspOpenSam %ld\n", NetStatus )); } goto Cleanup; } // // Open the Domain // NetStatus = UaspOpenDomain( SamServerHandle, DOMAIN_LOOKUP, TRUE, // Account Domain &DomainHandle, NULL); // DomainId if ( NetStatus != NERR_Success ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppChangeMember: UaspOpenDomain returned %ld\n", NetStatus )); } goto Cleanup; } // // Open the group // NetStatus = GrouppOpenGroup( DomainHandle, AddMember ? GROUP_ADD_MEMBER : GROUP_REMOVE_MEMBER, GroupName, &GroupHandle, NULL ); // Relative Id if ( NetStatus != NERR_Success ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppChangeMember: GrouppOpenGroup returned %ld\n", NetStatus )); } goto Cleanup; } // // Convert User name to relative ID. // RtlInitUnicodeString( &NameString, UserName ); Status = SamLookupNamesInDomain( DomainHandle, 1, &NameString, &RelativeId, &NameUse ); if ( !NT_SUCCESS(Status) ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppChangeMember: SamLookupNamesInDomain returned %lX\n", Status )); } if ( Status == STATUS_NONE_MAPPED ) { NetStatus = NERR_UserNotFound; } else { NetStatus = NetpNtStatusToApiStatus( Status ); } goto Cleanup; } if ( *NameUse != SidTypeUser ) { NetStatus = NERR_UserNotFound; goto Cleanup; } // // Add the user as a member of the group. // // SE_GROUP_MANDATORY might be conflict with the attributes of the group, so // try that attribute both ways. // if ( AddMember ) { Status = SamAddMemberToGroup( GroupHandle, *RelativeId, SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED ); if ( Status == STATUS_INVALID_GROUP_ATTRIBUTES ) { Status = SamAddMemberToGroup( GroupHandle, *RelativeId, SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED ); } // // Delete the user as a member of the group // } else { Status = SamRemoveMemberFromGroup( GroupHandle, *RelativeId); } IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppChangeMember: SamAdd(orRemove)MemberFromGroup returned %lX\n", Status )); } if ( !NT_SUCCESS(Status) ) { NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } NetStatus = NERR_Success; // // Clean up. // Cleanup: if ( RelativeId != NULL ) { Status = SamFreeMemory( RelativeId ); NetpAssert( NT_SUCCESS(Status) ); } if ( NameUse != NULL ) { Status = SamFreeMemory( NameUse ); NetpAssert( NT_SUCCESS(Status) ); } if ( GroupHandle != NULL ) { (VOID) SamCloseHandle( GroupHandle ); } UaspCloseDomain( DomainHandle ); if ( SamServerHandle != NULL ) { (VOID) SamCloseHandle( SamServerHandle ); } return NetStatus; } // GrouppChangeMember NET_API_STATUS GrouppGetInfo( IN SAM_HANDLE DomainHandle, IN ULONG RelativeId, IN DWORD Level, OUT PVOID *Buffer ) /*++ Routine Description: Internal routine to get group information Arguments: DomainHandle - Supplies the Handle of the domain the group is in. RelativeId - Supplies the relative ID of the group to open. Level - Level of information required. 0, 1 and 2 are valid. Buffer - Returns a pointer to the return information structure. Caller must deallocate buffer using NetApiBufferFree. Return Value: Error code for the operation. --*/ { NET_API_STATUS NetStatus; NTSTATUS Status; SAM_HANDLE GroupHandle = NULL; GROUP_GENERAL_INFORMATION *GroupGeneral = NULL; PVOID lastVarData; DWORD BufferSize; DWORD FixedSize; PSID GroupSid = NULL; ULONG RidToReturn = RelativeId; PGROUP_INFO_0 grpi0; // // Validate the level // if ( Level == 2 ) { ULONG Mode; Status = SamGetCompatibilityMode(DomainHandle, &Mode); if (!NT_SUCCESS(Status)) { NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } switch (Mode) { case SAM_SID_COMPATIBILITY_STRICT: NetStatus = ERROR_NOT_SUPPORTED; goto Cleanup; case SAM_SID_COMPATIBILITY_LAX: RidToReturn = 0; break; } } // // Open the group // Status = SamOpenGroup( DomainHandle, GROUP_READ_INFORMATION, RelativeId, &GroupHandle); if ( !NT_SUCCESS(Status) ) { NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } // // Get the information about the group. // Status = SamQueryInformationGroup( GroupHandle, GroupReplicationInformation, (PVOID *)&GroupGeneral); if ( ! NT_SUCCESS( Status ) ) { NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } // // Obtain the group's sid // if ( Level == 3 ) { NetStatus = NetpSamRidToSid(DomainHandle, RelativeId, &GroupSid); if ( NetStatus != NERR_Success ) { goto Cleanup; } } // // Figure out how big the return buffer needs to be // switch ( Level ) { case 0: FixedSize = sizeof( GROUP_INFO_0 ); BufferSize = FixedSize + GroupGeneral->Name.Length + sizeof(WCHAR); break; case 1: FixedSize = sizeof( GROUP_INFO_1 ); BufferSize = FixedSize + GroupGeneral->Name.Length + sizeof(WCHAR) + GroupGeneral->AdminComment.Length + sizeof(WCHAR); break; case 2: FixedSize = sizeof( GROUP_INFO_2 ); BufferSize = FixedSize + GroupGeneral->Name.Length + sizeof(WCHAR) + GroupGeneral->AdminComment.Length + sizeof(WCHAR); break; case 3: FixedSize = sizeof( GROUP_INFO_3 ); BufferSize = FixedSize + GroupGeneral->Name.Length + sizeof(WCHAR) + GroupGeneral->AdminComment.Length + sizeof(WCHAR) + RtlLengthSid(GroupSid); break; default: NetStatus = ERROR_INVALID_LEVEL; goto Cleanup; } // // Allocate the return buffer. // BufferSize = ROUND_UP_COUNT( BufferSize, ALIGN_DWORD ); *Buffer = MIDL_user_allocate( BufferSize ); if (*Buffer == NULL ) { NetStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } lastVarData = (PBYTE) ((LPBYTE)(*Buffer)) + BufferSize; // // Fill the name into the return buffer. // NetpAssert( offsetof( GROUP_INFO_0, grpi0_name ) == offsetof( GROUP_INFO_1, grpi1_name ) ); NetpAssert( offsetof( GROUP_INFO_1, grpi1_name ) == offsetof( GROUP_INFO_2, grpi2_name ) ); NetpAssert( offsetof( GROUP_INFO_2, grpi2_name ) == offsetof( GROUP_INFO_3, grpi3_name ) ); NetpAssert( offsetof( GROUP_INFO_1, grpi1_comment ) == offsetof( GROUP_INFO_2, grpi2_comment ) ); NetpAssert( offsetof( GROUP_INFO_2, grpi2_comment ) == offsetof( GROUP_INFO_3, grpi3_comment ) ); grpi0 = ((PGROUP_INFO_0)*Buffer); // // Fill in the return buffer. // switch ( Level ) { case 3: { PGROUP_INFO_3 grpi3 = ((PGROUP_INFO_3)grpi0); NetpAssert( NULL != GroupSid ); if ( !NetpCopyDataToBuffer( (LPBYTE) GroupSid, RtlLengthSid(GroupSid), ((LPBYTE)(*Buffer)) + FixedSize, (PBYTE*) &lastVarData, (LPBYTE *)&grpi3->grpi3_group_sid, ALIGN_DWORD ) ) { NetStatus = NERR_InternalError; goto Cleanup; } ((PGROUP_INFO_3)grpi3)->grpi3_attributes = GroupGeneral->Attributes; // // Fall through to the next level // } case 2: // // copy info level 2 only fields // if ( Level == 2 ) { ((PGROUP_INFO_2)grpi0)->grpi2_group_id = RidToReturn; ((PGROUP_INFO_2)grpi0)->grpi2_attributes = GroupGeneral->Attributes; } /* FALL THROUGH FOR OTHER FIELDS */ case 1: // // copy fields common to info level 1 and 2. // if ( !NetpCopyStringToBuffer( GroupGeneral->AdminComment.Buffer, GroupGeneral->AdminComment.Length/sizeof(WCHAR), ((LPBYTE)(*Buffer)) + FixedSize, (LPWSTR*)&lastVarData, &((PGROUP_INFO_1)grpi0)->grpi1_comment ) ) { NetStatus = NERR_InternalError; goto Cleanup; } /* FALL THROUGH FOR NAME FIELD */ case 0: // // copy common field (name field) in the buffer. // if ( !NetpCopyStringToBuffer( GroupGeneral->Name.Buffer, GroupGeneral->Name.Length/sizeof(WCHAR), ((LPBYTE)(*Buffer)) + FixedSize, (LPWSTR*)&lastVarData, &grpi0->grpi0_name ) ) { NetStatus = NERR_InternalError; goto Cleanup; } break; default: NetStatus = ERROR_INVALID_LEVEL; goto Cleanup; } NetStatus = NERR_Success; // // Cleanup and return. // Cleanup: if ( GroupGeneral ) { Status = SamFreeMemory( GroupGeneral ); NetpAssert( NT_SUCCESS(Status) ); } if ( GroupHandle ) { (VOID) SamCloseHandle( GroupHandle ); } if ( GroupSid ) { NetpMemoryFree( GroupSid ); } IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppGetInfo: returns %ld\n", NetStatus )); } return NetStatus; } // GrouppGetInfo NET_API_STATUS GrouppOpenGroup( IN SAM_HANDLE DomainHandle, IN ACCESS_MASK DesiredAccess, IN LPCWSTR GroupName, OUT PSAM_HANDLE GroupHandle OPTIONAL, OUT PULONG RelativeId OPTIONAL ) /*++ Routine Description: Open a Sam Group by Name Arguments: DomainHandle - Supplies the Domain Handle. DesiredAccess - Supplies access mask indicating desired access to group. GroupName - Group name of the group. GroupHandle - Returns a handle to the group. If NULL, group is not actually opened (merely the relative ID is returned). RelativeId - Returns the relative ID of the group. If NULL the relative Id is not returned. Return Value: Error code for the operation. --*/ { NTSTATUS Status; NET_API_STATUS NetStatus; // // Variables for converting names to relative IDs // UNICODE_STRING NameString; PSID_NAME_USE NameUse; PULONG LocalRelativeId; RtlInitUnicodeString( &NameString, GroupName ); // // Convert group name to relative ID. // Status = SamLookupNamesInDomain( DomainHandle, 1, &NameString, &LocalRelativeId, &NameUse ); if ( !NT_SUCCESS(Status) ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppOpenGroup: %wZ: SamLookupNamesInDomain %lX\n", &NameString, Status )); } return NetpNtStatusToApiStatus( Status ); } if ( *NameUse != SidTypeGroup ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppOpenGroup: %wZ: Name is not a group %ld\n", &NameString, *NameUse )); } NetStatus = NERR_GroupNotFound; goto Cleanup; } // // Open the group // if ( GroupHandle != NULL ) { Status = SamOpenGroup( DomainHandle, DesiredAccess, *LocalRelativeId, GroupHandle); if ( !NT_SUCCESS(Status) ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppOpenGroup: %wZ: SamOpenGroup %lX\n", &NameString, Status )); } NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } } // // Return the relative Id if it's wanted. // if ( RelativeId != NULL ) { *RelativeId = *LocalRelativeId; } NetStatus = NERR_Success; // // Cleanup // Cleanup: if ( LocalRelativeId != NULL ) { Status = SamFreeMemory( LocalRelativeId ); NetpAssert( NT_SUCCESS(Status) ); } if ( NameUse != NULL ) { Status = SamFreeMemory( NameUse ); NetpAssert( NT_SUCCESS(Status) ); } return NetStatus; } // GrouppOpenGroup VOID GrouppRelocationRoutine( IN DWORD Level, IN OUT PBUFFER_DESCRIPTOR BufferDescriptor, IN PTRDIFF_T Offset ) /*++ Routine Description: Routine to relocate the pointers from the fixed portion of a NetGroupEnum enumeration buffer to the string portion of an enumeration buffer. It is called as a callback routine from NetpAllocateEnumBuffer when it re-allocates such a buffer. NetpAllocateEnumBuffer copied the fixed portion and string portion into the new buffer before calling this routine. Arguments: Level - Level of information in the buffer. BufferDescriptor - Description of the new buffer. Offset - Offset to add to each pointer in the fixed portion. Return Value: Returns the error code for the operation. --*/ { DWORD EntryCount; DWORD EntryNumber; DWORD FixedSize; IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppRelocationRoutine: entering\n" )); } // // Compute the number of fixed size entries // switch (Level) { case 0: FixedSize = sizeof(GROUP_INFO_0); break; case 1: FixedSize = sizeof(GROUP_INFO_1); break; case 2: FixedSize = sizeof(GROUP_INFO_2); break; default: NetpAssert( FALSE ); return; } EntryCount = ((DWORD)(BufferDescriptor->FixedDataEnd - BufferDescriptor->Buffer)) / FixedSize; // // Loop relocating each field in each fixed size structure // for ( EntryNumber=0; EntryNumberBuffer + FixedSize * EntryNumber; switch ( Level ) { case 2: case 1: RELOCATE_ONE( ((PGROUP_INFO_1)TheStruct)->grpi1_comment, Offset ); // // Drop through to case 0 // case 0: RELOCATE_ONE( ((PGROUP_INFO_0)TheStruct)->grpi0_name, Offset ); break; default: return; } } return; } // GrouppRelocationRoutine VOID GrouppMemberRelocationRoutine( IN DWORD Level, IN OUT PBUFFER_DESCRIPTOR BufferDescriptor, IN PTRDIFF_T Offset ) /*++ Routine Description: Routine to relocate the pointers from the fixed portion of a NetGroupGetUsers enumeration buffer to the string portion of an enumeration buffer. It is called as a callback routine from NetpAllocateEnumBuffer when it re-allocates such a buffer. NetpAllocateEnumBuffer copied the fixed portion and string portion into the new buffer before calling this routine. Arguments: Level - Level of information in the buffer. BufferDescriptor - Description of the new buffer. Offset - Offset to add to each pointer in the fixed portion. Return Value: Returns the error code for the operation. --*/ { DWORD EntryCount; DWORD EntryNumber; DWORD FixedSize; IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppMemberRelocationRoutine: entering\n" )); } // // Compute the number of fixed size entries // switch (Level) { case 0: FixedSize = sizeof(GROUP_USERS_INFO_0); break; case 1: FixedSize = sizeof(GROUP_USERS_INFO_1); break; default: NetpAssert( FALSE ); return; } EntryCount = ((DWORD)(BufferDescriptor->FixedDataEnd - BufferDescriptor->Buffer)) / FixedSize; // // Loop relocating each field in each fixed size structure // for ( EntryNumber=0; EntryNumberBuffer + FixedSize * EntryNumber; // // Both info levels only have one field to relocate // RELOCATE_ONE( ((PGROUP_USERS_INFO_0)TheStruct)->grui0_name, Offset ); } return; } // GrouppMemberRelocationRoutine NET_API_STATUS GrouppSetUsers ( IN LPCWSTR ServerName OPTIONAL, IN LPCWSTR GroupName, IN DWORD Level, IN LPBYTE Buffer, IN DWORD NewMemberCount, IN BOOL DeleteGroup ) /*++ Routine Description: Set the list of members of a group and optionally delete the group when finished. The members specified by "Buffer" are called new members. The current members of the group are called old members. Members which are on both the old and new list are common members. The SAM API allows only one member to be added or deleted at a time. This API allows all of the members of a group to be specified en-masse. This API is careful to always leave the group membership in the SAM database in a reasonable state. It does by mergeing the list of old and new members, then only changing those memberships which absolutely need changing. Group membership is restored to its previous state (if possible) if an error occurs during changing the group membership. Arguments: ServerName - A pointer to a string containing the name of the remote server on which the function is to execute. A NULL pointer or string specifies the local machine. GroupName - Name of the group to modify. Level - Level of information provided. Must be 0 or 1. Buffer - A pointer to the buffer containing an array of NewMemberCount the group membership information structures. NewMemberCount - Number of entries in Buffer. DeleteGroup - TRUE if the group is to be deleted after changing the membership. Return Value: Error code for the operation. --*/ { NET_API_STATUS NetStatus; NTSTATUS Status; SAM_HANDLE SamServerHandle = NULL; SAM_HANDLE DomainHandle = NULL; SAM_HANDLE GroupHandle = NULL; ACCESS_MASK DesiredAccess; // // Variables for dealing with old or new lists of members // PULONG NewRelativeIds = NULL; // Relative Ids of the new members PULONG OldRelativeIds = NULL; // Relative Ids of the old members PULONG OldAttributes = NULL; // Attributes of the old members PSID_NAME_USE NewNameUse = NULL;// Name usage of the new members PSID_NAME_USE OldNameUse = NULL;// Name usage of the old members PUNICODE_STRING NameStrings = NULL; // Names of a list of members ULONG OldMemberCount; // Number of current members in the group ULONG DefaultMemberAttributes; // Default attributes for new members DWORD FixedSize; // // Define an internal member list structure. // // The structure defines a list of new members to be added, members whose // attributes merely need to be changed, and members which // need to be deleted. The list is maintained in relative ID sorted // order. // struct _MEMBER_DESCRIPTION { struct _MEMBER_DESCRIPTION * Next; // Next entry in linked list; ULONG RelativeId; // Relative ID of this member enum _Action { // Action taken for this member AddMember, // Add Member to group RemoveMember, // Remove Member from group SetAttributesMember, // Change the Members attributes IgnoreMember // Ignore this member } Action; ULONG NewAttributes; // Attributes to set for the member BOOL Done; // True if this action has been taken ULONG OldAttributes; // Attributes to restore on a recovery } *MemberList = NULL , *CurEntry, **Entry; // // Connect to the SAM server // NetStatus = UaspOpenSam( ServerName, FALSE, // Don't try null session &SamServerHandle ); if ( NetStatus != NERR_Success ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppSetUsers: Cannot UaspOpenSam %ld\n", NetStatus )); } goto Cleanup; } // // Open the Domain // NetStatus = UaspOpenDomain( SamServerHandle, DOMAIN_LOOKUP, TRUE, // Account Domain &DomainHandle, NULL); // DomainId if ( NetStatus != NERR_Success ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppSetUsers: UaspOpenDomain returns %ld\n", NetStatus )); } goto Cleanup; } // // Open the group // DesiredAccess = GROUP_READ_INFORMATION | GROUP_LIST_MEMBERS | GROUP_ADD_MEMBER | GROUP_REMOVE_MEMBER; if ( DeleteGroup ) { NetpAssert( NewMemberCount == 0 ); DesiredAccess |= DELETE; } NetStatus = GrouppOpenGroup( DomainHandle, DesiredAccess, GroupName, &GroupHandle, NULL ); // Relative Id if ( NetStatus != NERR_Success ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppSetUsers: GrouppOpenGroup returns %ld\n", NetStatus )); } goto Cleanup; } // // Validate the level // switch (Level) { case 0: { // // Determine the attributes of the group as a whole. Use that // for deciding on the default attributes for new members. // PGROUP_ATTRIBUTE_INFORMATION Attributes; Status = SamQueryInformationGroup( GroupHandle, GroupAttributeInformation, (PVOID*)&Attributes ); if ( !NT_SUCCESS(Status) ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppSetUsers: SamQueryInformationGroup returns %lX\n", Status )); } NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } DefaultMemberAttributes = (Attributes->Attributes & SE_GROUP_MANDATORY) | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED ; FixedSize = sizeof( GROUP_USERS_INFO_0 ); Status = SamFreeMemory( Attributes ); NetpAssert( NT_SUCCESS(Status) ); break; } case 1: FixedSize = sizeof( GROUP_USERS_INFO_1 ); break; default: NetStatus = ERROR_INVALID_LEVEL; goto Cleanup; } // // Determine the Relative Id and usage of each of the new members. // if ( NewMemberCount > 0 ) { DWORD NewIndex; // Index to a new member PGROUP_USERS_INFO_0 grui0; // // Allocate a buffer big enough to contain all the string variables // for the new member names. // NameStrings = NetpMemoryAllocate( NewMemberCount * sizeof(UNICODE_STRING) ); if ( NameStrings == NULL ) { NetStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } // // Fill in the list of member name strings for each new member. // NetpAssert( offsetof( GROUP_USERS_INFO_0, grui0_name ) == offsetof( GROUP_USERS_INFO_1, grui1_name ) ); for ( NewIndex=0, grui0 = (PGROUP_USERS_INFO_0)Buffer; NewIndexgrui0_name); } // // Convert the member names to relative Ids. // Status = SamLookupNamesInDomain( DomainHandle, NewMemberCount, NameStrings, &NewRelativeIds, &NewNameUse ); if ( !NT_SUCCESS( Status )) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppSetUsers: SamLookupNamesInDomain returns %lX\n", Status )); } if ( Status == STATUS_NONE_MAPPED ) { NetStatus = NERR_UserNotFound; goto Cleanup; } NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } // // Build a member entry for each of the new members. // The list is maintained in RelativeId sorted order. // for ( NewIndex=0; NewIndexRelativeId < NewRelativeIds[NewIndex] ) { Entry = &( (*Entry)->Next ); } // // If this is not a duplicate entry, allocate a new member structure // and fill it in. // // Just ignore duplicate relative Ids. // if ( *Entry == NULL || (*Entry)->RelativeId > NewRelativeIds[NewIndex] ) { CurEntry = NetpMemoryAllocate( sizeof(struct _MEMBER_DESCRIPTION)); if ( CurEntry == NULL ) { NetStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } CurEntry->Next = *Entry; CurEntry->RelativeId = NewRelativeIds[NewIndex]; CurEntry->Action = AddMember; CurEntry->Done = FALSE; CurEntry->NewAttributes = ( Level == 1 ) ? ((PGROUP_USERS_INFO_1)Buffer)[NewIndex].grui1_attributes : DefaultMemberAttributes; *Entry = CurEntry; } } NetpMemoryFree( NameStrings ); NameStrings = NULL; Status = SamFreeMemory( NewRelativeIds ); NewRelativeIds = NULL; NetpAssert( NT_SUCCESS(Status) ); Status = SamFreeMemory( NewNameUse ); NewNameUse = NULL; NetpAssert( NT_SUCCESS(Status) ); } // // Determine the number of old members for this group and the // relative ID's of the old members. // Status = SamGetMembersInGroup( GroupHandle, &OldRelativeIds, &OldAttributes, &OldMemberCount ); if ( !NT_SUCCESS( Status ) ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppSetUsers: SamGetMembersInGroup returns %lX\n", Status )); } NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } // // If there are any old members, // Merge them into the list. // if ( OldMemberCount > 0 ) { ULONG OldIndex; // Index to current entry PUNICODE_STRING Names; // // Determine the usage for all the returned relative Ids. // Status = SamLookupIdsInDomain( DomainHandle, OldMemberCount, OldRelativeIds, &Names, &OldNameUse ); if ( !NT_SUCCESS( Status ) ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppSetUsers: SamLookupIdsInDomain returns %lX\n", Status )); } if ( Status == STATUS_NONE_MAPPED ) { NetStatus = NERR_InternalError ; goto Cleanup; } NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } Status = SamFreeMemory( Names ); // Don't need names at all NetpAssert( NT_SUCCESS(Status) ); // // Loop for each current member // for ( OldIndex=0; OldIndexRelativeId < OldRelativeIds[OldIndex] ) { Entry = &( (*Entry)->Next ); } // // If this entry is not already in the list, // this is a member which exists now but should be deleted. // if( *Entry == NULL || (*Entry)->RelativeId > OldRelativeIds[OldIndex]){ CurEntry = NetpMemoryAllocate(sizeof(struct _MEMBER_DESCRIPTION)); if ( CurEntry == NULL ) { NetStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } CurEntry->Next = *Entry; CurEntry->RelativeId = OldRelativeIds[OldIndex]; CurEntry->Action = RemoveMember; CurEntry->Done = FALSE; CurEntry->OldAttributes = OldAttributes[OldIndex]; *Entry = CurEntry; // // Handle the case where this member is already in the list // } else { // // Watch out for SAM returning the same member twice. // if ( (*Entry)->Action != AddMember ) { Status = NERR_InternalError; goto Cleanup; } // // If this is info level 1 and the requested attributes are // different than the current attributes, // Remember to change the attributes. // if ( Level == 1 && (*Entry)->NewAttributes != OldAttributes[OldIndex] ) { (*Entry)->OldAttributes = OldAttributes[OldIndex]; (*Entry)->Action = SetAttributesMember; // // This is either info level 0 or the level 1 attributes // are the same as the existing attributes. // } else { (*Entry)->Action = IgnoreMember; } } } } // // Loop through the list adding all new members. // We do this in a separate loop to minimize the damage that happens // should we get an error and not be able to recover. // for ( CurEntry = MemberList; CurEntry != NULL ; CurEntry=CurEntry->Next ) { if ( CurEntry->Action == AddMember ) { Status = SamAddMemberToGroup( GroupHandle, CurEntry->RelativeId, CurEntry->NewAttributes ); if ( !NT_SUCCESS( Status ) ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppSetUsers: SamAddMemberToGroup returns %lX\n", Status )); } NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } CurEntry->Done = TRUE; } } // // Loop through the list deleting all old members and changing the // attributes of all common members. // for ( CurEntry = MemberList; CurEntry != NULL ; CurEntry=CurEntry->Next ) { if ( CurEntry->Action == RemoveMember ) { Status = SamRemoveMemberFromGroup( GroupHandle, CurEntry->RelativeId); if ( !NT_SUCCESS( Status ) ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppSetUsers: SamRemoveMemberFromGroup returns %lX\n", Status )); } NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } } else if ( CurEntry->Action == SetAttributesMember ) { Status = SamSetMemberAttributesOfGroup( GroupHandle, CurEntry->RelativeId, CurEntry->NewAttributes); if ( !NT_SUCCESS( Status ) ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppSetUsers: SamSetMemberAttributesOfGroup returns %lX\n", Status )); } NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } } CurEntry->Done = TRUE; } // // Delete the group if requested to do so. // if ( DeleteGroup ) { Status = SamDeleteGroup( GroupHandle ); if ( !NT_SUCCESS( Status ) ) { IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppSetUsers: SamDeleteGroup returns %lX\n", Status )); } NetStatus = NetpNtStatusToApiStatus( Status ); // // Put the group memberships back the way they were. // goto Cleanup; } GroupHandle = NULL; } NetStatus = NERR_Success; // // Clean up. // Cleanup: // // Walk the member list cleaning up any damage we've done // for ( CurEntry = MemberList; CurEntry != NULL ; ) { struct _MEMBER_DESCRIPTION *DelEntry; if ( NetStatus != NERR_Success && CurEntry->Done ) { switch (CurEntry->Action) { case AddMember: Status = SamRemoveMemberFromGroup( GroupHandle, CurEntry->RelativeId ); NetpAssert( NT_SUCCESS(Status) ); break; case RemoveMember: Status = SamAddMemberToGroup( GroupHandle, CurEntry->RelativeId, CurEntry->OldAttributes ); NetpAssert( NT_SUCCESS(Status) ); break; case SetAttributesMember: Status = SamSetMemberAttributesOfGroup( GroupHandle, CurEntry->RelativeId, CurEntry->OldAttributes ); NetpAssert( NT_SUCCESS(Status) ); break; default: break; } } DelEntry = CurEntry; CurEntry = CurEntry->Next; NetpMemoryFree( DelEntry ); } if ( NameStrings != NULL ) { NetpMemoryFree( NameStrings ); } if (NewRelativeIds != NULL) { Status = SamFreeMemory( NewRelativeIds ); NetpAssert( NT_SUCCESS(Status) ); } if (NewNameUse != NULL) { Status = SamFreeMemory( NewNameUse ); NetpAssert( NT_SUCCESS(Status) ); } if (OldNameUse != NULL) { Status = SamFreeMemory( OldNameUse ); NetpAssert( NT_SUCCESS(Status) ); } if (OldRelativeIds != NULL) { Status = SamFreeMemory( OldRelativeIds ); NetpAssert( NT_SUCCESS(Status) ); } if (OldAttributes != NULL) { Status = SamFreeMemory( OldAttributes ); NetpAssert( NT_SUCCESS(Status) ); } if (GroupHandle != NULL) { (VOID) SamCloseHandle( GroupHandle ); } UaspCloseDomain( DomainHandle ); if ( SamServerHandle != NULL ) { (VOID) SamCloseHandle( SamServerHandle ); } IF_DEBUG( UAS_DEBUG_GROUP ) { NetpKdPrint(( "GrouppSetUsers: returns %ld\n", NetStatus )); } return NetStatus; } // GrouppSetUsers