windows-nt/Source/XPSP1/NT/shell/ext/dsui/dsuiext/ds.cpp
2020-09-26 16:20:57 +08:00

544 lines
16 KiB
C++

#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);
}