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.
445 lines
16 KiB
445 lines
16 KiB
#include "pch.h"
|
|
#include <atlbase.h>
|
|
#include "dsrole.h"
|
|
#include "strsafe.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ Display specifier helpers/cache functions
|
|
/----------------------------------------------------------------------------*/
|
|
|
|
#define DEFAULT_LANGUAGE 0x409
|
|
|
|
#define DISPLAY_SPECIFIERS L"CN=displaySpecifiers"
|
|
#define SPECIFIER_PREFIX L"CN="
|
|
#define SPECIFIER_POSTFIX L"-Display"
|
|
#define DEFAULT_SPECIFIER L"default"
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ GetDisplaySpecifier
|
|
/ -------------------
|
|
/ Get the specified display specifier (sic), given it an LANGID etc.
|
|
/
|
|
/ In:
|
|
/ pccgi -> CLASSCACHEGETINFO structure.
|
|
/ riid = interface
|
|
/ ppvObject = object requested
|
|
/
|
|
/ Out:
|
|
HRESULT
|
|
/----------------------------------------------------------------------------*/
|
|
|
|
HRESULT _GetServerConfigPath(LPWSTR pszConfigPath, int cchConfigPath, LPCLASSCACHEGETINFO pccgi)
|
|
{
|
|
HRESULT hres;
|
|
IADs* padsRootDSE = NULL;
|
|
BSTR bstrConfigContainer = NULL;
|
|
VARIANT variant = {0};
|
|
INT cchString;
|
|
LPWSTR pszServer = pccgi->pServer;
|
|
LPWSTR pszMachineServer = NULL;
|
|
|
|
//
|
|
// open the RootDSE for the server we are interested in, if we are using the default
|
|
// server then lets just use the cached version.
|
|
//
|
|
|
|
hres = GetCacheInfoRootDSE(pccgi, &padsRootDSE);
|
|
if ( (hres == HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN)) && !pccgi->pServer )
|
|
{
|
|
TraceMsg("Failed to get the RootDSE from the server - not found");
|
|
|
|
DSROLE_PRIMARY_DOMAIN_INFO_BASIC *pInfo;
|
|
if ( DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (BYTE**)&pInfo) == WN_SUCCESS )
|
|
{
|
|
if ( pInfo->DomainNameDns )
|
|
{
|
|
Trace(TEXT("Machine domain is: %s"), pInfo->DomainNameDns);
|
|
|
|
CLASSCACHEGETINFO ccgi = *pccgi;
|
|
ccgi.pServer = pInfo->DomainNameDns;
|
|
|
|
hres = GetCacheInfoRootDSE(&ccgi, &padsRootDSE);
|
|
if ( SUCCEEDED(hres) )
|
|
{
|
|
hres = LocalAllocStringW(&pszMachineServer, pInfo->DomainNameDns);
|
|
pszServer = pszMachineServer;
|
|
}
|
|
}
|
|
|
|
DsRoleFreeMemory(pInfo);
|
|
}
|
|
}
|
|
FailGracefully(hres, "Failed to get the IADs for the RootDSE");
|
|
|
|
//
|
|
// we now have the RootDSE, so lets read the config container path and compose
|
|
// a string that the outside world cna use
|
|
//
|
|
|
|
hres = padsRootDSE->Get(CComBSTR(L"configurationNamingContext"), &variant);
|
|
FailGracefully(hres, "Failed to get the 'configurationNamingContext' property");
|
|
|
|
if ( V_VT(&variant) != VT_BSTR )
|
|
ExitGracefully(hres, E_FAIL, "configurationNamingContext is not a BSTR");
|
|
|
|
// copy the string
|
|
|
|
(void)StringCchCopy(pszConfigPath, cchConfigPath, L"LDAP://");
|
|
|
|
if ( pszServer )
|
|
{
|
|
(void)StringCchCat(pszConfigPath, cchConfigPath, pszServer);
|
|
(void)StringCchCat(pszConfigPath, cchConfigPath, L"/");
|
|
}
|
|
|
|
hres = StringCchCat(pszConfigPath, cchConfigPath, V_BSTR(&variant));
|
|
FailGracefully(hres, "Failed to complete the config path");
|
|
|
|
Trace(TEXT("Server config path is: %s"), pszConfigPath);
|
|
hres = S_OK; // success
|
|
|
|
exit_gracefully:
|
|
|
|
DoRelease(padsRootDSE);
|
|
SysFreeString(bstrConfigContainer);
|
|
LocalFreeStringW(&pszMachineServer);
|
|
VariantClear(&variant);
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT _ComposeSpecifierPath(LPWSTR pSpecifier, LANGID langid, LPWSTR pConfigPath, IADsPathname* pDsPathname, BSTR *pbstrDisplaySpecifier)
|
|
{
|
|
HRESULT hr = pDsPathname->Set(CComBSTR(pConfigPath), ADS_SETTYPE_FULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pDsPathname->AddLeafElement(CComBSTR(DISPLAY_SPECIFIERS));
|
|
|
|
if ( !langid )
|
|
langid = GetUserDefaultUILanguage();
|
|
|
|
TCHAR szLANGID[16];
|
|
hr = StringCchPrintf(szLANGID, ARRAYSIZE(szLANGID), TEXT("CN=%x"), langid);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pDsPathname->AddLeafElement(CComBSTR(szLANGID));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if ( pSpecifier )
|
|
{
|
|
WCHAR szSpecifierFull[INTERNET_MAX_URL_LENGTH];
|
|
(void)StringCchCopy(szSpecifierFull, ARRAYSIZE(szSpecifierFull), SPECIFIER_PREFIX);
|
|
(void)StringCchCat(szSpecifierFull, ARRAYSIZE(szSpecifierFull), pSpecifier);
|
|
|
|
hr = StringCchCat(szSpecifierFull, ARRAYSIZE(szSpecifierFull), SPECIFIER_POSTFIX);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Trace(TEXT("szSpecifierFull: %s"), szSpecifierFull);
|
|
hr = pDsPathname->AddLeafElement(CComBSTR(szSpecifierFull)); // add to the name we are dealing with
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return FAILED(hr) ? hr:pDsPathname->Retrieve(ADS_FORMAT_WINDOWS, pbstrDisplaySpecifier);
|
|
}
|
|
|
|
HRESULT GetDisplaySpecifier(LPCLASSCACHEGETINFO pccgi, REFIID riid, LPVOID* ppvObject)
|
|
{
|
|
HRESULT hr;
|
|
IADsPathname* pDsPathname = NULL;
|
|
BSTR bstrDisplaySpecifier = NULL;
|
|
WCHAR szConfigPath[INTERNET_MAX_URL_LENGTH];
|
|
|
|
TraceEnter(TRACE_CACHE, "GetDisplaySpecifier");
|
|
Trace(TEXT("Display specifier %s, LANGID %x"), pccgi->pObjectClass, pccgi->langid);
|
|
|
|
// When dealing with the local case lets ensure that we enable/disable the flags
|
|
// accordingly.
|
|
|
|
if ( !(pccgi->dwFlags & CLASSCACHE_DSAVAILABLE) && !ShowDirectoryUI() )
|
|
{
|
|
ExitGracefully(hr, HRESULT_FROM_WIN32(ERROR_DS_NO_SUCH_OBJECT), "ShowDirectoryUI returned FALSE, and the CLASSCAHCE_DSAVAILABLE flag is not set");
|
|
}
|
|
|
|
hr = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IADsPathname, &pDsPathname));
|
|
FailGracefully(hr, "Failed to get the IADsPathname interface");
|
|
|
|
// check to see if we have a valid server config path
|
|
|
|
if ( !pccgi->pServerConfigPath )
|
|
{
|
|
hr = _GetServerConfigPath(szConfigPath, ARRAYSIZE(szConfigPath), pccgi);
|
|
FailGracefully(hr, "Failed to allocate server config path");
|
|
}
|
|
else
|
|
{
|
|
hr = StringCchCopy(szConfigPath, ARRAYSIZE(szConfigPath), pccgi->pServerConfigPath);
|
|
FailGracefully(hr, "Failed to copy the config path");
|
|
}
|
|
|
|
hr = _ComposeSpecifierPath(pccgi->pObjectClass, pccgi->langid, szConfigPath, pDsPathname, &bstrDisplaySpecifier);
|
|
FailGracefully(hr, "Failed to retrieve the display specifier path");
|
|
|
|
// attempt to bind to the display specifier object, if we fail to find the object
|
|
// then try defaults.
|
|
|
|
Trace(TEXT("Calling GetObject on: %s"), bstrDisplaySpecifier);
|
|
|
|
hr = ClassCache_OpenObject(bstrDisplaySpecifier, riid, ppvObject, pccgi);
|
|
|
|
SysFreeString(bstrDisplaySpecifier);
|
|
if ( hr == HRESULT_FROM_WIN32(ERROR_DS_NO_SUCH_OBJECT) )
|
|
{
|
|
// Display specifier not found. Try the default specifier in the
|
|
// caller's locale. The default specifier is the catch-all for classes
|
|
// that don't have their own specifier.
|
|
|
|
hr = _ComposeSpecifierPath(DEFAULT_SPECIFIER, pccgi->langid, szConfigPath, pDsPathname, &bstrDisplaySpecifier);
|
|
FailGracefully(hr, "Failed to retrieve the display specifier path");
|
|
Trace(TEXT("Calling GetObject on: %s"), bstrDisplaySpecifier);
|
|
|
|
hr = ClassCache_OpenObject(bstrDisplaySpecifier, riid, ppvObject, pccgi);
|
|
|
|
SysFreeString(bstrDisplaySpecifier);
|
|
if ((hr == HRESULT_FROM_WIN32(ERROR_DS_NO_SUCH_OBJECT)) && (pccgi->langid != DEFAULT_LANGUAGE))
|
|
{
|
|
// Now try the object's specifier in the default locale.
|
|
|
|
hr = _ComposeSpecifierPath(pccgi->pObjectClass, DEFAULT_LANGUAGE, szConfigPath, pDsPathname, &bstrDisplaySpecifier);
|
|
FailGracefully(hr, "Failed to retrieve the display specifier path");
|
|
Trace(TEXT("Calling GetObject on: %s"), bstrDisplaySpecifier);
|
|
|
|
hr = ClassCache_OpenObject(bstrDisplaySpecifier, riid, ppvObject, pccgi);
|
|
|
|
SysFreeString(bstrDisplaySpecifier);
|
|
if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_SUCH_OBJECT))
|
|
{
|
|
// Finally try the default specifier in the default locale.
|
|
|
|
hr = _ComposeSpecifierPath(DEFAULT_SPECIFIER, DEFAULT_LANGUAGE, szConfigPath, pDsPathname, &bstrDisplaySpecifier);
|
|
FailGracefully(hr, "Failed to retrieve the display specifier path");
|
|
Trace(TEXT("Calling GetObject on: %s"), bstrDisplaySpecifier);
|
|
|
|
hr = ClassCache_OpenObject(bstrDisplaySpecifier, riid, ppvObject, pccgi);
|
|
SysFreeString(bstrDisplaySpecifier);
|
|
}
|
|
}
|
|
}
|
|
|
|
FailGracefully(hr, "Failed in ADsOpenObject for display specifier");
|
|
|
|
// hr = S_OK; // success
|
|
|
|
exit_gracefully:
|
|
|
|
DoRelease(pDsPathname);
|
|
|
|
TraceLeaveResult(hr);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ GetServerAndCredentails
|
|
/ -----------------------
|
|
/ Read the server and credentails information from the IDataObject.
|
|
/
|
|
/ In:
|
|
/ pccgi -> CLASSCACHEGETINFO structure to be filled
|
|
/
|
|
/ Out:
|
|
/ HRESULT
|
|
/----------------------------------------------------------------------------*/
|
|
HRESULT GetServerAndCredentails(CLASSCACHEGETINFO *pccgi)
|
|
{
|
|
HRESULT hres;
|
|
STGMEDIUM medium = { TYMED_NULL };
|
|
FORMATETC fmte = {g_cfDsDispSpecOptions, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
DSDISPLAYSPECOPTIONS *pdso = NULL;
|
|
|
|
TraceEnter(TRACE_UI, "GetServerAndCredentails");
|
|
|
|
// we can only get this information if we have a pDataObject to call.
|
|
|
|
pccgi->pUserName = NULL;
|
|
pccgi->pPassword = NULL;
|
|
pccgi->pServer = NULL;
|
|
pccgi->pServerConfigPath = NULL;
|
|
|
|
if ( pccgi->pDataObject )
|
|
{
|
|
if ( SUCCEEDED(pccgi->pDataObject->GetData(&fmte, &medium)) )
|
|
{
|
|
pdso = (DSDISPLAYSPECOPTIONS*)GlobalLock(medium.hGlobal);
|
|
TraceAssert(pdso);
|
|
|
|
// mirror the flags into the CCGI structure
|
|
|
|
if ( pdso->dwFlags & DSDSOF_SIMPLEAUTHENTICATE )
|
|
{
|
|
TraceMsg("Setting simple authentication");
|
|
pccgi->dwFlags |= CLASSCACHE_SIMPLEAUTHENTICATE;
|
|
}
|
|
|
|
if ( pdso->dwFlags & DSDSOF_DSAVAILABLE )
|
|
{
|
|
TraceMsg("Setting 'DS is available' flags");
|
|
pccgi->dwFlags |= CLASSCACHE_DSAVAILABLE;
|
|
}
|
|
|
|
// if we have credentail information that should be copied then lets grab
|
|
// that and put it into the structure.
|
|
|
|
if ( pdso->dwFlags & DSDSOF_HASUSERANDSERVERINFO )
|
|
{
|
|
if ( pdso->offsetUserName )
|
|
{
|
|
LPCWSTR pszUserName = (LPCWSTR)ByteOffset(pdso, pdso->offsetUserName);
|
|
hres = LocalAllocStringW(&pccgi->pUserName, pszUserName);
|
|
FailGracefully(hres, "Failed to copy the user name");
|
|
}
|
|
|
|
if ( pdso->offsetPassword )
|
|
{
|
|
LPCWSTR pszPassword = (LPCWSTR)ByteOffset(pdso, pdso->offsetPassword);
|
|
hres = LocalAllocStringW(&pccgi->pPassword, pszPassword);
|
|
FailGracefully(hres, "Failed to copy the password");
|
|
}
|
|
|
|
if ( pdso->offsetServer )
|
|
{
|
|
LPCWSTR pszServer = (LPCWSTR)ByteOffset(pdso, pdso->offsetServer);
|
|
hres = LocalAllocStringW(&pccgi->pServer, pszServer);
|
|
FailGracefully(hres, "Failed to copy the server");
|
|
}
|
|
|
|
if ( pdso->offsetServerConfigPath )
|
|
{
|
|
LPCWSTR pszServerConfigPath = (LPCWSTR)ByteOffset(pdso, pdso->offsetServerConfigPath);
|
|
hres = LocalAllocStringW(&pccgi->pServerConfigPath, pszServerConfigPath);
|
|
FailGracefully(hres, "Failed to copy the server config path");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
hres = S_OK; // success
|
|
|
|
exit_gracefully:
|
|
|
|
if ( FAILED(hres) )
|
|
{
|
|
SecureLocalFreeStringW(&pccgi->pUserName);
|
|
SecureLocalFreeStringW(&pccgi->pPassword);
|
|
SecureLocalFreeStringW(&pccgi->pServer);
|
|
|
|
LocalFreeStringW(&pccgi->pServerConfigPath);
|
|
}
|
|
|
|
if (pdso)
|
|
GlobalUnlock(medium.hGlobal);
|
|
|
|
ReleaseStgMedium(&medium);
|
|
|
|
TraceLeaveResult(hres);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ GetAttributePrefix
|
|
/ ------------------
|
|
/ Get the attribtue prefix we must use to pick up information from the
|
|
/ cache / DS. This is part of the IDataObject we are given, if not then
|
|
/ we default to shell behaviour.
|
|
/
|
|
/ In:
|
|
/ ppAttributePrefix -> receives the attribute prefix string
|
|
/ pDataObject = IDataObject to query against.
|
|
/
|
|
/ Out:
|
|
/ HRESULT
|
|
/----------------------------------------------------------------------------*/
|
|
HRESULT GetAttributePrefix(LPWSTR* ppAttributePrefix, IDataObject* pDataObject)
|
|
{
|
|
HRESULT hr;
|
|
STGMEDIUM medium = { TYMED_NULL };
|
|
FORMATETC fmte = {g_cfDsDispSpecOptions, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
PDSDISPLAYSPECOPTIONS pOptions;
|
|
LPWSTR pPrefix = NULL;
|
|
|
|
TraceEnter(TRACE_UI, "GetAttributePrefix");
|
|
|
|
if ( (SUCCEEDED(pDataObject->GetData(&fmte, &medium))) && (medium.tymed == TYMED_HGLOBAL) )
|
|
{
|
|
pOptions = (PDSDISPLAYSPECOPTIONS)medium.hGlobal;
|
|
pPrefix = (LPWSTR)ByteOffset(pOptions, pOptions->offsetAttribPrefix);
|
|
|
|
Trace(TEXT("pOptions->dwSize %d"), pOptions->dwSize);
|
|
Trace(TEXT("pOptions->dwFlags %08x"), pOptions->dwFlags);
|
|
Trace(TEXT("pOptions->offsetAttribPrefix %d (%s)"), pOptions->offsetAttribPrefix, pPrefix);
|
|
|
|
hr = LocalAllocStringW(ppAttributePrefix, pPrefix);
|
|
FailGracefully(hr, "Failed when copying prefix from StgMedium");
|
|
}
|
|
else
|
|
{
|
|
hr = LocalAllocStringW(ppAttributePrefix, DS_PROP_SHELL_PREFIX);
|
|
FailGracefully(hr, "Failed when defaulting the attribute prefix string");
|
|
}
|
|
|
|
Trace(TEXT("Resulting prefix: %s"), *ppAttributePrefix);
|
|
|
|
// hr = S_OK; // success
|
|
|
|
exit_gracefully:
|
|
|
|
ReleaseStgMedium(&medium);
|
|
|
|
TraceLeaveResult(hr);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ GetCacheInfoRootDSE
|
|
/ -------------------
|
|
/ Get the RootDSE given an CLASSCACHEGETINFO structure
|
|
/
|
|
/ In:
|
|
/ pccgi -> CLASSCACHEGETINFO structure.
|
|
/ pads -> IADs* interface
|
|
/
|
|
/ Out:
|
|
HRESULT
|
|
/----------------------------------------------------------------------------*/
|
|
|
|
HRESULT GetCacheInfoRootDSE(LPCLASSCACHEGETINFO pccgi, IADs **ppads)
|
|
{
|
|
HRESULT hres;
|
|
LPWSTR pszRootDSE = L"/RootDSE";
|
|
WCHAR szBuffer[INTERNET_MAX_URL_LENGTH];
|
|
|
|
TraceEnter(TRACE_CACHE, "GetRootDSE");
|
|
|
|
(void)StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), L"LDAP://");
|
|
|
|
if (pccgi->pServer)
|
|
(void)StringCchCat(szBuffer, ARRAYSIZE(szBuffer), pccgi->pServer);
|
|
else
|
|
pszRootDSE++;
|
|
|
|
hres = StringCchCat(szBuffer, ARRAYSIZE(szBuffer), pszRootDSE);
|
|
FailGracefully(hres, "Failed to compute RootDSE path, buffer too small");
|
|
|
|
Trace(TEXT("RootDSE path is: %s"), szBuffer);
|
|
|
|
hres = ClassCache_OpenObject(szBuffer, IID_PPV_ARG(IADs, ppads), pccgi);
|
|
|
|
exit_gracefully:
|
|
|
|
TraceLeaveResult(hres);
|
|
}
|
|
|