[PATCH] nfsidmap:umich_ldap: Add support for SASL binds.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



umich_ldap can now do a sasl interactive bind to LDAP server based
on the values of new tunables added.
The tunables are similar to the ones in nslcd for SASL binds.

Signed-off-by: Srikrishan Malik <srikrishanmalik@xxxxxxxxx>
---
 configure.ac                   |  23 ++-
 support/nfsidmap/Makefile.am   |   5 +-
 support/nfsidmap/idmapd.conf   |  23 +++
 support/nfsidmap/idmapd.conf.5 |  37 ++++-
 support/nfsidmap/umich_ldap.c  | 250 ++++++++++++++++++++++++++++++++-
 5 files changed, 330 insertions(+), 8 deletions(-)

diff --git a/configure.ac b/configure.ac
index bb8b000e..00b32800 100644
--- a/configure.ac
+++ b/configure.ac
@@ -453,12 +453,33 @@ if test "x$enable_ldap" != "xno" ; then
                               [have_ldap="yes"],[have_ldap="no"])],
                 [have_ldap="no"])
         if test "x$have_ldap" = "xyes" ; then
-                AC_DEFINE([ENABLE_LDAP], 1, [Enable LDAP Support])
+                dnl check for sasl funcs
+                AC_CHECK_HEADERS(sasl.h sasl/sasl.h)
+                AC_CHECK_HEADERS(gsssasl.h)
+                AC_CHECK_TYPE(sasl_interact_t,[have_sasl_interact_t="yes"],,[
+                        #ifdef HAVE_SASL_SASL_H
+                        #include <sasl/sasl.h>
+                        #elif defined(HAVE_SASL_H)
+                        #include <sasl.h>
+                        #endif])
+		AC_CHECK_LIB([ldap],[ldap_sasl_interactive_bind_s],[have_ldap_sasl_interactive_bind_s="yes"])
+		AC_CHECK_LIB([gssapi_krb5],[gss_krb5_ccache_name],[have_gss_krb5_ccache_name="yes"])
+		if test "x$have_sasl_interact_t" = "xyes" -a \
+			"x$have_ldap_sasl_interactive_bind_s" = "xyes" -a \
+			"x$have_gss_krb5_ccache_name" = "xyes"; then
+			AC_DEFINE([HAVE_LDAP_SASL_INTERACTIVE_BIND_S],[1],[Has ldap_sasl_interactive_bind_s function])
+			AC_DEFINE([HAVE_GSS_KRB5_CCACHE_NAME],[1],[Has gss_krb5_ccache_name function])
+			AC_CHECK_HEADERS(gssapi/gssapi.h gssapi/gssapi_generic.h gssapi/gssapi_krb5.h gssapi.h krb5.h)
+			AC_DEFINE([ENABLE_LDAP_SASL],1,[Enable LDAP SASL support])
+			have_ldap_sasl="yes"
+		fi
+		AC_DEFINE([ENABLE_LDAP], 1, [Enable LDAP Support])
         elif test "x$enable_ldap$have_ldap" = "xyesno" ; then
                 AC_MSG_ERROR(LDAP support not found!)
         fi
 fi
 AM_CONDITIONAL(ENABLE_LDAP, test "x$have_ldap" = "xyes")
+AM_CONDITIONAL(ENABLE_LDAP_SASL, test "x$have_ldap_sasl" = "xyes")
 
 dnl Should we build gums mapping library?
 AC_ARG_ENABLE([gums],
diff --git a/support/nfsidmap/Makefile.am b/support/nfsidmap/Makefile.am
index 9c21fa34..cb2c0ced 100644
--- a/support/nfsidmap/Makefile.am
+++ b/support/nfsidmap/Makefile.am
@@ -14,6 +14,9 @@ GUMS_MAPPING_LIB = gums.la
 else
 GUMS_MAPPING_LIB =
 endif
+if ENABLE_LDAP_SASL
+KRB5_GSS_LIB=-lgssapi_krb5
+endif
 lib_LTLIBRARIES = libnfsidmap.la
 pkgplugin_LTLIBRARIES = nsswitch.la static.la $(UMICH_LDAP_LIB) $(GUMS_MAPPING_LIB)
 
@@ -43,7 +46,7 @@ static_la_LIBADD = ../../support/nfs/libnfsconf.la
 
 umich_ldap_la_SOURCES = umich_ldap.c
 umich_ldap_la_LDFLAGS = -module -avoid-version
-umich_ldap_la_LIBADD = -lldap ../../support/nfs/libnfsconf.la
+umich_ldap_la_LIBADD = -lldap $(KRB5_GSS_LIB) ../../support/nfs/libnfsconf.la
 
 gums_la_SOURCES = gums.c
 gums_la_LDFLAGS = -module -avoid-version
diff --git a/support/nfsidmap/idmapd.conf b/support/nfsidmap/idmapd.conf
index b673c7d7..aeeca1bf 100644
--- a/support/nfsidmap/idmapd.conf
+++ b/support/nfsidmap/idmapd.conf
@@ -110,6 +110,29 @@ LDAP_base = dc=local,dc=domain,dc=edu
 # is not set to "never"
 #LDAP_ca_cert = /etc/ldapca.cert
 
+# SASL mechanism to use while binding to LDAP
+#LDAP_sasl_mech = <SASL mech>
+
+# SASL realm to be used for SASL auth
+#LDAP_sasl_realm = <SASL realm>
+
+# Authentication identity to be used for SASL auth
+#LDAP_sasl_authcid = <SASL authcid>
+
+# Authorization identity for SASL auth
+#LDAP_sasl_authzid = <SASL authzid>
+
+# Cyrus SASL security properties
+#LDAP_sasl_secprops = <secprops>
+
+# Specifies whether the LDAP server hostname should be canonicalised.
+# If set to yes LDAP lib with do a reverse hostname lookup.
+# If this is not set the LDAP library's default will be used.
+#LDAP_sasl_canonicalize <yes | no>
+
+# Specifies the kerberos ticket cache to be used
+#LDAP_sasl_krb5_ccname = <kerberos ticket cache>
+
 # Objectclass mapping information
 
 # Mapping for the person (account) object class
diff --git a/support/nfsidmap/idmapd.conf.5 b/support/nfsidmap/idmapd.conf.5
index 61fbb613..fdd6d589 100644
--- a/support/nfsidmap/idmapd.conf.5
+++ b/support/nfsidmap/idmapd.conf.5
@@ -206,6 +206,39 @@ It can take the same values as ldap.conf(5)'s
 tunable.
 (Default: "hard")
 .TP
+.B LDAP_timeout_seconds
+Number of seconds before timing out an LDAP request
+(Default: 4)
+.TP
+.B LDAP_sasl_mech
+SASL mechanism to be used for sasl authentication.  Required
+if SASL auth is to be used (Default: None)
+.TP
+.B LDAP_realm
+SASL realm to be used for sasl authentication. (Default: None)
+.TP
+.B LDAP_sasl_authcid
+Authentication identity to be used for sasl authentication. (Default: None)
+.TP
+.B LDAP_sasl_authzid
+Authorization identity to be used for sasl authentication. (Default: None)
+.TP
+.B LDAP_sasl_secprops
+Cyrus SASL security properties. It can  the same values as ldap.conf(5)'s
+sasl_secprops.
+.TP
+.B LDAP_sasl_canonicalize
+Specifies whether the LDAP server hostname should be canonicalised.
+If set to yes LDAP lib with do a reverse hostname lookup.
+If this is not set the LDAP library's default will be used. (Default:
+None)
+.TP
+.B LDAP_sasl_krb5_ccname
+Path to kerberos credential cache. If it is not set then the value
+of environment variable KRB5CCNAME will be used. If the environment
+variable is not set then the default mechanism of kerberos library
+will be used.
+.TP
 .B NFSv4_person_objectclass
 The object class name for people accounts in your local LDAP schema
 (Default: NFSv4RemotePerson)
@@ -257,10 +290,6 @@ is true, this is the attribute to be searched for.
 .B NFSv4_grouplist_filter
 An optional search filter for determining group membership.
 (No Default)
-.TP
-.B LDAP_timeout_seconds
-Number of seconds before timing out an LDAP request
-(Default: 4)
 .\"
 .\" -------------------------------------------------------------------
 .\" An Example
diff --git a/support/nfsidmap/umich_ldap.c b/support/nfsidmap/umich_ldap.c
index b7445c37..d5a7731a 100644
--- a/support/nfsidmap/umich_ldap.c
+++ b/support/nfsidmap/umich_ldap.c
@@ -31,6 +31,7 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
+#include "config.h"
 
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -43,6 +44,15 @@
 #include <limits.h>
 #include <pwd.h>
 #include <err.h>
+#ifdef HAVE_GSSAPI_GSSAPI_KRB5_H
+#include <gssapi/gssapi_krb5.h>
+#endif /* HAVE_GSSAPI_GSSAPI_KRB5_H */
+#ifdef HAVE_SASL_H
+#include <sasl.h>
+#endif /* HAVE_SASL_H */
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#endif /* HAVE_SASL_SASL_H */
 /* We are using deprecated functions, get the prototypes... */
 #define LDAP_DEPRECATED 1
 #include <ldap.h>
@@ -105,6 +115,13 @@ struct umich_ldap_info {
 				   looking up user groups */
 	int ldap_timeout;	/* Timeout in seconds for searches
 				   by ldap_search_st */
+	char *sasl_mech;	/* sasl mech to be used */
+	char *sasl_realm;	/* SASL realm for SASL authentication */
+	char *sasl_authcid;	/* authentication identity to be used  */
+	char *sasl_authzid;	/* authorization identity to be used */
+	char *sasl_secprops;	/* Cyrus SASL security properties. */
+	int sasl_canonicalize;	/* canonicalize LDAP server host name */
+	char *sasl_krb5_ccname;	/* krb5 ticket cache */
 };
 
 /* GLOBAL data */
@@ -122,6 +139,13 @@ static struct umich_ldap_info ldap_info = {
 	.tls_reqcert = LDAP_OPT_X_TLS_HARD,
 	.memberof_for_groups = 0,
 	.ldap_timeout = DEFAULT_UMICH_SEARCH_TIMEOUT,
+	.sasl_mech = NULL,
+	.sasl_realm = NULL,
+	.sasl_authcid = NULL,
+	.sasl_authzid = NULL,
+	.sasl_secprops = NULL,
+	.sasl_canonicalize = -1, /* leave to the LDAP lib */
+	.sasl_krb5_ccname = NULL,
 };
 
 static struct ldap_map_names ldap_map = {
@@ -138,6 +162,119 @@ static struct ldap_map_names ldap_map = {
 	.NFSv4_grouplist_filter = NULL,
 };
 
+#ifdef ENABLE_LDAP_SASL
+
+/**
+ * Set the path of the krb5 ticket cache
+ * use gss_krb5_ccache_name if available else set the env var
+ */
+static int set_krb5_ccname(const char *krb5_ccache_name)
+{
+	int retval = 0;
+#ifdef HAVE_GSS_KRB5_CCACHE_NAME
+	OM_uint32 status;
+
+	if (gss_krb5_ccache_name(&status, krb5_ccache_name, NULL) !=
+		GSS_S_COMPLETE) {
+		IDMAP_LOG(5,
+		  ("Failed to set creds cache for kerberos, minor_status(%d)",
+		   status));
+		retval = status;
+		goto out;
+	}
+#else /* HAVE_GSS_KRB5_CCACHE_NAME */
+	char *env;
+	int buflen = 0;
+
+	buflen = strlen("KRB5CCNAME=") + strlen(krb5_ccache_name) + 1;
+	env = malloc(buflen);
+	if (env == NULL) {
+		retval = ENOMEM;
+		goto out;
+	}
+	snprintf(env, buflen, "KRB5CCNAME=%s", krb5_ccache_name);
+	if (putenv(env) != 0) {
+		retval = errno;
+		IDMAP_LOG(5, ("Failed to set creds cache for kerberos, err(%d)",
+			      retval));
+	}
+#endif /* else HAVE_GSS_KRB5_CCACHE_NAME */
+out:
+	return retval;
+}
+
+/**
+ * SASL interact callback
+ */
+static int sasl_interact_cb(__attribute__((unused)) LDAP * ld,
+		__attribute__((unused)) unsigned int flags, void *defaults,
+		void *ctx)
+{
+	struct umich_ldap_info *linfo = defaults;
+	sasl_interact_t *interact = ctx;
+
+	while (interact->id != SASL_CB_LIST_END) {
+		switch (interact->id) {
+		case SASL_CB_AUTHNAME:
+			if (linfo->sasl_authcid == NULL ||
+			    linfo->sasl_authcid[0] == '\0') {
+				IDMAP_LOG(2, ("SASL_CB_AUTHNAME asked in "
+					    "callback but not found in conf"));
+			} else {
+				IDMAP_LOG(5,
+					  ("Setting SASL_CB_AUTHNAME to %s",
+					   linfo->sasl_authcid));
+				interact->result = linfo->sasl_authcid;
+				interact->len = strlen(linfo->sasl_authcid);
+			}
+			break;
+		case SASL_CB_PASS:
+			if (linfo->passwd == NULL || linfo->passwd[0] == '\0') {
+				IDMAP_LOG(2, ("SASL_CB_PASS asked in callback "
+					      "but not found in conf"));
+			} else {
+				IDMAP_LOG(5,
+					  ("Setting SASL_CB_PASS to ***"));
+				interact->result = linfo->passwd;
+				interact->len = strlen(linfo->passwd);
+			}
+			break;
+		case SASL_CB_GETREALM:
+			if (linfo->sasl_realm == NULL ||
+			    linfo->sasl_realm[0] == '\0') {
+				IDMAP_LOG(2, ("SASL_CB_GETREALM asked in "
+					    "callback but not found in conf"));
+			} else {
+				IDMAP_LOG(5,
+					  ("Setting SASL_CB_GETREALM to %s",
+					   linfo->sasl_realm));
+				interact->result = linfo->sasl_realm;
+				interact->len = strlen(linfo->sasl_realm);
+			}
+			break;
+		case SASL_CB_USER:
+			if (linfo->sasl_authzid == NULL ||
+			    linfo->sasl_authzid[0] == '\0') {
+				IDMAP_LOG(2, ("SASL_CB_USER asked in callback "
+					      "but not found in conf"));
+			} else {
+				IDMAP_LOG(5, ("Setting SASL_CB_USER to %s",
+					      linfo->sasl_authzid));
+				interact->result = linfo->sasl_authzid;
+				interact->len = strlen(linfo->sasl_authzid);
+			}
+			break;
+		default:
+			IDMAP_LOG(2, ("Undefined value requested %d",
+				      interact->id));
+			break;
+		}
+		interact++;
+	}
+	return LDAP_SUCCESS;
+}
+#endif /* ENABLE_LDAP_SASL */
+
 /* Local routines */
 
 static int
@@ -244,7 +381,57 @@ ldap_init_and_bind(LDAP **pld,
 	/* If we have a DN (and password) attempt an authenticated bind */
 	if (linfo->user_dn) {
 retry_bind:
+#ifdef ENABLE_LDAP_SASL
+		if (linfo->sasl_mech != NULL && linfo->sasl_mech[0] != '\0') {
+		/* use sasl bind */
+			if (linfo->sasl_canonicalize != -1) {
+				lerr = ldap_set_option(ld,
+						LDAP_OPT_X_SASL_NOCANON,
+						linfo->sasl_canonicalize ?
+						  LDAP_OPT_OFF : LDAP_OPT_ON);
+				if (lerr != LDAP_SUCCESS) {
+					IDMAP_LOG(2, ("ldap_init_and_bind: "
+						    "setting sasl_canonicalize"
+						    " failed: %s (%d)",
+						    ldap_err2string(lerr),
+						    lerr));
+					goto out;
+				}
+			}
+			if (linfo->sasl_secprops != NULL &&
+			    linfo->sasl_secprops[0] != '\0') {
+				lerr = ldap_set_option(ld,
+						LDAP_OPT_X_SASL_SECPROPS,
+						(void *) linfo->sasl_secprops);
+				if (lerr != LDAP_SUCCESS) {
+					IDMAP_LOG(2, ("ldap_init_and_bind: "
+						      "setting sasl_secprops"
+						      " failed: %s (%d)",
+						      ldap_err2string(lerr),
+						      lerr));
+					goto out;
+				}
+			}
+			if (linfo->sasl_krb5_ccname != NULL &&
+			    linfo->sasl_krb5_ccname[0] != '\0') {
+				lerr = set_krb5_ccname(linfo->sasl_krb5_ccname);
+				if (lerr != 0) {
+					IDMAP_LOG(2,
+						("ldap_init_and_bind: Failed "
+						 "to set krb5 ticket cache, "
+						 "err=%d", lerr));
+				}
+			}
+			lerr = ldap_sasl_interactive_bind_s(ld, linfo->user_dn,
+				linfo->sasl_mech, NULL, NULL, LDAP_SASL_QUIET,
+				sasl_interact_cb, linfo);
+		} else {
+			lerr = ldap_simple_bind_s(ld, linfo->user_dn,
+						  linfo->passwd);
+		}
+#else /* ENABLE_LDAP_SASL */
 		lerr = ldap_simple_bind_s(ld, linfo->user_dn, linfo->passwd);
+#endif /* else ENABLE_LDAP_SASL */
 		if (lerr) {
 			char *errmsg;
 			if (lerr == LDAP_PROTOCOL_ERROR) {
@@ -267,10 +454,22 @@ retry_bind:
 				}
 				goto retry_bind;
 			}
-			IDMAP_LOG(2, ("ldap_init_and_bind: ldap_simple_bind_s "
+#ifdef ENABLE_LDAP_SASL
+			IDMAP_LOG(2, ("ldap_init_and_bind: %s "
+				  "to [%s] as user '%s': %s (%d)",
+				  (linfo->sasl_mech != NULL &&
+				   linfo->sasl_mech[0] != '\0') ?
+				   "ldap_sasl_interactive_bind_s" :
+				   "ldap_simple_bind_s",
+				  server_url, linfo->user_dn,
+				  ldap_err2string(lerr), lerr));
+#else /* ENABLE_LDAP_SASL */
+			IDMAP_LOG(2, ("ldap_init_and_bind: ldap_simple_bind_s"
 				  "to [%s] as user '%s': %s (%d)",
 				  server_url, linfo->user_dn,
 				  ldap_err2string(lerr), lerr));
+
+#endif /* else ENABLE_LDAP_SASL */
 			if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS)
 					&& (errmsg != NULL)&& (*errmsg != '\0')) {
 				IDMAP_LOG(2, ("ldap_init_and_bind: "
@@ -1155,6 +1354,30 @@ umichldap_init(void)
 				      (ldap_info.use_ssl) ?
 				      LDAPS_PORT : LDAP_PORT);
 
+	ldap_info.sasl_mech = conf_get_str(LDAP_SECTION, "LDAP_sasl_mech");
+	ldap_info.sasl_realm = conf_get_str(LDAP_SECTION, "LDAP_sasl_realm");
+	ldap_info.sasl_authcid = conf_get_str(LDAP_SECTION,
+					      "LDAP_sasl_authcid");
+	ldap_info.sasl_authzid = conf_get_str(LDAP_SECTION,
+					      "LDAP_sasl_authzid");
+	ldap_info.sasl_secprops = conf_get_str(LDAP_SECTION,
+					       "LDAP_sasl_secprops");
+
+	/* If it is not set let the ldap lib work with the lib default */
+	canonicalize = conf_get_str_with_def(LDAP_SECTION,
+					     "LDAP_sasl_canonicalize", "undef");
+	if ((strcasecmp(canonicalize, "true") == 0) ||
+	    (strcasecmp(canonicalize, "on") == 0) ||
+	    (strcasecmp(canonicalize, "yes") == 0)) {
+		ldap_info.sasl_canonicalize = 1;
+	} else if ((strcasecmp(canonicalize, "false") == 0) ||
+	    (strcasecmp(canonicalize, "off") == 0) ||
+	    (strcasecmp(canonicalize, "no") == 0)) {
+		ldap_info.sasl_canonicalize = 0;
+	}
+	ldap_info.sasl_krb5_ccname = conf_get_str(LDAP_SECTION,
+						  "LDAP_sasl_krb5_ccname");
+
 	/* Verify required information is supplied */
 	if (server_in == NULL || strlen(server_in) == 0)
 		strncat(missing_msg, "LDAP_server ", sizeof(missing_msg)-1);
@@ -1167,7 +1390,8 @@ umichldap_init(void)
 	}
 
 	ldap_info.server = server_in;
-	canonicalize = conf_get_str_with_def(LDAP_SECTION, "LDAP_canonicalize_name", "yes");
+	canonicalize = conf_get_str_with_def(LDAP_SECTION,
+					     "LDAP_canonicalize_name", "yes");
 	if ((strcasecmp(canonicalize, "true") == 0) ||
 	    (strcasecmp(canonicalize, "on") == 0) ||
 	    (strcasecmp(canonicalize, "yes") == 0)) {
@@ -1296,6 +1520,28 @@ umichldap_init(void)
 		  ldap_info.tls_reqcert));
 	IDMAP_LOG(1, ("umichldap_init: use_memberof_for_groups : %s",
 		  ldap_info.memberof_for_groups ? "yes" : "no"));
+	IDMAP_LOG(1, ("umichldap_init: sasl_mech: %s",
+		  (ldap_info.sasl_mech && strlen(ldap_info.sasl_mech) != 0) ?
+		  ldap_info.sasl_mech : "<not-supplied>"));
+	IDMAP_LOG(1, ("umichldap_init: sasl_realm: %s",
+		  (ldap_info.sasl_realm && strlen(ldap_info.sasl_realm) != 0) ?
+		  ldap_info.sasl_realm : "<not-supplied>"));
+	IDMAP_LOG(1, ("umichldap_init: sasl_authcid: %s",
+		  (ldap_info.sasl_authcid &&
+		   strlen(ldap_info.sasl_authcid) != 0) ?
+		  ldap_info.sasl_authcid : "<not-supplied>"));
+	IDMAP_LOG(1, ("umichldap_init: sasl_authzid: %s",
+		  (ldap_info.sasl_authzid &&
+		   strlen(ldap_info.sasl_authzid) != 0) ?
+		  ldap_info.sasl_authzid : "<not-supplied>"));
+	IDMAP_LOG(1, ("umichldap_init: sasl_secprops: %s",
+		  (ldap_info.sasl_secprops &&
+		   strlen(ldap_info.sasl_secprops) != 0) ?
+		  ldap_info.sasl_secprops : "<not-supplied>"));
+	IDMAP_LOG(1, ("umichldap_init: sasl_canonicalize: %d",
+		      ldap_info.sasl_canonicalize));
+	IDMAP_LOG(1, ("umichldap_init: sasl_krb5_ccname: %s",
+		      ldap_info.sasl_krb5_ccname));
 
 	IDMAP_LOG(1, ("umichldap_init: NFSv4_person_objectclass : %s",
 		  ldap_map.NFSv4_person_objcls));
-- 
2.25.1




[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux