krb5 commit: Add SASL support to LDAP KDB module

Greg Hudson ghudson at MIT.EDU
Sat Jul 19 16:39:25 EDT 2014


https://github.com/krb5/krb5/commit/e94082d8c923cff454c1bc53b377ba394a3cec3c
commit e94082d8c923cff454c1bc53b377ba394a3cec3c
Author: Greg Hudson <ghudson at mit.edu>
Date:   Mon Jun 16 12:41:03 2014 -0400

    Add SASL support to LDAP KDB module
    
    Add variables for the SASL mechanism, authcid, authzid, and realm.  If
    a SASL mechanism is set, perform an interactive bind with that
    mechanism.  If <sasl/sasl.h> is found at build time, provide the
    authcid, authzid, and realm in the interaction function, and provide a
    SASL secret read from the service password file (under the authcid) if
    we found one.
    
    Based on a patch from Zoran Pericic <zpericic at netst.org>.
    
    ticket: 7944 (new)

 src/Makefile.in                                  |    1 +
 src/config/pre.in                                |    3 +
 src/configure.in                                 |    6 ++
 src/include/k5-int.h                             |    8 ++
 src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h      |    4 +
 src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c |   87 +++++++++++++++++++---
 src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c     |   68 +++++++++++++++++
 7 files changed, 167 insertions(+), 10 deletions(-)

diff --git a/src/Makefile.in b/src/Makefile.in
index 522f21d..60a17d9 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -555,6 +555,7 @@ pyrunenv.vals: Makefile
 		eval echo 'env['\\\'$$i\\\''] = '\\\'\$$$$i\\\'; \
 	done > $@
 	echo "tls_impl = '$(TLS_IMPL)'" >> $@
+	echo "have_sasl = '$(HAVE_SASL)'" >> $@
 
 runenv.py: pyrunenv.vals
 	echo 'env = {}' > $@
diff --git a/src/config/pre.in b/src/config/pre.in
index 002c2f7..c7cff81 100644
--- a/src/config/pre.in
+++ b/src/config/pre.in
@@ -434,6 +434,9 @@ TLS_IMPL	= @TLS_IMPL@
 TLS_IMPL_CFLAGS = @TLS_IMPL_CFLAGS@
 TLS_IMPL_LIBS	= @TLS_IMPL_LIBS@
 
+# Whether we have the SASL header file for the LDAP KDB module
+HAVE_SASL = @HAVE_SASL@
+
 # error table rules
 #
 ### /* these are invoked as $(...) foo.et, which works, but could be better */
diff --git a/src/configure.in b/src/configure.in
index 2e22470..659c4f8 100644
--- a/src/configure.in
+++ b/src/configure.in
@@ -1171,6 +1171,12 @@ if test -n "$OPENLDAP_PLUGIN"; then
   AC_DEFINE([ENABLE_LDAP], 1, [Define if LDAP KDB support within the Kerberos library (mainly ASN.1 code) should be enabled.])
   AC_SUBST(LDAP_LIBS)
 
+  AC_CHECK_HEADERS([sasl/sasl.h], [HAVE_SASL=yes], [HAVE_SASL=no])
+  AC_SUBST(HAVE_SASL)
+  if test "$HAVE_SASL" = no; then
+    AC_MSG_WARN([not building LDAP SASL support])
+  fi
+
   K5_GEN_MAKEFILE(plugins/kdb/ldap)
   K5_GEN_MAKEFILE(plugins/kdb/ldap/ldap_util)
   K5_GEN_MAKEFILE(plugins/kdb/ldap/libkdb_ldap)
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 38846eb..d9cb5a4 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -239,7 +239,15 @@ typedef unsigned char   u_char;
 #define KRB5_CONF_KRB524_SERVER                "krb524_server"
 #define KRB5_CONF_LDAP_CONNS_PER_SERVER        "ldap_conns_per_server"
 #define KRB5_CONF_LDAP_KADMIND_DN              "ldap_kadmind_dn"
+#define KRB5_CONF_LDAP_KADMIND_SASL_AUTHCID    "ldap_kadmind_sasl_authcid"
+#define KRB5_CONF_LDAP_KADMIND_SASL_AUTHZID    "ldap_kadmind_sasl_authzid"
+#define KRB5_CONF_LDAP_KADMIND_SASL_MECH       "ldap_kadmind_sasl_mech"
+#define KRB5_CONF_LDAP_KADMIND_SASL_REALM      "ldap_kadmind_sasl_realm"
 #define KRB5_CONF_LDAP_KDC_DN                  "ldap_kdc_dn"
+#define KRB5_CONF_LDAP_KDC_SASL_AUTHCID        "ldap_kdc_sasl_authcid"
+#define KRB5_CONF_LDAP_KDC_SASL_AUTHZID        "ldap_kdc_sasl_authzid"
+#define KRB5_CONF_LDAP_KDC_SASL_MECH           "ldap_kdc_sasl_mech"
+#define KRB5_CONF_LDAP_KDC_SASL_REALM          "ldap_kdc_sasl_realm"
 #define KRB5_CONF_LDAP_KERBEROS_CONTAINER_DN   "ldap_kerberos_container_dn"
 #define KRB5_CONF_LDAP_SERVERS                 "ldap_servers"
 #define KRB5_CONF_LDAP_SERVICE_PASSWORD_FILE   "ldap_service_password_file"
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h
index 319c701..3e98b53 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h
+++ b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h
@@ -199,6 +199,10 @@ typedef struct _krb5_ldap_context {
     char                          *bind_dn;
     char                          *bind_pwd;
     char                          *service_password_file;
+    char                          *sasl_mech;
+    char                          *sasl_authcid;
+    char                          *sasl_authzid;
+    char                          *sasl_realm;
     char                          *root_certificate_file;
     krb5_ui_4                     cert_count; /* certificate count */
     k5_mutex_t                    hndl_lock;
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c
index 78ea428..16ac60b 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c
@@ -36,6 +36,9 @@
 #include "ldap_main.h"
 #include "ldap_service_stash.h"
 #include <kdb5.h>
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#endif
 
 /* Ensure that we have the parameters we need to authenticate to the LDAP
  * server.  Read the password if necessary. */
@@ -44,6 +47,19 @@ validate_context(krb5_context context, krb5_ldap_context *ctx)
 {
     krb5_error_code ret;
 
+    if (ctx->sasl_mech != NULL) {
+        /* Read the password for use as the SASL secret if we can, but do not
+         * require one as not all mechanisms need it. */
+        if (ctx->bind_pwd == NULL && ctx->sasl_authcid != NULL &&
+            ctx->service_password_file != NULL) {
+            (void)krb5_ldap_readpassword(context, ctx->service_password_file,
+                                         ctx->sasl_authcid, &ctx->bind_pwd);
+        }
+        return 0;
+    }
+
+    /* For a simple bind, a DN and password are required. */
+
     if (ctx->bind_dn == NULL) {
         k5_setmsg(context, EINVAL, _("LDAP bind dn value missing"));
         return EINVAL;
@@ -77,22 +93,73 @@ validate_context(krb5_context context, krb5_ldap_context *ctx)
  * Internal Functions called by init functions.
  */
 
+#ifdef HAVE_SASL_SASL_H
+
+static int
+interact(LDAP *ld, unsigned flags, void *defaults, void *sin)
+{
+    sasl_interact_t *in = NULL;
+    krb5_ldap_context *ctx = defaults;
+
+    for (in = sin; in != NULL && in->id != SASL_CB_LIST_END; in++) {
+        if (in->id == SASL_CB_AUTHNAME)
+            in->result = ctx->sasl_authcid;
+        else if (in->id == SASL_CB_USER)
+            in->result = ctx->sasl_authzid;
+        else if (in->id == SASL_CB_GETREALM)
+            in->result = ctx->sasl_realm;
+        else if (in->id == SASL_CB_PASS)
+            in->result = ctx->bind_pwd;
+        else
+            return LDAP_OTHER;
+        in->len = (in->result != NULL) ? strlen(in->result) : 0;
+    }
+
+    return LDAP_SUCCESS;
+}
+
+#else /* HAVE_SASL_SASL_H */
+
+/* We can't define an interaction function, so only non-interactive mechs like
+ * EXTERNAL can work. */
+static int
+interact(LDAP *ld, unsigned flags, void *defaults, void *sin)
+{
+    return LDAP_OTHER;
+}
+
+#endif
+
 static krb5_error_code
 authenticate(krb5_ldap_context *ctx, krb5_ldap_server_handle *server)
 {
     int st;
     struct berval bv;
 
-    bv.bv_val = ctx->bind_pwd;
-    bv.bv_len = strlen(ctx->bind_pwd);
-    st = ldap_sasl_bind_s(server->ldap_handle, ctx->bind_dn, NULL, &bv, NULL,
-                          NULL, NULL);
-    if (st != LDAP_SUCCESS) {
-        k5_setmsg(ctx->kcontext, KRB5_KDB_ACCESS_ERROR,
-                  _("Cannot bind to LDAP server '%s' as '%s': %s"),
-                  server->server_info->server_name, ctx->bind_dn,
-                  ldap_err2string(st));
-        return KRB5_KDB_ACCESS_ERROR;
+    if (ctx->sasl_mech != NULL) {
+        st = ldap_sasl_interactive_bind_s(server->ldap_handle, NULL,
+                                          ctx->sasl_mech, NULL, NULL,
+                                          LDAP_SASL_QUIET, interact, ctx);
+        if (st != LDAP_SUCCESS) {
+            k5_setmsg(ctx->kcontext, KRB5_KDB_ACCESS_ERROR,
+                      _("Cannot bind to LDAP server '%s' with SASL mechanism "
+                        "'%s': %s"), server->server_info->server_name,
+                      ctx->sasl_mech, ldap_err2string(st));
+            return KRB5_KDB_ACCESS_ERROR;
+        }
+    } else {
+        /* Do a simple bind with DN and password. */
+        bv.bv_val = ctx->bind_pwd;
+        bv.bv_len = strlen(ctx->bind_pwd);
+        st = ldap_sasl_bind_s(server->ldap_handle, ctx->bind_dn, NULL, &bv,
+                              NULL, NULL, NULL);
+        if (st != LDAP_SUCCESS) {
+            k5_setmsg(ctx->kcontext, KRB5_KDB_ACCESS_ERROR,
+                      _("Cannot bind to LDAP server '%s' as '%s': %s"),
+                      server->server_info->server_name, ctx->bind_dn,
+                      ldap_err2string(st));
+            return KRB5_KDB_ACCESS_ERROR;
+        }
     }
     return 0;
 }
diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c
index 6c1ac5d..4a29aa5 100644
--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c
+++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c
@@ -243,6 +243,34 @@ krb5_ldap_parse_db_params(krb5_context context, char **db_args)
                 ret = ENOMEM;
                 goto cleanup;
             }
+        } else if (!strcmp(opt, "sasl_mech")) {
+            free(ctx->sasl_mech);
+            ctx->sasl_mech = strdup(val);
+            if (ctx->sasl_mech == NULL) {
+                ret = ENOMEM;
+                goto cleanup;
+            }
+        } else if (!strcmp(opt, "sasl_authcid")) {
+            free(ctx->sasl_authcid);
+            ctx->sasl_authcid = strdup(val);
+            if (ctx->sasl_authcid == NULL) {
+                ret = ENOMEM;
+                goto cleanup;
+            }
+        } else if (!strcmp(opt, "sasl_authzid")) {
+            free(ctx->sasl_authzid);
+            ctx->sasl_authzid = strdup(val);
+            if (ctx->sasl_authzid == NULL) {
+                ret = ENOMEM;
+                goto cleanup;
+            }
+        } else if (!strcmp(opt, "sasl_realm")) {
+            free(ctx->sasl_realm);
+            ctx->sasl_realm = strdup(val);
+            if (ctx->sasl_realm == NULL) {
+                ret = ENOMEM;
+                goto cleanup;
+            }
         } else if (!strcmp(opt, "host")) {
             ret = add_server_entry(context, val);
             if (ret)
@@ -334,6 +362,42 @@ krb5_ldap_read_server_params(krb5_context context, char *conf_section,
             return ret;
     }
 
+    if (ldap_context->sasl_mech == NULL) {
+        name = choose_var(srv_type, KRB5_CONF_LDAP_KDC_SASL_MECH,
+                          KRB5_CONF_LDAP_KADMIND_SASL_MECH);
+        ret = prof_get_string_def(context, conf_section, name,
+                                  &ldap_context->sasl_mech);
+        if (ret)
+            return ret;
+    }
+
+    if (ldap_context->sasl_authcid == NULL) {
+        name = choose_var(srv_type, KRB5_CONF_LDAP_KDC_SASL_AUTHCID,
+                          KRB5_CONF_LDAP_KADMIND_SASL_AUTHCID);
+        ret = prof_get_string_def(context, conf_section, name,
+                                  &ldap_context->sasl_authcid);
+        if (ret)
+            return ret;
+    }
+
+    if (ldap_context->sasl_authzid == NULL) {
+        name = choose_var(srv_type, KRB5_CONF_LDAP_KDC_SASL_AUTHZID,
+                          KRB5_CONF_LDAP_KADMIND_SASL_AUTHZID);
+        ret = prof_get_string_def(context, conf_section, name,
+                                  &ldap_context->sasl_authzid);
+        if (ret)
+            return ret;
+    }
+
+    if (ldap_context->sasl_realm == NULL) {
+        name = choose_var(srv_type, KRB5_CONF_LDAP_KDC_SASL_REALM,
+                          KRB5_CONF_LDAP_KADMIND_SASL_REALM);
+        ret = prof_get_string_def(context, conf_section, name,
+                                  &ldap_context->sasl_realm);
+        if (ret)
+            return ret;
+    }
+
     /* Read the LDAP server URL list. */
     if (ldap_context->server_info_list == NULL) {
         ret = profile_get_string(context->profile, KDB_MODULE_SECTION,
@@ -394,6 +458,10 @@ krb5_ldap_free_server_context_params(krb5_ldap_context *ctx)
     free(list);
     ctx->server_info_list = NULL;
 
+    free(ctx->sasl_mech);
+    free(ctx->sasl_authcid);
+    free(ctx->sasl_authzid);
+    free(ctx->sasl_realm);
     free(ctx->conf_section);
     free(ctx->bind_dn);
     zapfreestr(ctx->bind_pwd);


More information about the cvs-krb5 mailing list