Source code of Windows XP (NT5)
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.

543 lines
16 KiB

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