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.

356 lines
9.0 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // FILE
  4. //
  5. // ldapcxn.cpp
  6. //
  7. // SYNOPSIS
  8. //
  9. // This file defines the class LDAPConnection.
  10. //
  11. // MODIFICATION HISTORY
  12. //
  13. // 05/08/1998 Original version.
  14. // 09/16/1998 Perform access check after bind.
  15. // 03/23/1999 Set timeout for connect.
  16. // 04/14/1999 Specify domain and server when opening a connection.
  17. // 07/09/1999 Disable auto reconnect.
  18. // 09/14/1999 Always specify timeout for LDAP searches.
  19. // 01/25/2000 Encrypt LDAP connections.
  20. // Pass server name (not domain) to ldap_initW.
  21. //
  22. ///////////////////////////////////////////////////////////////////////////////
  23. #include <ias.h>
  24. #include <iasutil.h>
  25. #include <ntldap.h>
  26. #define SECURITY_WIN32
  27. #include <security.h>
  28. #include <new>
  29. #include <ldapcxn.h>
  30. // Search timeout.
  31. LDAP_TIMEVAL LDAPConnection::SEARCH_TIMEOUT = { 10, 0 };
  32. namespace
  33. {
  34. // Timeout for LDAP connects.
  35. LDAP_TIMEVAL CONNECT_TIMEOUT = { 15, 0 };
  36. // RDN for the dummy object used for access checks.
  37. const WCHAR DUMMY_OBJECT[] =
  38. L"CN=RAS and IAS Servers Access Check,CN=System,";
  39. /* Credentials for Kerberos-only bind.
  40. SEC_WINNT_AUTH_IDENTITY_EXW BIND_CREDENTIALS =
  41. {
  42. SEC_WINNT_AUTH_IDENTITY_VERSION,
  43. sizeof(SEC_WINNT_AUTH_IDENTITY_EXW),
  44. NULL,
  45. 0,
  46. NULL,
  47. 0,
  48. NULL,
  49. 0,
  50. SEC_WINNT_AUTH_IDENTITY_UNICODE,
  51. L"Kerberos",
  52. 8
  53. };
  54. */
  55. }
  56. void LDAPConnection::Release() throw ()
  57. {
  58. if (!InterlockedDecrement(&refCount))
  59. {
  60. delete this;
  61. }
  62. }
  63. DWORD LDAPConnection::createInstance(
  64. PCWSTR domain,
  65. PCWSTR server,
  66. LDAPConnection** cxn
  67. ) throw ()
  68. {
  69. DWORD status;
  70. ULONG opt;
  71. // Check the input parameters.
  72. if (cxn == NULL) { return ERROR_INVALID_PARAMETER; }
  73. // Initialize the connection.
  74. LDAP* ld = ldap_initW(
  75. const_cast<PWCHAR>(server),
  76. LDAP_PORT
  77. );
  78. if (ld == NULL)
  79. {
  80. return LdapMapErrorToWin32(LdapGetLastError());
  81. }
  82. // Set the domain name.
  83. status = ldap_set_optionW(ld, LDAP_OPT_DNSDOMAIN_NAME, &domain);
  84. if (status != LDAP_SUCCESS)
  85. {
  86. IASTraceLdapFailure("ldap_set_optionW", status, ld);
  87. ldap_unbind(ld);
  88. return LdapMapErrorToWin32(status);
  89. }
  90. opt = PtrToUlong(LDAP_OPT_ON);
  91. status = ldap_set_optionW(ld, LDAP_OPT_AREC_EXCLUSIVE, &opt);
  92. if (status != LDAP_SUCCESS)
  93. {
  94. IASTraceLdapFailure("ldap_set_optionW", status, ld);
  95. ldap_unbind(ld);
  96. return LdapMapErrorToWin32(status);
  97. }
  98. // Turn on encryption.
  99. opt = PtrToUlong(LDAP_OPT_ON);
  100. status = ldap_set_optionW(ld, LDAP_OPT_ENCRYPT, &opt);
  101. if (status != LDAP_SUCCESS)
  102. {
  103. IASTraceLdapFailure("ldap_set_optionW", status, ld);
  104. ldap_unbind(ld);
  105. return LdapMapErrorToWin32(status);
  106. }
  107. // Connect to the server ...
  108. status = ldap_connect(ld, &CONNECT_TIMEOUT);
  109. if (status == LDAP_SUCCESS)
  110. {
  111. // ... and bind the connection.
  112. status = ldap_bind_sW(ld, NULL, NULL, LDAP_AUTH_NEGOTIATE);
  113. if (status != LDAP_SUCCESS)
  114. {
  115. IASTraceLdapFailure("ldap_bind_s", status, ld);
  116. ldap_unbind(ld);
  117. return LdapMapErrorToWin32(status);
  118. }
  119. }
  120. else
  121. {
  122. IASTraceLdapFailure("ldap_connect", status, ld);
  123. ldap_unbind(ld);
  124. return LdapMapErrorToWin32(status);
  125. }
  126. // Turn off automatic chasing of referrals.
  127. opt = PtrToUlong(LDAP_OPT_OFF);
  128. ldap_set_option(ld, LDAP_OPT_REFERRALS, &opt);
  129. // Turn off auto reconnect of bad connections.
  130. opt = PtrToUlong(LDAP_OPT_OFF);
  131. ldap_set_option(ld, LDAP_OPT_AUTO_RECONNECT, &opt);
  132. // Turn on ldap_conn_from_msg.
  133. opt = PtrToUlong(LDAP_OPT_ON);
  134. ldap_set_option(ld, LDAP_OPT_REF_DEREF_CONN_PER_MSG, &opt);
  135. // Create the LDAPConnection wrapper.
  136. LDAPConnection* newCxn = new (std::nothrow) LDAPConnection(ld);
  137. if (newCxn == NULL)
  138. {
  139. ldap_unbind(ld);
  140. return ERROR_NOT_ENOUGH_MEMORY;
  141. }
  142. // Read the RootDSE.
  143. status = newCxn->readRootDSE();
  144. // Check access permissions.
  145. if (status == NO_ERROR)
  146. {
  147. status = newCxn->checkAccess();
  148. }
  149. // Process the result.
  150. if (status == NO_ERROR)
  151. {
  152. *cxn = newCxn;
  153. }
  154. else
  155. {
  156. newCxn->Release();
  157. }
  158. return status;
  159. }
  160. //////////
  161. //
  162. // NOTE: This doesn't have to be serialized since it's only called from
  163. // within createInstance.
  164. //
  165. //////////
  166. DWORD LDAPConnection::checkAccess() throw ()
  167. {
  168. // Allocate a temporary buffer for the DN.
  169. size_t len = wcslen(base) * sizeof(WCHAR) + sizeof(DUMMY_OBJECT);
  170. PWSTR dn = (PWSTR)_alloca(len);
  171. // Construct the DN of the dummy object.
  172. memcpy(dn, DUMMY_OBJECT, sizeof(DUMMY_OBJECT));
  173. wcscat(dn, base);
  174. // Try to read the dummy object.
  175. PWCHAR attrs[] = { L"CN", NULL };
  176. LDAPMessage* res = NULL;
  177. ULONG ldapError = ldap_search_ext_sW(
  178. connection,
  179. dn,
  180. LDAP_SCOPE_BASE,
  181. L"(objectclass=*)",
  182. attrs,
  183. TRUE,
  184. NULL,
  185. NULL,
  186. &SEARCH_TIMEOUT,
  187. 0,
  188. &res
  189. );
  190. // We have two different error codes.
  191. if (ldapError == LDAP_SUCCESS)
  192. {
  193. ldapError = res->lm_returncode;
  194. }
  195. DWORD status = NO_ERROR;
  196. if (ldapError != LDAP_SUCCESS)
  197. {
  198. TraceFailure("ldap_search_ext_sW", ldapError);
  199. status = LdapMapErrorToWin32(ldapError);
  200. }
  201. else
  202. {
  203. // Get the first attribute from the first entry.
  204. BerElement* ptr;
  205. PWCHAR attr = ldap_first_attributeW(
  206. connection,
  207. ldap_first_entry(connection, res),
  208. &ptr
  209. );
  210. // If we couldn't read any attributes, then we must not be a member
  211. // of the RAS and IAS Servers group.
  212. if (attr == NULL)
  213. {
  214. status = ERROR_ACCESS_DENIED;
  215. }
  216. }
  217. ldap_msgfree(res);
  218. return status;
  219. }
  220. //////////
  221. //
  222. // NOTE: This doesn't have to be serialized since it's only called from
  223. // within createInstance.
  224. //
  225. //////////
  226. DWORD LDAPConnection::readRootDSE() throw ()
  227. {
  228. //////////
  229. // Read the defaultNamingContext from the RootDSE.
  230. //////////
  231. PWCHAR attrs[] = { LDAP_OPATT_DEFAULT_NAMING_CONTEXT_W, NULL };
  232. LDAPMessage* res = NULL;
  233. ULONG ldapError = ldap_search_ext_sW(
  234. connection,
  235. L"",
  236. LDAP_SCOPE_BASE,
  237. L"(objectclass=*)",
  238. attrs,
  239. 0,
  240. NULL,
  241. NULL,
  242. &SEARCH_TIMEOUT,
  243. 0,
  244. &res
  245. );
  246. // We have two different error codes.
  247. if (ldapError == LDAP_SUCCESS)
  248. {
  249. ldapError = res->lm_returncode;
  250. }
  251. if (ldapError != LDAP_SUCCESS)
  252. {
  253. TraceFailure("ldap_search_ext_sW", ldapError);
  254. ldap_msgfree(res);
  255. return LdapMapErrorToWin32(ldapError);
  256. }
  257. //////////
  258. // The search succeeded, so get the attribute value.
  259. //////////
  260. PWCHAR* vals = ldap_get_valuesW(
  261. connection,
  262. ldap_first_entry(connection, res),
  263. LDAP_OPATT_DEFAULT_NAMING_CONTEXT_W
  264. );
  265. DWORD status;
  266. if (vals && *vals)
  267. {
  268. // We got something, so save the value.
  269. base = ias_wcsdup(*vals);
  270. status = base ? NO_ERROR : ERROR_NOT_ENOUGH_MEMORY;
  271. }
  272. else
  273. {
  274. // It's a mandatory attribute but we can't see it, so we must not have
  275. // sufficient permission.
  276. status = ERROR_ACCESS_DENIED;
  277. }
  278. ldap_value_freeW(vals);
  279. ldap_msgfree(res);
  280. return status;
  281. }
  282. void LDAPConnection::TraceFailure(
  283. PCSTR functionName,
  284. ULONG errorCode
  285. )
  286. {
  287. IASTraceLdapFailure(functionName, errorCode, connection);
  288. }
  289. void IASTraceLdapFailure(
  290. PCSTR functionName,
  291. ULONG errorCode,
  292. LDAP* cxn
  293. )
  294. {
  295. _ASSERT(functionName != NULL);
  296. IASTracePrintf("LDAP ERROR in %s. Code = %d", functionName, errorCode);
  297. PWCHAR errorString = NULL;
  298. ULONG result = ldap_get_optionW(
  299. cxn,
  300. LDAP_OPT_SERVER_ERROR,
  301. (void*) &errorString
  302. );
  303. if (result == LDAP_SUCCESS)
  304. {
  305. IASTracePrintf("Extended error string: %S", errorString);
  306. // do what you want with the string here
  307. ldap_memfree(errorString);
  308. }
  309. }