On 4/4/20 5:02 AM, Srikrishan Malik wrote: > 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. Committed... (tag: nfs-utils-2-4-4-rc3) steved. > > 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)); >