Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1107 lines
32 KiB

//+------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation, 1994 - 1996.
//
// File: member.cxx
//
// Classes: CMemberCheck
//
// History: Nov-94 DaveMont Created.
//
//-------------------------------------------------------------------
#include <aclpch.hxx>
#pragma hdrstop
extern "C"
{
#include <ntprov.hxx>
#include <strings.h>
}
SID WORLD_SID = {SID_REVISION,
1,
SECURITY_WORLD_SID_AUTHORITY,
SECURITY_WORLD_RID};
#define MARTA_MAX_RECURSION_COUNT 256
//+---------------------------------------------------------------------------
//
// Member: CMemberCheck::Init, public
//
// Synopsis: initializes the class
//
// Arguments: none
//
// Returns: ERROR_SUCCESS -- Success
//
//----------------------------------------------------------------------------
DWORD CMemberCheck::Init()
{
acDebugOut((DEB_TRACE, "In CMemberCheck::Init\n"));
DWORD dwErr = ERROR_SUCCESS;
//
// get the local machine name
//
ULONG cSize = MAX_COMPUTERNAME_LENGTH + 1;
if(GetComputerName(_wszComputerName, &cSize) == FALSE)
{
dwErr = GetLastError();
}
acDebugOut((DEB_TRACE, "Out CMemberCheck::Init: %lu\n", dwErr));
return(dwErr);
}
//+---------------------------------------------------------------------------
//
// Member: CMemberCheck::IsMemberOf, public
//
// Synopsis: Checks if the current sid is a member of the input sid.
// The current sid is a part of our initialize TRUSTEE_NODE and
// the input sid is in our passed in TRUSTEE_NODE
//
// Arguments: [IN pTrusteeNode] -- Input TRUSTEE_NODE
// [OUT pfIsMemberOf] -- Where the results are returned.
// Is TRUE if the initialization
// SID is a member of the input
// SID.
//
// Returns: ERROR_SUCCESS -- Success
//
//----------------------------------------------------------------------------
DWORD CMemberCheck::IsMemberOf(IN PTRUSTEE_NODE pTrusteeNode,
OUT PBOOL pfIsMemberOf)
{
acDebugOut((DEB_TRACE, "In CMemberCheck::IsMemberOf\n"));
DWORD dwErr = ERROR_SUCCESS;
if(RtlEqualSid(pTrusteeNode->pSid, &WORLD_SID) == TRUE)
{
*pfIsMemberOf = TRUE;
}
else if(RtlEqualSid(_pCurrentNode->pSid, pTrusteeNode->pSid) == TRUE)
{
//
// If they're the same sid, they're bound to be a member of eachother
//
*pfIsMemberOf = TRUE;
}
else if(pTrusteeNode->SidType == SidTypeGroup)
{
//
// We'll have to look it up, and check for group membership
//
dwErr = CheckGroup(pTrusteeNode->pSid,
pfIsMemberOf,
1);
}
else if(pTrusteeNode->SidType == SidTypeAlias)
{
//
// We'll have to expand the alias and look
//
dwErr = CheckAlias(pTrusteeNode->pSid,
pfIsMemberOf,
1);
}
else
{
//
// Well, here's something we don't know how to handle
//
*pfIsMemberOf = FALSE;
}
acDebugOut((DEB_TRACE,
"Out CMemberCheck::IsMemberOf(%lx)(%d)\n",
dwErr,
*pfIsMemberOf));
return(dwErr);
}
//+---------------------------------------------------------------------------
//
// Member: GetDomainName, private
//
// Synopsis: gets the domain name for the given sid
//
// Arguments: [IN pSid] -- Input Sid
// [OUT ppwszDomainName] -- To return the domain name
//
// Returns: ERROR_SUCCESS -- Success
//
//----------------------------------------------------------------------------
DWORD
GetDomainName(
IN PSID pSid,
OUT PWSTR *ppwszDomainName
)
{
LPWSTR Name = NULL;
LPWSTR RefName = NULL;
SID_NAME_USE SidType;
//
// Lookup the sid and get the name for the user.
//
DWORD dwErr = AccLookupAccountName(
NULL,
pSid,
ppwszDomainName,
&RefName,
&SidType
);
if (dwErr == ERROR_SUCCESS)
{
//
// The returned string is of the type "Domain\\User". Strip off the
// backslash to get the name of the domain.
//
PWSTR pwszTmp = wcschr(*ppwszDomainName, L'\\');
if(pwszTmp != NULL)
{
*pwszTmp = L'\0';
}
//
// We do not need this one. Free it.
//
AccFree(RefName);
}
return dwErr;
}
//+---------------------------------------------------------------------------
//
// Member: CMemberCheck::GetDomainInfo, private
//
// Synopsis: gets the domain handle for the domain of the specified account
//
// Arguments: [IN pSid] -- Input Sid
//
// Returns: ERROR_SUCCESS -- Success
// ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed
//
//----------------------------------------------------------------------------
DWORD CMemberCheck::GetDomainInfo(IN PSID pSid)
{
acDebugOut((DEB_TRACE, "In CMemberCheck::GetDomainInfo\n"));
NTSTATUS Status = STATUS_SUCCESS;
BOOL fSidMatched = FALSE;
PISID pCheckSid;
DWORD dwErr = LoadDLLFuncTable();
if(dwErr != ERROR_SUCCESS)
{
return(dwErr);
}
//
// allocate a spare sid so we can grovel in it for the domain id
//
pCheckSid = (PISID)AccAlloc(RtlLengthSid(pSid));
if(pCheckSid != NULL)
{
Status = RtlCopySid(RtlLengthSid(pSid),
pCheckSid,
pSid);
if(NT_SUCCESS(Status))
{
//
// make it the domain identifier
//
if(pCheckSid->SubAuthorityCount > 1)
{
--(pCheckSid->SubAuthorityCount);
}
//
// if we already have a domain sid, check it against the input sid
//
if(_pDomainSid != NULL)
{
if(RtlEqualSid(pCheckSid, _pDomainSid))
{
//
// in this case we are all done.
//
AccFree(pCheckSid);
pCheckSid = NULL;
fSidMatched = TRUE;
}
}
if ( fSidMatched == FALSE)
{
PDOMAIN_CONTROLLER_INFO DomainInfo = NULL;
//
// Free the current sid
//
AccFree(_pDomainSid);
_pDomainSid = NULL;
if(_hDomain)
{
(*DLLFuncs.PSamCloseHandle)(_hDomain);
_hDomain = NULL;
}
SAM_HANDLE hSam = NULL;
PWSTR pwszDomainName = NULL;
dwErr = GetDomainName(pSid, &pwszDomainName);
if(dwErr == ERROR_SUCCESS)
{
//
// If we know the domain name of the input sid, check for
// well known, and local names
//
if(pwszDomainName != NULL)
{
WCHAR wszStringBuffer[256];
if (!LoadString(ghDll,
ACCPROV_BUILTIN,
wszStringBuffer,
sizeof( wszStringBuffer ) / sizeof( WCHAR ))
) {
wszStringBuffer[0] = L'\0';
}
if(_wcsicmp(pwszDomainName,
wszStringBuffer) != 0)
{
if (!LoadString(ghDll,
ACCPROV_NTAUTHORITY,
wszStringBuffer,
sizeof( wszStringBuffer ) / sizeof( WCHAR ))
) {
wszStringBuffer[0] = L'\0';
}
if(_wcsicmp(pwszDomainName,
wszStringBuffer) != 0)
{
if(_wcsicmp(_wszComputerName,
pwszDomainName) != 0)
{
dwErr = DsGetDcName(NULL, pwszDomainName, NULL, NULL, 0, &DomainInfo);
}
}
}
}
if(dwErr == ERROR_SUCCESS)
{
UNICODE_STRING UnicodeString = {0};
if (DomainInfo != NULL)
{
RtlInitUnicodeString(&UnicodeString, DomainInfo->DomainControllerName);
}
OBJECT_ATTRIBUTES ObjAttrib;
Status = (*DLLFuncs.PSamConnect)(
DomainInfo ? &UnicodeString : NULL,
&hSam,
GENERIC_EXECUTE,
&ObjAttrib);
if(NT_SUCCESS(Status))
{
//
// open the domain
//
Status = (*DLLFuncs.PSamOpenDomain)(
hSam,
GENERIC_READ | DOMAIN_LOOKUP,
pCheckSid,
&_hDomain);
(*DLLFuncs.PSamCloseHandle)(hSam);
}
dwErr = RtlNtStatusToDosError(Status);
}
if (DomainInfo)
{
NetApiBufferFree(DomainInfo);
DomainInfo = NULL;
}
AccFree(pwszDomainName);
if(dwErr == ERROR_SUCCESS)
{
//
// We have a new DomainSid
//
_pDomainSid = pCheckSid;
pCheckSid = NULL;
}
}
}
}
else
{
dwErr = RtlNtStatusToDosError(Status);
}
}
else
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
}
if (pCheckSid != NULL)
{
AccFree(pCheckSid);
}
acDebugOut((DEB_TRACE, "Out CMemberCheck::_GetDomainInfo: %lu\n",
dwErr));
return(dwErr);
}
//+---------------------------------------------------------------------------
//
// Member: CMemberCheck::CheckDomainUsers, private
//
// Synopsis: Checks if the Group is Domain Users and if the Users Domain sid
// is the same as that of the group.
//
// Arguments: [IN pSid] -- Input Sid
// [OUT pfIsMemberOf] -- Where the results are returned.
// Is TRUE if the current SID
// is a member of the input SID
// group.
// [OUT pbQuitEarly] -- Is TRUE if the group is Domain Users
//
// Returns: ERROR_SUCCESS -- Success
//
//----------------------------------------------------------------------------
DWORD CMemberCheck::CheckDomainUsers(IN PSID pSid,
OUT PBOOL pfIsMemberOf,
OUT PBOOL pbQuitEarly)
{
DWORD Rid = ((PISID) pSid)->SubAuthority[((PISID) pSid)->SubAuthorityCount-1];
BOOL b = FALSE;
BOOL bEqual = FALSE;
SAM_HANDLE hUser = 0;
PUCHAR Buffer = NULL;
NTSTATUS status = STATUS_SUCCESS;
if (Rid != DOMAIN_GROUP_RID_USERS)
{
//
// No need to do anything. Just return.
//
return ERROR_SUCCESS;
}
//
// Since it is domain users we will quit early.
//
*pbQuitEarly = TRUE;
b = EqualDomainSid(pSid, _pCurrentNode->pSid, &bEqual);
//
// ERROR_NON_DOMAIN_SID is returned for wellknown sids.
// It is ok to ignore this error and continue.
//
if ((b == FALSE) && (GetLastError() != ERROR_NON_DOMAIN_SID))
{
return GetLastError();
}
//
// If the domains do not match, return FALSE.
//
if (!bEqual)
{
return ERROR_SUCCESS;
}
//
// Get the Rid for the user.
//
DWORD dwRelativeId = *RtlSubAuthoritySid(
_pCurrentNode->pSid,
*RtlSubAuthorityCountSid(_pCurrentNode->pSid) - 1
);
//
// Open the user for read.
//
status = SamOpenUser(
_hDomain,
USER_READ_GENERAL,
dwRelativeId,
&hUser
);
if (!NT_SUCCESS(status))
{
return RtlNtStatusToDosError(status);
}
//
// Get the primary group information for the user.
//
status = SamQueryInformationUser(
hUser,
UserPrimaryGroupInformation,
(PVOID *) &Buffer
);
SamCloseHandle(hUser);
if (!NT_SUCCESS(status))
{
return RtlNtStatusToDosError(status);
}
//
// If the primary group matched then return TRUE.
//
if (DOMAIN_GROUP_RID_USERS == ((USER_PRIMARY_GROUP_INFORMATION *) Buffer)->PrimaryGroupId)
{
*pfIsMemberOf = TRUE;
}
(VOID) SamFreeMemory(Buffer);
return ERROR_SUCCESS;
}
//+---------------------------------------------------------------------------
//
// Member: CMemberCheck::CheckGroup, private
//
// Synopsis: Checks if the objects account is in the specifed group
// account
//
// Arguments: [IN pSid] -- Input Sid
// [OUT pfIsMemberOf] -- Where the results are returned.
// Is TRUE if the current SID
// is a member of the input SID
// group.
// [IN RecursionCount] -- Recursion level
//
// Returns: ERROR_SUCCESS -- Success
//
//----------------------------------------------------------------------------
DWORD CMemberCheck::CheckGroup(IN PSID pSid,
OUT PBOOL pfIsMemberOf,
IN DWORD RecursionCount)
{
acDebugOut((DEB_TRACE, "In CMemberCheck::CheckGroup\n"));
NTSTATUS Status;
SAM_HANDLE hSam = NULL;
ULONG rid = ((PISID)(pSid))->SubAuthority[
((PISID)(pSid))->SubAuthorityCount-1];
BYTE LocalBuffer[8 + 4 * SID_MAX_SUB_AUTHORITIES];
PISID LocalSid = (PISID) LocalBuffer;
PULONG attributes = NULL;
PULONG Members = NULL;
ULONG cMembers;
DWORD dwErr;
BOOL bQuitEarly = FALSE;
*pfIsMemberOf = FALSE;
if (RecursionCount > MARTA_MAX_RECURSION_COUNT)
{
return ERROR_CIRCULAR_DEPENDENCY;
}
dwErr = LoadDLLFuncTable();
if (dwErr != ERROR_SUCCESS)
{
acDebugOut((DEB_TRACE, "Out CMemberCheck::CheckGroup: %lu\n", dwErr));
return(dwErr);
}
dwErr = GetDomainInfo(pSid);
if(dwErr != ERROR_SUCCESS)
{
acDebugOut((DEB_TRACE, "Out CMemberCheck::CheckGroup: %lu\n", dwErr));
return(dwErr);
}
Status = RtlCopySid(RtlLengthSid(_pDomainSid), LocalSid, _pDomainSid);
if(!NT_SUCCESS(Status))
{
return RtlNtStatusToDosError(Status);
}
//
// Special case the Domain Users sid.
//
dwErr = CheckDomainUsers(pSid, pfIsMemberOf, &bQuitEarly);
if (dwErr != ERROR_SUCCESS)
{
return(dwErr);
}
if (bQuitEarly)
{
//
// We are looking at Domain Users group. No need to enumerate this one.
//
return ERROR_SUCCESS;
}
//
// open the group
//
Status = (*DLLFuncs.PSamOpenGroup)(_hDomain,
GENERIC_READ,
rid,
&hSam);
if(NT_SUCCESS(Status))
{
//
// Get the members
//
Status = (*DLLFuncs.PSamGetMembersInGroup)(hSam,
&Members,
&attributes,
&cMembers);
if(NT_SUCCESS(Status) && cMembers )
{
//
// ugly sid rid twiddling
//
++(LocalSid->SubAuthorityCount);
//
// loop thru the members and check if the user sid is an immediate
// member.
//
for (ULONG iIndex = 0; iIndex < cMembers; iIndex++ )
{
//
// Plug the rid into the sid
//
LocalSid->SubAuthority[LocalSid->SubAuthorityCount-1] =
Members[iIndex];
//
// and compare
//
if(RtlEqualSid(_pCurrentNode->pSid,LocalSid) == TRUE)
{
*pfIsMemberOf = TRUE;
break;
}
}
//
// If we did not match the sid, enumerate recursively.
//
if (*pfIsMemberOf == FALSE)
{
ULONG SidLength = RtlLengthSid(LocalSid);
ULONG TotalSize = cMembers * (sizeof(PSID) + SidLength);
PUCHAR Buffer = NULL;
PSID *Sids = NULL;
//
// Allocate memory to hold the sid array.
//
Buffer = (PUCHAR) AccAlloc(TotalSize);
Sids = (PSID *) Buffer;
if (Sids != NULL)
{
PLSA_TRANSLATED_NAME Names = NULL;
Buffer += (sizeof(PSID) * cMembers);
//
// Copy the sids into the allocated array.
//
for (ULONG iIndex = 0; iIndex < cMembers; iIndex++ )
{
Sids[iIndex] = Buffer;
Buffer += SidLength;
LocalSid->SubAuthority[LocalSid->SubAuthorityCount-1] =
Members[iIndex];
Status = RtlCopySid(SidLength, Sids[iIndex], LocalSid);
if (!NT_SUCCESS( Status ))
{
break;
}
}
if (NT_SUCCESS( Status ))
{
//
// Do a single lookup and get the types of the sids
// in the group.
//
dwErr = GetSidTypeMultiple(
cMembers,
Sids,
&Names
);
if (dwErr == ERROR_SUCCESS)
{
//
// Loop thru the sids and call the recursive routines
// if the sidtype is a group or alias.
//
for (ULONG iIndex = 0; iIndex < cMembers; iIndex++ )
{
if (Names[iIndex].Use == SidTypeGroup)
{
dwErr = CheckGroup(Sids[iIndex], pfIsMemberOf, RecursionCount+1);
if (dwErr != ERROR_SUCCESS)
{
break;
}
if (*pfIsMemberOf == TRUE)
{
//
// We have a match. There is no need to
// enumerate any more.
//
break;
}
}
else if (Names[iIndex].Use == SidTypeAlias)
{
dwErr = CheckAlias(Sids[iIndex], pfIsMemberOf, RecursionCount+1);
if (dwErr != ERROR_SUCCESS)
{
break;
}
if (*pfIsMemberOf == TRUE)
{
//
// We have a match. There is no need to
// enumerate any more.
//
break;
}
}
}
(VOID) LsaFreeMemory(Names);
}
}
AccFree(Sids);
}
else
{
Status = STATUS_NO_MEMORY;
}
}
}
if (attributes != NULL)
{
LocalFree(attributes);
attributes = NULL;
}
if (Members != NULL)
{
LocalFree(Members);
Members = NULL;
}
(*DLLFuncs.PSamCloseHandle)(hSam);
}
if(!NT_SUCCESS(Status))
{
dwErr = RtlNtStatusToDosError(Status);
}
acDebugOut((DEB_TRACE, "Out CMemberCheck::CheckGroup: %lu\n", dwErr));
return(dwErr);
}
//+---------------------------------------------------------------------------
//
// Member: CMemberCheck::GetSidType, private
//
// Synopsis: Returns the type of the Sid
//
// Arguments: [IN pSid] -- Input Sid
// [OUT pSidType] -- Returns the type of Sid.
//
// Returns: ERROR_SUCCESS -- Success
//
//----------------------------------------------------------------------------
DWORD CMemberCheck::GetSidType(
IN PSID Sid,
OUT SID_NAME_USE *pSidType)
{
LPWSTR Name = NULL;
LPWSTR DomainName = NULL;
DWORD dwErr;
dwErr = AccLookupAccountName(NULL,
Sid,
&Name,
&DomainName,
pSidType);
if (dwErr == ERROR_SUCCESS)
{
AccFree(Name);
AccFree(DomainName);
}
return dwErr;
}
//+---------------------------------------------------------------------------
//
// Member: CMemberCheck::GetSidTypeMultiple, private
//
// Synopsis: Returns the tanslated names of the Sids
//
// Arguments: [IN Count] -- Number of sids
// [IN pSid] -- Input Sid
// [OUT pNames] -- Returns lsa names structure
//
// Returns: ERROR_SUCCESS -- Success
//
//----------------------------------------------------------------------------
DWORD CMemberCheck::GetSidTypeMultiple(
IN LONG Count,
IN PSID *Sids,
OUT PLSA_TRANSLATED_NAME *pNames
)
{
OBJECT_ATTRIBUTES ObjectAttributes;
LSA_HANDLE PolicyHandle;
PLSA_REFERENCED_DOMAIN_LIST ReferencedDomains;
PLSA_TRANSLATED_NAME Names = NULL;
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
NTSTATUS Status;
NTSTATUS TmpStatus;
*pNames = NULL;
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
SecurityQualityOfService.EffectiveOnly = FALSE;
//
// Set up the object attributes prior to opening the LSA.
//
InitializeObjectAttributes(
&ObjectAttributes,
NULL,
0L,
NULL,
NULL
);
//
// The InitializeObjectAttributes macro presently stores NULL for
// the SecurityQualityOfService field, so we must manually copy that
// structure for now.
//
ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
Status = LsaOpenPolicy(
NULL,
&ObjectAttributes,
POLICY_LOOKUP_NAMES,
&PolicyHandle
);
if ( !NT_SUCCESS( Status )) {
return RtlNtStatusToDosError(Status);
}
Status = LsaLookupSids(
PolicyHandle,
Count,
Sids,
&ReferencedDomains,
&Names
);
TmpStatus = LsaClose( PolicyHandle );
//
// If an error was returned, check specifically for STATUS_NONE_MAPPED.
// In this case, we may need to dispose of the returned Referenced Domain
// List and Names structures. For all other errors, LsaLookupSids()
// frees these structures prior to exit.
//
if ( !NT_SUCCESS( Status )) {
if (Status == STATUS_NONE_MAPPED) {
if (ReferencedDomains != NULL) {
TmpStatus = LsaFreeMemory( ReferencedDomains );
ASSERT( NT_SUCCESS( TmpStatus ));
}
if (Names != NULL) {
TmpStatus = LsaFreeMemory( Names );
ASSERT( NT_SUCCESS( TmpStatus ));
}
}
return RtlNtStatusToDosError(Status);
}
if (ReferencedDomains != NULL) {
Status = LsaFreeMemory( ReferencedDomains );
ASSERT( NT_SUCCESS( Status ));
}
*pNames = Names;
return ERROR_SUCCESS;
}
//+---------------------------------------------------------------------------
//
// Member: CMemberCheck::CheckAlias, private
//
// Synopsis: checks if the objects account is in the specifed alias account
//
// Arguments: [IN pSid] -- Input Sid
// [OUT pfIsMemberOf] -- Where the results are returned.
// Is TRUE if the current SID
// is a member of the input SID
// group.
// [IN RecursionCount] -- Recursion level
//
// Returns: ERROR_SUCCESS -- Success
//
//----------------------------------------------------------------------------
DWORD CMemberCheck::CheckAlias(IN PSID pSid,
OUT PBOOL pfIsMemberOf,
IN DWORD RecursionCount)
{
acDebugOut((DEB_TRACE, "In CMemberCheck::CheckAlias\n"));
NTSTATUS Status;
SAM_HANDLE hSam = NULL;
ULONG rid = ((PISID)(pSid))->SubAuthority[
((PISID)(pSid))->SubAuthorityCount-1];
ULONG_PTR * Members;
ULONG cMembers;
DWORD dwErr;
*pfIsMemberOf = FALSE;
if (RecursionCount > MARTA_MAX_RECURSION_COUNT)
{
return ERROR_CIRCULAR_DEPENDENCY;
}
dwErr = LoadDLLFuncTable();
if (dwErr != ERROR_SUCCESS)
{
acDebugOut((DEB_TRACE, "Out CMemberCheck::CheckGroup: %lu\n", dwErr));
return(dwErr);
}
dwErr = GetDomainInfo(pSid);
if(dwErr != ERROR_SUCCESS)
{
acDebugOut((DEB_TRACE, "Out CMemberCheck::CheckGroup: %lu\n", dwErr));
return(dwErr);
}
//
// open the alias
//
Status = (*DLLFuncs.PSamOpenAlias)(_hDomain,
GENERIC_READ,
rid,
&hSam);
if(NT_SUCCESS(Status))
{
//
// get the members
//
Status = (*DLLFuncs.PSamGetMembersInAlias)(hSam,
(void ***)&Members,
&cMembers);
if(NT_SUCCESS(Status) && cMembers)
{
//
// loop thru the members
//
for (ULONG iIndex = 0; iIndex < cMembers; iIndex++)
{
if(RtlEqualSid(_pCurrentNode->pSid,
((SID **)(Members))[iIndex]) == TRUE)
{
*pfIsMemberOf = TRUE;
break;
}
}
//
// If we did not match the sid, enumerate recursively.
//
if (*pfIsMemberOf == FALSE)
{
PLSA_TRANSLATED_NAME Names = NULL;
//
// Do a single lookup and get the types of the sids
// in the group.
//
dwErr = GetSidTypeMultiple(
cMembers,
(PSID *) Members,
&Names
);
if (dwErr == ERROR_SUCCESS)
{
//
// Loop thru the sids and call the recursive routines
// if the sidtype is a group or an alias.
//
for (ULONG iIndex = 0; iIndex < cMembers; iIndex++ )
{
if (Names[iIndex].Use == SidTypeGroup)
{
dwErr = CheckGroup(((SID **) (Members))[iIndex], pfIsMemberOf, RecursionCount+1);
if (dwErr != ERROR_SUCCESS)
{
break;
}
if (*pfIsMemberOf == TRUE)
{
//
// We have a match. There is no need to
// enumerate any more.
//
break;
}
}
else if (Names[iIndex].Use == SidTypeAlias)
{
dwErr = CheckAlias(((SID **) (Members))[iIndex], pfIsMemberOf, RecursionCount+1);
if (dwErr != ERROR_SUCCESS)
{
break;
}
if (*pfIsMemberOf == TRUE)
{
//
// We have a match. There is no need to
// enumerate any more.
//
break;
}
}
}
(VOID) LsaFreeMemory(Names);
}
}
if(cMembers > 0)
{
LocalFree(Members);
}
}
(*DLLFuncs.PSamCloseHandle)(hSam);
}
if(!NT_SUCCESS(Status))
{
dwErr = RtlNtStatusToDosError(Status);
}
acDebugOut((DEB_TRACE, "Out CMemberCheck::CheckGroup: %lu\n", dwErr));
return(dwErr);
}