DWORD AD_OfflineFindObjectsBySidList( IN PLSA_AD_PROVIDER_STATE pState, IN size_t sCount, IN PSTR* ppszSidList, OUT PLSA_SECURITY_OBJECT** pppObjects ) { DWORD dwError = LW_ERROR_SUCCESS; PLSA_SECURITY_OBJECT *ppObjects = NULL; /* * Lookup users and groups from the cache. */ dwError = ADCacheFindObjectsBySidList( pState->hCacheConnection, sCount, ppszSidList, &ppObjects); BAIL_ON_LSA_ERROR(dwError); *pppObjects = ppObjects; ppObjects = NULL; cleanup: ADCacheSafeFreeObjectList(sCount, &ppObjects); return dwError; error: *pppObjects = NULL; goto cleanup; }
DWORD AD_OfflineGetGroupMembers( IN PLSA_AD_PROVIDER_STATE pState, IN PCSTR pszGroupSid, OUT size_t* psMemberObjectsCount, OUT PLSA_SECURITY_OBJECT** pppMemberObjects ) { DWORD dwError = LW_ERROR_SUCCESS; size_t sGroupMembershipsCount = 0; PLSA_GROUP_MEMBERSHIP* ppGroupMemberships = NULL; size_t sMemberSidsCount = 0; // Only free top level array, do not free string pointers as they // track elements inside ppMemberships. PSTR* ppszMemberSids = NULL; size_t sObjectsCount = 0; PLSA_SECURITY_OBJECT* ppObjects = NULL; size_t sIndex = 0; dwError = ADCacheGetGroupMembers( pState->hCacheConnection, pszGroupSid, AD_GetTrimUserMembershipEnabled(pState), &sGroupMembershipsCount, &ppGroupMemberships); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateMemory( sizeof(*ppszMemberSids) * sGroupMembershipsCount, (PVOID*)&ppszMemberSids); BAIL_ON_LSA_ERROR(dwError); sMemberSidsCount = 0; for (sIndex = 0; sIndex < sGroupMembershipsCount; sIndex++) { if (ppGroupMemberships[sIndex]->pszChildSid) { ppszMemberSids[sMemberSidsCount++] = ppGroupMemberships[sIndex]->pszChildSid; } } dwError = AD_OfflineFindObjectsBySidList( pState, sMemberSidsCount, ppszMemberSids, &ppObjects); BAIL_ON_LSA_ERROR(dwError); sObjectsCount = sMemberSidsCount; AD_FilterNullEntries(ppObjects, &sObjectsCount); *psMemberObjectsCount = sObjectsCount; *pppMemberObjects = ppObjects; ppObjects = NULL; sObjectsCount = 0; cleanup: ADCacheSafeFreeObjectList(sObjectsCount, &ppObjects); LW_SAFE_FREE_MEMORY(ppszMemberSids); ADCacheSafeFreeGroupMembershipList(sGroupMembershipsCount, &ppGroupMemberships); return dwError; error: *psMemberObjectsCount = 0; *pppMemberObjects = NULL; goto cleanup; }
DWORD AD_GroupExpansionDataGetResults( IN PLSA_AD_GROUP_EXPANSION_DATA pExpansionData, OUT OPTIONAL PBOOLEAN pbIsFullyExpanded, OUT size_t* psUserMembersCount, OUT PLSA_SECURITY_OBJECT** pppUserMembers ) { DWORD dwError = 0; LW_HASH_ITERATOR hashIterator; LW_HASH_ENTRY* pHashEntry = NULL; size_t sHashCount = 0; PLSA_SECURITY_OBJECT* ppUserMembers = NULL; size_t sUserMembersCount = 0; BOOLEAN bIsFullyExpanded = FALSE; dwError = pExpansionData->dwLastError; BAIL_ON_LSA_ERROR(dwError); // Fill in the final list of users and return it. sHashCount = pExpansionData->pUsers->sCount; dwError = LwAllocateMemory( sizeof(*ppUserMembers) * sHashCount, (PVOID*)&ppUserMembers); BAIL_ON_LSA_ERROR(dwError); dwError = LwHashGetIterator(pExpansionData->pUsers, &hashIterator); BAIL_ON_LSA_ERROR(dwError); for (sUserMembersCount = 0; (pHashEntry = LwHashNext(&hashIterator)) != NULL; sUserMembersCount++) { PLSA_SECURITY_OBJECT pUser = (PLSA_SECURITY_OBJECT) pHashEntry->pKey; dwError = LwHashRemoveKey(pExpansionData->pUsers, pUser); BAIL_ON_LSA_ERROR(dwError); ppUserMembers[sUserMembersCount] = pUser; } if (sUserMembersCount != sHashCount) { dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } if (!pExpansionData->bDiscardedDueToDepth && (pExpansionData->pGroupsToExpand->sCount == 0)) { bIsFullyExpanded = TRUE; } cleanup: if (pbIsFullyExpanded) { *pbIsFullyExpanded = bIsFullyExpanded; } *psUserMembersCount = sUserMembersCount; *pppUserMembers = ppUserMembers; return dwError; error: ADCacheSafeFreeObjectList(sUserMembersCount, &ppUserMembers); sUserMembersCount = 0; if (dwError && !pExpansionData->dwLastError) { pExpansionData->dwLastError = dwError; } goto cleanup; }
DWORD AD_GroupExpansionDataAddExpansionResults( IN PLSA_AD_GROUP_EXPANSION_DATA pExpansionData, IN DWORD dwExpandedGroupDepth, IN OUT size_t* psMembersCount, IN OUT PLSA_SECURITY_OBJECT** pppMembers ) { DWORD dwError = 0; size_t sMembersCount = *psMembersCount; PLSA_SECURITY_OBJECT* ppMembers = *pppMembers; dwError = pExpansionData->dwLastError; BAIL_ON_LSA_ERROR(dwError); if (dwExpandedGroupDepth > pExpansionData->dwMaxDepth) { // This should never happen dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } if ((sMembersCount + pExpansionData->pUsers->sCount) * 2 > pExpansionData->pUsers->sTableSize) { dwError = LwHashResize( pExpansionData->pUsers, (sMembersCount + pExpansionData->pUsers->sCount + 10) * 3); BAIL_ON_LSA_ERROR(dwError); } if ((sMembersCount + pExpansionData->pGroupsToExpand->sCount) * 2 > pExpansionData->pGroupsToExpand->sTableSize) { dwError = LwHashResize( pExpansionData->pGroupsToExpand, (sMembersCount + pExpansionData->pGroupsToExpand->sCount + 10) * 3); BAIL_ON_LSA_ERROR(dwError); } for (; sMembersCount > 0; sMembersCount--) { PLSA_SECURITY_OBJECT pCurrentMember = ppMembers[sMembersCount-1]; if (!pCurrentMember) { continue; } if (pCurrentMember->type == LSA_OBJECT_TYPE_USER) { if (!LwHashExists(pExpansionData->pUsers, ppMembers[sMembersCount-1])) { dwError = LwHashSetValue( pExpansionData->pUsers, ppMembers[sMembersCount-1], (PVOID)(size_t)dwExpandedGroupDepth); BAIL_ON_LSA_ERROR(dwError); ppMembers[sMembersCount-1] = NULL; } else { ADCacheSafeFreeObject(&ppMembers[sMembersCount-1]); } } else if (pCurrentMember->type == LSA_OBJECT_TYPE_GROUP) { if (dwExpandedGroupDepth >= pExpansionData->dwMaxDepth) { pExpansionData->bDiscardedDueToDepth = TRUE; ADCacheSafeFreeObject(&ppMembers[sMembersCount-1]); } else if (LwHashExists(pExpansionData->pExpandedGroups, pCurrentMember) || LwHashExists(pExpansionData->pGroupsToExpand, pCurrentMember)) { ADCacheSafeFreeObject(&ppMembers[sMembersCount-1]); } else { dwError = LwHashSetValue( pExpansionData->pGroupsToExpand, ppMembers[sMembersCount-1], (PVOID)(size_t)dwExpandedGroupDepth); BAIL_ON_LSA_ERROR(dwError); ppMembers[sMembersCount-1] = NULL; } } else { // some other kind of object -- should not happen ADCacheSafeFreeObject(&ppMembers[sMembersCount-1]); } } cleanup: if (ppMembers && (sMembersCount == 0)) { ADCacheSafeFreeObjectList(sMembersCount, &ppMembers); } *psMembersCount = sMembersCount; *pppMembers = ppMembers; return dwError; error: ADCacheSafeFreeObjectList(sMembersCount, &ppMembers); if (dwError && !pExpansionData->dwLastError) { pExpansionData->dwLastError = dwError; } goto cleanup; }
DWORD ADLdap_GetObjectGroupMembership( IN PAD_PROVIDER_CONTEXT pContext, IN PLSA_SECURITY_OBJECT pObject, OUT int* piPrimaryGroupIndex, OUT size_t* psNumGroupsFound, OUT PLSA_SECURITY_OBJECT** pppGroupInfoList ) { DWORD dwError = 0; PLSA_DM_LDAP_CONNECTION pConn = NULL; PSTR pszFullDomainName = NULL; INT i = 0; PLSA_SECURITY_OBJECT* ppGroupInfoList = NULL; size_t sNumGroupsFound = 0; int iPrimaryGroupIndex = -1; DWORD gcMembershipCount = 0; PSTR* ppGcMembershipList = NULL; DWORD dcMembershipCount = 0; PSTR* ppDcMembershipList = NULL; PLW_HASH_TABLE pGroupHash = NULL; LSA_TRUST_DIRECTION trustDirection = LSA_TRUST_DIRECTION_UNKNOWN; LSA_TRUST_MODE trustMode = LSA_TRUST_MODE_UNKNOWN; DWORD index = 0; DWORD totalSidCount = 0; PSTR* ppTotalSidList = NULL; // If we cannot get dn, then we cannot get DN information for this objects, hence BAIL if (LW_IS_NULL_OR_EMPTY_STR(pObject->pszDN)) { dwError = LW_ERROR_NO_SUCH_USER; BAIL_ON_LSA_ERROR(dwError); } dwError = LwLdapConvertDNToDomain( pObject->pszDN, &pszFullDomainName); BAIL_ON_LSA_ERROR(dwError); // Note that this function is called only for 2-way trusts. However, // the trust could be an external trust or a forest trust. We can only // query the GC if there is a forest trust. dwError = AD_DetermineTrustModeandDomainName( pContext->pState, pszFullDomainName, &trustDirection, &trustMode, NULL, NULL); BAIL_ON_LSA_ERROR(dwError); // We could only have the DN path for the user if (s)he came from a two way // trusted domain or the joined domain. LSA_ASSERT(LSA_TRUST_DIRECTION_TWO_WAY == trustDirection || LSA_TRUST_DIRECTION_SELF == trustDirection); if (trustMode != LSA_TRUST_MODE_EXTERNAL) { // Get forest info from domain's GC since there is a forest trust. // This will only include universal group information. (The domain // global groups will not include membership info in the GC.) dwError = LsaDmLdapOpenGc( pContext, pszFullDomainName, &pConn); BAIL_ON_LSA_ERROR(dwError); dwError = ADLdap_GetAttributeValuesList( pConn, pObject->pszDN, AD_LDAP_MEMBEROF_TAG, TRUE, TRUE, &gcMembershipCount, &ppGcMembershipList); BAIL_ON_LSA_ERROR(dwError); LsaDmLdapClose(pConn); pConn = NULL; } dwError = LsaDmLdapOpenDc( pContext, pszFullDomainName, &pConn); BAIL_ON_LSA_ERROR(dwError); dwError = ADLdap_GetAttributeValuesList( pConn, pObject->pszDN, AD_LDAP_MEMBEROF_TAG, TRUE, TRUE, &dcMembershipCount, &ppDcMembershipList); BAIL_ON_LSA_ERROR(dwError); dwError = LwHashCreate( (dcMembershipCount + gcMembershipCount + 1) * 2, LwHashCaselessStringCompare, LwHashCaselessStringHash, ADLdap_FreeHashStringValue, NULL, &pGroupHash); BAIL_ON_LSA_ERROR(dwError); for (index = 0; index < gcMembershipCount; index++) { PSTR pSid = ppGcMembershipList[index]; if (!LwHashExists(pGroupHash, pSid)) { // Set the value of the hash entry as NULL so this string is not // freed with the hash. dwError = LwHashSetValue(pGroupHash, pSid, NULL); BAIL_ON_LSA_ERROR(dwError); } } for (index = 0; index < dcMembershipCount; index++) { PSTR pSid = ppDcMembershipList[index]; if (!LwHashExists(pGroupHash, pSid)) { // Set the value of the hash entry as NULL so this string is not // freed with the hash. dwError = LwHashSetValue(pGroupHash, pSid, NULL); BAIL_ON_LSA_ERROR(dwError); } } if (pObject->type == LSA_OBJECT_TYPE_USER && pObject->userInfo.pszPrimaryGroupSid) { // Add the pszPrimaryGroupSID entry to the hash PSTR pSid = pObject->userInfo.pszPrimaryGroupSid; if (!LwHashExists(pGroupHash, pSid)) { // Set the value of the hash entry as NULL so this string is not // freed with the hash. dwError = LwHashSetValue(pGroupHash, pSid, NULL); BAIL_ON_LSA_ERROR(dwError); } } // Check if the user came from a domain other than the computer's domain if (LSA_TRUST_DIRECTION_TWO_WAY == trustDirection) { dwError = ADLdap_AddDomainLocalGroups( pContext, pObject, pGroupHash); BAIL_ON_LSA_ERROR(dwError); } dwError = ADLdap_MoveHashKeysToArray( pGroupHash, &totalSidCount, (PVOID**)(PVOID)&ppTotalSidList); BAIL_ON_LSA_ERROR(dwError); dwError = AD_FindObjectsBySidList( pContext, totalSidCount, ppTotalSidList, &sNumGroupsFound, &ppGroupInfoList); BAIL_ON_LSA_ERROR(dwError); AD_FilterNullEntries( ppGroupInfoList, &sNumGroupsFound); // Determine primary group index if (pObject->type == LSA_OBJECT_TYPE_USER && pObject->userInfo.pszPrimaryGroupSid && ppGroupInfoList && sNumGroupsFound) { for (i = (INT)sNumGroupsFound - 1; i >= 0; i--) { if (!strcmp(ppGroupInfoList[i]->pszObjectSid, pObject->userInfo.pszPrimaryGroupSid)) { iPrimaryGroupIndex = i; break; } } } *psNumGroupsFound = sNumGroupsFound; *pppGroupInfoList = ppGroupInfoList; *piPrimaryGroupIndex = iPrimaryGroupIndex; cleanup: LwHashSafeFree(&pGroupHash); LW_SAFE_FREE_STRING(pszFullDomainName); LwFreeStringArray(ppGcMembershipList, gcMembershipCount); LwFreeStringArray(ppDcMembershipList, dcMembershipCount); // Do not free the string pointers inside. They are borrowed from the // hash. LW_SAFE_FREE_MEMORY(ppTotalSidList); LsaDmLdapClose(pConn); return dwError; error: *pppGroupInfoList = NULL; *psNumGroupsFound = 0; *piPrimaryGroupIndex = -1; if ( dwError != LW_ERROR_DOMAIN_IS_OFFLINE ) { LSA_LOG_ERROR("Failed to group memberships of SID=%s. [error code:%u]", pObject->pszObjectSid, dwError); } ADCacheSafeFreeObjectList((DWORD)sNumGroupsFound, &ppGroupInfoList); goto cleanup; }
DWORD ADLdap_GetGroupMembers( IN PAD_PROVIDER_CONTEXT pContext, IN PCSTR pszDomainName, IN PCSTR pszSid, OUT size_t* psCount, OUT PLSA_SECURITY_OBJECT** pppResults ) { DWORD dwError = LW_ERROR_SUCCESS; DWORD dwSidCount = 0; PSTR pszDnsDomainName = NULL; PLSA_SECURITY_OBJECT pGroupObj = NULL; PLSA_SECURITY_OBJECT* ppResults = NULL; PSTR *ppszLDAPValues = NULL; size_t sFoundCount = 0; PLSA_DM_LDAP_CONNECTION pConn = NULL; dwError = AD_FindObjectBySid( pContext, pszSid, &pGroupObj); BAIL_ON_LSA_ERROR(dwError); if (pGroupObj->type != LSA_OBJECT_TYPE_GROUP) { dwError = LW_ERROR_DATA_ERROR; BAIL_ON_LSA_ERROR(dwError); } dwError = LsaDmWrapGetDomainName( pContext->pState->hDmState, pszDomainName, &pszDnsDomainName, NULL); BAIL_ON_LSA_ERROR(dwError); dwError = LsaDmLdapOpenDc( pContext, pszDnsDomainName, &pConn); BAIL_ON_LSA_ERROR(dwError); dwError = ADLdap_GetAttributeValuesList( pConn, pGroupObj->pszDN, AD_LDAP_MEMBER_TAG, TRUE, TRUE, &dwSidCount, &ppszLDAPValues); BAIL_ON_LSA_ERROR(dwError); dwError = AD_FindObjectsBySidList( pContext, dwSidCount, ppszLDAPValues, &sFoundCount, &ppResults); BAIL_ON_LSA_ERROR(dwError); *psCount = sFoundCount; *pppResults = ppResults; cleanup: LW_SAFE_FREE_STRING(pszDnsDomainName); ADCacheSafeFreeObject(&pGroupObj); LwFreeStringArray(ppszLDAPValues, dwSidCount); LsaDmLdapClose(pConn); return dwError; error: *psCount = 0; *pppResults = NULL; LSA_LOG_ERROR("Failed to find group's members of objectSid=%s. [error code:%u]", LSA_SAFE_LOG_STRING(pszSid), dwError); ADCacheSafeFreeObjectList((DWORD)sFoundCount, &ppResults); goto cleanup; }