bfd5b6
From 196163b419f3a22c78bd14095b91822fe08aa595 Mon Sep 17 00:00:00 2001
bfd5b6
From: Sumit Bose <sbose@redhat.com>
bfd5b6
Date: Thu, 19 Dec 2019 07:22:33 +0100
bfd5b6
Subject: [PATCH 2/2] add option use-ldaps
bfd5b6
bfd5b6
In general using the LDAP port with GSS-SPNEGO should satifiy all
bfd5b6
requirements an AD DC should have for authentication on an encrypted
bfd5b6
LDAP connection.
bfd5b6
bfd5b6
But if e.g. the LDAP port is blocked by a firewall using the LDAPS port
bfd5b6
with TLS encryption might be an alternative. For this use case the
bfd5b6
--use-ldaps option is added.
bfd5b6
bfd5b6
Related to https://bugzilla.redhat.com/show_bug.cgi?id=1762420
bfd5b6
---
bfd5b6
 doc/adcli.xml    | 24 +++++++++++++++
bfd5b6
 library/adconn.c | 79 ++++++++++++++++++++++++++++++++++++++++++------
bfd5b6
 library/adconn.h |  4 +++
bfd5b6
 tools/computer.c |  9 ++++++
bfd5b6
 tools/entry.c    | 11 +++++++
bfd5b6
 5 files changed, 118 insertions(+), 9 deletions(-)
bfd5b6
bfd5b6
diff --git a/doc/adcli.xml b/doc/adcli.xml
bfd5b6
index 9605b4a..3cefe48 100644
bfd5b6
--- a/doc/adcli.xml
bfd5b6
+++ b/doc/adcli.xml
bfd5b6
@@ -123,6 +123,30 @@
bfd5b6
 			If not specified, then an appropriate domain controller
bfd5b6
 			is automatically discovered.</para></listitem>
bfd5b6
 		</varlistentry>
bfd5b6
+		<varlistentry>
bfd5b6
+			<term><option>--use-ldaps</option></term>
bfd5b6
+			<listitem><para>Connect to the domain controller
bfd5b6
+			with LDAPS. By default the LDAP port is used and SASL
bfd5b6
+			GSS-SPNEGO or GSSAPI is used for authentication and to
bfd5b6
+			establish encryption. This should satisfy all
bfd5b6
+			requirements set on the server side and LDAPS should
bfd5b6
+			only be used if the LDAP port is not accessible due to
bfd5b6
+			firewalls or other reasons.</para>
bfd5b6
+			<para> Please note that the place where CA certificates
bfd5b6
+			can be found to validate the AD DC certificates
bfd5b6
+			must be configured in the OpenLDAP configuration
bfd5b6
+			file, e.g. <filename>/etc/openldap/ldap.conf</filename>.
bfd5b6
+			As an alternative it can be specified with the help of
bfd5b6
+			an environment variable, e.g.
bfd5b6
+<programlisting>
bfd5b6
+$ LDAPTLS_CACERT=/path/to/ad_dc_ca_cert.pem adcli join --use-ldaps -D domain.example.com
bfd5b6
+...
bfd5b6
+</programlisting>
bfd5b6
+			Please see
bfd5b6
+			<citerefentry><refentrytitle>ldap.conf</refentrytitle>
bfd5b6
+			<manvolnum>5</manvolnum></citerefentry> for details.
bfd5b6
+			</para></listitem>
bfd5b6
+		</varlistentry>
bfd5b6
 		<varlistentry>
bfd5b6
 			<term><option>-C, --login-ccache=<parameter>ccache_name</parameter></option></term>
bfd5b6
 			<listitem><para>Use the specified kerberos credential
bfd5b6
diff --git a/library/adconn.c b/library/adconn.c
bfd5b6
index a3f4548..8a55776 100644
bfd5b6
--- a/library/adconn.c
bfd5b6
+++ b/library/adconn.c
bfd5b6
@@ -70,6 +70,7 @@ struct _adcli_conn_ctx {
bfd5b6
 	char *domain_name;
bfd5b6
 	char *domain_realm;
bfd5b6
 	char *domain_controller;
bfd5b6
+	bool use_ldaps;
bfd5b6
 	char *canonical_host;
bfd5b6
 	char *domain_short;
bfd5b6
 	char *domain_sid;
bfd5b6
@@ -773,7 +774,8 @@ int ldap_init_fd (ber_socket_t fd, int proto, LDAP_CONST char *url, struct ldap
bfd5b6
 
bfd5b6
 static LDAP *
bfd5b6
 connect_to_address (const char *host,
bfd5b6
-                    const char *canonical_host)
bfd5b6
+                    const char *canonical_host,
bfd5b6
+                    bool use_ldaps)
bfd5b6
 {
bfd5b6
 	struct addrinfo *res = NULL;
bfd5b6
 	struct addrinfo *ai;
bfd5b6
@@ -783,6 +785,16 @@ connect_to_address (const char *host,
bfd5b6
 	char *url;
bfd5b6
 	int sock;
bfd5b6
 	int rc;
bfd5b6
+	int opt_rc;
bfd5b6
+	const char *port = "389";
bfd5b6
+	const char *proto = "ldap";
bfd5b6
+	const char *errmsg = NULL;
bfd5b6
+
bfd5b6
+	if (use_ldaps) {
bfd5b6
+		port = "636";
bfd5b6
+		proto = "ldaps";
bfd5b6
+		_adcli_info ("Using LDAPS to connect to %s", host);
bfd5b6
+	}
bfd5b6
 
bfd5b6
 	memset (&hints, '\0', sizeof(hints));
bfd5b6
 #ifdef AI_ADDRCONFIG
bfd5b6
@@ -794,7 +806,7 @@ connect_to_address (const char *host,
bfd5b6
 	if (!canonical_host)
bfd5b6
 		canonical_host = host;
bfd5b6
 
bfd5b6
-	rc = getaddrinfo (host, "389", &hints, &res;;
bfd5b6
+	rc = getaddrinfo (host, port, &hints, &res;;
bfd5b6
 	if (rc != 0) {
bfd5b6
 		_adcli_err ("Couldn't resolve host name: %s: %s", host, gai_strerror (rc));
bfd5b6
 		return NULL;
bfd5b6
@@ -810,7 +822,7 @@ connect_to_address (const char *host,
bfd5b6
 			close (sock);
bfd5b6
 		} else {
bfd5b6
 			error = 0;
bfd5b6
-			if (asprintf (&url, "ldap://%s", canonical_host) < 0)
bfd5b6
+			if (asprintf (&url, "%s://%s", proto, canonical_host) < 0)
bfd5b6
 				return_val_if_reached (NULL);
bfd5b6
 			rc = ldap_init_fd (sock, 1, url, &ldap);
bfd5b6
 			free (url);
bfd5b6
@@ -820,6 +832,25 @@ connect_to_address (const char *host,
bfd5b6
 				            ldap_err2string (rc));
bfd5b6
 				break;
bfd5b6
 			}
bfd5b6
+
bfd5b6
+			if (use_ldaps) {
bfd5b6
+				rc = ldap_install_tls (ldap);
bfd5b6
+				if (rc != LDAP_SUCCESS) {
bfd5b6
+					opt_rc = ldap_get_option (ldap,
bfd5b6
+					                          LDAP_OPT_DIAGNOSTIC_MESSAGE,
bfd5b6
+					                          (void *) &errmsg);
bfd5b6
+					if (opt_rc != LDAP_SUCCESS) {
bfd5b6
+						errmsg = NULL;
bfd5b6
+					}
bfd5b6
+					_adcli_err ("Couldn't initialize TLS [%s]: %s",
bfd5b6
+					            ldap_err2string (rc),
bfd5b6
+					            errmsg == NULL ? "- no details -"
bfd5b6
+					                           : errmsg);
bfd5b6
+					ldap_unbind_ext_s (ldap, NULL, NULL);
bfd5b6
+					ldap = NULL;
bfd5b6
+					break;
bfd5b6
+				}
bfd5b6
+			}
bfd5b6
 		}
bfd5b6
 	}
bfd5b6
 
bfd5b6
@@ -856,7 +887,8 @@ connect_and_lookup_naming (adcli_conn *conn,
bfd5b6
 	if (!canonical_host)
bfd5b6
 		canonical_host = disco->host_addr;
bfd5b6
 
bfd5b6
-	ldap = connect_to_address (disco->host_addr, canonical_host);
bfd5b6
+	ldap = connect_to_address (disco->host_addr, canonical_host,
bfd5b6
+	                           adcli_conn_get_use_ldaps (conn));
bfd5b6
 	if (ldap == NULL)
bfd5b6
 		return ADCLI_ERR_DIRECTORY;
bfd5b6
 
bfd5b6
@@ -1041,14 +1073,28 @@ authenticate_to_directory (adcli_conn *conn)
bfd5b6
 	status = gss_krb5_ccache_name (&minor, conn->login_ccache_name, NULL);
bfd5b6
 	return_unexpected_if_fail (status == 0);
bfd5b6
 
bfd5b6
-	/* Clumsily tell ldap + cyrus-sasl that we want encryption */
bfd5b6
-	ssf = 1;
bfd5b6
-	ret = ldap_set_option (conn->ldap, LDAP_OPT_X_SASL_SSF_MIN, &ssf;;
bfd5b6
-	return_unexpected_if_fail (ret == 0);
bfd5b6
+	if (adcli_conn_get_use_ldaps (conn)) {
bfd5b6
+		/* do not use SASL encryption on LDAPS connection */
bfd5b6
+		ssf = 0;
bfd5b6
+		ret = ldap_set_option (conn->ldap, LDAP_OPT_X_SASL_SSF_MIN, &ssf;;
bfd5b6
+		return_unexpected_if_fail (ret == 0);
bfd5b6
+		ret = ldap_set_option (conn->ldap, LDAP_OPT_X_SASL_SSF_MAX, &ssf;;
bfd5b6
+		return_unexpected_if_fail (ret == 0);
bfd5b6
+	} else {
bfd5b6
+		/* Clumsily tell ldap + cyrus-sasl that we want encryption */
bfd5b6
+		ssf = 1;
bfd5b6
+		ret = ldap_set_option (conn->ldap, LDAP_OPT_X_SASL_SSF_MIN, &ssf;;
bfd5b6
+		return_unexpected_if_fail (ret == 0);
bfd5b6
+	}
bfd5b6
 
bfd5b6
-	if (adcli_conn_server_has_sasl_mech (conn, "GSS-SPNEGO")) {
bfd5b6
+	/* There are issues with cryrus-sasl and GSS-SPNEGO with TLS even if
bfd5b6
+	 * ssf_max is set to 0. To be on the safe side GSS-SPNEGO is only used
bfd5b6
+	 * without LDAPS. */
bfd5b6
+	if (adcli_conn_server_has_sasl_mech (conn, "GSS-SPNEGO")
bfd5b6
+	                     && !adcli_conn_get_use_ldaps (conn)) {
bfd5b6
 		mech =  "GSS-SPNEGO";
bfd5b6
 	}
bfd5b6
+	_adcli_info ("Using %s for SASL bind", mech);
bfd5b6
 
bfd5b6
 	ret = ldap_sasl_interactive_bind_s (conn->ldap, NULL, mech, NULL, NULL,
bfd5b6
 	                                    LDAP_SASL_QUIET, sasl_interact, NULL);
bfd5b6
@@ -1230,6 +1276,7 @@ adcli_conn_new (const char *domain_name)
bfd5b6
 	conn->refs = 1;
bfd5b6
 	conn->logins_allowed = ADCLI_LOGIN_COMPUTER_ACCOUNT | ADCLI_LOGIN_USER_ACCOUNT;
bfd5b6
 	adcli_conn_set_domain_name (conn, domain_name);
bfd5b6
+	adcli_conn_set_use_ldaps (conn, false);
bfd5b6
 	return conn;
bfd5b6
 }
bfd5b6
 
bfd5b6
@@ -1389,6 +1436,20 @@ adcli_conn_set_domain_controller (adcli_conn *conn,
bfd5b6
 	no_more_disco (conn);
bfd5b6
 }
bfd5b6
 
bfd5b6
+bool
bfd5b6
+adcli_conn_get_use_ldaps (adcli_conn *conn)
bfd5b6
+{
bfd5b6
+	return_val_if_fail (conn != NULL, NULL);
bfd5b6
+	return conn->use_ldaps;
bfd5b6
+}
bfd5b6
+
bfd5b6
+void
bfd5b6
+adcli_conn_set_use_ldaps (adcli_conn *conn, bool value)
bfd5b6
+{
bfd5b6
+	return_if_fail (conn != NULL);
bfd5b6
+	conn->use_ldaps = value;
bfd5b6
+}
bfd5b6
+
bfd5b6
 const char *
bfd5b6
 adcli_conn_get_domain_short (adcli_conn *conn)
bfd5b6
 {
bfd5b6
diff --git a/library/adconn.h b/library/adconn.h
bfd5b6
index 8e88045..3e287b1 100644
bfd5b6
--- a/library/adconn.h
bfd5b6
+++ b/library/adconn.h
bfd5b6
@@ -89,6 +89,10 @@ const char *        adcli_conn_get_domain_controller (adcli_conn *conn);
bfd5b6
 void                adcli_conn_set_domain_controller (adcli_conn *conn,
bfd5b6
                                                       const char *value);
bfd5b6
 
bfd5b6
+bool                adcli_conn_get_use_ldaps         (adcli_conn *conn);
bfd5b6
+void                adcli_conn_set_use_ldaps         (adcli_conn *conn,
bfd5b6
+                                                      bool value);
bfd5b6
+
bfd5b6
 const char *        adcli_conn_get_domain_short      (adcli_conn *conn);
bfd5b6
 
bfd5b6
 const char *        adcli_conn_get_domain_sid        (adcli_conn *conn);
bfd5b6
diff --git a/tools/computer.c b/tools/computer.c
bfd5b6
index ac8a203..7bf8f9b 100644
bfd5b6
--- a/tools/computer.c
bfd5b6
+++ b/tools/computer.c
bfd5b6
@@ -112,12 +112,14 @@ typedef enum {
bfd5b6
 	opt_trusted_for_delegation,
bfd5b6
 	opt_add_service_principal,
bfd5b6
 	opt_remove_service_principal,
bfd5b6
+	opt_use_ldaps,
bfd5b6
 } Option;
bfd5b6
 
bfd5b6
 static adcli_tool_desc common_usages[] = {
bfd5b6
 	{ opt_domain, "active directory domain name" },
bfd5b6
 	{ opt_domain_realm, "kerberos realm for the domain" },
bfd5b6
 	{ opt_domain_controller, "domain controller to connect to" },
bfd5b6
+	{ opt_use_ldaps, "use LDAPS port for communication" },
bfd5b6
 	{ opt_host_fqdn, "override the fully qualified domain name of the\n"
bfd5b6
 	                 "local machine" },
bfd5b6
 	{ opt_host_keytab, "filename for the host kerberos keytab" },
bfd5b6
@@ -306,6 +308,9 @@ parse_option (Option opt,
bfd5b6
 	case opt_remove_service_principal:
bfd5b6
 		adcli_enroll_add_service_principal_to_remove (enroll, optarg);
bfd5b6
 		return ADCLI_SUCCESS;
bfd5b6
+	case opt_use_ldaps:
bfd5b6
+		adcli_conn_set_use_ldaps (conn, true);
bfd5b6
+		return ADCLI_SUCCESS;
bfd5b6
 	case opt_verbose:
bfd5b6
 		return ADCLI_SUCCESS;
bfd5b6
 
bfd5b6
@@ -352,6 +357,7 @@ adcli_tool_computer_join (adcli_conn *conn,
bfd5b6
 		{ "domain-realm", required_argument, NULL, opt_domain_realm },
bfd5b6
 		{ "domain-controller", required_argument, NULL, opt_domain_controller },
bfd5b6
 		{ "domain-server", required_argument, NULL, opt_domain_controller }, /* compat */
bfd5b6
+		{ "use-ldaps", no_argument, 0, opt_use_ldaps },
bfd5b6
 		{ "login-user", required_argument, NULL, opt_login_user },
bfd5b6
 		{ "user", required_argument, NULL, opt_login_user }, /* compat */
bfd5b6
 		{ "login-ccache", optional_argument, NULL, opt_login_ccache },
bfd5b6
@@ -681,6 +687,7 @@ adcli_tool_computer_preset (adcli_conn *conn,
bfd5b6
 		{ "domain", required_argument, NULL, opt_domain },
bfd5b6
 		{ "domain-realm", required_argument, NULL, opt_domain_realm },
bfd5b6
 		{ "domain-controller", required_argument, NULL, opt_domain_controller },
bfd5b6
+		{ "use-ldaps", no_argument, 0, opt_use_ldaps },
bfd5b6
 		{ "domain-ou", required_argument, NULL, opt_domain_ou },
bfd5b6
 		{ "login-user", required_argument, NULL, opt_login_user },
bfd5b6
 		{ "login-ccache", optional_argument, NULL, opt_login_ccache },
bfd5b6
@@ -793,6 +800,7 @@ adcli_tool_computer_reset (adcli_conn *conn,
bfd5b6
 		{ "domain", required_argument, NULL, opt_domain },
bfd5b6
 		{ "domain-realm", required_argument, NULL, opt_domain_realm },
bfd5b6
 		{ "domain-controller", required_argument, NULL, opt_domain_controller },
bfd5b6
+		{ "use-ldaps", no_argument, 0, opt_use_ldaps },
bfd5b6
 		{ "login-user", required_argument, NULL, opt_login_user },
bfd5b6
 		{ "login-ccache", optional_argument, NULL, opt_login_ccache },
bfd5b6
 		{ "login-type", required_argument, NULL, opt_login_type },
bfd5b6
@@ -881,6 +889,7 @@ adcli_tool_computer_delete (adcli_conn *conn,
bfd5b6
 		{ "domain", required_argument, NULL, opt_domain },
bfd5b6
 		{ "domain-realm", required_argument, NULL, opt_domain_realm },
bfd5b6
 		{ "domain-controller", required_argument, NULL, opt_domain_controller },
bfd5b6
+		{ "use-ldaps", no_argument, 0, opt_use_ldaps },
bfd5b6
 		{ "login-user", required_argument, NULL, opt_login_user },
bfd5b6
 		{ "login-ccache", optional_argument, NULL, opt_login_ccache },
bfd5b6
 		{ "no-password", no_argument, 0, opt_no_password },
bfd5b6
diff --git a/tools/entry.c b/tools/entry.c
bfd5b6
index f361845..05e4313 100644
bfd5b6
--- a/tools/entry.c
bfd5b6
+++ b/tools/entry.c
bfd5b6
@@ -53,6 +53,7 @@ typedef enum {
bfd5b6
 	opt_unix_gid,
bfd5b6
 	opt_unix_shell,
bfd5b6
 	opt_nis_domain,
bfd5b6
+	opt_use_ldaps,
bfd5b6
 } Option;
bfd5b6
 
bfd5b6
 static adcli_tool_desc common_usages[] = {
bfd5b6
@@ -67,6 +68,7 @@ static adcli_tool_desc common_usages[] = {
bfd5b6
 	{ opt_domain, "active directory domain name" },
bfd5b6
 	{ opt_domain_realm, "kerberos realm for the domain" },
bfd5b6
 	{ opt_domain_controller, "domain directory server to connect to" },
bfd5b6
+	{ opt_use_ldaps, "use LDAPS port for communication" },
bfd5b6
 	{ opt_login_ccache, "kerberos credential cache file which contains\n"
bfd5b6
 	                    "ticket to used to connect to the domain" },
bfd5b6
 	{ opt_login_user, "user (usually administrative) login name of\n"
bfd5b6
@@ -136,6 +138,9 @@ parse_option (Option opt,
bfd5b6
 			stdin_password = 1;
bfd5b6
 		}
bfd5b6
 		return ADCLI_SUCCESS;
bfd5b6
+	case opt_use_ldaps:
bfd5b6
+		adcli_conn_set_use_ldaps (conn, true);
bfd5b6
+		return ADCLI_SUCCESS;
bfd5b6
 	case opt_verbose:
bfd5b6
 		return ADCLI_SUCCESS;
bfd5b6
 	default:
bfd5b6
@@ -172,6 +177,7 @@ adcli_tool_user_create (adcli_conn *conn,
bfd5b6
 		{ "domain", required_argument, NULL, opt_domain },
bfd5b6
 		{ "domain-realm", required_argument, NULL, opt_domain_realm },
bfd5b6
 		{ "domain-controller", required_argument, NULL, opt_domain_controller },
bfd5b6
+		{ "use-ldaps", no_argument, 0, opt_use_ldaps },
bfd5b6
 		{ "login-user", required_argument, NULL, opt_login_user },
bfd5b6
 		{ "login-ccache", optional_argument, NULL, opt_login_ccache },
bfd5b6
 		{ "no-password", no_argument, 0, opt_no_password },
bfd5b6
@@ -306,6 +312,7 @@ adcli_tool_user_delete (adcli_conn *conn,
bfd5b6
 		{ "domain", required_argument, NULL, opt_domain },
bfd5b6
 		{ "domain-realm", required_argument, NULL, opt_domain_realm },
bfd5b6
 		{ "domain-controller", required_argument, NULL, opt_domain_controller },
bfd5b6
+		{ "use-ldaps", no_argument, 0, opt_use_ldaps },
bfd5b6
 		{ "login-user", required_argument, NULL, opt_login_user },
bfd5b6
 		{ "login-ccache", optional_argument, NULL, opt_login_ccache },
bfd5b6
 		{ "no-password", no_argument, 0, opt_no_password },
bfd5b6
@@ -394,6 +401,7 @@ adcli_tool_group_create (adcli_conn *conn,
bfd5b6
 		{ "domain", required_argument, NULL, opt_domain },
bfd5b6
 		{ "domain-realm", required_argument, NULL, opt_domain_realm },
bfd5b6
 		{ "domain-controller", required_argument, NULL, opt_domain_controller },
bfd5b6
+		{ "use-ldaps", no_argument, 0, opt_use_ldaps },
bfd5b6
 		{ "domain-ou", required_argument, NULL, opt_domain_ou },
bfd5b6
 		{ "login-user", required_argument, NULL, opt_login_user },
bfd5b6
 		{ "login-ccache", optional_argument, NULL, opt_login_ccache },
bfd5b6
@@ -496,6 +504,7 @@ adcli_tool_group_delete (adcli_conn *conn,
bfd5b6
 		{ "domain", required_argument, NULL, opt_domain },
bfd5b6
 		{ "domain-realm", required_argument, NULL, opt_domain_realm },
bfd5b6
 		{ "domain-controller", required_argument, NULL, opt_domain_controller },
bfd5b6
+		{ "use-ldaps", no_argument, 0, opt_use_ldaps },
bfd5b6
 		{ "login-user", required_argument, NULL, opt_login_user },
bfd5b6
 		{ "login-ccache", optional_argument, NULL, opt_login_ccache },
bfd5b6
 		{ "no-password", no_argument, 0, opt_no_password },
bfd5b6
@@ -622,6 +631,7 @@ adcli_tool_member_add (adcli_conn *conn,
bfd5b6
 		{ "domain", required_argument, NULL, opt_domain },
bfd5b6
 		{ "domain-realm", required_argument, NULL, opt_domain_realm },
bfd5b6
 		{ "domain-controller", required_argument, NULL, opt_domain_controller },
bfd5b6
+		{ "use-ldaps", no_argument, 0, opt_use_ldaps },
bfd5b6
 		{ "login-user", required_argument, NULL, opt_login_user },
bfd5b6
 		{ "login-ccache", optional_argument, NULL, opt_login_ccache },
bfd5b6
 		{ "no-password", no_argument, 0, opt_no_password },
bfd5b6
@@ -722,6 +732,7 @@ adcli_tool_member_remove (adcli_conn *conn,
bfd5b6
 		{ "domain", required_argument, NULL, opt_domain },
bfd5b6
 		{ "domain-realm", required_argument, NULL, opt_domain_realm },
bfd5b6
 		{ "domain-controller", required_argument, NULL, opt_domain_controller },
bfd5b6
+		{ "use-ldaps", no_argument, 0, opt_use_ldaps },
bfd5b6
 		{ "login-user", required_argument, NULL, opt_login_user },
bfd5b6
 		{ "login-ccache", optional_argument, NULL, opt_login_ccache },
bfd5b6
 		{ "no-password", no_argument, 0, opt_no_password },
bfd5b6
-- 
bfd5b6
2.21.1
bfd5b6