//+------------------------------------------------------------------- // // 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 #pragma hdrstop #include #include // // 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); }