* @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
*
char *srv_err = NULL; // Server's extended error message.
char *p, *a;
- int freeit = false; // Whether the message should be freed after being processed.
+ bool freeit = false; // Whether the message should be freed after being processed.
int len;
struct timeval tv; // Holds timeout values.
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)) {
len = rlm_ldap_common_dn(dn, part_dn);
if (len < 0) break;
- our_err = talloc_asprintf(conn, "Match stopped here: [%.*s]%s", len, dn, part_dn ? part_dn : "");
+ our_err = talloc_typed_asprintf(conn, "Match stopped here: [%.*s]%s", len, dn, part_dn ? part_dn : "");
goto error_string;
if (lib_errno != srv_errno) {
a = talloc_asprintf_append(p, "LDAP lib error: %s (%u), srv error: %s (%u). ",
- ldap_err2string(lib_errno), lib_errno,
+ ldap_err2string(lib_errno), lib_errno,
ldap_err2string(srv_errno), srv_errno);
if (!a) {
talloc_free(p);
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);
/*
(*pconn)->rebound = false;
}
- LDAP_DBG_REQ("Performing search in '%s' with filter '%s'", dn, filter);
-
+ if (filter) {
+ LDAP_DBG_REQ("Performing search in '%s' with filter '%s', scope '%s'", dn, filter,
+ fr_int2str(ldap_scope, scope, "<INVALID>"));
+ } else {
+ LDAP_DBG_REQ("Performing unfiltered search in '%s', scope '%s'", dn,
+ fr_int2str(ldap_scope, scope, "<INVALID>"));
+ }
/*
* If LDAP search produced an error it should also be logged
* to the ld. result should pick it up without us
*/
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;
}
char filter[LDAP_MAX_FILTER_STR_LEN];
char base_dn[LDAP_MAX_DN_STR_LEN];
- int freeit = false; //!< Whether the message should
+ bool freeit = false; //!< Whether the message should
//!< be freed after being processed.
*rcode = RLM_MODULE_FAIL;
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;
vals = ldap_get_values(conn->handle, entry, inst->userobj_access_attr);
if (vals) {
if (inst->access_positive) {
- if (strncmp(vals[0], "false", 5) == 0) {
- RDEBUG("\"%s\" attribute exists but is set to 'false' - user locked out");
+ if (strncasecmp(vals[0], "false", 5) == 0) {
+ RDEBUG("\"%s\" attribute exists but is set to 'false' - user locked out",
+ inst->userobj_access_attr);
rcode = RLM_MODULE_USERLOCK;
}
/* RLM_MODULE_OK set above... */
- } else {
+ } else if (strncasecmp(vals[0], "false", 5) != 0) {
RDEBUG("\"%s\" attribute exists - user locked out", inst->userobj_access_attr);
rcode = RLM_MODULE_USERLOCK;
}
!pairfind(request->config_items, PW_USER_PASSWORD, 0, TAG_ANY) &&
!pairfind(request->config_items, PW_PASSWORD_WITH_HEADER, 0, TAG_ANY) &&
!pairfind(request->config_items, PW_CRYPT_PASSWORD, 0, TAG_ANY)) {
- RWDEBUG("No \"reference\" password added. Ensure the admin user has permission to "
+ RWDEBUG("No \"known good\" password added. Ensure the admin user has permission to "
"read the password attribute");
RWDEBUG("PAP authentication will *NOT* work with Active Directory (if that is what you "
"were trying to configure)");
}
#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.
LDAP_ERR("Could not set %s: %s", _name, ldap_err2string(ldap_errno)); \
}
+#define do_ldap_global_option(_option, _name, _value) \
+ if (ldap_set_option(NULL, _option, _value) != LDAP_OPT_SUCCESS) { \
+ ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno); \
+ LDAP_ERR("Could not set %s: %s", _name, ldap_err2string(ldap_errno)); \
+ }
+
+
if (inst->ldap_debug) {
- do_ldap_option(LDAP_OPT_DEBUG_LEVEL, "ldap_debug", &(inst->ldap_debug));
+ do_ldap_global_option(LDAP_OPT_DEBUG_LEVEL, "ldap_debug", &(inst->ldap_debug));
+ }
+
+ /*
+ * Leave "dereference" unset to use the OpenLDAP default.
+ */
+ if (inst->dereference_str) {
+ do_ldap_option(LDAP_OPT_DEREF, "dereference", &(inst->dereference));
}
/*
}
}
- 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));
/*
* Set all of the TLS options
*/
-
-# ifdef LDAP_OPT_X_TLS_NEWCTX
- {
- /* Always use the new TLS configuration context */
- int is_server = 0;
- do_ldap_option(LDAP_OPT_X_TLS_NEWCTX, "new TLS context", &is_server);
-
- }
-# endif
-
if (inst->tls_mode) {
do_ldap_option(LDAP_OPT_X_TLS, "tls_mode", &(inst->tls_mode));
}
# endif
/*
+ * Counter intuitively the TLS context appears to need to be initialised
+ * after all the TLS options are set on the handle.
+ */
+# ifdef LDAP_OPT_X_TLS_NEWCTX
+ {
+ /* Always use the new TLS configuration context */
+ int is_server = 0;
+ do_ldap_option(LDAP_OPT_X_TLS_NEWCTX, "new TLS context", &is_server);
+
+ }
+# endif
+
+ /*
* And finally start the TLS code.
*/
if (inst->start_tls) {
if (inst->port == 636) {
- WDEBUG("Told to Start TLS on LDAPS port this will probably fail, please correct the "
+ WARN("Told to Start TLS on LDAPS port this will probably fail, please correct the "
"configuration");
}
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).
* @param inst rlm_ldap configuration.
* @param request Current request (may be NULL).
*/
-ldap_handle_t *rlm_ldap_get_socket(ldap_instance_t const *inst, REQUEST *request)
+ldap_handle_t *rlm_ldap_get_socket(ldap_instance_t const *inst, UNUSED REQUEST *request)
{
- ldap_handle_t *conn;
-
- conn = fr_connection_get(inst->pool);
- if (!conn) {
- REDEBUG("All ldap connections are in use");
-
- return NULL;
- }
-
- return conn;
+ 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.