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.

275 lines
5.8 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // FILE
  4. //
  5. // iasntds.cpp
  6. //
  7. // SYNOPSIS
  8. //
  9. // Defines global objects and functions for the IAS NTDS API.
  10. //
  11. // MODIFICATION HISTORY
  12. //
  13. // 05/11/1998 Original version.
  14. // 07/13/1998 Clean up header file dependencies.
  15. // 08/25/1998 Added IASNtdsQueryUserAttributes.
  16. // 09/02/1998 Added 'scope' parameter to IASNtdsQueryUserAttributes.
  17. // 03/10/1999 Added IASNtdsIsNativeModeDomain.
  18. // 03/23/1999 Retry failed searches.
  19. // 05/11/1999 Ask for at least one attribute or else you get them all.
  20. // 09/14/1999 Move SEARCH_TIMEOUT to LDAPConnection.
  21. //
  22. ///////////////////////////////////////////////////////////////////////////////
  23. #include <ias.h>
  24. #include <iasntds.h>
  25. #include <ldapcxn.h>
  26. #include <ntcache.h>
  27. namespace
  28. {
  29. // Global object caches.
  30. NTCache theDomains;
  31. // Initialization reference count.
  32. LONG refCount = 0;
  33. }
  34. DWORD
  35. WINAPI
  36. IASNtdsInitialize( VOID )
  37. {
  38. std::_Lockit _Lk;
  39. ++refCount;
  40. return NO_ERROR;
  41. }
  42. VOID
  43. WINAPI
  44. IASNtdsUninitialize( VOID )
  45. {
  46. std::_Lockit _Lk;
  47. if (--refCount == 0)
  48. {
  49. theDomains.clear();
  50. }
  51. }
  52. BOOL
  53. WINAPI
  54. IASNtdsIsNativeModeDomain(
  55. IN PCWSTR domain
  56. )
  57. {
  58. return theDomains.getMode(domain) == NTDomain::MODE_NATIVE;
  59. }
  60. //////////
  61. // Retrieve a single entry from the DS. Handles all clean-up on failure.
  62. //////////
  63. DWORD
  64. WINAPI
  65. GetSingleEntry(
  66. LDAPConnection* cxn,
  67. PWCHAR base,
  68. ULONG scope,
  69. PWCHAR filter,
  70. PWCHAR attrs[],
  71. LDAPMessage **res
  72. ) throw ()
  73. {
  74. //////////
  75. // Perform the search.
  76. //////////
  77. ULONG error = ldap_search_ext_sW(
  78. *cxn,
  79. base,
  80. scope,
  81. filter,
  82. attrs,
  83. FALSE,
  84. NULL,
  85. NULL,
  86. &LDAPConnection::SEARCH_TIMEOUT,
  87. 0,
  88. res
  89. );
  90. //////////
  91. // Process the results.
  92. //////////
  93. if (error != LDAP_SUCCESS && error != LDAP_PARTIAL_RESULTS)
  94. {
  95. cxn->disable();
  96. error = LdapMapErrorToWin32(error);
  97. }
  98. else if ((*res)->lm_returncode != LDAP_SUCCESS)
  99. {
  100. error = LdapMapErrorToWin32((*res)->lm_returncode);
  101. }
  102. else if (ldap_count_entries(*cxn, *res) != 1)
  103. {
  104. error = ERROR_NO_SUCH_USER;
  105. }
  106. else
  107. {
  108. return NO_ERROR;
  109. }
  110. //////////
  111. // The search failed, so clean-up.
  112. //////////
  113. ldap_msgfree(*res);
  114. *res = NULL;
  115. IASTraceFailure("ldap_search_ext_sW", error);
  116. return error;
  117. }
  118. //////////
  119. // Constants used for building LDAP search filters.
  120. //////////
  121. const WCHAR USER_FILTER_PREFIX[] = L"(sAMAccountName=";
  122. const WCHAR USER_FILTER_SUFFIX[] = L")";
  123. const size_t MAX_USERNAME_LENGTH = 256;
  124. const size_t USER_FILTER_LENGTH = sizeof(USER_FILTER_PREFIX)/sizeof(WCHAR) +
  125. MAX_USERNAME_LENGTH +
  126. sizeof(USER_FILTER_SUFFIX)/sizeof(WCHAR);
  127. //////////
  128. // Empty attribute list.
  129. //////////
  130. PWCHAR NO_ATTRS[] = { L"cn", NULL };
  131. DWORD
  132. WINAPI
  133. QueryUserAttributesOnce(
  134. IN PCWSTR domainName,
  135. IN PCWSTR username,
  136. IN ULONG scope,
  137. IN PWCHAR attrs[],
  138. OUT LDAPMessage** res
  139. )
  140. {
  141. //////////
  142. // Retrieve a connection to the domain.
  143. //////////
  144. CComPtr<LDAPConnection> cxn;
  145. DWORD error = theDomains.getConnection(domainName, &cxn);
  146. switch (error)
  147. {
  148. case NO_ERROR:
  149. {
  150. IASTracePrintf("Sending LDAP search to %s.", cxn->getHost());
  151. break;
  152. }
  153. case ERROR_DS_NOT_INSTALLED:
  154. {
  155. IASTracePrintf("DS not installed for domain %S.", domainName);
  156. return error;
  157. }
  158. default:
  159. {
  160. IASTraceFailure("NTDomain::getConnection", error);
  161. IASTracePrintf("Could not open an LDAP connection to domain %S.",
  162. domainName);
  163. return error;
  164. }
  165. }
  166. //////////
  167. // Initialize the search filter.
  168. //////////
  169. WCHAR searchFilter[USER_FILTER_LENGTH];
  170. wcscpy (searchFilter, USER_FILTER_PREFIX);
  171. wcsncat(searchFilter, username, MAX_USERNAME_LENGTH);
  172. wcscat (searchFilter, USER_FILTER_SUFFIX);
  173. //////////
  174. // Query the DS. If scope == LDAP_SCOPE_BASE, then we won't retrieve the
  175. // actual attributes yet.
  176. //////////
  177. error = GetSingleEntry(
  178. cxn,
  179. const_cast<PWCHAR>(cxn->getBase()),
  180. LDAP_SCOPE_SUBTREE,
  181. searchFilter,
  182. (scope == LDAP_SCOPE_BASE ? NO_ATTRS : attrs),
  183. res
  184. );
  185. if (error == NO_ERROR && scope == LDAP_SCOPE_BASE)
  186. {
  187. // All we care about is the user's DN.
  188. PWCHAR dn = ldap_get_dnW(*cxn, ldap_first_entry(*cxn, *res));
  189. ldap_msgfree(*res);
  190. // Now get the actual attributes.
  191. error = GetSingleEntry(
  192. cxn,
  193. dn,
  194. LDAP_SCOPE_BASE,
  195. L"(objectclass=*)",
  196. attrs,
  197. res
  198. );
  199. ldap_memfree(dn);
  200. }
  201. return error;
  202. }
  203. DWORD
  204. WINAPI
  205. IASNtdsQueryUserAttributes(
  206. IN PCWSTR domainName,
  207. IN PCWSTR username,
  208. IN ULONG scope,
  209. IN PWCHAR attrs[],
  210. OUT LDAPMessage** res
  211. )
  212. {
  213. DWORD retval = QueryUserAttributesOnce(
  214. domainName,
  215. username,
  216. scope,
  217. attrs,
  218. res
  219. );
  220. switch (retval)
  221. {
  222. case NO_ERROR:
  223. case ERROR_DS_NOT_INSTALLED:
  224. case ERROR_NO_SUCH_USER:
  225. case ERROR_ACCESS_DENIED:
  226. return retval;
  227. }
  228. IASTraceString("Retrying LDAP search.");
  229. return QueryUserAttributesOnce(
  230. domainName,
  231. username,
  232. scope,
  233. attrs,
  234. res
  235. );
  236. }