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.

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