/* Editor Settings: expandtabs and use 4 spaces for indentation * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: * * -*- mode: c, c-basic-offset: 4 -*- */ /* * Copyright Likewise Software 2004-2008 * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. You should have received a copy of the GNU General * Public License along with this program. If not, see * . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ /* * Copyright (C) Likewise Software. All rights reserved. * * Module Name: * * batch.c * * Abstract: * * Likewise Security and Authentication Subsystem (LSASS) * * Active Directory Authentication Provider * * Authors: Wei Fu (wfu@likewisesoftware.com) * Danilo Almeida (dalmeida@likewisesoftware.com) */ #include "adprovider.h" #include "batch_build.h" #include "batch_marshal.h" #include "batch_gather.h" #include "batch_p.h" static PCSTR LsaAdBatchParseDcPart( IN PCSTR pszDn ) { PCSTR pszFound = NULL; const char DC_PART[] = "dc="; if (!strncasecmp(pszDn, DC_PART, sizeof(DC_PART))) { return pszDn; } pszFound = strstr(pszDn, ",dc="); if (pszFound) { return pszFound + 1; } pszFound = strstr(pszDn, ",DC="); if (pszFound) { return pszFound + 1; } pszFound = strstr(pszDn, ",Dc="); if (pszFound) { return pszFound + 1; } pszFound = strstr(pszDn, ",dC="); if (pszFound) { return pszFound + 1; } return NULL; } static DWORD LsaAdBatchCheckDomainModeCompatibility( IN PCSTR pszDnsDomainName, IN BOOLEAN bIsExternalTrust, IN OPTIONAL PCSTR pszDomainDN ) { DWORD dwError = 0; PLSA_DM_LDAP_CONNECTION pConn = NULL; PSTR pszCellDN = NULL; ADConfigurationMode adMode = UnknownMode; PSTR pszLocalDomainDn = NULL; PCSTR pszDomainDnToUse = pszDomainDN; // When the primary domain is default mode, need make sure the // trusted domain are in the same mode. Delete those domains // that have inconsistent execution mode. if (gpADProviderData->dwDirectoryMode != DEFAULT_MODE) { goto cleanup; } if (bIsExternalTrust) { // Exclude all the external trusts in default mode to inherit the feature from 4.0 // To be specific, external trust in default mode is not supported. dwError = LW_ERROR_INCOMPATIBLE_MODES_BETWEEN_TRUSTEDDOMAINS; BAIL_ON_LSA_ERROR(dwError); } if (!pszDomainDnToUse) { dwError = LwLdapConvertDomainToDN(pszDnsDomainName, &pszLocalDomainDn); BAIL_ON_LSA_ERROR(dwError); pszDomainDnToUse = pszLocalDomainDn; } dwError = LsaDmLdapOpenDc(pszDnsDomainName, &pConn); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateStringPrintf(&pszCellDN, "CN=$LikewiseIdentityCell,%s", pszDomainDnToUse); BAIL_ON_LSA_ERROR(dwError); dwError = ADGetConfigurationMode( pConn, pszCellDN, &adMode); BAIL_ON_LSA_ERROR(dwError); if (adMode != gpADProviderData->adConfigurationMode) { dwError = LW_ERROR_INCOMPATIBLE_MODES_BETWEEN_TRUSTEDDOMAINS; BAIL_ON_LSA_ERROR(dwError); } cleanup: LsaDmLdapClose(pConn); LW_SAFE_FREE_STRING(pszCellDN); LW_SAFE_FREE_STRING(pszLocalDomainDn); return dwError; error: goto cleanup; } static DWORD LsaAdBatchGetDomainEntryType( IN PCSTR pszDomainName, IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN OPTIONAL PCSTR pszDomainDN, OUT PBOOLEAN pbSkip, OUT PBOOLEAN pbIsOneWayTrust ) { DWORD dwError = 0; DWORD dwTrustDirection = LSA_TRUST_DIRECTION_UNKNOWN; DWORD dwTrustMode = LSA_TRUST_MODE_UNKNOWN; BOOLEAN bIsExternalTrust = FALSE; BOOLEAN bSkip = FALSE; BOOLEAN bIsOneWayTrust = FALSE; // check trust information to determine whether we need this domain dwError = AD_DetermineTrustModeandDomainName(pszDomainName, &dwTrustDirection, &dwTrustMode, NULL, NULL); if (LW_ERROR_NO_SUCH_DOMAIN == dwError) { dwError = 0; bSkip = TRUE; goto cleanup; } BAIL_ON_LSA_ERROR(dwError); switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_DN: if (dwTrustDirection != LSA_TRUST_DIRECTION_TWO_WAY && dwTrustDirection != LSA_TRUST_DIRECTION_SELF) { bSkip = TRUE; } break; case LSA_AD_BATCH_QUERY_TYPE_BY_SID: case LSA_AD_BATCH_QUERY_TYPE_BY_NT4: switch (gpADProviderData->dwDirectoryMode) { case DEFAULT_MODE: if (dwTrustDirection != LSA_TRUST_DIRECTION_TWO_WAY && dwTrustDirection != LSA_TRUST_DIRECTION_SELF) { bSkip = TRUE; } break; case CELL_MODE: case UNPROVISIONED_MODE: if (dwTrustDirection != LSA_TRUST_DIRECTION_ONE_WAY && dwTrustDirection != LSA_TRUST_DIRECTION_TWO_WAY && dwTrustDirection != LSA_TRUST_DIRECTION_SELF) { bSkip = TRUE; } else if (dwTrustDirection == LSA_TRUST_DIRECTION_ONE_WAY) { bIsOneWayTrust = TRUE; } break; default: dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } break; default: dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } if (!bSkip) { bIsExternalTrust = (dwTrustMode == LSA_TRUST_MODE_EXTERNAL) ? TRUE : FALSE; dwError = LsaAdBatchCheckDomainModeCompatibility(pszDomainName, bIsExternalTrust, pszDomainDN); if (dwError == LW_ERROR_INCOMPATIBLE_MODES_BETWEEN_TRUSTEDDOMAINS) { dwError = 0; bSkip = TRUE; LSA_LOG_DEBUG("Mark trusted domain %s [skip] due to incompatible modes from primary domain %s", pszDomainName, gpADProviderData->szDomain); } BAIL_ON_LSA_ERROR(dwError); } cleanup: *pbSkip = bSkip; *pbIsOneWayTrust = bIsOneWayTrust; return dwError; error: goto cleanup; } DWORD LsaAdBatchGetDomainFromNT4Name( OUT PSTR* ppszDomainName, IN PCSTR pszNT4Name ) { DWORD dwError = 0; PCSTR pszSeparator = NULL; size_t sLength = 0; PSTR pszDomainName = NULL; pszSeparator = strchr(pszNT4Name, LsaGetDomainSeparator()); if (!pszSeparator) { dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } sLength = pszSeparator - pszNT4Name; dwError = LwStrndup(pszNT4Name, sLength, &pszDomainName); BAIL_ON_LSA_ERROR(dwError); cleanup: *ppszDomainName = pszDomainName; return dwError; error: LW_SAFE_FREE_STRING(pszDomainName); goto cleanup; } static void LsaAdBatchFreeDomainListElements( IN OUT PLSA_LIST_LINKS pDomainList) { if (pDomainList && pDomainList->Next && pDomainList->Prev) { while (!LsaListIsEmpty(pDomainList)) { PLSA_LIST_LINKS pLinks = LsaListRemoveTail(pDomainList); PLSA_AD_BATCH_DOMAIN_ENTRY pEntry = LW_STRUCT_FROM_FIELD(pLinks, LSA_AD_BATCH_DOMAIN_ENTRY, DomainEntryListLinks); LsaAdBatchDestroyDomainEntry(&pEntry); } } } static void LsaAdBatchFreeBatchItemListElements( IN OUT PLSA_LIST_LINKS pBatchItemList) { if (pBatchItemList && pBatchItemList->Next && pBatchItemList->Prev) { while (!LsaListIsEmpty(pBatchItemList)) { PLSA_LIST_LINKS pLinks = LsaListRemoveTail(pBatchItemList); PLSA_AD_BATCH_ITEM pItem = LW_STRUCT_FROM_FIELD(pLinks, LSA_AD_BATCH_ITEM, BatchItemListLinks); LsaAdBatchDestroyBatchItem(&pItem); } } } static DWORD LsaAdBatchCreateDomainEntry( OUT PLSA_AD_BATCH_DOMAIN_ENTRY* ppEntry, IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN PCSTR pszMatchTerm ) { DWORD dwError = 0; PLSA_AD_BATCH_DOMAIN_ENTRY pEntry = NULL; PSTR pszDnsDomainName = NULL; PSTR pszNetbiosDomainName = NULL; PCSTR pszDcPart = NULL; PSTR pszDomainSid = NULL; BOOLEAN bSkip = FALSE; BOOLEAN bIsOneWayTrust = FALSE; PSTR pszDomainName = NULL; switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_DN: dwError = LwLdapConvertDNToDomain(pszMatchTerm, &pszDnsDomainName); BAIL_ON_LSA_ERROR(dwError); dwError = LsaDmWrapGetDomainName(pszDnsDomainName, NULL, &pszNetbiosDomainName); BAIL_ON_LSA_ERROR(dwError); pszDcPart = pszMatchTerm; break; case LSA_AD_BATCH_QUERY_TYPE_BY_SID: dwError = LsaDmEngineGetDomainNameAndSidByObjectSidWithDiscovery( pszMatchTerm, &pszDnsDomainName, &pszNetbiosDomainName, &pszDomainSid); BAIL_ON_LSA_ERROR(dwError); break; case LSA_AD_BATCH_QUERY_TYPE_BY_NT4: dwError = LsaAdBatchGetDomainFromNT4Name(&pszDomainName, pszMatchTerm); BAIL_ON_LSA_ERROR(dwError); dwError = LsaDmEngineGetDomainNameWithDiscovery( pszDomainName, &pszDnsDomainName, &pszNetbiosDomainName); BAIL_ON_LSA_ERROR(dwError); break; default: dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } dwError = LsaAdBatchGetDomainEntryType( pszDnsDomainName, QueryType, pszDcPart, &bSkip, &bIsOneWayTrust); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateMemory(sizeof(*pEntry), (PVOID*)&pEntry); BAIL_ON_LSA_ERROR(dwError); LsaListInit(&pEntry->BatchItemList); pEntry->QueryType = QueryType; pEntry->pszDnsDomainName = pszDnsDomainName; pszDnsDomainName = NULL; pEntry->pszNetbiosDomainName = pszNetbiosDomainName; pszNetbiosDomainName = NULL; if (bSkip) { SetFlag(pEntry->Flags, LSA_AD_BATCH_DOMAIN_ENTRY_FLAG_SKIP); LSA_LOG_DEBUG("Trusted domain %s' is marked skip", pEntry->pszDnsDomainName); } if (bIsOneWayTrust) { SetFlag(pEntry->Flags, LSA_AD_BATCH_DOMAIN_ENTRY_FLAG_IS_ONE_WAY_TRUST); } switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_DN: LSA_ASSERT(pszDcPart); pEntry->QueryMatch.ByDn.pszDcPart = pszDcPart; pszDcPart = NULL; break; case LSA_AD_BATCH_QUERY_TYPE_BY_SID: LSA_ASSERT(pszDomainSid); pEntry->QueryMatch.BySid.pszDomainSid = pszDomainSid; pszDomainSid = NULL; pEntry->QueryMatch.BySid.sDomainSidLength = strlen(pEntry->QueryMatch.BySid.pszDomainSid); break; case LSA_AD_BATCH_QUERY_TYPE_BY_NT4: pEntry->QueryMatch.ByNT4.sNetbiosDomainNameLength = strlen(pEntry->pszNetbiosDomainName); pEntry->QueryMatch.ByNT4.sDnsDomainNameLength = strlen(pEntry->pszDnsDomainName); LSA_ASSERT(pEntry->QueryMatch.ByNT4.sNetbiosDomainNameLength > 0); LSA_ASSERT(pEntry->QueryMatch.ByNT4.sDnsDomainNameLength > 0); break; default: dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } *ppEntry = pEntry; cleanup: LW_SAFE_FREE_STRING(pszDnsDomainName); LW_SAFE_FREE_STRING(pszNetbiosDomainName); LW_SAFE_FREE_STRING(pszDomainSid); LW_SAFE_FREE_STRING(pszDomainName); return dwError; error: *ppEntry = NULL; LsaAdBatchDestroyDomainEntry(&pEntry); goto cleanup; } static VOID LsaAdBatchDestroyDomainEntry( IN OUT PLSA_AD_BATCH_DOMAIN_ENTRY* ppEntry ) { PLSA_AD_BATCH_DOMAIN_ENTRY pEntry = *ppEntry; if (pEntry) { LW_SAFE_FREE_STRING(pEntry->pszDnsDomainName); LW_SAFE_FREE_STRING(pEntry->pszNetbiosDomainName); switch (pEntry->QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_SID: LW_SAFE_FREE_STRING(pEntry->QueryMatch.BySid.pszDomainSid); break; } while (!LsaListIsEmpty(&pEntry->BatchItemList)) { PLSA_LIST_LINKS pLinks = LsaListRemoveTail(&pEntry->BatchItemList); PLSA_AD_BATCH_ITEM pItem = LW_STRUCT_FROM_FIELD(pLinks, LSA_AD_BATCH_ITEM, BatchItemListLinks); LsaAdBatchDestroyBatchItem(&pItem); } LwFreeMemory(pEntry); *ppEntry = NULL; } } static DWORD LsaAdBatchCreateBatchItem( OUT PLSA_AD_BATCH_ITEM* ppItem, IN PLSA_AD_BATCH_DOMAIN_ENTRY pDomainEntry, IN LSA_AD_BATCH_QUERY_TYPE QueryTermType, IN OPTIONAL PCSTR pszString, IN OPTIONAL PDWORD pdwId ) { DWORD dwError = 0; PLSA_AD_BATCH_ITEM pItem = NULL; PCSTR pszQueryString = pszString; if (!LSA_IS_XOR(!LW_IS_NULL_OR_EMPTY_STR(pszString), pdwId)) { dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } dwError = LwAllocateMemory(sizeof(*pItem), (PVOID*)&pItem); BAIL_ON_LSA_ERROR(dwError); if (LSA_AD_BATCH_QUERY_TYPE_BY_NT4 == QueryTermType) { LSA_ASSERT(pszQueryString); // We only want the SAM account name portion. pszQueryString = index(pszQueryString, LsaGetDomainSeparator()); if (!pszQueryString) { dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } pszQueryString++; if (LW_IS_NULL_OR_EMPTY_STR(pszQueryString)) { dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } } pItem->QueryTerm.Type = QueryTermType; if (pszQueryString) { pItem->QueryTerm.pszString = pszQueryString; } else if (pdwId) { pItem->QueryTerm.dwId = *pdwId; } cleanup: if (dwError) { LsaAdBatchDestroyBatchItem(&pItem); } *ppItem = pItem; return dwError; error: // Do not handle error here, instead, do it in cleanup because of 'goto cleanup' goto cleanup; } static VOID LsaAdBatchDestroyBatchItem( IN OUT PLSA_AD_BATCH_ITEM* ppItem ) { PLSA_AD_BATCH_ITEM pItem = *ppItem; if (pItem) { LsaAdBatchDestroyBatchItemContents(pItem); LwFreeMemory(pItem); *ppItem = NULL; } } VOID LsaAdBatchDestroyBatchItemContents( IN OUT PLSA_AD_BATCH_ITEM pItem ) { if (IsSetFlag(pItem->Flags, LSA_AD_BATCH_ITEM_FLAG_ALLOCATED_MATCH_TERM)) { LW_SAFE_FREE_STRING(pItem->pszQueryMatchTerm); } LW_SAFE_FREE_STRING(pItem->pszSid); LW_SAFE_FREE_STRING(pItem->pszSamAccountName); LW_SAFE_FREE_STRING(pItem->pszDn); switch (pItem->ObjectType) { case LSA_AD_BATCH_OBJECT_TYPE_USER: LW_SAFE_FREE_STRING(pItem->UserInfo.pszAlias); LW_SAFE_FREE_STRING(pItem->UserInfo.pszPasswd); LW_SAFE_FREE_STRING(pItem->UserInfo.pszGecos); LW_SAFE_FREE_STRING(pItem->UserInfo.pszHomeDirectory); LW_SAFE_FREE_STRING(pItem->UserInfo.pszShell); LW_SAFE_FREE_STRING(pItem->UserInfo.pszUserPrincipalName); break; case LSA_AD_BATCH_OBJECT_TYPE_GROUP: LW_SAFE_FREE_STRING(pItem->GroupInfo.pszAlias); LW_SAFE_FREE_STRING(pItem->GroupInfo.pszPasswd); break; } memset(pItem, 0, sizeof(*pItem)); } static DWORD LsaAdBatchGetDomainMatchTerm( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN PCSTR pszQueryTerm, OUT PCSTR* ppszMatchTerm ) { DWORD dwError = 0; PCSTR pszMatchTerm = NULL; switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_DN: pszMatchTerm = LsaAdBatchParseDcPart(pszQueryTerm); break; case LSA_AD_BATCH_QUERY_TYPE_BY_SID: pszMatchTerm = pszQueryTerm; break; case LSA_AD_BATCH_QUERY_TYPE_BY_NT4: pszMatchTerm = pszQueryTerm; break; default: dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } *ppszMatchTerm = pszMatchTerm; cleanup: return dwError; error: *ppszMatchTerm = NULL; goto cleanup; } static BOOLEAN LsaAdBatchIsDomainSidMatch( IN PCSTR pszDomainSid, IN size_t sDomainSidLength, IN PCSTR pszObjectSid ) { BOOLEAN bIsMatch = FALSE; if (!strncasecmp(pszObjectSid, pszDomainSid, sDomainSidLength) && (!pszObjectSid[sDomainSidLength] || ('-' == pszObjectSid[sDomainSidLength]))) { bIsMatch = TRUE; } return bIsMatch; } static BOOLEAN LsaAdBatchIsDomainNameMatch( IN PCSTR pszDomainName, IN size_t sDomainNameLength, IN PCSTR pszObjectNT4Name ) { BOOLEAN bIsMatch = FALSE; if (!strncasecmp(pszObjectNT4Name, pszDomainName, sDomainNameLength) && (!pszObjectNT4Name[sDomainNameLength] || (LsaGetDomainSeparator() == pszObjectNT4Name[sDomainNameLength]))) { bIsMatch = TRUE; } return bIsMatch; } static DWORD LsaAdBatchMatchDomain( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN PCSTR pszMatchTerm, IN PLSA_AD_BATCH_DOMAIN_ENTRY pEntry, OUT PBOOLEAN pbIsMatch ) { DWORD dwError = 0; BOOLEAN bIsMatch = FALSE; switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_DN: if (!strcasecmp(pEntry->QueryMatch.ByDn.pszDcPart, pszMatchTerm)) { bIsMatch = TRUE; } break; case LSA_AD_BATCH_QUERY_TYPE_BY_SID: if (LsaAdBatchIsDomainSidMatch(pEntry->QueryMatch.BySid.pszDomainSid, pEntry->QueryMatch.BySid.sDomainSidLength, pszMatchTerm)) { bIsMatch = TRUE; } break; case LSA_AD_BATCH_QUERY_TYPE_BY_NT4: if (LsaAdBatchIsDomainNameMatch(pEntry->pszNetbiosDomainName, pEntry->QueryMatch.ByNT4.sNetbiosDomainNameLength, pszMatchTerm) || LsaAdBatchIsDomainNameMatch(pEntry->pszDnsDomainName, pEntry->QueryMatch.ByNT4.sDnsDomainNameLength, pszMatchTerm)) { bIsMatch = TRUE; } default: dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } *pbIsMatch = bIsMatch; cleanup: return dwError; error: *pbIsMatch = FALSE; goto cleanup; } DWORD LsaAdBatchQueryCellConfigurationMode( IN PCSTR pszDnsDomainName, IN PCSTR pszCellDN, OUT ADConfigurationMode* pAdMode ) { DWORD dwError = 0; PLSA_DM_LDAP_CONNECTION pConn = NULL; ADConfigurationMode adMode = UnknownMode; dwError = LsaDmLdapOpenDc(pszDnsDomainName, &pConn); BAIL_ON_LSA_ERROR(dwError); dwError = ADGetConfigurationMode( pConn, pszCellDN, &adMode); BAIL_ON_LSA_ERROR(dwError); *pAdMode = adMode; cleanup: LsaDmLdapClose(pConn); return dwError; error: *pAdMode = UnknownMode; goto cleanup; } BOOLEAN LsaAdBatchIsDefaultSchemaMode( VOID ) { return ((DEFAULT_MODE == gpADProviderData->dwDirectoryMode) && (SchemaMode == gpADProviderData->adConfigurationMode)); } BOOLEAN LsaAdBatchIsUnprovisionedMode( VOID ) { return (UNPROVISIONED_MODE == gpADProviderData->dwDirectoryMode); } DWORD LsaAdBatchConvertQTListToBIList( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN DWORD dwQueryItemsCount, IN OPTIONAL PSTR* ppszQueryList, IN OPTIONAL PDWORD pdwId, OUT PLSA_LIST_LINKS pBatchItemList, OUT PDWORD pdwTotalBatchItemCount ) { DWORD dwError = 0; DWORD i = 0; DWORD dwTotalBatchItemCount = 0; if (!LSA_IS_XOR(ppszQueryList, pdwId)) { LSA_ASSERT(FALSE); dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } LsaListInit(pBatchItemList); for (i = 0; i < dwQueryItemsCount; i++) { PLSA_AD_BATCH_ITEM pBatchItem = NULL; // Set pBatchItem->pDomainEntry to NULL, // Showing we have no knowledge of which domain this BatchItem belongs to at this point if (ppszQueryList) { dwError = LsaAdBatchCreateBatchItem( &pBatchItem, NULL, QueryType, ppszQueryList[i], NULL); BAIL_ON_LSA_ERROR(dwError); } else if (pdwId) { dwError = LsaAdBatchCreateBatchItem( &pBatchItem, NULL, QueryType, NULL, &pdwId[i]); BAIL_ON_LSA_ERROR(dwError); } if (pBatchItem) { LsaListInsertTail(pBatchItemList, &pBatchItem->BatchItemListLinks); dwTotalBatchItemCount++; } } *pdwTotalBatchItemCount = dwTotalBatchItemCount; cleanup: return dwError; error: goto cleanup; } // Note: Before calling the following function, we should // (1) have pszSid stored in pBatchItem; Or // (2) pszSid being NULL (meaning we couldn't resolve such object into Sid from Pseudo) static DWORD LsaAdBatchSplitBIListToBIListPerDomain( IN OUT PLSA_LIST_LINKS pBatchItemList, OUT PLSA_LIST_LINKS pDomainList ) { DWORD dwError = 0; PLSA_LIST_LINKS pDomainLinks = NULL; PLSA_AD_BATCH_ITEM pBatchItem = NULL; if (!pBatchItemList) { dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } LsaListInit(pDomainList); while (!LsaListIsEmpty(pBatchItemList)) { PCSTR pszMatchTerm = NULL; PLSA_LIST_LINKS pBILinks = NULL; PLSA_AD_BATCH_DOMAIN_ENTRY pFoundEntry = NULL; pBILinks = LsaListRemoveHead(pBatchItemList); pBatchItem = LW_STRUCT_FROM_FIELD(pBILinks, LSA_AD_BATCH_ITEM, BatchItemListLinks); if (LW_IS_NULL_OR_EMPTY_STR(pBatchItem->pszSid)) { LsaAdBatchDestroyBatchItem(&pBatchItem); continue; } dwError = LsaAdBatchGetDomainMatchTerm( LSA_AD_BATCH_QUERY_TYPE_BY_SID, pBatchItem->pszSid, &pszMatchTerm); BAIL_ON_LSA_ERROR(dwError); for (pDomainLinks = pDomainList->Next; pDomainLinks != pDomainList; pDomainLinks = pDomainLinks->Next) { PLSA_AD_BATCH_DOMAIN_ENTRY pEntry = LW_STRUCT_FROM_FIELD(pDomainLinks, LSA_AD_BATCH_DOMAIN_ENTRY, DomainEntryListLinks); BOOLEAN bIsMatch = FALSE; dwError = LsaAdBatchMatchDomain( LSA_AD_BATCH_QUERY_TYPE_BY_SID, pszMatchTerm, pEntry, &bIsMatch); BAIL_ON_LSA_ERROR(dwError); if (bIsMatch) { pFoundEntry = pEntry; break; } } if (!pFoundEntry) { dwError = LsaAdBatchCreateDomainEntry( &pFoundEntry, LSA_AD_BATCH_QUERY_TYPE_BY_SID, pszMatchTerm); if (LW_ERROR_NO_SUCH_DOMAIN == dwError) { LSA_LOG_DEBUG("Domain not found for query item - '%s'", pBatchItem->pszSid); dwError = 0; continue; } BAIL_ON_LSA_ERROR(dwError); LsaListInsertTail(pDomainList, &pFoundEntry->DomainEntryListLinks); } if (!IsSetFlag(pFoundEntry->Flags, LSA_AD_BATCH_DOMAIN_ENTRY_FLAG_SKIP)) { LsaListInsertTail(&pFoundEntry->BatchItemList, &pBatchItem->BatchItemListLinks); pBatchItem = NULL; pFoundEntry->dwBatchItemCount++; } // Destroy pBatchItem else { LsaAdBatchDestroyBatchItem(&pBatchItem); } pFoundEntry = NULL; } cleanup: LsaAdBatchDestroyBatchItem(&pBatchItem); return dwError; error: goto cleanup; } static DWORD LsaAdBatchSplitQTListToBIListPerDomain( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN DWORD dwQueryItemsCount, IN PSTR* ppszQueryList, OUT PLSA_LIST_LINKS pDomainList ) { DWORD dwError = 0; PLSA_LIST_LINKS pLinks = NULL; DWORD i = 0; LsaListInit(pDomainList); for (i = 0; i < dwQueryItemsCount; i++) { PCSTR pszMatchTerm = NULL; PLSA_AD_BATCH_DOMAIN_ENTRY pFoundEntry = NULL; dwError = LsaAdBatchGetDomainMatchTerm( QueryType, ppszQueryList[i], &pszMatchTerm); BAIL_ON_LSA_ERROR(dwError); for (pLinks = pDomainList->Next; pLinks != pDomainList; pLinks = pLinks->Next) { PLSA_AD_BATCH_DOMAIN_ENTRY pEntry = LW_STRUCT_FROM_FIELD(pLinks, LSA_AD_BATCH_DOMAIN_ENTRY, DomainEntryListLinks); BOOLEAN bIsMatch = FALSE; dwError = LsaAdBatchMatchDomain( QueryType, pszMatchTerm, pEntry, &bIsMatch); BAIL_ON_LSA_ERROR(dwError); if (bIsMatch) { pFoundEntry = pEntry; break; } } if (!pFoundEntry) { dwError = LsaAdBatchCreateDomainEntry( &pFoundEntry, QueryType, pszMatchTerm); if (LW_ERROR_NO_SUCH_DOMAIN == dwError) { LSA_LOG_DEBUG("Domain not found for query item - '%s'", ppszQueryList[i]); dwError = 0; continue; } BAIL_ON_LSA_ERROR(dwError); LsaListInsertTail(pDomainList, &pFoundEntry->DomainEntryListLinks); } if (!IsSetFlag(pFoundEntry->Flags, LSA_AD_BATCH_DOMAIN_ENTRY_FLAG_SKIP)) { PLSA_AD_BATCH_ITEM pBatchItem = NULL; dwError = LsaAdBatchCreateBatchItem( &pBatchItem, pFoundEntry, QueryType, ppszQueryList[i], NULL); BAIL_ON_LSA_ERROR(dwError); if (pBatchItem) { LsaListInsertTail(&pFoundEntry->BatchItemList, &pBatchItem->BatchItemListLinks); } pFoundEntry->dwBatchItemCount++; } pFoundEntry = NULL; } cleanup: return dwError; error: goto cleanup; } static DWORD LsaAdBatchResolveObjectsForDomainList( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN PLSA_LIST_LINKS pDomainList, IN BOOLEAN bResolvePseudoObjects, OUT PDWORD pdwObjectsCount, OUT PLSA_SECURITY_OBJECT** pppObjects ) { DWORD dwError = 0; // Do not free pLinks PLSA_LIST_LINKS pLinks = NULL; DWORD dwObjectsCount = 0; PLSA_SECURITY_OBJECT* ppObjects = NULL; DWORD dwCurrentIndex = 0; for (pLinks = pDomainList->Next; pLinks != pDomainList; pLinks = pLinks->Next) { PLSA_AD_BATCH_DOMAIN_ENTRY pEntry = LW_STRUCT_FROM_FIELD(pLinks, LSA_AD_BATCH_DOMAIN_ENTRY, DomainEntryListLinks); if (IsSetFlag(pEntry->Flags, LSA_AD_BATCH_DOMAIN_ENTRY_FLAG_SKIP)) { continue; } dwError = LsaAdBatchFindObjectsForDomainEntry( QueryType, bResolvePseudoObjects, pEntry); BAIL_ON_LSA_ERROR(dwError); dwObjectsCount += pEntry->dwBatchItemCount; } dwError = LwAllocateMemory( dwObjectsCount * sizeof(*ppObjects), (PVOID*)&ppObjects); BAIL_ON_LSA_ERROR(dwError); // Combine results dwCurrentIndex = 0; for (pLinks = pDomainList->Next; pLinks != pDomainList; pLinks = pLinks->Next) { PLSA_AD_BATCH_DOMAIN_ENTRY pEntry = LW_STRUCT_FROM_FIELD(pLinks, LSA_AD_BATCH_DOMAIN_ENTRY, DomainEntryListLinks); DWORD dwDomainObjectsCount = 0; if (IsSetFlag(pEntry->Flags, LSA_AD_BATCH_DOMAIN_ENTRY_FLAG_SKIP)) { continue; } dwError = LsaAdBatchMarshalList( pEntry->pszDnsDomainName, pEntry->pszNetbiosDomainName, &pEntry->BatchItemList, dwObjectsCount - dwCurrentIndex, &ppObjects[dwCurrentIndex], &dwDomainObjectsCount); BAIL_ON_LSA_ERROR(dwError); dwCurrentIndex += dwDomainObjectsCount; } LSA_ASSERT(dwCurrentIndex <= dwObjectsCount); // Compress the output if (dwCurrentIndex < dwObjectsCount) { PLSA_SECURITY_OBJECT* ppTempObjects = NULL; dwError = LwAllocateMemory( dwCurrentIndex * sizeof(*ppTempObjects), (PVOID*)&ppTempObjects); BAIL_ON_LSA_ERROR(dwError); memcpy(ppTempObjects, ppObjects, sizeof(*ppObjects) * dwCurrentIndex); LwFreeMemory(ppObjects); ppObjects = ppTempObjects; dwObjectsCount = dwCurrentIndex; } *pdwObjectsCount = dwObjectsCount; *pppObjects = ppObjects; cleanup: return dwError; error: *pdwObjectsCount = 0; *pppObjects = NULL; ADCacheSafeFreeObjectList(dwObjectsCount, &ppObjects); goto cleanup; } static DWORD LsaAdBatchFindObjectsRealBeforePseudo( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN DWORD dwQueryItemsCount, IN PSTR* ppszQueryList, OUT PDWORD pdwObjectsCount, OUT PLSA_SECURITY_OBJECT** pppObjects ) { DWORD dwError = 0; DWORD dwObjectsCount = 0; PLSA_SECURITY_OBJECT* ppObjects = NULL; LSA_LIST_LINKS DomainList = {0}; dwError = LsaAdBatchSplitQTListToBIListPerDomain( QueryType, dwQueryItemsCount, ppszQueryList, &DomainList); BAIL_ON_LSA_ERROR(dwError); dwError = LsaAdBatchResolveObjectsForDomainList( QueryType, &DomainList, TRUE, &dwObjectsCount, &ppObjects); BAIL_ON_LSA_ERROR(dwError); *pdwObjectsCount = dwObjectsCount; *pppObjects = ppObjects; cleanup: LsaAdBatchFreeDomainListElements(&DomainList); return dwError; error: *pdwObjectsCount = 0; *pppObjects = NULL; ADCacheSafeFreeObjectList(dwObjectsCount, &ppObjects); goto cleanup; } static DWORD LsaAdBatchFindObjectsPseudoBeforeReal( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN DWORD dwQueryItemsCount, IN OPTIONAL PSTR* ppszQueryList, IN OPTIONAL PDWORD pdwId, OUT PDWORD pdwObjectsCount, OUT PLSA_SECURITY_OBJECT** pppObjects ) { DWORD dwError = 0; DWORD dwObjectsCount = 0; PLSA_SECURITY_OBJECT* ppObjects = NULL; LSA_LIST_LINKS BatchItemList = {0}; LSA_LIST_LINKS DomainList = {0}; DWORD dwTotalBatchItemCount = 0; BOOLEAN bResolvedPseudo = FALSE; if (!LSA_IS_XOR(ppszQueryList, pdwId)) { LSA_ASSERT(FALSE); dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } dwError = LsaAdBatchConvertQTListToBIList( QueryType, dwQueryItemsCount, ppszQueryList, pdwId, &BatchItemList, &dwTotalBatchItemCount); BAIL_ON_LSA_ERROR(dwError); // We have no knowledge of domain information at this point dwError = LsaAdBatchResolvePseudoObjects( QueryType, NULL, dwTotalBatchItemCount, &BatchItemList, &bResolvedPseudo); BAIL_ON_LSA_ERROR(dwError); dwError = LsaAdBatchSplitBIListToBIListPerDomain( &BatchItemList, &DomainList); BAIL_ON_LSA_ERROR(dwError); dwError = LsaAdBatchResolveObjectsForDomainList( LSA_AD_BATCH_QUERY_TYPE_BY_SID, &DomainList, bResolvedPseudo ? FALSE : TRUE, &dwObjectsCount, &ppObjects); BAIL_ON_LSA_ERROR(dwError); *pdwObjectsCount = dwObjectsCount; *pppObjects = ppObjects; cleanup: LsaAdBatchFreeDomainListElements(&DomainList); LsaAdBatchFreeBatchItemListElements(&BatchItemList); return dwError; error: *pdwObjectsCount = 0; *pppObjects = NULL; ADCacheSafeFreeObjectList(dwObjectsCount, &ppObjects); goto cleanup; } DWORD LsaAdBatchFindSingleObject( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN OPTIONAL PCSTR pszQueryTerm, IN OPTIONAL PDWORD pdwId, OUT PLSA_SECURITY_OBJECT* ppObject ) { DWORD dwError = 0; DWORD dwCount = 0; PLSA_SECURITY_OBJECT* ppObjects = NULL; PLSA_SECURITY_OBJECT pObject = NULL; if (!LSA_IS_XOR(!LW_IS_NULL_OR_EMPTY_STR(pszQueryTerm), pdwId)) { LSA_ASSERT(FALSE); dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } if (!LW_IS_NULL_OR_EMPTY_STR(pszQueryTerm)) { dwError = LsaAdBatchFindObjects( QueryType, 1, (PSTR*)&pszQueryTerm, NULL, &dwCount, &ppObjects); BAIL_ON_LSA_ERROR(dwError); } else if (pdwId) { dwError = LsaAdBatchFindObjects( QueryType, 1, NULL, pdwId, &dwCount, &ppObjects); BAIL_ON_LSA_ERROR(dwError); } if (dwCount < 1 || !ppObjects[0]) { dwError = LW_ERROR_NO_SUCH_OBJECT; BAIL_ON_LSA_ERROR(dwError); } else if (dwCount > 1) { LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } pObject = ppObjects[0]; ppObjects[0] = NULL; cleanup: ADCacheSafeFreeObjectList(dwCount, &ppObjects); *ppObject = pObject; return dwError; error: ADCacheSafeFreeObject(&pObject); goto cleanup; } static DWORD LsaAdBatchFindObjectsInternal( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN DWORD dwQueryItemsCount, IN OPTIONAL PSTR* ppszQueryList, IN OPTIONAL PDWORD pdwId, OUT PDWORD pdwObjectsCount, OUT PLSA_SECURITY_OBJECT** pppObjects ) { DWORD dwError = 0; if (!LSA_IS_XOR(ppszQueryList, pdwId)) { LSA_ASSERT(FALSE); dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_DN: case LSA_AD_BATCH_QUERY_TYPE_BY_SID: case LSA_AD_BATCH_QUERY_TYPE_BY_NT4: case LSA_AD_BATCH_QUERY_TYPE_BY_USER_ALIAS: case LSA_AD_BATCH_QUERY_TYPE_BY_GROUP_ALIAS: if (pdwId) { LSA_ASSERT(FALSE); dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } break; case LSA_AD_BATCH_QUERY_TYPE_BY_UID: case LSA_AD_BATCH_QUERY_TYPE_BY_GID: if (ppszQueryList) { LSA_ASSERT(FALSE); dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } break; default: LSA_ASSERT(FALSE); dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_DN: case LSA_AD_BATCH_QUERY_TYPE_BY_SID: case LSA_AD_BATCH_QUERY_TYPE_BY_NT4: dwError = LsaAdBatchFindObjectsRealBeforePseudo( QueryType, dwQueryItemsCount, ppszQueryList, pdwObjectsCount, pppObjects); BAIL_ON_LSA_ERROR(dwError); break; case LSA_AD_BATCH_QUERY_TYPE_BY_USER_ALIAS: case LSA_AD_BATCH_QUERY_TYPE_BY_GROUP_ALIAS: case LSA_AD_BATCH_QUERY_TYPE_BY_UID: case LSA_AD_BATCH_QUERY_TYPE_BY_GID: dwError = LsaAdBatchFindObjectsPseudoBeforeReal( QueryType, dwQueryItemsCount, ppszQueryList, pdwId, pdwObjectsCount, pppObjects); BAIL_ON_LSA_ERROR(dwError); break; default: LSA_ASSERT(FALSE); dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } cleanup: return dwError; error: goto cleanup; } // When we come in with uid/gid in unprovision mode, we might get either type (user/group) back // Filter out the wrong typed objects static DWORD LsaAdBatchFilterMisTypeObjects( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN DWORD dwObjectsCount, IN OUT PLSA_SECURITY_OBJECT** pppObjects, OUT PDWORD pdwRemainingObjectsCount ) { DWORD dwError = 0; PLSA_SECURITY_OBJECT* ppObjects = *pppObjects; DWORD dwRemainingObjectsCount = 0; PLSA_SECURITY_OBJECT* ppRemainingObjects = NULL; DWORD i = 0; if (LSA_AD_BATCH_QUERY_TYPE_BY_UID != QueryType && LSA_AD_BATCH_QUERY_TYPE_BY_GID != QueryType) { dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } dwError = LwAllocateMemory( dwObjectsCount * sizeof(*ppRemainingObjects), (PVOID*)&ppRemainingObjects); BAIL_ON_LSA_ERROR(dwError); for (i = 0; i < dwObjectsCount; i++) { switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_UID: if (LSA_OBJECT_TYPE_USER == ppObjects[i]->type) { ppRemainingObjects[dwRemainingObjectsCount++] = ppObjects[i]; ppObjects[i] = NULL; } break; case LSA_AD_BATCH_QUERY_TYPE_BY_GID: if (LSA_OBJECT_TYPE_GROUP == ppObjects[i]->type) { ppRemainingObjects[dwRemainingObjectsCount++] = ppObjects[i]; ppObjects[i] = NULL; } break; } } *pdwRemainingObjectsCount = dwRemainingObjectsCount; *pppObjects = ppRemainingObjects; cleanup: ADCacheSafeFreeObjectList(dwObjectsCount, &ppObjects); return dwError; error: ADCacheSafeFreeObjectList(dwRemainingObjectsCount, &ppRemainingObjects); *pdwRemainingObjectsCount = 0; *ppRemainingObjects = NULL; goto cleanup; } DWORD LsaAdBatchFindObjects( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN DWORD dwQueryItemsCount, IN OPTIONAL PSTR* ppszQueryList, IN OPTIONAL PDWORD pdwId, OUT PDWORD pdwObjectsCount, OUT PLSA_SECURITY_OBJECT** pppObjects ) { DWORD dwError = 0; DWORD dwObjectsCount = 0; PLSA_SECURITY_OBJECT* ppObjects = NULL; dwError = LsaAdBatchFindObjectsInternal( QueryType, dwQueryItemsCount, ppszQueryList, pdwId, &dwObjectsCount, &ppObjects); BAIL_ON_LSA_ERROR(dwError); if (LsaAdBatchIsUnprovisionedMode() && (LSA_AD_BATCH_QUERY_TYPE_BY_UID == QueryType || LSA_AD_BATCH_QUERY_TYPE_BY_GID == QueryType)) { dwError = LsaAdBatchFilterMisTypeObjects( QueryType, dwObjectsCount, &ppObjects, &dwObjectsCount); BAIL_ON_LSA_ERROR(dwError); } *pdwObjectsCount = dwObjectsCount; *pppObjects = ppObjects; cleanup: return dwError; error: ADCacheSafeFreeObjectList(dwObjectsCount, &ppObjects); *pdwObjectsCount = 0; *pppObjects = NULL; goto cleanup; } static DWORD LsaAdBatchFindObjectsForDomainEntry( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN BOOLEAN bResolvePseudoObjects, IN OUT PLSA_AD_BATCH_DOMAIN_ENTRY pEntry ) { return LsaAdBatchFindObjectsForDomain( QueryType, pEntry->pszDnsDomainName, pEntry->pszNetbiosDomainName, IsSetFlag(pEntry->Flags, LSA_AD_BATCH_DOMAIN_ENTRY_FLAG_IS_ONE_WAY_TRUST), bResolvePseudoObjects, pEntry->dwBatchItemCount, &pEntry->BatchItemList); } static DWORD LsaAdBatchFindObjectsForDomain( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN PCSTR pszDnsDomainName, IN PCSTR pszNetbiosDomainName, IN BOOLEAN bIsOneWayTrust, IN BOOLEAN bResolvePseudoObjects, IN DWORD dwCount, IN OUT PLSA_LIST_LINKS pBatchItemList ) { DWORD dwError = 0; // Do not delete PLSA_LIST_LINKS pLinks = NULL; if (bIsOneWayTrust && LSA_AD_BATCH_QUERY_TYPE_BY_DN == QueryType) { // This should never happen, this domain should already be skipped. LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } if (bIsOneWayTrust && ((LSA_AD_BATCH_QUERY_TYPE_BY_SID == QueryType) || (LSA_AD_BATCH_QUERY_TYPE_BY_NT4 == QueryType))) { dwError = LsaAdBatchResolveRpcObjects( QueryType, pszDnsDomainName, pszNetbiosDomainName, dwCount, pBatchItemList); BAIL_ON_LSA_ERROR(dwError); } else { dwError = LsaAdBatchResolveRealObjects( QueryType, pszDnsDomainName, dwCount, pBatchItemList); BAIL_ON_LSA_ERROR(dwError); } // If Default schema, real objects already have the pseudo information // If Unprovisioned mode, no need to get PseudoMessages. if (!LsaAdBatchIsDefaultSchemaMode() && !LsaAdBatchIsUnprovisionedMode() && bResolvePseudoObjects) { dwError = LsaAdBatchResolvePseudoObjects( LSA_AD_BATCH_QUERY_TYPE_BY_SID, pszDnsDomainName, dwCount, pBatchItemList, NULL); BAIL_ON_LSA_ERROR(dwError); } cleanup: return dwError; error: for (pLinks = pBatchItemList->Next; pLinks != pBatchItemList; pLinks = pLinks->Next) { PLSA_AD_BATCH_ITEM pBatchItem = LW_STRUCT_FROM_FIELD(pLinks, LSA_AD_BATCH_ITEM, BatchItemListLinks); if (pBatchItem->Flags & LSA_AD_BATCH_ITEM_FLAG_ERROR) { LSA_LOG_ERROR( "An error occurred while looking up information for user/group (sid = '%s', name = '%s\\%s')", LSA_SAFE_LOG_STRING(pBatchItem->pszSid), LSA_SAFE_LOG_STRING(pszNetbiosDomainName), LSA_SAFE_LOG_STRING(pBatchItem->pszSamAccountName)); } } goto cleanup; } static DWORD LsaAdBatchGetMaxQuerySize( VOID ) { return LSA_AD_BATCH_MAX_QUERY_SIZE; } static DWORD LsaAdBatchGetMaxQueryCount( VOID ) { return LSA_AD_BATCH_MAX_QUERY_COUNT; } static DWORD LsaAdBatchResolveRpcObjects( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN PCSTR pszDnsDomainName, IN PCSTR pszNetbiosDomainName, IN DWORD dwTotalItemCount, // List of PLSA_AD_BATCH_ITEM IN OUT PLSA_LIST_LINKS pBatchItemList ) { DWORD dwError = 0; DWORD dwMaxQueryCount = LsaAdBatchGetMaxQueryCount(); PSTR* ppszQueryList = NULL; PLSA_TRANSLATED_NAME_OR_SID* ppTranslatedNames = NULL; DWORD dwQueryCount = 0; DWORD dwCount = 0; DWORD i = 0; PLSA_LIST_LINKS pLinks = NULL; PLSA_LIST_LINKS pNextLinks = NULL; for (pLinks = pBatchItemList->Next; pLinks != pBatchItemList; pLinks = pNextLinks) { pNextLinks = NULL; LwFreeStringArray(ppszQueryList, dwQueryCount); ppszQueryList = NULL; if (ppTranslatedNames) { // Note that there are dwQueryCount elements, not dwCount. // If dwCount != dwQueryCount, some elements are NULL. LsaFreeTranslatedNameList(ppTranslatedNames, dwQueryCount); } ppTranslatedNames = NULL; dwError = LsaAdBatchBuildQueryForRpc( pszNetbiosDomainName, QueryType, pLinks, pBatchItemList, &pNextLinks, dwMaxQueryCount, &dwQueryCount, &ppszQueryList); BAIL_ON_LSA_ERROR(dwError); switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_SID: dwError = LsaDmWrapNetLookupNamesByObjectSids( gpADProviderData->szDomain, dwQueryCount, ppszQueryList, &ppTranslatedNames, &dwCount); BAIL_ON_LSA_ERROR(dwError); break; case LSA_AD_BATCH_QUERY_TYPE_BY_NT4: dwError = LsaDmWrapNetLookupObjectSidsByNames( gpADProviderData->szDomain, dwQueryCount, ppszQueryList, &ppTranslatedNames, &dwCount); BAIL_ON_LSA_ERROR(dwError); break; default: LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } if (dwCount > dwQueryCount) { LSA_LOG_ERROR("Too many results returned (got %u, expected %u)", dwCount, dwQueryCount); dwError = LW_ERROR_RPC_ERROR; BAIL_ON_LSA_ERROR(dwError); } else if (dwCount == 0) { continue; } for (i = 0; i < dwQueryCount; i++) { if (!ppTranslatedNames[i]) { continue; } XXX; // speed up by using a parallel list for direct a lookup. dwError = LsaAdBatchProcessRpcObject( QueryType, pLinks, pNextLinks, ppszQueryList[i], ppTranslatedNames[i]); BAIL_ON_LSA_ERROR(dwError); } } cleanup: LwFreeStringArray(ppszQueryList, dwQueryCount); if (ppTranslatedNames) { // Note that there are dwQueryCount elements, not dwCount. // If dwCount != dwQueryCount, some elements are NULL. LsaFreeTranslatedNameList(ppTranslatedNames, dwQueryCount); } return dwError; error: goto cleanup; } static DWORD LsaAdBatchResolveRealObjects( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN PCSTR pszDnsDomainName, IN DWORD dwTotalItemCount, // List of PLSA_AD_BATCH_ITEM IN OUT PLSA_LIST_LINKS pBatchItemList ) { DWORD dwError = 0; HANDLE hDirectory = 0; PLSA_DM_LDAP_CONNECTION pConn = NULL; LDAP* pLd = NULL; PSTR pszScopeDn = NULL; PSTR pszQuery = 0; PSTR szAttributeList[] = { // AD attributes: // - common: AD_LDAP_OBJECTCLASS_TAG, AD_LDAP_OBJECTSID_TAG, AD_LDAP_SAM_NAME_TAG, AD_LDAP_DN_TAG, // - user-specific: AD_LDAP_PRIMEGID_TAG, AD_LDAP_UPN_TAG, AD_LDAP_USER_CTRL_TAG, AD_LDAP_ACCOUT_EXP_TAG, AD_LDAP_PWD_LASTSET_TAG, // schema mode: // - (group alias) or (user gecos in unprovisioned mode): AD_LDAP_DISPLAY_NAME_TAG, // - unix properties (alias is just user alias): AD_LDAP_ALIAS_TAG, AD_LDAP_UID_TAG, AD_LDAP_GID_TAG, AD_LDAP_PASSWD_TAG, AD_LDAP_GECOS_TAG, AD_LDAP_HOMEDIR_TAG, AD_LDAP_SHELL_TAG, NULL }; LDAPMessage* pMessage = NULL; PLSA_LIST_LINKS pLinks = NULL; PLSA_LIST_LINKS pNextLinks = NULL; DWORD dwMaxQuerySize = LsaAdBatchGetMaxQuerySize(); DWORD dwMaxQueryCount = LsaAdBatchGetMaxQueryCount(); dwError = LwLdapConvertDomainToDN( pszDnsDomainName, &pszScopeDn); BAIL_ON_LSA_ERROR(dwError); dwError = LsaDmLdapOpenDc(pszDnsDomainName, &pConn); BAIL_ON_LSA_ERROR(dwError); for (pLinks = pBatchItemList->Next; pLinks != pBatchItemList; pLinks = pNextLinks) { DWORD dwQueryCount = 0; DWORD dwCount = 0; LDAPMessage* pCurrentMessage = NULL; pNextLinks = NULL; LW_SAFE_FREE_STRING(pszQuery); if (pMessage) { ldap_msgfree(pMessage); pMessage = NULL; } dwError = LsaAdBatchBuildQueryForReal( QueryType, pLinks, pBatchItemList, &pNextLinks, dwMaxQuerySize, dwMaxQueryCount, &dwQueryCount, &pszQuery); BAIL_ON_LSA_ERROR(dwError); dwError = LsaDmLdapDirectorySearch( pConn, pszScopeDn, LDAP_SCOPE_SUBTREE, pszQuery, szAttributeList, &hDirectory, &pMessage); BAIL_ON_LSA_ERROR(dwError); pLd = LwLdapGetSession(hDirectory); dwCount = ldap_count_entries(pLd, pMessage); if (dwCount > dwQueryCount) { LSA_LOG_ERROR("Too many results returned (got %u, expected %u)", dwCount, dwQueryCount); dwError = LW_ERROR_LDAP_ERROR; BAIL_ON_LSA_ERROR(dwError); } else if (dwCount == 0) { continue; } pCurrentMessage = ldap_first_entry(pLd, pMessage); while (pCurrentMessage) { dwError = LsaAdBatchProcessRealObject( QueryType, pLinks, pNextLinks, hDirectory, pCurrentMessage); BAIL_ON_LSA_ERROR(dwError); pCurrentMessage = ldap_next_entry(pLd, pCurrentMessage); } } cleanup: LsaDmLdapClose(pConn); LW_SAFE_FREE_STRING(pszScopeDn); LW_SAFE_FREE_STRING(pszQuery); if (pMessage) { ldap_msgfree(pMessage); } return dwError; error: goto cleanup; } static DWORD LsaAdBatchBuildQueryScopeForPseudo( IN BOOLEAN bIsSchemaMode, IN LSA_PROVISIONING_MODE Mode, IN OPTIONAL PCSTR pszDnsDomainName, IN OPTIONAL PCSTR pszCellDn, OUT PSTR* ppszScopeDn ) { DWORD dwError = 0; PSTR pszDcPart = NULL; PSTR pszScopeDn = NULL; switch (Mode) { case LSA_PROVISIONING_MODE_DEFAULT_CELL: if (bIsSchemaMode) { // ASSERT LSA_LOG_ERROR("Schema mode default cell does not need pseudo-objects"); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } dwError = LwLdapConvertDomainToDN(pszDnsDomainName, &pszDcPart); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateStringPrintf(&pszScopeDn, "CN=$LikewiseIdentityCell,%s", pszDcPart); BAIL_ON_LSA_ERROR(dwError); break; case LSA_PROVISIONING_MODE_NON_DEFAULT_CELL: dwError = LwAllocateString(pszCellDn, &pszScopeDn); BAIL_ON_LSA_ERROR(dwError); break; default: // ASSERT LSA_LOG_ERROR("Unexpected mode %u", Mode); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } *ppszScopeDn = pszScopeDn; cleanup: LW_SAFE_FREE_STRING(pszDcPart); return dwError; error: *ppszScopeDn = NULL; LW_SAFE_FREE_STRING(pszScopeDn); goto cleanup; } static DWORD LsaAdBatchResolvePseudoObjectSidsViaGcDefaultMode( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN DWORD dwTotalItemCount, // List of PLSA_AD_BATCH_ITEM IN OUT PLSA_LIST_LINKS pBatchItemList ) { DWORD dwError = LW_ERROR_SUCCESS; DWORD dwTotalFoundCount = 0; DWORD dwFoundInDomainCount = 0; PSTR* ppszDomainNames = NULL; DWORD dwDomainCount = 0; DWORD i = 0; dwError = LsaAdBatchResolvePseudoObjectsInternalDefaultOrCell( QueryType, gpADProviderData->szDomain, NULL, TRUE, gpADProviderData->adConfigurationMode, pBatchItemList, &dwFoundInDomainCount); BAIL_ON_LSA_ERROR(dwError); dwTotalFoundCount += dwFoundInDomainCount; if (dwTotalFoundCount == dwTotalItemCount) { goto cleanup; } else if (dwTotalFoundCount > dwTotalItemCount) { dwError = LW_ERROR_DUPLICATE_USER_OR_GROUP; BAIL_ON_LSA_ERROR(dwError); } if (UNPROVISIONED_MODE == gpADProviderData->dwDirectoryMode) { dwError = LsaDmEnumDomainNames(NULL, NULL, &ppszDomainNames, &dwDomainCount); BAIL_ON_LSA_ERROR(dwError); } else { dwError = LsaDmWrapEnumExtraTwoWayForestTrustDomains(&ppszDomainNames, &dwDomainCount); BAIL_ON_LSA_ERROR(dwError); } for (i = 0; (i < dwDomainCount) && (dwTotalFoundCount < dwTotalItemCount); i++) { DWORD dwTrustDirection = LSA_TRUST_DIRECTION_UNKNOWN; DWORD dwTrustMode = LSA_TRUST_MODE_UNKNOWN; BOOLEAN bIsExternalTrust = FALSE; if (UNPROVISIONED_MODE != gpADProviderData->dwDirectoryMode) { // check trust information to determine whether we need this domain dwError = AD_DetermineTrustModeandDomainName(ppszDomainNames[i], &dwTrustDirection, &dwTrustMode, NULL, NULL); if (LW_ERROR_NO_SUCH_DOMAIN == dwError || LSA_TRUST_DIRECTION_TWO_WAY != dwTrustDirection) { LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; } BAIL_ON_LSA_ERROR(dwError); } bIsExternalTrust = (dwTrustMode == LSA_TRUST_MODE_EXTERNAL) ? TRUE : FALSE; dwError = LsaAdBatchCheckDomainModeCompatibility(ppszDomainNames[i], bIsExternalTrust, NULL); if (dwError == LW_ERROR_INCOMPATIBLE_MODES_BETWEEN_TRUSTEDDOMAINS) { dwError = 0; continue; } BAIL_ON_LSA_ERROR(dwError); // We only process the forests that have compatible adMode with the primiary domain, // Hence the mode should be the same as "gpADProviderData->adConfigurationMode" dwError = LsaAdBatchResolvePseudoObjectsInternalDefaultOrCell( QueryType, ppszDomainNames[i], NULL, TRUE, gpADProviderData->adConfigurationMode, pBatchItemList, &dwFoundInDomainCount); BAIL_ON_LSA_ERROR(dwError); dwTotalFoundCount += dwFoundInDomainCount; } cleanup: LwFreeStringArray(ppszDomainNames, dwDomainCount); return dwError; error: goto cleanup; } static DWORD LsaAdBatchResolvePseudoObjectsUnprovMode( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN DWORD dwTotalItemCount, // List of PLSA_AD_BATCH_ITEM IN OUT PLSA_LIST_LINKS pBatchItemList ) { DWORD dwError = 0; DWORD i = 0; PLSA_LIST_LINKS pLinks = NULL; BOOLEAN bIsUser = FALSE; if (LSA_AD_BATCH_QUERY_TYPE_BY_USER_ALIAS == QueryType || LSA_AD_BATCH_QUERY_TYPE_BY_GROUP_ALIAS == QueryType) { if (!ADUnprovPlugin_SupportsAliases()) { dwError = LW_ERROR_NOT_SUPPORTED; BAIL_ON_LSA_ERROR(dwError); } } switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_UID: case LSA_AD_BATCH_QUERY_TYPE_BY_USER_ALIAS: bIsUser = TRUE; break; case LSA_AD_BATCH_QUERY_TYPE_BY_GID: case LSA_AD_BATCH_QUERY_TYPE_BY_GROUP_ALIAS: bIsUser = FALSE; break; default: dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } for (pLinks = pBatchItemList->Next; pLinks != pBatchItemList; pLinks = pLinks->Next) { PLSA_AD_BATCH_ITEM pBatchItem = LW_STRUCT_FROM_FIELD(pLinks, LSA_AD_BATCH_ITEM, BatchItemListLinks); PSTR* ppszAlias = NULL; DWORD dwId = 0; if (bIsUser) { ppszAlias = &pBatchItem->UserInfo.pszAlias; } else { ppszAlias = &pBatchItem->GroupInfo.pszAlias; } switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_UID: case LSA_AD_BATCH_QUERY_TYPE_BY_GID: dwError = ADUnprovPlugin_QueryById( bIsUser, pBatchItem->QueryTerm.dwId, &pBatchItem->pszSid, ppszAlias); BAIL_ON_LSA_ERROR(dwError); dwId = pBatchItem->QueryTerm.dwId; break; case LSA_AD_BATCH_QUERY_TYPE_BY_USER_ALIAS: case LSA_AD_BATCH_QUERY_TYPE_BY_GROUP_ALIAS: dwError = ADUnprovPlugin_QueryByAlias( bIsUser, pBatchItem->QueryTerm.pszString, &pBatchItem->pszSid, &dwId); BAIL_ON_LSA_ERROR(dwError); break; default: dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } if (bIsUser) { pBatchItem->UserInfo.uid = (uid_t)dwId; } else { pBatchItem->GroupInfo.gid = (gid_t)dwId; } i++; } cleanup: return dwError; error: goto cleanup; } static DWORD LsaAdBatchResolvePseudoObjectsDefaultMode( IN LSA_AD_BATCH_QUERY_TYPE QueryType, // If pszDnsDomainName is NULL, we are resolving pseudo objects walking through all the available domains // If pszDnsDomainName is not NULL, we only resolving pseudo objects within that particular domain IN OPTIONAL PCSTR pszDnsDomainName, IN DWORD dwTotalItemCount, // List of PLSA_AD_BATCH_ITEM IN OUT PLSA_LIST_LINKS pBatchItemList, OUT OPTIONAL PBOOLEAN pbResolvedPseudo ) { DWORD dwError = 0; BOOLEAN bResolvedPseudo = TRUE; if (!LW_IS_NULL_OR_EMPTY_STR(pszDnsDomainName)) { // Know the domain to search. dwError = LsaAdBatchResolvePseudoObjectsInternalDefaultOrCell( QueryType, pszDnsDomainName, NULL, FALSE, gpADProviderData->adConfigurationMode, pBatchItemList, NULL); BAIL_ON_LSA_ERROR(dwError); } else { // Need to search all domains. This happens when there is a // search by Unix attribute in default or unprovisioned mode. // Note that will do a GC search and therefore not // actually resolve full pseudo information. LSA_ASSERT(pbResolvedPseudo); bResolvedPseudo = FALSE; dwError = LsaAdBatchResolvePseudoObjectSidsViaGcDefaultMode( QueryType, dwTotalItemCount, pBatchItemList); BAIL_ON_LSA_ERROR(dwError); } cleanup: if (pbResolvedPseudo) { *pbResolvedPseudo = bResolvedPseudo; } return dwError; error: bResolvedPseudo = FALSE; goto cleanup; } static DWORD LsaAdBatchResolvePseudoObjects( IN LSA_AD_BATCH_QUERY_TYPE QueryType, // If pszDnsDomainName is NULL, we are resolving pseudo objects walking through all the available domains // If pszDnsDomainName is not NULL, we only resolving pseudo objects within that particular domain IN OPTIONAL PCSTR pszDnsDomainName, IN DWORD dwTotalItemCount, // List of PLSA_AD_BATCH_ITEM IN OUT PLSA_LIST_LINKS pBatchItemList, OUT OPTIONAL PBOOLEAN pbResolvedPseudo ) { DWORD dwError = 0; BOOLEAN bResolvedPseudo = TRUE; switch (gpADProviderData->dwDirectoryMode) { case CELL_MODE: // Need to search the cell(s). dwError = LsaAdBatchResolvePseudoObjectsWithLinkedCells( QueryType, dwTotalItemCount, pBatchItemList); BAIL_ON_LSA_ERROR(dwError); break; case UNPROVISIONED_MODE: dwError = LsaAdBatchResolvePseudoObjectsUnprovMode( QueryType, dwTotalItemCount, pBatchItemList); BAIL_ON_LSA_ERROR(dwError); break; case DEFAULT_MODE: dwError = LsaAdBatchResolvePseudoObjectsDefaultMode( QueryType, pszDnsDomainName, dwTotalItemCount, pBatchItemList, &bResolvedPseudo); BAIL_ON_LSA_ERROR(dwError); break; default: dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } cleanup: if (pbResolvedPseudo) { *pbResolvedPseudo = bResolvedPseudo; } return dwError; error: bResolvedPseudo = FALSE; goto cleanup; } DWORD LsaAdBatchIsDefaultCell( IN PCSTR pszCellDN, OUT PBOOLEAN pbIsDefaultCell ) { DWORD dwError = 0; PSTR pszRootDN = NULL; PSTR pszDefaultCellDN = NULL; BOOLEAN bIsDefaultCell = FALSE; dwError = LwLdapConvertDomainToDN(gpADProviderData->szDomain, &pszRootDN); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateStringPrintf( &pszDefaultCellDN, "CN=$LikewiseIdentityCell,%s", pszRootDN); BAIL_ON_LSA_ERROR(dwError); if (!strcasecmp(pszCellDN, pszDefaultCellDN)) { bIsDefaultCell = TRUE; } cleanup: LW_SAFE_FREE_STRING(pszRootDN); LW_SAFE_FREE_STRING(pszDefaultCellDN); *pbIsDefaultCell = bIsDefaultCell; return dwError; error: goto cleanup; } static DWORD LsaAdBatchResolvePseudoObjectsWithLinkedCells( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN DWORD dwTotalItemCount, // List of PLSA_AD_BATCH_ITEM IN OUT PLSA_LIST_LINKS pBatchItemList ) { DWORD dwError = LW_ERROR_SUCCESS; PDLINKEDLIST pCellNode = NULL; DWORD dwTotalFoundCount = 0; DWORD dwFoundInCellCount = 0; BOOLEAN bIsDefaultCell = FALSE; dwError = LsaAdBatchResolvePseudoObjectsInternalDefaultOrCell( QueryType, NULL, gpADProviderData->cell.szCellDN, FALSE, gpADProviderData->adConfigurationMode, pBatchItemList, &dwFoundInCellCount); BAIL_ON_LSA_ERROR(dwError); dwTotalFoundCount += dwFoundInCellCount; for (pCellNode = gpADProviderData->pCellList; pCellNode && (dwTotalFoundCount < dwTotalItemCount); pCellNode = pCellNode->pNext) { ADConfigurationMode adMode = UnknownMode; PAD_LINKED_CELL_INFO pCellInfo = (PAD_LINKED_CELL_INFO)pCellNode->pItem; if (!pCellInfo) { // This should never happen. continue; } // determine schema/non-schema mode in the current cell dwError = LsaAdBatchQueryCellConfigurationMode( gpADProviderData->szDomain, pCellInfo->pszCellDN, &adMode); BAIL_ON_LSA_ERROR(dwError); if (adMode == UnknownMode) { continue; } dwError = LsaAdBatchIsDefaultCell(pCellInfo->pszCellDN, &bIsDefaultCell); BAIL_ON_LSA_ERROR(dwError); if (bIsDefaultCell && SchemaMode == adMode) { dwError = LsaAdBatchResolvePseudoObjectsInternalDefaultSchema( QueryType, gpADProviderData->szDomain, pBatchItemList, &dwFoundInCellCount); BAIL_ON_LSA_ERROR(dwError); } else { dwError = LsaAdBatchResolvePseudoObjectsInternalDefaultOrCell( QueryType, NULL, pCellInfo->pszCellDN, FALSE, adMode, pBatchItemList, &dwFoundInCellCount); BAIL_ON_LSA_ERROR(dwError); } dwTotalFoundCount += dwFoundInCellCount; } cleanup: return dwError; error: goto cleanup; } static DWORD LsaAdBatchResolvePseudoObjectsInternalDefaultSchema( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN PCSTR pszDnsDomainName, // List of PLSA_AD_BATCH_ITEM IN OUT PLSA_LIST_LINKS pBatchItemList, OUT OPTIONAL PDWORD pdwTotalItemFoundCount ) { DWORD dwError = 0; HANDLE hDirectory = NULL; LDAP* pLd = NULL; PSTR pszQuery = 0; PSTR szAttributeList[] = { AD_LDAP_OBJECTCLASS_TAG, AD_LDAP_OBJECTSID_TAG, AD_LDAP_SAM_NAME_TAG, AD_LDAP_DN_TAG, AD_LDAP_PRIMEGID_TAG, AD_LDAP_UPN_TAG, AD_LDAP_USER_CTRL_TAG, AD_LDAP_ACCOUT_EXP_TAG, AD_LDAP_PWD_LASTSET_TAG, AD_LDAP_DISPLAY_NAME_TAG, AD_LDAP_ALIAS_TAG, AD_LDAP_UID_TAG, AD_LDAP_GID_TAG, AD_LDAP_PASSWD_TAG, AD_LDAP_GECOS_TAG, AD_LDAP_HOMEDIR_TAG, AD_LDAP_SHELL_TAG, NULL }; LDAPMessage* pMessage = NULL; PLSA_LIST_LINKS pLinks = NULL; PLSA_LIST_LINKS pNextLinks = NULL; DWORD dwMaxQuerySize = LsaAdBatchGetMaxQuerySize(); DWORD dwMaxQueryCount = LsaAdBatchGetMaxQueryCount(); DWORD dwTotalItemFoundCount = 0; PLSA_DM_LDAP_CONNECTION pConn = NULL; PSTR pszDomainDN = NULL; if (LW_IS_NULL_OR_EMPTY_STR(pszDnsDomainName)) { dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } dwError = LsaDmLdapOpenDc(pszDnsDomainName, &pConn); BAIL_ON_LSA_ERROR(dwError); dwError = LwLdapConvertDomainToDN(pszDnsDomainName, &pszDomainDN); BAIL_ON_LSA_ERROR(dwError); for (pLinks = pBatchItemList->Next; pLinks != pBatchItemList; pLinks = pNextLinks) { DWORD dwQueryCount = 0; DWORD dwCount = 0; LDAPMessage* pCurrentMessage = NULL; pNextLinks = NULL; LW_SAFE_FREE_STRING(pszQuery); if (pMessage) { ldap_msgfree(pMessage); pMessage = NULL; } dwError = LsaAdBatchBuildQueryForPseudoDefaultSchema( QueryType, pLinks, pBatchItemList, &pNextLinks, dwMaxQuerySize, dwMaxQueryCount, &dwQueryCount, &pszQuery); BAIL_ON_LSA_ERROR(dwError); if (LW_IS_NULL_OR_EMPTY_STR(pszQuery)) { break; } dwError = LsaDmLdapDirectorySearch( pConn, pszDomainDN, LDAP_SCOPE_SUBTREE, pszQuery, szAttributeList, &hDirectory, &pMessage); BAIL_ON_LSA_ERROR(dwError); pLd = LwLdapGetSession(hDirectory); dwCount = ldap_count_entries(pLd, pMessage); if (dwCount > dwQueryCount) { LSA_LOG_ERROR("Too many results returned (got %u, expected %u)", dwCount, dwQueryCount); dwError = LW_ERROR_LDAP_ERROR; BAIL_ON_LSA_ERROR(dwError); } else if (dwCount == 0) { continue; } dwCount = 0; pCurrentMessage = ldap_first_entry(pLd, pMessage); while (pCurrentMessage) { dwError = LsaAdBatchProcessPseudoObjectDefaultSchema( QueryType, pLinks, pNextLinks, hDirectory, pCurrentMessage); BAIL_ON_LSA_ERROR(dwError); dwCount++; pCurrentMessage = ldap_next_entry(pLd, pCurrentMessage); } dwTotalItemFoundCount += dwCount; } if (pdwTotalItemFoundCount) { *pdwTotalItemFoundCount = dwTotalItemFoundCount; } cleanup: LsaDmLdapClose(pConn); LW_SAFE_FREE_STRING(pszQuery); LW_SAFE_FREE_STRING(pszDomainDN); if (pMessage) { ldap_msgfree(pMessage); } return dwError; error: if (pdwTotalItemFoundCount) { *pdwTotalItemFoundCount = 0; } goto cleanup; } static DWORD LsaAdBatchResolvePseudoObjectsInternalDefaultOrCell( IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN OPTIONAL PCSTR pszDnsDomainName, IN OPTIONAL PCSTR pszCellDn, IN BOOLEAN bDoGCSearch, IN ADConfigurationMode adMode, // List of PLSA_AD_BATCH_ITEM IN OUT PLSA_LIST_LINKS pBatchItemList, OUT OPTIONAL PDWORD pdwTotalItemFoundCount ) { DWORD dwError = 0; HANDLE hDirectory = NULL; LDAP* pLd = NULL; PSTR pszScopeDn = NULL; PSTR pszQuery = 0; PSTR szAttributeList[] = { // AD attributes: // - common: AD_LDAP_OBJECTCLASS_TAG, AD_LDAP_OBJECTSID_TAG, AD_LDAP_SAM_NAME_TAG, AD_LDAP_DN_TAG, // - user-specific: // In default schema mode, we will do GC search, // even we look up real objects directly, GC does not provide those user-specific attribute values. // Those values will be looked up when we look up real objects in the second step #if 0 // Indexed and in GC: AD_LDAP_PRIMEGID_TAG, AD_LDAP_UPN_TAG, AD_LDAP_USER_CTRL_TAG, // Not indexed and not in GC: AD_LDAP_ACCOUT_EXP_TAG, AD_LDAP_PWD_LASTSET_TAG, #endif // non-schema mode: AD_LDAP_KEYWORDS_TAG, // schema mode: // - group alias: AD_LDAP_DISPLAY_NAME_TAG, // - unix properties (alias is just user alias): AD_LDAP_ALIAS_TAG, AD_LDAP_UID_TAG, AD_LDAP_GID_TAG, // A GC search (default mode) will not find these: AD_LDAP_PASSWD_TAG, AD_LDAP_GECOS_TAG, AD_LDAP_HOMEDIR_TAG, AD_LDAP_SHELL_TAG, NULL }; LDAPMessage* pMessage = NULL; PLSA_LIST_LINKS pLinks = NULL; PLSA_LIST_LINKS pNextLinks = NULL; DWORD dwMaxQuerySize = LsaAdBatchGetMaxQuerySize(); DWORD dwMaxQueryCount = LsaAdBatchGetMaxQueryCount(); PSTR pszDomainName = NULL; DWORD dwTotalItemFoundCount = 0; PSTR pUserPseudoDN = NULL; PLSA_DM_LDAP_CONNECTION pConn = NULL; if (bDoGCSearch && LW_IS_NULL_OR_EMPTY_STR(pszDnsDomainName)) { dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } if (bDoGCSearch) { dwError = LwAllocateString("", &pszScopeDn); BAIL_ON_LSA_ERROR(dwError); dwError = LsaDmLdapOpenGc(pszDnsDomainName, &pConn); BAIL_ON_LSA_ERROR(dwError); } else { dwError = LsaAdBatchBuildQueryScopeForPseudo( (adMode == SchemaMode), gpADProviderData->dwDirectoryMode, pszDnsDomainName, pszCellDn, &pszScopeDn); BAIL_ON_LSA_ERROR(dwError); // Need to do this because the pseudo-object domain // could be different from the real object's domain // (e.g., non-default cell). dwError = LwLdapConvertDNToDomain( pszScopeDn, &pszDomainName); BAIL_ON_LSA_ERROR(dwError); dwError = LsaDmLdapOpenDc(pszDomainName, &pConn); BAIL_ON_LSA_ERROR(dwError); } for (pLinks = pBatchItemList->Next; pLinks != pBatchItemList; pLinks = pNextLinks) { DWORD dwQueryCount = 0; DWORD dwCount = 0; LDAPMessage* pCurrentMessage = NULL; pNextLinks = NULL; LW_SAFE_FREE_STRING(pszQuery); if (pMessage) { ldap_msgfree(pMessage); pMessage = NULL; } dwError = LsaAdBatchBuildQueryForPseudo( (adMode == SchemaMode), QueryType, pLinks, pBatchItemList, &pNextLinks, dwMaxQuerySize, dwMaxQueryCount, &dwQueryCount, &pszQuery); BAIL_ON_LSA_ERROR(dwError); if (LW_IS_NULL_OR_EMPTY_STR(pszQuery)) { break; } dwError = LsaDmLdapDirectorySearch( pConn, pszScopeDn, LDAP_SCOPE_SUBTREE, pszQuery, szAttributeList, &hDirectory, &pMessage); BAIL_ON_LSA_ERROR(dwError); pLd = LwLdapGetSession(hDirectory); dwCount = ldap_count_entries(pLd, pMessage); // In Default Non-schema mode, we might get entries in non-default cells due to the GC search // Hence, dwCount can be more than dwQueryCount if (!(NonSchemaMode == adMode && bDoGCSearch) && dwCount > dwQueryCount) { LSA_LOG_ERROR("Too many results returned (got %u, expected %u)", dwCount, dwQueryCount); dwError = LW_ERROR_LDAP_ERROR; BAIL_ON_LSA_ERROR(dwError); } else if (dwCount == 0) { continue; } dwCount = 0; pCurrentMessage = ldap_first_entry(pLd, pMessage); while (pCurrentMessage) { // Default Non-schema mode doing a GC search if (NonSchemaMode == adMode && bDoGCSearch) { LW_SAFE_FREE_STRING(pUserPseudoDN); dwError = LwLdapGetDN( hDirectory, pCurrentMessage, &pUserPseudoDN); BAIL_ON_LSA_ERROR(dwError); LwStrToUpper(pUserPseudoDN); // Make sure the found pseudo object is enabled in default cell; // Otherwise, skip this pCurrentMessage if (!strstr(pUserPseudoDN, ",CN=$LIKEWISEIDENTITYCELL,DC=")) { pCurrentMessage = ldap_next_entry(pLd, pCurrentMessage); continue; } } dwError = LsaAdBatchProcessPseudoObject( QueryType, pLinks, pNextLinks, bDoGCSearch, (adMode == SchemaMode), hDirectory, pCurrentMessage); BAIL_ON_LSA_ERROR(dwError); dwCount++; pCurrentMessage = ldap_next_entry(pLd, pCurrentMessage); } dwTotalItemFoundCount += dwCount; } if (pdwTotalItemFoundCount) { *pdwTotalItemFoundCount = dwTotalItemFoundCount; } cleanup: LsaDmLdapClose(pConn); LW_SAFE_FREE_STRING(pszDomainName); LW_SAFE_FREE_STRING(pszScopeDn); LW_SAFE_FREE_STRING(pszQuery); LW_SAFE_FREE_STRING(pUserPseudoDN); if (pMessage) { ldap_msgfree(pMessage); } return dwError; error: if (pdwTotalItemFoundCount) { *pdwTotalItemFoundCount = 0; } goto cleanup; } static BOOLEAN LsaAdBatchIsUserOrGroupObjectType( IN LSA_AD_BATCH_OBJECT_TYPE ObjectType ) { BOOLEAN bIsOk = FALSE; switch (ObjectType) { case LSA_AD_BATCH_OBJECT_TYPE_USER: case LSA_AD_BATCH_OBJECT_TYPE_GROUP: bIsOk = TRUE; break; default: break; } return bIsOk; } static DWORD LsaAdBatchGetObjectTypeFromRealMessage( OUT PLSA_AD_BATCH_OBJECT_TYPE pObjectType, IN HANDLE hDirectory, IN LDAPMessage* pMessage ) { DWORD dwError = 0; LSA_AD_BATCH_OBJECT_TYPE objectType = 0; PSTR* ppszValues = NULL; DWORD dwValuesCount = 0; DWORD i = 0; dwError = LwLdapGetStrings( hDirectory, pMessage, AD_LDAP_OBJECTCLASS_TAG, &ppszValues, &dwValuesCount); BAIL_ON_LSA_ERROR(dwError); for (i = 0; i < dwValuesCount; i++) { if (!strcasecmp(ppszValues[i], "user")) { objectType = LSA_AD_BATCH_OBJECT_TYPE_USER; break; } else if (!strcasecmp(ppszValues[i], "group")) { objectType = LSA_AD_BATCH_OBJECT_TYPE_GROUP; break; } } if (!LsaAdBatchIsUserOrGroupObjectType(objectType)) { dwError = LW_ERROR_DATA_ERROR; BAIL_ON_LSA_ERROR(dwError); } cleanup: LwFreeStringArray(ppszValues, dwValuesCount); *pObjectType = objectType; return dwError; error: goto cleanup; } static DWORD LsaAdBatchGetObjectTypeFromPseudoKeywords( IN DWORD dwKeywordValuesCount, IN PSTR* ppszKeywordValues ) { LSA_AD_BATCH_OBJECT_TYPE objectType = 0; DWORD i = 0; for (i = 0; i < dwKeywordValuesCount; i++) { if (!strcasecmp(ppszKeywordValues[i], "objectClass=" AD_LDAP_CLASS_LW_USER)) { objectType = LSA_AD_BATCH_OBJECT_TYPE_USER; break; } else if (!strcasecmp(ppszKeywordValues[i], "objectClass=" AD_LDAP_CLASS_LW_GROUP)) { objectType = LSA_AD_BATCH_OBJECT_TYPE_GROUP; break; } } return objectType; } static DWORD LsaAdBatchGetObjectTypeFromPseudoMessage( OUT PLSA_AD_BATCH_OBJECT_TYPE pObjectType, IN BOOLEAN bIsSchemaMode, IN OPTIONAL DWORD dwKeywordValuesCount, IN OPTIONAL PSTR* ppszKeywordValues, IN HANDLE hDirectory, IN LDAPMessage* pMessage ) { DWORD dwError = 0; LSA_AD_BATCH_OBJECT_TYPE objectType = 0; LSA_AD_BATCH_OBJECT_TYPE keywordsObjectType = 0; PSTR* ppszValues = NULL; DWORD dwValuesCount = 0; DWORD i = 0; if (LsaAdBatchIsDefaultSchemaMode()) { dwError = LsaAdBatchGetObjectTypeFromRealMessage( &objectType, hDirectory, pMessage); BAIL_ON_LSA_ERROR(dwError); goto cleanup; } keywordsObjectType = LsaAdBatchGetObjectTypeFromPseudoKeywords( dwKeywordValuesCount, ppszKeywordValues); if (!keywordsObjectType) { LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } if (!LsaAdBatchIsUserOrGroupObjectType(keywordsObjectType)) { dwError = LW_ERROR_DATA_ERROR; BAIL_ON_LSA_ERROR(dwError); } // double-check against the object class. dwError = LwLdapGetStrings( hDirectory, pMessage, AD_LDAP_OBJECTCLASS_TAG, &ppszValues, &dwValuesCount); BAIL_ON_LSA_ERROR(dwError); for (i = 0; i < dwValuesCount; i++) { if (bIsSchemaMode) { if (!strcasecmp(ppszValues[i], AD_LDAP_CLASS_SCHEMA_USER)) { objectType = LSA_AD_BATCH_OBJECT_TYPE_USER; break; } else if (!strcasecmp(ppszValues[i], AD_LDAP_CLASS_SCHEMA_GROUP)) { objectType = LSA_AD_BATCH_OBJECT_TYPE_GROUP; break; } } else { if (!strcasecmp(ppszValues[i], AD_LDAP_CLASS_NON_SCHEMA)) { objectType = keywordsObjectType; break; } } } if (!LsaAdBatchIsUserOrGroupObjectType(objectType)) { dwError = LW_ERROR_DATA_ERROR; BAIL_ON_LSA_ERROR(dwError); } if (objectType != keywordsObjectType) { LSA_LOG_DEBUG("Object type mismatch: %u vs %u", keywordsObjectType, objectType); dwError = LW_ERROR_DATA_ERROR; BAIL_ON_LSA_ERROR(dwError); } cleanup: LwFreeStringArray(ppszValues, dwValuesCount); *pObjectType = objectType; return dwError; error: goto cleanup; } static PCSTR LsaAdBatchFindKeywordAttribute( IN DWORD dwKeywordValuesCount, IN PSTR* ppszKeywordValues, IN PCSTR pszAttributeName ) { PCSTR pszAttributeValue = NULL; size_t sNameLen = 0; size_t i = 0; if (dwKeywordValuesCount > 0) { sNameLen = strlen(pszAttributeName); } for (i = 0; i < dwKeywordValuesCount; i++) { PCSTR pszKeywordValue = ppszKeywordValues[i]; // Look for ldap values which are in the form = if (!strncasecmp(pszKeywordValue, pszAttributeName, sNameLen) && pszKeywordValue[sNameLen] == '=') { pszAttributeValue = pszKeywordValue + sNameLen + 1; break; } } return pszAttributeValue; } PCSTR LsaAdBatchFindKeywordAttributeWithEqual( IN DWORD dwKeywordValuesCount, IN PSTR* ppszKeywordValues, IN PCSTR pszAttributeNameWithEqual, IN size_t sAttributeNameWithEqualLength ) { PCSTR pszAttributeValue = NULL; size_t i = 0; LSA_ASSERT('=' == pszAttributeNameWithEqual[sAttributeNameWithEqualLength-1]); for (i = 0; i < dwKeywordValuesCount; i++) { PCSTR pszKeywordValue = ppszKeywordValues[i]; // Look for ldap values which are in the form = if (!strncasecmp(pszKeywordValue, pszAttributeNameWithEqual, sAttributeNameWithEqualLength)) { pszAttributeValue = pszKeywordValue + sAttributeNameWithEqualLength; break; } } return pszAttributeValue; } static DWORD LsaAdBatchGetCompareStringFromRealObject( OUT PSTR* ppszCompareString, IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN HANDLE hDirectory, IN LDAPMessage* pMessage ) { DWORD dwError = 0; PSTR pszCompare = NULL; switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_DN: dwError = LwLdapGetDN(hDirectory, pMessage, &pszCompare); BAIL_ON_LSA_ERROR(dwError); break; case LSA_AD_BATCH_QUERY_TYPE_BY_SID: dwError = ADLdap_GetObjectSid(hDirectory, pMessage, &pszCompare); BAIL_ON_LSA_ERROR(dwError); break; case LSA_AD_BATCH_QUERY_TYPE_BY_NT4: dwError = LwLdapGetString( hDirectory, pMessage, AD_LDAP_SAM_NAME_TAG, &pszCompare); BAIL_ON_LSA_ERROR(dwError); break; default: LSA_ASSERT(FALSE); dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } if (LW_IS_NULL_OR_EMPTY_STR(pszCompare)) { dwError = LW_ERROR_DATA_ERROR; BAIL_ON_LSA_ERROR(dwError); } cleanup: *ppszCompareString = pszCompare; return dwError; error: LW_SAFE_FREE_STRING(pszCompare); goto cleanup; } static DWORD LsaAdBatchGetCompareStringFromPseudoObjectDefaultSchema( OUT PSTR* ppszCompareString, IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN HANDLE hDirectory, IN LDAPMessage* pMessage ) { DWORD dwError = 0; PSTR pszCompare = NULL; switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_DN: dwError = LwLdapGetDN(hDirectory, pMessage, &pszCompare); BAIL_ON_LSA_ERROR(dwError); break; case LSA_AD_BATCH_QUERY_TYPE_BY_SID: dwError = ADLdap_GetObjectSid(hDirectory, pMessage, &pszCompare); BAIL_ON_LSA_ERROR(dwError); break; case LSA_AD_BATCH_QUERY_TYPE_BY_NT4: dwError = LwLdapGetString( hDirectory, pMessage, AD_LDAP_SAM_NAME_TAG, &pszCompare); BAIL_ON_LSA_ERROR(dwError); break; case LSA_AD_BATCH_QUERY_TYPE_BY_USER_ALIAS: dwError = LwLdapGetString( hDirectory, pMessage, AD_LDAP_ALIAS_TAG, &pszCompare); BAIL_ON_LSA_ERROR(dwError); break; case LSA_AD_BATCH_QUERY_TYPE_BY_GROUP_ALIAS: dwError = LwLdapGetString( hDirectory, pMessage, AD_LDAP_DISPLAY_NAME_TAG, &pszCompare); BAIL_ON_LSA_ERROR(dwError); break; case LSA_AD_BATCH_QUERY_TYPE_BY_UID: dwError = LwLdapGetString( hDirectory, pMessage, AD_LDAP_UID_TAG, &pszCompare); BAIL_ON_LSA_ERROR(dwError); break; case LSA_AD_BATCH_QUERY_TYPE_BY_GID: dwError = LwLdapGetString( hDirectory, pMessage, AD_LDAP_GID_TAG, &pszCompare); BAIL_ON_LSA_ERROR(dwError); break; default: LSA_ASSERT(FALSE); dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } if (LW_IS_NULL_OR_EMPTY_STR(pszCompare)) { dwError = LW_ERROR_DATA_ERROR; BAIL_ON_LSA_ERROR(dwError); } cleanup: *ppszCompareString = pszCompare; return dwError; error: LW_SAFE_FREE_STRING(pszCompare); goto cleanup; } static DWORD LsaAdBatchGetCompareStringFromPseudoObject( OUT PCSTR* ppszCompareString, OUT PSTR* ppszFreeString, IN LSA_AD_BATCH_QUERY_TYPE QueryType, IN BOOLEAN bIsSchemaMode, IN OPTIONAL DWORD dwKeywordValuesCount, IN OPTIONAL PSTR* ppszKeywordValues, IN HANDLE hDirectory, IN LDAPMessage* pMessage ) { DWORD dwError = 0; PCSTR pszAttributeName = NULL; PSTR pszFreeCompare = NULL; PCSTR pszCompare = NULL; LSA_ASSERT(LSA_IS_XOR(LsaAdBatchIsDefaultSchemaMode(), ppszKeywordValues)); switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_SID: // The SID backlink is stored in keywords except for in // default schema mode. But in default schema mode, // we never do a pseudo lookup by SID. LSA_ASSERT(!LsaAdBatchIsDefaultSchemaMode()); pszCompare = LsaAdBatchFindKeywordAttributeStatic( dwKeywordValuesCount, ppszKeywordValues, AD_LDAP_BACKLINK_PSEUDO_TAG); if (LW_IS_NULL_OR_EMPTY_STR(pszCompare)) { dwError = LW_ERROR_INVALID_SID; BAIL_ON_LSA_ERROR(dwError); } break; case LSA_AD_BATCH_QUERY_TYPE_BY_USER_ALIAS: pszAttributeName = AD_LDAP_ALIAS_TAG; break; case LSA_AD_BATCH_QUERY_TYPE_BY_GROUP_ALIAS: pszAttributeName = AD_LDAP_DISPLAY_NAME_TAG; break; case LSA_AD_BATCH_QUERY_TYPE_BY_UID: pszAttributeName = AD_LDAP_UID_TAG; break; case LSA_AD_BATCH_QUERY_TYPE_BY_GID: pszAttributeName = AD_LDAP_GID_TAG; break; default: LSA_ASSERT(FALSE); dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } if (pszAttributeName) { if (bIsSchemaMode) { dwError = LwLdapGetString( hDirectory, pMessage, pszAttributeName, &pszFreeCompare); BAIL_ON_LSA_ERROR(dwError); pszCompare = pszFreeCompare; } else { // Note that pszCompare is valid only as long // as the keyword strings are not freed. // The caller must keep any returned keyword // string around as long as it uses the compare // string result. pszCompare = LsaAdBatchFindKeywordAttribute( dwKeywordValuesCount, ppszKeywordValues, pszAttributeName); } } cleanup: LSA_ASSERT(!pszFreeCompare || (pszCompare == pszFreeCompare)); *ppszFreeString = pszFreeCompare; *ppszCompareString = pszCompare; return dwError; error: LW_SAFE_FREE_STRING(pszFreeCompare); pszCompare = NULL; goto cleanup; } static DWORD LsaAdBatchProcessRpcObject( IN LSA_AD_BATCH_QUERY_TYPE QueryType, // List of PLSA_AD_BATCH_ITEM IN OUT PLSA_LIST_LINKS pStartBatchItemListLinks, IN PLSA_LIST_LINKS pEndBatchItemListLinks, IN PSTR pszObjectNT4NameOrSid, IN PLSA_TRANSLATED_NAME_OR_SID pTranslatedName ) { DWORD dwError = 0; PSTR pszSid = NULL; PSTR pszSamAccountName = NULL; PCSTR pszFoundSamAccountName = NULL; PLSA_LOGIN_NAME_INFO pLoginNameInfo = NULL; PCSTR pszCompare = NULL; LSA_AD_BATCH_OBJECT_TYPE objectType = 0; LSA_AD_BATCH_OBJECT_TYPE desiredObjectType = 0; PLSA_LIST_LINKS pLinks = NULL; PLSA_AD_BATCH_ITEM pFoundItem = NULL; switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_SID: dwError = LsaCrackDomainQualifiedName( pTranslatedName->pszNT4NameOrSid, gpADProviderData->szDomain, &pLoginNameInfo); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateString(pszObjectNT4NameOrSid, &pszSid); BAIL_ON_LSA_ERROR(dwError); LSA_XFER_STRING(pLoginNameInfo->pszName, pszSamAccountName); pszCompare = pszSid; break; case LSA_AD_BATCH_QUERY_TYPE_BY_NT4: // The name is in backslash format, not separator pszFoundSamAccountName = index(pszObjectNT4NameOrSid, '\\'); if (!pszFoundSamAccountName) { LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } pszFoundSamAccountName++; if (!pszFoundSamAccountName[0]) { LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } dwError = LwAllocateString(pszFoundSamAccountName, &pszSamAccountName); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateString(pTranslatedName->pszNT4NameOrSid, &pszSid); BAIL_ON_LSA_ERROR(dwError); pszCompare = pszSamAccountName; break; default: LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } dwError = LsaAdBatchAccountTypeToObjectType( pTranslatedName->ObjectType, &objectType); BAIL_ON_LSA_ERROR(dwError); if (!LsaAdBatchIsUserOrGroupObjectType(objectType)) { // We found something else. LSA_LOG_DEBUG("Found non-user/non-group object type %d", pTranslatedName->ObjectType); dwError = LW_ERROR_DATA_ERROR; } desiredObjectType = LsaAdBatchGetObjectTypeFromQueryType(QueryType); if ((desiredObjectType != LSA_AD_BATCH_OBJECT_TYPE_UNDEFINED) && (objectType != desiredObjectType)) { LSA_LOG_DEBUG("Object type mismatch: got %u instead of %u", objectType, desiredObjectType); // This cannot happen because we restrict the type we search on. LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } for (pLinks = pStartBatchItemListLinks; pLinks != pEndBatchItemListLinks; pLinks = pLinks->Next) { PLSA_AD_BATCH_ITEM pItem = LW_STRUCT_FROM_FIELD(pLinks, LSA_AD_BATCH_ITEM, BatchItemListLinks); if (pItem->pszQueryMatchTerm && !strcasecmp(pItem->pszQueryMatchTerm, pszCompare)) { pFoundItem = pItem; break; } } if (!pFoundItem) { PCSTR pszType = LsaAdBatchGetQueryTypeAsString(QueryType); LSA_LOG_DEBUG("Did not find batch item for message for %s '%s'", pszType, pszCompare); LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } dwError = LsaAdBatchGatherRpcObject( pFoundItem, objectType, &pszSid, &pszSamAccountName); BAIL_ON_LSA_ERROR(dwError); cleanup: LW_SAFE_FREE_STRING(pszSid); LW_SAFE_FREE_STRING(pszSamAccountName); LSA_SAFE_FREE_LOGIN_NAME_INFO(pLoginNameInfo); return dwError; error: goto cleanup; } static DWORD LsaAdBatchProcessRealObject( IN LSA_AD_BATCH_QUERY_TYPE QueryType, // List of PLSA_AD_BATCH_ITEM IN OUT PLSA_LIST_LINKS pStartBatchItemListLinks, IN PLSA_LIST_LINKS pEndBatchItemListLinks, IN HANDLE hDirectory, IN LDAPMessage* pMessage ) { DWORD dwError = 0; PSTR pszCompare = NULL; LSA_AD_BATCH_OBJECT_TYPE objectType = 0; LSA_AD_BATCH_OBJECT_TYPE desiredObjectType = 0; PLSA_LIST_LINKS pLinks = NULL; PLSA_AD_BATCH_ITEM pFoundItem = NULL; // Get compare string dwError = LsaAdBatchGetCompareStringFromRealObject( &pszCompare, QueryType, hDirectory, pMessage); BAIL_ON_LSA_ERROR(dwError); // Get and check object type dwError = LsaAdBatchGetObjectTypeFromRealMessage( &objectType, hDirectory, pMessage); BAIL_ON_LSA_ERROR(dwError); // Sanity check. desiredObjectType = LsaAdBatchGetObjectTypeFromQueryType(QueryType); if ((desiredObjectType != LSA_AD_BATCH_OBJECT_TYPE_UNDEFINED) && (objectType != desiredObjectType)) { PCSTR pszType = LsaAdBatchGetQueryTypeAsString(QueryType); LSA_LOG_DEBUG("Object type mismatch for %s '%s' - got %u instead of %u", pszType, pszCompare, objectType, desiredObjectType); // This cannot happen because we restrict the type we search on. LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } // Search of corresponding batch item for (pLinks = pStartBatchItemListLinks; pLinks != pEndBatchItemListLinks; pLinks = pLinks->Next) { PLSA_AD_BATCH_ITEM pItem = LW_STRUCT_FROM_FIELD(pLinks, LSA_AD_BATCH_ITEM, BatchItemListLinks); XXX; // may want to just skip if no query term...hmm... LSA_ASSERT(pItem->pszQueryMatchTerm); if (!strcasecmp(pItem->pszQueryMatchTerm, pszCompare)) { pFoundItem = pItem; break; } } if (!pFoundItem) { PCSTR pszType = LsaAdBatchGetQueryTypeAsString(QueryType); LSA_LOG_DEBUG("Did not find batch item for message for %s '%s'", pszType, pszCompare); LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } dwError = LsaAdBatchGatherRealObject( pFoundItem, objectType, (LSA_AD_BATCH_QUERY_TYPE_BY_SID == QueryType) ? &pszCompare : NULL, hDirectory, pMessage); BAIL_ON_LSA_ERROR(dwError); cleanup: LW_SAFE_FREE_STRING(pszCompare); return dwError; error: goto cleanup; } static DWORD LsaAdBatchProcessPseudoObject( IN LSA_AD_BATCH_QUERY_TYPE QueryType, // List of PLSA_AD_BATCH_ITEM IN OUT PLSA_LIST_LINKS pStartBatchItemListLinks, IN PLSA_LIST_LINKS pEndBatchItemListLinks, IN BOOLEAN bIsGcSearch, IN BOOLEAN bIsSchemaMode, IN HANDLE hDirectory, IN LDAPMessage* pMessage ) { DWORD dwError = 0; BOOLEAN bIsValid = FALSE; PSTR* ppszKeywordValues = NULL; DWORD dwKeywordValuesCount = 0; PSTR pszFreeCompare = NULL; PCSTR pszCompare = NULL; LSA_AD_BATCH_OBJECT_TYPE objectType = 0; LSA_AD_BATCH_OBJECT_TYPE desiredObjectType = 0; PLSA_LIST_LINKS pLinks = NULL; PLSA_AD_BATCH_ITEM pFoundItem = NULL; dwError = LwLdapIsValidADEntry( hDirectory, pMessage, &bIsValid); BAIL_ON_LSA_ERROR(dwError); if (!bIsValid) { dwError = LW_ERROR_LDAP_FAILED_GETDN; BAIL_ON_LSA_ERROR(dwError); } if (!LsaAdBatchIsDefaultSchemaMode()) { dwError = LwLdapGetStrings( hDirectory, pMessage, AD_LDAP_KEYWORDS_TAG, &ppszKeywordValues, &dwKeywordValuesCount); BAIL_ON_LSA_ERROR(dwError); } // Get compare string dwError = LsaAdBatchGetCompareStringFromPseudoObject( &pszCompare, &pszFreeCompare, QueryType, bIsSchemaMode, dwKeywordValuesCount, ppszKeywordValues, hDirectory, pMessage); if (LW_IS_NULL_OR_EMPTY_STR(pszCompare)) { LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } // Get and check object type dwError = LsaAdBatchGetObjectTypeFromPseudoMessage( &objectType, bIsSchemaMode, dwKeywordValuesCount, ppszKeywordValues, hDirectory, pMessage); BAIL_ON_LSA_ERROR(dwError); // Sanity check. desiredObjectType = LsaAdBatchGetObjectTypeFromQueryType(QueryType); if ((desiredObjectType != LSA_AD_BATCH_OBJECT_TYPE_UNDEFINED) && (objectType != desiredObjectType)) { PCSTR pszType = LsaAdBatchGetQueryTypeAsString(QueryType); LSA_LOG_DEBUG("Object type mismatch for %s '%s' - got %u instead of %u", pszType, pszCompare, objectType, desiredObjectType); // This cannot happen because we restrict the type we search on. LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } // Search of corresponding batch item for (pLinks = pStartBatchItemListLinks; pLinks != pEndBatchItemListLinks; pLinks = pLinks->Next) { PLSA_AD_BATCH_ITEM pItem = LW_STRUCT_FROM_FIELD(pLinks, LSA_AD_BATCH_ITEM, BatchItemListLinks); if (!pItem->pszQueryMatchTerm) { // There are two possible cases here: // // 1) This is a linked cell case where we are searching // linked cells since we keep using the main batch // item list (though this case should go away in the // future as we just keep an unresolved batch items list). // // 2) This is an item for which we did not find a real object. // This case might be eliminated in the future by removing // unresolvable objects from the batch items list. continue; } if (!strcasecmp(pItem->pszQueryMatchTerm, pszCompare)) { pFoundItem = pItem; break; } } if (!pFoundItem) { PCSTR pszType = LsaAdBatchGetQueryTypeAsString(QueryType); LSA_LOG_DEBUG("Did not find batch item for message for %s '%s'", pszType, pszCompare); LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } if (bIsGcSearch) { dwError = LsaAdBatchGatherPseudoObjectSidFromGc( pFoundItem, objectType, dwKeywordValuesCount, ppszKeywordValues, hDirectory, pMessage); BAIL_ON_LSA_ERROR(dwError); } else { dwError = LsaAdBatchGatherPseudoObject( pFoundItem, objectType, bIsSchemaMode, dwKeywordValuesCount, ppszKeywordValues, hDirectory, pMessage); BAIL_ON_LSA_ERROR(dwError); } cleanup: LwFreeStringArray(ppszKeywordValues, dwKeywordValuesCount); LW_SAFE_FREE_STRING(pszFreeCompare); return dwError; error: goto cleanup; } static DWORD LsaAdBatchProcessPseudoObjectDefaultSchema( IN LSA_AD_BATCH_QUERY_TYPE QueryType, // List of PLSA_AD_BATCH_ITEM IN OUT PLSA_LIST_LINKS pStartBatchItemListLinks, IN PLSA_LIST_LINKS pEndBatchItemListLinks, IN HANDLE hDirectory, IN LDAPMessage* pMessage ) { DWORD dwError = 0; PSTR pszCompare = NULL; LSA_AD_BATCH_OBJECT_TYPE objectType = 0; LSA_AD_BATCH_OBJECT_TYPE desiredObjectType = 0; PLSA_LIST_LINKS pLinks = NULL; PLSA_AD_BATCH_ITEM pFoundItem = NULL; // Get compare string dwError = LsaAdBatchGetCompareStringFromPseudoObjectDefaultSchema( &pszCompare, QueryType, hDirectory, pMessage); BAIL_ON_LSA_ERROR(dwError); // Get and check object type dwError = LsaAdBatchGetObjectTypeFromRealMessage( &objectType, hDirectory, pMessage); BAIL_ON_LSA_ERROR(dwError); // Sanity check. desiredObjectType = LsaAdBatchGetObjectTypeFromQueryType(QueryType); if ((desiredObjectType != LSA_AD_BATCH_OBJECT_TYPE_UNDEFINED) && (objectType != desiredObjectType)) { PCSTR pszType = LsaAdBatchGetQueryTypeAsString(QueryType); LSA_LOG_DEBUG("Object type mismatch for %s '%s' - got %u instead of %u", pszType, pszCompare, objectType, desiredObjectType); // This cannot happen because we restrict the type we search on. LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } // Search of corresponding batch item for (pLinks = pStartBatchItemListLinks; pLinks != pEndBatchItemListLinks; pLinks = pLinks->Next) { PLSA_AD_BATCH_ITEM pItem = LW_STRUCT_FROM_FIELD(pLinks, LSA_AD_BATCH_ITEM, BatchItemListLinks); if (!pItem->pszQueryMatchTerm) { // There are two possible cases here: // // 1) This is a linked cell case where we are searching // linked cells since we keep using the main batch // item list (though this case should go away in the // future as we just keep an unresolved batch items list). // // 2) This is an item for which we did not find a real object. // This case might be eliminated in the future by removing // unresolvable objects from the batch items list. continue; } if (!strcasecmp(pItem->pszQueryMatchTerm, pszCompare)) { pFoundItem = pItem; break; } } if (!pFoundItem) { PCSTR pszType = LsaAdBatchGetQueryTypeAsString(QueryType); LSA_LOG_DEBUG("Did not find batch item for message for %s '%s'", pszType, pszCompare); LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } dwError = LsaAdBatchGatherPseudoObjectDefaultSchema( pFoundItem, objectType, (LSA_AD_BATCH_QUERY_TYPE_BY_SID == QueryType) ? &pszCompare : NULL, hDirectory, pMessage); BAIL_ON_LSA_ERROR(dwError); cleanup: LW_SAFE_FREE_STRING(pszCompare); return dwError; error: goto cleanup; } static PCSTR LsaAdBatchGetQueryTypeAsString( IN LSA_AD_BATCH_QUERY_TYPE QueryType ) { PCSTR pszType = NULL; switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_DN: pszType = "DN"; break; case LSA_AD_BATCH_QUERY_TYPE_BY_SID: pszType = "SID"; break; case LSA_AD_BATCH_QUERY_TYPE_BY_NT4: pszType = "NT4 name"; break; case LSA_AD_BATCH_QUERY_TYPE_BY_USER_ALIAS: pszType = "user alias"; break; case LSA_AD_BATCH_QUERY_TYPE_BY_GROUP_ALIAS: pszType = "group alias"; break; case LSA_AD_BATCH_QUERY_TYPE_BY_UID: pszType = "uid"; break; case LSA_AD_BATCH_QUERY_TYPE_BY_GID: pszType = "gid"; break; default: pszType = ""; break; } return pszType; } static BOOLEAN LsaAdBatchGetQueryTypeIsString( IN LSA_AD_BATCH_QUERY_TYPE QueryType ) { BOOLEAN bIsString = FALSE; switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_DN: case LSA_AD_BATCH_QUERY_TYPE_BY_SID: case LSA_AD_BATCH_QUERY_TYPE_BY_NT4: case LSA_AD_BATCH_QUERY_TYPE_BY_USER_ALIAS: case LSA_AD_BATCH_QUERY_TYPE_BY_GROUP_ALIAS: bIsString = TRUE; break; case LSA_AD_BATCH_QUERY_TYPE_BY_UID: case LSA_AD_BATCH_QUERY_TYPE_BY_GID: break; default: break; } return bIsString; } VOID LsaAdBatchQueryTermDebugInfo( IN PLSA_AD_BATCH_QUERY_TERM pQueryTerm, OUT OPTIONAL PCSTR* ppszType, OUT OPTIONAL PBOOLEAN pbIsString, OUT OPTIONAL PCSTR* ppszString, OUT OPTIONAL PDWORD pdwId ) { PCSTR pszType = LsaAdBatchGetQueryTypeAsString(pQueryTerm->Type); BOOLEAN bIsString = LsaAdBatchGetQueryTypeIsString(pQueryTerm->Type); PCSTR pszString = NULL; DWORD dwId = 0; if (bIsString) { pszString = pQueryTerm->pszString; } else { dwId = pQueryTerm->dwId; } if (ppszType) { *ppszType = pszType; } if (pbIsString) { *pbIsString = bIsString; } if (ppszString) { *ppszString= pszString; } if (pdwId) { *pdwId = dwId; } } DWORD LsaAdBatchAccountTypeToObjectType( IN LSA_OBJECT_TYPE AccountType, OUT PLSA_AD_BATCH_OBJECT_TYPE pObjectType ) { DWORD dwError = 0; LSA_AD_BATCH_OBJECT_TYPE objectType = 0; switch (AccountType) { case LSA_OBJECT_TYPE_USER: objectType = LSA_AD_BATCH_OBJECT_TYPE_USER; break; case LSA_OBJECT_TYPE_GROUP: objectType = LSA_AD_BATCH_OBJECT_TYPE_GROUP; break; default: LSA_ASSERT(FALSE); dwError = LW_ERROR_INTERNAL; } *pObjectType = objectType; return dwError; } LSA_AD_BATCH_OBJECT_TYPE LsaAdBatchGetObjectTypeFromQueryType( IN LSA_AD_BATCH_QUERY_TYPE QueryType ) { LSA_AD_BATCH_OBJECT_TYPE objectType = LSA_AD_BATCH_OBJECT_TYPE_UNDEFINED; switch (QueryType) { case LSA_AD_BATCH_QUERY_TYPE_BY_UID: case LSA_AD_BATCH_QUERY_TYPE_BY_USER_ALIAS: objectType = LSA_AD_BATCH_OBJECT_TYPE_USER; break; case LSA_AD_BATCH_QUERY_TYPE_BY_GID: case LSA_AD_BATCH_QUERY_TYPE_BY_GROUP_ALIAS: objectType = LSA_AD_BATCH_OBJECT_TYPE_GROUP; break; default: // The default is undefined (i.e., any). break; } return objectType; } BOOLEAN LsaAdBatchHasValidCharsForSid( IN PCSTR pszSidString ) { BOOLEAN bHasOnlyValidChars = TRUE; PCSTR pszCurrent = pszSidString; while (*pszCurrent) { if (!(*pszCurrent == '-' || (*pszCurrent == 'S' || *pszCurrent == 's') || (*pszCurrent >= '0' && *pszCurrent <= '9'))) { bHasOnlyValidChars = FALSE; break; } pszCurrent++; } return bHasOnlyValidChars; }