aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSrikrishan Malik <srikrishanmalik@gmail.com>2020-04-13 14:22:08 -0400
committerSteve Dickson <steved@redhat.com>2020-04-13 14:47:24 -0400
commit1699fc34fe74ceda67e45453890a654c59f2b9e3 (patch)
tree14f3ab8988cf8697c166c1edf38a5b2b3e033d6a
parent266dd839e6399108cabe4b2d63c10dac5d8066dd (diff)
downloadnfs-utils-1699fc34fe74ceda67e45453890a654c59f2b9e3.tar.gz
nfsidmap:umich_ldap: Add support for SASL binds.
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@gmail.com Signed-off-by: Steve Dickson <steved@redhat.com>
-rw-r--r--configure.ac23
-rw-r--r--support/nfsidmap/Makefile.am5
-rw-r--r--support/nfsidmap/idmapd.conf23
-rw-r--r--support/nfsidmap/idmapd.conf.537
-rw-r--r--support/nfsidmap/umich_ldap.c250
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 35575a95..f5b9de0e 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 regex.la $(UMICH_LDAP_LIB) $(GUMS_MAPPING_LIB)
@@ -47,7 +50,7 @@ regex_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 e554a44e..d2fd3a20 100644
--- a/support/nfsidmap/idmapd.conf.5
+++ b/support/nfsidmap/idmapd.conf.5
@@ -258,6 +258,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)
@@ -309,10 +342,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));