Blame SOURCES/0001-Make-adcli-info-DC-location-mechanism-more-compliant.patch

ebcf82
From 0a0d0f66409eb83e06b7dc50543c2f6c15a36bc4 Mon Sep 17 00:00:00 2001
ebcf82
From: Alexey A Nikitin <nikitin@amazon.com>
ebcf82
Date: Mon, 29 Oct 2018 20:40:36 -0700
ebcf82
Subject: [PATCH] Make 'adcli info' DC location mechanism more compliant with
ebcf82
 [MS-ADTS] and [MS-NRPC]
ebcf82
ebcf82
AD specifications say that DC locator must attempt to find a suitable DC for the client. That means going through all of the DCs in SRV RRs one by one until one of them answers.
ebcf82
ebcf82
The problem with adcli's original behavior is that it queries only five DCs from SRV, ever. This becomes a problem if for any reason there is a large number of DCs in the domain from which the client cannot get a CLDAP response.
ebcf82
---
ebcf82
 library/addisco.c | 146 +++++++++++++++++++++++++++++-----------------
ebcf82
 1 file changed, 94 insertions(+), 52 deletions(-)
ebcf82
ebcf82
diff --git a/library/addisco.c b/library/addisco.c
ebcf82
index 8cc5bf0..6e73ead 100644
ebcf82
--- a/library/addisco.c
ebcf82
+++ b/library/addisco.c
ebcf82
@@ -41,8 +41,10 @@
ebcf82
 #include <string.h>
ebcf82
 #include <time.h>
ebcf82
 
ebcf82
-/* Number of servers to do discovery against */
ebcf82
-#define DISCO_COUNT 5
ebcf82
+/* Number of servers to do discovery against.
ebcf82
+ * For AD DS maximum number of DCs is 1200.
ebcf82
+ */
ebcf82
+#define DISCO_COUNT 1200
ebcf82
 
ebcf82
 /* The time period in which to do rapid requests */
ebcf82
 #define DISCO_FEVER  1
ebcf82
@@ -453,6 +455,51 @@ parse_disco (LDAP *ldap,
ebcf82
 	return usability;
ebcf82
 }
ebcf82
 
ebcf82
+static int
ebcf82
+ldap_disco_poller (LDAP **ldap,
ebcf82
+                   LDAPMessage **message,
ebcf82
+                   adcli_disco **results,
ebcf82
+                   const char **addrs)
ebcf82
+{
ebcf82
+	int found = ADCLI_DISCO_UNUSABLE;
ebcf82
+	int close_ldap;
ebcf82
+	int parsed;
ebcf82
+	int ret = 0;
ebcf82
+	struct timeval tvpoll = { 0, 0 };
ebcf82
+
ebcf82
+	switch (ldap_result (*ldap, LDAP_RES_ANY, 1, &tvpoll, message)) {
ebcf82
+		case LDAP_RES_SEARCH_ENTRY:
ebcf82
+		case LDAP_RES_SEARCH_RESULT:
ebcf82
+			parsed = parse_disco (*ldap, *addrs, *message, results);
ebcf82
+			if (parsed > found)
ebcf82
+				found = parsed;
ebcf82
+			ldap_msgfree (*message);
ebcf82
+			close_ldap = 1;
ebcf82
+			break;
ebcf82
+		case -1:
ebcf82
+			ldap_get_option (*ldap, LDAP_OPT_RESULT_CODE, &ret;;
ebcf82
+			close_ldap = 1;
ebcf82
+			break;
ebcf82
+		default:
ebcf82
+			ldap_msgfree (*message);
ebcf82
+			close_ldap = 0;
ebcf82
+			break;
ebcf82
+	}
ebcf82
+
ebcf82
+	if (ret != LDAP_SUCCESS) {
ebcf82
+		_adcli_ldap_handle_failure (*ldap, ADCLI_ERR_CONFIG,
ebcf82
+		                            "Couldn't perform discovery search");
ebcf82
+	}
ebcf82
+
ebcf82
+	/* Done with this connection */
ebcf82
+	if (close_ldap) {
ebcf82
+		ldap_unbind_ext_s (*ldap, NULL, NULL);
ebcf82
+		*ldap = NULL;
ebcf82
+	}
ebcf82
+
ebcf82
+	return found;
ebcf82
+}
ebcf82
+
ebcf82
 static int
ebcf82
 ldap_disco (const char *domain,
ebcf82
             srvinfo *srv,
ebcf82
@@ -477,6 +524,7 @@ ldap_disco (const char *domain,
ebcf82
 	int num, i;
ebcf82
 	int ret;
ebcf82
 	int have_any = 0;
ebcf82
+	struct timeval interval;
ebcf82
 
ebcf82
 	if (domain) {
ebcf82
 		value = _adcli_ldap_escape_filter (domain);
ebcf82
@@ -540,7 +588,6 @@ ldap_disco (const char *domain,
ebcf82
 				version = LDAP_VERSION3;
ebcf82
 				ldap_set_option (ldap[num], LDAP_OPT_PROTOCOL_VERSION, &version);
ebcf82
 				ldap_set_option (ldap[num], LDAP_OPT_REFERRALS , 0);
ebcf82
-				_adcli_info ("Sending netlogon pings to domain controller: %s", url);
ebcf82
 				addrs[num] = srv->hostname;
ebcf82
 				have_any = 1;
ebcf82
 				num++;
ebcf82
@@ -555,70 +602,65 @@ ldap_disco (const char *domain,
ebcf82
 		freeaddrinfo (res);
ebcf82
 	}
ebcf82
 
ebcf82
-	/* Wait for the first response. Poor mans fd watch */
ebcf82
-	for (started = now = time (NULL);
ebcf82
-	     have_any && found != ADCLI_DISCO_USABLE && now < started + DISCO_TIME;
ebcf82
-	     now = time (NULL)) {
ebcf82
+	/* Initial send and short time wait */
ebcf82
+	interval.tv_sec = 0;
ebcf82
+	for (i = 0; ADCLI_DISCO_UNUSABLE == found && i < num; ++i) {
ebcf82
+		int parsed;
ebcf82
+
ebcf82
+		if (NULL == ldap[i])
ebcf82
+			continue;
ebcf82
 
ebcf82
-		struct timeval tvpoll = { 0, 0 };
ebcf82
-		struct timeval interval;
ebcf82
+		have_any = 1;
ebcf82
+		_adcli_info ("Sending NetLogon ping to domain controller: %s", addrs[i]);
ebcf82
 
ebcf82
-		/* If in the initial period, send feverishly */
ebcf82
-		if (now < started + DISCO_FEVER) {
ebcf82
-			interval.tv_sec = 0;
ebcf82
-			interval.tv_usec = 100 * 1000;
ebcf82
+		ret = ldap_search_ext (ldap[i], "", LDAP_SCOPE_BASE,
ebcf82
+		                       filter, attrs, 0, NULL, NULL, NULL,
ebcf82
+		                       -1, &msgidp);
ebcf82
+
ebcf82
+		if (ret != LDAP_SUCCESS) {
ebcf82
+			_adcli_ldap_handle_failure (ldap[i], ADCLI_ERR_CONFIG,
ebcf82
+			                            "Couldn't perform discovery search");
ebcf82
+			ldap_unbind_ext_s (ldap[i], NULL, NULL);
ebcf82
+			ldap[i] = NULL;
ebcf82
+		}
ebcf82
+
ebcf82
+		/* From https://msdn.microsoft.com/en-us/library/ff718294.aspx first
ebcf82
+		 * five DCs are given 0.4 seconds timeout, next five are given 0.2
ebcf82
+		 * seconds, and the rest are given 0.1 seconds
ebcf82
+		 */
ebcf82
+		if (i < 5) {
ebcf82
+			interval.tv_usec = 400000;
ebcf82
+		} else if (i < 10) {
ebcf82
+			interval.tv_usec = 200000;
ebcf82
 		} else {
ebcf82
-			interval.tv_sec = 1;
ebcf82
-			interval.tv_usec = 0;
ebcf82
+			interval.tv_usec = 100000;
ebcf82
 		}
ebcf82
+		select (0, NULL, NULL, NULL, &interval);
ebcf82
+
ebcf82
+		parsed = ldap_disco_poller (&(ldap[i]), &message, results, &(addrs[i]));
ebcf82
+		if (parsed > found)
ebcf82
+			found = parsed;
ebcf82
+	}
ebcf82
+
ebcf82
+	/* Wait some more until LDAP timeout (DISCO_TIME) */
ebcf82
+	for (started = now = time (NULL);
ebcf82
+	     have_any && ADCLI_DISCO_UNUSABLE == found && now < started + DISCO_TIME;
ebcf82
+	     now = time (NULL)) {
ebcf82
 
ebcf82
 		select (0, NULL, NULL, NULL, &interval);
ebcf82
 
ebcf82
 		have_any = 0;
ebcf82
-		for (i = 0; found != ADCLI_DISCO_USABLE && i < num; i++) {
ebcf82
-			int close_ldap;
ebcf82
+		for (i = 0; ADCLI_DISCO_UNUSABLE == found && i < num; ++i) {
ebcf82
 			int parsed;
ebcf82
 
ebcf82
 			if (ldap[i] == NULL)
ebcf82
 				continue;
ebcf82
 
ebcf82
-			ret = 0;
ebcf82
 			have_any = 1;
ebcf82
-			switch (ldap_result (ldap[i], LDAP_RES_ANY, 1, &tvpoll, &message)) {
ebcf82
-			case LDAP_RES_SEARCH_ENTRY:
ebcf82
-			case LDAP_RES_SEARCH_RESULT:
ebcf82
-				parsed = parse_disco (ldap[i], addrs[i], message, results);
ebcf82
-				if (parsed > found)
ebcf82
-					found = parsed;
ebcf82
-				ldap_msgfree (message);
ebcf82
-				close_ldap = 1;
ebcf82
-				break;
ebcf82
-			case 0:
ebcf82
-				ret = ldap_search_ext (ldap[i], "", LDAP_SCOPE_BASE,
ebcf82
-				                       filter, attrs, 0, NULL, NULL, NULL,
ebcf82
-				                       -1, &msgidp);
ebcf82
-				close_ldap = (ret != 0);
ebcf82
-				break;
ebcf82
-			case -1:
ebcf82
-				ldap_get_option (ldap[i], LDAP_OPT_RESULT_CODE, &ret;;
ebcf82
-				close_ldap = 1;
ebcf82
-				break;
ebcf82
-			default:
ebcf82
-				ldap_msgfree (message);
ebcf82
-				close_ldap = 0;
ebcf82
-				break;
ebcf82
-			}
ebcf82
-
ebcf82
-			if (ret != LDAP_SUCCESS) {
ebcf82
-				_adcli_ldap_handle_failure (ldap[i], ADCLI_ERR_CONFIG,
ebcf82
-				                            "Couldn't perform discovery search");
ebcf82
-			}
ebcf82
 
ebcf82
-			/* Done with this connection */
ebcf82
-			if (close_ldap) {
ebcf82
-				ldap_unbind_ext_s (ldap[i], NULL, NULL);
ebcf82
-				ldap[i] = NULL;
ebcf82
-			}
ebcf82
+			parsed = ldap_disco_poller (&(ldap[i]), &message, results, &(addrs[i]));
ebcf82
+			if (parsed > found)
ebcf82
+				found = parsed;
ebcf82
 		}
ebcf82
 	}
ebcf82
 
ebcf82
-- 
ebcf82
2.26.2
ebcf82