DWORD ADLdap_MoveHashKeysToArray( IN OUT PLW_HASH_TABLE pHash, OUT PDWORD pCount, OUT PVOID** pppValues ) { LW_HASH_ITERATOR hashIterator = {0}; DWORD count = (DWORD) LwHashGetKeyCount(pHash); DWORD index = 0; DWORD dwError = 0; PVOID* ppValues = NULL; LW_HASH_ENTRY* pHashEntry = NULL; if (count) { dwError = LwAllocateMemory( sizeof(ppValues[0]) * count, OUT_PPVOID(&ppValues)); BAIL_ON_LSA_ERROR(dwError); dwError = LwHashGetIterator(pHash, &hashIterator); BAIL_ON_LSA_ERROR(dwError); for (index = 0; (pHashEntry = LwHashNext(&hashIterator)) != NULL; index++) { ppValues[index] = pHashEntry->pKey; } } *pCount = count; *pppValues = ppValues; cleanup: return dwError; error: *pCount = 0; *pppValues = NULL; LW_SAFE_FREE_MEMORY(ppValues); goto cleanup; }
DWORD LocalDirQueryMemberOf( IN HANDLE hProvider, IN LSA_FIND_FLAGS FindFlags, IN DWORD dwSidCount, IN PSTR* ppszSids, OUT PDWORD pdwGroupSidCount, OUT PSTR** pppszGroupSids ) { DWORD dwError = 0; DWORD dwIndex = 0; PLW_HASH_TABLE pGroupHash = NULL; LW_HASH_ITERATOR hashIterator = {0}; LW_HASH_ENTRY* pHashEntry = NULL; DWORD dwGroupSidCount = 0; PSTR* ppszGroupSids = NULL; dwError = LwHashCreate( 13, LwHashCaselessStringCompare, LwHashCaselessStringHash, NULL, NULL, &pGroupHash); BAIL_ON_LSA_ERROR(dwError); for (dwIndex = 0; dwIndex < dwSidCount; dwIndex++) { dwError = LocalDirQueryMemberOfInternal( hProvider, FindFlags, ppszSids[dwIndex], pGroupHash); BAIL_ON_LSA_ERROR(dwError); } dwGroupSidCount = (DWORD) LwHashGetKeyCount(pGroupHash); if (dwGroupSidCount) { dwError = LwAllocateMemory( sizeof(*ppszGroupSids) * dwGroupSidCount, OUT_PPVOID(&ppszGroupSids)); BAIL_ON_LSA_ERROR(dwError); dwError = LwHashGetIterator(pGroupHash, &hashIterator); BAIL_ON_LSA_ERROR(dwError); for(dwIndex = 0; (pHashEntry = LwHashNext(&hashIterator)) != NULL; dwIndex++) { ppszGroupSids[dwIndex] = (PSTR) pHashEntry->pValue; pHashEntry->pValue = NULL; } } *pdwGroupSidCount = dwGroupSidCount; *pppszGroupSids = ppszGroupSids; cleanup: if (pGroupHash) { if (LwHashGetIterator(pGroupHash, &hashIterator) == 0) { while ((pHashEntry = LwHashNext(&hashIterator))) { LW_SAFE_FREE_MEMORY(pHashEntry->pValue); } } LwHashSafeFree(&pGroupHash); } return dwError; error: *pdwGroupSidCount = 0; *pppszGroupSids = NULL; if (ppszGroupSids) { LwFreeStringArray(ppszGroupSids, dwGroupSidCount); } goto cleanup; }
static DWORD UmnSrvUpdateADAccountsByHash( HANDLE hLsass, PLW_EVENTLOG_CONNECTION pEventlog, HANDLE hReg, HKEY hParameters, PLW_HASH_TABLE pUsers, long long PreviousRun, long long Now ) { DWORD dwError = 0; HKEY hUsers = NULL; HKEY hGroups = NULL; LW_HASH_ITERATOR usersIterator = { 0 }; LW_HASH_ENTRY* pEntry = NULL; DWORD groupSidCount = 0; PSTR* ppGroupSids = NULL; DWORD lookupGroupSidCount = 0; DWORD lookupGroupSidCapacity = 0; // Only free the first level of this array, do not free the strings it // points to. PSTR* ppLookupGroupSids = NULL; LSA_QUERY_LIST list = { 0 }; PLSA_SECURITY_OBJECT *ppLookedupGroups = NULL; PLW_HASH_TABLE pGroups = NULL; PLW_HASH_TABLE pNameToUser = NULL; PLW_HASH_TABLE pNameToGroup = NULL; DWORD i = 0; // Do not free PLSA_SECURITY_OBJECT pGroup = NULL; // Do not free PLSA_SECURITY_OBJECT pExisting = NULL; PSTR pNewName = NULL; dwError = LwHashCreate( 100, LwHashStringCompare, LwHashStringHash, UmnSrvHashFreeObjectValue, NULL, &pGroups); BAIL_ON_UMN_ERROR(dwError); dwError = LwHashCreate( 100, LwHashStringCompare, LwHashStringHash, NULL, NULL, &pNameToGroup); BAIL_ON_UMN_ERROR(dwError); dwError = LwHashCreate( pUsers->sCount * 2, LwHashStringCompare, LwHashStringHash, NULL, NULL, &pNameToUser); BAIL_ON_UMN_ERROR(dwError); dwError = RegOpenKeyExA( hReg, hParameters, "AD Users", 0, KEY_ALL_ACCESS, &hUsers); BAIL_ON_UMN_ERROR(dwError); dwError = RegOpenKeyExA( hReg, hParameters, "AD Groups", 0, KEY_ALL_ACCESS, &hGroups); BAIL_ON_UMN_ERROR(dwError); dwError = LwHashGetIterator( pUsers, &usersIterator); BAIL_ON_UMN_ERROR(dwError); while((pEntry = LwHashNext(&usersIterator)) != NULL) { PLSA_SECURITY_OBJECT pUser = (PLSA_SECURITY_OBJECT)pEntry->pValue; if (gbPollerThreadShouldExit) { dwError = ERROR_CANCELLED; BAIL_ON_UMN_ERROR(dwError); } dwError = LwHashGetValue( pNameToUser, pUser->userInfo.pszUnixName, (PVOID*)&pExisting); if (dwError != ERROR_NOT_FOUND) { BAIL_ON_UMN_ERROR(dwError); dwError = LwAllocateStringPrintf( &pNewName, "%s\\%s", pUser->pszNetbiosDomainName, pUser->pszSamAccountName); BAIL_ON_UMN_ERROR(dwError); UMN_LOG_ERROR("Found conflict on user name '%hhs'. Sid %hhs will now be reported as name '%s' instead because its alias conflicts with sid %hhs.", pUser->userInfo.pszUnixName, pUser->pszObjectSid, pNewName, pExisting->pszObjectSid); BAIL_ON_UMN_ERROR(dwError); LW_SAFE_FREE_STRING(pUser->userInfo.pszUnixName); pUser->userInfo.pszUnixName = pNewName; pNewName = NULL; } dwError = LwHashSetValue( pNameToUser, pUser->userInfo.pszUnixName, pUser); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvUpdateADUser( pEventlog, hReg, hUsers, PreviousRun, Now, pUser); if (dwError == ERROR_NO_UNICODE_TRANSLATION) { // Error message already logged dwError = 0; continue; } BAIL_ON_UMN_ERROR(dwError); if (ppGroupSids) { LsaFreeSidList( groupSidCount, ppGroupSids); } dwError = LsaQueryMemberOf( hLsass, NULL, 0, 1, &pUser->pszObjectSid, &groupSidCount, &ppGroupSids); BAIL_ON_UMN_ERROR(dwError); if (groupSidCount > lookupGroupSidCapacity) { LW_SAFE_FREE_MEMORY(ppLookupGroupSids); dwError = LwAllocateMemory( groupSidCount * sizeof(ppLookupGroupSids[0]), (PVOID*)&ppLookupGroupSids); BAIL_ON_UMN_ERROR(dwError); lookupGroupSidCapacity = groupSidCount; } lookupGroupSidCount = 0; for (i = 0; i < groupSidCount; i++) { dwError = LwHashGetValue( pGroups, ppGroupSids[i], (PVOID*)&pGroup); if (dwError == ERROR_NOT_FOUND) { ppLookupGroupSids[lookupGroupSidCount++] = ppGroupSids[i]; } else { BAIL_ON_UMN_ERROR(dwError); UMN_LOG_VERBOSE("Found AD user %s is a member of processed group %s", pUser->userInfo.pszUnixName, pGroup->groupInfo.pszUnixName); if (!pGroup->enabled) { UMN_LOG_VERBOSE("Skipping unenabled group %s", pGroup->groupInfo.pszUnixName); } else { dwError = UmnSrvUpdateADGroupMember( pEventlog, hReg, hGroups, PreviousRun, Now, pGroup, pUser->userInfo.pszUnixName); BAIL_ON_UMN_ERROR(dwError); } } } if (lookupGroupSidCount) { list.ppszStrings = (PCSTR *)ppLookupGroupSids; dwError = LsaFindObjects( hLsass, NULL, 0, LSA_OBJECT_TYPE_GROUP, LSA_QUERY_TYPE_BY_SID, lookupGroupSidCount, list, &ppLookedupGroups); BAIL_ON_UMN_ERROR(dwError); for (i = 0; i < lookupGroupSidCount; i++) { if (gbPollerThreadShouldExit) { dwError = ERROR_CANCELLED; BAIL_ON_UMN_ERROR(dwError); } pGroup = ppLookedupGroups[i]; if (!pGroup) { UMN_LOG_ERROR("Unable to find group sid %s that user %s is a member of", ppLookupGroupSids[i], pUser->userInfo.pszUnixName); continue; } UMN_LOG_VERBOSE("Found AD user %s is a member of unprocessed group %s", pUser->userInfo.pszUnixName, pGroup->groupInfo.pszUnixName); dwError = LwHashGetValue( pNameToGroup, pGroup->groupInfo.pszUnixName, (PVOID*)&pExisting); if (dwError != ERROR_NOT_FOUND) { BAIL_ON_UMN_ERROR(dwError); dwError = LwAllocateStringPrintf( &pNewName, "%s\\%s", pGroup->pszNetbiosDomainName, pGroup->pszSamAccountName); BAIL_ON_UMN_ERROR(dwError); UMN_LOG_ERROR("Found conflict on group name '%hhs'. Sid %hhs will now be reported as name '%s' instead because its alias conflicts with sid %hhs.", pGroup->groupInfo.pszUnixName, pGroup->pszObjectSid, pNewName, pExisting->pszObjectSid); BAIL_ON_UMN_ERROR(dwError); LW_SAFE_FREE_STRING(pGroup->groupInfo.pszUnixName); pGroup->groupInfo.pszUnixName = pNewName; pNewName = NULL; } dwError = LwHashSetValue( pNameToGroup, pGroup->groupInfo.pszUnixName, pGroup); BAIL_ON_UMN_ERROR(dwError); if (!pGroup->enabled) { UMN_LOG_VERBOSE("Skipping unenabled group %s", pGroup->groupInfo.pszUnixName); } else { dwError = UmnSrvUpdateADGroup( pEventlog, hReg, hGroups, PreviousRun, Now, pGroup); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvUpdateADGroupMember( pEventlog, hReg, hGroups, PreviousRun, Now, pGroup, pUser->userInfo.pszUnixName); BAIL_ON_UMN_ERROR(dwError); } dwError = LwHashSetValue( pGroups, pGroup->pszObjectSid, pGroup); BAIL_ON_UMN_ERROR(dwError); ppLookedupGroups[i] = NULL; } LsaFreeSecurityObjectList( lookupGroupSidCount, ppLookedupGroups); ppLookedupGroups = NULL; } } dwError = UmnSrvFindDeletedUsers( pEventlog, hReg, "AD Users", hUsers, Now); BAIL_ON_UMN_ERROR(dwError); dwError = UmnSrvFindDeletedGroups( pEventlog, hReg, "AD Groups", hGroups, Now); BAIL_ON_UMN_ERROR(dwError); cleanup: LW_SAFE_FREE_STRING(pNewName); LW_SAFE_FREE_MEMORY(ppLookupGroupSids); if (ppGroupSids) { LsaFreeSidList( groupSidCount, ppGroupSids); } if (ppLookedupGroups) { LsaFreeSecurityObjectList( lookupGroupSidCount, ppLookedupGroups); } if (hUsers) { RegCloseKey(hReg, hUsers); } if (hGroups) { RegCloseKey(hReg, hGroups); } LwHashSafeFree(&pGroups); LwHashSafeFree(&pNameToUser); LwHashSafeFree(&pNameToGroup); return dwError; error: 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_GroupExpansionDataGetNextGroupToExpand( IN PLSA_AD_GROUP_EXPANSION_DATA pExpansionData, OUT PLSA_SECURITY_OBJECT* ppGroupToExpand, OUT PDWORD pdwGroupToExpandDepth ) { DWORD dwError = 0; PLSA_SECURITY_OBJECT pGroupToExpand = NULL; DWORD dwGroupToExpandDepth = 0; const LW_HASH_ENTRY* pHashEntry = NULL; dwError = pExpansionData->dwLastError; BAIL_ON_LSA_ERROR(dwError); if (pExpansionData->pGroupsToExpand->sCount < 1) { // Nothing to return goto cleanup; } if (pExpansionData->bIsIteratorInitialized) { pHashEntry = LwHashNext(&pExpansionData->GroupsToExpandIterator); } if (!pHashEntry) { // Either the iterator is not initialized or we // reached the end of the hash table and need to start over. dwError = LwHashGetIterator( pExpansionData->pGroupsToExpand, &pExpansionData->GroupsToExpandIterator); BAIL_ON_LSA_ERROR(dwError); pExpansionData->bIsIteratorInitialized = TRUE; pHashEntry = LwHashNext(&pExpansionData->GroupsToExpandIterator); if (!pHashEntry) { dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } } pGroupToExpand = (PLSA_SECURITY_OBJECT) pHashEntry->pKey; dwGroupToExpandDepth = (size_t) pHashEntry->pValue; dwGroupToExpandDepth++; // Move the object to the expanded list. Note that the object is // not necessarily expanded yet, but we must remove it from // the "to expand" list. It does not hurt to track it in the // "expanded" list. dwError = LwHashSetValue(pExpansionData->pExpandedGroups, pGroupToExpand, (PVOID)(size_t)dwGroupToExpandDepth); BAIL_ON_LSA_ERROR(dwError); dwError = LwHashRemoveKey(pExpansionData->pGroupsToExpand, pGroupToExpand); if (dwError) { LSA_LOG_DEBUG("ASSERT: cannot fail"); } BAIL_ON_LSA_ERROR(dwError); cleanup: *ppGroupToExpand = pGroupToExpand; *pdwGroupToExpandDepth = dwGroupToExpandDepth; return dwError; error: ADCacheSafeFreeObject(&pGroupToExpand); dwGroupToExpandDepth = 0; if (dwError && !pExpansionData->dwLastError) { pExpansionData->dwLastError = dwError; } goto cleanup; }