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.

291 lines
7.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. ldap_set_optionW(ld, LDAP_OPT_DNSDOMAIN_NAME, &domain);
  84. // Turn on encryption.
  85. opt = PtrToUlong(LDAP_OPT_ON);
  86. ldap_set_option(ld, LDAP_OPT_ENCRYPT, &opt);
  87. // Connect to the server ...
  88. status = ldap_connect(ld, &CONNECT_TIMEOUT);
  89. if (status == LDAP_SUCCESS)
  90. {
  91. // ... and bind the connection.
  92. status = ldap_bind_sW(ld, NULL, NULL, LDAP_AUTH_NEGOTIATE);
  93. }
  94. if (status != LDAP_SUCCESS)
  95. {
  96. ldap_unbind(ld);
  97. return LdapMapErrorToWin32(status);
  98. }
  99. // Turn off automatic chasing of referrals.
  100. opt = PtrToUlong(LDAP_OPT_OFF);
  101. ldap_set_option(ld, LDAP_OPT_REFERRALS, &opt);
  102. // Turn off auto reconnect of bad connections.
  103. opt = PtrToUlong(LDAP_OPT_OFF);
  104. ldap_set_option(ld, LDAP_OPT_AUTO_RECONNECT, &opt);
  105. // Turn on ldap_conn_from_msg.
  106. opt = PtrToUlong(LDAP_OPT_ON);
  107. ldap_set_option(ld, LDAP_OPT_REF_DEREF_CONN_PER_MSG, &opt);
  108. // Create the LDAPConnection wrapper.
  109. LDAPConnection* newCxn = new (std::nothrow) LDAPConnection(ld);
  110. if (newCxn == NULL)
  111. {
  112. ldap_unbind(ld);
  113. return ERROR_NOT_ENOUGH_MEMORY;
  114. }
  115. // Read the RootDSE.
  116. status = newCxn->readRootDSE();
  117. // Check access permissions.
  118. if (status == NO_ERROR) { status = newCxn->checkAccess(); }
  119. // Process the result.
  120. if (status == NO_ERROR)
  121. {
  122. *cxn = newCxn;
  123. }
  124. else
  125. {
  126. newCxn->Release();
  127. }
  128. return status;
  129. }
  130. //////////
  131. //
  132. // NOTE: This doesn't have to be serialized since it's only called from
  133. // within createInstance.
  134. //
  135. //////////
  136. DWORD LDAPConnection::checkAccess() throw ()
  137. {
  138. // Allocate a temporary buffer for the DN.
  139. size_t len = wcslen(base) * sizeof(WCHAR) + sizeof(DUMMY_OBJECT);
  140. PWSTR dn = (PWSTR)_alloca(len);
  141. // Construct the DN of the dummy object.
  142. memcpy(dn, DUMMY_OBJECT, sizeof(DUMMY_OBJECT));
  143. wcscat(dn, base);
  144. // Try to read the dummy object.
  145. PWCHAR attrs[] = { L"CN", NULL };
  146. LDAPMessage* res = NULL;
  147. ULONG ldapError = ldap_search_ext_sW(
  148. connection,
  149. dn,
  150. LDAP_SCOPE_BASE,
  151. L"(objectclass=*)",
  152. attrs,
  153. TRUE,
  154. NULL,
  155. NULL,
  156. &SEARCH_TIMEOUT,
  157. 0,
  158. &res
  159. );
  160. // We have two different error codes.
  161. if (ldapError == LDAP_SUCCESS)
  162. {
  163. ldapError = res->lm_returncode;
  164. }
  165. DWORD status = NO_ERROR;
  166. if (ldapError != LDAP_SUCCESS)
  167. {
  168. status = LdapMapErrorToWin32(ldapError);
  169. }
  170. else
  171. {
  172. // Get the first attribute from the first entry.
  173. BerElement* ptr;
  174. PWCHAR attr = ldap_first_attributeW(
  175. connection,
  176. ldap_first_entry(connection, res),
  177. &ptr
  178. );
  179. // If we couldn't read any attributes, then we must not be a member
  180. // of the RAS and IAS Servers group.
  181. if (attr == NULL) { status = ERROR_ACCESS_DENIED; }
  182. }
  183. ldap_msgfree(res);
  184. return status;
  185. }
  186. //////////
  187. //
  188. // NOTE: This doesn't have to be serialized since it's only called from
  189. // within createInstance.
  190. //
  191. //////////
  192. DWORD LDAPConnection::readRootDSE() throw ()
  193. {
  194. //////////
  195. // Read the defaultNamingContext from the RootDSE.
  196. //////////
  197. PWCHAR attrs[] = { LDAP_OPATT_DEFAULT_NAMING_CONTEXT_W, NULL };
  198. LDAPMessage* res = NULL;
  199. ULONG ldapError = ldap_search_ext_sW(
  200. connection,
  201. L"",
  202. LDAP_SCOPE_BASE,
  203. L"(objectclass=*)",
  204. attrs,
  205. 0,
  206. NULL,
  207. NULL,
  208. &SEARCH_TIMEOUT,
  209. 0,
  210. &res
  211. );
  212. // We have two different error codes.
  213. if (ldapError == LDAP_SUCCESS)
  214. {
  215. ldapError = res->lm_returncode;
  216. }
  217. if (ldapError != LDAP_SUCCESS)
  218. {
  219. ldap_msgfree(res);
  220. return LdapMapErrorToWin32(ldapError);
  221. }
  222. //////////
  223. // The search succeeded, so get the attribute value.
  224. //////////
  225. PWCHAR* vals = ldap_get_valuesW(
  226. connection,
  227. ldap_first_entry(connection, res),
  228. LDAP_OPATT_DEFAULT_NAMING_CONTEXT_W
  229. );
  230. DWORD status;
  231. if (vals && *vals)
  232. {
  233. // We got something, so save the value.
  234. base = ias_wcsdup(*vals);
  235. status = base ? NO_ERROR : ERROR_NOT_ENOUGH_MEMORY;
  236. }
  237. else
  238. {
  239. // It's a mandatory attribute but we can't see it, so we must not have
  240. // sufficient permission.
  241. status = ERROR_ACCESS_DENIED;
  242. }
  243. ldap_value_freeW(vals);
  244. ldap_msgfree(res);
  245. return status;
  246. }