summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2022-09-16 18:26:45 +0200
committerSumit Bose <sbose@redhat.com>2022-09-26 19:19:53 +0200
commit1a6e1d520ce0376a9a44b649ef08085881c87bb4 (patch)
tree6ac3f998f6269c9cbb41c6ec232e31037aa75f1d
parent054b24d5837cb32f94b6b659620caca2b567e4f6 (diff)
disco: fall back to LDAPS if CLDAP ping was not successful
If the --use-ldaps option is used and there is no reply on the CLDAP 389/udp port adcli will try to send the request to the LDAPS port 636/tcp. Resolves: https://gitlab.freedesktop.org/realmd/adcli/-/issues/31
-rw-r--r--library/adconn.c38
-rw-r--r--library/adconn.h7
-rw-r--r--library/addisco.c193
-rw-r--r--library/addisco.h6
-rw-r--r--tools/info.c4
5 files changed, 226 insertions, 22 deletions
diff --git a/library/adconn.c b/library/adconn.c
index 7bab852..37405cc 100644
--- a/library/adconn.c
+++ b/library/adconn.c
@@ -75,6 +75,7 @@ struct _adcli_conn_ctx {
char *domain_short;
char *domain_sid;
adcli_disco *domain_disco;
+ enum conn_is_writeable is_writeable;
char *default_naming_context;
char *configuration_naming_context;
char **supported_capabilities;
@@ -149,10 +150,14 @@ disco_dance_if_necessary (adcli_conn *conn)
return;
if (conn->domain_controller)
- adcli_disco_host (conn->domain_controller, &conn->domain_disco);
+ adcli_disco_host (conn->domain_controller,
+ adcli_conn_get_use_ldaps (conn),
+ &conn->domain_disco);
else if (conn->domain_name)
- adcli_disco_domain (conn->domain_name, &conn->domain_disco);
+ adcli_disco_domain (conn->domain_name,
+ adcli_conn_get_use_ldaps (conn),
+ &conn->domain_disco);
if (conn->domain_disco) {
if (!conn->domain_short && conn->domain_disco->domain_short) {
@@ -1182,6 +1187,26 @@ lookup_domain_sid (adcli_conn *conn)
}
static void
+lookup_is_writeable (adcli_conn *conn)
+{
+ char *attrs[] = { "NetLogon", NULL };
+ LDAPMessage *results;
+ int ret;
+
+ ret = ldap_search_ext_s (conn->ldap, "", LDAP_SCOPE_BASE,
+ "(&(NtVer=\\06\\00\\00\\00)(AAC=\\00\\00\\00\\00))",
+ attrs, 0, NULL, NULL, NULL, -1, &results);
+ if (ret == LDAP_SUCCESS) {
+ conn->is_writeable = disco_get_writeable (conn->ldap, results);
+ ldap_msgfree (results);
+ } else {
+ _adcli_ldap_handle_failure (conn->ldap, ADCLI_ERR_DIRECTORY,
+ "Couldn't lookup writeable state");
+ conn->is_writeable = IS_UNKNOWN;
+ }
+}
+
+static void
conn_clear_state (adcli_conn *conn)
{
conn->ldap_authenticated = 0;
@@ -1262,6 +1287,7 @@ adcli_conn_connect (adcli_conn *conn)
lookup_short_name (conn);
lookup_domain_sid (conn);
+ lookup_is_writeable (conn);
return ADCLI_SUCCESS;
}
@@ -1702,11 +1728,9 @@ adcli_conn_server_has_sasl_mech (adcli_conn *conn,
bool adcli_conn_is_writeable (adcli_conn *conn)
{
- disco_dance_if_necessary (conn);
-
- if (conn->domain_disco == NULL) {
- return false;
+ if (conn->is_writeable == IS_UNKNOWN) {
+ lookup_is_writeable (conn);
}
- return ( (conn->domain_disco->flags & ADCLI_DISCO_WRITABLE) != 0);
+ return (conn->is_writeable == IS_WRITEABLE);
}
diff --git a/library/adconn.h b/library/adconn.h
index 1d5faa8..3a3c32b 100644
--- a/library/adconn.h
+++ b/library/adconn.h
@@ -35,6 +35,13 @@ typedef enum {
ADCLI_LOGIN_USER_ACCOUNT = 1 << 2,
} adcli_login_type;
+enum conn_is_writeable {
+ IS_UNKNOWN = 0,
+ IS_NOT_WRITEABLE = 1,
+ IS_WRITEABLE
+};
+
+
#define ADCLI_CAP_OID "1.2.840.113556.1.4.800"
#define ADCLI_CAP_LDAP_INTEG_OID "1.2.840.113556.1.4.1791"
#define ADCLI_CAP_V51_OID "1.2.840.113556.1.4.1670"
diff --git a/library/addisco.c b/library/addisco.c
index f3b3546..61726ac 100644
--- a/library/addisco.c
+++ b/library/addisco.c
@@ -455,6 +455,35 @@ parse_disco (LDAP *ldap,
return usability;
}
+enum conn_is_writeable disco_get_writeable (LDAP *ldap, LDAPMessage *message)
+{
+ adcli_disco *disco = NULL;
+ LDAPMessage *entry;
+ struct berval **bvs;
+
+ entry = ldap_first_entry (ldap, message);
+ if (entry != NULL) {
+ bvs = ldap_get_values_len (ldap, entry, "NetLogon");
+ if (bvs != NULL) {
+ if (!bvs[0])
+ disco = NULL;
+ else
+ disco = parse_disco_data (bvs[0]);
+ ldap_value_free_len (bvs);
+ }
+ }
+
+ if (disco == NULL) {
+ return IS_UNKNOWN;
+ }
+
+ if ( (disco->flags & ADCLI_DISCO_WRITABLE) != 0) {
+ return IS_WRITEABLE;
+ } else {
+ return IS_NOT_WRITEABLE;
+ }
+}
+
static int
ldap_disco_poller (LDAP **ldap,
LDAPMessage **message,
@@ -501,8 +530,143 @@ ldap_disco_poller (LDAP **ldap,
}
static int
+ldaps_disco (const char *domain,
+ srvinfo *srv,
+ adcli_disco **results)
+{
+ char *attrs[] = { "NetLogon", NULL };
+ LDAP *ldap[DISCO_COUNT];
+ const char *addrs[DISCO_COUNT];
+ int found = ADCLI_DISCO_UNUSABLE;
+ LDAPMessage *message;
+ char buffer[1024];
+ struct addrinfo hints;
+ struct addrinfo *res;
+ const char *scheme;
+ int msgidp;
+ int version;
+ char *url;
+ char *filter;
+ char *value;
+ int num;
+ int ret;
+ struct timeval interval;
+ int parsed;
+
+ if (domain) {
+ value = _adcli_ldap_escape_filter (domain);
+ return_val_if_fail (value != NULL, 0);
+ if (asprintf (&filter, "(&(DnsDomain=%s)(NtVer=\\06\\00\\00\\00))", value) < 0)
+ return_val_if_reached (0);
+ free (value);
+ } else {
+ if (asprintf (&filter, "(&(NtVer=\\06\\00\\00\\00)(AAC=\\00\\00\\00\\00))") < 0)
+ return_val_if_reached (0);
+ }
+
+ memset (addrs, 0, sizeof (addrs));
+ memset (ldap, 0, sizeof (ldap));
+
+ scheme = "ldaps";
+
+ /*
+ * The ai_socktype and ai_protocol hint fields are unused below,
+ * but are set in order to prevent duplicate returns from
+ * getaddrinfo().
+ */
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ /* For ldaps we have to use the DNS name of the LDAP server so that
+ * libldap can check if the name from the certificate matches the
+ * server name. With AI_NUMERICHOST we check if srv->hostname is an IP
+ * address or not. */
+ hints.ai_flags |= AI_NUMERICHOST;
+ hints.ai_flags |= AI_NUMERICSERV;
+#ifdef AI_ADDRCONFIG
+ hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+
+ for (num = 0; ADCLI_DISCO_UNUSABLE == found && srv != NULL && num < DISCO_COUNT; srv = srv->next) {
+ ret = getaddrinfo (srv->hostname, "389", &hints, &res);
+ if (ret == 0) {
+ ret = getnameinfo (res->ai_addr, res->ai_addrlen,
+ buffer, sizeof (buffer), NULL, 0,
+ NI_NAMEREQD);
+ freeaddrinfo (res);
+ if (ret != 0) {
+ _adcli_warn ("Couldn't resolve server name: %s: %s",
+ srv->hostname, gai_strerror (ret));
+ continue;
+ }
+ } else if (ret != 0 && ret != EAI_NONAME) {
+ _adcli_warn ("Couldn't resolve server host: %s: %s",
+ srv->hostname, gai_strerror (ret));
+ continue;
+ }
+
+ if (asprintf (&url, "%s://%s", scheme, buffer) < 0) {
+ return_val_if_reached (0);
+ }
+
+ ret = ldap_initialize (&ldap[num], url);
+ if (ret == LDAP_SUCCESS) {
+ version = LDAP_VERSION3;
+ ldap_set_option (ldap[num], LDAP_OPT_PROTOCOL_VERSION, &version);
+ ldap_set_option (ldap[num], LDAP_OPT_REFERRALS , 0);
+ addrs[num] = srv->hostname;
+
+ } else {
+ _adcli_err ("Couldn't perform discovery on server: %s: %s", url, ldap_err2string (ret));
+ free (url);
+ continue;
+ }
+ free (url);
+
+ _adcli_info ("Sending LDAPS NetLogon ping to domain controller: %s", addrs[num]);
+
+ ret = ldap_search_ext (ldap[num], "", LDAP_SCOPE_BASE,
+ filter, attrs, 0, NULL, NULL, NULL,
+ -1, &msgidp);
+
+ if (ret != LDAP_SUCCESS) {
+ _adcli_ldap_handle_failure (ldap[num], ADCLI_ERR_CONFIG,
+ "Couldn't perform discovery search");
+ ldap_unbind_ext_s (ldap[num], NULL, NULL);
+ ldap[num] = NULL;
+ continue;
+ }
+
+ /* From https://msdn.microsoft.com/en-us/library/ff718294.aspx first
+ * five DCs are given 0.4 seconds timeout, next five are given 0.2
+ * seconds, and the rest are given 0.1 seconds
+ */
+ if (num < 5) {
+ interval.tv_usec = 400000;
+ } else if (num < 10) {
+ interval.tv_usec = 200000;
+ } else {
+ interval.tv_usec = 100000;
+ }
+ select (0, NULL, NULL, NULL, &interval);
+
+ parsed = ldap_disco_poller (&(ldap[num]), &message, results, &(addrs[num]));
+ if (ldap[num] != NULL) {
+ ldap_unbind_ext_s (ldap[num], NULL, NULL);
+ }
+ if (parsed > found) {
+ found = parsed;
+ }
+ }
+
+ free (filter);
+ return found;
+}
+
+static int
ldap_disco (const char *domain,
srvinfo *srv,
+ bool use_ldaps,
adcli_disco **results)
{
char *attrs[] = { "NetLogon", NULL };
@@ -525,6 +689,7 @@ ldap_disco (const char *domain,
int ret;
int have_any = 0;
struct timeval interval;
+ srvinfo *my_srv;
if (domain) {
value = _adcli_ldap_escape_filter (domain);
@@ -559,11 +724,12 @@ ldap_disco (const char *domain,
hints.ai_flags |= AI_ADDRCONFIG;
#endif
- for (num = 0; srv != NULL; srv = srv->next) {
- ret = getaddrinfo (srv->hostname, "389", &hints, &res);
+ my_srv = srv;
+ for (num = 0; my_srv != NULL; my_srv = my_srv->next) {
+ ret = getaddrinfo (my_srv->hostname, "389", &hints, &res);
if (ret != 0) {
_adcli_warn ("Couldn't resolve server host: %s: %s",
- srv->hostname, gai_strerror (ret));
+ my_srv->hostname, gai_strerror (ret));
continue;
}
@@ -588,7 +754,7 @@ ldap_disco (const char *domain,
version = LDAP_VERSION3;
ldap_set_option (ldap[num], LDAP_OPT_PROTOCOL_VERSION, &version);
ldap_set_option (ldap[num], LDAP_OPT_REFERRALS , 0);
- addrs[num] = srv->hostname;
+ addrs[num] = my_srv->hostname;
have_any = 1;
num++;
@@ -671,6 +837,11 @@ ldap_disco (const char *domain,
}
free (filter);
+
+ if (found == ADCLI_DISCO_UNUSABLE && use_ldaps) {
+ found = ldaps_disco (domain, srv, results);
+ }
+
return found;
}
@@ -698,7 +869,7 @@ fill_disco (adcli_disco **results,
}
static int
-site_disco (adcli_disco *disco,
+site_disco (adcli_disco *disco, bool use_ldaps,
adcli_disco **results)
{
srvinfo *srv;
@@ -741,7 +912,7 @@ site_disco (adcli_disco *disco,
* Now that we have discovered the site domain controllers do a
* second round of cldap discovery.
*/
- found = ldap_disco (disco->domain, srv, results);
+ found = ldap_disco (disco->domain, srv, use_ldaps, results);
fill_disco (results, ADCLI_DISCO_MAYBE,
disco->domain, disco->client_site, srv);
@@ -752,7 +923,7 @@ site_disco (adcli_disco *disco,
}
int
-adcli_disco_domain (const char *domain,
+adcli_disco_domain (const char *domain, bool use_ldaps,
adcli_disco **results)
{
char *rrname;
@@ -791,10 +962,10 @@ adcli_disco_domain (const char *domain,
if (ret != 0)
return 0;
- found = ldap_disco (domain, srv, results);
+ found = ldap_disco (domain, srv, use_ldaps, results);
if (found == ADCLI_DISCO_MAYBE) {
assert (*results);
- found = site_disco (*results, results);
+ found = site_disco (*results, use_ldaps, results);
}
fill_disco (results, ADCLI_DISCO_MAYBE, domain, NULL, srv);
@@ -804,7 +975,7 @@ adcli_disco_domain (const char *domain,
}
int
-adcli_disco_host (const char *host,
+adcli_disco_host (const char *host, bool use_ldaps,
adcli_disco **results)
{
srvinfo srv;
@@ -817,7 +988,7 @@ adcli_disco_host (const char *host,
memset (&srv, 0, sizeof (srv));
srv.hostname = (char *)host;
- return ldap_disco (NULL, &srv, results);
+ return ldap_disco (NULL, &srv, use_ldaps, results);
}
void
diff --git a/library/addisco.h b/library/addisco.h
index 06b69a9..718db7d 100644
--- a/library/addisco.h
+++ b/library/addisco.h
@@ -56,10 +56,10 @@ typedef struct _adcli_disco {
struct _adcli_disco *next;
} adcli_disco;
-int adcli_disco_domain (const char *domain,
+int adcli_disco_domain (const char *domain, bool use_ldaps,
adcli_disco **disco);
-int adcli_disco_host (const char *host,
+int adcli_disco_host (const char *host, bool use_ldaps,
adcli_disco **disco);
void adcli_disco_free (adcli_disco *disco);
@@ -72,4 +72,6 @@ enum {
int adcli_disco_usable (adcli_disco *disco);
+enum conn_is_writeable disco_get_writeable (LDAP *ldap, LDAPMessage *message);
+
#endif /* ADDISCO_H_ */
diff --git a/tools/info.c b/tools/info.c
index c63e0ff..fe1246a 100644
--- a/tools/info.c
+++ b/tools/info.c
@@ -168,14 +168,14 @@ adcli_tool_info (adcli_conn *unused,
}
if (server) {
- adcli_disco_host (server, &disco);
+ adcli_disco_host (server, false, &disco);
if (disco == NULL) {
warnx ("couldn't discover domain controller: %s", server);
return 1;
}
for_host = 1;
} else if (domain) {
- adcli_disco_domain (domain, &disco);
+ adcli_disco_domain (domain, false, &disco);
if (disco == NULL) {
warnx ("couldn't discover domain: %s", domain);
return 1;