/*++ Copyright (c) 1987-1996 Microsoft Corporation Module Name: rgroups.c Abstract: Routines to expand transitive group membership. Author: Mike Swift (mikesw) 8-May-1998 Environment: User mode only. Contains NT-specific code. Requires ANSI C extensions: slash-slash comments, long external names. Revision History: --*/ // // Common include files. // #include "logonsrv.h" // Include files common to entire service #pragma hdrstop PSID NlpCopySid( IN PSID Sid ) /*++ Routine Description: Given a SID allocatees space for a new SID from the LSA heap and copies the original SID. Arguments: Sid - The original SID. Return Value: Sid - Returns a pointer to a buffer allocated from the LsaHeap containing the resultant Sid. --*/ { PSID NewSid; ULONG Size; Size = RtlLengthSid( Sid ); if ((NewSid = MIDL_user_allocate( Size )) == NULL ) { return NULL; } if ( !NT_SUCCESS( RtlCopySid( Size, NewSid, Sid ) ) ) { MIDL_user_free( NewSid ); return NULL; } return NewSid; } NTSTATUS NlpBuildPacSidList( IN PNETLOGON_VALIDATION_SAM_INFO4 UserInfo, OUT PSAMPR_PSID_ARRAY Sids ) /*++ Routine Description: Given the validation information for a user, expands the group member- ships and user id into a list of sids Arguments: UserInfo - user's validation information Sids - receives an array of all the user's group sids and user id Return Value: STATUS_INSUFFICIENT_RESOURCES - there wasn't enough memory to create the list of sids. --*/ { NTSTATUS Status = STATUS_SUCCESS; NET_API_STATUS NetStatus; ULONG Size = 0, i; Sids->Count = 0; Sids->Sids = NULL; if (UserInfo->UserId != 0) { Size += sizeof(SAMPR_SID_INFORMATION); } Size += UserInfo->GroupCount * (ULONG)sizeof(SAMPR_SID_INFORMATION); // // If there are extra SIDs, add space for them // if (UserInfo->UserFlags & LOGON_EXTRA_SIDS) { Size += UserInfo->SidCount * (ULONG)sizeof(SAMPR_SID_INFORMATION); } Sids->Sids = (PSAMPR_SID_INFORMATION) MIDL_user_allocate( Size ); if ( Sids->Sids == NULL ) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlZeroMemory( Sids->Sids, Size ); // // Start copying SIDs into the structure // i = 0; // // If the UserId is non-zero, then it contians the users RID. // if ( UserInfo->UserId ) { NetStatus = NetpDomainIdToSid( UserInfo->LogonDomainId, UserInfo->UserId, (PSID *) &Sids->Sids[0].SidPointer ); if( NetStatus != ERROR_SUCCESS ) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } Sids->Count++; } // // Copy over all the groups passed as RIDs // for ( i=0; i < UserInfo->GroupCount; i++ ) { NetStatus = NetpDomainIdToSid( UserInfo->LogonDomainId, UserInfo->GroupIds[i].RelativeId, (PSID *) &Sids->Sids[Sids->Count].SidPointer ); if( NetStatus != ERROR_SUCCESS ) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } Sids->Count++; } // // Add in the extra SIDs // // // ???: no need to allocate these // if (UserInfo->UserFlags & LOGON_EXTRA_SIDS) { for ( i = 0; i < UserInfo->SidCount; i++ ) { Sids->Sids[Sids->Count].SidPointer = NlpCopySid( UserInfo->ExtraSids[i].Sid ); if (Sids->Sids[Sids->Count].SidPointer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } Sids->Count++; } } // // Deallocate any memory we've allocated // Cleanup: if (!NT_SUCCESS(Status)) { if (Sids->Sids != NULL) { for (i = 0; i < Sids->Count ;i++ ) { if (Sids->Sids[i].SidPointer != NULL) { MIDL_user_free(Sids->Sids[i].SidPointer); } } MIDL_user_free(Sids->Sids); Sids->Sids = NULL; Sids->Count = 0; } } return Status; } NTSTATUS NlpAddResourceGroupsToSamInfo ( IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel, IN OUT PNETLOGON_VALIDATION_SAM_INFO4 *ValidationInformation, IN PSAMPR_PSID_ARRAY ResourceGroups ) /*++ Routine Description: This function converts a NETLOGON_VALIDATION_SAM_INFO version 1, 2, or 4 to a NETLOGON_VALIDATION_SAM_INFO version 4 and optionally adds in an array of ResourceGroup sids. Since version 4 is a superset of the other two levels, the returned structure can be used even though one of the other info levels are needed. Arguments: ValidationLevel -- Specifies the level of information passed as input in ValidationInformation. Must be NetlogonValidationSamInfo or NetlogonValidationSamInfo2, NetlogonValidationSamInfo4 NetlogonValidationSamInfo4 is always returned on output. ValidationInformation -- Specifies the NETLOGON_VALIDATION_SAM_INFO to convert. ResourceGroups - The list of resource groups to add to the structure. If NULL, no resource groups are added. Return Value: STATUS_INSUFFICIENT_RESOURCES: not enough memory to allocate the new structure. --*/ { ULONG Length; PNETLOGON_VALIDATION_SAM_INFO4 SamInfo = *ValidationInformation; PNETLOGON_VALIDATION_SAM_INFO4 SamInfo4; PBYTE Where; ULONG Index; ULONG GroupIndex; ULONG ExtraSids = 0; // // Calculate the size of the new structure // Length = sizeof( NETLOGON_VALIDATION_SAM_INFO4 ) + SamInfo->GroupCount * sizeof(GROUP_MEMBERSHIP) + RtlLengthSid( SamInfo->LogonDomainId ); // // Add space for extra sids & resource groups // if ( ValidationLevel != NetlogonValidationSamInfo && (SamInfo->UserFlags & LOGON_EXTRA_SIDS) != 0 ) { for (Index = 0; Index < SamInfo->SidCount ; Index++ ) { Length += sizeof(NETLOGON_SID_AND_ATTRIBUTES) + RtlLengthSid(SamInfo->ExtraSids[Index].Sid); } ExtraSids += SamInfo->SidCount; } if ( ResourceGroups != NULL ) { for (Index = 0; Index < ResourceGroups->Count ; Index++ ) { Length += sizeof(NETLOGON_SID_AND_ATTRIBUTES) + RtlLengthSid(ResourceGroups->Sids[Index].SidPointer); } ExtraSids += ResourceGroups->Count; } // // Round up now to take into account the round up in the // middle of marshalling // Length = ROUND_UP_COUNT(Length, sizeof(WCHAR)) + SamInfo->LogonDomainName.Length + sizeof(WCHAR) + SamInfo->LogonServer.Length + sizeof(WCHAR) + SamInfo->EffectiveName.Length + sizeof(WCHAR) + SamInfo->FullName.Length + sizeof(WCHAR) + SamInfo->LogonScript.Length + sizeof(WCHAR) + SamInfo->ProfilePath.Length + sizeof(WCHAR) + SamInfo->HomeDirectory.Length + sizeof(WCHAR) + SamInfo->HomeDirectoryDrive.Length + sizeof(WCHAR); if ( ValidationLevel == NetlogonValidationSamInfo4 ) { Length += SamInfo->DnsLogonDomainName.Length + sizeof(WCHAR) + SamInfo->Upn.Length + sizeof(WCHAR); // // The ExpansionStrings may be used to transport byte aligned data Length = ROUND_UP_COUNT(Length, sizeof(WCHAR)) + SamInfo->ExpansionString1.Length + sizeof(WCHAR); Length = ROUND_UP_COUNT(Length, sizeof(WCHAR)) + SamInfo->ExpansionString2.Length + sizeof(WCHAR); Length = ROUND_UP_COUNT(Length, sizeof(WCHAR)) + SamInfo->ExpansionString3.Length + sizeof(WCHAR); Length = ROUND_UP_COUNT(Length, sizeof(WCHAR)) + SamInfo->ExpansionString4.Length + sizeof(WCHAR); Length = ROUND_UP_COUNT(Length, sizeof(WCHAR)) + SamInfo->ExpansionString5.Length + sizeof(WCHAR); Length = ROUND_UP_COUNT(Length, sizeof(WCHAR)) + SamInfo->ExpansionString6.Length + sizeof(WCHAR); Length = ROUND_UP_COUNT(Length, sizeof(WCHAR)) + SamInfo->ExpansionString7.Length + sizeof(WCHAR); Length = ROUND_UP_COUNT(Length, sizeof(WCHAR)) + SamInfo->ExpansionString8.Length + sizeof(WCHAR); Length = ROUND_UP_COUNT(Length, sizeof(WCHAR)) + SamInfo->ExpansionString9.Length + sizeof(WCHAR); Length = ROUND_UP_COUNT(Length, sizeof(WCHAR)) + SamInfo->ExpansionString10.Length + sizeof(WCHAR); } Length = ROUND_UP_COUNT( Length, sizeof(WCHAR) ); SamInfo4 = (PNETLOGON_VALIDATION_SAM_INFO4) MIDL_user_allocate( Length ); if ( !SamInfo4 ) { *ValidationInformation = NULL; return STATUS_INSUFFICIENT_RESOURCES; } // // First copy the whole structure, since most parts are the same // RtlCopyMemory( SamInfo4, SamInfo, sizeof(NETLOGON_VALIDATION_SAM_INFO)); RtlZeroMemory( &((LPBYTE)SamInfo4)[sizeof(NETLOGON_VALIDATION_SAM_INFO)], sizeof(NETLOGON_VALIDATION_SAM_INFO4) - sizeof(NETLOGON_VALIDATION_SAM_INFO) ); // // Copy all the variable length data // Where = (PBYTE) (SamInfo4 + 1); RtlCopyMemory( Where, SamInfo->GroupIds, SamInfo->GroupCount * sizeof( GROUP_MEMBERSHIP) ); SamInfo4->GroupIds = (PGROUP_MEMBERSHIP) Where; Where += SamInfo->GroupCount * sizeof( GROUP_MEMBERSHIP ); // // Copy the extra groups // if (ExtraSids != 0) { ULONG SidLength; SamInfo4->ExtraSids = (PNETLOGON_SID_AND_ATTRIBUTES) Where; Where += sizeof(NETLOGON_SID_AND_ATTRIBUTES) * ExtraSids; GroupIndex = 0; if ( ValidationLevel != NetlogonValidationSamInfo && (SamInfo->UserFlags & LOGON_EXTRA_SIDS) != 0 ) { for (Index = 0; Index < SamInfo->SidCount ; Index++ ) { SamInfo4->ExtraSids[GroupIndex].Attributes = SamInfo->ExtraSids[Index].Attributes; SamInfo4->ExtraSids[GroupIndex].Sid = (PSID) Where; SidLength = RtlLengthSid(SamInfo->ExtraSids[Index].Sid); RtlCopyMemory( Where, SamInfo->ExtraSids[Index].Sid, SidLength ); Where += SidLength; GroupIndex++; } } // // Add the resource groups // if ( ResourceGroups != NULL ) { for (Index = 0; Index < ResourceGroups->Count ; Index++ ) { SamInfo4->ExtraSids[GroupIndex].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT; SamInfo4->ExtraSids[GroupIndex].Sid = (PSID) Where; SidLength = RtlLengthSid(ResourceGroups->Sids[Index].SidPointer); RtlCopyMemory( Where, ResourceGroups->Sids[Index].SidPointer, SidLength ); Where += SidLength; GroupIndex++; } } SamInfo4->SidCount = GroupIndex; NlAssert(GroupIndex == ExtraSids); } RtlCopyMemory( Where, SamInfo->LogonDomainId, RtlLengthSid( SamInfo->LogonDomainId ) ); SamInfo4->LogonDomainId = (PSID) Where; Where += RtlLengthSid( SamInfo->LogonDomainId ); // // Copy the WCHAR-aligned data // Where = ROUND_UP_POINTER(Where, sizeof(WCHAR) ); NlpPutString( &SamInfo4->EffectiveName, &SamInfo->EffectiveName, &Where ); NlpPutString( &SamInfo4->FullName, &SamInfo->FullName, &Where ); NlpPutString( &SamInfo4->LogonScript, &SamInfo->LogonScript, &Where ); NlpPutString( &SamInfo4->ProfilePath, &SamInfo->ProfilePath, &Where ); NlpPutString( &SamInfo4->HomeDirectory, &SamInfo->HomeDirectory, &Where ); NlpPutString( &SamInfo4->HomeDirectoryDrive, &SamInfo->HomeDirectoryDrive, &Where ); NlpPutString( &SamInfo4->LogonServer, &SamInfo->LogonServer, &Where ); NlpPutString( &SamInfo4->LogonDomainName, &SamInfo->LogonDomainName, &Where ); if ( ValidationLevel == NetlogonValidationSamInfo4 ) { NlpPutString( &SamInfo4->DnsLogonDomainName, &SamInfo->DnsLogonDomainName, &Where ); NlpPutString( &SamInfo4->Upn, &SamInfo->Upn, &Where ); NlpPutString( &SamInfo4->ExpansionString1, &SamInfo->ExpansionString1, &Where ); Where = ROUND_UP_POINTER(Where, sizeof(WCHAR) ); NlpPutString( &SamInfo4->ExpansionString2, &SamInfo->ExpansionString2, &Where ); Where = ROUND_UP_POINTER(Where, sizeof(WCHAR) ); NlpPutString( &SamInfo4->ExpansionString3, &SamInfo->ExpansionString3, &Where ); Where = ROUND_UP_POINTER(Where, sizeof(WCHAR) ); NlpPutString( &SamInfo4->ExpansionString4, &SamInfo->ExpansionString4, &Where ); Where = ROUND_UP_POINTER(Where, sizeof(WCHAR) ); NlpPutString( &SamInfo4->ExpansionString5, &SamInfo->ExpansionString5, &Where ); Where = ROUND_UP_POINTER(Where, sizeof(WCHAR) ); NlpPutString( &SamInfo4->ExpansionString6, &SamInfo->ExpansionString6, &Where ); Where = ROUND_UP_POINTER(Where, sizeof(WCHAR) ); NlpPutString( &SamInfo4->ExpansionString7, &SamInfo->ExpansionString7, &Where ); Where = ROUND_UP_POINTER(Where, sizeof(WCHAR) ); NlpPutString( &SamInfo4->ExpansionString8, &SamInfo->ExpansionString8, &Where ); Where = ROUND_UP_POINTER(Where, sizeof(WCHAR) ); NlpPutString( &SamInfo4->ExpansionString9, &SamInfo->ExpansionString9, &Where ); Where = ROUND_UP_POINTER(Where, sizeof(WCHAR) ); NlpPutString( &SamInfo4->ExpansionString10, &SamInfo->ExpansionString10, &Where ); Where = ROUND_UP_POINTER(Where, sizeof(WCHAR) ); } MIDL_user_free(SamInfo); *ValidationInformation = SamInfo4; return STATUS_SUCCESS; } NTSTATUS NlpExpandResourceGroupMembership( IN NETLOGON_VALIDATION_INFO_CLASS ValidationLevel, IN OUT PNETLOGON_VALIDATION_SAM_INFO4 * UserInfo, IN PDOMAIN_INFO DomainInfo ) /*++ Routine Description: Given the validation information for a user, expands the group member- ships and user id into a list of sids Arguments: ValidationLevel -- Specifies the level of information passed as input in UserInfo. Must be NetlogonValidationSamInfo or NetlogonValidationSamInfo2, NetlogonValidationSamInfo4 NetlogonValidationSamInfo4 is always returned on output. UserInfo - user's validation information This structure is updated to include the resource groups that the user is a member of DomainInfo - Structure identifying the hosted domain used to determine the group membership. Return Value: STATUS_INSUFFICIENT_RESOURCES - there wasn't enough memory to create the list of sids. --*/ { NTSTATUS Status = STATUS_SUCCESS; SAMPR_PSID_ARRAY SidList = {0}; PSAMPR_PSID_ARRAY ResourceGroups = NULL; ULONG Index; Status = NlpBuildPacSidList( *UserInfo, &SidList ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Call SAM to get the sids // Status = SamIGetResourceGroupMembershipsTransitive( DomainInfo->DomSamAccountDomainHandle, &SidList, 0, // no flags &ResourceGroups ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Build a new validation information structure // if (ResourceGroups->Count != 0) { Status = NlpAddResourceGroupsToSamInfo( ValidationLevel, UserInfo, ResourceGroups ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } Cleanup: SamIFreeSidArray( ResourceGroups ); if (SidList.Sids != NULL) { for (Index = 0; Index < SidList.Count ;Index++ ) { if (SidList.Sids[Index].SidPointer != NULL) { MIDL_user_free(SidList.Sids[Index].SidPointer); } } MIDL_user_free(SidList.Sids); } return(Status); }