Leaked source code of windows server 2003
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.

547 lines
16 KiB

  1. #include "pch.h"
  2. #include <atlbase.h>
  3. #include "lm.h"
  4. #include "ntdsapi.h"
  5. #include "dsgetdc.h"
  6. #include "dsrole.h"
  7. #include "security.h"
  8. #pragma hdrstop
  9. /*-----------------------------------------------------------------------------
  10. / _StringFromSearchColumnArray
  11. / ----------------------------
  12. / Given an ADS_SEARCH_COLUMN attempt to get the string version of that
  13. / property.
  14. /
  15. / In:
  16. / pColumn -> ADS_SEARCH_COLUMN structure to be unpicked
  17. / i = index for the column to be fetched
  18. / pBuffer, pLen = updated accordingly
  19. /
  20. / Out:
  21. / HRESULT
  22. /----------------------------------------------------------------------------*/
  23. VOID _StringFromSearchColumnArray(PADS_SEARCH_COLUMN pColumn, INT i, LPWSTR pBuffer, UINT* pLen)
  24. {
  25. LPWSTR pValue;
  26. TCHAR szBuffer[MAX_PATH];
  27. TraceEnter(TRACE_DS, "_StringFromSearchColumnArray");
  28. switch ( pColumn->dwADsType )
  29. {
  30. case ADSTYPE_DN_STRING:
  31. case ADSTYPE_CASE_EXACT_STRING:
  32. case ADSTYPE_CASE_IGNORE_STRING:
  33. case ADSTYPE_PRINTABLE_STRING:
  34. case ADSTYPE_NUMERIC_STRING:
  35. PutStringElementW(pBuffer, pLen, pColumn->pADsValues[i].DNString);
  36. break;
  37. case ADSTYPE_BOOLEAN:
  38. PutStringElementW(pBuffer, pLen, (pColumn->pADsValues[i].Boolean) ? L"1":L"0");
  39. break;
  40. case ADSTYPE_INTEGER:
  41. wnsprintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("%d"), (INT)pColumn->pADsValues[i].Integer);
  42. PutStringElementW(pBuffer, pLen, szBuffer);
  43. break;
  44. case ADSTYPE_OCTET_STRING:
  45. {
  46. for ( ULONG j = 0; j < pColumn->pADsValues[i].OctetString.dwLength; j++)
  47. {
  48. wnsprintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("%02x"), ((LPBYTE)pColumn->pADsValues[i].OctetString.lpValue)[j]);
  49. PutStringElementW(pBuffer, pLen, szBuffer);
  50. }
  51. break;
  52. }
  53. case ADSTYPE_LARGE_INTEGER:
  54. wnsprintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("%e"), (double)pColumn->pADsValues[i].Integer);
  55. PutStringElementW(pBuffer, pLen, szBuffer);
  56. break;
  57. default:
  58. break;
  59. }
  60. TraceLeave();
  61. }
  62. /*-----------------------------------------------------------------------------
  63. / StringFromSearchColumn
  64. / ----------------------
  65. / Given an ADS_SEARCH_COLUMN attempt to get the string version of that
  66. / property.
  67. /
  68. / In:
  69. / pColumn -> ADS_SEARCH_COLUMN structure to be unpicked
  70. / pBuffer, pLen = the buffer to be filled (NULL accepted for both)
  71. /
  72. / Out:
  73. / HRESULT
  74. /----------------------------------------------------------------------------*/
  75. VOID _StringFromSearchColumn(PADS_SEARCH_COLUMN pColumn, LPWSTR pBuffer, UINT* pLen)
  76. {
  77. DWORD index;
  78. TraceEnter(TRACE_DS, "_StringFromSearchColumn");
  79. if ( pBuffer )
  80. pBuffer[0] = TEXT('\0');
  81. for ( index = 0 ; index != pColumn->dwNumValues; index++ )
  82. {
  83. if ( index > 0 )
  84. PutStringElementW(pBuffer, pLen, L", ");
  85. _StringFromSearchColumnArray(pColumn, index, pBuffer, pLen);
  86. }
  87. TraceLeave();
  88. }
  89. STDAPI StringFromSearchColumn(PADS_SEARCH_COLUMN pColumn, LPWSTR* ppBuffer)
  90. {
  91. HRESULT hr;
  92. UINT len = 0;
  93. TraceEnter(TRACE_DS, "StringFromSearchColumn");
  94. _StringFromSearchColumn(pColumn, NULL, &len);
  95. if ( len )
  96. {
  97. hr = LocalAllocStringLenW(ppBuffer, len);
  98. FailGracefully(hr, "Failed to allocate buffer for string");
  99. _StringFromSearchColumn(pColumn, *ppBuffer, NULL);
  100. Trace(TEXT("Resulting string: %s"), *ppBuffer);
  101. }
  102. hr = S_OK;
  103. exit_gracefully:
  104. TraceLeaveResult(hr);
  105. }
  106. /*-----------------------------------------------------------------------------
  107. / ObjectClassFromSearchColumn
  108. / ----------------------------
  109. / Given an ADS_SEARCH_COLUMN extract the object class from it. Object class
  110. / is a multi-value property therefore we need to try and find which element
  111. / is the real class name.
  112. /
  113. / All object have a base class "top", therefore we check the last element
  114. / of the property array, if that is "top" then we use the first element,
  115. / otherwise the last.
  116. /
  117. / In:
  118. / pBuffer, cchBuffer = buffer to be filled
  119. / pColumn -> ADS_SEARCH_COLUMN structure to be unpicked
  120. /
  121. / Out:
  122. / HRESULT
  123. /----------------------------------------------------------------------------*/
  124. STDAPI ObjectClassFromSearchColumn(PADS_SEARCH_COLUMN pColumn, LPWSTR* ppBuffer)
  125. {
  126. HRESULT hr;
  127. WCHAR szBuffer[MAX_PATH];
  128. ULONG i;
  129. TraceEnter(TRACE_DS, "ObjectClassFromSearchColumn");
  130. szBuffer[0] = TEXT('\0');
  131. _StringFromSearchColumnArray(pColumn, 0, szBuffer, NULL);
  132. LPCWSTR sTop=L"top";
  133. if ( !StrCmpIW(szBuffer, sTop) )
  134. {
  135. szBuffer[0] = TEXT('\0');
  136. _StringFromSearchColumnArray(pColumn, pColumn->dwNumValues-1, szBuffer, NULL);
  137. }
  138. hr = LocalAllocStringW(ppBuffer, szBuffer);
  139. FailGracefully(hr, "Failed to get alloc string buffer");
  140. // hr = S_OK; // success
  141. exit_gracefully:
  142. TraceLeaveResult(hr);
  143. }
  144. /*-----------------------------------------------------------------------------
  145. / GetArrayContents
  146. / ----------------
  147. / Given a VARIANT call the callback function with each element that we
  148. / see in it. If the VARIANT is an array then call the callback in the
  149. / correct order to give sensible results.
  150. /
  151. / In:
  152. / pVariant -> VARAINT to be unpacked
  153. / pCB, pData -> callback to be called for each item
  154. /
  155. / Out:
  156. / HRESULT
  157. /----------------------------------------------------------------------------*/
  158. INT _GetArrayCompareCB(LPVOID p1, LPVOID p2, LPARAM lParam)
  159. {
  160. HRESULT hr;
  161. WCHAR szBuffer[MAX_PATH];
  162. LONG i = 0;
  163. TraceEnter(TRACE_DS, "_GetArrayCompareCB");
  164. hr = GetStringElementW((BSTR)p1, 0, szBuffer, ARRAYSIZE(szBuffer));
  165. FailGracefully(hr, "Failed to get the position value");
  166. i = StringToDWORD(szBuffer);
  167. hr = GetStringElementW((BSTR)p2, 0, szBuffer, ARRAYSIZE(szBuffer));
  168. FailGracefully(hr, "Failed to get the position value");
  169. exit_gracefully:
  170. TraceLeaveValue(i - StringToDWORD(szBuffer));
  171. }
  172. STDAPI GetArrayContents(LPVARIANT pVariant, LPGETARRAYCONTENTCB pCB, LPVOID pData)
  173. {
  174. HRESULT hr;
  175. LONG arrayMin, arrayMax, i;
  176. WCHAR szBuffer[MAX_PATH];
  177. VARIANT varElement;
  178. HDPA hdpa = NULL;
  179. LPWSTR pValue;
  180. DWORD dwIndex;
  181. TraceEnter(TRACE_DS, "GetArrayContents");
  182. VariantInit(&varElement);
  183. switch ( V_VT(pVariant) )
  184. {
  185. case VT_BSTR:
  186. {
  187. hr = GetStringElementW(V_BSTR(pVariant), 0, szBuffer, ARRAYSIZE(szBuffer));
  188. FailGracefully(hr, "Failed to get the position value");
  189. dwIndex = StringToDWORD(szBuffer);
  190. pValue = wcschr(V_BSTR(pVariant), TEXT(',')); // NB: can return NULL (eg. not found)
  191. TraceAssert(pValue);
  192. if ( pValue )
  193. {
  194. hr = (*pCB)(dwIndex, pValue+1, pData);
  195. FailGracefully(hr, "Failed when calling with VT_BSTR");
  196. }
  197. break;
  198. }
  199. case VT_VARIANT | VT_ARRAY:
  200. {
  201. // read the VARIANTs into the DPA, don't worry about order just pick up
  202. // the contents of the array
  203. if ( (V_ARRAY(pVariant))->rgsabound[0].cElements < 1 )
  204. ExitGracefully(hr, E_FAIL, "Array less than 1 element in size");
  205. hr = SafeArrayGetLBound(V_ARRAY(pVariant), 1, (LONG*)&arrayMin);
  206. if ( SUCCEEDED(hr) )
  207. hr = SafeArrayGetUBound(V_ARRAY(pVariant), 1, (LONG*)&arrayMax);
  208. FailGracefully(hr, "Failed to the the array boundaries");
  209. hdpa = DPA_Create(arrayMax-arrayMin);
  210. if ( !hdpa )
  211. ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate DPA");
  212. Trace(TEXT("arrayMin %d, arrayMax %d"), arrayMin, arrayMax);
  213. for ( i = arrayMin; i <= arrayMax; i++ )
  214. {
  215. hr = SafeArrayGetElement(V_ARRAY(pVariant), (LONG*)&i, &varElement);
  216. FailGracefully(hr, "Failed to look up in variant array");
  217. if ( V_VT(&varElement) == VT_BSTR )
  218. {
  219. hr = StringDPA_AppendStringW(hdpa, V_BSTR(&varElement), NULL);
  220. FailGracefully(hr, "Failed to add the string to the DPA");
  221. }
  222. VariantClear(&varElement);
  223. }
  224. // now sort the DPA based on the first element. then pass them
  225. // out the the caller, skipping the leading character
  226. if ( DPA_GetPtrCount(hdpa) > 0 )
  227. {
  228. DPA_Sort(hdpa, _GetArrayCompareCB, NULL);
  229. for ( i = 0 ; i != DPA_GetPtrCount(hdpa); i++ )
  230. {
  231. hr = GetStringElementW(StringDPA_GetStringW(hdpa, i), 0, szBuffer, ARRAYSIZE(szBuffer));
  232. FailGracefully(hr, "Failed to get the position value");
  233. dwIndex = StringToDWORD(szBuffer);
  234. pValue = wcschr((BSTR)DPA_FastGetPtr(hdpa, i), TEXT(',')); // nb: can be null one exit
  235. TraceAssert(pValue);
  236. if ( pValue )
  237. {
  238. hr = (*pCB)(dwIndex, pValue+1, pData);
  239. FailGracefully(hr, "Failed when calling with VT_BSTR (from array)");
  240. }
  241. }
  242. }
  243. break;
  244. }
  245. case VT_EMPTY:
  246. {
  247. TraceMsg("VARIANT is empty");
  248. break;
  249. }
  250. }
  251. hr = S_OK;
  252. exit_gracefully:
  253. VariantClear(&varElement);
  254. StringDPA_Destroy(&hdpa);
  255. TraceLeaveResult(hr);
  256. }
  257. /*-----------------------------------------------------------------------------
  258. / GetDisplayNameFromADsPath
  259. / -------------------------
  260. / Convert the ADsPath to its display name with a suitable prefix.
  261. /
  262. / In:
  263. / pszPath -> ADsPath to be displayed
  264. / pszBuffer, cchBuffer = buffer to return the name into
  265. / padp -> IADsPathname for increased perf
  266. / fPrefix = add the NTDS:// or not.
  267. /
  268. / Out:
  269. / HRESULT
  270. /----------------------------------------------------------------------------*/
  271. #define NAME_PREFIX L"ntds://"
  272. #define CCH_NAME_PREFIX 7
  273. #define CHECK_WIN32(err) ((err) == ERROR_SUCCESS)
  274. STDAPI GetDisplayNameFromADsPath(LPCWSTR pszPath, LPWSTR pszBuffer, INT cchBuffer, IADsPathname *padp, BOOL fPrefix)
  275. {
  276. HRESULT hres;
  277. BSTR bstrName = NULL;
  278. PDS_NAME_RESULTW pDsNameResult = NULL;
  279. DWORD dwError;
  280. INT i;
  281. TraceEnter(TRACE_DS, "GetDisplayNameFromADsPath");
  282. if ( !padp )
  283. {
  284. hres = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IADsPathname, &padp));
  285. FailGracefully(hres, "Failed to get IADsPathname interface");
  286. }
  287. else
  288. {
  289. padp->AddRef();
  290. }
  291. if ( pszPath )
  292. {
  293. hres = padp->Set(CComBSTR(pszPath), ADS_SETTYPE_FULL);
  294. if ( SUCCEEDED(hres) )
  295. {
  296. hres = padp->Retrieve(ADS_FORMAT_X500_DN, &bstrName);
  297. FailGracefully(hres, "Failed to retreieve the X500 DN version");
  298. }
  299. else
  300. {
  301. bstrName = SysAllocString(pszPath);
  302. if ( !bstrName )
  303. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to clone the string");
  304. }
  305. }
  306. else
  307. {
  308. hres = padp->Retrieve(ADS_FORMAT_X500_DN, &bstrName);
  309. FailGracefully(hres, "Failed to retreieve the X500 DN version");
  310. }
  311. //
  312. // try to syntatically crack the name we have
  313. //
  314. dwError = DsCrackNamesW(NULL, DS_NAME_FLAG_SYNTACTICAL_ONLY, DS_UNKNOWN_NAME, DS_CANONICAL_NAME,
  315. 1, &bstrName, &pDsNameResult);
  316. if ( !CHECK_WIN32(dwError) || !CHECK_WIN32(pDsNameResult->rItems->status) )
  317. ExitGracefully(hres, E_FAIL, "Failed to crack the name");
  318. i = lstrlenW(pDsNameResult->rItems->pName)+(fPrefix ? CCH_NAME_PREFIX:0);
  319. if ( i > cchBuffer )
  320. ExitGracefully(hres, E_FAIL, "Buffer too small");
  321. *pszBuffer = L'\0';
  322. if ( fPrefix )
  323. StrCatBuffW(pszBuffer, NAME_PREFIX, cchBuffer);
  324. StrCatBuffW(pszBuffer, pDsNameResult->rItems->pName, cchBuffer);
  325. if ( pszBuffer[i-1] == L'/' )
  326. pszBuffer[i-1] = L'\0'; // trim trailing
  327. hres = S_OK;
  328. exit_gracefully:
  329. if ( pDsNameResult )
  330. DsFreeNameResultW(pDsNameResult);
  331. DoRelease(padp);
  332. SysFreeString(bstrName);
  333. TraceLeaveResult(hres);
  334. }
  335. /*-----------------------------------------------------------------------------
  336. / CheckDsPolicy
  337. / -------------
  338. / Check under HKCU,Software\Policies\Microsoft\Windows\Directory UI
  339. / for the given key/value which are assumed to be DWORD values.
  340. /
  341. / In:
  342. / pSubKey = sub key to be opened / = NULL
  343. / pValue = value name to be checked
  344. /
  345. / Out:
  346. / HRESULT
  347. /----------------------------------------------------------------------------*/
  348. STDAPI_(DWORD) CheckDsPolicy(LPCTSTR pSubKey, LPCTSTR pValue)
  349. {
  350. DWORD dwFlag = 0;
  351. TCHAR szBuffer[MAX_PATH];
  352. DWORD dwType, cbSize;
  353. HKEY hKey = NULL;
  354. TraceEnter(TRACE_DS, "CheckDsPolicy");
  355. // format the key, this is stored under HKCU, if the user gives a sub
  356. // key then lets ensure that we look under that
  357. StrCpyN(szBuffer, TEXT("Software\\Policies\\Microsoft\\Windows\\Directory UI"), ARRAYSIZE(szBuffer));
  358. if ( pSubKey )
  359. {
  360. StrCatBuff(szBuffer, TEXT("\\"), ARRAYSIZE(szBuffer));
  361. StrCatBuff(szBuffer, pSubKey, ARRAYSIZE(szBuffer));
  362. }
  363. Trace(TEXT("Directory policy key is: %s"), szBuffer);
  364. // Open the key and then query for the value, ensuring that the value is
  365. // stored in a DWORD.
  366. if ( CHECK_WIN32(RegOpenKeyEx(HKEY_CURRENT_USER, szBuffer, 0, KEY_READ, &hKey)) )
  367. {
  368. if ( (CHECK_WIN32(RegQueryValueEx(hKey, pValue, NULL, &dwType, NULL, &cbSize))) &&
  369. (dwType == REG_DWORD) &&
  370. (cbSize == SIZEOF(dwFlag)) )
  371. {
  372. // already checked the type and size of the reg value above
  373. RegQueryValueEx(hKey, pValue, NULL, NULL, (LPBYTE)&dwFlag, &cbSize);
  374. Trace(TEXT("Policy value %s is %08x"), pValue, dwFlag);
  375. }
  376. }
  377. if ( hKey )
  378. RegCloseKey(hKey);
  379. TraceLeaveValue(dwFlag);
  380. }
  381. /*-----------------------------------------------------------------------------
  382. / ShowDirectoryUI
  383. / ---------------
  384. / Check to see if we should make the directory UI visible. This we do
  385. / by seeing if the machine and user is logged into a valid DS.
  386. /
  387. / RichardW added an new variable to the environement block "USERDNSDOMAIN"
  388. / which if present we will show the UI, otherwise not. This is not the
  389. / perfect solution, but works.
  390. /
  391. / In:
  392. / Out:
  393. / BOOL
  394. /----------------------------------------------------------------------------*/
  395. STDAPI_(BOOL) ShowDirectoryUI(VOID)
  396. {
  397. BOOL fResult = FALSE;
  398. TraceEnter(TRACE_DS, "ShowDirectoryUI");
  399. if ( GetEnvironmentVariable(TEXT("USERDNSDOMAIN"), NULL, 0) )
  400. {
  401. TraceMsg("USERDNSDOMAIN defined in environment, therefore returning TRUE");
  402. fResult = TRUE;
  403. }
  404. if ( !fResult )
  405. {
  406. DSROLE_PRIMARY_DOMAIN_INFO_BASIC *pInfo;
  407. DWORD dwError = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (BYTE**)&pInfo);
  408. if ( CHECK_WIN32(dwError) )
  409. {
  410. if ( pInfo->DomainNameDns )
  411. {
  412. TraceMsg("Machine domain is DNS, therefore we assume DS is available");
  413. fResult = TRUE;
  414. }
  415. DsRoleFreeMemory(pInfo);
  416. }
  417. }
  418. return fResult;
  419. }
  420. // call either ADsOpenObject or AdminToolsOpenObject based on the simple authenticate flag.
  421. HRESULT OpenDsObject(LPCWSTR pszPath, LPCWSTR pszUserName, LPCWSTR pszPassword, REFIID riid, void **ppv, BOOL fNotSecure, BOOL fDontSignSeal)
  422. {
  423. static DWORD additionalFlags = GetADsOpenObjectFlags();
  424. DWORD dwFlags = additionalFlags;
  425. if (!fNotSecure)
  426. dwFlags |= ADS_SECURE_AUTHENTICATION;
  427. if (fDontSignSeal)
  428. dwFlags &= ~(ADS_USE_SIGNING | ADS_USE_SEALING);
  429. return ADsOpenObject(pszPath, pszUserName, pszPassword, dwFlags, riid, ppv);
  430. }