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.

532 lines
16 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 2000
  5. //
  6. // File: querybld.cpp
  7. //
  8. // Contents: Contains implementatin of functions to build query.
  9. //
  10. // History: 24-Sep-2000 Hiteshr Created
  11. //
  12. //
  13. //--------------------------------------------------------------------------
  14. #include "pch.h"
  15. #include "cstrings.h"
  16. #include "querytable.h"
  17. #include "usage.h"
  18. #include "querybld.h"
  19. #include <lmaccess.h> // UF_ACCOUNTDISABLE and UF_DONT_EXPIRE_PASSWD
  20. #include <ntldap.h> // LDAP_MATCHING_RULE_BIT_AND_W
  21. static const LPWSTR g_szUserAccountCtrlQuery = L"(userAccountControl:" LDAP_MATCHING_RULE_BIT_AND_W L":=%u)";
  22. static const LPWSTR g_szServerIsGCQuery = L"(&(objectCategory=NTDS-DSA)(options:" LDAP_MATCHING_RULE_BIT_AND_W L":=1))";
  23. static const LPWSTR g_szCommonQueryFormat= L"(%s=%s)";
  24. //+--------------------------------------------------------------------------
  25. //
  26. // Function: LdapEscape
  27. //
  28. // Synopsis: Escape the characters in *[pszInput] as required by
  29. // RFC 2254.
  30. //
  31. // Arguments: [pszInput] - string to escape
  32. //
  33. // History: 06-23-2000 DavidMun Created
  34. //
  35. // Notes: RFC 2254
  36. //
  37. // If a value should contain any of the following characters
  38. //
  39. // Character ASCII value
  40. // ---------------------------
  41. // * 0x2a
  42. // ( 0x28
  43. // ) 0x29
  44. // \ 0x5c
  45. // NUL 0x00
  46. //
  47. // the character must be encoded as the backslash '\'
  48. // character (ASCII 0x5c) followed by the two hexadecimal
  49. // digits representing the ASCII value of the encoded
  50. // character. The case of the two hexadecimal digits is not
  51. // significant.
  52. //
  53. //---------------------------------------------------------------------------
  54. bool
  55. LdapEscape(IN LPCWSTR pszInput, OUT CComBSTR& strFilter)
  56. {
  57. if(!pszInput)
  58. return FALSE;
  59. int iLen = (int)wcslen(pszInput);
  60. for( int i = 0; i < iLen; ++i)
  61. {
  62. switch (*(pszInput+i))
  63. {
  64. case L'(':
  65. strFilter += L"\\28";
  66. break;
  67. case L')':
  68. strFilter += L"\\29";
  69. break;
  70. case L'\\':
  71. if( i + 1 < iLen )
  72. {
  73. // \\ is treated as '\'
  74. switch (*(pszInput+i + 1))
  75. {
  76. case L'\\':
  77. strFilter += L"\\5c";
  78. i++;
  79. break;
  80. // \* is treated as '*'
  81. case L'*':
  82. strFilter += L"\\2a";
  83. i++;
  84. break;
  85. default:
  86. // \X is treated \X only
  87. strFilter += L"\\5c";
  88. break;
  89. }
  90. }
  91. else
  92. strFilter += L"\\5c";
  93. break;
  94. default:
  95. strFilter.Append(pszInput+i,1);
  96. break;
  97. }
  98. }
  99. return TRUE;
  100. }
  101. HRESULT MakeQuery(IN LPCWSTR pszAttrName,
  102. IN LPCWSTR pszCommandLineFilter,
  103. OUT CComBSTR& strFilter)
  104. {
  105. ENTER_FUNCTION_HR(LEVEL3_LOGGING, MakeQuery, hr);
  106. if(!pszAttrName || !pszCommandLineFilter)
  107. {
  108. ASSERT(FALSE);
  109. hr = E_INVALIDARG;
  110. return hr;
  111. }
  112. CComBSTR strEscapedCLFilter;
  113. LdapEscape(pszCommandLineFilter,strEscapedCLFilter);
  114. strFilter = L"(";
  115. strFilter += pszAttrName;
  116. strFilter += L"=";
  117. strFilter += strEscapedCLFilter;
  118. strFilter += L")";
  119. return hr;
  120. }
  121. //+--------------------------------------------------------------------------
  122. //
  123. // Function: CommonFilterFunc
  124. //
  125. // Synopsis: This function takes the input filter from the commandline
  126. // and converts it into ldapfilter.
  127. // For ex -user (ab* | bc*) is converted to |(cn=ab*)(cn=bc*)
  128. // The pEntry->pszName given the attribute name to use in
  129. // filter( cn in above example).
  130. //
  131. // Arguments: [pRecord - IN] : the command line argument structure used
  132. // to retrieve the filter entered by user
  133. // [pObjectEntry - IN] : pointer to the DSQUERY_ATTR_TABLE_ENTRY
  134. // which has info on attribute corresponding
  135. // switch in pRecord
  136. // [pVoid - IN] :Not used.
  137. // [strFilter - OUT] :Contains the output filter.
  138. // Returns: HRESULT : S_OK if everything succeeded
  139. // E_INVALIDARG if the object entry wasn't found
  140. // Anything else is a failure code from an ADSI call
  141. //
  142. // History: 25-Sep-2000 hiteshr Created
  143. //
  144. //---------------------------------------------------------------------------
  145. HRESULT CommonFilterFunc(IN DSQUERY_ATTR_TABLE_ENTRY *pEntry,
  146. IN ARG_RECORD* pRecord,
  147. IN PVOID ,
  148. OUT CComBSTR& strFilter)
  149. {
  150. ENTER_FUNCTION_HR(LEVEL3_LOGGING, CommonFilterFunc, hr);
  151. //validate input
  152. if( !pEntry || !pRecord
  153. //validate DSQUERY_ATTR_TABLE_ENTRY entry
  154. || !pEntry->pszName || !pEntry->nAttributeID
  155. //validate pRecord
  156. || !pRecord->bDefined || !pRecord->strValue)
  157. {
  158. ASSERT(FALSE);
  159. hr = E_INVALIDARG;
  160. return hr;
  161. }
  162. //Make Query
  163. hr = MakeQuery(pEntry->pszName,
  164. pRecord->strValue,
  165. strFilter);
  166. DEBUG_OUTPUT(LEVEL3_LOGGING, L"filter = %s", strFilter);
  167. return hr;
  168. }
  169. //+--------------------------------------------------------------------------
  170. //
  171. // Function: StarFilterFunc
  172. //
  173. // Synopsis: Filter Function for dsquery *. It returns the value of
  174. // -filter flag.
  175. //
  176. // Arguments: [pRecord - IN] : the command line argument structure used
  177. // to retrieve the filter entered by user
  178. // [pObjectEntry - IN] : pointer to the DSQUERY_ATTR_TABLE_ENTRY
  179. // which has info on attribute corresponding
  180. // switch in pRecord
  181. // [pVoid - IN] :Not used.
  182. // [strFilter - OUT] :Contains the output filter.
  183. // Returns: HRESULT : S_OK if everything succeeded
  184. // E_INVALIDARG if the object entry wasn't found
  185. // Anything else is a failure code from an ADSI call
  186. //
  187. // History: 25-Sep-2000 hiteshr Created
  188. //
  189. //---------------------------------------------------------------------------
  190. HRESULT StarFilterFunc(IN DSQUERY_ATTR_TABLE_ENTRY *pEntry,
  191. IN ARG_RECORD* pRecord,
  192. IN PVOID ,
  193. OUT CComBSTR& strFilter)
  194. {
  195. ENTER_FUNCTION_HR(LEVEL3_LOGGING, StarFilterFunc, hr);
  196. //validate input
  197. if(!pEntry || !pRecord
  198. //validate DSQUERY_ATTR_TABLE_ENTRY entry
  199. || !pEntry->nAttributeID
  200. //validate pRecord
  201. || !pRecord->bDefined || !pRecord->strValue)
  202. {
  203. ASSERT(FALSE);
  204. hr = E_INVALIDARG;
  205. return hr;
  206. }
  207. strFilter = pRecord->strValue;
  208. DEBUG_OUTPUT(LEVEL3_LOGGING, L"filter = %s", strFilter);
  209. return hr;
  210. }
  211. //+--------------------------------------------------------------------------
  212. //
  213. // Function: InactiveFilterFunc
  214. //
  215. // Synopsis: Filter Function for account disabled query.
  216. //
  217. // Arguments: [pRecord - IN] : Not Used
  218. // [pObjectEntry - IN] : Not Used
  219. // [pVoid - IN] :Not used.
  220. // [strFilter - OUT] :Contains the output filter.
  221. // Returns: HRESULT : S_OK if everything succeeded
  222. // E_INVALIDARG if the object entry wasn't found
  223. // Anything else is a failure code from an ADSI call
  224. //
  225. // History: 25-Sep-2000 hiteshr Created
  226. //
  227. //---------------------------------------------------------------------------
  228. HRESULT InactiveFilterFunc(IN DSQUERY_ATTR_TABLE_ENTRY *pEntry,
  229. IN ARG_RECORD* pRecord,
  230. IN PVOID ,
  231. OUT CComBSTR& strFilter)
  232. {
  233. ENTER_FUNCTION_HR(LEVEL3_LOGGING, InactiveFilterFunc, hr);
  234. if( !pEntry || !pRecord || !pRecord->bDefined )
  235. {
  236. hr = E_INVALIDARG;
  237. return hr;
  238. }
  239. //
  240. //Unit at commandline is Week
  241. //
  242. int nDays = pRecord->nValue * 7;
  243. FILETIME ftCurrent;
  244. ::GetSystemTimeAsFileTime(&ftCurrent);
  245. LARGE_INTEGER li;
  246. li.LowPart = ftCurrent.dwLowDateTime;
  247. li.HighPart = ftCurrent.dwHighDateTime;
  248. //
  249. //Get the number of days since the reference time
  250. //
  251. int nDaysSince1600 = (int)(li.QuadPart/(((LONGLONG) (24 * 60) * 60) * 10000000));
  252. if(nDaysSince1600 < nDays)
  253. {
  254. hr = E_INVALIDARG;
  255. return hr;
  256. }
  257. li.QuadPart -= ((((LONGLONG)nDays * 24) * 60) * 60) * 10000000;
  258. CComBSTR strTime;
  259. litow(li, strTime);
  260. WCHAR buffer[256];
  261. wsprintf(buffer,L"(lastLogonTimestamp<=%s)",strTime);
  262. strFilter = buffer;
  263. DEBUG_OUTPUT(LEVEL3_LOGGING, L"filter = %s", strFilter);
  264. return hr;
  265. }
  266. //+--------------------------------------------------------------------------
  267. //
  268. // Function: StalepwdFilterFunc
  269. //
  270. // Synopsis: Filter Function for Stale Password query.
  271. //
  272. // Arguments: [pRecord - IN] : Not Used
  273. // [pObjectEntry - IN] : Not Used
  274. // [pVoid - IN] :Not used.
  275. // [strFilter - OUT] :Contains the output filter.
  276. // Returns: HRESULT : S_OK if everything succeeded
  277. // E_INVALIDARG if the object entry wasn't found
  278. // Anything else is a failure code from an ADSI call
  279. //
  280. // History: 25-Sep-2000 hiteshr Created
  281. //
  282. //---------------------------------------------------------------------------
  283. HRESULT StalepwdFilterFunc(IN DSQUERY_ATTR_TABLE_ENTRY *pEntry,
  284. IN ARG_RECORD* pRecord,
  285. IN PVOID ,
  286. OUT CComBSTR& strFilter)
  287. {
  288. ENTER_FUNCTION_HR(LEVEL3_LOGGING, StalepwdFilterFunc, hr);
  289. if( !pEntry || !pRecord || !pRecord->bDefined )
  290. {
  291. hr = E_INVALIDARG;
  292. return hr;
  293. }
  294. int nDays = pRecord->nValue;
  295. FILETIME ftCurrent;
  296. ::GetSystemTimeAsFileTime(&ftCurrent);
  297. LARGE_INTEGER li;
  298. li.LowPart = ftCurrent.dwLowDateTime;
  299. li.HighPart = ftCurrent.dwHighDateTime;
  300. //
  301. //Get the number of days since the reference time
  302. //
  303. int nDaysSince1600 = (int)(li.QuadPart/(((LONGLONG) (24 * 60) * 60) * 10000000));
  304. if(nDaysSince1600 < nDays)
  305. {
  306. hr = E_INVALIDARG;
  307. return hr;
  308. }
  309. li.QuadPart -= ((((ULONGLONG)nDays * 24) * 60) * 60) * 10000000;
  310. CComBSTR strTime;
  311. litow(li, strTime);
  312. WCHAR buffer[256];
  313. wsprintf(buffer,L"(pwdLastSet<=%s)",strTime);
  314. strFilter = buffer;
  315. DEBUG_OUTPUT(LEVEL3_LOGGING, L"filter = %s", strFilter);
  316. return hr;
  317. }
  318. //+--------------------------------------------------------------------------
  319. //
  320. // Function: DisabledFilterFunc
  321. //
  322. // Synopsis: Filter Function for account disabled query.
  323. //
  324. // Arguments: [pRecord - IN] : Not Used
  325. // [pObjectEntry - IN] : Not Used
  326. // [pVoid - IN] :Not used.
  327. // [strFilter - OUT] :Contains the output filter.
  328. // Returns: HRESULT : S_OK if everything succeeded
  329. // E_INVALIDARG if the object entry wasn't found
  330. // Anything else is a failure code from an ADSI call
  331. //
  332. // History: 25-Sep-2000 hiteshr Created
  333. //
  334. //---------------------------------------------------------------------------
  335. HRESULT DisabledFilterFunc(IN DSQUERY_ATTR_TABLE_ENTRY *,
  336. IN ARG_RECORD* ,
  337. IN PVOID ,
  338. OUT CComBSTR& strFilter)
  339. {
  340. ENTER_FUNCTION_HR(LEVEL3_LOGGING, DisabledFilterFunc, hr);
  341. WCHAR buffer[256]; //This is long enough
  342. wsprintf(buffer, g_szUserAccountCtrlQuery,UF_ACCOUNTDISABLE);
  343. strFilter = buffer;
  344. DEBUG_OUTPUT(LEVEL3_LOGGING, L"filter = %s", strFilter);
  345. return hr;
  346. }
  347. //+--------------------------------------------------------------------------
  348. //
  349. // Function: SubnetSiteFilterFunc
  350. //
  351. // Synopsis: Filter Function for -site switch in dsquery subnet.
  352. //
  353. // Arguments: [pEntry - IN] : Not Used
  354. // [pRecord - IN] : Command Line value supplied by user
  355. // [pVoid - IN] : suffix for the siteobject attribute.
  356. // [strFilter - OUT] :Contains the output filter.
  357. // Returns: HRESULT : S_OK if everything succeeded
  358. //
  359. // History: 24-April-2001 hiteshr Created
  360. //
  361. //---------------------------------------------------------------------------
  362. HRESULT SubnetSiteFilterFunc(IN DSQUERY_ATTR_TABLE_ENTRY *,
  363. IN ARG_RECORD* pRecord,
  364. IN PVOID pParam,
  365. OUT CComBSTR& strFilter)
  366. {
  367. ENTER_FUNCTION_HR(LEVEL3_LOGGING, InactiveFilterFunc, hr);
  368. if( !pRecord || !pRecord->bDefined || !pParam)
  369. {
  370. hr = E_INVALIDARG;
  371. return hr;
  372. }
  373. CComBSTR strEscapedCLFilter;
  374. LdapEscape(pRecord->strValue,strEscapedCLFilter);
  375. strFilter = L"(siteobject=cn=";
  376. strFilter += strEscapedCLFilter;
  377. strFilter += L",";
  378. strFilter += *(static_cast<BSTR*>(pParam));
  379. strFilter += L")";
  380. return hr;
  381. }
  382. //+--------------------------------------------------------------------------
  383. //
  384. // Function: BuildQueryFilter
  385. //
  386. // Synopsis: This function builds the LDAP query filter for given object type.
  387. //
  388. // Arguments: [pCommandArgs - IN] :the command line argument structure used
  389. // to retrieve the values of switches
  390. // [pObjectEntry - IN] :Contains info about the object type
  391. // [pParam -IN] :This value is passed to filter function.
  392. // [strLDAPFilter - OUT] :Contains the output filter.
  393. // Returns: HRESULT : S_OK if everything succeeded
  394. // E_INVALIDARG if the object entry wasn't found
  395. // Anything else is a failure code from an ADSI call
  396. //
  397. // History: 25-Sep-2000 hiteshr Created
  398. //
  399. //---------------------------------------------------------------------------
  400. HRESULT BuildQueryFilter(PARG_RECORD pCommandArgs,
  401. PDSQueryObjectTableEntry pObjectEntry,
  402. PVOID pParam,
  403. CComBSTR& strLDAPFilter)
  404. {
  405. ENTER_FUNCTION_HR(LEVEL3_LOGGING, BuildQueryFilter, hr);
  406. if( !pCommandArgs || !pObjectEntry )
  407. {
  408. ASSERT(FALSE);
  409. hr = E_INVALIDARG;
  410. return hr;
  411. }
  412. DSQUERY_ATTR_TABLE_ENTRY** pAttributeTable;
  413. DWORD dwAttributeCount;
  414. pAttributeTable = pObjectEntry->pAttributeTable;
  415. dwAttributeCount = pObjectEntry->dwAttributeCount;
  416. if( !pAttributeTable || !dwAttributeCount )
  417. {
  418. hr = E_INVALIDARG;
  419. return hr;
  420. }
  421. BOOL bUseDefaultFilter = TRUE;
  422. CComBSTR strFilter;
  423. for( UINT i = 0; i < dwAttributeCount; ++i )
  424. {
  425. if(pCommandArgs[pAttributeTable[i]->nAttributeID].bDefined)
  426. {
  427. bUseDefaultFilter = FALSE;
  428. CComBSTR strLocalFilter;
  429. hr = pAttributeTable[i]->pMakeFilterFunc(pAttributeTable[i],
  430. pCommandArgs + pAttributeTable[i]->nAttributeID,
  431. pParam,
  432. strLocalFilter);
  433. if(FAILED(hr))
  434. return hr;
  435. strFilter += strLocalFilter;
  436. DEBUG_OUTPUT(FULL_LOGGING, L"Current filter = %s", strFilter);
  437. }
  438. }
  439. //
  440. //If none of the commandline filter switches are specified, use
  441. //default filter
  442. //
  443. strLDAPFilter = L"(";
  444. if(bUseDefaultFilter)
  445. {
  446. strLDAPFilter += pObjectEntry->pszDefaultFilter;
  447. }
  448. else
  449. {
  450. if(pObjectEntry->pszPrefixFilter)
  451. {
  452. strLDAPFilter += pObjectEntry->pszPrefixFilter;
  453. strLDAPFilter += strFilter;
  454. }
  455. else
  456. strLDAPFilter += strFilter;
  457. }
  458. strLDAPFilter += L")";
  459. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ldapfilter = %s", strLDAPFilter);
  460. return hr;
  461. }