Leaked source code of windows server 2003
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.

317 lines
6.9 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. IASGlobalLockSentry sentry;
  39. ++refCount;
  40. return NO_ERROR;
  41. }
  42. VOID
  43. WINAPI
  44. IASNtdsUninitialize( VOID )
  45. {
  46. IASGlobalLockSentry sentry;
  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->TraceFailure("ldap_search_ext_sW", error);
  96. cxn->disable();
  97. error = LdapMapErrorToWin32(error);
  98. }
  99. else if ((*res)->lm_returncode != LDAP_SUCCESS)
  100. {
  101. error = LdapMapErrorToWin32((*res)->lm_returncode);
  102. cxn->TraceFailure("ldap_search_ext_sW", (*res)->lm_returncode);
  103. }
  104. else if (ldap_count_entries(*cxn, *res) != 1)
  105. {
  106. error = ERROR_NO_SUCH_USER;
  107. }
  108. else
  109. {
  110. return NO_ERROR;
  111. }
  112. //////////
  113. // The search failed, so clean-up.
  114. //////////
  115. if (*res != NULL)
  116. {
  117. ldap_msgfree(*res);
  118. *res = NULL;
  119. }
  120. return error;
  121. }
  122. //////////
  123. // Constants used for building LDAP search filters.
  124. //////////
  125. const WCHAR USER_FILTER_PREFIX[] = L"(sAMAccountName=";
  126. const WCHAR USER_FILTER_SUFFIX[] = L")";
  127. const size_t MAX_USERNAME_LENGTH = 256;
  128. const size_t USER_FILTER_LENGTH = sizeof(USER_FILTER_PREFIX)/sizeof(WCHAR) +
  129. MAX_USERNAME_LENGTH +
  130. sizeof(USER_FILTER_SUFFIX)/sizeof(WCHAR);
  131. //////////
  132. // Empty attribute list.
  133. //////////
  134. PWCHAR NO_ATTRS[] = { L"cn", NULL };
  135. DWORD
  136. WINAPI
  137. QueryUserAttributesOnce(
  138. IN PCWSTR domainName,
  139. IN PCWSTR username,
  140. IN ULONG scope,
  141. IN PWCHAR attrs[],
  142. OUT PIAS_NTDS_RESULT result
  143. )
  144. {
  145. //////////
  146. // Retrieve a connection to the domain.
  147. //////////
  148. CComPtr<LDAPConnection> cxn;
  149. DWORD error = theDomains.getConnection(domainName, &cxn);
  150. switch (error)
  151. {
  152. case NO_ERROR:
  153. {
  154. IASTracePrintf("Sending LDAP search to %s.", cxn->getHost());
  155. break;
  156. }
  157. case ERROR_DS_NOT_INSTALLED:
  158. {
  159. IASTracePrintf("DS not installed for domain %S.", domainName);
  160. return error;
  161. }
  162. default:
  163. {
  164. IASTracePrintf("Could not open an LDAP connection to domain %S.",
  165. domainName);
  166. IASTraceFailure("NTDomain::getConnection", error);
  167. return error;
  168. }
  169. }
  170. //////////
  171. // Initialize the search filter.
  172. //////////
  173. WCHAR searchFilter[USER_FILTER_LENGTH];
  174. wcscpy (searchFilter, USER_FILTER_PREFIX);
  175. wcsncat(searchFilter, username, MAX_USERNAME_LENGTH);
  176. wcscat (searchFilter, USER_FILTER_SUFFIX);
  177. //////////
  178. // Query the DS. If scope == LDAP_SCOPE_BASE, then we won't retrieve the
  179. // actual attributes yet.
  180. //////////
  181. LDAPMessage* res;
  182. error = GetSingleEntry(
  183. cxn,
  184. const_cast<PWCHAR>(cxn->getBase()),
  185. LDAP_SCOPE_SUBTREE,
  186. searchFilter,
  187. (scope == LDAP_SCOPE_BASE ? NO_ATTRS : attrs),
  188. &res
  189. );
  190. if (error == NO_ERROR && scope == LDAP_SCOPE_BASE)
  191. {
  192. // All we care about is the user's DN.
  193. PWCHAR dn = ldap_get_dnW(*cxn, ldap_first_entry(*cxn, res));
  194. ldap_msgfree(res);
  195. // Now get the actual attributes.
  196. error = GetSingleEntry(
  197. cxn,
  198. dn,
  199. LDAP_SCOPE_BASE,
  200. L"(objectclass=*)",
  201. attrs,
  202. &res
  203. );
  204. ldap_memfree(dn);
  205. }
  206. if (error == NO_ERROR)
  207. {
  208. LDAPConnection* rawCxn = cxn;
  209. rawCxn->AddRef();
  210. result->cxn = rawCxn;
  211. result->msg = res;
  212. }
  213. return error;
  214. }
  215. DWORD
  216. WINAPI
  217. IASNtdsQueryUserAttributes(
  218. IN PCWSTR domainName,
  219. IN PCWSTR username,
  220. IN ULONG scope,
  221. IN PWCHAR attrs[],
  222. OUT PIAS_NTDS_RESULT result
  223. )
  224. {
  225. if (result == 0)
  226. {
  227. return ERROR_INVALID_PARAMETER;
  228. }
  229. result->cxn = 0;
  230. result->msg = 0;
  231. DWORD retval = QueryUserAttributesOnce(
  232. domainName,
  233. username,
  234. scope,
  235. attrs,
  236. result
  237. );
  238. switch (retval)
  239. {
  240. case NO_ERROR:
  241. case ERROR_DS_NOT_INSTALLED:
  242. case ERROR_NO_SUCH_USER:
  243. case ERROR_ACCESS_DENIED:
  244. return retval;
  245. }
  246. IASTraceString("Retrying LDAP search.");
  247. return QueryUserAttributesOnce(
  248. domainName,
  249. username,
  250. scope,
  251. attrs,
  252. result
  253. );
  254. }
  255. VOID
  256. WINAPI
  257. IASNtdsFreeResult(
  258. PIAS_NTDS_RESULT result
  259. )
  260. {
  261. if (result != 0)
  262. {
  263. if (result->cxn != 0)
  264. {
  265. static_cast<LDAPConnection*>(result->cxn)->Release();
  266. result->cxn = 0;
  267. }
  268. if (result->msg != 0)
  269. {
  270. ldap_msgfree(result->msg);
  271. result->msg = 0;
  272. }
  273. }
  274. }