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.

368 lines
8.5 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // FILE
  4. //
  5. // ntdomain.cpp
  6. //
  7. // SYNOPSIS
  8. //
  9. // Defines the clas NTDomain.
  10. //
  11. // MODIFICATION HISTORY
  12. //
  13. // 05/07/1998 Original version.
  14. // 06/23/1998 Changes to DCLocator. Use ntldap constants.
  15. // 07/13/1998 Clean up header file dependencies.
  16. // 02/18/1999 Connect by DNS name not address.
  17. // 03/10/1999 Cache mixed-mode and native-mode connections.
  18. // 03/12/1999 Do not perform I/O from constructor.
  19. // 04/14/1999 Specify domain and server when opening a connection.
  20. // 09/14/1999 Always specify timeout for LDAP searches.
  21. //
  22. ///////////////////////////////////////////////////////////////////////////////
  23. #include <ias.h>
  24. #include <iasutil.h>
  25. #include <iaslsa.h>
  26. #include <iasntds.h>
  27. #include <lm.h>
  28. #include <ldapcxn.h>
  29. #include <limits.h>
  30. #include <ntdomain.h>
  31. //////////
  32. // Attributes of interest.
  33. //////////
  34. const WCHAR NT_MIXED_DOMAIN[] = L"nTMixedDomain";
  35. //////////
  36. // Default search filter.
  37. //////////
  38. const WCHAR ANY_OBJECT[] = L"(objectclass=*)";
  39. //////////
  40. // Domain attributes that we need.
  41. //////////
  42. const PCWSTR DOMAIN_ATTRS[] = {
  43. NT_MIXED_DOMAIN,
  44. NULL
  45. };
  46. //////////
  47. // Utility function for getting the current system time as a 64-bit integer.
  48. //////////
  49. inline DWORDLONG GetSystemTimeAsDWORDLONG() throw ()
  50. {
  51. ULARGE_INTEGER ft;
  52. GetSystemTimeAsFileTime((LPFILETIME)&ft);
  53. return ft.QuadPart;
  54. }
  55. //////////
  56. // Number of 100 nsec intervals in one second.
  57. //////////
  58. const DWORDLONG ONE_SECOND = 10000000ui64;
  59. //////////
  60. // Defaults for poll interval and retry interval.
  61. //////////
  62. DWORDLONG NTDomain::pollInterval = 60 * 60 * ONE_SECOND;
  63. DWORDLONG NTDomain::retryInterval = 1 * 60 * ONE_SECOND;
  64. inline NTDomain::NTDomain(PWSTR domainName)
  65. : refCount(1),
  66. name(domainName),
  67. mode(MODE_UNKNOWN),
  68. connection(NULL),
  69. status(NO_ERROR),
  70. expiry(0)
  71. { }
  72. inline NTDomain::~NTDomain()
  73. {
  74. if (connection) { connection->Release(); }
  75. delete[] name;
  76. }
  77. inline BOOL NTDomain::isExpired() throw ()
  78. {
  79. return GetSystemTimeAsDWORDLONG() >= expiry;
  80. }
  81. inline BOOL NTDomain::isConnected() throw ()
  82. {
  83. if (connection && connection->isDisabled())
  84. {
  85. closeConnection();
  86. }
  87. return connection != NULL;
  88. }
  89. void NTDomain::Release() throw ()
  90. {
  91. if (!InterlockedDecrement(&refCount))
  92. {
  93. delete this;
  94. }
  95. }
  96. DWORD NTDomain::getConnection(LDAPConnection** cxn) throw ()
  97. {
  98. Lock();
  99. // Is it time to try for a new connection ?
  100. if (!isConnected() && isExpired())
  101. {
  102. findServer();
  103. }
  104. // Return the current connection ...
  105. if (*cxn = connection) { (*cxn)->AddRef(); }
  106. // ... and status to the caller.
  107. DWORD retval = status;
  108. Unlock();
  109. return retval;
  110. }
  111. NTDomain::Mode NTDomain::getMode() throw ()
  112. {
  113. Lock();
  114. if (isExpired())
  115. {
  116. if (isConnected())
  117. {
  118. readDomainMode();
  119. }
  120. else
  121. {
  122. findServer();
  123. }
  124. }
  125. Mode retval = mode;
  126. Unlock();
  127. return retval;
  128. }
  129. NTDomain* NTDomain::createInstance(PCWSTR name) throw ()
  130. {
  131. // We copy the domain name here, so that we don't have to throw an
  132. // exception from the constructor.
  133. PWSTR nameCopy = ias_wcsdup(name);
  134. if (!nameCopy) { return NULL; }
  135. return new (std::nothrow) NTDomain(nameCopy);
  136. }
  137. void NTDomain::openConnection(
  138. PCWSTR domain,
  139. PCWSTR server
  140. ) throw ()
  141. {
  142. closeConnection();
  143. IASTracePrintf("Opening LDAP connection to %S.", server);
  144. status = LDAPConnection::createInstance(
  145. domain,
  146. server,
  147. &connection
  148. );
  149. if (status == ERROR_ACCESS_DENIED)
  150. {
  151. IASTraceString("Access denied -- purging Kerberos ticket cache.");
  152. IASPurgeTicketCache();
  153. IASTracePrintf("Retrying LDAP connection to %S.", server);
  154. status = LDAPConnection::createInstance(
  155. domain,
  156. server,
  157. &connection
  158. );
  159. }
  160. if (status == NO_ERROR) { readDomainMode(); }
  161. if (status == NO_ERROR)
  162. {
  163. IASTraceString("LDAP connect succeeded.");
  164. }
  165. else
  166. {
  167. IASTraceFailure("LDAP connect", status);
  168. }
  169. }
  170. void NTDomain::closeConnection() throw ()
  171. {
  172. if (connection)
  173. {
  174. connection->Release();
  175. connection = NULL;
  176. }
  177. expiry = 0;
  178. }
  179. void NTDomain::findServer() throw ()
  180. {
  181. // First try to get a DC from the cache.
  182. PDOMAIN_CONTROLLER_INFO dci1 = NULL;
  183. status = IASGetDcName(
  184. name,
  185. DS_DIRECTORY_SERVICE_PREFERRED,
  186. &dci1
  187. );
  188. if (status == NO_ERROR)
  189. {
  190. if (dci1->Flags & DS_DS_FLAG)
  191. {
  192. openConnection(
  193. dci1->DomainName,
  194. dci1->DomainControllerName + 2
  195. );
  196. }
  197. else
  198. {
  199. // No DS. We'll treat this as if IASGetDcName failed.
  200. NetApiBufferFree(dci1);
  201. dci1 = NULL;
  202. status = ERROR_DS_NOT_INSTALLED;
  203. }
  204. }
  205. // If the cached DC failed, try again with the force flag.
  206. if (status != NO_ERROR)
  207. {
  208. PDOMAIN_CONTROLLER_INFO dci2;
  209. DWORD err = IASGetDcName(
  210. name,
  211. DS_DIRECTORY_SERVICE_PREFERRED |
  212. DS_FORCE_REDISCOVERY,
  213. &dci2
  214. );
  215. if (err == NO_ERROR)
  216. {
  217. if (dci2->Flags & DS_DS_FLAG)
  218. {
  219. // Don't bother connecting unless this is a different DC than we
  220. // tried above.
  221. if (!dci1 ||
  222. wcscmp(
  223. dci1->DomainControllerName,
  224. dci2->DomainControllerName
  225. ))
  226. {
  227. openConnection(
  228. dci2->DomainName,
  229. dci2->DomainControllerName + 2
  230. );
  231. }
  232. }
  233. else
  234. {
  235. status = ERROR_DS_NOT_INSTALLED;
  236. }
  237. NetApiBufferFree(dci2);
  238. }
  239. else
  240. {
  241. status = err;
  242. }
  243. }
  244. NetApiBufferFree(dci1);
  245. /////////
  246. // Process the result of our 'find'.
  247. /////////
  248. if (status == ERROR_DS_NOT_INSTALLED)
  249. {
  250. mode = MODE_NT4;
  251. expiry = GetSystemTimeAsDWORDLONG() + pollInterval;
  252. }
  253. else if (status != NO_ERROR)
  254. {
  255. expiry = GetSystemTimeAsDWORDLONG() + retryInterval;
  256. }
  257. else if (mode == MODE_NATIVE)
  258. {
  259. expiry = _UI64_MAX;
  260. }
  261. else
  262. {
  263. // mode == MODE_MIXED
  264. expiry = GetSystemTimeAsDWORDLONG() + pollInterval;
  265. }
  266. }
  267. void NTDomain::readDomainMode() throw ()
  268. {
  269. LDAPMessage* res = NULL;
  270. ULONG ldapError = ldap_search_ext_sW(
  271. *connection,
  272. const_cast<PWCHAR>(connection->getBase()),
  273. LDAP_SCOPE_BASE,
  274. const_cast<PWCHAR>(ANY_OBJECT),
  275. const_cast<PWCHAR*>(DOMAIN_ATTRS),
  276. 0,
  277. NULL,
  278. NULL,
  279. &LDAPConnection::SEARCH_TIMEOUT,
  280. 0,
  281. &res
  282. );
  283. // We have to check two error codes.
  284. if (ldapError == LDAP_SUCCESS)
  285. {
  286. ldapError = res->lm_returncode;
  287. }
  288. if (ldapError == LDAP_SUCCESS)
  289. {
  290. PWCHAR* vals = ldap_get_valuesW(
  291. *connection,
  292. ldap_first_entry(*connection, res),
  293. const_cast<PWCHAR>(NT_MIXED_DOMAIN)
  294. );
  295. if (vals && *vals)
  296. {
  297. mode = wcstoul(*vals, NULL, 10) ? MODE_MIXED : MODE_NATIVE;
  298. }
  299. else
  300. {
  301. status = ERROR_ACCESS_DENIED;
  302. }
  303. ldap_value_freeW(vals);
  304. }
  305. else
  306. {
  307. status = LdapMapErrorToWin32(ldapError);
  308. connection->TraceFailure("ldap_get_valuesW",
  309. ldapError
  310. );
  311. }
  312. ldap_msgfree(res);
  313. if (status != NO_ERROR)
  314. {
  315. closeConnection();
  316. }
  317. }