windows-nt/Source/XPSP1/NT/ds/security/ntmarta/newsrc/rightsca.cxx
2020-09-26 16:20:57 +08:00

747 lines
22 KiB
C++

//+-------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1998 - 1998.
//
// File: rightsca.cxx
//
// Contents: Implementation of the DS access control rights cache
//
// History: 20-Feb-98 MacM Created
//
//--------------------------------------------------------------------
#include <aclpch.hxx>
#pragma hdrstop
#include <stdio.h>
#include <alsup.hxx>
//
// Global name/id cache
//
PACTRL_RIGHTS_CACHE grgRightsNameCache[ACTRL_OBJ_ID_TABLE_SIZE];
//
// Last connection info/time we read from the schema
//
static ACTRL_ID_SCHEMA_INFO LastSchemaRead;
static RTL_RESOURCE RightsCacheLock;
BOOL bRightsCacheLockInitialized = FALSE;
#define ACTRL_EXT_RIGHTS_CONTAINER L"CN=Extended-Rights,"
//+----------------------------------------------------------------------------
//
// Function: AccctrlInitializeRightsCache
//
// Synopsis: Initialize the access control rights lookup cache
//
// Arguments: VOID
//
// Returns: ERROR_SUCCESS -- Success
//
//-----------------------------------------------------------------------------
DWORD AccctrlInitializeRightsCache(VOID)
{
DWORD dwErr;
if (TRUE == bRightsCacheLockInitialized)
{
//
// Just a precautionary measure to make sure that we do not initialize
// multiple times.
//
ASSERT(FALSE);
return ERROR_SUCCESS;
}
memset(grgRightsNameCache, 0,
sizeof(PACTRL_RIGHTS_CACHE) * ACTRL_OBJ_ID_TABLE_SIZE);
memset(&LastSchemaRead, 0, sizeof(ACTRL_ID_SCHEMA_INFO));
__try
{
RtlInitializeResource(&RightsCacheLock);
dwErr = ERROR_SUCCESS;
bRightsCacheLockInitialized = TRUE;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
dwErr = RtlNtStatusToDosError(GetExceptionCode());
}
return dwErr;
}
//+----------------------------------------------------------------------------
//
// Function: AccctrlFreeRightsCache
//
// Synopsis: Frees any memory allocated for the id name/guid cache
//
// Arguments: VOID
//
// Returns: VOID
//
//-----------------------------------------------------------------------------
VOID AccctrlFreeRightsCache(VOID)
{
INT i,j;
PACTRL_RIGHTS_CACHE pNode, pNext;
if (FALSE == bRightsCacheLockInitialized)
{
return;
}
for(i = 0; i < ACTRL_OBJ_ID_TABLE_SIZE; i++)
{
pNode = grgRightsNameCache[i];
while(pNode != NULL)
{
pNext = pNode->pNext;
for (j = 0; j < (INT)pNode->cRights; j++)
{
AccFree(pNode->RightsList[j]);
}
AccFree(pNode->RightsList);
AccFree(pNode);
pNode = pNext;
}
}
AccFree(LastSchemaRead.pwszPath);
RtlDeleteResource(&RightsCacheLock);
bRightsCacheLockInitialized = FALSE;
}
//+----------------------------------------------------------------------------
//
// Function: AccctrlFindRightsNode
//
// Synopsis: Looks up the node for the given class GUID
//
// Arguments: [ClassGuid] -- Class guid to look up
//
// Returns: NULL -- Node not found
// else a valid node pointer
//
//-----------------------------------------------------------------------------
PACTRL_RIGHTS_CACHE
AccctrlpLookupClassGuidInCache(IN PGUID ClassGuid)
{
PACTRL_RIGHTS_CACHE pNode = NULL;
pNode = grgRightsNameCache[ActrlHashGuid(ClassGuid)];
while(pNode != NULL)
{
if(memcmp(ClassGuid, &pNode->ObjectClassGuid,sizeof(GUID)) == 0)
{
break;
}
pNode = pNode->pNext;
}
#if DBG
if(pNode != NULL )
{
CHAR szGuid[38];
PGUID pGuid = &pNode->ObjectClassGuid;
sprintf(szGuid, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
pGuid->Data1,pGuid->Data2,pGuid->Data3,pGuid->Data4[0],
pGuid->Data4[1],pGuid->Data4[2],pGuid->Data4[3],
pGuid->Data4[4],pGuid->Data4[5],pGuid->Data4[6],
pGuid->Data4[7]);
acDebugOut((DEB_TRACE_LOOKUP,
"Found guid %s\n",
szGuid));
}
#endif
return(pNode);
}
//+----------------------------------------------------------------------------
//
// Function: AccctrlpInsertRightsNode
//
// Synopsis: Updates the information for an existing node or creates and
// inserts a new one
//
// Arguments: [AppliesTo] -- Guid this control right applies to
// [RightsGuid] -- Control right
//
// Returns: ERROR_SUCCESS -- Success
// ERROR_NOT_ENOUGH_MEMORY A memory allocation failed
//
//-----------------------------------------------------------------------------
DWORD AccctrlpInsertRightsNode(IN PGUID AppliesTo,
IN PWSTR ControlRight)
{
DWORD dwErr = ERROR_SUCCESS;
PACTRL_RIGHTS_CACHE Node, pNext, pTrail = NULL;
PWSTR *NewList;
BOOL NewNode = FALSE;
//
// First, find the existing node, if it exists
//
Node = AccctrlpLookupClassGuidInCache( AppliesTo );
if(Node == NULL)
{
//
// Have to create and insert a new one
//
Node = (PACTRL_RIGHTS_CACHE)AccAlloc(sizeof(ACTRL_RIGHTS_CACHE));
if(Node == NULL)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
}
else
{
NewNode = TRUE;
memcpy(&Node->ObjectClassGuid, AppliesTo, sizeof(GUID));
Node->cRights=0;
pNext = grgRightsNameCache[ActrlHashGuid(AppliesTo)];
while(pNext != NULL)
{
if(memcmp(AppliesTo, &(pNext->ObjectClassGuid), sizeof(GUID)) == 0)
{
dwErr = ERROR_ALREADY_EXISTS;
acDebugOut((DEB_TRACE_LOOKUP, "Guid collision. Bailing\n"));
break;
}
pTrail = pNext;
pNext = pNext->pNext;
}
}
if(dwErr == ERROR_SUCCESS)
{
if(pTrail == NULL)
{
grgRightsNameCache[ActrlHashGuid(AppliesTo)] = Node;
}
else {
pTrail->pNext = Node;
}
}
}
//
// Now, insert the new applies to list
if(dwErr == ERROR_SUCCESS)
{
NewList = (PWSTR *)AccAlloc((Node->cRights + 1) * sizeof(PWSTR));
if(NewList==NULL)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
}
else
{
memcpy(NewList, Node->RightsList, Node->cRights * sizeof( PWSTR ));
NewList[Node->cRights] = (PWSTR)AccAlloc((wcslen(ControlRight) + 1) * sizeof(WCHAR));
if(NewList[Node->cRights] == NULL)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
AccFree(NewList);
}
else
{
wcscpy(NewList[Node->cRights], ControlRight);
Node->cRights++;
AccFree(Node->RightsList);
Node->RightsList = NewList;
}
}
}
//
// Clean up if necessary
//
if(dwErr != ERROR_SUCCESS && NewNode == TRUE)
{
AccFree(Node);
}
return(dwErr);
}
//+---------------------------------------------------------------------------
//
// Function: AccDsReadAndInsertExtendedRights
//
// Synopsis: Reads the full list of extended rights from the schema
//
// Arguments: [IN pLDAP] -- LDAP connection to use
// [OUT pcItems] -- Where the count of items
// is returned
// [OUT RightsList] -- Where the list of rights
// entries is returned.
//
// Notes:
//
// Returns: ERROR_SUCCESS -- Success
//
//----------------------------------------------------------------------------
DWORD
AccDsReadAndInsertExtendedRights(IN PLDAP pLDAP)
{
DWORD dwErr = ERROR_SUCCESS;
PWSTR *ppwszValues = NULL, *ppwszApplies = NULL;
PWSTR rgwszAttribs[3];
PWSTR pwszERContainer = NULL;
PDS_NAME_RESULTW pNameRes = NULL;
LDAPMessage *pMessage, *pEntry;
ULONG cEntries, i, j;
PACTRL_RIGHTS_CACHE pCurrentEntry;
GUID RightsGuid;
//
// Get the subschema path
//
if(dwErr == ERROR_SUCCESS)
{
rgwszAttribs[0] = L"configurationNamingContext";
rgwszAttribs[1] = NULL;
dwErr = ldap_search_s(pLDAP,
NULL,
LDAP_SCOPE_BASE,
L"(objectClass=*)",
rgwszAttribs,
0,
&pMessage);
if(dwErr == ERROR_SUCCESS)
{
pEntry = ldap_first_entry(pLDAP,
pMessage);
if(pEntry == NULL)
{
dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
}
else
{
//
// Now, we'll have to get the values
//
ppwszValues = ldap_get_values(pLDAP,
pEntry,
rgwszAttribs[0]);
ldap_msgfree(pMessage);
if(ppwszValues == NULL)
{
dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
}
else
{
pwszERContainer = (PWSTR)AccAlloc((wcslen(ppwszValues[0]) * sizeof(WCHAR)) +
sizeof(ACTRL_EXT_RIGHTS_CONTAINER));
if(pwszERContainer == NULL)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
}
else
{
wcscpy(pwszERContainer,
ACTRL_EXT_RIGHTS_CONTAINER);
wcscat(pwszERContainer,
ppwszValues[0]);
rgwszAttribs[0] = L"rightsGuid";
rgwszAttribs[1] = L"appliesTo";
rgwszAttribs[2] = NULL;
//
// Read the control access rights
//
dwErr = ldap_search_s(pLDAP,
pwszERContainer,
LDAP_SCOPE_ONELEVEL,
L"(objectClass=controlAccessRight)",
rgwszAttribs,
0,
&pMessage);
dwErr = LdapMapErrorToWin32( dwErr );
AccFree(pwszERContainer);
}
ldap_value_free(ppwszValues);
//
// Process the entries
//
if(dwErr == ERROR_SUCCESS)
{
pEntry = ldap_first_entry(pLDAP,
pMessage);
if(pEntry == NULL)
{
dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
}
else
{
cEntries = ldap_count_entries( pLDAP, pMessage );
for(i = 0; i < cEntries && dwErr == ERROR_SUCCESS; i++) {
ppwszValues = ldap_get_values(pLDAP,
pEntry,
rgwszAttribs[0]);
if(ppwszValues == NULL)
{
dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
}
else
{
//
// Then the list of applies to
//
ppwszApplies = ldap_get_values(pLDAP,
pEntry,
rgwszAttribs[1]);
j = 0;
while(ppwszApplies[j] != NULL && dwErr == ERROR_SUCCESS)
{
dwErr = UuidFromString(ppwszApplies[j],
&RightsGuid);
if(dwErr == ERROR_SUCCESS)
{
dwErr = AccctrlpInsertRightsNode( &RightsGuid,
ppwszValues[0]);
}
j++;
}
ldap_value_free(ppwszApplies);
ppwszApplies = NULL;
ldap_value_free(ppwszValues);
}
pEntry = ldap_next_entry( pLDAP, pEntry );
}
}
}
ldap_msgfree(pMessage);
}
}
}
else
{
dwErr = LdapMapErrorToWin32( dwErr );
}
}
return(dwErr) ;
}
//+----------------------------------------------------------------------------
//
// Function: AccctrlpLoadRightsCacheFromSchema
//
// Synopsis: Reads the control rights schema cache and adds the entries into the
// cache
//
// Arguments: [pLDAP] -- LDAP connection to the server
// [pwszPath] -- DS path to the object
//
// Returns: ERROR_SUCCESS -- Success
//
//-----------------------------------------------------------------------------
DWORD AccctrlpLoadRightsCacheFromSchema(PLDAP pLDAP,
PWSTR pwszDsPath)
{
DWORD dwErr = ERROR_SUCCESS;
PLDAP pLocalLDAP = pLDAP;
ULONG cValues[2];
PWSTR *ppwszValues[2];
acDebugOut((DEB_TRACE_LOOKUP, "Reloading rights cache from schema\n"));
//
// If we have no parameters, just return...
//
if(pLDAP == NULL && pwszDsPath == NULL)
{
return(ERROR_SUCCESS);
}
//
// See if we need to read... If our data is over 5 minutes old or if our path referenced is
// not the same as the last one...
//
#define FIVE_MINUTES 300000
if((LastSchemaRead.LastReadTime != 0 &&
(GetTickCount() - LastSchemaRead.LastReadTime < FIVE_MINUTES)) &&
DoPropertiesMatch(pwszDsPath, LastSchemaRead.pwszPath) &&
((pLDAP == NULL && LastSchemaRead.fLDAP == FALSE) ||
(pLDAP != NULL && memcmp(pLDAP, &(LastSchemaRead.LDAP), sizeof(LDAP)))))
{
acDebugOut((DEB_TRACE_LOOKUP,"Cache up to date...\n"));
return(ERROR_SUCCESS);
}
else
{
//
// Need to reinitialize it...
//
if(pLDAP == NULL)
{
LastSchemaRead.fLDAP = FALSE;
}
else
{
LastSchemaRead.fLDAP = TRUE;
memcpy(&(LastSchemaRead.LDAP), pLDAP, sizeof(LDAP));
}
AccFree(LastSchemaRead.pwszPath);
if(pwszDsPath != NULL)
{
ACC_ALLOC_AND_COPY_STRINGW(pwszDsPath, LastSchemaRead.pwszPath, dwErr);
}
LastSchemaRead.LastReadTime = GetTickCount();
}
if(dwErr == ERROR_SUCCESS && pLocalLDAP == NULL)
{
PWSTR pwszServer = NULL, pwszObject = NULL;
dwErr = DspSplitPath( pwszDsPath, &pwszServer, &pwszObject );
if(dwErr == ERROR_SUCCESS)
{
dwErr = BindToDSObject(pwszServer, pwszObject, &pLocalLDAP);
LocalFree(pwszServer);
}
}
//
// Now, get the info. First, extended rights, then the schema info
//
if(dwErr == ERROR_SUCCESS)
{
dwErr = AccDsReadAndInsertExtendedRights(pLocalLDAP);
}
//
// See if we need to release our ldap connection
//
if(pLocalLDAP != pLDAP && pLocalLDAP != NULL)
{
UnBindFromDSObject(&pLocalLDAP);
}
return(dwErr);
}
//+----------------------------------------------------------------------------
//
// Function: AccctrlLookupRightByName
//
// Synopsis: Returns the list of control rights for a given object class
//
// Arguments: [pLDAP] -- LDAP connection to the server
// [pwszPath] -- DS path to the object
// [pwszName] -- Object class name
// [pCount] -- Where the count if items is returned
// [ppRightsList] -- List of control rights
// [ppwszNameList] -- List of control rights names
//
// Returns: ERROR_SUCCESS -- Success
// ERROR_NOT_ENOUGH_MEMORY A memory allocation failed
// ERROR_NOT_FOUND -- No such ID exists
//
//-----------------------------------------------------------------------------
DWORD
AccctrlLookupRightsByName(IN PLDAP pLDAP,
IN PWSTR pwszDsPath,
IN PWSTR pwszName,
OUT PULONG pCount,
OUT PACTRL_CONTROL_INFOW *ControlInfo)
{
DWORD dwErr = ERROR_SUCCESS;
GUID *ObjectClassGuid, RightsGuid;
PACTRL_RIGHTS_CACHE Node;
ULONG Size = 0, i;
PWSTR GuidName, Current;
if((pwszDsPath == NULL && pLDAP == NULL) || pwszName == NULL)
{
*pCount = 0;
*ControlInfo = NULL;
return(ERROR_SUCCESS);
}
RtlAcquireResourceShared(&RightsCacheLock, TRUE);
//
// This is a multi-staged process. First, we have to lookup the guid associated
// with the object class name. Then, we get the list of control rights for it
//
dwErr = AccctrlLookupGuid(pLDAP,
pwszDsPath,
pwszName,
FALSE, // Don't allocate
&ObjectClassGuid);
if(dwErr == ERROR_SUCCESS)
{
Node = AccctrlpLookupClassGuidInCache(ObjectClassGuid);
if(Node == NULL)
{
RtlConvertSharedToExclusive( &RightsCacheLock );
dwErr = AccctrlpLoadRightsCacheFromSchema(pLDAP, pwszDsPath );
if(dwErr == ERROR_SUCCESS)
{
Node = AccctrlpLookupClassGuidInCache(ObjectClassGuid);
if(Node == NULL)
{
dwErr = ERROR_NOT_FOUND;
}
}
}
if(Node != NULL)
{
//
// Size all of the return strings
//
for (i = 0;i < Node->cRights && dwErr == ERROR_SUCCESS; i++) {
dwErr = UuidFromString( Node->RightsList[i],&RightsGuid);
if(dwErr == ERROR_SUCCESS)
{
dwErr = AccctrlLookupIdName(pLDAP,
pwszDsPath,
&RightsGuid,
FALSE,
FALSE,
&GuidName);
if(dwErr == ERROR_SUCCESS)
{
Size += wcslen( GuidName ) + 1;
Size += wcslen( Node->RightsList[i] ) + 1;
}
}
}
//
// Now, allocate the return information
//
if(dwErr == ERROR_SUCCESS)
{
*ControlInfo = (PACTRL_CONTROL_INFOW)AccAlloc((Size * sizeof(WCHAR)) +
(Node->cRights * sizeof(ACTRL_CONTROL_INFOW)));
if(*ControlInfo == NULL)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
}
else
{
Current = (PWSTR)((*ControlInfo) + Node->cRights);
for (i = 0;i < Node->cRights && dwErr == ERROR_SUCCESS; i++) {
UuidFromString( Node->RightsList[i],&RightsGuid);
dwErr = AccctrlLookupIdName(pLDAP,
pwszDsPath,
&RightsGuid,
FALSE,
FALSE,
&GuidName);
if(dwErr == ERROR_SUCCESS)
{
(*ControlInfo)[i].lpControlId = Current;
wcscpy(Current, Node->RightsList[i]);
Current += wcslen(Node->RightsList[i]);
*Current = L'\0';
Current++;
(*ControlInfo)[i].lpControlName = Current;
wcscpy(Current, GuidName);
Current += wcslen(GuidName);
*Current = L'\0';
Current++;
}
}
if(dwErr != ERROR_SUCCESS)
{
AccFree(*ControlInfo);
}
else
{
*pCount = Node->cRights;
}
}
}
}
}
RtlReleaseResource(&RightsCacheLock);
return(dwErr);
}