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.

548 lines
15 KiB

  1. #include "stdafx.h"
  2. #include <winldap.h>
  3. #include "Common.hpp"
  4. #include "UString.hpp"
  5. #include "EaLen.hpp"
  6. #include "exldap.h"
  7. CLdapConnection::CLdapConnection()
  8. {
  9. m_exchServer[0] = 0;
  10. m_LD = NULL;
  11. m_port = LDAP_PORT;
  12. m_bUseSSL = FALSE;
  13. // try to dynamically load the LDAP DLL
  14. m_hDll = LoadLibrary(L"wldap32.dll");
  15. ldap_open = NULL;
  16. ldap_parse_result = NULL;
  17. ldap_parse_page_control = NULL;
  18. ldap_controls_free = NULL;
  19. ber_bvfree = NULL;
  20. ldap_first_entry = NULL;
  21. ldap_next_entry = NULL;
  22. ldap_value_free = NULL;
  23. ldap_get_values = NULL;
  24. ldap_create_page_control = NULL;
  25. ldap_search_ext_s = NULL;
  26. ldap_count_entries = NULL;
  27. ldap_msgfree = NULL;
  28. ldap_modify_s = NULL;
  29. LdapGetLastError = NULL;
  30. ldap_bind_sW = NULL;
  31. ldap_unbind = NULL;
  32. ldap_get_option = NULL;
  33. ldap_set_option = NULL;
  34. LdapMapErrorToWin32 = NULL;
  35. if ( m_hDll )
  36. {
  37. ldap_open = (LDAP_OPEN *)GetProcAddress(m_hDll,"ldap_openW");
  38. ldap_parse_result = (LDAP_PARSE_RESULT *)GetProcAddress(m_hDll,"ldap_parse_resultW");
  39. ldap_parse_page_control = (LDAP_PARSE_PAGE_CONTROL*)GetProcAddress(m_hDll,"ldap_parse_page_controlW");
  40. ldap_controls_free = (LDAP_CONTROLS_FREE*)GetProcAddress(m_hDll,"ldap_controls_freeW");
  41. ber_bvfree = (BER_BVFREE*)GetProcAddress(m_hDll,"ber_bvfree");
  42. ldap_first_entry = (LDAP_FIRST_ENTRY*)GetProcAddress(m_hDll,"ldap_first_entry");
  43. ldap_next_entry = (LDAP_NEXT_ENTRY*)GetProcAddress(m_hDll,"ldap_next_entry");
  44. ldap_value_free = (LDAP_VALUE_FREE*)GetProcAddress(m_hDll,"ldap_value_freeW");
  45. ldap_get_values = (LDAP_GET_VALUES*)GetProcAddress(m_hDll,"ldap_get_valuesW");
  46. ldap_create_page_control = (LDAP_CREATE_PAGE_CONTROL*)GetProcAddress(m_hDll,"ldap_create_page_controlW");
  47. ldap_search_ext_s = (LDAP_SEARCH_EXT_S*)GetProcAddress(m_hDll,"ldap_search_ext_sW");
  48. ldap_count_entries = (LDAP_COUNT_ENTRIES*)GetProcAddress(m_hDll,"ldap_count_entries");
  49. ldap_msgfree = (LDAP_MSGFREE*)GetProcAddress(m_hDll,"ldap_msgfree");
  50. ldap_modify_s = (LDAP_MODIFY_S*)GetProcAddress(m_hDll,"ldap_modify_sW");
  51. LdapGetLastError = (LDAPGETLASTERROR*)GetProcAddress(m_hDll,"LdapGetLastError");
  52. ldap_bind_sW = (LDAP_BIND*)GetProcAddress(m_hDll,"ldap_bind_sW");
  53. ldap_unbind = (LDAP_UNBIND*)GetProcAddress(m_hDll,"ldap_unbind");
  54. ldap_get_option = (LDAP_GET_OPTION*)GetProcAddress(m_hDll,"ldap_get_option");
  55. ldap_set_option = (LDAP_SET_OPTION*)GetProcAddress(m_hDll,"ldap_set_option");
  56. LdapMapErrorToWin32 = (LDAPMAPERRORTOWIN32*)GetProcAddress(m_hDll,"LdapMapErrorToWin32");
  57. }
  58. }
  59. CLdapConnection::~CLdapConnection()
  60. {
  61. Close();
  62. if ( m_hDll )
  63. {
  64. FreeLibrary(m_hDll);
  65. ldap_open = NULL;
  66. ldap_parse_result = NULL;
  67. ldap_parse_page_control = NULL;
  68. ldap_controls_free = NULL;
  69. ber_bvfree = NULL;
  70. ldap_first_entry = NULL;
  71. ldap_next_entry = NULL;
  72. ldap_value_free = NULL;
  73. ldap_get_values = NULL;
  74. ldap_create_page_control = NULL;
  75. ldap_search_ext_s = NULL;
  76. ldap_count_entries = NULL;
  77. ldap_msgfree = NULL;
  78. ldap_modify_s = NULL;
  79. LdapGetLastError = NULL;
  80. ldap_bind_sW = NULL;
  81. ldap_unbind = NULL;
  82. LdapMapErrorToWin32 = NULL;
  83. }
  84. }
  85. DWORD CLdapConnection::Connect(WCHAR const * server, ULONG port = LDAP_PORT)
  86. {
  87. DWORD rc = 0;
  88. safecopy(m_exchServer,server);
  89. m_LD = CLdapConnection::ldap_open(m_exchServer,LDAP_SSL_PORT);
  90. if (! m_LD )
  91. {
  92. // try the non-SSL port
  93. m_LD = ldap_open(m_exchServer,port);
  94. }
  95. if ( ! m_LD )
  96. {
  97. rc = CLdapConnection::LdapGetLastError();
  98. }
  99. else
  100. {
  101. ULONG flags = 0;
  102. // set version to 3
  103. rc = ldap_get_option(m_LD, LDAP_OPT_VERSION,&flags);
  104. if ( ! rc )
  105. {
  106. flags = LDAP_VERSION3;
  107. rc = ldap_set_option(m_LD,LDAP_OPT_VERSION, &flags);
  108. }
  109. if (! rc )
  110. {
  111. if ( *m_credentials )
  112. {
  113. rc = CLdapConnection::ldap_bind_s(m_LD,m_credentials,m_password,LDAP_AUTH_SIMPLE);
  114. if ( rc )
  115. {
  116. rc = CLdapConnection::ldap_bind_s(m_LD,NULL,NULL,LDAP_AUTH_NTLM);
  117. }
  118. }
  119. else
  120. {
  121. rc = CLdapConnection::ldap_bind_s(m_LD,NULL,NULL,LDAP_AUTH_NTLM);
  122. }
  123. }
  124. if ( rc )
  125. {
  126. rc = CLdapConnection::LdapMapErrorToWin32(rc);
  127. }
  128. }
  129. return rc;
  130. }
  131. void CLdapConnection::Close()
  132. {
  133. if ( m_LD )
  134. {
  135. CLdapConnection::ldap_unbind(m_LD);
  136. m_LD = NULL;
  137. }
  138. }
  139. DWORD CLdapConnection::UpdateSimpleStringValue(WCHAR const * dn, WCHAR const * property, WCHAR const * value)
  140. {
  141. DWORD rc = ERROR_NOT_FOUND;
  142. if ( m_LD )
  143. {
  144. LDAPMod * mods[2];
  145. LDAPMod mod1;
  146. WCHAR * strVals[] = { const_cast<WCHAR*>(value),NULL };
  147. mods[0] = &mod1;
  148. mods[0]->mod_op = LDAP_MOD_REPLACE;
  149. mods[0]->mod_type = const_cast<WCHAR*>(property);
  150. mods[0]->mod_vals.modv_strvals = strVals;
  151. mods[1] = NULL;
  152. rc = CLdapConnection::ldap_modify_s(m_LD,const_cast<WCHAR*>(dn),mods);
  153. if ( rc )
  154. {
  155. rc = CLdapConnection::LdapMapErrorToWin32(rc);
  156. }
  157. }
  158. return rc;
  159. }
  160. // Helper function for SidToString - converts one BYTE of the SID into a string representation
  161. void
  162. CLdapConnection::AddByteToString(
  163. WCHAR ** string, // i/o- pointer to current location in string
  164. BYTE value // in - value (from SID) to add to the string
  165. )
  166. {
  167. WCHAR hi,
  168. lo;
  169. BYTE hiVal,
  170. loVal;
  171. loVal = value & 0x0F;
  172. hiVal = value & 0xF0;
  173. hiVal = hiVal >> 4;
  174. if ( hiVal < 10 )
  175. {
  176. hi=L'0' + hiVal;
  177. }
  178. else
  179. {
  180. hi=L'A' + ( hiVal - 10 );
  181. }
  182. if ( loVal < 10 )
  183. {
  184. lo=L'0' + loVal;
  185. }
  186. else
  187. {
  188. lo=L'A' + (loVal - 10 );
  189. }
  190. swprintf(*string,L"%c%c",hi,lo);
  191. *string+=2;
  192. }
  193. BYTE // ret- value for the digit, or 0 if value is not a valid hex digit
  194. CLdapConnection::HexValue(
  195. WCHAR value // in - character representing a hex digit
  196. )
  197. {
  198. BYTE val = 0;
  199. switch ( toupper((char)value) )
  200. {
  201. case L'1': val = 1; break;
  202. case L'2': val = 2; break;
  203. case L'3': val = 3; break;
  204. case L'4': val = 4; break;
  205. case L'5': val = 5; break;
  206. case L'6': val = 6; break;
  207. case L'7': val = 7; break;
  208. case L'8': val = 8; break;
  209. case L'9': val = 9; break;
  210. case L'A': val = 0xA; break;
  211. case L'B': val = 0xB; break;
  212. case L'C': val = 0xC; break;
  213. case L'D': val = 0xD; break;
  214. case L'E': val = 0xE; break;
  215. case L'F': val = 0xF; break;
  216. }
  217. return val;
  218. }
  219. BOOL // ret- 0=success, or ERROR_INSUFFICIENT_BUFFER
  220. CLdapConnection::BytesToString(
  221. BYTE * pBytes, // in - SID to represent as a string
  222. WCHAR * sidString, // out- buffer that will contain the
  223. DWORD numBytes // in - number of bytes in the buffer to copy
  224. )
  225. {
  226. BOOL bSuccess = TRUE;
  227. WCHAR * curr = sidString;
  228. // add each byte of the SID to the output string
  229. for ( int i = 0 ; i < (int)numBytes ; i++)
  230. {
  231. AddByteToString(&curr,pBytes[i]);
  232. }
  233. return bSuccess;
  234. }
  235. BOOL
  236. CLdapConnection::StringToBytes(
  237. WCHAR const * pString, // in - string representing the data
  238. BYTE * pBytes // out- binary representation of the data
  239. )
  240. {
  241. BOOL bSuccess = TRUE;
  242. int len = UStrLen(pString) / 2;
  243. for ( int i = 0 ; i < len ; i++, pString += 2 )
  244. {
  245. // each byte is represented by 2 characters
  246. WCHAR str[3];
  247. BYTE hi,lo;
  248. safecopy(str,pString);
  249. hi = HexValue(str[0]);
  250. lo = HexValue(str[1]);
  251. pBytes[i] = ((hi << 4)+lo);
  252. }
  253. return bSuccess;
  254. }
  255. CLdapEnum::~CLdapEnum()
  256. {
  257. if ( m_message )
  258. {
  259. m_connection.ldap_msgfree(m_message);
  260. m_message = NULL;
  261. }
  262. }
  263. DWORD
  264. CLdapEnum::Open(
  265. WCHAR const * query, // in - query to execute
  266. WCHAR const * basePoint, // in - basepoint for query
  267. short scope, // in - scope: 0=base only, 1=one level, 2=recursive
  268. long pageSize, // in - page size to use for large searches
  269. int numAttributes, // in - number of attributes to retrieve for each matching item
  270. WCHAR ** attrs // in - array of attribute names to retrieve for each matching item
  271. )
  272. {
  273. // open and bind before calling this function
  274. ULONG result;
  275. PLDAPSearch searchBlock = NULL;
  276. PLDAPControl serverControls[2];
  277. l_timeval timeout = { 1000,1000 };
  278. ULONG totalCount = 0;
  279. berval cookie1 = { 0, NULL };
  280. DWORD numRead = 0;
  281. if ( m_message )
  282. {
  283. m_connection.ldap_msgfree(m_message);
  284. m_message = NULL;
  285. }
  286. LDAP * ld = m_connection.GetHandle();
  287. safecopy(m_query,query);
  288. safecopy(m_basepoint,basePoint);
  289. m_scope = scope;
  290. m_pageSize = pageSize;
  291. m_nAttributes = numAttributes;
  292. m_AttrNames = attrs;
  293. result = m_connection.ldap_create_page_control(ld,
  294. pageSize,
  295. &cookie1,
  296. FALSE, // is critical
  297. &serverControls[0]
  298. );
  299. serverControls[1] = NULL;
  300. result = m_connection.ldap_search_ext_s(ld,
  301. m_basepoint,
  302. m_scope,
  303. m_query,
  304. m_AttrNames,
  305. FALSE,
  306. serverControls,
  307. NULL,
  308. NULL,
  309. 0,
  310. &m_message);
  311. if ( ! result )
  312. {
  313. m_nReturned = m_connection.ldap_count_entries(ld,m_message);
  314. m_nCurrent = 0;
  315. m_bOpen = TRUE;
  316. }
  317. return m_connection.LdapMapErrorToWin32(result);
  318. }
  319. DWORD
  320. CLdapEnum::Next(
  321. PWCHAR ** ppAttrs // out- array of values for the next matching item
  322. )
  323. {
  324. DWORD rc = 0;
  325. if ( ! m_bOpen )
  326. {
  327. rc = ERROR_NOT_FOUND;
  328. }
  329. else
  330. {
  331. if ( m_nReturned > m_nCurrent )
  332. {
  333. // return the next entry from the current page
  334. return GetNextEntry(ppAttrs);
  335. }
  336. else
  337. {
  338. // see if there are more pages of results to get
  339. rc = GetNextPage();
  340. if (! rc )
  341. {
  342. return GetNextEntry(ppAttrs);
  343. }
  344. }
  345. }
  346. return rc;
  347. }
  348. void CLdapEnum::FreeData(WCHAR ** values)
  349. {
  350. for ( int i = 0 ; m_AttrNames[i] ; i++ )
  351. {
  352. if ( values[i] )
  353. {
  354. delete [] values[i];
  355. values[i] = NULL;
  356. }
  357. }
  358. delete [] values;
  359. }
  360. DWORD
  361. CLdapEnum::GetNextEntry(
  362. PWCHAR ** ppAttrs
  363. )
  364. {
  365. DWORD rc = 0;
  366. WCHAR ** pValues = new PWCHAR[m_nAttributes+1];
  367. if ( m_nCurrent == 0 )
  368. {
  369. m_currMsg = m_connection.ldap_first_entry(m_connection.GetHandle(),m_message);
  370. }
  371. else
  372. {
  373. m_currMsg = m_connection.ldap_next_entry(m_connection.GetHandle(),m_currMsg);
  374. }
  375. if ( m_currMsg )
  376. {
  377. int curr;
  378. for ( curr = 0 ; m_AttrNames[curr] ; curr++ )
  379. {
  380. pValues[curr] = NULL;
  381. WCHAR ** allvals = m_connection.ldap_get_values(m_connection.GetHandle(),m_currMsg,m_AttrNames[curr] );
  382. if ( allvals )
  383. {
  384. pValues[curr] = new WCHAR[UStrLen(allvals[0])+1];
  385. UStrCpy(pValues[curr],allvals[0]);
  386. m_connection.ldap_value_free(allvals);
  387. allvals =NULL;
  388. }
  389. }
  390. }
  391. (*ppAttrs) = pValues;
  392. m_nCurrent++;
  393. return rc;
  394. }
  395. DWORD
  396. CLdapEnum::GetNextPage()
  397. {
  398. ULONG result = 0;
  399. LDAP * ld = m_connection.GetHandle();
  400. berval * currCookie = NULL;
  401. berval * cookie2 = NULL;
  402. WCHAR * matched = NULL;
  403. PLDAPControl * currControls = NULL;
  404. ULONG retcode = 0;
  405. PLDAPControl * clientControls = NULL;
  406. WCHAR * errMsg = NULL;
  407. PLDAPControl serverControls[2];
  408. // Get the server control from the message, and make a new control with the cookie from the server
  409. result = m_connection.ldap_parse_result(ld,m_message,&retcode,NULL,NULL,NULL,&currControls,FALSE);
  410. m_connection.ldap_msgfree(m_message);
  411. m_message = NULL;
  412. if ( ! result )
  413. {
  414. result = m_connection.ldap_parse_page_control(ld,currControls,&m_totalCount,&currCookie);
  415. // under Exchange 5.5, before SP 2, this will fail with LDAP_CONTROL_NOT_FOUND when there are
  416. // no more search results. With Exchange 5.5 SP 2, this succeeds, and gives us a cookie that will
  417. // cause us to start over at the beginning of the search results.
  418. }
  419. if ( ! result )
  420. {
  421. if ( currCookie->bv_len == 0 && currCookie->bv_val == 0 )
  422. {
  423. // under Exchange 5.5, SP 2, this means we're at the end of the results.
  424. // if we pass in this cookie again, we will start over at the beginning of the search results.
  425. result = LDAP_CONTROL_NOT_FOUND;
  426. }
  427. serverControls[0] = NULL;
  428. serverControls[1] = NULL;
  429. if ( ! result )
  430. {
  431. result = m_connection.ldap_create_page_control(ld,
  432. m_pageSize,
  433. currCookie,
  434. FALSE,
  435. serverControls);
  436. }
  437. m_connection.ldap_controls_free(currControls);
  438. currControls = NULL;
  439. m_connection.ber_bvfree(currCookie);
  440. currCookie = NULL;
  441. }
  442. // continue the search with the new cookie
  443. if ( ! result )
  444. {
  445. result = m_connection.ldap_search_ext_s(ld,
  446. m_basepoint,
  447. m_scope,
  448. m_query,
  449. m_AttrNames,
  450. FALSE,
  451. serverControls,
  452. NULL,
  453. NULL,
  454. 0,
  455. &m_message);
  456. if ( result && result != LDAP_CONTROL_NOT_FOUND )
  457. {
  458. // LDAP_CONTROL_NOT_FOUND means that we have reached the end of the search results
  459. // in Exchange 5.5, before SP 2 (the server doesn't return a page control when there
  460. // are no more pages, so we get LDAP_CONTROL_NOT_FOUND when we try to extract the page
  461. // control from the search results).
  462. }
  463. }
  464. if ( ! result )
  465. {
  466. m_nReturned = m_connection.ldap_count_entries(ld,m_message);
  467. m_nCurrent = 0;
  468. }
  469. return m_connection.LdapMapErrorToWin32(result);
  470. }