]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
Add SASL support to LDAP KDB module
authorGreg Hudson <ghudson@mit.edu>
Mon, 16 Jun 2014 16:41:03 +0000 (12:41 -0400)
committerGreg Hudson <ghudson@mit.edu>
Sat, 19 Jul 2014 20:34:57 +0000 (16:34 -0400)
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@netst.org>.

ticket: 7944 (new)

src/Makefile.in
src/config/pre.in
src/configure.in
src/include/k5-int.h
src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.h
src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c
src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c

index 522f21d44f4c24390c5c05b125679ce043226fe4..60a17d913f5fbd978169d42e2ef4989345bedeb3 100644 (file)
@@ -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 = {}' > $@
index 002c2f7a56702c9be542dc6f0b7c2cc9ec13bd71..c7cff814c633713e473b357f96fa1ebde0bf5351 100644 (file)
@@ -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 */
index 2e22470155159e004876bec88e80f23fed872da3..659c4f8c79c8834ed761f4deb57c628243df3d63 100644 (file)
@@ -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)
index 38846eb518c894b5b7c404b15c391a33d7835d37..d9cb5a4b2751d0d5926e9c18e1b894e3b1d7da49 100644 (file)
@@ -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"
index 319c7017d475262cd5714934b55e8cc50531c57f..3e98b53147d1ecf36cfbcce147ec174c3b43bc3e 100644 (file)
@@ -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;
index 78ea428056f7ff7b70ea8034edf85208201f5713..16ac60be3dd217b8b8cc0cba395e04dba3b15587 100644 (file)
@@ -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;
 }
index 6c1ac5d3b9eae08a06ae81cd1b2c470a8949ca9f..4a29aa5b5a1f7e6826941a08af7eb6281ad9a0f4 100644 (file)
@@ -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);