You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3567 lines
113 KiB
3567 lines
113 KiB
//*************************************************************
|
|
//
|
|
// Group Policy Support - Queries about the Policies
|
|
//
|
|
// Microsoft Confidential
|
|
// Copyright (c) Microsoft Corporation 1997-1998
|
|
// All rights reserved
|
|
//
|
|
//*************************************************************
|
|
|
|
#include "gphdr.h"
|
|
#include <strsafe.h>
|
|
|
|
//*************************************************************
|
|
//
|
|
// AddGPO()
|
|
//
|
|
// Purpose: Adds a GPO to the list
|
|
//
|
|
// Parameters: lpGPOList - list of GPOs
|
|
// dwFlags - Flags
|
|
// bFound - Was Gpo found ?
|
|
// bAccessGranted - Was access granted ?
|
|
// bDisabled - Is this Gpo disabled ?
|
|
// dwOptions - Options
|
|
// dwVersion - Version number
|
|
// lpDSPath - DS path
|
|
// lpFileSysPath - File system path
|
|
// lpDisplayName - Friendly display name
|
|
// lpGPOName - GPO name
|
|
// lpExtensions - Extensions relevant to this GPO
|
|
// lpDSObject - LSDOU
|
|
// pSD - Ptr to security descriptor
|
|
// cbSDLen - Length of security descriptor in bytes
|
|
// GPOLink - GPO link type
|
|
// lpLink - SDOU this GPO is linked to
|
|
// lParam - lParam
|
|
// bFront - Head or end of list
|
|
// bBlock - Block from above flag
|
|
// bVerbose - Verbose output flag
|
|
//
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL AddGPO (PGROUP_POLICY_OBJECT * lpGPOList,
|
|
DWORD dwFlags, BOOL bFound, BOOL bAccessGranted, BOOL bDisabled, DWORD dwOptions,
|
|
DWORD dwVersion, LPTSTR lpDSPath, LPTSTR lpFileSysPath,
|
|
LPTSTR lpDisplayName, LPTSTR lpGPOName, LPTSTR lpExtensions,
|
|
PSECURITY_DESCRIPTOR pSD, DWORD cbSDLen,
|
|
GPO_LINK GPOLink, LPTSTR lpLink,
|
|
LPARAM lParam, BOOL bFront, BOOL bBlock, BOOL bVerbose, BOOL bProcessGPO)
|
|
{
|
|
PGROUP_POLICY_OBJECT lpNew, lpTemp;
|
|
DWORD dwSize;
|
|
XLastError xe;
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Check if this item should be excluded from the list
|
|
//
|
|
|
|
if (bBlock) {
|
|
if (!(dwOptions & GPO_FLAG_FORCE)) {
|
|
DebugMsg((DM_VERBOSE, TEXT("AddGPO: GPO %s will not be added to the list since the Block flag is set and this GPO is not in enforce mode."),
|
|
lpDisplayName));
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_SKIP_GPO);
|
|
ev.AddArg(lpDisplayName); ev.Report();
|
|
}
|
|
|
|
if (dwFlags & GP_PLANMODE) {
|
|
DebugMsg((DM_VERBOSE, TEXT("AddGPO: GPO %s will will still be queried for since this is planning mode."),
|
|
lpDisplayName));
|
|
bProcessGPO = FALSE;
|
|
}
|
|
else
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Calculate the size of the new GPO item
|
|
//
|
|
|
|
dwSize = sizeof (GROUP_POLICY_OBJECT);
|
|
|
|
if (lpDSPath) {
|
|
dwSize += ((lstrlen(lpDSPath) + 1) * sizeof(TCHAR));
|
|
}
|
|
|
|
if (lpFileSysPath) {
|
|
dwSize += ((lstrlen(lpFileSysPath) + 1) * sizeof(TCHAR));
|
|
}
|
|
|
|
if (lpDisplayName) {
|
|
dwSize += ((lstrlen(lpDisplayName) + 1) * sizeof(TCHAR));
|
|
}
|
|
|
|
if (lpExtensions) {
|
|
dwSize += ((lstrlen(lpExtensions) + 1) * sizeof(TCHAR));
|
|
}
|
|
|
|
if (lpLink) {
|
|
dwSize += ((lstrlen(lpLink) + 1) * sizeof(TCHAR));
|
|
}
|
|
|
|
dwSize += sizeof(GPOPROCDATA);
|
|
|
|
//
|
|
// Allocate space for it
|
|
//
|
|
|
|
lpNew = (PGROUP_POLICY_OBJECT) LocalAlloc (LPTR, dwSize);
|
|
|
|
if (!lpNew) {
|
|
DebugMsg((DM_WARNING, TEXT("AddGPO: Failed to allocate memory with %d"),
|
|
GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Fill in item
|
|
//
|
|
|
|
LPGPOPROCDATA lpGpoProcData;
|
|
|
|
lpNew->lParam2 = (LPARAM)(((LPBYTE)lpNew) + sizeof(GROUP_POLICY_OBJECT));
|
|
lpGpoProcData = (LPGPOPROCDATA)lpNew->lParam2;
|
|
lpGpoProcData->bProcessGPO = bProcessGPO;
|
|
|
|
|
|
|
|
lpNew->dwOptions = dwOptions;
|
|
lpNew->dwVersion = dwVersion;
|
|
|
|
if (lpDSPath) {
|
|
lpNew->lpDSPath = (LPTSTR)(((LPBYTE)lpNew) + sizeof(GROUP_POLICY_OBJECT) + sizeof(GPOPROCDATA));
|
|
hr = StringCchCopy (lpNew->lpDSPath, lstrlen(lpDSPath) + 1, lpDSPath);
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
if (lpFileSysPath) {
|
|
if (lpDSPath) {
|
|
lpNew->lpFileSysPath = lpNew->lpDSPath + lstrlen (lpNew->lpDSPath) + 1;
|
|
} else {
|
|
lpNew->lpFileSysPath = (LPTSTR)(((LPBYTE)lpNew) + sizeof(GROUP_POLICY_OBJECT) + sizeof(GPOPROCDATA));
|
|
}
|
|
|
|
hr = StringCchCopy (lpNew->lpFileSysPath, lstrlen(lpFileSysPath) + 1, lpFileSysPath);
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
|
|
if (lpDisplayName) {
|
|
if (lpFileSysPath) {
|
|
lpNew->lpDisplayName = lpNew->lpFileSysPath + lstrlen (lpNew->lpFileSysPath) + 1;
|
|
} else {
|
|
|
|
if (lpDSPath)
|
|
{
|
|
lpNew->lpDisplayName = lpNew->lpDSPath + lstrlen (lpNew->lpDSPath) + 1;
|
|
}
|
|
else
|
|
{
|
|
lpNew->lpDisplayName = (LPTSTR)(((LPBYTE)lpNew) + sizeof(GROUP_POLICY_OBJECT) + sizeof(GPOPROCDATA));
|
|
}
|
|
}
|
|
|
|
hr = StringCchCopy (lpNew->lpDisplayName, lstrlen(lpDisplayName) + 1, lpDisplayName);
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
|
|
if (lpGPOName) {
|
|
DmAssert( lstrlen(lpGPOName) < 50 );
|
|
hr = StringCchCopy (lpNew->szGPOName, 50, lpGPOName);
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
if (lpExtensions) {
|
|
if (lpDisplayName) {
|
|
lpNew->lpExtensions = lpNew->lpDisplayName + lstrlen(lpNew->lpDisplayName) + 1;
|
|
} else {
|
|
|
|
if (lpFileSysPath) {
|
|
lpNew->lpExtensions = lpNew->lpFileSysPath + lstrlen(lpNew->lpFileSysPath) + 1;
|
|
} else {
|
|
|
|
if (lpDSPath) {
|
|
lpNew->lpExtensions = lpNew->lpDSPath + lstrlen(lpNew->lpDSPath) + 1;
|
|
} else {
|
|
lpNew->lpExtensions = (LPTSTR)(((LPBYTE)lpNew) + sizeof(GROUP_POLICY_OBJECT) + sizeof(GPOPROCDATA));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
hr = StringCchCopy (lpNew->lpExtensions, lstrlen(lpExtensions) + 1, lpExtensions);
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
if (lpLink) {
|
|
if (lpExtensions) {
|
|
lpNew->lpLink = lpNew->lpExtensions + lstrlen(lpNew->lpExtensions) + 1;
|
|
} else {
|
|
if (lpDisplayName) {
|
|
lpNew->lpLink = lpNew->lpDisplayName + lstrlen(lpNew->lpDisplayName) + 1;
|
|
} else {
|
|
|
|
if (lpFileSysPath) {
|
|
lpNew->lpLink = lpNew->lpFileSysPath + lstrlen(lpNew->lpFileSysPath) + 1;
|
|
} else {
|
|
|
|
if (lpDSPath) {
|
|
lpNew->lpLink = lpNew->lpDSPath + lstrlen(lpNew->lpDSPath) + 1;
|
|
} else {
|
|
lpNew->lpLink = (LPTSTR)(((LPBYTE)lpNew) + sizeof(GROUP_POLICY_OBJECT) + sizeof(GPOPROCDATA));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = StringCchCopy (lpNew->lpLink, lstrlen(lpLink) + 1, lpLink);
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
lpNew->GPOLink = GPOLink;
|
|
lpNew->lParam = lParam;
|
|
|
|
//
|
|
// Add item to link list
|
|
//
|
|
|
|
if (*lpGPOList) {
|
|
|
|
if (bFront) {
|
|
|
|
(*lpGPOList)->pPrev = lpNew;
|
|
lpNew->pNext = *lpGPOList;
|
|
*lpGPOList = lpNew;
|
|
|
|
} else {
|
|
|
|
lpTemp = *lpGPOList;
|
|
|
|
while (lpTemp->pNext != NULL) {
|
|
lpTemp = lpTemp->pNext;
|
|
}
|
|
|
|
lpTemp->pNext = lpNew;
|
|
lpNew->pPrev = lpTemp;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// First item in the list
|
|
//
|
|
|
|
*lpGPOList = lpNew;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// AddGPOToRsopList
|
|
//
|
|
// Purpose: Adds GPO to list of GPOs being logged by Rsop
|
|
//
|
|
// Parameters: ppGpContainerList - List of Gp Containers
|
|
// dwFlags - Flags
|
|
// bFound - Was Gpo found ?
|
|
// bAccessGranted - Was access granted ?
|
|
// bDisabled - Is this Gpo disabled ?
|
|
// dwOptions - Options
|
|
// dwVersion - Version number
|
|
// lpDSPath - DS path
|
|
// lpFileSysPath - File system path
|
|
// lpDisplayName - Friendly display name
|
|
// lpGPOName - GPO name
|
|
// pSD - Pointer to security descriptor
|
|
// cbSDLen - Length of security descriptor in bytes
|
|
// bFilterAllowed - Does GPO pass filter check
|
|
// pwszFilterId - WQL filter id
|
|
// szSOM - SOM
|
|
// dwGPOOptions - GPO options
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL AddGPOToRsopList( LPGPCONTAINER *ppGpContainerList,
|
|
DWORD dwFlags,
|
|
BOOL bFound,
|
|
BOOL bAccessGranted,
|
|
BOOL bDisabled,
|
|
DWORD dwVersion,
|
|
LPTSTR lpDSPath,
|
|
LPTSTR lpFileSysPath,
|
|
LPTSTR lpDisplayName,
|
|
LPTSTR lpGPOName,
|
|
PSECURITY_DESCRIPTOR pSD,
|
|
DWORD cbSDLen,
|
|
BOOL bFilterAllowed,
|
|
WCHAR *pwszFilterId,
|
|
LPWSTR szSOM,
|
|
DWORD dwOptions)
|
|
{
|
|
// Fixing bug 568213
|
|
XLastError xe;
|
|
|
|
GPCONTAINER *pGpContainer = AllocGpContainer( dwFlags,
|
|
bFound,
|
|
bAccessGranted,
|
|
bDisabled,
|
|
dwVersion,
|
|
lpDSPath,
|
|
lpFileSysPath,
|
|
lpDisplayName,
|
|
lpGPOName,
|
|
pSD,
|
|
cbSDLen,
|
|
bFilterAllowed,
|
|
pwszFilterId,
|
|
szSOM,
|
|
dwOptions );
|
|
if ( pGpContainer == NULL ) {
|
|
DebugMsg((DM_VERBOSE, TEXT("AddGPO: Failed to allocate memory for Gp Container.")));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Prepend to GpContainer list
|
|
//
|
|
|
|
pGpContainer->pNext = *ppGpContainerList;
|
|
*ppGpContainerList = pGpContainer;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// AddLocalGPO()
|
|
//
|
|
// Purpose: Adds a local Gpo to the list of SOMs
|
|
//
|
|
// Parameters: ppSOMList - List of SOMs
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL AddLocalGPO( LPSCOPEOFMGMT *ppSOMList )
|
|
{
|
|
GPLINK *pGpLink = NULL;
|
|
XLastError xe;
|
|
SCOPEOFMGMT *pSOM = AllocSOM( L"Local" );
|
|
|
|
if ( pSOM == NULL ) {
|
|
DebugMsg((DM_WARNING, TEXT("AddLocalGPO: Unable to allocate memory for SOM object")));
|
|
return FALSE;
|
|
}
|
|
|
|
pSOM->dwType = GPLinkMachine;
|
|
// Local GPO cannot be blocked from above
|
|
|
|
|
|
pGpLink = AllocGpLink( L"LocalGPO", 0 );
|
|
if ( pGpLink == NULL ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("AddLocalGPO: Unable to allocate memory for GpLink object")));
|
|
FreeSOM( pSOM );
|
|
return FALSE;
|
|
}
|
|
|
|
pSOM->pGpLinkList = pGpLink;
|
|
pSOM->pNext = *ppSOMList;
|
|
*ppSOMList = pSOM;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// ProcessGPO()
|
|
//
|
|
// Purpose: Processes a specific GPO
|
|
//
|
|
// Parameters: lpGPOPath - Path to the GPO
|
|
// lpDSPath - DS object
|
|
// dwFlags - GetGPOList flags
|
|
// HANDLE - user or machine aceess token
|
|
// lpGPOList - List of GPOs
|
|
// ppGpContainerList - List of Gp containers
|
|
// dwGPOOptions - Link options
|
|
// bDeferred - Should ldap query be deferred ?
|
|
// bVerbose - Verbose output
|
|
// GPOLink - GPO link type
|
|
// lpDSObject - SDOU this gpo is linked to
|
|
// pld - LDAP info
|
|
// pLDAP - LDAP api
|
|
// pLdapMsg - LDAP message
|
|
// bBlock - Block flag
|
|
// bRsopToken - Rsop security token
|
|
// pGpoFilter - Gpo filter
|
|
// pLocator - WMI interface class
|
|
// bAddGPO - In planning mode we want to get the gpodata even if
|
|
// the GPO is not going to be applied
|
|
//
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL ProcessGPO(LPTSTR lpGPOPath,
|
|
DWORD dwFlags,
|
|
HANDLE hToken,
|
|
PGROUP_POLICY_OBJECT *lpGPOList,
|
|
LPGPCONTAINER *ppGpContainerList,
|
|
DWORD dwGPOOptions,
|
|
BOOL bDeferred,
|
|
BOOL bVerbose,
|
|
GPO_LINK GPOLink,
|
|
LPTSTR lpDSObject,
|
|
PLDAP pld,
|
|
PLDAP_API pLDAP,
|
|
PLDAPMessage pMessage,
|
|
BOOL bBlock,
|
|
PRSOPTOKEN pRsopToken,
|
|
CGpoFilter *pGpoFilter,
|
|
CLocator *pLocator,
|
|
BOOL bProcessGPO )
|
|
{
|
|
ULONG ulResult, i;
|
|
BOOL bResult = FALSE;
|
|
BOOL bFound = FALSE;
|
|
BOOL bOwnLdapMsg = FALSE; // LDAP message owned by us (if true) or caller (if false)
|
|
BOOL bAccessGranted;
|
|
DWORD dwFunctionalityVersion = 2;
|
|
DWORD dwVersion = 0;
|
|
DWORD dwGPOFlags = 0;
|
|
DWORD dwGPTVersion = 0;
|
|
TCHAR szGPOName[80];
|
|
TCHAR *pszGPTPath = 0;
|
|
TCHAR *pszFriendlyName = 0;
|
|
LPTSTR lpPath, lpEnd, lpTemp;
|
|
TCHAR *pszExtensions = 0;
|
|
TCHAR szLDAP[] = TEXT("LDAP://");
|
|
INT iStrLen = lstrlen(szLDAP);
|
|
BYTE berValue[8];
|
|
LDAPControl SeInfoControl = { LDAP_SERVER_SD_FLAGS_OID_W, { 5, (PCHAR)berValue }, TRUE };
|
|
LDAPControl referralControl = { LDAP_SERVER_DOMAIN_SCOPE_OID_W, { 0, NULL}, TRUE };
|
|
PLDAPControl ServerControls[] = { &SeInfoControl, &referralControl, NULL };
|
|
TCHAR szSDProperty[] = TEXT("nTSecurityDescriptor");
|
|
TCHAR szCommonName[] = TEXT("cn");
|
|
TCHAR szDisplayName[] = TEXT("displayName");
|
|
TCHAR szFileSysPath[] = TEXT("gPCFileSysPath");
|
|
TCHAR szVersion[] = TEXT("versionNumber");
|
|
TCHAR szFunctionalityVersion[] = GPO_FUNCTIONALITY_VERSION;
|
|
TCHAR szFlags[] = TEXT("flags");
|
|
TCHAR szWmiFilter[] = TEXT("gPCWQLFilter");
|
|
|
|
PWSTR rgAttribs[12] = {szSDProperty,
|
|
szFileSysPath,
|
|
szCommonName,
|
|
szDisplayName,
|
|
szVersion,
|
|
szFunctionalityVersion,
|
|
szFlags,
|
|
GPO_MACHEXTENSION_NAMES,
|
|
GPO_USEREXTENSION_NAMES,
|
|
szObjectClass,
|
|
szWmiFilter,
|
|
NULL };
|
|
LPTSTR *lpValues;
|
|
PSECURITY_DESCRIPTOR pSD = NULL; // Security Descriptor
|
|
DWORD cbSDLen = 0; // Length of security descriptor in bytes
|
|
BOOL bRsopLogging = (ppGpContainerList != NULL);
|
|
BOOL bOldGpoVersion = FALSE;
|
|
BOOL bDisabled = FALSE;
|
|
BOOL bNoGpoData = FALSE;
|
|
BOOL bFilterAllowed = FALSE;
|
|
WCHAR *pwszFilterId = NULL;
|
|
XLastError xe;
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Verbose output
|
|
//
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: ==============================")));
|
|
|
|
//
|
|
// Skip the starting LDAP provider if found
|
|
//
|
|
|
|
if (CompareString (LOCALE_INVARIANT, NORM_IGNORECASE,
|
|
lpGPOPath, iStrLen, szLDAP, iStrLen) == CSTR_EQUAL)
|
|
{
|
|
lpPath = lpGPOPath + iStrLen;
|
|
}
|
|
else
|
|
{
|
|
lpPath = lpGPOPath;
|
|
}
|
|
|
|
if ( bDeferred )
|
|
{
|
|
bResult = AddGPO (lpGPOList, dwFlags, TRUE, TRUE, FALSE, dwGPOOptions, 0, lpPath,
|
|
0, 0, 0, 0, 0, 0, GPOLink, lpDSObject, 0,
|
|
FALSE, bBlock, bVerbose, bProcessGPO);
|
|
if (!bResult)
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: Failed to add GPO <%s> to the list."), lpPath));
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: Deferring search for <%s>"), lpGPOPath));
|
|
|
|
return bResult;
|
|
}
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: Searching <%s>"), lpGPOPath));
|
|
|
|
//
|
|
// Check if this user or machine has access to the GPO, and if so,
|
|
// should that GPO be applied to them.
|
|
//
|
|
|
|
if (!CheckGPOAccess (pld, pLDAP, hToken, pMessage, szSDProperty, dwFlags, &pSD, &cbSDLen, &bAccessGranted, pRsopToken)) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: CheckGPOAccess failed for <%s>"), lpGPOPath));
|
|
CEvents ev(TRUE, EVENT_FAILED_ACCESS_CHECK);
|
|
ev.AddArg(lpGPOPath); ev.AddArgWin32Error(GetLastError()); ev.Report();
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
if (!bAccessGranted) {
|
|
if (dwFlags & GPO_LIST_FLAG_MACHINE) {
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: Machine does not have access to the GPO and so will not be applied.")));
|
|
} else {
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: User does not have access to the GPO and so will not be applied.")));
|
|
}
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_NO_ACCESS);
|
|
ev.AddArg(lpGPOPath); ev.Report();
|
|
}
|
|
|
|
bResult = TRUE; // GPO is not getting applied
|
|
if ( !bRsopLogging ) {
|
|
goto Exit;
|
|
}
|
|
} else {
|
|
|
|
if (dwFlags & GPO_LIST_FLAG_MACHINE) {
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: Machine has access to this GPO.")));
|
|
} else {
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: User has access to this GPO.")));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// only if access is granted will we eval WQL filters
|
|
if ( bAccessGranted ) {
|
|
|
|
if (!FilterCheck(pld, pLDAP, pMessage, pRsopToken, szWmiFilter, pGpoFilter, pLocator, &bFilterAllowed, &pwszFilterId ) ) {
|
|
xe = GetLastError();
|
|
|
|
if (xe == WBEM_E_NOT_FOUND) {
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: CheckFilterAcess failed for <%s>. Filter not found"), lpGPOPath));
|
|
CEvents ev(TRUE, EVENT_WMIFILTER_NOTFOUND);
|
|
ev.AddArg(lpGPOPath); ev.Report();
|
|
bFilterAllowed = FALSE;
|
|
}
|
|
else if (xe == HRESULT_FROM_WIN32(ERROR_SERVICE_DISABLED)) {
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: CheckFilterAcess failed for <%s>. WMI service is disabled"), lpGPOPath));
|
|
CEvents ev(TRUE, EVENT_WMISERVICE_DISABLED);
|
|
ev.AddArg(lpGPOPath); ev.Report();
|
|
bFilterAllowed = FALSE;
|
|
}
|
|
else {
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: CheckFilterAcess failed for <%s>"), lpGPOPath));
|
|
CEvents ev(TRUE, EVENT_FAILED_FILTER_CHECK);
|
|
ev.AddArg(lpGPOPath); ev.Report();
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
|
|
if ( (dwFlags & GP_PLANMODE) && (dwFlags & GPO_LIST_FLAG_MACHINE) && (dwFlags & FLAG_ASSUME_COMP_WQLFILTER_TRUE) ) {
|
|
bFilterAllowed = TRUE;
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: Machine WQL filter is assumed to be true.")));
|
|
}
|
|
else if ( (dwFlags & GP_PLANMODE) && ((dwFlags & GPO_LIST_FLAG_MACHINE) == 0) && (dwFlags & FLAG_ASSUME_USER_WQLFILTER_TRUE) ) {
|
|
bFilterAllowed = TRUE;
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: User WQL filter is assumed to be true.")));
|
|
}
|
|
|
|
if (!bFilterAllowed)
|
|
{
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: The GPO does not pass the filter check and so will not be applied.")));
|
|
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_NO_FILTER_ALLOWED);
|
|
ev.AddArg(lpGPOPath); ev.Report();
|
|
}
|
|
|
|
bResult = TRUE; // GPO is not getting applied
|
|
if ( !bRsopLogging ) {
|
|
goto Exit;
|
|
}
|
|
|
|
} else {
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: GPO passes the filter check.")));
|
|
}
|
|
|
|
}
|
|
else {
|
|
bFilterAllowed = FALSE;
|
|
}
|
|
|
|
//
|
|
// Either user has access to this GPO, or Rsop logging is enabled so retrieve remaining GPO attributes
|
|
//
|
|
|
|
//
|
|
// Check if this object is a GPO
|
|
//
|
|
|
|
lpValues = pLDAP->pfnldap_get_values(pld, pMessage, szObjectClass);
|
|
|
|
if (lpValues) {
|
|
|
|
bFound = FALSE;
|
|
for ( i=0; lpValues[i] != NULL; i++) {
|
|
if ( lstrcmp( lpValues[i], szDSClassGPO ) == 0 ) {
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pLDAP->pfnldap_value_free (lpValues);
|
|
|
|
if ( !bFound ) {
|
|
xe = ERROR_DS_MISSING_REQUIRED_ATT;
|
|
// seems like objectclass=dsgpo is required attr
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: Object <%s> is not a GPO"), lpGPOPath ));
|
|
CEvents ev(TRUE, EVENT_INCORRECT_CLASS);
|
|
ev.AddArg(lpGPOPath); ev.AddArg(szDSClassGPO); ev.Report();
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// In the results, get the values that match the gPCFunctionalityVersion attribute
|
|
//
|
|
|
|
lpValues = pLDAP->pfnldap_get_values(pld, pMessage, szFunctionalityVersion);
|
|
|
|
if (lpValues) {
|
|
|
|
dwFunctionalityVersion = StringToInt (*lpValues);
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: Found functionality version of: %d"),
|
|
dwFunctionalityVersion));
|
|
pLDAP->pfnldap_value_free (lpValues);
|
|
|
|
} else {
|
|
|
|
ulResult = pLDAP->pfnLdapGetLastError();
|
|
|
|
if (ulResult == LDAP_NO_SUCH_ATTRIBUTE) {
|
|
if (dwFlags & GPO_LIST_FLAG_MACHINE) {
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: Machine does not have access to <%s>"), lpGPOPath));
|
|
} else {
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: User does not have access to <%s>"), lpGPOPath));
|
|
}
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_NO_ACCESS);
|
|
ev.AddArg(lpGPOPath); ev.Report();
|
|
}
|
|
bResult = TRUE;
|
|
|
|
} else {
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(ulResult);
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: GPO %s does not have a functionality version number, error = 0x%x."), lpGPOPath, ulResult));
|
|
CEvents ev(TRUE, EVENT_CORRUPT_GPO_FUNCVERSION);
|
|
ev.AddArg(lpGPOPath); ev.Report();
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// In the results, get the values that match the gPCFileSystemPath attribute
|
|
//
|
|
|
|
lpValues = pLDAP->pfnldap_get_values (pld, pMessage, szFileSysPath);
|
|
|
|
if (lpValues) {
|
|
// Fixing bug 568261
|
|
|
|
DWORD dwGPTPathLength = lstrlen(*lpValues) + MAX(lstrlen(TEXT("\\Machine")), lstrlen(TEXT("\\gpt.ini"))) + 1; //Take the MAX of MACHINE and gpt.ini
|
|
|
|
pszGPTPath = (LPWSTR) LocalAlloc( LPTR, (dwGPTPathLength) * sizeof(TCHAR) );
|
|
|
|
if ( pszGPTPath == 0) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: Unable to allocate memory")));
|
|
pLDAP->pfnldap_value_free (lpValues);
|
|
goto Exit;
|
|
}
|
|
|
|
hr = StringCchCopy (pszGPTPath, dwGPTPathLength, *lpValues);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: Found file system path of: <%s>"), pszGPTPath));
|
|
pLDAP->pfnldap_value_free (lpValues);
|
|
|
|
lpEnd = CheckSlash (pszGPTPath);
|
|
|
|
//
|
|
// Get the GPT version number
|
|
//
|
|
|
|
hr = StringCchCopy (lpEnd, dwGPTPathLength - (lpEnd - pszGPTPath), TEXT("gpt.ini"));
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
//
|
|
// Skip access to sysvol if AGP or filtercheck fails
|
|
//
|
|
|
|
if (bAccessGranted && bFilterAllowed) {
|
|
WIN32_FILE_ATTRIBUTE_DATA fad;
|
|
|
|
//
|
|
// Check for the existence of the gpt.ini file.
|
|
//
|
|
|
|
if (GetFileAttributesEx(pszGPTPath, GetFileExInfoStandard, &fad)) {
|
|
dwGPTVersion = GetPrivateProfileInt(TEXT("General"), TEXT("Version"), 0, pszGPTPath);
|
|
}
|
|
else {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: Couldn't find the group policy template file <%s>, error = 0x%x."), pszGPTPath, GetLastError()));
|
|
CEvents ev(TRUE, EVENT_GPT_NOTACCESSIBLE);
|
|
ev.AddArg(lpGPOPath); ev.AddArg(pszGPTPath); ev.AddArgWin32Error(GetLastError()); ev.Report();
|
|
goto Exit;
|
|
}
|
|
|
|
}
|
|
else {
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: Sysvol access skipped because GPO is not getting applied.")));
|
|
dwGPTVersion = 0xffffffff;
|
|
}
|
|
|
|
if (dwFlags & GPO_LIST_FLAG_MACHINE) {
|
|
hr = StringCchCopy (lpEnd, dwGPTPathLength - (lpEnd - pszGPTPath), TEXT("Machine"));
|
|
} else {
|
|
hr = StringCchCopy (lpEnd, dwGPTPathLength - (lpEnd - pszGPTPath), TEXT("User"));
|
|
}
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
} else {
|
|
ulResult = pLDAP->pfnLdapGetLastError();
|
|
|
|
if (ulResult == LDAP_NO_SUCH_ATTRIBUTE) {
|
|
if (dwFlags & GPO_LIST_FLAG_MACHINE) {
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: Machine does not have access to <%s>"), lpGPOPath));
|
|
} else {
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: User does not have access to <%s>"), lpGPOPath));
|
|
}
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_NO_ACCESS);
|
|
ev.AddArg(lpGPOPath); ev.Report();
|
|
}
|
|
bResult = TRUE;
|
|
|
|
} else {
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(ulResult);
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: GPO %s does not have a file system path, error = 0x%x."), lpGPOPath, ulResult));
|
|
CEvents ev(TRUE, EVENT_CORRUPT_GPO_FSPATH);
|
|
ev.AddArg(lpGPOPath); ev.Report();
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// In the results, get the values that match the common name attribute
|
|
//
|
|
|
|
lpValues = pLDAP->pfnldap_get_values(pld, pMessage, szCommonName);
|
|
|
|
if (lpValues && ValidateGuid(*lpValues)) {
|
|
|
|
DmAssert( lstrlen(*lpValues) < 80 );
|
|
|
|
hr = StringCchCopy (szGPOName, ARRAYSIZE(szGPOName), *lpValues);
|
|
|
|
if (FAILED(hr)) {
|
|
xe = ERROR_INSUFFICIENT_BUFFER;
|
|
CEvents ev(TRUE, EVENT_CORRUPT_GPO_COMMONNAME);
|
|
ev.AddArg(lpGPOPath); ev.Report();
|
|
goto Exit;
|
|
}
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: Found common name of: <%s>"), szGPOName));
|
|
pLDAP->pfnldap_value_free (lpValues);
|
|
|
|
} else {
|
|
ulResult = pLDAP->pfnLdapGetLastError();
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(ulResult);
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: GPO %s does not have a common name (a GUID)."), lpGPOPath));
|
|
CEvents ev(TRUE, EVENT_CORRUPT_GPO_COMMONNAME);
|
|
ev.AddArg(lpGPOPath); ev.Report();
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// In the results, get the values that match the display name attribute
|
|
//
|
|
|
|
lpValues = pLDAP->pfnldap_get_values(pld, pMessage, szDisplayName);
|
|
|
|
if (lpValues) {
|
|
|
|
DWORD dwFriendlyLength = lstrlen(*lpValues)+1;
|
|
pszFriendlyName = (LPWSTR) LocalAlloc( LPTR, (dwFriendlyLength) * sizeof(TCHAR) );
|
|
if ( pszFriendlyName == 0) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: Unable to allocate memory")));
|
|
pLDAP->pfnldap_value_free (lpValues);
|
|
goto Exit;
|
|
}
|
|
|
|
hr = StringCchCopy (pszFriendlyName, dwFriendlyLength, *lpValues);
|
|
ASSERT(SUCCEEDED(hr));
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: Found display name of: <%s>"), pszFriendlyName));
|
|
pLDAP->pfnldap_value_free (lpValues);
|
|
|
|
|
|
} else {
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: No display name for this object.")));
|
|
|
|
pszFriendlyName = (LPWSTR) LocalAlloc( LPTR, 2 * sizeof(TCHAR) );
|
|
if ( pszFriendlyName == 0) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: Unable to allocate memory")));
|
|
goto Exit;
|
|
}
|
|
|
|
pszFriendlyName[0] = TEXT('\0');
|
|
}
|
|
|
|
|
|
//
|
|
// In the results, get the values that match the version attribute
|
|
//
|
|
|
|
lpValues = pLDAP->pfnldap_get_values(pld, pMessage, szVersion);
|
|
|
|
if (lpValues) {
|
|
|
|
dwVersion = StringToInt (*lpValues);
|
|
|
|
if (dwFlags & GPO_LIST_FLAG_MACHINE) {
|
|
dwVersion = MAKELONG(LOWORD(dwVersion), LOWORD(dwGPTVersion));
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: Found machine version of: GPC is %d, GPT is %d"), LOWORD(dwVersion), HIWORD(dwVersion)));
|
|
|
|
} else {
|
|
dwVersion = MAKELONG(HIWORD(dwVersion), HIWORD(dwGPTVersion));
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: Found user version of: GPC is %d, GPT is %d"), LOWORD(dwVersion), HIWORD(dwVersion)));
|
|
}
|
|
|
|
pLDAP->pfnldap_value_free (lpValues);
|
|
|
|
} else {
|
|
// start treating this as an error.
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(pLDAP->pfnLdapGetLastError());
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: GPO %s does not have a version number."), lpGPOPath));
|
|
CEvents ev(TRUE, EVENT_NODSVERSION);
|
|
ev.AddArg(lpGPOPath); ev.AddArgLdapError(pLDAP->pfnLdapGetLastError()); ev.Report();
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// In the results, get the values that match the flags attribute
|
|
//
|
|
|
|
lpValues = pLDAP->pfnldap_get_values(pld, pMessage, szFlags);
|
|
|
|
if (lpValues) {
|
|
|
|
dwGPOFlags = StringToInt (*lpValues);
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: Found flags of: %d"), dwGPOFlags));
|
|
pLDAP->pfnldap_value_free (lpValues);
|
|
|
|
|
|
} else {
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: No flags for this object.")));
|
|
}
|
|
|
|
|
|
//
|
|
// In the results, get the values that match the extension names attribute
|
|
//
|
|
|
|
lpValues = pLDAP->pfnldap_get_values(pld, pMessage,
|
|
(dwFlags & GPO_LIST_FLAG_MACHINE) ? GPO_MACHEXTENSION_NAMES
|
|
: GPO_USEREXTENSION_NAMES );
|
|
if (lpValues) {
|
|
|
|
if ( lstrcmpi( *lpValues, TEXT(" ") ) == 0 ) {
|
|
|
|
//
|
|
// A blank char is also a null property case, because Adsi doesn't commit null strings
|
|
//
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: No client-side extensions for this object.")));
|
|
|
|
} else {
|
|
DWORD dwExtLength = lstrlen(*lpValues)+1;
|
|
pszExtensions = (LPWSTR) LocalAlloc( LPTR, (dwExtLength) * sizeof(TCHAR) );
|
|
if ( pszExtensions == 0 ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: Unable to allocate memory")));
|
|
pLDAP->pfnldap_value_free (lpValues);
|
|
goto Exit;
|
|
|
|
}
|
|
|
|
hr = StringCchCopy( pszExtensions, dwExtLength, *lpValues );
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: Found extensions: %s"), pszExtensions));
|
|
}
|
|
|
|
pLDAP->pfnldap_value_free (lpValues);
|
|
|
|
} else {
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: No client-side extensions for this object.")));
|
|
}
|
|
|
|
|
|
//
|
|
// Log which GPO we found
|
|
//
|
|
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_FOUND_GPO);
|
|
ev.AddArg(pszFriendlyName); ev.AddArg(szGPOName); ev.Report();
|
|
}
|
|
|
|
|
|
//
|
|
// Check the functionalty version number
|
|
//
|
|
|
|
if (dwFunctionalityVersion < 2) {
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: GPO %s was created by an old version of the Group Policy Editor. It will be skipped."), pszFriendlyName));
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_GPO_TOO_OLD);
|
|
ev.AddArg(pszFriendlyName); ev.Report();
|
|
}
|
|
bOldGpoVersion = TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Check if the GPO is disabled
|
|
//
|
|
|
|
if (((dwFlags & GPO_LIST_FLAG_MACHINE) &&
|
|
(dwGPOFlags & GPO_OPTION_DISABLE_MACHINE)) ||
|
|
(!(dwFlags & GPO_LIST_FLAG_MACHINE) &&
|
|
(dwGPOFlags & GPO_OPTION_DISABLE_USER))) {
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: GPO %s is disabled. It will be skipped."), pszFriendlyName));
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_GPO_DISABLED);
|
|
ev.AddArg(pszFriendlyName); ev.Report();
|
|
}
|
|
bDisabled = TRUE;
|
|
}
|
|
|
|
//
|
|
// Check if the version number is 0, if so there isn't any data
|
|
// in the GPO and we can skip it
|
|
//
|
|
|
|
if (dwVersion == 0) {
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: GPO %s doesn't contain any data since the version number is 0. It will be skipped."), pszFriendlyName));
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_GPO_NO_DATA);
|
|
ev.AddArg(pszFriendlyName); ev.Report();
|
|
}
|
|
bNoGpoData = TRUE;
|
|
}
|
|
|
|
//
|
|
// Put the correct container name on the front of the LDAP path
|
|
//
|
|
|
|
DWORD dwTempLength = lstrlen(lpGPOPath) + 20;
|
|
lpTemp = (LPWSTR) LocalAlloc (LPTR, (dwTempLength) * sizeof(TCHAR));
|
|
|
|
if (!lpTemp) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: Failed to allocate memory with %d"), GetLastError()));
|
|
CEvents ev(TRUE, EVENT_OUT_OF_MEMORY);
|
|
ev.AddArgWin32Error(GetLastError()); ev.Report();
|
|
goto Exit;
|
|
}
|
|
|
|
if (dwFlags & GPO_LIST_FLAG_MACHINE) {
|
|
hr = StringCchCopy (lpTemp, dwTempLength, TEXT("LDAP://CN=Machine,"));
|
|
} else {
|
|
hr = StringCchCopy (lpTemp, dwTempLength, TEXT("LDAP://CN=User,"));
|
|
}
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
hr = StringCchCat (lpTemp, dwTempLength, lpPath);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
//
|
|
// Add this GPO to the list
|
|
//
|
|
|
|
if ( bRsopLogging ) {
|
|
bResult = AddGPOToRsopList( ppGpContainerList,
|
|
dwFlags,
|
|
TRUE,
|
|
bAccessGranted,
|
|
bDisabled,
|
|
dwVersion,
|
|
lpTemp,
|
|
pszGPTPath,
|
|
pszFriendlyName,
|
|
szGPOName,
|
|
pSD,
|
|
cbSDLen,
|
|
bFilterAllowed,
|
|
pwszFilterId,
|
|
lpDSObject,
|
|
dwGPOOptions );
|
|
if (!bResult) {
|
|
xe = GetLastError();
|
|
LocalFree(lpTemp);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if ( bProcessGPO && bAccessGranted && !bOldGpoVersion && !bDisabled && !bNoGpoData && bFilterAllowed)
|
|
{
|
|
bResult = AddGPO (lpGPOList, dwFlags, TRUE, bAccessGranted, bDisabled,
|
|
dwGPOOptions, dwVersion, lpTemp,
|
|
pszGPTPath, pszFriendlyName, szGPOName, pszExtensions, pSD, cbSDLen, GPOLink, lpDSObject, 0,
|
|
FALSE, bBlock, bVerbose, bProcessGPO);
|
|
}
|
|
|
|
if (!bResult) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("ProcessGPO: Failed to add GPO <%s> to the list."), pszFriendlyName));
|
|
}
|
|
|
|
LocalFree (lpTemp);
|
|
|
|
Exit:
|
|
|
|
if ( pSD )
|
|
LocalFree( pSD );
|
|
|
|
if ( pszGPTPath )
|
|
LocalFree( pszGPTPath );
|
|
|
|
if ( pszFriendlyName )
|
|
LocalFree( pszFriendlyName );
|
|
|
|
if ( pszExtensions )
|
|
LocalFree( pszExtensions );
|
|
|
|
if ( pwszFilterId )
|
|
LocalFree( pwszFilterId );
|
|
|
|
if (pMessage && bOwnLdapMsg ) {
|
|
pLDAP->pfnldap_msgfree (pMessage);
|
|
}
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("ProcessGPO: ==============================")));
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// SearchDSObject()
|
|
//
|
|
// Purpose: Searches the specified DS object for GPOs and
|
|
// if found, adds them to the list.
|
|
//
|
|
// Parameters: lpDSObject - DS object to search
|
|
// dwFlags - GetGPOList & GP_PLANMODE flags
|
|
// pGPOForcedList - List of forced GPOs
|
|
// pGPONonForcedList - List of non-forced GPOs
|
|
// ppSOMList - List of LSDOUs
|
|
// ppGpContainerList - List of Gp Containers
|
|
// bVerbose - Verbose output
|
|
// GPOLink - GPO link type
|
|
// pld - LDAP info
|
|
// pLDAP - LDAP api
|
|
// bBlock - Pointer to the block flag
|
|
//
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL SearchDSObject (LPTSTR lpDSObject, DWORD dwFlags, HANDLE hToken, PGROUP_POLICY_OBJECT *pGPOForcedList,
|
|
PGROUP_POLICY_OBJECT *pGPONonForcedList,
|
|
LPSCOPEOFMGMT *ppSOMList, LPGPCONTAINER *ppGpContainerList,
|
|
BOOL bVerbose,
|
|
GPO_LINK GPOLink, PLDAP pld, PLDAP_API pLDAP, PLDAPMessage pLDAPMsg,BOOL *bBlock, PRSOPTOKEN pRsopToken )
|
|
{
|
|
PGROUP_POLICY_OBJECT pForced = NULL, pNonForced = NULL, lpGPO;
|
|
LPTSTR *lpValues;
|
|
ULONG ulResult;
|
|
BOOL bResult = FALSE;
|
|
BOOL bOwnLdapMsg = FALSE; // LDAP message owned by us (if true) or caller (if false)
|
|
DWORD dwGPOOptions, dwOptions = 0;
|
|
LPTSTR lpTemp, lpList, lpDSClass;
|
|
BYTE berValue[8];
|
|
LDAPControl SeInfoControl = { LDAP_SERVER_SD_FLAGS_OID_W, { 5, (PCHAR)berValue }, TRUE };
|
|
PLDAPControl ServerControls[] = { &SeInfoControl, NULL };
|
|
|
|
TCHAR szGPLink[] = TEXT("gPLink");
|
|
TCHAR szGPOPath[512];
|
|
TCHAR szGPOOptions[12];
|
|
TCHAR szGPOptions[] = TEXT("gPOptions");
|
|
TCHAR szSDProperty[] = TEXT("nTSecurityDescriptor");
|
|
ULONG i = 0;
|
|
LPTSTR lpFullDSObject = NULL;
|
|
BOOL bFound = FALSE;
|
|
LPTSTR lpAttr[] = { szGPLink,
|
|
szGPOptions,
|
|
// szObjectClass, not needed
|
|
szSDProperty,
|
|
NULL
|
|
};
|
|
SCOPEOFMGMT *pSOM = NULL;
|
|
BOOL bRsopLogging = (ppSOMList != NULL);
|
|
BOOL bAllGPOs = (dwFlags & FLAG_NO_GPO_FILTER) && (dwFlags & GP_PLANMODE);
|
|
XLastError xe;
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Setup the BER encoding for the SD
|
|
//
|
|
|
|
berValue[0] = 0x30;
|
|
berValue[1] = 0x03;
|
|
berValue[2] = 0x02; // denotes an integer
|
|
berValue[3] = 0x01; // denotes size
|
|
berValue[4] = (BYTE)((DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION) & 0xF);
|
|
|
|
|
|
if ( !pRsopToken )
|
|
{
|
|
//
|
|
// if it is not planning mode, don't get the SD
|
|
//
|
|
|
|
lpAttr[2] = NULL;
|
|
ServerControls[0] = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Search for the object
|
|
//
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("SearchDSObject: Searching <%s>"), lpDSObject));
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_SEARCHING);
|
|
ev.AddArg(lpDSObject); ev.Report();
|
|
}
|
|
|
|
if ( bRsopLogging )
|
|
{
|
|
pSOM = AllocSOM( lpDSObject );
|
|
if ( !pSOM ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("SearchDSObject: Unable to allocate memory for SOM object. Leaving. ")));
|
|
goto Exit;
|
|
}
|
|
pSOM->dwType = GPOLink;
|
|
pSOM->bBlocked = *bBlock;
|
|
|
|
}
|
|
|
|
if ( pLDAPMsg == NULL ) {
|
|
|
|
bOwnLdapMsg = TRUE;
|
|
|
|
ulResult = pLDAP->pfnldap_search_ext_s(pld, lpDSObject, LDAP_SCOPE_BASE,
|
|
szDSClassAny, lpAttr, FALSE,
|
|
(PLDAPControl*)ServerControls,
|
|
NULL, NULL, 0, &pLDAPMsg);
|
|
|
|
if (ulResult != LDAP_SUCCESS) {
|
|
|
|
if (ulResult == LDAP_NO_SUCH_ATTRIBUTE) {
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("SearchDSObject: No GPO(s) for this object.")));
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_NO_GPOS); ev.AddArg(lpDSObject); ev.Report();
|
|
}
|
|
bResult = TRUE;
|
|
|
|
} else if (ulResult == LDAP_NO_SUCH_OBJECT) {
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("SearchDSObject: Object not found in DS (this is ok). Leaving. ")));
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_NO_DS_OBJECT);
|
|
ev.AddArg(lpDSObject); ev.Report();
|
|
}
|
|
bResult = TRUE;
|
|
|
|
} else if (ulResult == LDAP_SIZELIMIT_EXCEEDED) {
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(ulResult);
|
|
DebugMsg((DM_WARNING, TEXT("SearchDSObject: Too many linked GPOs in search.") ));
|
|
CEvents ev(TRUE, EVENT_TOO_MANY_GPOS); ev.Report();
|
|
|
|
} else {
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(ulResult);
|
|
DebugMsg((DM_VERBOSE, TEXT("SearchDSObject: Failed to find DS object <%s> due to error %d."),
|
|
lpDSObject, ulResult));
|
|
CEvents ev(TRUE, EVENT_GPLINK_NOT_FOUND);
|
|
ev.AddArg(lpDSObject); ev.AddArgLdapError(ulResult); ev.Report();
|
|
}
|
|
|
|
goto Exit;
|
|
|
|
}
|
|
}
|
|
|
|
if ( bRsopLogging && pRsopToken && !bAllGPOs )
|
|
{
|
|
//
|
|
// In Rsop planning mode, check access to OU
|
|
//
|
|
|
|
BOOL bAccessGranted = FALSE;
|
|
BOOL bOk;
|
|
|
|
bOk = CheckOUAccess(pLDAP,
|
|
pld,
|
|
pLDAPMsg,
|
|
pRsopToken,
|
|
&bAccessGranted );
|
|
|
|
if ( !bOk )
|
|
{
|
|
xe = GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
if ( !bAccessGranted )
|
|
{
|
|
//
|
|
// no access for the user on the OU. Exit
|
|
//
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("SearchDSObject: Access denied in planning mode to SOM <%s>"), lpDSObject));
|
|
|
|
if (pLDAPMsg && bOwnLdapMsg )
|
|
{
|
|
pLDAP->pfnldap_msgfree (pLDAPMsg);
|
|
pLDAPMsg = 0;
|
|
}
|
|
|
|
CEvents ev(TRUE, EVENT_OU_ACCESSDENIED);
|
|
ev.AddArg(lpDSObject); ev.Report();
|
|
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// In the results, get the values that match the gPOptions attribute
|
|
//
|
|
|
|
lpValues = pLDAP->pfnldap_get_values(pld, pLDAPMsg, szGPOptions);
|
|
|
|
if (lpValues && *lpValues) {
|
|
dwOptions = StringToInt (*lpValues);
|
|
pLDAP->pfnldap_value_free (lpValues);
|
|
}
|
|
|
|
|
|
//
|
|
// In the results, get the values that match the gPLink attribute
|
|
//
|
|
|
|
lpValues = pLDAP->pfnldap_get_values(pld, pLDAPMsg, szGPLink);
|
|
|
|
|
|
if (lpValues && *lpValues) {
|
|
|
|
lpList = *lpValues;
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("SearchDSObject: Found GPO(s): <%s>"), lpList));
|
|
DWORD dwFullLength = lstrlen(lpDSObject) + 8;
|
|
lpFullDSObject = (LPWSTR) LocalAlloc (LPTR, (dwFullLength) * sizeof(TCHAR));
|
|
|
|
if (!lpFullDSObject) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("SearchDSObject: Failed to allocate memory for full DS Object path name with %d"),
|
|
GetLastError()));
|
|
pLDAP->pfnldap_value_free (lpValues);
|
|
goto Exit;
|
|
}
|
|
|
|
hr = StringCchCopy (lpFullDSObject, dwFullLength, TEXT("LDAP://"));
|
|
ASSERT(SUCCEEDED(hr));
|
|
hr = StringCchCat (lpFullDSObject, dwFullLength, lpDSObject);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
while (*lpList) {
|
|
DWORD dwLenRemaining;
|
|
|
|
//
|
|
// Pull off the GPO ldap path
|
|
//
|
|
|
|
lpTemp = szGPOPath;
|
|
dwLenRemaining = (ARRAYSIZE(szGPOPath))-1; // (len of array) - (end of string character)
|
|
dwGPOOptions = 0;
|
|
|
|
while (*lpList && (*lpList != TEXT('['))) {
|
|
lpList++;
|
|
}
|
|
|
|
if (!(*lpList)) {
|
|
break;
|
|
}
|
|
|
|
lpList++;
|
|
|
|
while ((dwLenRemaining) && *lpList && (*lpList != TEXT(';'))) {
|
|
*lpTemp++ = *lpList++;
|
|
dwLenRemaining--;
|
|
}
|
|
|
|
if (*lpList != TEXT(';')) {
|
|
break;
|
|
}
|
|
|
|
*lpTemp = TEXT('\0');
|
|
|
|
|
|
lpList++;
|
|
|
|
lpTemp = szGPOOptions;
|
|
dwLenRemaining = (ARRAYSIZE(szGPOOptions))-1; // (len of array) - (end of string character)
|
|
*lpTemp = TEXT('\0');
|
|
|
|
while ((dwLenRemaining) && *lpList && (*lpList != TEXT(']'))) {
|
|
*lpTemp++ = *lpList++;
|
|
dwLenRemaining--;
|
|
}
|
|
|
|
if (*lpList != TEXT(']')) {
|
|
break;
|
|
}
|
|
|
|
|
|
*lpTemp = TEXT('\0');
|
|
lpList++;
|
|
|
|
dwGPOOptions = StringToInt (szGPOOptions);
|
|
|
|
if ( bRsopLogging ) {
|
|
|
|
GPLINK *pGpLink = AllocGpLink( szGPOPath, dwGPOOptions );
|
|
if ( pGpLink == NULL ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("SearchDSObject: Unable to allocate memory for GpLink object. Leaving. ")));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Append GpLink to end of SOM list
|
|
//
|
|
|
|
if ( pSOM->pGpLinkList == NULL ) {
|
|
pSOM->pGpLinkList = pGpLink;
|
|
} else {
|
|
|
|
GPLINK *pTrailPtr = NULL;
|
|
GPLINK *pCurPtr = pSOM->pGpLinkList;
|
|
|
|
while ( pCurPtr != NULL ) {
|
|
pTrailPtr = pCurPtr;
|
|
pCurPtr = pCurPtr->pNext;
|
|
}
|
|
|
|
pTrailPtr->pNext = pGpLink;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Check if this link is disabled
|
|
//
|
|
|
|
BOOL bProcessGPO = TRUE;
|
|
|
|
if ( ( dwGPOOptions & GPO_FLAG_DISABLE ) && !bAllGPOs )
|
|
{
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("SearchDSObject: The link to GPO %s is disabled. It will be skipped for processing."), szGPOPath));
|
|
if (bVerbose)
|
|
{
|
|
CEvents ev(FALSE, EVENT_GPO_LINK_DISABLED);
|
|
ev.AddArg(szGPOPath); ev.Report();
|
|
}
|
|
|
|
bProcessGPO = FALSE;
|
|
}
|
|
|
|
if (bProcessGPO || (dwFlags & GP_PLANMODE)) {
|
|
|
|
|
|
if (!bProcessGPO) {
|
|
DebugMsg((DM_VERBOSE, TEXT("SearchDSObject: The link to GPO %s is disabled. GPO is still being queried. Planning mode."), szGPOPath));
|
|
}
|
|
|
|
if ( !ProcessGPO( szGPOPath,
|
|
dwFlags,
|
|
hToken,
|
|
(dwGPOOptions & GPO_FLAG_FORCE) ? &pForced : &pNonForced,
|
|
ppGpContainerList,
|
|
dwGPOOptions,
|
|
TRUE,
|
|
bVerbose,
|
|
GPOLink,
|
|
lpFullDSObject,
|
|
pld,
|
|
pLDAP,
|
|
0,
|
|
*bBlock,
|
|
pRsopToken,
|
|
0,
|
|
0,
|
|
bProcessGPO ) )
|
|
{
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("SearchDSObject: ProcessGPO failed.")));
|
|
pLDAP->pfnldap_value_free (lpValues);
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
pLDAP->pfnldap_value_free (lpValues);
|
|
|
|
|
|
//
|
|
// Set the block flag now if requested. This way OU's, domains, etc
|
|
// higher in the namespace will have GPOs removed if appropriate
|
|
//
|
|
|
|
if (dwOptions & GPC_BLOCK_POLICY) {
|
|
*bBlock = TRUE;
|
|
|
|
if ( bRsopLogging )
|
|
pSOM->bBlocking = TRUE;
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("SearchDSObject: <%s> has the Block From Above attribute set"), lpDSObject));
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_BLOCK_ENABLED);
|
|
ev.AddArg(lpDSObject); ev.Report();
|
|
}
|
|
}
|
|
|
|
|
|
} else {
|
|
DebugMsg((DM_VERBOSE, TEXT("SearchDSObject: No GPO(s) for this object.")));
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_NO_GPOS); ev.AddArg(lpDSObject); ev.Report();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Merge the temp and real lists together
|
|
// First the non-forced lists
|
|
//
|
|
|
|
if (pNonForced) {
|
|
|
|
lpGPO = pNonForced;
|
|
|
|
while (lpGPO->pNext) {
|
|
lpGPO = lpGPO->pNext;
|
|
}
|
|
|
|
lpGPO->pNext = *pGPONonForcedList;
|
|
if (*pGPONonForcedList) {
|
|
(*pGPONonForcedList)->pPrev = lpGPO;
|
|
}
|
|
|
|
*pGPONonForcedList = pNonForced;
|
|
}
|
|
|
|
//
|
|
// Now the forced lists
|
|
//
|
|
|
|
if (pForced) {
|
|
|
|
lpGPO = *pGPOForcedList;
|
|
|
|
if (lpGPO) {
|
|
while (lpGPO->pNext) {
|
|
lpGPO = lpGPO->pNext;
|
|
}
|
|
|
|
lpGPO->pNext = pForced;
|
|
pForced->pPrev = lpGPO;
|
|
|
|
} else {
|
|
*pGPOForcedList = pForced;
|
|
}
|
|
}
|
|
|
|
bResult = TRUE;
|
|
|
|
Exit:
|
|
if ( !bResult && pSOM != NULL ) {
|
|
FreeSOM( pSOM );
|
|
}
|
|
else {
|
|
if ( bResult && bRsopLogging ) {
|
|
|
|
//
|
|
// Insert SOM at the beginning
|
|
//
|
|
|
|
pSOM->pNext = *ppSOMList;
|
|
*ppSOMList = pSOM;
|
|
}
|
|
}
|
|
|
|
if (lpFullDSObject) {
|
|
LocalFree (lpFullDSObject);
|
|
}
|
|
|
|
if (pLDAPMsg && bOwnLdapMsg ) {
|
|
pLDAP->pfnldap_msgfree (pLDAPMsg);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// AllocDnEntry()
|
|
//
|
|
// Purpose: Allocates a new struct for dn entry
|
|
//
|
|
//
|
|
// Parameters: pwszDN - Distinguished name
|
|
//
|
|
// Return: Pointer if successful
|
|
// NULL if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
DNENTRY * AllocDnEntry( LPTSTR pwszDN )
|
|
{
|
|
DNENTRY *pDnEntry = (DNENTRY *) LocalAlloc (LPTR, sizeof(DNENTRY));
|
|
XLastError xe;
|
|
HRESULT hr = S_OK;
|
|
|
|
if ( pDnEntry == NULL ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("AllocDnEntry: Failed to alloc pDnEntry with 0x%x."),
|
|
GetLastError()));
|
|
return NULL;
|
|
}
|
|
|
|
pDnEntry->pwszDN = (LPTSTR) LocalAlloc (LPTR, (lstrlen(pwszDN) + 1) * sizeof(TCHAR) );
|
|
|
|
if ( pDnEntry->pwszDN == NULL ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("AllocDnEntry: Failed to alloc pwszDN with 0x%x."),
|
|
GetLastError()));
|
|
LocalFree( pDnEntry );
|
|
return NULL;
|
|
}
|
|
|
|
hr = StringCchCopy( pDnEntry->pwszDN, lstrlen(pwszDN) + 1, pwszDN );
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
return pDnEntry;
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// FreeDnEntry()
|
|
//
|
|
// Purpose: Frees dn entry struct
|
|
//
|
|
//*************************************************************
|
|
|
|
void FreeDnEntry( DNENTRY *pDnEntry )
|
|
{
|
|
if ( pDnEntry ) {
|
|
if ( pDnEntry->pwszDN )
|
|
LocalFree( pDnEntry->pwszDN );
|
|
|
|
LocalFree( pDnEntry );
|
|
}
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// AllocLdapQuery()
|
|
//
|
|
// Purpose: Allocates a new struct for ldap query
|
|
//
|
|
//
|
|
// Parameters: pwszDomain - Domain of Gpo
|
|
//
|
|
// Return: Pointer if successful
|
|
// NULL if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
LDAPQUERY * AllocLdapQuery( LPTSTR pwszDomain )
|
|
{
|
|
const INIT_ALLOC_SIZE = 1000;
|
|
LDAPQUERY *pQuery = (LDAPQUERY *) LocalAlloc (LPTR, sizeof(LDAPQUERY));
|
|
XLastError xe;
|
|
HRESULT hr = S_OK;
|
|
|
|
if ( pQuery == NULL ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("AllocLdapQuery: Failed to alloc pQuery with 0x%x."),
|
|
GetLastError()));
|
|
return NULL;
|
|
}
|
|
|
|
pQuery->pwszDomain = (LPTSTR) LocalAlloc (LPTR, (lstrlen(pwszDomain) + 1) * sizeof(TCHAR) );
|
|
|
|
if ( pQuery->pwszDomain == NULL ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("AllocLdapQuery: Failed to alloc pwszDomain with 0x%x."),
|
|
GetLastError()));
|
|
LocalFree( pQuery );
|
|
return NULL;
|
|
}
|
|
|
|
pQuery->pwszFilter = (LPTSTR) LocalAlloc (LPTR, INIT_ALLOC_SIZE );
|
|
|
|
if ( pQuery->pwszFilter == NULL ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("AllocLdapQuery: Failed to alloc pwszFilter with 0x%x."),
|
|
GetLastError()));
|
|
LocalFree( pQuery->pwszDomain );
|
|
LocalFree( pQuery );
|
|
return NULL;
|
|
}
|
|
|
|
hr = StringCchCopy( pQuery->pwszDomain, lstrlen(pwszDomain) + 1, pwszDomain );
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
hr = StringCchCopy( pQuery->pwszFilter, INIT_ALLOC_SIZE, L"(|)" );
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
pQuery->cbLen = 8; // 8 = (lstrlen(L"(|)") + 1) * sizeof(TCHAR)
|
|
pQuery->cbAllocLen = INIT_ALLOC_SIZE;
|
|
|
|
return pQuery;
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// FreeLdapQuery()
|
|
//
|
|
// Purpose: Frees ldap query struct
|
|
//
|
|
//*************************************************************
|
|
|
|
void FreeLdapQuery( PLDAP_API pLDAP, LDAPQUERY *pQuery )
|
|
{
|
|
DNENTRY *pDnEntry = NULL;
|
|
|
|
if ( pQuery ) {
|
|
|
|
if ( pQuery->pwszDomain )
|
|
LocalFree( pQuery->pwszDomain );
|
|
|
|
if ( pQuery->pwszFilter )
|
|
LocalFree( pQuery->pwszFilter );
|
|
|
|
if ( pQuery->pMessage )
|
|
pLDAP->pfnldap_msgfree( pQuery->pMessage );
|
|
|
|
if ( pQuery->pLdapHandle && pQuery->bOwnLdapHandle )
|
|
pLDAP->pfnldap_unbind( pQuery->pLdapHandle );
|
|
|
|
pDnEntry = pQuery->pDnEntry;
|
|
|
|
while ( pDnEntry ) {
|
|
DNENTRY *pTemp = pDnEntry->pNext;
|
|
FreeDnEntry( pDnEntry );
|
|
pDnEntry = pTemp;
|
|
}
|
|
|
|
LocalFree( pQuery );
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// MatchDnWithDeferredItems()
|
|
//
|
|
// Purpose: Matches the dns from ldap query with the deferred items
|
|
//
|
|
// Parameters: pLDAP - LDAP function table pointer
|
|
// ppLdapQuery - LDAP query list
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL MatchDnWithDeferredItems( PLDAP_API pLDAP, LDAPQUERY *pLdapQuery, BOOL bOUProcessing )
|
|
{
|
|
PLDAPMessage pMsg = pLDAP->pfnldap_first_entry( pLdapQuery->pLdapHandle, pLdapQuery->pMessage );
|
|
|
|
while ( pMsg ) {
|
|
|
|
WCHAR *pwszDN = pLDAP->pfnldap_get_dn( pLdapQuery->pLdapHandle, pMsg ); // fixing bug 568263
|
|
|
|
if (!pwszDN)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
DNENTRY *pCurPtr = pLdapQuery->pDnEntry;
|
|
|
|
while ( pCurPtr ) {
|
|
|
|
INT iResult = CompareString ( LOCALE_INVARIANT, NORM_IGNORECASE,
|
|
pwszDN, -1, pCurPtr->pwszDN, -1 );
|
|
if ( iResult == CSTR_EQUAL ) {
|
|
|
|
//
|
|
// Store the pointer to ldap message so that it can be used
|
|
// later to retrieve necessary attributes.
|
|
//
|
|
if ( bOUProcessing )
|
|
pCurPtr->pDeferredOU->pOUMsg = pMsg;
|
|
else {
|
|
LPGPOPROCDATA lpGpoProcData = (LPGPOPROCDATA)pCurPtr->pDeferredGPO->lParam2;
|
|
|
|
pCurPtr->pDeferredGPO->lParam = (LPARAM) pMsg;
|
|
lpGpoProcData->pLdapHandle = pLdapQuery->pLdapHandle;
|
|
}
|
|
|
|
pCurPtr = pCurPtr->pNext;
|
|
|
|
} else if ( iResult == CSTR_LESS_THAN ) {
|
|
|
|
//
|
|
// Since dns are in ascending order,
|
|
// we are done.
|
|
//
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Advance down the list
|
|
//
|
|
|
|
pCurPtr = pCurPtr->pNext;
|
|
|
|
} // final else
|
|
|
|
} // while pcurptr
|
|
|
|
pLDAP->pfnldap_memfree( pwszDN );
|
|
|
|
pMsg = pLDAP->pfnldap_next_entry( pLdapQuery->pLdapHandle, pMsg );
|
|
|
|
} // while pmsg
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
LPWSTR DsQuoteSearchFilter( LPCWSTR );
|
|
|
|
//*************************************************************
|
|
//
|
|
// AddDnToFilter()
|
|
//
|
|
// Purpose: ORs in the new dn to the ldap filter
|
|
//
|
|
// Parameters: ppLdapQuery - LDAP query list
|
|
// pGPO - Deferred GPO
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL AddDnToFilter( LDAPQUERY *pLdapQuery, LPTSTR pwszDN )
|
|
{
|
|
const DN_SIZE = 20; // 20 = # chars in "(dis..=)"
|
|
BOOL bSuccess = FALSE;
|
|
LPWSTR szQuotedDN;
|
|
HRESULT hr = S_OK;
|
|
|
|
szQuotedDN = DsQuoteSearchFilter( pwszDN );
|
|
|
|
if ( ! szQuotedDN )
|
|
{
|
|
DebugMsg((DM_WARNING, TEXT("GetGPOInfo: DsQuoteSearchFilter failed with = <%d>"), GetLastError() ));
|
|
goto AddDnToFilter_ExitAndCleanup;
|
|
}
|
|
|
|
DWORD cbNew = (lstrlen(szQuotedDN) + DN_SIZE) * sizeof(TCHAR); // + 1 is not needed because \0 is already part of filter string
|
|
|
|
DWORD cbSizeRequired = pLdapQuery->cbLen + cbNew;
|
|
|
|
if ( cbSizeRequired >= pLdapQuery->cbAllocLen ) {
|
|
|
|
//
|
|
// Need to grow buffer because of overflow
|
|
//
|
|
|
|
LPTSTR pwszNewFilter = (LPTSTR) LocalAlloc (LPTR, cbSizeRequired * 2);
|
|
|
|
if ( pwszNewFilter == NULL ) {
|
|
DebugMsg((DM_WARNING, TEXT("AddDnToFilter: Unable to allocate new filter string") ));
|
|
goto AddDnToFilter_ExitAndCleanup;
|
|
}
|
|
|
|
hr = StringCchCopy( pwszNewFilter, cbSizeRequired, pLdapQuery->pwszFilter );
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
LocalFree( pLdapQuery->pwszFilter );
|
|
pLdapQuery->pwszFilter = pwszNewFilter;
|
|
|
|
pLdapQuery->cbAllocLen = cbSizeRequired * 2;
|
|
}
|
|
|
|
DmAssert( cbSizeRequired < pLdapQuery->cbAllocLen );
|
|
|
|
//
|
|
// Overwrite last ")" and then append the new dn name term
|
|
//
|
|
|
|
hr = StringCchCopy( &pLdapQuery->pwszFilter[pLdapQuery->cbLen/sizeof(WCHAR) - 2], (pLdapQuery->cbAllocLen - pLdapQuery->cbLen)/sizeof(WCHAR) + 2, L"(distinguishedName=" );
|
|
ASSERT(SUCCEEDED(hr));
|
|
hr = StringCchCat( pLdapQuery->pwszFilter, pLdapQuery->cbAllocLen/sizeof(WCHAR), szQuotedDN );
|
|
ASSERT(SUCCEEDED(hr));
|
|
hr = StringCchCat( pLdapQuery->pwszFilter, pLdapQuery->cbAllocLen/sizeof(WCHAR), L"))" );
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
pLdapQuery->cbLen += cbNew;
|
|
|
|
bSuccess = TRUE;
|
|
|
|
AddDnToFilter_ExitAndCleanup:
|
|
|
|
if ( szQuotedDN )
|
|
{
|
|
LocalFree( szQuotedDN );
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// InsertDN()
|
|
//
|
|
// Purpose: Adds a distinguished name entry to ldap query's
|
|
// names linked list
|
|
//
|
|
// Parameters: ppLdapQuery - LDAP query list
|
|
// pwszDN - DN
|
|
// pDeferredOU - Deferred OU
|
|
// pDeferredGPO - Deferred GPO
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL InsertDN( LDAPQUERY *pLdapQuery, LPTSTR pwszDN,
|
|
DNENTRY *pDeferredOU, PGROUP_POLICY_OBJECT pDeferredGPO )
|
|
{
|
|
DNENTRY *pNewEntry = NULL;
|
|
DNENTRY *pTrailPtr = NULL;
|
|
DNENTRY *pCurPtr = pLdapQuery->pDnEntry;
|
|
XLastError xe;
|
|
|
|
DmAssert( !( pDeferredOU && pDeferredGPO ) );
|
|
|
|
while ( pCurPtr != NULL ) {
|
|
|
|
INT iResult = CompareString ( LOCALE_INVARIANT, NORM_IGNORECASE,
|
|
pwszDN, -1, pCurPtr->pwszDN, -1 );
|
|
|
|
if ( iResult == CSTR_EQUAL || iResult == CSTR_LESS_THAN ) {
|
|
|
|
//
|
|
// Duplicate or since dn's are in ascending order, add new entry
|
|
//
|
|
|
|
pNewEntry = AllocDnEntry( pwszDN );
|
|
if ( pNewEntry == NULL )
|
|
return FALSE;
|
|
|
|
if ( !AddDnToFilter( pLdapQuery, pwszDN ) ) {
|
|
xe = GetLastError();
|
|
FreeDnEntry( pNewEntry );
|
|
return FALSE;
|
|
}
|
|
|
|
if ( pDeferredOU )
|
|
pNewEntry->pDeferredOU = pDeferredOU;
|
|
else
|
|
pNewEntry->pDeferredGPO = pDeferredGPO;
|
|
|
|
pNewEntry->pNext = pCurPtr;
|
|
if ( pTrailPtr == NULL )
|
|
pLdapQuery->pDnEntry = pNewEntry;
|
|
else
|
|
pTrailPtr->pNext = pNewEntry;
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Advance down the list
|
|
//
|
|
|
|
pTrailPtr = pCurPtr;
|
|
pCurPtr = pCurPtr->pNext;
|
|
|
|
}
|
|
|
|
} // while
|
|
|
|
//
|
|
// Null list or end of list case.
|
|
//
|
|
|
|
pNewEntry = AllocDnEntry( pwszDN );
|
|
if ( pNewEntry == NULL ) {
|
|
xe = GetLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !AddDnToFilter( pLdapQuery, pwszDN ) ) {
|
|
xe = GetLastError();
|
|
FreeDnEntry( pNewEntry );
|
|
return FALSE;
|
|
}
|
|
|
|
if ( pDeferredOU )
|
|
pNewEntry->pDeferredOU = pDeferredOU;
|
|
else
|
|
pNewEntry->pDeferredGPO = pDeferredGPO;
|
|
|
|
pNewEntry->pNext = pCurPtr;
|
|
if ( pTrailPtr == NULL )
|
|
pLdapQuery->pDnEntry = pNewEntry;
|
|
else
|
|
pTrailPtr->pNext = pNewEntry;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// AddDN()
|
|
//
|
|
// Purpose: Adds a distinguished name entry to ldap query
|
|
//
|
|
// Parameters: ppLdapQuery - LDAP query list
|
|
// pwszDN - DN name
|
|
// pDeferredOU - Deferred OU
|
|
// pDeferredGPO - Deferred GPO
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL AddDN( PLDAP_API pLDAP, LDAPQUERY **ppLdapQuery,
|
|
LPTSTR pwszDN, DNENTRY *pDeferredOU, PGROUP_POLICY_OBJECT pDeferredGPO )
|
|
{
|
|
LPTSTR pwszDomain = NULL;
|
|
LPTSTR pwszTemp = pwszDN;
|
|
LDAPQUERY *pNewQuery = NULL;
|
|
LDAPQUERY *pTrailPtr = NULL;
|
|
LDAPQUERY *pCurPtr = *ppLdapQuery;
|
|
XLastError xe;
|
|
|
|
DmAssert( !( pDeferredOU && pDeferredGPO ) );
|
|
|
|
//
|
|
// Find the domain to which the GPO belongs
|
|
//
|
|
|
|
if ( pwszTemp == NULL ) {
|
|
DebugMsg((DM_WARNING, TEXT("AddDN: Null pwszDN. Exiting.") ));
|
|
return FALSE;
|
|
}
|
|
|
|
while ( *pwszTemp ) {
|
|
|
|
//
|
|
// The check below needs to be more sophisticated to take care
|
|
// of spaces in names etc.
|
|
//
|
|
|
|
if (CompareString ( LOCALE_INVARIANT, NORM_IGNORECASE,
|
|
pwszTemp, 16, TEXT("cn=configuration"), 16) == CSTR_EQUAL ) {
|
|
DebugMsg((DM_VERBOSE, TEXT("AddDN: DN %s is under cn=configuration container. queueing for rebinding"), pwszDN ));
|
|
pwszDomain = pwszTemp;
|
|
break;
|
|
}
|
|
|
|
if (CompareString ( LOCALE_INVARIANT, NORM_IGNORECASE,
|
|
pwszTemp, 3, TEXT("DC="), 3) == CSTR_EQUAL ) {
|
|
pwszDomain = pwszTemp;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Move to the next chunk of the DN name
|
|
//
|
|
|
|
while ( *pwszTemp && (*pwszTemp != TEXT(',')))
|
|
pwszTemp++;
|
|
|
|
if ( *pwszTemp == TEXT(','))
|
|
pwszTemp++;
|
|
|
|
}
|
|
|
|
if ( pwszDomain == NULL ) {
|
|
xe = ERROR_INVALID_DATA;
|
|
DebugMsg((DM_WARNING, TEXT("AddDN: Domain not found for <%s>. Exiting."), pwszDN ));
|
|
return FALSE;
|
|
}
|
|
|
|
while ( pCurPtr != NULL ) {
|
|
|
|
INT iResult = CompareString ( LOCALE_INVARIANT, NORM_IGNORECASE,
|
|
pwszDomain, -1, pCurPtr->pwszDomain, -1 );
|
|
if ( iResult == CSTR_EQUAL ) {
|
|
|
|
BOOL bOk = InsertDN( pCurPtr, pwszDN, pDeferredOU, pDeferredGPO );
|
|
return bOk;
|
|
|
|
} else if ( iResult == CSTR_LESS_THAN ) {
|
|
|
|
//
|
|
// Since domains are in ascending order,
|
|
// pwszDomain is not in list, so add.
|
|
//
|
|
|
|
pNewQuery = AllocLdapQuery( pwszDomain );
|
|
if ( pNewQuery == NULL ) {
|
|
xe = GetLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !InsertDN( pNewQuery, pwszDN, pDeferredOU, pDeferredGPO ) ) {
|
|
xe = GetLastError();
|
|
FreeLdapQuery( pLDAP, pNewQuery );
|
|
return FALSE;
|
|
}
|
|
|
|
pNewQuery->pNext = pCurPtr;
|
|
if ( pTrailPtr == NULL )
|
|
*ppLdapQuery = pNewQuery;
|
|
else
|
|
pTrailPtr->pNext = pNewQuery;
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Advance down the list
|
|
//
|
|
|
|
pTrailPtr = pCurPtr;
|
|
pCurPtr = pCurPtr->pNext;
|
|
|
|
}
|
|
|
|
} // while
|
|
|
|
//
|
|
// Null list or end of list case.
|
|
//
|
|
|
|
pNewQuery = AllocLdapQuery( pwszDomain );
|
|
|
|
if ( pNewQuery == NULL ) {
|
|
xe = GetLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !InsertDN( pNewQuery, pwszDN, pDeferredOU, pDeferredGPO ) ) {
|
|
xe = GetLastError();
|
|
FreeLdapQuery( pLDAP, pNewQuery );
|
|
return FALSE;
|
|
}
|
|
|
|
pNewQuery->pNext = pCurPtr;
|
|
|
|
if ( pTrailPtr == NULL )
|
|
*ppLdapQuery = pNewQuery;
|
|
else
|
|
pTrailPtr->pNext = pNewQuery;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// EvalList()
|
|
//
|
|
// Purpose: Encapsulates common processing functionality for
|
|
// forced and nonforced lists
|
|
//
|
|
// Parameters: pLDAP - LDAP api
|
|
// dwFlags - GetGPOList flags
|
|
// bVerbose - Verbose flag
|
|
// hToken - User or machine token
|
|
// pDeferredList - List of deferred GPOs
|
|
// ppGPOList - List of evaluated GPOs
|
|
// ppGpContainerList - List of Gp Containers
|
|
// pGpoFilter - Gpo filter
|
|
// pLocator - WMI interfaces
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL EvalList( PLDAP_API pLDAP,
|
|
DWORD dwFlags,
|
|
HANDLE hToken,
|
|
BOOL bVerbose,
|
|
PGROUP_POLICY_OBJECT pDeferredList,
|
|
PGROUP_POLICY_OBJECT *ppGPOList,
|
|
LPGPCONTAINER *ppGpContainerList,
|
|
PRSOPTOKEN pRsopToken,
|
|
CGpoFilter *pGpoFilter,
|
|
CLocator *pLocator )
|
|
{
|
|
PGROUP_POLICY_OBJECT pGPOTemp = pDeferredList;
|
|
|
|
while ( pGPOTemp ) {
|
|
|
|
PLDAPMessage pGPOMsg = (PLDAPMessage) pGPOTemp->lParam;
|
|
|
|
if ( pGPOMsg == NULL ) {
|
|
DebugMsg((DM_VERBOSE, TEXT("EvalList: Object <%s> cannot be accessed"),
|
|
pGPOTemp->lpDSPath ));
|
|
|
|
if (dwFlags & GP_PLANMODE) {
|
|
CEvents ev(TRUE, EVENT_OBJECT_NOT_FOUND_PLANNING);
|
|
ev.AddArg(pGPOTemp->lpDSPath); ev.Report();
|
|
}
|
|
else {
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_OBJECT_NOT_FOUND);
|
|
ev.AddArg(pGPOTemp->lpDSPath); ev.AddArg((DWORD)0); ev.Report();
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
DmAssert( pGPOTemp->lParam2 != NULL );
|
|
DmAssert( ((LPGPOPROCDATA)(pGPOTemp->lParam2))->pLdapHandle != NULL );
|
|
|
|
if ( !ProcessGPO( pGPOTemp->lpDSPath,
|
|
dwFlags,
|
|
hToken,
|
|
ppGPOList,
|
|
ppGpContainerList,
|
|
pGPOTemp->dwOptions,
|
|
FALSE,
|
|
bVerbose,
|
|
pGPOTemp->GPOLink,
|
|
pGPOTemp->lpLink,
|
|
((LPGPOPROCDATA)(pGPOTemp->lParam2))->pLdapHandle,
|
|
pLDAP,
|
|
pGPOMsg,
|
|
FALSE,
|
|
pRsopToken,
|
|
pGpoFilter,
|
|
pLocator,
|
|
((LPGPOPROCDATA)(pGPOTemp->lParam2))->bProcessGPO ) )
|
|
{
|
|
DebugMsg((DM_WARNING, TEXT("EvalList: ProcessGPO failed") ));
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
pGPOTemp = pGPOTemp->pNext;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// EvaluateDeferredGPOs()
|
|
//
|
|
// Purpose: Uses a single ldap query to evaluate deferred
|
|
// GPO lists.
|
|
//
|
|
// Parameters: pldBound - Bound LDAP handle
|
|
// pLDAP - LDAP api
|
|
// pwszDomainBound - Domain already bound to
|
|
// dwFlags - GetGPOList flags
|
|
// hToken - User or machine token
|
|
// pDeferredForcedList - List of deferred forced GPOs
|
|
// pDeferredNonForcedList - List of deferred non-forced GPOs
|
|
// pGPOForcedList - List of forced GPOs
|
|
// pGPONonForcedList - List of non-forced GPOs
|
|
// ppGpContainerList - List of Gp Containers
|
|
// pGpoFilter - Gpo filter
|
|
// pLocator - WMI interfaces
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL EvaluateDeferredGPOs (PLDAP pldBound,
|
|
PLDAP_API pLDAP,
|
|
LPTSTR pwszDomainBound,
|
|
DWORD dwFlags,
|
|
HANDLE hToken,
|
|
BOOL bVerbose,
|
|
PGROUP_POLICY_OBJECT pDeferredForcedList,
|
|
PGROUP_POLICY_OBJECT pDeferredNonForcedList,
|
|
PGROUP_POLICY_OBJECT *ppForcedList,
|
|
PGROUP_POLICY_OBJECT *ppNonForcedList,
|
|
LPGPCONTAINER *ppGpContainerList,
|
|
PRSOPTOKEN pRsopToken,
|
|
CGpoFilter *pGpoFilter,
|
|
CLocator *pLocator )
|
|
{
|
|
ULONG ulResult;
|
|
BOOL bResult = FALSE;
|
|
BYTE berValue[8];
|
|
LDAPControl SeInfoControl = { LDAP_SERVER_SD_FLAGS_OID_W, { 5, (PCHAR)berValue }, TRUE };
|
|
LDAPControl referralControl = { LDAP_SERVER_DOMAIN_SCOPE_OID_W, { 0, NULL}, TRUE };
|
|
PLDAPControl ServerControls[] = { &SeInfoControl, &referralControl, NULL };
|
|
TCHAR szSDProperty[] = TEXT("nTSecurityDescriptor");
|
|
TCHAR szCommonName[] = TEXT("cn");
|
|
TCHAR szDisplayName[] = TEXT("displayName");
|
|
TCHAR szFileSysPath[] = TEXT("gPCFileSysPath");
|
|
TCHAR szVersion[] = TEXT("versionNumber");
|
|
TCHAR szFunctionalityVersion[] = GPO_FUNCTIONALITY_VERSION;
|
|
TCHAR szFlags[] = TEXT("flags");
|
|
TCHAR szWmiFilter[] = TEXT("gPCWQLFilter");
|
|
|
|
PWSTR rgAttribs[12] = {szSDProperty,
|
|
szFileSysPath,
|
|
szCommonName,
|
|
szDisplayName,
|
|
szVersion,
|
|
szFunctionalityVersion,
|
|
szFlags,
|
|
GPO_MACHEXTENSION_NAMES,
|
|
GPO_USEREXTENSION_NAMES,
|
|
szObjectClass,
|
|
szWmiFilter,
|
|
NULL };
|
|
PGROUP_POLICY_OBJECT pGPOTemp = pDeferredForcedList;
|
|
LDAPQUERY *pLdapQuery = NULL, *pQuery = NULL;
|
|
VOID *pData;
|
|
PDS_API pdsApi;
|
|
BOOL bRsopPlanningMode = (pRsopToken != 0);
|
|
BOOL bConfigContainer = FALSE;
|
|
|
|
*ppForcedList = NULL;
|
|
*ppNonForcedList = NULL;
|
|
XLastError xe;
|
|
|
|
if ( pDeferredForcedList == NULL && pDeferredNonForcedList == NULL )
|
|
return TRUE;
|
|
|
|
//
|
|
// Demand load ntdsapi.dll
|
|
//
|
|
|
|
pdsApi = LoadDSApi();
|
|
|
|
if ( pdsApi == 0 ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("EvaluateDeferredGpos: Failed to load ntdsapi.dll")));
|
|
goto Exit;
|
|
}
|
|
|
|
while ( pGPOTemp ) {
|
|
|
|
if ( !AddDN( pLDAP, &pLdapQuery, pGPOTemp->lpDSPath, NULL, pGPOTemp ) ) {
|
|
xe = GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
pGPOTemp = pGPOTemp->pNext;
|
|
|
|
}
|
|
|
|
pGPOTemp = pDeferredNonForcedList;
|
|
while ( pGPOTemp ) {
|
|
|
|
if ( !AddDN( pLDAP, &pLdapQuery, pGPOTemp->lpDSPath, NULL, pGPOTemp ) ) {
|
|
xe = GetLastError();
|
|
goto Exit;
|
|
}
|
|
pGPOTemp = pGPOTemp->pNext;
|
|
|
|
}
|
|
|
|
//
|
|
// Setup the BER encoding
|
|
//
|
|
|
|
berValue[0] = 0x30;
|
|
berValue[1] = 0x03;
|
|
berValue[2] = 0x02; // denotes an integer
|
|
berValue[3] = 0x01; // denotes size
|
|
berValue[4] = (BYTE)((DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION) & 0xF);
|
|
|
|
pQuery = pLdapQuery;
|
|
while ( pQuery ) {
|
|
|
|
//
|
|
// The check below needs to be more sophisticated to take care
|
|
// of spaces in names etc.
|
|
//
|
|
// It is assumed that the configuration
|
|
// container would be common across the whole forest and will
|
|
// not need a new bind..
|
|
//
|
|
|
|
if (CompareString ( LOCALE_INVARIANT, NORM_IGNORECASE,
|
|
pQuery->pwszDomain, 16, TEXT("cn=configuration"), 16) == CSTR_EQUAL ) {
|
|
DebugMsg((DM_VERBOSE, TEXT("EvaluateDeferredGPOs: DN %s is under cn=configuration container"), pQuery->pwszDomain ));
|
|
bConfigContainer = TRUE;
|
|
}
|
|
else
|
|
bConfigContainer = FALSE;
|
|
|
|
|
|
//
|
|
// Check if this is a cross-domain Gpo and hence needs a new bind
|
|
//
|
|
|
|
WCHAR *pDomainString[1];
|
|
PDS_NAME_RESULT pNameResult = NULL;
|
|
PLDAP pLdapHandle = NULL;
|
|
|
|
if (!bConfigContainer)
|
|
pDomainString[0] = pQuery->pwszDomain;
|
|
else {
|
|
DebugMsg((DM_VERBOSE, TEXT("EvaluateDeferredGPOs: The GPO is under the config container. Querying seperately\n")));
|
|
|
|
//
|
|
// This is a configuration container and we have to figure
|
|
// out the domain name still..
|
|
//
|
|
|
|
LPTSTR pwszTemp = pQuery->pwszDomain;
|
|
|
|
pDomainString[0] = NULL;
|
|
|
|
while ( *pwszTemp ) {
|
|
|
|
if (CompareString ( LOCALE_INVARIANT, NORM_IGNORECASE,
|
|
pwszTemp, 3, TEXT("DC="), 3) == CSTR_EQUAL ) {
|
|
pDomainString[0] = pwszTemp;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Move to the next chunk of the DN name
|
|
//
|
|
|
|
while ( *pwszTemp && (*pwszTemp != TEXT(',')))
|
|
pwszTemp++;
|
|
|
|
if ( *pwszTemp == TEXT(','))
|
|
pwszTemp++;
|
|
|
|
}
|
|
|
|
if ( pDomainString[0] == NULL ) {
|
|
xe = ERROR_INVALID_DATA;
|
|
DebugMsg((DM_WARNING, TEXT("EvaluateDeferredGPOs: Domain not found for <%s>. Exiting."), pQuery->pwszDomain ));
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
ulResult = pdsApi->pfnDsCrackNames( (HANDLE) -1,
|
|
DS_NAME_FLAG_SYNTACTICAL_ONLY,
|
|
DS_FQDN_1779_NAME,
|
|
DS_CANONICAL_NAME,
|
|
1,
|
|
pDomainString,
|
|
&pNameResult );
|
|
|
|
if ( ulResult != ERROR_SUCCESS
|
|
|| pNameResult->cItems == 0
|
|
|| pNameResult->rItems[0].status != ERROR_SUCCESS
|
|
|| pNameResult->rItems[0].pDomain == NULL ) {
|
|
|
|
xe = ulResult;
|
|
DebugMsg((DM_VERBOSE, TEXT("EvaluateDeferredGPOs: DsCrackNames failed with 0x%x."), ulResult ));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Optimize same domain Gpo queries by not doing an unnecessary bind
|
|
//
|
|
|
|
pQuery->pLdapHandle = pldBound;
|
|
|
|
if (CompareString (LOCALE_INVARIANT, NORM_IGNORECASE,
|
|
pwszDomainBound, -1, pNameResult->rItems[0].pDomain, -1) != CSTR_EQUAL) {
|
|
|
|
//
|
|
// Cross-domain Gpo query and so need to bind to new domain
|
|
//
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("EvaluateDeferredGPOs: Doing an ldap bind to cross-domain <%s>"),
|
|
pNameResult->rItems[0].pDomain));
|
|
|
|
pLdapHandle = pLDAP->pfnldap_init( pNameResult->rItems[0].pDomain, LDAP_PORT);
|
|
|
|
if (!pLdapHandle) {
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(pLDAP->pfnLdapGetLastError());
|
|
|
|
DebugMsg((DM_WARNING, TEXT("EvaluateDeferredGPOs: ldap_init for <%s> failed with = 0x%x or %d"),
|
|
pNameResult->rItems[0].pDomain, pLDAP->pfnLdapGetLastError(), GetLastError()));
|
|
CEvents ev(TRUE, EVENT_FAILED_DS_INIT);
|
|
ev.AddArg(pNameResult->rItems[0].pDomain); ev.AddArgLdapError(pLDAP->pfnLdapGetLastError()); ev.Report();
|
|
|
|
pdsApi->pfnDsFreeNameResult( pNameResult );
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Turn on Packet integrity flag
|
|
//
|
|
|
|
pData = (VOID *) LDAP_OPT_ON;
|
|
ulResult = pLDAP->pfnldap_set_option(pLdapHandle, LDAP_OPT_SIGN, &pData);
|
|
|
|
if (ulResult != LDAP_SUCCESS) {
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(ulResult);
|
|
DebugMsg((DM_WARNING, TEXT("EvaluateDeferredGPOs: Failed to turn on LDAP_OPT_SIGN with %d"), ulResult));
|
|
pdsApi->pfnDsFreeNameResult( pNameResult );
|
|
pLDAP->pfnldap_unbind(pLdapHandle);
|
|
pLdapHandle = 0;
|
|
goto Exit;
|
|
}
|
|
|
|
ulResult = pLDAP->pfnldap_connect(pLdapHandle, 0);
|
|
|
|
if (ulResult != LDAP_SUCCESS) {
|
|
CEvents ev(TRUE, EVENT_FAILED_DS_CONNECT);
|
|
ev.AddArg(pNameResult->rItems[0].pDomain); ev.AddArgLdapError(ulResult); ev.Report();
|
|
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(ulResult);
|
|
DebugMsg((DM_WARNING, TEXT("EvaluateDeferredGPOs: Failed to connect with %d"), ulResult));
|
|
pdsApi->pfnDsFreeNameResult( pNameResult );
|
|
pLDAP->pfnldap_unbind(pLdapHandle);
|
|
pLdapHandle = 0;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Transfer ownerhip of ldap handle to pQuery struct
|
|
//
|
|
|
|
pQuery->pLdapHandle = pLdapHandle;
|
|
pQuery->bOwnLdapHandle = TRUE;
|
|
|
|
if ( !bRsopPlanningMode && (dwFlags & GPO_LIST_FLAG_MACHINE) ) {
|
|
|
|
//
|
|
// For machine policies specifically ask for Kerberos as the only authentication
|
|
// mechanism. Otherwise if Kerberos were to fail for some reason, then NTLM is used
|
|
// and localsystem context has no real credentials, which means that we won't get
|
|
// any GPOs back.
|
|
//
|
|
|
|
SEC_WINNT_AUTH_IDENTITY_EXW secIdentity;
|
|
|
|
secIdentity.Version = SEC_WINNT_AUTH_IDENTITY_VERSION;
|
|
secIdentity.Length = sizeof(SEC_WINNT_AUTH_IDENTITY_EXW);
|
|
secIdentity.User = 0;
|
|
secIdentity.UserLength = 0;
|
|
secIdentity.Domain = 0;
|
|
secIdentity.DomainLength = 0;
|
|
secIdentity.Password = 0;
|
|
secIdentity.PasswordLength = 0;
|
|
secIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
|
|
secIdentity.PackageList = wszKerberos;
|
|
secIdentity.PackageListLength = lstrlen( wszKerberos );
|
|
|
|
ulResult = pLDAP->pfnldap_bind_s (pLdapHandle, NULL, (WCHAR *)&secIdentity, LDAP_AUTH_SSPI);
|
|
|
|
} else
|
|
ulResult = pLDAP->pfnldap_bind_s (pLdapHandle, NULL, NULL, LDAP_AUTH_SSPI);
|
|
|
|
if (ulResult != LDAP_SUCCESS) {
|
|
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(ulResult);
|
|
DebugMsg((DM_WARNING, TEXT("EvaluateDeferredGPOs: ldap_bind_s failed with = <%d>"),
|
|
ulResult));
|
|
CEvents ev(TRUE, EVENT_FAILED_DS_BIND);
|
|
ev.AddArg(pNameResult->rItems[0].pDomain); ev.AddArgLdapError(ulResult); ev.Report();
|
|
|
|
pdsApi->pfnDsFreeNameResult( pNameResult );
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("EvaluateDeferredGPOs: Bind sucessful")));
|
|
|
|
}
|
|
|
|
pdsApi->pfnDsFreeNameResult( pNameResult );
|
|
|
|
//
|
|
// Turn referrals off because this is a single domain call
|
|
//
|
|
|
|
pData = (VOID *) LDAP_OPT_OFF;
|
|
ulResult = pLDAP->pfnldap_set_option( pQuery->pLdapHandle, LDAP_OPT_REFERRALS, &pData );
|
|
if ( ulResult != LDAP_SUCCESS )
|
|
{
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(ulResult);
|
|
DebugMsg((DM_WARNING, TEXT("EvalauteDeferredGPOs: Failed to turn off referrals with error %d"), ulResult));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Search for GPOs
|
|
//
|
|
|
|
//
|
|
// restrict the search to just the policies container
|
|
//
|
|
|
|
DmAssert( pQuery->pwszDomain != NULL && pQuery->pwszFilter != NULL );
|
|
|
|
XPtrLF<WCHAR> xszGPOSearchBase;
|
|
DWORD cChLen;
|
|
|
|
cChLen = (1+lstrlen(pQuery->pwszDomain)+lstrlen(DOMAIN_GPO_LOCATION_FMT));
|
|
|
|
xszGPOSearchBase = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*cChLen);
|
|
|
|
if (!xszGPOSearchBase)
|
|
{
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("EvalauteDeferredGPOs: Failed to allocate memory for GPO base search with %d"), GetLastError()));
|
|
goto Exit;
|
|
}
|
|
|
|
HRESULT hr;
|
|
hr = StringCchPrintf(xszGPOSearchBase, cChLen,
|
|
DOMAIN_GPO_LOCATION_FMT,
|
|
pQuery->pwszDomain);
|
|
|
|
DmAssert(SUCCEEDED(hr));
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("EvaluateDeferredGPOs: Searching for GPOs in %s"), (LPWSTR)xszGPOSearchBase));
|
|
|
|
ulResult = pLDAP->pfnldap_search_ext_s(pQuery->pLdapHandle,
|
|
(LPWSTR)xszGPOSearchBase,
|
|
LDAP_SCOPE_SUBTREE,
|
|
pQuery->pwszFilter, rgAttribs, 0,
|
|
(PLDAPControl*)ServerControls,
|
|
NULL, NULL, 0x10000, &pQuery->pMessage);
|
|
|
|
//
|
|
// If the search fails, store the error code and return
|
|
//
|
|
|
|
if (ulResult != LDAP_SUCCESS) {
|
|
|
|
if (ulResult == LDAP_NO_SUCH_ATTRIBUTE) {
|
|
DebugMsg((DM_VERBOSE, TEXT("EvaluateDeferredGPOs: All objects can not be accessed.")));
|
|
|
|
if (dwFlags & GP_PLANMODE) {
|
|
CEvents ev(TRUE, EVENT_NO_GPOS2_PLANNING); ev.Report();
|
|
}
|
|
else {
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_NO_GPOS2); ev.Report();
|
|
}
|
|
}
|
|
bResult = TRUE;
|
|
|
|
} else if (ulResult == LDAP_NO_SUCH_OBJECT) {
|
|
DebugMsg((DM_VERBOSE, TEXT("EvalateDeferredGPOs: Objects do not exist.") ));
|
|
|
|
if (dwFlags & GP_PLANMODE) {
|
|
// Same error or different
|
|
CEvents ev(TRUE, EVENT_NO_GPOS2_PLANNING); ev.Report();
|
|
}
|
|
else {
|
|
if (bVerbose) {
|
|
CEvents ev(FALSE, EVENT_NO_GPOS2); ev.Report();
|
|
}
|
|
}
|
|
|
|
bResult = TRUE;
|
|
|
|
} else if (ulResult == LDAP_SIZELIMIT_EXCEEDED) {
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(ulResult);
|
|
DebugMsg((DM_WARNING, TEXT("EvalateDeferredGPOs: Too many GPOs in search.") ));
|
|
CEvents ev(TRUE, EVENT_TOO_MANY_GPOS); ev.Report();
|
|
|
|
} else {
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(ulResult);
|
|
DebugMsg((DM_WARNING, TEXT("EvaluteDeferredGPOs: Failed to search with error 0x%x"), ulResult));
|
|
CEvents ev(TRUE, EVENT_FAILED_GPO_SEARCH);
|
|
ev.AddArgLdapError(ulResult); ev.Report();
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// If the search succeeds, but the message is empty,
|
|
// store the error code and return
|
|
//
|
|
|
|
if ( pQuery->pMessage == NULL ) {
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(pQuery->pLdapHandle->ld_errno);
|
|
DebugMsg((DM_WARNING, TEXT("EvaluateDeferredGPOs: Search returned an empty message structure. Error = 0x%x"),
|
|
pQuery->pLdapHandle->ld_errno));
|
|
goto Exit;
|
|
}
|
|
|
|
if ( !MatchDnWithDeferredItems( pLDAP, pQuery, FALSE ) ) {
|
|
xe = GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
pQuery = pQuery->pNext;
|
|
|
|
} // while
|
|
|
|
if ( !EvalList( pLDAP, dwFlags, hToken, bVerbose,
|
|
pDeferredForcedList, ppForcedList, ppGpContainerList, pRsopToken, pGpoFilter, pLocator ) ) {
|
|
xe = GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
if ( !EvalList( pLDAP, dwFlags, hToken, bVerbose,
|
|
pDeferredNonForcedList, ppNonForcedList, ppGpContainerList, pRsopToken, pGpoFilter, pLocator ) ) {
|
|
xe = GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
bResult = TRUE;
|
|
|
|
Exit:
|
|
|
|
//
|
|
// Free all resources except for ppForcedList, ppNonForcedList
|
|
// which are owned by caller.
|
|
//
|
|
|
|
while ( pLdapQuery ) {
|
|
pQuery = pLdapQuery->pNext;
|
|
FreeLdapQuery( pLDAP, pLdapQuery );
|
|
pLdapQuery = pQuery;
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// AddOU()
|
|
//
|
|
// Purpose: Appends an OU or domain to deferred list.
|
|
//
|
|
// Parameters: ppOUList - OU list to append to
|
|
// pwszOU - OU name
|
|
// gpoLink - Type of Gpo
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL AddOU( DNENTRY **ppOUList, LPTSTR pwszOU, GPO_LINK gpoLink )
|
|
{
|
|
DNENTRY *pOUTemp = *ppOUList;
|
|
DNENTRY *pOULast = NULL;
|
|
|
|
DNENTRY *pOUNew = AllocDnEntry( pwszOU );
|
|
if ( pOUNew == NULL ) {
|
|
return FALSE;
|
|
}
|
|
|
|
pOUNew->gpoLink = gpoLink;
|
|
|
|
while ( pOUTemp ) {
|
|
pOULast = pOUTemp;
|
|
pOUTemp = pOUTemp->pNext;
|
|
}
|
|
|
|
if ( pOULast )
|
|
pOULast->pNext = pOUNew;
|
|
else
|
|
*ppOUList = pOUNew;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// EvaluateDeferredOUs()
|
|
//
|
|
// Purpose: Uses a single Ldap query to evaluate all OUs
|
|
//
|
|
// Parameters: ppOUList - OU list to append to
|
|
// dwFlags - GetGPOList flags
|
|
// pGPOForcedList - List of forced GPOs
|
|
// pGPONonForcedList - List of non-forced GPOs
|
|
// ppSOMList - List of LSDOUs
|
|
// ppGpContainerList - List of Gp Containers
|
|
// bVerbose - Verbose output
|
|
// pld - LDAP info
|
|
// pLDAP - LDAP api
|
|
// pLDAPMsg - LDAP message
|
|
// bBlock - Pointer to the block flag
|
|
// hToken - User / machine token
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL EvaluateDeferredOUs( DNENTRY *pOUList,
|
|
DWORD dwFlags,
|
|
HANDLE hToken,
|
|
PGROUP_POLICY_OBJECT *ppDeferredForcedList,
|
|
PGROUP_POLICY_OBJECT *ppDeferredNonForcedList,
|
|
LPSCOPEOFMGMT *ppSOMList,
|
|
LPGPCONTAINER *ppGpContainerList,
|
|
BOOL bVerbose,
|
|
PLDAP pld,
|
|
PLDAP_API pLDAP,
|
|
BOOL *pbBlock,
|
|
PRSOPTOKEN pRsopToken)
|
|
{
|
|
ULONG ulResult;
|
|
BOOL bResult = FALSE;
|
|
LDAPQUERY *pLdapQuery = NULL;
|
|
BYTE berValue[8];
|
|
LDAPControl SeInfoControl = { LDAP_SERVER_SD_FLAGS_OID_W, { 5, (PCHAR)berValue }, TRUE };
|
|
PLDAPControl ServerControls[] = { &SeInfoControl, NULL };
|
|
|
|
TCHAR szGPLink[] = TEXT("gPLink");
|
|
TCHAR szGPOptions[] = TEXT("gPOptions");
|
|
TCHAR szSDProperty[] = TEXT("nTSecurityDescriptor");
|
|
LPTSTR lpAttr[] = { szGPLink,
|
|
szGPOptions,
|
|
szSDProperty,
|
|
NULL
|
|
};
|
|
DNENTRY *pOUTemp = pOUList;
|
|
VOID *pData;
|
|
XLastError xe;
|
|
|
|
|
|
//
|
|
// Setup the BER encoding for the SD
|
|
//
|
|
|
|
berValue[0] = 0x30;
|
|
berValue[1] = 0x03;
|
|
berValue[2] = 0x02; // denotes an integer
|
|
berValue[3] = 0x01; // denotes size
|
|
berValue[4] = (BYTE)((DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION) & 0xF);
|
|
|
|
|
|
if ( !pRsopToken )
|
|
{
|
|
//
|
|
// if it is not planning mode, don't get the SD
|
|
//
|
|
|
|
lpAttr[2] = NULL;
|
|
ServerControls[0] = NULL;
|
|
}
|
|
|
|
|
|
if ( pOUTemp == NULL )
|
|
return TRUE;
|
|
|
|
while ( pOUTemp ) {
|
|
if ( !AddDN( pLDAP, &pLdapQuery, pOUTemp->pwszDN, pOUTemp, NULL ) ) {
|
|
xe = GetLastError();
|
|
goto Exit;
|
|
}
|
|
pOUTemp = pOUTemp->pNext;
|
|
}
|
|
|
|
pLdapQuery->pLdapHandle = pld;
|
|
|
|
//
|
|
// Turn referrals off because this is a single domain call
|
|
//
|
|
|
|
if ( !pRsopToken )
|
|
{
|
|
pData = (VOID *) LDAP_OPT_OFF;
|
|
ulResult = pLDAP->pfnldap_set_option( pLdapQuery->pLdapHandle, LDAP_OPT_REFERRALS, &pData );
|
|
if ( ulResult != LDAP_SUCCESS ) {
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(ulResult);
|
|
DebugMsg((DM_WARNING, TEXT("EvaluteDeferredOUs: Failed to turn off referrals with error %d"), ulResult));
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
|
|
ulResult = pLDAP->pfnldap_search_ext_s(pld, pLdapQuery->pwszDomain, LDAP_SCOPE_SUBTREE,
|
|
pLdapQuery->pwszFilter, lpAttr, FALSE,
|
|
(PLDAPControl*)ServerControls,
|
|
NULL, NULL, 0, &pLdapQuery->pMessage);
|
|
|
|
|
|
//
|
|
// If the search fails, store the error code and return
|
|
//
|
|
|
|
if (ulResult != LDAP_SUCCESS) {
|
|
|
|
if (ulResult == LDAP_NO_SUCH_ATTRIBUTE) {
|
|
DebugMsg((DM_VERBOSE, TEXT("EvaluateDeferredOUs: All objects can not be accessed.")));
|
|
bResult = TRUE;
|
|
|
|
} else if (ulResult == LDAP_NO_SUCH_OBJECT) {
|
|
DebugMsg((DM_VERBOSE, TEXT("EvalateDeferredOUs: Objects do not exist.") ));
|
|
bResult = TRUE;
|
|
|
|
} else if (ulResult == LDAP_SIZELIMIT_EXCEEDED) {
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(ulResult);
|
|
DebugMsg((DM_WARNING, TEXT("EvalateDeferredOUs: Too many linked GPOs in search.") ));
|
|
CEvents ev(TRUE, EVENT_TOO_MANY_GPOS); ev.Report();
|
|
|
|
} else {
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(ulResult);
|
|
DebugMsg((DM_WARNING, TEXT("EvaluateDeferredOUs: Failed to search with error %d"), ulResult));
|
|
CEvents ev(TRUE, EVENT_FAILED_OU_SEARCH);
|
|
ev.AddArg(ulResult); ev.Report();
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// If the search succeeds, but the message is empty,
|
|
// store the error code and return
|
|
//
|
|
|
|
if ( pLdapQuery->pMessage == NULL ) {
|
|
xe = pLDAP->pfnLdapMapErrorToWin32(pld->ld_errno);
|
|
DebugMsg((DM_WARNING, TEXT("EvaluateDeferredOUs: Search returned an empty message structure. Error = %d"),
|
|
pld->ld_errno));
|
|
goto Exit;
|
|
}
|
|
|
|
if ( !MatchDnWithDeferredItems( pLDAP, pLdapQuery, TRUE ) ) {
|
|
xe = GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Evaluate the OU list
|
|
//
|
|
|
|
pOUTemp = pOUList;
|
|
|
|
while ( pOUTemp ) {
|
|
|
|
PLDAPMessage pOUMsg = pOUTemp->pOUMsg;
|
|
|
|
if ( pOUMsg == NULL ) {
|
|
xe = ERROR_INVALID_DATA;
|
|
DebugMsg((DM_WARNING, TEXT("EvaluateDeferredOUs: Object <%s> cannot be accessed"),
|
|
pOUTemp->pwszDN ));
|
|
|
|
CEvents ev(TRUE, EVENT_OU_NOTFOUND);
|
|
ev.AddArg(pOUTemp->pwszDN); ev.Report();
|
|
|
|
goto Exit;
|
|
|
|
} else {
|
|
if ( !SearchDSObject( pOUTemp->pwszDN, dwFlags, hToken, ppDeferredForcedList, ppDeferredNonForcedList,
|
|
ppSOMList, ppGpContainerList,
|
|
bVerbose, pOUTemp->gpoLink, pld, pLDAP, pOUMsg, pbBlock, pRsopToken)) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("EvaluateDeferredOUs: SearchDSObject failed") ));
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
pOUTemp = pOUTemp->pNext;
|
|
|
|
}
|
|
|
|
bResult = TRUE;
|
|
|
|
Exit:
|
|
|
|
while ( pLdapQuery ) {
|
|
LDAPQUERY *pQuery = pLdapQuery->pNext;
|
|
FreeLdapQuery( pLDAP, pLdapQuery );
|
|
pLdapQuery = pQuery;
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// GetMachineDomainDS()
|
|
//
|
|
// Purpose: Obtain the machine domain DS
|
|
//
|
|
// Parameters: pNetApi32 - netapi32.dll
|
|
// pLdapApi - wldap32.dll
|
|
//
|
|
// Return: valid PLDAP if successful
|
|
// 0 if an error occurs
|
|
//
|
|
//*************************************************************
|
|
PLDAP
|
|
GetMachineDomainDS( PNETAPI32_API pNetApi32, PLDAP_API pLdapApi )
|
|
{
|
|
PLDAP pld = 0;
|
|
|
|
DWORD dwResult = 0;
|
|
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pDsInfo = NULL;
|
|
ULONG ulResult;
|
|
VOID *pData;
|
|
XLastError xe;
|
|
|
|
//
|
|
// get the machine domain name
|
|
//
|
|
|
|
dwResult = pNetApi32->pfnDsRoleGetPrimaryDomainInformation( NULL,
|
|
DsRolePrimaryDomainInfoBasic,
|
|
(PBYTE *)&pDsInfo );
|
|
if ( dwResult == ERROR_SUCCESS )
|
|
{
|
|
SEC_WINNT_AUTH_IDENTITY_EXW secIdentity;
|
|
|
|
pld = pLdapApi->pfnldap_init( pDsInfo->DomainNameDns, LDAP_PORT );
|
|
|
|
|
|
if (!pld) {
|
|
xe = pLdapApi->pfnLdapMapErrorToWin32(pLdapApi->pfnLdapGetLastError());
|
|
DebugMsg((DM_WARNING, TEXT("GetMachineDomainDS: ldap_open for <%s> failed with = 0x%x or %d"),
|
|
pDsInfo->DomainNameDns, pLdapApi->pfnLdapGetLastError(), GetLastError()));
|
|
return pld;
|
|
}
|
|
|
|
//
|
|
// Turn on Packet integrity flag
|
|
//
|
|
|
|
pData = (VOID *) LDAP_OPT_ON;
|
|
ulResult = pLdapApi->pfnldap_set_option(pld, LDAP_OPT_SIGN, &pData);
|
|
|
|
if (ulResult != LDAP_SUCCESS) {
|
|
|
|
xe = pLdapApi->pfnLdapMapErrorToWin32(ulResult);
|
|
DebugMsg((DM_WARNING, TEXT("GetMachineDomainDS: Failed to turn on LDAP_OPT_SIGN with %d"), ulResult));
|
|
pLdapApi->pfnldap_unbind(pld);
|
|
pld = 0;
|
|
return pld;
|
|
}
|
|
|
|
ulResult = pLdapApi->pfnldap_connect(pld, 0);
|
|
|
|
if (ulResult != LDAP_SUCCESS) {
|
|
|
|
xe = pLdapApi->pfnLdapMapErrorToWin32(ulResult);
|
|
DebugMsg((DM_WARNING, TEXT("GetMachineDomainDS: Failed to connect with %d"), ulResult));
|
|
pLdapApi->pfnldap_unbind(pld);
|
|
pld = 0;
|
|
return pld;
|
|
}
|
|
|
|
//
|
|
// For machine policies specifically ask for Kerberos as the only authentication
|
|
// mechanism. Otherwise if Kerberos were to fail for some reason, then NTLM is used
|
|
// and localsystem context has no real credentials, which means that we won't get
|
|
// any GPOs back.
|
|
//
|
|
|
|
secIdentity.Version = SEC_WINNT_AUTH_IDENTITY_VERSION;
|
|
secIdentity.Length = sizeof(SEC_WINNT_AUTH_IDENTITY_EXW);
|
|
secIdentity.User = 0;
|
|
secIdentity.UserLength = 0;
|
|
secIdentity.Domain = 0;
|
|
secIdentity.DomainLength = 0;
|
|
secIdentity.Password = 0;
|
|
secIdentity.PasswordLength = 0;
|
|
secIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
|
|
secIdentity.PackageList = wszKerberos;
|
|
secIdentity.PackageListLength = lstrlen( wszKerberos );
|
|
|
|
if ( (ulResult = pLdapApi->pfnldap_bind_s (pld, 0, (WCHAR *)&secIdentity, LDAP_AUTH_SSPI)) != LDAP_SUCCESS )
|
|
{
|
|
DebugMsg((DM_WARNING, TEXT("GetMachineDomainDS: ldap_bind_s failed with %d"), ulResult));
|
|
xe = pLdapApi->pfnLdapMapErrorToWin32(ulResult);
|
|
pLdapApi->pfnldap_unbind(pld);
|
|
pld = 0;
|
|
}
|
|
|
|
pNetApi32->pfnDsRoleFreeMemory( pDsInfo );
|
|
}
|
|
else
|
|
{
|
|
xe = dwResult;
|
|
DebugMsg((DM_WARNING, TEXT("GetMachineDomainDS: The domain does not have a DS")));
|
|
}
|
|
|
|
|
|
return pld;
|
|
}
|
|
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// AllocSOM()
|
|
//
|
|
// Purpose: Allocates a new struct for SOM
|
|
//
|
|
//
|
|
// Parameters: pwszSOMId - Name of SOM
|
|
//
|
|
// Return: Pointer if successful
|
|
// NULL if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
SCOPEOFMGMT *AllocSOM( LPWSTR pwszSOMId )
|
|
{
|
|
XLastError xe;
|
|
HRESULT hr = S_OK;
|
|
SCOPEOFMGMT *pSOM = (SCOPEOFMGMT *) LocalAlloc( LPTR, sizeof(SCOPEOFMGMT) );
|
|
|
|
if ( pSOM == NULL ) {
|
|
xe = GetLastError();
|
|
return NULL;
|
|
}
|
|
|
|
pSOM->pwszSOMId = (LPWSTR) LocalAlloc( LPTR, (lstrlen(pwszSOMId) + 1) * sizeof(WCHAR) );
|
|
if ( pSOM->pwszSOMId == NULL ) {
|
|
xe = GetLastError();
|
|
LocalFree( pSOM );
|
|
return NULL;
|
|
}
|
|
|
|
hr = StringCchCopy( pSOM->pwszSOMId, lstrlen(pwszSOMId) + 1, pwszSOMId );
|
|
ASSERT(SUCCEEDED(hr));
|
|
return pSOM;
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// FreeSOM()
|
|
//
|
|
// Purpose: Frees SOM struct
|
|
//
|
|
// Parameters: pSOM - SOM to free
|
|
//
|
|
//*************************************************************
|
|
|
|
void FreeSOM( SCOPEOFMGMT *pSOM )
|
|
{
|
|
GPLINK *pGpLink = NULL;
|
|
|
|
if ( pSOM ) {
|
|
|
|
LocalFree( pSOM->pwszSOMId );
|
|
|
|
pGpLink = pSOM->pGpLinkList;
|
|
while ( pGpLink ) {
|
|
GPLINK *pTemp = pGpLink->pNext;
|
|
FreeGpLink( pGpLink );
|
|
pGpLink = pTemp;
|
|
}
|
|
|
|
LocalFree( pSOM );
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// AllocGpLink()
|
|
//
|
|
// Purpose: Allocates a new struct for GpLink
|
|
//
|
|
//
|
|
// Parameters: pwszGPO - Name of GPO
|
|
//
|
|
// Return: Pointer if successful
|
|
// NULL if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
GPLINK *AllocGpLink( LPWSTR pwszGPO, DWORD dwOptions )
|
|
{
|
|
//
|
|
// Strip out "LDAP://" prefix to get canonical Gpo path
|
|
//
|
|
|
|
WCHAR wszPrefix[] = TEXT("LDAP://");
|
|
INT iPrefixLen = lstrlen( wszPrefix );
|
|
WCHAR *pwszPath = pwszGPO;
|
|
GPLINK *pGpLink = NULL;
|
|
XLastError xe;
|
|
HRESULT hr = S_OK;
|
|
|
|
if ( (lstrlen(pwszGPO) > iPrefixLen)
|
|
&& CompareString( LOCALE_INVARIANT, NORM_IGNORECASE,
|
|
pwszGPO, iPrefixLen, wszPrefix, iPrefixLen ) == CSTR_EQUAL ) {
|
|
pwszPath = pwszGPO + iPrefixLen;
|
|
}
|
|
|
|
pGpLink = (GPLINK *) LocalAlloc( LPTR, sizeof(GPLINK) );
|
|
|
|
if ( pGpLink == NULL ) {
|
|
xe = GetLastError();
|
|
return NULL;
|
|
}
|
|
|
|
pGpLink->pwszGPO = (LPWSTR) LocalAlloc( LPTR, (lstrlen(pwszPath) + 1) * sizeof(WCHAR) );
|
|
if ( pGpLink->pwszGPO == NULL ) {
|
|
xe = GetLastError();
|
|
LocalFree( pGpLink );
|
|
return NULL;
|
|
}
|
|
|
|
hr = StringCchCopy( pGpLink->pwszGPO, lstrlen(pwszPath) + 1, pwszPath );
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
pGpLink->bEnabled = (dwOptions & GPO_FLAG_DISABLE) ? FALSE : TRUE;
|
|
|
|
if ( dwOptions & GPO_FLAG_FORCE )
|
|
pGpLink->bNoOverride = TRUE;
|
|
|
|
return pGpLink;
|
|
}
|
|
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// FreeGpLink()
|
|
//
|
|
// Purpose: Frees GpLink struct
|
|
//
|
|
// Parameters: pGpLink - GpLink to free
|
|
//
|
|
//*************************************************************
|
|
|
|
void FreeGpLink( GPLINK *pGpLink )
|
|
{
|
|
if ( pGpLink ) {
|
|
LocalFree( pGpLink->pwszGPO );
|
|
LocalFree( pGpLink );
|
|
}
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// AllocGpContainer()
|
|
//
|
|
// Purpose: Allocates a new struct for GpContainer
|
|
//
|
|
//
|
|
// Parameters: dwFlags - Flags
|
|
// bFound - Was Gpo found ?
|
|
// bAccessGranted - Was access granted ?
|
|
// bDisabled - Is Gp Container disabled ?
|
|
// dwVersion - Version #
|
|
// lpDSPath - DS path to Gpo
|
|
// lpFileSysPath - Sysvol path to Gpo
|
|
// lpDisplayName - Friendly name
|
|
// lpGpoName - Guid name
|
|
// pSD - Security descriptor
|
|
// cbSDLen - Length of security descriptor
|
|
// bFilterAllowed - Does GPO pass filter check
|
|
// pwszFilterId - WQL filter id
|
|
//
|
|
// Return: Pointer if successful
|
|
// NULL if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
GPCONTAINER *AllocGpContainer( DWORD dwFlags,
|
|
BOOL bFound,
|
|
BOOL bAccessGranted,
|
|
BOOL bDisabled,
|
|
DWORD dwVersion,
|
|
LPTSTR lpDSPath,
|
|
LPTSTR lpFileSysPath,
|
|
LPTSTR lpDisplayName,
|
|
LPTSTR lpGpoName,
|
|
PSECURITY_DESCRIPTOR pSD,
|
|
DWORD cbSDLen,
|
|
BOOL bFilterAllowed,
|
|
WCHAR *pwszFilterId,
|
|
LPWSTR szSOM,
|
|
DWORD dwOptions )
|
|
{
|
|
WCHAR wszMachPrefix[] = TEXT("LDAP://CN=Machine,");
|
|
INT iMachPrefixLen = lstrlen( wszMachPrefix );
|
|
WCHAR wszUserPrefix[] = TEXT("LDAP://CN=User,");
|
|
INT iUserPrefixLen = lstrlen( wszUserPrefix );
|
|
WCHAR *pwszPath = lpDSPath;
|
|
BOOL bResult = FALSE;
|
|
GPCONTAINER *pGpContainer = NULL;
|
|
XLastError xe;
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Strip out prefix, if any, to get the canonical path to Gpo
|
|
//
|
|
|
|
if ( (lstrlen(lpDSPath) > iUserPrefixLen)
|
|
&& CompareString( LOCALE_INVARIANT, NORM_IGNORECASE,
|
|
lpDSPath, iUserPrefixLen, wszUserPrefix, iUserPrefixLen ) == CSTR_EQUAL ) {
|
|
pwszPath = lpDSPath + iUserPrefixLen;
|
|
} else if ( (lstrlen(lpDSPath) > iMachPrefixLen)
|
|
&& CompareString( LOCALE_INVARIANT, NORM_IGNORECASE,
|
|
lpDSPath, iMachPrefixLen, wszMachPrefix, iMachPrefixLen ) == CSTR_EQUAL ) {
|
|
pwszPath = lpDSPath + iMachPrefixLen;
|
|
}
|
|
|
|
pGpContainer = (GPCONTAINER *) LocalAlloc( LPTR, sizeof(GPCONTAINER) );
|
|
|
|
if ( pGpContainer == NULL ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("AllocGpContainer: Unable to allocate memory for GpContainer object")));
|
|
return NULL;
|
|
}
|
|
|
|
pGpContainer->bAccessDenied = !bAccessGranted;
|
|
pGpContainer->bFound = bFound;
|
|
|
|
if ( dwFlags & GP_MACHINE ) {
|
|
pGpContainer->bMachDisabled = bDisabled;
|
|
pGpContainer->dwMachVersion = dwVersion;
|
|
} else {
|
|
pGpContainer->bUserDisabled = bDisabled;
|
|
pGpContainer->dwUserVersion = dwVersion;
|
|
}
|
|
|
|
if ( pwszPath ) {
|
|
|
|
pGpContainer->pwszDSPath = (LPWSTR) LocalAlloc( LPTR, (lstrlen(pwszPath) + 1) * sizeof(WCHAR) );
|
|
if ( pGpContainer->pwszDSPath == NULL ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("AllocGpContainer: Unable to allocate memory for GpContainer object")));
|
|
goto Exit;
|
|
}
|
|
|
|
hr = StringCchCopy( pGpContainer->pwszDSPath, lstrlen(pwszPath) + 1, pwszPath );
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
if ( lpGpoName ) {
|
|
|
|
pGpContainer->pwszGPOName = (LPWSTR) LocalAlloc( LPTR, (lstrlen(lpGpoName) + 1) * sizeof(WCHAR) );
|
|
if ( pGpContainer->pwszGPOName == NULL ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("AllocGpContainer: Unable to allocate memory for GpContainer object")));
|
|
goto Exit;
|
|
}
|
|
|
|
hr = StringCchCopy( pGpContainer->pwszGPOName, lstrlen(lpGpoName) + 1, lpGpoName );
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
if ( lpDisplayName ) {
|
|
|
|
pGpContainer->pwszDisplayName = (LPWSTR) LocalAlloc( LPTR, (lstrlen(lpDisplayName) + 1) * sizeof(WCHAR) );
|
|
if ( pGpContainer->pwszDisplayName == NULL ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("AllocGpContainer: Unable to allocate memory for GpContainer object")));
|
|
goto Exit;
|
|
}
|
|
|
|
hr = StringCchCopy( pGpContainer->pwszDisplayName, lstrlen(lpDisplayName) + 1, lpDisplayName );
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
if ( lpFileSysPath ) {
|
|
|
|
pGpContainer->pwszFileSysPath = (LPWSTR) LocalAlloc( LPTR, (lstrlen(lpFileSysPath) + 1) * sizeof(WCHAR) );
|
|
if ( pGpContainer->pwszFileSysPath == NULL ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("AllocGpContainer: Unable to allocate memory for GpContainer object")));
|
|
goto Exit;
|
|
}
|
|
|
|
hr = StringCchCopy( pGpContainer->pwszFileSysPath, lstrlen(lpFileSysPath) + 1, lpFileSysPath );
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
if ( cbSDLen != 0 ) {
|
|
|
|
pGpContainer->pSD = (PSECURITY_DESCRIPTOR) LocalAlloc( LPTR, cbSDLen );
|
|
if ( pGpContainer->pSD == NULL ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("AllocGpContainer: Unable to allocate memory for GpContainer object")));
|
|
goto Exit;
|
|
}
|
|
|
|
CopyMemory( pGpContainer->pSD, pSD, cbSDLen );
|
|
|
|
}
|
|
|
|
pGpContainer->cbSDLen = cbSDLen;
|
|
|
|
pGpContainer->bFilterAllowed = bFilterAllowed;
|
|
|
|
if ( pwszFilterId ) {
|
|
|
|
pGpContainer->pwszFilterId = (LPWSTR) LocalAlloc( LPTR, (lstrlen(pwszFilterId) + 1) * sizeof(WCHAR) );
|
|
if ( pGpContainer->pwszFilterId == NULL ) {
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("AllocGpContainer: Unable to allocate memory for GpContainer object")));
|
|
goto Exit;
|
|
}
|
|
|
|
hr = StringCchCopy( pGpContainer->pwszFilterId, lstrlen(pwszFilterId) + 1, pwszFilterId );
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
if ( szSOM )
|
|
{
|
|
pGpContainer->szSOM = (LPWSTR) LocalAlloc( LPTR, (lstrlen(szSOM) + 1) * sizeof(WCHAR) );
|
|
if ( !pGpContainer->szSOM )
|
|
{
|
|
xe = GetLastError();
|
|
DebugMsg((DM_WARNING, TEXT("AllocGpContainer: Unable to allocate memory for GpContainer object")));
|
|
goto Exit;
|
|
}
|
|
hr = StringCchCopy( pGpContainer->szSOM, lstrlen(szSOM) + 1, szSOM );
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
pGpContainer->dwOptions = dwOptions;
|
|
|
|
bResult = TRUE;
|
|
|
|
Exit:
|
|
|
|
if ( !bResult )
|
|
{
|
|
LocalFree( pGpContainer->pwszDSPath );
|
|
LocalFree( pGpContainer->pwszGPOName );
|
|
LocalFree( pGpContainer->pwszDisplayName );
|
|
LocalFree( pGpContainer->pwszFileSysPath );
|
|
LocalFree( pGpContainer->pSD );
|
|
LocalFree( pGpContainer->pwszFilterId );
|
|
LocalFree( pGpContainer->szSOM );
|
|
|
|
LocalFree( pGpContainer );
|
|
|
|
return 0;
|
|
}
|
|
|
|
return pGpContainer;
|
|
}
|
|
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// FreeGpContainer()
|
|
//
|
|
// Purpose: Frees GpContainer struct
|
|
//
|
|
// Parameters: pGpContainer - Gp Container to free
|
|
//
|
|
//*************************************************************
|
|
|
|
void FreeGpContainer( GPCONTAINER *pGpContainer )
|
|
{
|
|
if ( pGpContainer ) {
|
|
|
|
LocalFree( pGpContainer->pwszDSPath );
|
|
LocalFree( pGpContainer->pwszGPOName );
|
|
LocalFree( pGpContainer->pwszDisplayName );
|
|
LocalFree( pGpContainer->pwszFileSysPath );
|
|
LocalFree( pGpContainer->pSD );
|
|
LocalFree( pGpContainer->pwszFilterId );
|
|
LocalFree( pGpContainer->szSOM );
|
|
LocalFree( pGpContainer );
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// FreeSOMList()
|
|
//
|
|
// Purpose: Frees list of SOMs
|
|
//
|
|
// Parameters: pSOMList - SOM list to free
|
|
//
|
|
//*************************************************************
|
|
|
|
void FreeSOMList( SCOPEOFMGMT *pSOMList )
|
|
{
|
|
if ( pSOMList == NULL )
|
|
return;
|
|
|
|
while ( pSOMList ) {
|
|
SCOPEOFMGMT *pTemp = pSOMList->pNext;
|
|
FreeSOM( pSOMList );
|
|
pSOMList = pTemp;
|
|
}
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// FreeGpContainerList()
|
|
//
|
|
// Purpose: Frees list of Gp Containers
|
|
//
|
|
// Parameters: pGpContainerList - Gp Container list to free
|
|
//
|
|
//*************************************************************
|
|
|
|
void FreeGpContainerList( GPCONTAINER *pGpContainerList )
|
|
{
|
|
if ( pGpContainerList == NULL )
|
|
return;
|
|
|
|
while ( pGpContainerList ) {
|
|
GPCONTAINER *pTemp = pGpContainerList->pNext;
|
|
FreeGpContainer( pGpContainerList );
|
|
pGpContainerList = pTemp;
|
|
}
|
|
}
|
|
|
|
LPTSTR GetSomPath( LPTSTR szContainer )
|
|
{
|
|
while (*szContainer) {
|
|
|
|
//
|
|
// See if the DN name starts with OU=
|
|
//
|
|
|
|
if (CompareString (LOCALE_INVARIANT, NORM_IGNORECASE,
|
|
szContainer, 3, TEXT("OU="), 3) == CSTR_EQUAL) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// See if the DN name starts with DC=
|
|
//
|
|
|
|
else if (CompareString (LOCALE_INVARIANT, NORM_IGNORECASE,
|
|
szContainer, 3, TEXT("DC="), 3) == CSTR_EQUAL) {
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Move to the next chunk of the DN name
|
|
//
|
|
|
|
while (*szContainer && (*szContainer != TEXT(','))) {
|
|
szContainer++;
|
|
}
|
|
|
|
if (*szContainer == TEXT(',')) {
|
|
szContainer++;
|
|
}
|
|
}
|
|
|
|
if (!*szContainer) {
|
|
return NULL;
|
|
}
|
|
|
|
return szContainer;
|
|
}
|