# searches will start from.
base_dn = 'dc=example,dc=org'
- # SASL mechanism to use for administrative binds.
- # Uncomment for certificate auth or peercred auth (ldapi:// only).
-# sasl_mech = 'EXTERNAL'
+ # SASL parameters to use for admin binds
+ #
+ # When we're prompted by the SASL library, these control
+ # the responses given.
+ #
+ sasl {
+ # SASL mechanism
+# mech = 'PLAIN'
+
+ # SASL authorisation identity to proxy.
+# proxy = 'autz_id'
+
+ # SASL realm. Used for kerberos.
+# realm = 'example.org'
+ }
#
# Generic valuepair attribute
# to identify a single user object.
filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
- # SASL mechanism to use for user binds.
-# sasl_mech = 'PLAIN'
+ # SASL parameters to use for user binds
+ #
+ # When we're prompted by the SASL library, these control
+ # the responses given.
+ #
+ # Any of the config items below may be an attribute ref
+ # or and expansion, so different SASL mechs, proxy IDs
+ # and realms may be used for different users.
+ sasl {
+ # SASL mechanism
+# mech = 'PLAIN'
+
+ # SASL authorisation identity to proxy.
+# proxy = &User-Name
+
+ # SASL realm. Used for kerberos.
+# realm = 'example.org'
+ }
# Search scope, may be 'base', 'one', sub' or 'children'
# scope = 'sub'
* Perform all searches as the admin user.
*/
if (conn->rebound) {
- status = rlm_ldap_bind(inst, NULL, &conn, conn->inst->admin_dn, conn->inst->password,
- conn->inst->admin_sasl_mech, true);
+ status = rlm_ldap_bind(inst, NULL, &conn, conn->inst->admin_identity, conn->inst->admin_password,
+ &(conn->inst->admin_sasl), true);
if (status != LDAP_PROC_SUCCESS) {
ret = -1;
goto finish;
/* Define to 1 if you have the `ldap_initialize' function. */
#undef HAVE_LDAP_INITIALIZE
-/* Define to 1 if you have the `ldap_sasl_bind' function. */
-#undef HAVE_LDAP_SASL_BIND
+/* Define to 1 if you have the `ldap_sasl_interactive_bind' function. */
+#undef HAVE_LDAP_SASL_INTERACTIVE_BIND
/* Define to 1 if you have the `ldap_set_rebind_proc' function. */
#undef HAVE_LDAP_SET_REBIND_PROC
if test "x$fail" = "x"; then
- for ac_func in ldap_sasl_bind \
+ for ac_func in ldap_sasl_interactive_bind \
ldap_unbind_ext_s \
ldap_start_tls_s \
ldap_initialize \
if test "x$fail" = "x"; then
AC_CHECK_FUNCS(
- ldap_sasl_bind \
+ ldap_sasl_interactive_bind \
ldap_unbind_ext_s \
ldap_start_tls_s \
ldap_initialize \
* (with talloc_free).
* @return One of the LDAP_PROC_* (#ldap_rcode_t) values.
*/
-static ldap_rcode_t rlm_ldap_result(rlm_ldap_t const *inst, ldap_handle_t const *conn, int msgid, char const *dn,
- LDAPMessage **result, char const **error, char **extra)
+ldap_rcode_t rlm_ldap_result(rlm_ldap_t const *inst, ldap_handle_t const *conn, int msgid, char const *dn,
+ LDAPMessage **result, char const **error, char **extra)
{
ldap_rcode_t status = LDAP_PROC_SUCCESS;
*error = "Success";
break;
+ case LDAP_SASL_BIND_IN_PROGRESS:
+ *error = "Continuing";
+ status = LDAP_PROC_CONTINUE;
+ break;
+
case LDAP_NO_SUCH_OBJECT:
*error = "The specified DN wasn't found";
status = LDAP_PROC_BAD_DN;
talloc_free(our_err);
- if ((lib_errno || srv_errno) && *result) {
+ if ((status < 0) && *result) {
ldap_msgfree(*result);
*result = NULL;
}
* @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
* @param[in] dn of the user, may be NULL to bind anonymously.
* @param[in] password of the user, may be NULL if no password is specified.
- * @param[in] sasl_mech SASL mechanism to use for bind.
+ * @param[in] sasl mechanism to use for bind, and additional parameters.
* @param[in] retry if the server is down.
- * @return one of the LDAP_PROC_* values.
+ * @return One of the LDAP_PROC_* (#ldap_rcode_t) values.
*/
ldap_rcode_t rlm_ldap_bind(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn,
- char const *password, char const *sasl_mech, bool retry)
+ char const *password, ldap_sasl *sasl, bool retry)
{
- ldap_rcode_t status = LDAP_PROC_ERROR;
+ ldap_rcode_t status = LDAP_PROC_ERROR;
- int msgid = -1;
+ int msgid = -1;
- char const *error = NULL;
- char *extra = NULL;
+ char const *error = NULL;
+ char *extra = NULL;
- int i, num;
+ int i, num;
rad_assert(*pconn && (*pconn)->handle);
rad_assert(!retry || inst->pool);
+#ifndef HAVE_LDAP_SASL_INTERACTIVE_BIND
+ rad_assert(!sasl->mech);
+#endif
+
/*
* Bind as anonymous user
*/
*/
num = retry ? fr_connection_get_num(inst->pool) : 0;
for (i = num; i >= 0; i--) {
-#ifdef HAVE_LDAP_SASL_BIND
- if (sasl_mech) {
- struct berval cred;
-
- if (password) {
- memcpy(&cred.bv_val, &password, sizeof(cred.bv_val));
- cred.bv_len = talloc_array_length(password) - 1;
- } else {
- memset(&cred, 0, sizeof(cred));
- }
- ldap_sasl_bind((*pconn)->handle, dn, sasl_mech, &cred, NULL, NULL, &msgid);
+#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND
+ if (sasl->mech) {
+ status = rlm_ldap_sasl_interactive(inst, request, *pconn, dn, password, sasl,
+ &error, &extra);
} else
#endif
- msgid = ldap_bind((*pconn)->handle, dn, password, LDAP_AUTH_SIMPLE);
-
- /* We got a valid message ID */
- if (msgid >= 0) {
- if (request) {
- RDEBUG2("Waiting for bind result...");
- } else {
- DEBUG2("rlm_ldap (%s): Waiting for bind result...", inst->name);
+ {
+ msgid = ldap_bind((*pconn)->handle, dn, password, LDAP_AUTH_SIMPLE);
+
+ /* We got a valid message ID */
+ if (msgid >= 0) {
+ if (request) {
+ RDEBUG2("Waiting for bind result...");
+ } else {
+ DEBUG2("rlm_ldap (%s): Waiting for bind result...", inst->name);
+ }
}
- }
- status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
+ status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
+ }
switch (status) {
case LDAP_PROC_SUCCESS:
* Do all searches as the admin user.
*/
if ((*pconn)->rebound) {
- status = rlm_ldap_bind(inst, request, pconn, (*pconn)->inst->admin_dn, (*pconn)->inst->password,
- (*pconn)->inst->admin_sasl_mech, true);
+ status = rlm_ldap_bind(inst, request, pconn, (*pconn)->inst->admin_identity,
+ (*pconn)->inst->admin_password, &(*pconn)->inst->admin_sasl, true);
if (status != LDAP_PROC_SUCCESS) {
return LDAP_PROC_ERROR;
}
* Perform all modifications as the admin user.
*/
if ((*pconn)->rebound) {
- status = rlm_ldap_bind(inst, request, pconn, (*pconn)->inst->admin_dn, (*pconn)->inst->password,
- (*pconn)->inst->admin_sasl_mech, true);
+ status = rlm_ldap_bind(inst, request, pconn, (*pconn)->inst->admin_identity,
+ (*pconn)->inst->admin_password, &(*pconn)->inst->admin_sasl, true);
if (status != LDAP_PROC_SUCCESS) {
return LDAP_PROC_ERROR;
}
* Perform all searches as the admin user.
*/
if ((*pconn)->rebound) {
- status = rlm_ldap_bind(inst, request, pconn, (*pconn)->inst->admin_dn, (*pconn)->inst->password,
- (*pconn)->inst->admin_sasl_mech, true);
+ status = rlm_ldap_bind(inst, request, pconn, (*pconn)->inst->admin_identity,
+ (*pconn)->inst->admin_password, &(*pconn)->inst->admin_sasl, true);
if (status != LDAP_PROC_SUCCESS) {
*rcode = RLM_MODULE_FAIL;
return NULL;
DEBUG("rlm_ldap (%s): Rebinding to URL %s", conn->inst->name, url);
- status = rlm_ldap_bind(conn->inst, NULL, &conn, conn->inst->admin_dn, conn->inst->password,
- conn->inst->admin_sasl_mech, false);
+ status = rlm_ldap_bind(conn->inst, NULL, &conn, conn->inst->admin_identity, conn->inst->admin_password,
+ &(conn->inst->admin_sasl), false);
if (status != LDAP_PROC_SUCCESS) {
ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
}
#endif /* HAVE_LDAP_START_TLS_S */
- status = rlm_ldap_bind(inst, NULL, &conn, conn->inst->admin_dn, conn->inst->password,
- conn->inst->admin_sasl_mech, false);
+ status = rlm_ldap_bind(inst, NULL, &conn, conn->inst->admin_identity, conn->inst->admin_password,
+ &(conn->inst->admin_sasl), false);
if (status != LDAP_PROC_SUCCESS) {
goto error;
}
# define LDAP_CONST
#endif
+#define MOD_PREFIX "rlm_ldap" //!< The name of the module.
+
#define LDAP_MAX_ATTRMAP 128 //!< Maximum number of mappings between LDAP and
//!< FreeRADIUS attributes.
#define LDAP_MAP_RESERVED 4 //!< Number of additional items to allocate in expanded
#define LDAP_MAX_GROUP_NAME_LEN 128 //!< Maximum name of a group name.
#define LDAP_MAX_ATTR_STR_LEN 256 //!< Maximum length of an xlat expanded LDAP attribute.
#define LDAP_MAX_FILTER_STR_LEN 1024 //!< Maximum length of an xlat expanded filter.
-#define LDAP_MAX_DN_STR_LEN 2048 //!< Maximum length of an xlat expanded DN.
+#define LDAP_MAX_DN_STR_LEN 1024 //!< Maximum length of an xlat expanded DN.
+
+#define LDAP_VIRTUAL_DN_ATTR "dn" //!< 'Virtual' attribute which maps to the DN of the object.
typedef struct ldap_acct_section {
CONF_SECTION *cs; //!< Section configuration.
- char const *reference; //!< Configuration reference string.
+ char const *reference; //!< Configuration reference string.
} ldap_acct_section_t;
+typedef struct ldap_sasl {
+ char const *mech; //!< SASL mech(s) to try.
+ char const *proxy; //!< Identity to proxy.
+ char const *realm; //!< Kerberos realm.
+} ldap_sasl;
+
+typedef struct ldap_sasl_dynamic {
+ vp_tmpl_t *mech; //!< SASL mech(s) to try.
+ vp_tmpl_t *proxy; //!< Identity to proxy.
+ vp_tmpl_t *realm; //!< Kerberos realm.
+} ldap_sasl_dynamic;
+
typedef struct ldap_instance {
CONF_SECTION *cs; //!< Main configuration section for this instance.
fr_connection_pool_t *pool; //!< Connection pool instance.
- char const *config_server; //!< server from the config files
+ char const *config_server; //!< Server set in the config.
char *server; //!< Initial server to bind to.
uint16_t port; //!< Port to use when binding to the server.
- char const *admin_dn; //!< DN we bind as when we need to query the LDAP
+ char const *admin_identity; //!< Identity we bind as when we need to query the LDAP
//!< directory.
- char const *password; //!< Password used in administrative bind.
+ char const *admin_password; //!< Password used in administrative bind.
- char const *admin_sasl_mech; //!< SASL mechanism to use for administrative binds.
+ ldap_sasl admin_sasl; //!< SASL parameters used when binding as the admin.
char const *dereference_str; //!< When to dereference (never, searching, finding, always)
int dereference; //!< libldap value specifying dereferencing behaviour.
/*
* User object attributes and filters
*/
- char const *user_sasl_mech; //!< SASL mechanism to use for user binds.
vp_tmpl_t *userobj_filter; //!< Filter to retrieve only user objects.
vp_tmpl_t *userobj_base_dn; //!< DN to search for users under.
char const *userobj_scope_str; //!< Scope (sub, one, base).
char const *valuepair_attr; //!< Generic dynamic mapping attribute, contains a RADIUS
//!< attribute and value.
+ ldap_sasl_dynamic user_sasl; //!< SASL parameters used when binding as the user.
+
/*
* Group object attributes and filters
*/
-
char const *groupobj_filter; //!< Filter to retrieve only group objects.
vp_tmpl_t *groupobj_base_dn; //!< DN to search for users under.
char const *groupobj_scope_str; //!< Scope (sub, one, base).
*
*/
typedef enum {
+ LDAP_PROC_CONTINUE = 1, //!< Operation is in progress.
LDAP_PROC_SUCCESS = 0, //!< Operation was successfull.
LDAP_PROC_ERROR = -1, //!< Unrecoverable library/server error.
ssize_t rlm_ldap_xlat_filter(REQUEST *request, char const **sub, size_t sublen, char *out, size_t outlen);
ldap_rcode_t rlm_ldap_bind(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn,
- char const *password, char const *sasl_mech, bool retry);
+ char const *password, ldap_sasl *sasl, bool retry);
char const *rlm_ldap_error_str(ldap_handle_t const *conn);
/*
* ldap.c - Callbacks for the connection pool API.
*/
+ldap_rcode_t rlm_ldap_result(rlm_ldap_t const *inst, ldap_handle_t const *conn, int msgid, char const *dn,
+ LDAPMessage **result, char const **error, char **extra);
+
char *rlm_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in);
void *mod_conn_create(TALLOC_CTX *ctx, void *instance);
char const *edir_errstr(int code);
+/*
+ * sasl.s - SASL bind functions
+ */
+ldap_rcode_t rlm_ldap_sasl_interactive(rlm_ldap_t const *inst, REQUEST *request,
+ ldap_handle_t *pconn, char const *dn,
+ char const *password, ldap_sasl *sasl,
+ char const **error, char **extra);
#endif
#include "ldap.h"
+#include <freeradius-devel/map_proc.h>
+
/*
* Scopes
*/
{ NULL , -1 }
};
+static CONF_PARSER sasl_mech_dynamic[] = {
+ { "mech", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL | PW_TYPE_NOT_EMPTY, ldap_sasl_dynamic, mech), NULL },
+ { "proxy", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, ldap_sasl_dynamic, proxy), NULL },
+ { "realm", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, ldap_sasl_dynamic, realm), NULL }
+};
+
+static CONF_PARSER sasl_mech_static[] = {
+ { "mech", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_NOT_EMPTY, ldap_sasl, mech), NULL },
+ { "proxy", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_sasl, proxy), NULL },
+ { "realm", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_sasl, realm), NULL }
+};
+
/*
* TLS Configuration
*/
{ "access_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, userobj_access_attr), NULL },
{ "access_positive", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, access_positive), "yes" },
- { "sasl_mech", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, user_sasl_mech), NULL },
+ /* Should be deprecated */
+ { "sasl", FR_CONF_OFFSET(PW_TYPE_SUBSECTION, rlm_ldap_t, user_sasl), (void const *) sasl_mech_dynamic },
{ NULL, -1, 0, NULL, NULL }
};
{ "server", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, config_server), NULL }, /* Do not set to required */
{ "port", FR_CONF_OFFSET(PW_TYPE_SHORT, rlm_ldap_t, port), NULL },
- { "password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, rlm_ldap_t, password), NULL },
- { "identity", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, admin_dn), NULL },
- { "sasl_mech", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, admin_sasl_mech), NULL },
+ { "identity", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, admin_identity), NULL },
+ { "password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, rlm_ldap_t, admin_password), NULL },
+
+ /* Should be deprecated */
+ { "sasl", FR_CONF_OFFSET(PW_TYPE_SUBSECTION, rlm_ldap_t, admin_sasl), (void const *) sasl_mech_static },
{ "valuepair_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, valuepair_attr), NULL },
}
}
-#ifndef HAVE_LDAP_SASL_BIND
- if (inst->user_sasl_mech) {
- cf_log_err_cs(conf, "Configuration item 'user.sasl_mech' not supported. "
+#ifndef HAVE_LDAP_SASL_INTERACTIVE_BIND
+ if (inst->user_sasl.mech) {
+ cf_log_err_cs(conf, "Configuration item 'user.sasl.mech' not supported. "
"Linked libldap does not provide ldap_sasl_bind function");
goto error;
}
- if (inst->admin_sasl_mech) {
- cf_log_err_cs(conf, "Configuration item 'sasl_mech' not supported. "
- "Linked libldap does not provide ldap_sasl_bind function");
+ if (inst->admin_sasl.mech) {
+ cf_log_err_cs(conf, "Configuration item 'sasl.mech' not supported. "
+ "Linked libldap does not provide ldap_sasl_interactive_bind function");
goto error;
}
#endif
bool first = true;
cp = cf_pair_find(conf, "server");
- rad_assert(cp != NULL);
+ if (!cp) {
+ cf_log_err_cs(conf, "Configuration item 'server' must have a value");
+ return -1;
+ }
value = cf_pair_value(cp);
rlm_ldap_t *inst = instance;
ldap_handle_t *conn;
+ char sasl_mech_buff[LDAP_MAX_DN_STR_LEN];
+ char sasl_proxy_buff[LDAP_MAX_DN_STR_LEN];
+ char sasl_realm_buff[LDAP_MAX_DN_STR_LEN];
+ ldap_sasl sasl;
+
/*
* Ensure that we're being passed a plain-text password, and not
* anything else.
return RLM_MODULE_INVALID;
}
- RDEBUG("Login attempt by \"%s\"", request->username->vp_strvalue);
-
conn = mod_conn_get(inst, request);
if (!conn) return RLM_MODULE_FAIL;
+ /*
+ * Expand dynamic SASL fields
+ */
+ if (conn->inst->user_sasl.mech) {
+ memset(&sasl, 0, sizeof(sasl));
+
+ if (tmpl_expand(&sasl.mech, sasl_mech_buff, sizeof(sasl_mech_buff), request,
+ conn->inst->user_sasl.mech, rlm_ldap_escape_func, inst) < 0) {
+ REDEBUG("Failed expanding user.sasl.mech: %s", fr_strerror());
+ rcode = RLM_MODULE_FAIL;
+ goto finish;
+ }
+
+ if (conn->inst->user_sasl.proxy) {
+ if (tmpl_expand(&sasl.proxy, sasl_proxy_buff, sizeof(sasl_proxy_buff), request,
+ conn->inst->user_sasl.proxy, rlm_ldap_escape_func, inst) < 0) {
+ REDEBUG("Failed expanding user.sasl.proxy: %s", fr_strerror());
+ rcode = RLM_MODULE_FAIL;
+ goto finish;
+ }
+ }
+
+ if (conn->inst->user_sasl.realm) {
+ if (tmpl_expand(&sasl.realm, sasl_realm_buff, sizeof(sasl_realm_buff), request,
+ conn->inst->user_sasl.realm, rlm_ldap_escape_func, inst) < 0) {
+ REDEBUG("Failed expanding user.sasl.realm: %s", fr_strerror());
+ rcode = RLM_MODULE_FAIL;
+ goto finish;
+ }
+ }
+ }
+
+ RDEBUG("Login attempt by \"%s\"", request->username->vp_strvalue);
+
/*
* Get the DN by doing a search.
*/
return rcode;
}
-
- /*
- * Bind as the user
- */
conn->rebound = true;
status = rlm_ldap_bind(inst, request, &conn, dn, request->password->vp_strvalue,
- conn->inst->user_sasl_mech, true);
+ conn->inst->user_sasl.mech ? &sasl : NULL, true);
switch (status) {
case LDAP_PROC_SUCCESS:
rcode = RLM_MODULE_OK;
break;
};
+finish:
mod_conn_release(inst, conn);
return rcode;
* Bind as the user
*/
conn->rebound = true;
- status = rlm_ldap_bind(inst, request, &conn, dn, vp->vp_strvalue,
- conn->inst->user_sasl_mech, true);
+ status = rlm_ldap_bind(inst, request, &conn, dn, vp->vp_strvalue, NULL, true);
switch (status) {
case LDAP_PROC_SUCCESS:
rcode = RLM_MODULE_OK;
--- /dev/null
+/*
+ * This program is is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "ldap.h"
+
+#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND
+/**
+ * $Id$
+ * @file sasl.c
+ * @brief Functions to perform SASL binds against an LDAP directory.
+ *
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2015 The FreeRADIUS Server Project.
+ */
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
+
+#include <sasl/sasl.h>
+
+typedef struct rlm_ldap_sasl_ctx {
+ rlm_ldap_t const *inst; //!< LDAP instance
+ REQUEST *request; //!< The current request.
+
+ char const *identity; //!< User's DN or identity.
+ char const *password; //!< Bind password.
+
+ ldap_sasl *extra; //!< Extra fields (realm and proxy id).
+} rlm_ldap_sasl_ctx_t;
+
+static int _sasl_interact(UNUSED LDAP *handle, UNUSED unsigned flags, void *ctx, void *sasl_callbacks)
+{
+ rlm_ldap_sasl_ctx_t *this = ctx;
+ REQUEST *request = this->request;
+ rlm_ldap_t const *inst = this->inst;
+ sasl_interact_t *cb = sasl_callbacks;
+ sasl_interact_t *cb_p;
+
+ for (cb_p = cb; cb_p->id != SASL_CB_LIST_END; cb_p++) {
+ MOD_ROPTIONAL(RDEBUG3, DEBUG3, "SASL challenge : %s", cb_p->challenge);
+ MOD_ROPTIONAL(RDEBUG3, DEBUG3, "SASL prompt : %s", cb_p->prompt);
+
+ switch (cb_p->id) {
+ case SASL_CB_AUTHNAME:
+ cb_p->result = this->identity;
+ break;
+
+ case SASL_CB_PASS:
+ cb_p->result = this->password;
+ break;
+
+ case SASL_CB_USER:
+ cb_p->result = this->extra->proxy ? this->extra->proxy : this->identity;
+ break;
+
+ case SASL_CB_GETREALM:
+ if (this->extra->realm) cb_p->result = this->extra->realm;
+ break;
+
+ default:
+ break;
+ }
+ MOD_ROPTIONAL(RDEBUG3, DEBUG3, "SASL result : %s", cb_p->result ? (char const *)cb_p->result : "");
+ }
+ return SASL_OK;
+}
+
+ldap_rcode_t rlm_ldap_sasl_interactive(rlm_ldap_t const *inst, REQUEST *request,
+ ldap_handle_t *conn, char const *identity,
+ char const *password, ldap_sasl *sasl,
+ char const **error, char **extra)
+{
+ ldap_rcode_t status;
+ int ret = 0;
+ int msgid;
+ char const *mech;
+ LDAPMessage *result = NULL;
+ rlm_ldap_sasl_ctx_t sasl_ctx; /* SASL defaults */
+
+ memset(&sasl_ctx, 0, sizeof(sasl_ctx));
+
+ sasl_ctx.inst = inst;
+ sasl_ctx.request = request;
+ sasl_ctx.identity = identity;
+ sasl_ctx.password = password;
+
+ MOD_ROPTIONAL(RDEBUG2, DEBUG2, "Starting SASL mech(s): %s", sasl->mech);
+ do {
+ ret = ldap_sasl_interactive_bind(conn->handle, NULL, sasl->mech,
+ NULL, NULL, LDAP_SASL_AUTOMATIC,
+ _sasl_interact, &sasl_ctx, result,
+ &mech, &msgid);
+ ldap_msgfree(result); /* We always need to free the old message */
+ if (ret >= 0) MOD_ROPTIONAL(RDEBUG3, DEBUG3, "Continuing SASL mech %s...", mech);
+
+ status = rlm_ldap_result(inst, conn, msgid, identity, &result, error, extra);
+ /*
+ * Write the servers response to the debug log
+ */
+ if (((request && RDEBUG_ENABLED3) || DEBUG_ENABLED3) && result) {
+ struct berval *srv_cred;
+
+ if (ldap_parse_sasl_bind_result(conn->handle, result, &srv_cred, 0) == 0) {
+ char *escaped;
+
+ escaped = fr_aprints(request, srv_cred->bv_val, srv_cred->bv_len, '\0');
+ MOD_ROPTIONAL(RDEBUG3, DEBUG3, "SASL response : %s", escaped);
+
+ talloc_free(escaped);
+ ldap_memfree(srv_cred);
+ }
+ }
+ } while (status == LDAP_PROC_CONTINUE);
+ ldap_msgfree(result);
+
+ return status;
+}
+#endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND */