1108 lines
32 KiB
C++
1108 lines
32 KiB
C++
//+------------------------------------------------------------------
|
|
//
|
|
// 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);
|
|
}
|
|
|
|
|