|
|
#include "pch.h"
#include "lm.h"
#include "ntdsapi.h"
#include "dsgetdc.h"
#include "dsrole.h"
#include "security.h"
#pragma hdrstop
/*-----------------------------------------------------------------------------
/ Locals & helper functions /----------------------------------------------------------------------------*/
// BUGBUG: ChandanaS says use LocalFree when building for Win95 as the
// BUGBUG: NetApiBufferFree is not available, she will fix this in
// BUGBUG: time.
#if !defined(UNICODE)
#define NetApiBufferFree(x) LocalFree(x)
#endif
/*-----------------------------------------------------------------------------
/ _StringFromSearchColumnArray / ---------------------------- / Given an ADS_SEARCH_COLUMN attempt to get the string version of that / property. / / In: / pColumn -> ADS_SEARCH_COLUMN structure to be unpicked / i = index for the column to be fetched / pBuffer, pLen = updated accordingly / / Out: / HRESULT /----------------------------------------------------------------------------*/ VOID _StringFromSearchColumnArray(PADS_SEARCH_COLUMN pColumn, INT i, LPWSTR pBuffer, UINT* pLen) { LPWSTR pValue; TCHAR szBuffer[MAX_PATH]; USES_CONVERSION;
TraceEnter(TRACE_DS, "_StringFromSearchColumnArray");
switch ( pColumn->dwADsType ) { case ADSTYPE_DN_STRING: case ADSTYPE_CASE_EXACT_STRING: case ADSTYPE_CASE_IGNORE_STRING: case ADSTYPE_PRINTABLE_STRING: case ADSTYPE_NUMERIC_STRING: PutStringElementW(pBuffer, pLen, pColumn->pADsValues[i].DNString); break;
case ADSTYPE_BOOLEAN: PutStringElementW(pBuffer, pLen, (pColumn->pADsValues[i].Boolean) ? L"1":L"0"); break; case ADSTYPE_INTEGER: wsprintf(szBuffer, TEXT("%d"), (INT)pColumn->pADsValues[i].Integer); PutStringElementW(pBuffer, pLen, T2W(szBuffer)); break;
case ADSTYPE_OCTET_STRING: { for ( ULONG j = 0; j < pColumn->pADsValues[i].OctetString.dwLength; j++) { wsprintf(szBuffer, TEXT("%02x"), ((LPBYTE)pColumn->pADsValues[i].OctetString.lpValue)[j]); PutStringElementW(pBuffer, pLen, T2W(szBuffer)); }
break; }
case ADSTYPE_LARGE_INTEGER: wsprintf(szBuffer, TEXT("%e"), (double)pColumn->pADsValues[i].Integer); PutStringElementW(pBuffer, pLen, T2W(szBuffer)); break;
default: break; }
TraceLeave(); }
/*-----------------------------------------------------------------------------
/ StringFromSearchColumn / ---------------------- / Given an ADS_SEARCH_COLUMN attempt to get the string version of that / property. / / In: / pColumn -> ADS_SEARCH_COLUMN structure to be unpicked / pBuffer, pLen = the buffer to be filled (NULL accepted for both) / / Out: / HRESULT /----------------------------------------------------------------------------*/ VOID _StringFromSearchColumn(PADS_SEARCH_COLUMN pColumn, LPWSTR pBuffer, UINT* pLen) { DWORD index;
TraceEnter(TRACE_DS, "_StringFromSearchColumn");
if ( pBuffer ) pBuffer[0] = TEXT('\0');
for ( index = 0 ; index != pColumn->dwNumValues; index++ ) { if ( index > 0 ) PutStringElementW(pBuffer, pLen, L", ");
_StringFromSearchColumnArray(pColumn, index, pBuffer, pLen); }
TraceLeave(); }
STDAPI StringFromSearchColumn(PADS_SEARCH_COLUMN pColumn, LPWSTR* ppBuffer) { HRESULT hr; UINT len = 0;
TraceEnter(TRACE_DS, "StringFromSearchColumn");
_StringFromSearchColumn(pColumn, NULL, &len);
if ( len ) { hr = LocalAllocStringLenW(ppBuffer, len); FailGracefully(hr, "Failed to allocate buffer for string");
_StringFromSearchColumn(pColumn, *ppBuffer, NULL); Trace(TEXT("Resulting string: %s"), *ppBuffer); }
hr = S_OK;
exit_gracefully:
TraceLeaveResult(hr); }
/*-----------------------------------------------------------------------------
/ ObjectClassFromSearchColumn / ---------------------------- / Given an ADS_SEARCH_COLUMN extract the object class from it. Object class / is a multi-value property therefore we need to try and find which element / is the real class name. / / All object have a base class "top", therefore we check the last element / of the property array, if that is "top" then we use the first element, / otherwise the last. / / In: / pBuffer, cchBuffer = buffer to be filled / pColumn -> ADS_SEARCH_COLUMN structure to be unpicked / / Out: / HRESULT /----------------------------------------------------------------------------*/ STDAPI ObjectClassFromSearchColumn(PADS_SEARCH_COLUMN pColumn, LPWSTR* ppBuffer) { HRESULT hr; WCHAR szBuffer[MAX_PATH]; ULONG i; TraceEnter(TRACE_DS, "ObjectClassFromSearchColumn"); szBuffer[0] = TEXT('\0'); _StringFromSearchColumnArray(pColumn, 0, szBuffer, NULL);
if ( !StrCmpIW(szBuffer, L"top") ) { szBuffer[0] = TEXT('\0'); _StringFromSearchColumnArray(pColumn, pColumn->dwNumValues-1, szBuffer, NULL); }
hr = LocalAllocStringW(ppBuffer, szBuffer); FailGracefully(hr, "Failed to get alloc string buffer");
// hr = S_OK; // success
exit_gracefully:
TraceLeaveResult(hr); }
/*-----------------------------------------------------------------------------
/ GetArrayContents / ---------------- / Given a VARIANT call the callback function with each element that we / see in it. If the VARIANT is an array then call the callback in the / correct order to give sensible results. / / In: / pVariant -> VARAINT to be unpacked / pCB, pData -> callback to be called for each item / / Out: / HRESULT /----------------------------------------------------------------------------*/
INT _GetArrayCompareCB(LPVOID p1, LPVOID p2, LPARAM lParam) { HRESULT hr; WCHAR szBuffer[MAX_PATH]; LONG i = 0;
TraceEnter(TRACE_DS, "_GetArrayCompareCB");
hr = GetStringElementW((BSTR)p1, 0, szBuffer, ARRAYSIZE(szBuffer)); FailGracefully(hr, "Failed to get the position value");
i = StringToDWORD(szBuffer);
hr = GetStringElementW((BSTR)p2, 0, szBuffer, ARRAYSIZE(szBuffer)); FailGracefully(hr, "Failed to get the position value");
exit_gracefully:
TraceLeaveValue(i - StringToDWORD(szBuffer)); }
STDAPI GetArrayContents(LPVARIANT pVariant, LPGETARRAYCONTENTCB pCB, LPVOID pData) { HRESULT hr; LONG arrayMin, arrayMax, i; WCHAR szBuffer[MAX_PATH]; VARIANT varElement; HDPA hdpa = NULL; LPWSTR pValue; DWORD dwIndex;
TraceEnter(TRACE_DS, "GetArrayContents");
VariantInit(&varElement);
switch ( V_VT(pVariant) ) { case VT_BSTR: { hr = GetStringElementW(V_BSTR(pVariant), 0, szBuffer, ARRAYSIZE(szBuffer)); FailGracefully(hr, "Failed to get the position value");
dwIndex = StringToDWORD(szBuffer);
pValue = wcschr(V_BSTR(pVariant), TEXT(',')); // NB: can return NULL (eg. not found)
TraceAssert(pValue);
if ( pValue ) { hr = (*pCB)(dwIndex, pValue+1, pData); FailGracefully(hr, "Failed when calling with VT_BSTR"); }
break; }
case VT_VARIANT | VT_ARRAY: { // read the VARIANTs into the DPA, don't worry about order just pick up
// the contents of the array
if ( (V_ARRAY(pVariant))->rgsabound[0].cElements < 1 ) ExitGracefully(hr, E_FAIL, "Array less than 1 element in size");
hr = SafeArrayGetLBound(V_ARRAY(pVariant), 1, (LONG*)&arrayMin); if ( SUCCEEDED(hr) ) hr = SafeArrayGetUBound(V_ARRAY(pVariant), 1, (LONG*)&arrayMax);
FailGracefully(hr, "Failed to the the array boundaries");
hdpa = DPA_Create(arrayMax-arrayMin); if ( !hdpa ) ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate DPA");
Trace(TEXT("arrayMin %d, arrayMax %d"), arrayMin, arrayMax);
for ( i = arrayMin; i <= arrayMax; i++ ) { hr = SafeArrayGetElement(V_ARRAY(pVariant), (LONG*)&i, &varElement); FailGracefully(hr, "Failed to look up in variant array");
if ( V_VT(&varElement) == VT_BSTR ) { hr = StringDPA_AppendStringW(hdpa, V_BSTR(&varElement), NULL); FailGracefully(hr, "Failed to add the string to the DPA"); }
VariantClear(&varElement); }
// now sort the DPA based on the first element. then pass them
// out the the caller, skipping the leading character
if ( DPA_GetPtrCount(hdpa) > 0 ) { DPA_Sort(hdpa, _GetArrayCompareCB, NULL);
for ( i = 0 ; i != DPA_GetPtrCount(hdpa); i++ ) { hr = GetStringElementW(StringDPA_GetStringW(hdpa, i), 0, szBuffer, ARRAYSIZE(szBuffer)); FailGracefully(hr, "Failed to get the position value");
dwIndex = StringToDWORD(szBuffer);
pValue = wcschr((BSTR)DPA_FastGetPtr(hdpa, i), TEXT(',')); // nb: can be null one exit
TraceAssert(pValue);
if ( pValue ) { hr = (*pCB)(dwIndex, pValue+1, pData); FailGracefully(hr, "Failed when calling with VT_BSTR (from array)"); } } }
break; }
case VT_EMPTY: { TraceMsg("VARIANT is empty"); break; } }
hr = S_OK;
exit_gracefully:
VariantClear(&varElement); StringDPA_Destroy(&hdpa);
TraceLeaveResult(hr); }
/*-----------------------------------------------------------------------------
/ GetDisplayNameFromADsPath / ------------------------- / Convert the ADsPath to its display name with a suitable prefix. / / In: / pszPath -> ADsPath to be displayed / pszBuffer, cchBuffer = buffer to return the name into / padp -> IADsPathname for increased perf / fPrefix = add the NTDS:// or not.
/ / Out: / HRESULT /----------------------------------------------------------------------------*/
#define NAME_PREFIX L"ntds://"
#define CCH_NAME_PREFIX 7
#define CHECK_WIN32(err) ((err) == ERROR_SUCCESS)
STDAPI GetDisplayNameFromADsPath(LPCWSTR pszPath, LPWSTR pszBuffer, INT cchBuffer, IADsPathname *padp, BOOL fPrefix) { HRESULT hres; BSTR bstrName = NULL; PDS_NAME_RESULTW pDsNameResult = NULL; DWORD dwError; INT i; USES_CONVERSION;
TraceEnter(TRACE_DS, "GetDisplayNameFromADsPath");
if ( !padp ) { hres = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_IADsPathname, (LPVOID*)&padp); FailGracefully(hres, "Failed to get IADsPathname interface"); } else { padp->AddRef(); }
if ( pszPath ) { hres = padp->Set((LPWSTR)pszPath, ADS_SETTYPE_FULL); if ( SUCCEEDED(hres) ) { hres = padp->Retrieve(ADS_FORMAT_X500_DN, &bstrName); FailGracefully(hres, "Failed to retreieve the X500 DN version"); } else { bstrName = SysAllocString(pszPath); if ( !bstrName ) ExitGracefully(hres, E_OUTOFMEMORY, "Failed to clone the string"); } } else { hres = padp->Retrieve(ADS_FORMAT_X500_DN, &bstrName); FailGracefully(hres, "Failed to retreieve the X500 DN version"); }
//
// try to syntatically crack the name we have
//
dwError = DsCrackNamesW(NULL, DS_NAME_FLAG_SYNTACTICAL_ONLY, DS_UNKNOWN_NAME, DS_CANONICAL_NAME, 1, &bstrName, &pDsNameResult);
if ( !CHECK_WIN32(dwError) || !CHECK_WIN32(pDsNameResult->rItems->status) ) ExitGracefully(hres, E_FAIL, "Failed to crack the name");
i = lstrlenW(pDsNameResult->rItems->pName)+(fPrefix ? CCH_NAME_PREFIX:0); if ( i > cchBuffer ) ExitGracefully(hres, E_FAIL, "Buffer too small");
*pszBuffer = L'\0';
if ( fPrefix ) StrCatW(pszBuffer, NAME_PREFIX);
StrCatW(pszBuffer, pDsNameResult->rItems->pName); if ( pszBuffer[i-1] == L'/' ) pszBuffer[i-1] = L'\0'; // trim trailing
hres = S_OK;
exit_gracefully:
if ( pDsNameResult ) DsFreeNameResultW(pDsNameResult);
DoRelease(padp); SysFreeString(bstrName);
TraceLeaveResult(hres); }
/*-----------------------------------------------------------------------------
/ CheckDsPolicy / ------------- / Check under HKCU,Software\Policies\Microsoft\Windows\Directory UI / for the given key/value which are assumed to be DWORD values. / / In: / pSubKey = sub key to be opened / = NULL / pValue = value name to be checked / / Out: / HRESULT /----------------------------------------------------------------------------*/ STDAPI_(DWORD) CheckDsPolicy(LPCTSTR pSubKey, LPCTSTR pValue) { DWORD dwFlag = 0; TCHAR szBuffer[MAX_PATH]; DWORD dwType, cbSize; HKEY hKey = NULL;
TraceEnter(TRACE_DS, "CheckDsPolicy");
// format the key, this is stored under HKCU, if the user gives a sub
// key then lets ensure that we look under that
StrCpy(szBuffer, TEXT("Software\\Policies\\Microsoft\\Windows\\Directory UI"));
if ( pSubKey ) { StrCat(szBuffer, TEXT("\\")); StrCat(szBuffer, pSubKey); }
Trace(TEXT("Directopy policy key is: %s"), szBuffer);
// Open the key and then query for the value, ensuring that the value is
// stored in a DWORD.
if ( CHECK_WIN32(RegOpenKey(HKEY_CURRENT_USER, szBuffer, &hKey)) ) { if ( (CHECK_WIN32(RegQueryValueEx(hKey, pValue, NULL, &dwType, NULL, &cbSize))) && (dwType == REG_DWORD) && (cbSize == SIZEOF(dwFlag)) ) { RegQueryValueEx(hKey, pValue, NULL, NULL, (LPBYTE)&dwFlag, &cbSize); Trace(TEXT("Policy value %s is %08x"), pValue, dwFlag); } }
if ( hKey ) RegCloseKey(hKey);
TraceLeaveValue(dwFlag); }
/*-----------------------------------------------------------------------------
/ ShowDirectoryUI / --------------- / Check to see if we should make the directory UI visible. This we do / by seeing if the machine and user is logged into a valid DS. / / RichardW added an new variable to the environement block "USERDNSDOMAIN" / which if present we will show the UI, otherwise not. This is not the / perfect solution, but works. / / In: / Out: / BOOL /----------------------------------------------------------------------------*/ STDAPI_(BOOL) ShowDirectoryUI(VOID) { BOOL fResult = FALSE;
TraceEnter(TRACE_DS, "ShowDirectoryUI");
if ( GetEnvironmentVariable(TEXT("USERDNSDOMAIN"), NULL, 0) ) { TraceMsg("USERDNSDOMAIN defined in environment, therefore returning TRUE"); fResult = TRUE; }
if ( !fResult ) { DSROLE_PRIMARY_DOMAIN_INFO_BASIC *pInfo; DWORD dwError = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (BYTE**)&pInfo); if ( CHECK_WIN32(dwError) ) { if ( pInfo->DomainNameDns ) { TraceMsg("Machine domain is DNS, therefore we assume DS is available"); fResult = TRUE; }
DsRoleFreeMemory(pInfo); } }
TraceLeaveResult(fResult); }
|