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.

1448 lines
34 KiB

  1. /*++
  2. Copyright (c) 1997-2000 Microsoft Corporation
  3. Module Name:
  4. rndnt.cpp
  5. Abstract:
  6. This module contains implementation of CNTDirectory.
  7. --*/
  8. #include "stdafx.h"
  9. #include "rndnt.h"
  10. #include "rndldap.h"
  11. #include "rndcoll.h"
  12. HRESULT CNTDirectory::FinalConstruct(void)
  13. {
  14. LOG((MSP_TRACE, "CNTDirectory::FinalConstruct - enter"));
  15. HRESULT hr = CoCreateFreeThreadedMarshaler( GetControllingUnknown(),
  16. & m_pFTM );
  17. if ( FAILED(hr) )
  18. {
  19. LOG((MSP_INFO, "CNTDirectory::FinalConstruct - "
  20. "create FTM returned 0x%08x; exit", hr));
  21. return hr;
  22. }
  23. LOG((MSP_TRACE, "CNTDirectory::FinalConstruct - exit S_OK"));
  24. return S_OK;
  25. }
  26. /////////////////////////////////////////////////////////////////////////////
  27. // ldap helper functions
  28. /////////////////////////////////////////////////////////////////////////////
  29. //////////////////////////////////////////////////////////////////////////////
  30. // GetGlobalCatalogName (local helper funcion)
  31. //
  32. // This function asks the domain controller for the name of a server with a
  33. // Global Catalog. That's the server we actually do ldap_open() on below
  34. // in CNTDirectory::Connect().
  35. //
  36. // Argument: receives a pointer to a new'ed string containing the name
  37. // of the global catalog. This is a fully qualified domain name in
  38. // the format "foo.bar.com.", NOT "\\foo.bar.com.".
  39. //
  40. // Returns an HRESULT:
  41. // S_OK : it worked
  42. // E_OUTOFMEMORY : not enough memory to allocate the string
  43. // other : reason for failure of ::DsGetDcName()
  44. //
  45. //////////////////////////////////////////////////////////////////////////////
  46. HRESULT GetGlobalCatalogName(WCHAR ** ppszGlobalCatalogName)
  47. {
  48. return GetDomainControllerName(DS_GC_SERVER_REQUIRED,
  49. ppszGlobalCatalogName);
  50. }
  51. /////////////////////////////////////////////////////////////////////////////
  52. // private functions
  53. /////////////////////////////////////////////////////////////////////////////
  54. HRESULT CNTDirectory::LdapSearchUser(
  55. IN TCHAR * pName,
  56. OUT LDAPMessage ** ppLdapMsg
  57. )
  58. /*++
  59. Routine Description:
  60. Search a user in the Global Catalog.
  61. Arguments:
  62. pName - the user name.
  63. ppLdapMsg - the result of the search.
  64. Return Value:
  65. HRESULT.
  66. --*/
  67. {
  68. CTstr pFilter =
  69. new TCHAR [lstrlen(DS_USER_FILTER_FORMAT) + lstrlen(pName) + 1];
  70. BAIL_IF_NULL((TCHAR*)pFilter, E_OUTOFMEMORY);
  71. wsprintf(pFilter, DS_USER_FILTER_FORMAT, pName);
  72. // attribute to look for.
  73. TCHAR *Attributes[] =
  74. {
  75. (WCHAR *)UserAttributeName(UA_USERNAME),
  76. (WCHAR *)UserAttributeName(UA_TELEPHONE_NUMBER),
  77. (WCHAR *)UserAttributeName(UA_IPPHONE_PRIMARY),
  78. NULL
  79. };
  80. // do the search.
  81. ULONG res = DoLdapSearch(
  82. m_ldap, // ldap handle
  83. L"", // base dn is root, because it is Global catalog.
  84. LDAP_SCOPE_SUBTREE, // subtree search
  85. pFilter, // filter; see rndnt.h for the format
  86. Attributes, // array of attribute names
  87. FALSE, // return the attribute values
  88. ppLdapMsg // search results
  89. );
  90. BAIL_IF_LDAP_FAIL(res, "search for objects");
  91. return S_OK;
  92. }
  93. HRESULT CNTDirectory::MakeUserDNs(
  94. IN TCHAR * pName,
  95. OUT TCHAR *** pppDNs,
  96. OUT DWORD * pdwNumDNs
  97. )
  98. /*++
  99. Routine Description:
  100. Look for the DN of a user in the DS.
  101. Arguments:
  102. pName - the user name.
  103. ppDN - the user's DN.
  104. Return Value:
  105. HRESULT.
  106. --*/
  107. {
  108. LOG((MSP_INFO, "DS: MakeUserDNs: enter"));
  109. CLdapMsgPtr pLdapMsg; // auto release message.
  110. *pppDNs = NULL;
  111. *pdwNumDNs = 0;
  112. //
  113. // First find the desired user via the Global Catalog.
  114. //
  115. BAIL_IF_FAIL(LdapSearchUser(pName, &pLdapMsg),
  116. "DS: MakeUserDNs: Ldap Search User failed");
  117. //
  118. // Make sure we got the right number of entries. If we get 0, we're stuck.
  119. // The DS enforces domain-wide uniqueness on the samAccountName attribute,
  120. // so if we get more than one it means the same username is present in
  121. // more than one domain in our enterprise.
  122. //
  123. DWORD dwEntries = ldap_count_entries(m_ldap, pLdapMsg);
  124. if (dwEntries == 0)
  125. {
  126. LOG((MSP_ERROR, "DS: MakeUserDNs: entry count is 0 - no match"));
  127. return E_FAIL;
  128. }
  129. //
  130. // Allocate an array of pointers in which to return the DNs.
  131. //
  132. *pppDNs = new PTCHAR [ dwEntries ];
  133. if ( (*pppDNs) == NULL )
  134. {
  135. LOG((MSP_ERROR, "DS: MakeUserDNs: Not enough memory to allocate array of pointers"));
  136. return E_OUTOFMEMORY;
  137. }
  138. //
  139. // For each DN returned, allocate space for a private copy of the DN and
  140. // stick a pointer to that space in the array of pointers allocated
  141. // above.
  142. //
  143. //
  144. // Note that dwEntries is the number of entries in the ldap
  145. // message. *pdwNumDNs is the number of DNs we are able to
  146. // extract. For various reasons it is possible for
  147. // *pdwNumDNs to eventually become < dwEntries.
  148. //
  149. LDAPMessage * pEntry = NULL;
  150. for ( DWORD i = 0; i < dwEntries; i++ )
  151. {
  152. //
  153. // Get the entry from the ldap message.
  154. //
  155. if ( i == 0 )
  156. {
  157. pEntry = ldap_first_entry(m_ldap, pLdapMsg);
  158. }
  159. else
  160. {
  161. pEntry = ldap_next_entry(m_ldap, pEntry);
  162. }
  163. //
  164. // Get the DN from the message.
  165. //
  166. TCHAR * p = ldap_get_dn(m_ldap, pEntry);
  167. if ( p == NULL )
  168. {
  169. LOG((MSP_ERROR, "DS: MakeUserDNs: could not get DN - skipping"));
  170. continue;
  171. }
  172. LOG((MSP_INFO, "DS: MakeUserDNs: found user DN: %S", p));
  173. //
  174. // Allocate space for a copy of the DN.
  175. //
  176. TCHAR * pDN = new TCHAR [ lstrlen(p) + 1 ];
  177. if ( pDN == NULL )
  178. {
  179. ldap_memfree( p );
  180. LOG((MSP_ERROR, "DS: MakeUserDNs: could not allocate copy of "
  181. "DN - skipping"));
  182. continue;
  183. }
  184. //
  185. // Copy the DN and free the one ldap constructed.
  186. //
  187. lstrcpy( pDN, p );
  188. ldap_memfree( p );
  189. //
  190. // Save the DN in our array of DNs and update the size of the array.
  191. //
  192. (*pppDNs)[ *pdwNumDNs ] = pDN;
  193. (*pdwNumDNs) ++;
  194. }
  195. //
  196. // Check if we have anything to return.
  197. //
  198. if ( (*pdwNumDNs) == 0 )
  199. {
  200. LOG((MSP_ERROR, "DS: MakeUserDNs: had entries but could not "
  201. "retrieve any DNs - returning E_FAIL"));
  202. delete (*pppDNs);
  203. *pppDNs = NULL;
  204. return E_FAIL;
  205. }
  206. LOG((MSP_INFO, "DS: MakeUserDNs: exit S_OK"));
  207. return S_OK;
  208. }
  209. HRESULT CNTDirectory::AddUserIPPhone(
  210. IN ITDirectoryObject *pDirectoryObject
  211. )
  212. /*++
  213. Routine Description:
  214. Modify the user's IPPhone-Primary attribute.
  215. Arguments:
  216. pDirectoryObject - the object that has the user name and IP phone.
  217. Return Value:
  218. HRESULT.
  219. --*/
  220. {
  221. HRESULT hr;
  222. //
  223. // First get the private interface for attributes.
  224. //
  225. ITDirectoryObjectPrivate * pObjectPrivate;
  226. hr = pDirectoryObject->QueryInterface(
  227. IID_ITDirectoryObjectPrivate,
  228. (void **)&pObjectPrivate
  229. );
  230. if ( FAILED(hr) )
  231. {
  232. LOG((MSP_ERROR, "CNTDirectory::AddUserIPPhone - can't get the "
  233. "private directory object interface - exit 0x%08x", hr));
  234. return hr;
  235. }
  236. //
  237. // Get the user name.
  238. //
  239. BSTR bName;
  240. hr = pDirectoryObject->get_Name( & bName );
  241. if ( FAILED(hr) )
  242. {
  243. LOG((MSP_ERROR, "CNTDirectory::AddUserIPPhone - "
  244. "can't get user name - exit 0x%08x", hr));
  245. pObjectPrivate->Release();
  246. return hr;
  247. }
  248. //
  249. // Get the IP phone(machine name).
  250. //
  251. BSTR bIPPhone;
  252. hr = pObjectPrivate->GetAttribute( UA_IPPHONE_PRIMARY, &bIPPhone );
  253. pObjectPrivate->Release();
  254. if ( FAILED(hr) )
  255. {
  256. LOG((MSP_ERROR, "CNTDirectory::AddUserIPPhone - "
  257. "can't get IPPhone attribute - exit 0x%08x", hr));
  258. SysFreeString( bName );
  259. return hr;
  260. }
  261. //
  262. // resolve the machine name and get the fully qualified DNS name.
  263. // this is a pointer into a static hostp structure so we do not
  264. // need to free it
  265. //
  266. char * pchFullDNSName;
  267. hr = ResolveHostName(0, bIPPhone, &pchFullDNSName, NULL);
  268. SysFreeString(bIPPhone);
  269. if ( FAILED(hr) )
  270. {
  271. LOG((MSP_ERROR, "CNTDirectory::AddUserIPPhone - "
  272. "can't resolve hostname - exit 0x%08x", hr));
  273. SysFreeString( bName );
  274. return hr;
  275. }
  276. //
  277. // Convert the ASCII string into unicode string.
  278. // conversion memory allocates memory on stack
  279. //
  280. USES_CONVERSION;
  281. TCHAR * pFullDNSName = A2T(pchFullDNSName);
  282. if ( pFullDNSName == NULL)
  283. {
  284. LOG((MSP_ERROR, "CNTDirectory::AddUserIPPhone - "
  285. "can't convert to tchar - exit E_FAIL"));
  286. SysFreeString( bName );
  287. return E_FAIL;
  288. }
  289. //
  290. // Find the DNs of the user in DS.
  291. //
  292. TCHAR ** ppDNs;
  293. DWORD dwNumDNs;
  294. hr = MakeUserDNs( bName, & ppDNs, & dwNumDNs );
  295. SysFreeString( bName );
  296. if ( FAILED(hr) )
  297. {
  298. LOG((MSP_ERROR, "CNTDirectory::AddUserIPPhone - "
  299. "can't construct any DNs for user - exit 0x%08x", hr));
  300. return hr;
  301. }
  302. LOG((MSP_INFO, "%d DNs to try", dwNumDNs ));
  303. hr = E_FAIL;
  304. for ( DWORD i = 0; i < dwNumDNs; i++ )
  305. {
  306. //
  307. // If one of them worked, then don't bother with any more.
  308. // But we still need to delete the leftover DN strings.
  309. //
  310. if ( SUCCEEDED(hr) )
  311. {
  312. LOG((MSP_INFO, "skipping extra DN %S", ppDNs[i]));
  313. }
  314. else
  315. {
  316. //
  317. // Modify the user object.
  318. //
  319. LDAPMod mod[1]; // The modify sturctures used by LDAP
  320. //
  321. // the IPPhone-Primary attribute.
  322. //
  323. TCHAR * IPPhone[2] = {pFullDNSName, NULL};
  324. mod[0].mod_values = IPPhone;
  325. mod[0].mod_op = LDAP_MOD_REPLACE;
  326. mod[0].mod_type = (WCHAR *)UserAttributeName(UA_IPPHONE_PRIMARY);
  327. LDAPMod* mods[] = {&mod[0], NULL};
  328. LOG((MSP_INFO, "modifying %S", ppDNs[i] ));
  329. //
  330. // Call the modify function to modify the object.
  331. //
  332. hr = GetLdapHResultIfFailed(DoLdapModify(TRUE,
  333. m_ldapNonGC,
  334. ppDNs[i],
  335. mods));
  336. if ( SUCCEEDED(hr) )
  337. {
  338. LOG((MSP_INFO, "modifying %S succeeded; done", ppDNs[i] ));
  339. }
  340. else
  341. {
  342. LOG((MSP_INFO, "modifying %S failed 0x%08x; trying next",
  343. ppDNs[i], hr ));
  344. }
  345. }
  346. //
  347. // Skipping or not, we need to delete the string.
  348. //
  349. delete ppDNs[i];
  350. }
  351. //
  352. // Delete the array that holds the DNs.
  353. //
  354. delete ppDNs;
  355. return hr;
  356. }
  357. HRESULT CNTDirectory::DeleteUserIPPhone(
  358. IN ITDirectoryObject *pDirectoryObject
  359. )
  360. /*++
  361. Routine Description:
  362. Remove the user's IPPhone-Primary attribute.
  363. Arguments:
  364. pDirectoryObject - the object that has the user name.
  365. Return Value:
  366. HRESULT.
  367. --*/
  368. {
  369. //
  370. // Get the name of the user.
  371. //
  372. HRESULT hr;
  373. BSTR bName;
  374. hr = pDirectoryObject->get_Name(&bName);
  375. if ( FAILED(hr) )
  376. {
  377. LOG((MSP_ERROR, "CNTDirectory::DeleteUserIPPHone - "
  378. "can't get user name - exit 0x%08x", hr));
  379. return hr;
  380. }
  381. //
  382. // Get an array of DNs for this user name.
  383. //
  384. TCHAR ** ppDNs;
  385. DWORD dwNumDNs;
  386. hr = MakeUserDNs( bName, & ppDNs, & dwNumDNs );
  387. SysFreeString( bName );
  388. if ( FAILED(hr) )
  389. {
  390. LOG((MSP_ERROR, "CNTDirectory::DeleteUserIPPHone - "
  391. "can't get any DNs - exit 0x%08x", hr));
  392. return hr;
  393. }
  394. LOG((MSP_INFO, "CNTDirectory::DeleteUserIPPhone - "
  395. "%d DNs to try", dwNumDNs ));
  396. //
  397. // Loop through all the available DNs. Try each one
  398. // until one succeeds, then continue looping just
  399. // to delete the strings.
  400. //
  401. hr = E_FAIL;
  402. for ( DWORD i = 0; i < dwNumDNs; i++ )
  403. {
  404. //
  405. // If one of them worked, then don't bother with any more.
  406. // But we still need to delete the leftover DN strings.
  407. //
  408. if ( SUCCEEDED(hr) )
  409. {
  410. LOG((MSP_INFO, "skipping extra DN %S", ppDNs[i]));
  411. }
  412. else
  413. {
  414. LDAPMod mod; // The modify sturctures used by LDAP
  415. mod.mod_values = NULL;
  416. mod.mod_op = LDAP_MOD_DELETE;
  417. mod.mod_type = (WCHAR *)UserAttributeName(UA_IPPHONE_PRIMARY);
  418. LDAPMod* mods[] = {&mod, NULL};
  419. LOG((MSP_INFO, "modifying %S", ppDNs[i] ));
  420. //
  421. // Call the modify function to remove the attribute.
  422. //
  423. hr = GetLdapHResultIfFailed(DoLdapModify(TRUE,
  424. m_ldapNonGC,
  425. ppDNs[i],
  426. mods));
  427. if ( SUCCEEDED(hr) )
  428. {
  429. LOG((MSP_INFO, "modifying %S succeeded; done", ppDNs[i] ));
  430. }
  431. else
  432. {
  433. LOG((MSP_INFO, "modifying %S failed 0x%08x; trying next",
  434. ppDNs[i], hr ));
  435. }
  436. }
  437. //
  438. // Skipping or not, we need to delete the string.
  439. //
  440. delete ppDNs[i];
  441. }
  442. //
  443. // Delete the array that holds the DNs.
  444. //
  445. delete ppDNs;
  446. return hr;
  447. }
  448. HRESULT CNTDirectory::CreateUser(
  449. IN LDAPMessage * pEntry,
  450. IN ITDirectoryObject ** ppObject
  451. )
  452. /*++
  453. Routine Description:
  454. Create a user object based on the info in DS.
  455. Arguments:
  456. pEntry - the returned entry from DS.
  457. pObject - the created object that has the user name and IP phone.
  458. Return Value:
  459. HRESULT.
  460. --*/
  461. {
  462. // Get the name of the user.
  463. CBstr bName;
  464. BAIL_IF_FAIL(
  465. ::GetAttributeValue(
  466. m_ldap,
  467. pEntry,
  468. UserAttributeName(UA_USERNAME),
  469. &bName
  470. ),
  471. "get the user name"
  472. );
  473. // Create an empty user object.
  474. CComPtr<ITDirectoryObject> pObject;
  475. BAIL_IF_FAIL(::CreateEmptyUser(bName, &pObject), "CreateEmptyUser");
  476. // get the private interface for attributes.
  477. CComPtr <ITDirectoryObjectPrivate> pObjectPrivate;
  478. BAIL_IF_FAIL(
  479. pObject->QueryInterface(
  480. IID_ITDirectoryObjectPrivate,
  481. (void **)&pObjectPrivate
  482. ),
  483. "can't get the private directory object interface");
  484. // Get the machine name of the user.
  485. CBstr bAddress;
  486. if (SUCCEEDED(::GetAttributeValue(
  487. m_ldap,
  488. pEntry,
  489. UserAttributeName(UA_IPPHONE_PRIMARY),
  490. &bAddress
  491. )))
  492. {
  493. // Set the ipphone attribute.
  494. BAIL_IF_FAIL(pObjectPrivate->SetAttribute(UA_IPPHONE_PRIMARY, bAddress),
  495. "set ipPhone attribute");
  496. }
  497. // Get and set the phonenumber of the user. (optional)
  498. CBstr bPhone;
  499. if (SUCCEEDED(::GetAttributeValue(
  500. m_ldap,
  501. pEntry,
  502. UserAttributeName(UA_TELEPHONE_NUMBER),
  503. &bPhone
  504. )))
  505. {
  506. // Set the telephone attribute.
  507. BAIL_IF_FAIL(pObjectPrivate->SetAttribute(UA_TELEPHONE_NUMBER, bPhone),
  508. "set phone number");
  509. }
  510. *ppObject = pObject;
  511. (*ppObject)->AddRef();
  512. return S_OK;
  513. }
  514. HRESULT CNTDirectory::SearchUser(
  515. IN BSTR pName,
  516. OUT ITDirectoryObject *** pppDirectoryObject,
  517. OUT DWORD * pdwSize
  518. )
  519. /*++
  520. Routine Description:
  521. Search user and create an array of user object to return.
  522. Arguments:
  523. pName - the user name.
  524. pppDirectoryObject - the created array of user objects that have
  525. the user name and IP phone.
  526. pdwSize - the size of the array.
  527. Return Value:
  528. HRESULT.
  529. --*/
  530. {
  531. CLdapMsgPtr pLdapMsg; // auto release message.
  532. BAIL_IF_FAIL(LdapSearchUser(pName, &pLdapMsg),
  533. "Ldap Search User failed");
  534. DWORD dwEntries = ldap_count_entries(m_ldap, pLdapMsg);
  535. ITDirectoryObject ** pObjects = new PDIRECTORYOBJECT [dwEntries];
  536. BAIL_IF_NULL(pObjects, E_OUTOFMEMORY);
  537. DWORD dwCount = 0;
  538. LDAPMessage *pEntry = ldap_first_entry(m_ldap, pLdapMsg);
  539. while (pEntry != NULL)
  540. {
  541. HRESULT hr;
  542. hr = CreateUser(pEntry, &pObjects[dwCount]);
  543. if (SUCCEEDED(hr))
  544. {
  545. dwCount ++;
  546. }
  547. // Get next entry.
  548. pEntry = ldap_next_entry(m_ldap, pEntry);
  549. }
  550. *pppDirectoryObject = pObjects;
  551. *pdwSize = dwCount;
  552. return S_OK;
  553. }
  554. /////////////////////////////////////////////////////////////////////////////
  555. // NT Directory implementation
  556. /////////////////////////////////////////////////////////////////////////////
  557. STDMETHODIMP CNTDirectory::get_DirectoryType (
  558. OUT DIRECTORY_TYPE * pDirectoryType
  559. )
  560. // get the type of the directory.
  561. {
  562. if ( IsBadWritePtr(pDirectoryType, sizeof(DIRECTORY_TYPE) ) )
  563. {
  564. LOG((MSP_ERROR, "Directory.get_DirectoryType, invalid pointer"));
  565. return E_POINTER;
  566. }
  567. *pDirectoryType = m_Type;
  568. return S_OK;
  569. }
  570. STDMETHODIMP CNTDirectory::get_DisplayName (
  571. OUT BSTR *ppName
  572. )
  573. // get the display name of the directory.
  574. {
  575. BAIL_IF_BAD_WRITE_PTR(ppName, E_POINTER);
  576. *ppName = SysAllocString(L"NTDS");
  577. if (*ppName == NULL)
  578. {
  579. LOG((MSP_ERROR, "get_DisplayName: out of memory."));
  580. return E_OUTOFMEMORY;
  581. }
  582. return S_OK;
  583. }
  584. STDMETHODIMP CNTDirectory::get_IsDynamic(
  585. OUT VARIANT_BOOL *pfDynamic
  586. )
  587. // find out if the directory requires refresh. For NTDS, it is FALSE.
  588. {
  589. if ( IsBadWritePtr(pfDynamic, sizeof(VARIANT_BOOL) ) )
  590. {
  591. LOG((MSP_ERROR, "Directory.get_IsDynamic, invalid pointer"));
  592. return E_POINTER;
  593. }
  594. *pfDynamic = VARIANT_FALSE;
  595. return S_OK;
  596. }
  597. STDMETHODIMP CNTDirectory::get_DefaultObjectTTL(
  598. OUT long *pTTL // in seconds
  599. )
  600. // Since NTDS is not dynamic, this shouldn't be called.
  601. {
  602. return E_FAIL; // ZoltanS changed from E_UNEXPECTED
  603. }
  604. STDMETHODIMP CNTDirectory::put_DefaultObjectTTL(
  605. IN long TTL // in sechods
  606. )
  607. // Since NTDS is not dynamic, this shouldn't be called.
  608. {
  609. return E_FAIL; // ZoltanS changed from E_UNEXPECTED
  610. }
  611. STDMETHODIMP CNTDirectory::EnableAutoRefresh(
  612. IN VARIANT_BOOL fEnable
  613. )
  614. // Since NTDS is not dynamic, this shouldn't be called.
  615. {
  616. return E_FAIL; // ZoltanS changed from E_UNEXPECTED
  617. }
  618. //////////////////////////////////////////////////////////////////////////////
  619. STDMETHODIMP CNTDirectory::Connect(
  620. IN VARIANT_BOOL fSecure
  621. )
  622. // make ldap connection. Use ssl port or normal port.
  623. {
  624. CLock Lock(m_lock);
  625. if (m_ldap != NULL)
  626. {
  627. LOG((MSP_ERROR, "already connected."));
  628. return RND_ALREADY_CONNECTED;
  629. }
  630. // ZoltanS: either VARIANT_TRUE or TRUE will work
  631. // in case the caller doesn't know better
  632. if (fSecure)
  633. {
  634. // the port is flipped from regular port to ssl port.
  635. m_wPort = GetOtherPort(m_wPort);
  636. m_IsSsl = TRUE;
  637. }
  638. //
  639. // ZoltanS: Get the name of the global catalog. If there is not at least
  640. // one global catalog in this enterprise then we are toast.
  641. //
  642. HRESULT hr;
  643. WCHAR * pszGlobalCatalogName;
  644. // this allocates pszGlobalCatalogName
  645. BAIL_IF_FAIL(::GetGlobalCatalogName( &pszGlobalCatalogName ),
  646. "GetGlobalCatalogName failed");
  647. //
  648. // associate the ldap handle with the handle holder. in case of an error
  649. // and subsequent return (without being reset), the ldap handle is closed
  650. // ZoltanS: changed to use GC instead of NULL
  651. //
  652. CLdapPtr hLdap = ldap_init(pszGlobalCatalogName, m_wPort);
  653. if (hLdap == NULL)
  654. {
  655. LOG((MSP_ERROR, "ldap_init error: %d", GetLastError()));
  656. }
  657. //
  658. // ZoltanS: Deallocate the string that holds the name of the global
  659. // catalog; we are sure we won't need it anymore.
  660. //
  661. delete pszGlobalCatalogName;
  662. //
  663. // Now back to our regularly scheduled programming...
  664. //
  665. BAIL_IF_NULL((LDAP*)hLdap, HRESULT_FROM_WIN32(ERROR_BAD_NETPATH));
  666. LDAP_TIMEVAL Timeout;
  667. Timeout.tv_sec = REND_LDAP_TIMELIMIT;
  668. Timeout.tv_usec = 0;
  669. DWORD res = ldap_connect((LDAP*)hLdap, &Timeout);
  670. BAIL_IF_LDAP_FAIL(res, "connect to the server.");
  671. DWORD LdapVersion = 3;
  672. res = ldap_set_option((LDAP*)hLdap, LDAP_OPT_VERSION, &LdapVersion);
  673. BAIL_IF_LDAP_FAIL(res, "set ldap version to 3");
  674. res = ldap_set_option((LDAP*)hLdap, LDAP_OPT_TIMELIMIT, &Timeout);
  675. BAIL_IF_LDAP_FAIL(res, "set ldap timelimit");
  676. ULONG ldapOptionOn = PtrToUlong(LDAP_OPT_ON);
  677. res = ldap_set_option((LDAP*)hLdap, LDAP_OPT_AREC_EXCLUSIVE, &ldapOptionOn);
  678. BAIL_IF_LDAP_FAIL(res, "set ldap arec exclusive");
  679. if (m_IsSsl)
  680. {
  681. res = ldap_set_option(hLdap, LDAP_OPT_SSL, LDAP_OPT_ON);
  682. if( fSecure )
  683. {
  684. if( res != LDAP_SUCCESS )
  685. {
  686. LOG((MSP_ERROR, "Invalid Secure flag"));
  687. return E_INVALIDARG;
  688. }
  689. }
  690. else
  691. {
  692. BAIL_IF_LDAP_FAIL(res, "set ssl option");
  693. }
  694. }
  695. // if no directory path is specified, query the server
  696. // to determine the correct path
  697. BAIL_IF_FAIL(
  698. ::GetNamingContext(hLdap, &m_NamingContext),
  699. "can't get default naming context"
  700. );
  701. m_ldap = hLdap;
  702. // reset the holders so that they don't release anyting.
  703. hLdap = NULL;
  704. CLdapPtr hLdapNonGC = ldap_init(NULL, LDAP_PORT);
  705. if (hLdapNonGC == NULL)
  706. {
  707. LOG((MSP_ERROR, "ldap_init non-GC error: %d", GetLastError()));
  708. }
  709. BAIL_IF_NULL((LDAP*)hLdapNonGC, HRESULT_FROM_WIN32(ERROR_BAD_NETPATH));
  710. res = ldap_connect((LDAP*)hLdapNonGC, &Timeout);
  711. BAIL_IF_LDAP_FAIL(res, "connect to the server.");
  712. res = ldap_set_option((LDAP*)hLdapNonGC, LDAP_OPT_VERSION, &LdapVersion);
  713. BAIL_IF_LDAP_FAIL(res, "set ldap version to 3");
  714. res = ldap_set_option((LDAP*)hLdapNonGC, LDAP_OPT_TIMELIMIT, &Timeout);
  715. BAIL_IF_LDAP_FAIL(res, "set ldap timelimit");
  716. res = ldap_set_option((LDAP*)hLdapNonGC, LDAP_OPT_AREC_EXCLUSIVE, &ldapOptionOn);
  717. BAIL_IF_LDAP_FAIL(res, "set ldap arec exclusive");
  718. // res = ldap_set_option((LDAP*)hLdapNonGC, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
  719. // BAIL_IF_LDAP_FAIL(res, "set chase referrals to on");
  720. if (m_IsSsl)
  721. {
  722. res = ldap_set_option(hLdapNonGC, LDAP_OPT_SSL, LDAP_OPT_ON);
  723. BAIL_IF_LDAP_FAIL(res, "set ssl option");
  724. }
  725. m_ldapNonGC = hLdapNonGC;
  726. // reset the holders so that they don't release anyting.
  727. hLdapNonGC = NULL;
  728. return S_OK;
  729. }
  730. //
  731. // ITDirectory::Bind
  732. //
  733. // Bind to the server.
  734. //
  735. // Currently recognized flags:
  736. //
  737. // RENDBIND_AUTHENTICATE 0x00000001
  738. // RENDBIND_DEFAULTDOMAINNAME 0x00000002
  739. // RENDBIND_DEFAULTUSERNAME 0x00000004
  740. // RENDBIND_DEFAULTPASSWORD 0x00000008
  741. //
  742. // "Meta-flags" for convenience:
  743. // RENDBIND_DEFAULTCREDENTIALS 0x0000000e
  744. //
  745. //
  746. // All of this together means that the following three
  747. // forms are all equivalent:
  748. //
  749. // BSTR es = SysAllocString(L"");
  750. // hr = pITDirectory->Bind(es, es, es, RENDBIND_AUTHENTICATE |
  751. // RENDBIND_DEFAULTCREDENTIALS);
  752. // SysFreeString(es);
  753. //
  754. //
  755. // BSTR es = SysAllocString(L"");
  756. // hr = pITDirectory->Bind(es, es, es, RENDBIND_AUTHENTICATE |
  757. // RENDBIND_DEFAULTDOMAINNAME |
  758. // RENDBIND_DEFAULTUSERNAME |
  759. // RENDBIND_DEFAULTPASSWORD);
  760. // SysFreeString(es);
  761. //
  762. //
  763. // hr = pITDirectory->Bind(NULL, NULL, NULL, RENDBIND_AUTHENTICATE);
  764. //
  765. //
  766. STDMETHODIMP CNTDirectory::Bind (
  767. IN BSTR pDomainName,
  768. IN BSTR pUserName,
  769. IN BSTR pPassword,
  770. IN long lFlags
  771. )
  772. {
  773. LOG((MSP_TRACE, "CNTDirectory Bind - enter"));
  774. //
  775. // Determine if we should authenticate.
  776. //
  777. BOOL fAuthenticate = FALSE;
  778. if ( lFlags & RENDBIND_AUTHENTICATE )
  779. {
  780. fAuthenticate = TRUE;
  781. }
  782. //
  783. // For scripting compatibility, force string parameters to NULL based
  784. // on flags.
  785. //
  786. if ( lFlags & RENDBIND_DEFAULTDOMAINNAME )
  787. {
  788. pDomainName = NULL;
  789. }
  790. if ( lFlags & RENDBIND_DEFAULTUSERNAME )
  791. {
  792. pUserName = NULL;
  793. }
  794. if ( lFlags & RENDBIND_DEFAULTPASSWORD )
  795. {
  796. pPassword = NULL;
  797. }
  798. LOG((MSP_INFO, "Bind parameters: domain: `%S' user: `%S' pass: `%S'"
  799. "authenticate: %S)",
  800. (pDomainName) ? pDomainName : L"<null>",
  801. (pUserName) ? pUserName : L"<null>",
  802. (pPassword) ? pPassword : L"<null>",
  803. (fAuthenticate) ? L"yes" : L"no"));
  804. //
  805. // All flags processed -- lock and proceed with bind if connected.
  806. //
  807. CLock Lock(m_lock);
  808. if (m_ldap == NULL)
  809. {
  810. LOG((MSP_ERROR, "not connected."));
  811. return RND_NOT_CONNECTED;
  812. }
  813. //
  814. // ZoltanS: check the arguments. NULL has meaning in each case, so they are
  815. // OK for now. In each case we want to check any length string, so we
  816. // specify (UINT) -1 as the length.
  817. //
  818. if ( (pDomainName != NULL) && IsBadStringPtr(pDomainName, (UINT) -1 ) )
  819. {
  820. LOG((MSP_ERROR, "CNTDirectory::Bind: bad non-NULL pDomainName argument"));
  821. return E_POINTER;
  822. }
  823. if ( (pUserName != NULL) && IsBadStringPtr(pUserName, (UINT) -1 ) )
  824. {
  825. LOG((MSP_ERROR, "CNTDirectory::Bind: bad non-NULL pUserName argument"));
  826. return E_POINTER;
  827. }
  828. if ( (pPassword != NULL) && IsBadStringPtr(pPassword, (UINT) -1 ) )
  829. {
  830. LOG((MSP_ERROR, "CNTDirectory::Bind: bad non-NULL pPassword argument"));
  831. return E_POINTER;
  832. }
  833. ULONG res;
  834. if ( m_IsSsl || (!fAuthenticate) )
  835. {
  836. // if encrypted or no secure authentication is required,
  837. // simple bind is sufficient
  838. // ldap_simple_bind_s does not use sspi to get default credentials. We are
  839. // just specifying what we will actually pass on the wire.
  840. if (pPassword == NULL)
  841. {
  842. LOG((MSP_ERROR, "invalid Bind parameters: no password specified"));
  843. return E_INVALIDARG;
  844. }
  845. WCHAR * wszFullName;
  846. if ( (pDomainName == NULL) && (pUserName == NULL) )
  847. {
  848. // No domain / user doesn't make sense.
  849. LOG((MSP_ERROR, "invalid Bind paramters: domain and user not specified"));
  850. return E_INVALIDARG;
  851. }
  852. else if (pDomainName == NULL)
  853. {
  854. // username only is okay
  855. wszFullName = pUserName;
  856. }
  857. else if (pUserName == NULL)
  858. {
  859. // It doesn't make sense to specify domain but not user...
  860. LOG((MSP_ERROR, "invalid Bind paramters: domain specified but not user"));
  861. return E_INVALIDARG;
  862. }
  863. else
  864. {
  865. // We need domain\user. Allocate a string and sprintf into it.
  866. // The + 2 is for the "\" and for the null termination.
  867. wszFullName = new WCHAR[wcslen(pDomainName) + wcslen(pUserName) + 2];
  868. BAIL_IF_NULL(wszFullName, E_OUTOFMEMORY);
  869. wsprintf(wszFullName, L"%s\\%s", pDomainName, pUserName);
  870. }
  871. //
  872. // Do the simple bind.
  873. //
  874. res = ldap_simple_bind_s(m_ldap, wszFullName, pPassword);
  875. ULONG res2 = ldap_simple_bind_s(m_ldapNonGC, wszFullName, pPassword);
  876. //
  877. // If we constructed the full name string, we now need to delete it.
  878. //
  879. if (wszFullName != pUserName)
  880. {
  881. delete wszFullName;
  882. }
  883. //
  884. // Bail if the simple bind failed.
  885. //
  886. BAIL_IF_LDAP_FAIL(res, "ldap simple bind");
  887. BAIL_IF_LDAP_FAIL(res2, "ldap simple bind - non gc");
  888. }
  889. else // try an SSPI bind
  890. {
  891. // ZoltanS Note: the ldap bind code does not process NULL, NULL, NULL
  892. // in the SEC_WINNT_AUTH_IDENTITY blob, therefore it is special-cased.
  893. // ZoltanS: We used to use LDAP_AUTH_NTLM; now we use
  894. // LDAP_AUTH_NEGOTIATE to make sure we use the right domain for the
  895. // bind.
  896. if ( pDomainName || pUserName || pPassword )
  897. {
  898. // fill the credential structure
  899. SEC_WINNT_AUTH_IDENTITY AuthI;
  900. AuthI.User = (PTCHAR)pUserName;
  901. AuthI.UserLength = (pUserName == NULL)? 0: wcslen(pUserName);
  902. AuthI.Domain = (PTCHAR)pDomainName;
  903. AuthI.DomainLength = (pDomainName == NULL)? 0: wcslen(pDomainName);
  904. AuthI.Password = (PTCHAR)pPassword;
  905. AuthI.PasswordLength = (pPassword == NULL)? 0: wcslen(pPassword);
  906. AuthI.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
  907. res = ldap_bind_s(m_ldap, NULL, (TCHAR*)(&AuthI), LDAP_AUTH_NEGOTIATE);
  908. BAIL_IF_LDAP_FAIL(res, "bind with authentication");
  909. res = ldap_bind_s(m_ldapNonGC, NULL, (TCHAR*)(&AuthI), LDAP_AUTH_NEGOTIATE);
  910. BAIL_IF_LDAP_FAIL(res, "bind with authentication - non gc");
  911. }
  912. else
  913. {
  914. // Otherwise we've come in with NULL, NULL, NULL -
  915. // pass in NULL, NULL. The reason do this is that ldap bind code
  916. // does not process NULL, NULL, NULL in the
  917. // SEC_WINNT_AUTH_IDENTITY blob !!!
  918. ULONG res = ldap_bind_s(m_ldap, NULL, NULL, LDAP_AUTH_NEGOTIATE);
  919. BAIL_IF_LDAP_FAIL(res, "bind with NULL NULL NULL");
  920. res = ldap_bind_s(m_ldapNonGC, NULL, NULL, LDAP_AUTH_NEGOTIATE);
  921. BAIL_IF_LDAP_FAIL(res, "bind with NULL NULL NULL - non gc");
  922. }
  923. }
  924. LOG((MSP_TRACE, "CNTDirectory::Bind - exiting OK"));
  925. return S_OK;
  926. }
  927. STDMETHODIMP CNTDirectory::AddDirectoryObject (
  928. IN ITDirectoryObject *pDirectoryObject
  929. )
  930. // add an object to the DS.
  931. {
  932. BAIL_IF_BAD_READ_PTR(pDirectoryObject, E_POINTER);
  933. CLock Lock(m_lock);
  934. if (m_ldap == NULL)
  935. {
  936. LOG((MSP_ERROR, "not connected."));
  937. return RND_NOT_CONNECTED;
  938. }
  939. HRESULT hr;
  940. DIRECTORY_OBJECT_TYPE type;
  941. if (FAILED(hr = pDirectoryObject->get_ObjectType(&type)))
  942. {
  943. return hr;
  944. }
  945. switch (type)
  946. {
  947. case OT_CONFERENCE:
  948. hr = E_NOTIMPL;
  949. break;
  950. case OT_USER:
  951. hr = AddUserIPPhone(pDirectoryObject);
  952. break;
  953. }
  954. return hr;
  955. }
  956. STDMETHODIMP CNTDirectory::ModifyDirectoryObject (
  957. IN ITDirectoryObject *pDirectoryObject
  958. )
  959. // modify an object in the DS
  960. {
  961. BAIL_IF_BAD_READ_PTR(pDirectoryObject, E_POINTER);
  962. CLock Lock(m_lock);
  963. if (m_ldap == NULL)
  964. {
  965. LOG((MSP_ERROR, "not connected."));
  966. return RND_NOT_CONNECTED;
  967. }
  968. HRESULT hr;
  969. DIRECTORY_OBJECT_TYPE type;
  970. if (FAILED(hr = pDirectoryObject->get_ObjectType(&type)))
  971. {
  972. return hr;
  973. }
  974. switch (type)
  975. {
  976. case OT_CONFERENCE:
  977. hr = E_NOTIMPL;
  978. break;
  979. case OT_USER:
  980. hr = AddUserIPPhone(pDirectoryObject);
  981. break;
  982. }
  983. return hr;
  984. }
  985. STDMETHODIMP CNTDirectory::RefreshDirectoryObject (
  986. IN ITDirectoryObject *pDirectoryObject
  987. )
  988. // no refresh is necessary.
  989. {
  990. return S_OK;
  991. }
  992. STDMETHODIMP CNTDirectory::DeleteDirectoryObject (
  993. IN ITDirectoryObject *pDirectoryObject
  994. )
  995. // delete an object in the DS.
  996. {
  997. BAIL_IF_BAD_READ_PTR(pDirectoryObject, E_POINTER);
  998. CLock Lock(m_lock);
  999. if (m_ldap == NULL)
  1000. {
  1001. LOG((MSP_ERROR, "not connected."));
  1002. return RND_NOT_CONNECTED;
  1003. }
  1004. HRESULT hr;
  1005. DIRECTORY_OBJECT_TYPE type;
  1006. if (FAILED(hr = pDirectoryObject->get_ObjectType(&type)))
  1007. {
  1008. return hr;
  1009. }
  1010. switch (type)
  1011. {
  1012. case OT_CONFERENCE:
  1013. hr = E_NOTIMPL;
  1014. break;
  1015. case OT_USER:
  1016. hr = DeleteUserIPPhone(pDirectoryObject);
  1017. break;
  1018. }
  1019. return hr;
  1020. }
  1021. STDMETHODIMP CNTDirectory::get_DirectoryObjects (
  1022. IN DIRECTORY_OBJECT_TYPE DirectoryObjectType,
  1023. IN BSTR pName,
  1024. OUT VARIANT * pVariant
  1025. )
  1026. // look for objects in the ds. returns a collection used in VB.
  1027. {
  1028. BAIL_IF_BAD_READ_PTR(pName, E_POINTER);
  1029. BAIL_IF_BAD_WRITE_PTR(pVariant, E_POINTER);
  1030. CLock Lock(m_lock);
  1031. if (m_ldap == NULL)
  1032. {
  1033. LOG((MSP_ERROR, "not connected."));
  1034. return RND_NOT_CONNECTED;
  1035. }
  1036. HRESULT hr;
  1037. ITDirectoryObject **pObjects;
  1038. DWORD dwSize;
  1039. switch (DirectoryObjectType)
  1040. {
  1041. case OT_CONFERENCE:
  1042. hr = E_NOTIMPL;
  1043. break;
  1044. case OT_USER:
  1045. hr = SearchUser(pName, &pObjects, &dwSize);
  1046. break;
  1047. }
  1048. BAIL_IF_FAIL(hr, "Search for objects");
  1049. hr = CreateInterfaceCollection(dwSize, // count
  1050. &pObjects[0], // begin ptr
  1051. &pObjects[dwSize], // end ptr
  1052. pVariant); // return value
  1053. for (DWORD i = 0; i < dwSize; i ++)
  1054. {
  1055. pObjects[i]->Release();
  1056. }
  1057. delete pObjects;
  1058. BAIL_IF_FAIL(hr, "Create collection of directory objects");
  1059. return hr;
  1060. }
  1061. STDMETHODIMP CNTDirectory::EnumerateDirectoryObjects (
  1062. IN DIRECTORY_OBJECT_TYPE DirectoryObjectType,
  1063. IN BSTR pName,
  1064. OUT IEnumDirectoryObject ** ppEnumObject
  1065. )
  1066. // Enumerated object in ds.
  1067. {
  1068. BAIL_IF_BAD_READ_PTR(pName, E_POINTER);
  1069. BAIL_IF_BAD_WRITE_PTR(ppEnumObject, E_POINTER);
  1070. CLock Lock(m_lock);
  1071. if (m_ldap == NULL)
  1072. {
  1073. LOG((MSP_ERROR, "not connected."));
  1074. return RND_NOT_CONNECTED;
  1075. }
  1076. HRESULT hr;
  1077. ITDirectoryObject **pObjects;
  1078. DWORD dwSize;
  1079. switch (DirectoryObjectType)
  1080. {
  1081. case OT_CONFERENCE:
  1082. hr = E_NOTIMPL;
  1083. break;
  1084. case OT_USER:
  1085. hr = SearchUser(pName, &pObjects, &dwSize);
  1086. break;
  1087. }
  1088. BAIL_IF_FAIL(hr, "Search for objects");
  1089. hr = ::CreateDirectoryObjectEnumerator(
  1090. &pObjects[0],
  1091. &pObjects[dwSize],
  1092. ppEnumObject
  1093. );
  1094. for (DWORD i = 0; i < dwSize; i ++)
  1095. {
  1096. pObjects[i]->Release();
  1097. }
  1098. delete pObjects;
  1099. BAIL_IF_FAIL(hr, "Create enumerator of directory objects");
  1100. return hr;
  1101. }