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.

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