* @copyright 2013 Network RADIUS SARL <info@networkradius.com>
* @copyright 2013 The FreeRADIUS Server Project.
*/
-#include <freeradius-devel/radiusd.h>
-#include <freeradius-devel/modules.h>
-#include <freeradius-devel/rad_assert.h>
-
-#include <stdarg.h>
-#include <ctype.h>
-
-#include <lber.h>
-#include <ldap.h>
-#include "ldap.h"
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
+#include <freeradius-devel/rad_assert.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <lber.h>
+#include <ldap.h>
+#include "ldap.h"
/** Converts "bad" strings into ones which are safe for LDAP
*
goto process_error;
}
- process_error:
-
+process_error:
if ((lib_errno == LDAP_SUCCESS) && (srv_errno != LDAP_SUCCESS)) {
lib_errno = srv_errno;
} else if ((lib_errno != LDAP_SUCCESS) && (srv_errno == LDAP_SUCCESS)) {
ldap_memfree(part_dn);
}
- if (our_err) {
- talloc_free(our_err);
- }
+ talloc_free(our_err);
if ((lib_errno || srv_errno) && *result) {
ldap_msgfree(*result);
* @return one of the LDAP_PROC_* values.
*/
ldap_rcode_t rlm_ldap_bind(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn,
- char const *password, int retry)
+ char const *password, bool retry)
{
- ldap_rcode_t status;
+ ldap_rcode_t status = LDAP_PROC_ERROR;
int msgid;
char const *error = NULL;
char *extra = NULL;
+ int i, num;
+
rad_assert(*pconn && (*pconn)->handle);
+ rad_assert(!retry || inst->pool);
/*
* Bind as anonymous user
*/
if (!dn) dn = "";
-retry:
- 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->xlat_name);
+ /*
+ * For sanity, for when no connections are viable,
+ * and we can't make a new one.
+ */
+ num = retry ? fr_connection_get_num(inst->pool) : 0;
+ for (i = num; i >= 0; i--) {
+ 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->xlat_name);
+ }
}
- }
- status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
- switch (status) {
- case LDAP_PROC_SUCCESS:
- LDAP_DBG_REQ("Bind successful");
- break;
- case LDAP_PROC_NOT_PERMITTED:
- LDAP_ERR_REQ("Bind was not permitted: %s", error);
- LDAP_EXT_REQ();
+ status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
+ switch (status) {
+ case LDAP_PROC_SUCCESS:
+ LDAP_DBG_REQ("Bind successful");
+ break;
- break;
+ case LDAP_PROC_NOT_PERMITTED:
+ LDAP_ERR_REQ("Bind was not permitted: %s", error);
+ LDAP_EXT_REQ();
- case LDAP_PROC_REJECT:
- LDAP_ERR_REQ("Bind credentials incorrect: %s", error);
- LDAP_EXT_REQ();
+ break;
- break;
+ case LDAP_PROC_REJECT:
+ LDAP_ERR_REQ("Bind credentials incorrect: %s", error);
+ LDAP_EXT_REQ();
- case LDAP_PROC_RETRY:
- if (retry) {
- *pconn = fr_connection_reconnect(inst->pool, *pconn);
- if (*pconn) {
- LDAP_DBGW_REQ("Bind with %s to %s:%d failed: %s. Got new socket, retrying...",
- dn, inst->server, inst->port, error);
+ break;
- talloc_free(extra); /* don't leak debug info */
+ case LDAP_PROC_RETRY:
+ if (retry) {
+ *pconn = fr_connection_reconnect(inst->pool, *pconn);
+ if (*pconn) {
+ LDAP_DBGW_REQ("Bind with %s to %s:%d failed: %s. Got new socket, retrying...",
+ dn, inst->server, inst->port, error);
- goto retry;
- }
- };
+ talloc_free(extra); /* don't leak debug info */
- status = LDAP_PROC_ERROR;
+ continue;
+ }
+ };
+ status = LDAP_PROC_ERROR;
- /*
- * Were not allowed to retry, or there are no more
- * sockets, treat this as a hard failure.
- */
- /* FALL-THROUGH */
- default:
+ /*
+ * Were not allowed to retry, or there are no more
+ * sockets, treat this as a hard failure.
+ */
+ /* FALL-THROUGH */
+ default:
#ifdef HAVE_LDAP_INITIALIZE
- if (inst->is_url) {
- LDAP_ERR_REQ("Bind with %s to %s failed: %s", dn, inst->server, error);
- } else
+ if (inst->is_url) {
+ LDAP_ERR_REQ("Bind with %s to %s failed: %s", dn, inst->server, error);
+ } else
#endif
- {
- LDAP_ERR_REQ("Bind with %s to %s:%d failed: %s", dn, inst->server,
- inst->port, error);
+ {
+ LDAP_ERR_REQ("Bind with %s to %s:%d failed: %s", dn, inst->server,
+ inst->port, error);
+ }
+ LDAP_EXT_REQ();
+
+ break;
}
- LDAP_EXT_REQ();
break;
}
- if (extra) {
- talloc_free(extra);
+ if (retry && (i < 0)) {
+ LDAP_ERR_REQ("Hit reconnection limit");
+ status = LDAP_PROC_ERROR;
}
+ talloc_free(extra);
+
return status; /* caller closes the connection */
}
-
/** Search for something in the LDAP directory
*
* Binds as the administrative user and performs a search, dealing with any errors.
char const *error = NULL;
char *extra = NULL;
+ int i;
+
+
rad_assert(*pconn && (*pconn)->handle);
/*
*/
memset(&tv, 0, sizeof(tv));
tv.tv_sec = inst->res_timeout;
-retry:
- (void) ldap_search_ext((*pconn)->handle, dn, scope, filter, search_attrs, 0, NULL, NULL, &tv, 0, &msgid);
- LDAP_DBG_REQ("Waiting for search result...");
- status = rlm_ldap_result(inst, *pconn, msgid, dn, &our_result, &error, &extra);
- switch (status) {
+ /*
+ * For sanity, for when no connections are viable,
+ * and we can't make a new one.
+ */
+ for (i = fr_connection_get_num(inst->pool); i >= 0; i--) {
+ (void) ldap_search_ext((*pconn)->handle, dn, scope, filter, search_attrs,
+ 0, NULL, NULL, &tv, 0, &msgid);
+
+ LDAP_DBG_REQ("Waiting for search result...");
+ status = rlm_ldap_result(inst, *pconn, msgid, dn, &our_result, &error, &extra);
+ switch (status) {
case LDAP_PROC_SUCCESS:
break;
+
case LDAP_PROC_RETRY:
*pconn = fr_connection_reconnect(inst->pool, *pconn);
if (*pconn) {
talloc_free(extra); /* don't leak debug info */
- goto retry;
+ continue;
}
status = LDAP_PROC_ERROR;
if (extra) LDAP_ERR_REQ("%s", extra);
goto finish;
+ }
+
+ break;
+ }
+
+ if (i < 0) {
+ LDAP_ERR_REQ("Hit reconnection limit");
+ status = LDAP_PROC_ERROR;
+
+ goto finish;
}
count = ldap_count_entries((*pconn)->handle, our_result);
our_result = NULL;
}
- finish:
- if (extra) {
- talloc_free(extra);
- }
+finish:
+ talloc_free(extra);
/*
* We always need to get the result to count entries, but the caller
char const *error = NULL;
char *extra = NULL;
+ int i;
+
rad_assert(*pconn && (*pconn)->handle);
/*
(*pconn)->rebound = false;
}
- RDEBUG2("Modifying object with DN \"%s\"", dn);
- retry:
- (void) ldap_modify_ext((*pconn)->handle, dn, mods, NULL, NULL, &msgid);
+ /*
+ * For sanity, for when no connections are viable,
+ * and we can't make a new one.
+ */
+ for (i = fr_connection_get_num(inst->pool); i >= 0; i--) {
+ RDEBUG2("Modifying object with DN \"%s\"", dn);
+ (void) ldap_modify_ext((*pconn)->handle, dn, mods, NULL, NULL, &msgid);
+
+ RDEBUG2("Waiting for modify result...");
+ status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
+ switch (status) {
+ case LDAP_PROC_SUCCESS:
+ break;
- RDEBUG2("Waiting for modify result...");
- status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
- switch (status) {
- case LDAP_PROC_SUCCESS:
- break;
- case LDAP_PROC_RETRY:
- *pconn = fr_connection_reconnect(inst->pool, *pconn);
- if (*pconn) {
- RWDEBUG("Modify failed: %s. Got new socket, retrying...", error);
+ case LDAP_PROC_RETRY:
+ *pconn = fr_connection_reconnect(inst->pool, *pconn);
+ if (*pconn) {
+ RWDEBUG("Modify failed: %s. Got new socket, retrying...", error);
- talloc_free(extra); /* don't leak debug info */
+ talloc_free(extra); /* don't leak debug info */
- goto retry;
- }
+ continue;
+ }
- status = LDAP_PROC_ERROR;
+ status = LDAP_PROC_ERROR;
- /* FALL-THROUGH */
- default:
- REDEBUG("Failed modifying object: %s", error);
- REDEBUG("%s", extra);
+ /* FALL-THROUGH */
+ default:
+ REDEBUG("Failed modifying object: %s", error);
+ REDEBUG("%s", extra);
- goto finish;
+ goto finish;
+ }
+
+ break;
}
- finish:
- if (extra) {
- talloc_free(extra);
+ if (i < 0) {
+ LDAP_ERR_REQ("Hit reconnection limit");
+ status = LDAP_PROC_ERROR;
}
+finish:
+ talloc_free(extra);
+
return status;
}
if (radius_xlat(filter, sizeof(filter), request, inst->userobj_filter, rlm_ldap_escape_func, NULL) < 0) {
REDEBUG("Unable to create filter");
-
*rcode = RLM_MODULE_INVALID;
+
return NULL;
}
if (radius_xlat(base_dn, sizeof(base_dn), request, inst->userobj_base_dn, rlm_ldap_escape_func, NULL) < 0) {
REDEBUG("Unable to create base_dn");
-
*rcode = RLM_MODULE_INVALID;
+
return NULL;
}
switch (status) {
case LDAP_PROC_SUCCESS:
break;
+
case LDAP_PROC_NO_RESULT:
*rcode = RLM_MODULE_NOTFOUND;
return NULL;
+
default:
*rcode = RLM_MODULE_FAIL;
return NULL;
}
#endif
+/** Close and delete a connection
+ *
+ * Unbinds the LDAP connection, informing the server and freeing any memory, then releases the memory used by the
+ * connection handle.
+ *
+ * @param conn to destroy.
+ * @return always indicates success.
+ */
+static int _mod_conn_free(ldap_handle_t *conn)
+{
+ DEBUG3("rlm_ldap: Closing libldap handle %p", conn->handle);
+
+ if (conn->handle) ldap_unbind_s(conn->handle);
+
+ return 0;
+}
+
/** Create and return a new connection
*
* Create a new ldap connection and allocate memory for a new rlm_handle_t
*
+ * @param ctx to allocate connection handle memory in.
* @param instance rlm_ldap instance.
* @return A new connection handle or NULL on error.
*/
-void *mod_conn_create(void *instance)
+void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
{
ldap_rcode_t status;
/*
* Allocate memory for the handle.
*/
- conn = talloc_zero(instance, ldap_handle_t);
+ conn = talloc_zero(ctx, ldap_handle_t);
if (!conn) return NULL;
+ talloc_set_destructor(conn, _mod_conn_free);
conn->inst = inst;
conn->rebound = false;
goto error;
}
}
+ DEBUG3("rlm_ldap: New libldap handle %p", conn->handle);
/*
* We now have a connection structure, but no actual TCP connection.
}
/*
+ * Leave "dereference" unset to use the OpenLDAP default.
+ */
+ if (inst->dereference_str) {
+ do_ldap_option(LDAP_OPT_DEREF, "dereference", &(inst->dereference));
+ }
+
+ /*
* Leave "chase_referrals" unset to use the OpenLDAP default.
*/
if (!inst->chase_referrals_unset) {
}
}
- memset(&tv, 0, sizeof(tv));
- tv.tv_sec = inst->net_timeout;
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
+ if (inst->net_timeout) {
+ memset(&tv, 0, sizeof(tv));
+ tv.tv_sec = inst->net_timeout;
- do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout", &tv);
+ do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout", &tv);
+ }
+#endif
do_ldap_option(LDAP_OPT_TIMELIMIT, "srv_timelimit", &(inst->srv_timelimit));
return conn;
- error:
- if (conn->handle) ldap_unbind_s(conn->handle);
+error:
talloc_free(conn);
return NULL;
}
-
-/** Close and delete a connection
- *
- * Unbinds the LDAP connection, informing the server and freeing any memory, then releases the memory used by the
- * connection handle.
- *
- * @param instance rlm_ldap instance.
- * @param handle to destroy.
- * @return always indicates success.
- */
-int mod_conn_delete(UNUSED void *instance, void *handle)
-{
- ldap_handle_t *conn = handle;
-
- ldap_unbind_s(conn->handle);
- talloc_free(conn);
-
- return 0;
-}
-
-
/** Gets an LDAP socket from the connection pool
*
* Retrieve a socket from the connection pool, or NULL on error (of if no sockets are available).
return fr_connection_get(inst->pool);
}
-
/** Frees an LDAP socket back to the connection pool
*
* If the socket was rebound chasing a referral onto another server then we destroy it.