Pass a threadsafe ctx into fr_connection_pool create callback
[freeradius.git] / src / modules / rlm_ldap / ldap.c
index dd6c22e..6fe2473 100644 (file)
@@ -12,7 +12,7 @@
  *   along with this program; if not, write to the Free Software
  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
+
 /**
  * $Id$
  * @file ldap.c
  * @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
  *
  * @param in Raw unescaped string.
  * @param arg Any additional arguments (unused).
  */
-size_t rlm_ldap_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, const char *in, UNUSED void *arg)
+size_t rlm_ldap_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
 {
-       static const char encode[] = ",+\"\\<>;*=()";
-       static const char hextab[] = "0123456789abcdef";
+       static char const encode[] = ",+\"\\<>;*=()";
+       static char const hextab[] = "0123456789abcdef";
        size_t left = outlen;
-       
+
        if (*in && ((*in == ' ') || (*in == '#'))) {
                goto encode;
        }
-       
+
        while (*in) {
                /*
                 *      Encode unsafe characters.
@@ -87,9 +85,9 @@ size_t rlm_ldap_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, c
                *out++ = *in++;
                left--;
        }
-       
+
        *out = '\0';
-       
+
        return outlen - left;
 }
 
@@ -98,45 +96,45 @@ size_t rlm_ldap_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, c
  * @param str to check.
  * @return true if string is a DN, else false.
  */
-int rlm_ldap_is_dn(const char *str)
+int rlm_ldap_is_dn(char const *str)
 {
-       return strrchr(str, ',') == NULL ? FALSE : TRUE;
+       return strrchr(str, ',') == NULL ? false : true;
 }
 
 /** Find the place at which the two DN strings diverge
- * 
+ *
  * Returns the length of the non matching string in full.
  *
  * @param full DN.
  * @param part Partial DN as returned by ldap_parse_result.
  * @return the length of the portion of full which wasn't matched or -1 on error.
  */
-static size_t rlm_ldap_common_dn(const char *full, const char *part)
+static size_t rlm_ldap_common_dn(char const *full, char const *part)
 {
        size_t f_len, p_len, i;
-       
+
        if (!full) {
                return -1;
        }
-       
+
        f_len = strlen(full);
-       
+
        if (!part) {
                return -1;
        }
-       
+
        p_len = strlen(part);
        if (!p_len) {
                return f_len;
        }
-       
+
        if ((f_len < p_len) || !f_len) {
-               return -1; 
+               return -1;
        }
 
        for (i = 0; i < p_len; i++) {
                if (part[p_len - i] != full[f_len - i]) {
-                       return -1; 
+                       return -1;
                }
        }
 
@@ -152,17 +150,17 @@ static size_t rlm_ldap_common_dn(const char *full, const char *part)
  * @param sublen Number of potential subfilters in array.
  * @return length of expanded data.
  */
-ssize_t rlm_ldap_xlat_filter(REQUEST *request, const char **sub, size_t sublen, char *out, size_t outlen)
+ssize_t rlm_ldap_xlat_filter(REQUEST *request, char const **sub, size_t sublen, char *out, size_t outlen)
 {
        char buffer[LDAP_MAX_FILTER_STR_LEN + 1];
-       const char *in;
+       char const *in = NULL;
        char *p = buffer;
-       
-       size_t len = 0;
-       
+
+       ssize_t len = 0;
+
        unsigned int i;
        int cnt = 0;
-       
+
        /*
         *      Figure out how many filter elements we need to integrate
         */
@@ -170,7 +168,7 @@ ssize_t rlm_ldap_xlat_filter(REQUEST *request, const char **sub, size_t sublen,
                if (sub[i] && *sub[i]) {
                        in = sub[i];
                        cnt++;
-               } 
+               }
        }
 
        if (!cnt) {
@@ -182,49 +180,65 @@ ssize_t rlm_ldap_xlat_filter(REQUEST *request, const char **sub, size_t sublen,
                if (outlen < 3) {
                        goto oob;
                }
-               
+
                p[len++] = '(';
                p[len++] = '&';
-               
+
                for (i = 0; i < sublen; i++) {
                        if (sub[i] && (*sub[i] != '\0')) {
                                len += strlcpy(p + len, sub[i], outlen - len);
-                               
-                               if (len >= outlen) {
+
+                               if ((size_t) len >= outlen) {
                                        oob:
-                                       RDEBUGE("Out of buffer space creating filter");
-                                       
+                                       REDEBUG("Out of buffer space creating filter");
+
                                        return -1;
                                }
-                       } 
+                       }
                }
-               
+
                if ((outlen - len) < 2) {
                        goto oob;
                }
-               
+
                p[len++] = ')';
                p[len] = '\0';
-               
+
                in = buffer;
        }
 
-       len = radius_xlat(out, outlen, in, request, rlm_ldap_escape_func, NULL);
-       if (!len) {
-               RDEBUGE("Failed creating filter");
-               
+       len = radius_xlat(out, outlen, request, in, rlm_ldap_escape_func, NULL);
+       if (len < 0) {
+               REDEBUG("Failed creating filter");
+
                return -1;
        }
-       
+
        return len;
 }
 
+/** Return the error string associated with a handle
+ *
+ * @param conn to retrieve error from.
+ * @return error string.
+ */
+char const *rlm_ldap_error_str(ldap_handle_t const *conn)
+{
+       int lib_errno;
+       ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER, &lib_errno);
+       if (lib_errno == LDAP_SUCCESS) {
+               return "unknown";
+       }
+
+       return ldap_err2string(lib_errno);
+}
+
 /** Parse response from LDAP server dealing with any errors
  *
- * Should be called after an LDAP operation. Will check result of operation and if it was successful, then attempt 
+ * Should be called after an LDAP operation. Will check result of operation and if it was successful, then attempt
  * to retrieve and parse the result.
  *
- * Will also produce extended error output including any messages the server sent, and information about partial 
+ * Will also produce extended error output including any messages the server sent, and information about partial
  * DN matches.
  *
  * @param[in] inst of LDAP module.
@@ -233,51 +247,51 @@ ssize_t rlm_ldap_xlat_filter(REQUEST *request, const char **sub, size_t sublen,
  * @param[in] dn Last search or bind DN.
  * @param[out] result Where to write result, if NULL result will be freed.
  * @param[out] error Where to write the error string, may be NULL, must not be freed.
- * @param[out] extra Where to write additional error string to, may be NULL (faster) or must be freed 
+ * @param[out] extra Where to write additional error string to, may be NULL (faster) or must be freed
  *     (with talloc_free).
  * @return One of the LDAP_PROC_* codes.
  */
-static ldap_rcode_t rlm_ldap_result(const ldap_instance_t *inst, const ldap_handle_t *conn, int msgid, const char *dn,
-                                   LDAPMessage **result, const char **error, char **extra)
+static ldap_rcode_t rlm_ldap_result(ldap_instance_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;
 
        int lib_errno = LDAP_SUCCESS;   // errno returned by the library.
        int srv_errno = LDAP_SUCCESS;   // errno in the result message.
-       
+
        char *part_dn = NULL;           // Partial DN match.
        char *our_err = NULL;           // Our extended error message.
        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.
-       
+
        LDAPMessage *tmp_msg;           // Temporary message pointer storage if we weren't provided with one.
-       
-       const char *tmp_err;            // Temporary error pointer storage if we weren't provided with one.
-       
+
+       char const *tmp_err;            // Temporary error pointer storage if we weren't provided with one.
+
        if (!error) {
                error = &tmp_err;
        }
        *error = NULL;
-       
+
        if (extra) {
                *extra = NULL;
        }
-       
+
        /*
         *      We always need the result, but our caller may not
         */
        if (!result) {
                result = &tmp_msg;
-               freeit = TRUE;
+               freeit = true;
        }
-       
+
        *result = NULL;
-       
+
        /*
         *      Check if there was an error sending the request
         */
@@ -286,10 +300,10 @@ static ldap_rcode_t rlm_ldap_result(const ldap_instance_t *inst, const ldap_hand
        if (lib_errno != LDAP_SUCCESS) {
                goto process_error;
        }
-       
+
        memset(&tv, 0, sizeof(tv));
        tv.tv_sec = inst->res_timeout;
-       
+
        /*
         *      Now retrieve the result and check for errors
         *      ldap_result returns -1 on error, and 0 on timeout
@@ -297,16 +311,16 @@ static ldap_rcode_t rlm_ldap_result(const ldap_instance_t *inst, const ldap_hand
        lib_errno = ldap_result(conn->handle, msgid, 1, &tv, result);
        if (lib_errno == 0) {
                lib_errno = LDAP_TIMEOUT;
-               
+
                goto process_error;
        }
-       
+
        if (lib_errno == -1) {
                ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
                                &lib_errno);
                goto process_error;
        }
-       
+
        /*
         *      Parse the result and check for errors sent by the server
         */
@@ -318,73 +332,72 @@ static ldap_rcode_t rlm_ldap_result(const ldap_instance_t *inst, const ldap_hand
        if (freeit) {
                *result = NULL;
        }
-       
+
        if (lib_errno != LDAP_SUCCESS) {
                ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
                                &lib_errno);
                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)) {
                srv_errno = lib_errno;
        }
-       
+
        switch (lib_errno) {
        case LDAP_SUCCESS:
                *error = "Success";
-               
+
                break;
 
        case LDAP_NO_SUCH_OBJECT:
                *error = "The specified DN wasn't found, check base_dn and identity";
-               
+
                status = LDAP_PROC_BAD_DN;
-               
+
                if (!extra) break;
-               
-               /* 
+
+               /*
                 *      Build our own internal diagnostic string
                 */
                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;
 
        case LDAP_INSUFFICIENT_ACCESS:
                *error = "Insufficient access. Check the identity and password configuration directives";
-               
+
                status = LDAP_PROC_NOT_PERMITTED;
                break;
-               
+
        case LDAP_UNWILLING_TO_PERFORM:
                *error = "Server was unwilling to perform";
-       
+
                status = LDAP_PROC_NOT_PERMITTED;
                break;
-               
+
        case LDAP_TIMEOUT:
-               exec_trigger(NULL, inst->cs, "modules.ldap.timeout", TRUE);
-               
+               exec_trigger(NULL, inst->cs, "modules.ldap.timeout", true);
+
                *error = "Timed out while waiting for server to respond";
-                      
+
                status = LDAP_PROC_ERROR;
                break;
-               
+
        case LDAP_FILTER_ERROR:
                *error = "Bad search filter";
 
                status = LDAP_PROC_ERROR;
                break;
-               
+
        case LDAP_TIMELIMIT_EXCEEDED:
-               exec_trigger(NULL, inst->cs, "modules.ldap.timeout", TRUE);
-               
+               exec_trigger(NULL, inst->cs, "modules.ldap.timeout", true);
+
                *error = "Time limit exceeded";
                /* FALL-THROUGH */
 
@@ -392,33 +405,33 @@ static ldap_rcode_t rlm_ldap_result(const ldap_instance_t *inst, const ldap_hand
        case LDAP_UNAVAILABLE:
        case LDAP_SERVER_DOWN:
                status = LDAP_PROC_RETRY;
-               
+
                goto error_string;
-               
+
        case LDAP_INVALID_CREDENTIALS:
        case LDAP_CONSTRAINT_VIOLATION:
                status = LDAP_PROC_REJECT;
-               
+
                goto error_string;
-               
+
        case LDAP_OPERATIONS_ERROR:
                *error = "Please set 'chase_referrals=yes' and 'rebind=yes'. See the ldap module configuration "
                         "for details.";
-                        
+
                /* FALL-THROUGH */
        default:
                status = LDAP_PROC_ERROR;
-               
+
                error_string:
-               
+
                if (!*error) {
                        *error = ldap_err2string(lib_errno);
                }
-               
+
                if (!extra || ((lib_errno == srv_errno) && !our_err && !srv_err)) {
                        break;
                }
-               
+
                /*
                 *      Output the error codes from the library and server
                 */
@@ -426,14 +439,14 @@ static ldap_rcode_t rlm_ldap_result(const ldap_instance_t *inst, const ldap_hand
                if (!p) break;
 
                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,
+                       a = talloc_asprintf_append(p, "LDAP lib error: %s (%u), srv error: %s (%u). ",
+                                                  ldap_err2string(lib_errno), lib_errno,
                                                   ldap_err2string(srv_errno), srv_errno);
                        if (!a) {
                                talloc_free(p);
                                break;
                        }
-                       
+
                        p = a;
                }
 
@@ -443,45 +456,43 @@ static ldap_rcode_t rlm_ldap_result(const ldap_instance_t *inst, const ldap_hand
                                talloc_free(p);
                                break;
                        }
-                       
+
                        p = a;
                }
-               
+
                if (srv_err) {
                        a = talloc_asprintf_append_buffer(p, "Server said: %s. ", srv_err);
                        if (!a) {
                                talloc_free(p);
                                break;
                        }
-                       
+
                        p = a;
                }
-               
+
                *extra = p;
-               
+
                break;
        }
-       
+
        /*
         *      Cleanup memory
         */
        if (srv_err) {
                ldap_memfree(srv_err);
        }
-       
+
        if (part_dn) {
                ldap_memfree(part_dn);
        }
-       
-       if (our_err) {
-               talloc_free(our_err);
-       }
-       
+
+       talloc_free(our_err);
+
        if ((lib_errno || srv_errno) && *result) {
                ldap_msgfree(*result);
                *result = NULL;
        }
-       
+
        return status;
 }
 
@@ -497,94 +508,107 @@ static ldap_rcode_t rlm_ldap_result(const ldap_instance_t *inst, const ldap_hand
  * @param[in] retry if the server is down.
  * @return one of the LDAP_PROC_* values.
  */
-ldap_rcode_t rlm_ldap_bind(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn, const char *dn,
-                          const char *password, int retry)
+ldap_rcode_t rlm_ldap_bind(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn,
+                          char const *password, bool retry)
 {
-       ldap_rcode_t    status;
-       
+       ldap_rcode_t    status = LDAP_PROC_ERROR;
+
        int             msgid;
-       
-       const char      *error = NULL;
+
+       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();
-               
-               break;
+               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_REJECT:
-               LDAP_ERR_REQ("Bind credentials incorrect: %s", error);
-               LDAP_EXT_REQ();
+               case LDAP_PROC_NOT_PERMITTED:
+                       LDAP_ERR_REQ("Bind was not permitted: %s", error);
+                       LDAP_EXT_REQ();
 
-               break;
+                       break;
 
-       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);
-                               
-                               talloc_free(extra); /* don't leak debug info */
-                               
-                               goto retry;
-                       }
-               };
-               
-               status = LDAP_PROC_ERROR;
-               
-               /*
-                *      Were not allowed to retry, or there are no more
-                *      sockets, treat this as a hard failure.
-                */
-               /* FALL-THROUGH */
-       default:
+               case LDAP_PROC_REJECT:
+                       LDAP_ERR_REQ("Bind credentials incorrect: %s", error);
+                       LDAP_EXT_REQ();
+
+                       break;
+
+               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);
+
+                                       talloc_free(extra); /* don't leak debug info */
+
+                                       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:
 #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.
@@ -600,24 +624,28 @@ retry:
  *     May be NULL in which case result will be automatically freed after use.
  * @return One of the LDAP_PROC_* values.
  */
-ldap_rcode_t rlm_ldap_search(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
-                            const char *dn, int scope, const char *filter, const char * const *attrs,
+ldap_rcode_t rlm_ldap_search(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+                            char const *dn, int scope, char const *filter, char const * const *attrs,
                             LDAPMessage **result)
 {
        ldap_rcode_t    status;
-       
+       LDAPMessage     *our_result = NULL;
+
        int             msgid;          // Message id returned by
                                        // ldap_search_ext.
-                               
+
        int             count = 0;      // Number of results we got.
-       
+
        struct timeval  tv;             // Holds timeout values.
-       
-       const char      *error = NULL;
+
+       char const      *error = NULL;
        char            *extra = NULL;
 
+       int             i;
+
+
        rad_assert(*pconn && (*pconn)->handle);
-       
+
        /*
         *      OpenLDAP library doesn't declare attrs array as const, but
         *      it really should be *sigh*.
@@ -629,18 +657,23 @@ ldap_rcode_t rlm_ldap_search(const ldap_instance_t *inst, REQUEST *request, ldap
         *      Do all searches as the admin user.
         */
        if ((*pconn)->rebound) {
-               status = rlm_ldap_bind(inst, request, pconn, inst->admin_dn, inst->password, TRUE);
+               status = rlm_ldap_bind(inst, request, pconn, inst->admin_dn, inst->password, true);
                if (status != LDAP_PROC_SUCCESS) {
                        return LDAP_PROC_ERROR;
                }
 
                rad_assert(*pconn);
-               
-               (*pconn)->rebound = FALSE;
-       }
 
-       RDEBUG2("Performing search in '%s' with filter '%s'", dn, filter);
+               (*pconn)->rebound = false;
+       }
 
+       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
@@ -648,51 +681,82 @@ ldap_rcode_t rlm_ldap_search(const ldap_instance_t *inst, REQUEST *request, ldap
         */
        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);
 
-       RDEBUG2("Waiting for search result...");               
-       status = rlm_ldap_result(inst, *pconn, msgid, dn, 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) {
-                               RDEBUGW("Search failed: %s. Got new socket, retrying...", error);
-                               
+                               LDAP_DBGW_REQ("Search failed: %s. Got new socket, retrying...", error);
+
                                talloc_free(extra); /* don't leak debug info */
-                               
-                               goto retry;
+
+                               continue;
                        }
-                       
+
                        status = LDAP_PROC_ERROR;
-                       
+
                        /* FALL-THROUGH */
                default:
-                       RDEBUGE("Failed performing search: %s", error);
-                       if (extra) RDEBUGE("%s", extra);
+                       LDAP_ERR_REQ("Failed performing search: %s", error);
+                       if (extra) LDAP_ERR_REQ("%s", extra);
 
                        goto finish;
-       }
-       
-       if (result) {   
-               count = ldap_count_entries((*pconn)->handle, *result);
-               if (count == 0) {
-                       ldap_msgfree(*result);
-                       *result = NULL;
-               
-                       RDEBUG("Search returned no results");
-               
-                       status = LDAP_PROC_NO_RESULT;
                }
+
+               break;
        }
-       
-       finish:
-       if (extra) {
-               talloc_free(extra);
+
+       if (i < 0) {
+               LDAP_ERR_REQ("Hit reconnection limit");
+               status = LDAP_PROC_ERROR;
+
+               goto finish;
+       }
+
+       count = ldap_count_entries((*pconn)->handle, our_result);
+       if (count < 0) {
+               LDAP_ERR_REQ("Error counting results: %s", rlm_ldap_error_str(*pconn));
+               status = LDAP_PROC_ERROR;
+
+               ldap_msgfree(our_result);
+               our_result = NULL;
+       } else if (count == 0) {
+               LDAP_DBG_REQ("Search returned no results");
+               status = LDAP_PROC_NO_RESULT;
+
+               ldap_msgfree(our_result);
+               our_result = NULL;
+       }
+
+finish:
+       talloc_free(extra);
+
+       /*
+        *      We always need to get the result to count entries, but the caller
+        *      may not of requested one. If that's the case, free it, else write
+        *      it to where our caller said.
+        */
+       if (!result) {
+               if (our_result) {
+                       ldap_msgfree(our_result);
+               }
+       } else {
+               *result = our_result;
        }
-       
+
        return status;
 }
 
@@ -707,66 +771,79 @@ retry:
  * @param[in] mods to make, see 'man ldap_modify' for more information.
  * @return One of the LDAP_PROC_* values.
  */
-ldap_rcode_t rlm_ldap_modify(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
-                            const char *dn, LDAPMod *mods[])
+ldap_rcode_t rlm_ldap_modify(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+                            char const *dn, LDAPMod *mods[])
 {
        ldap_rcode_t    status;
-       
+
        int             msgid;          // Message id returned by ldap_search_ext.
-       
-       const char      *error = NULL;
-       char            *extra = NULL;                     
+
+       char const      *error = NULL;
+       char            *extra = NULL;
+
+       int             i;
 
        rad_assert(*pconn && (*pconn)->handle);
-               
+
        /*
         *      Perform all modifications as the admin user.
         */
        if ((*pconn)->rebound) {
-               status = rlm_ldap_bind(inst, request, pconn, inst->admin_dn, inst->password, TRUE);
+               status = rlm_ldap_bind(inst, request, pconn, inst->admin_dn, inst->password, true);
                if (status != LDAP_PROC_SUCCESS) {
                        return LDAP_PROC_ERROR;
                }
 
                rad_assert(*pconn);
-               
-               (*pconn)->rebound = FALSE;
+
+               (*pconn)->rebound = false;
        }
-       
-       RDEBUG2("Modifying object with DN \"%s\"", dn);
-       retry:
-       (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;
-               case LDAP_PROC_RETRY:
-                       *pconn = fr_connection_reconnect(inst->pool, *pconn);
-                       if (*pconn) {
-                               RDEBUGW("Modify failed: %s. Got new socket, retrying...", error);
-                               
-                               talloc_free(extra); /* don't leak debug info */
-                               
-                               goto retry;
-                       }
-                       
-                       status = LDAP_PROC_ERROR;
-                       
-                       /* FALL-THROUGH */
-               default:
-                       RDEBUGE("Failed modifying object: %s", error);
-                       RDEBUGE("%s", extra);
-                       
-                       goto finish;
-       }                    
-       
-       finish:
-       if (extra) {
-               talloc_free(extra);
+
+       /*
+        *      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;
+
+                       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 */
+
+                                       continue;
+                               }
+
+                               status = LDAP_PROC_ERROR;
+
+                               /* FALL-THROUGH */
+                       default:
+                               REDEBUG("Failed modifying object: %s", error);
+                               REDEBUG("%s", extra);
+
+                               goto finish;
+               }
+
+               break;
+       }
+
+       if (i < 0) {
+               LDAP_ERR_REQ("Hit reconnection limit");
+               status = LDAP_PROC_ERROR;
        }
-       
+
+finish:
+       talloc_free(extra);
+
        return status;
 }
 
@@ -777,7 +854,7 @@ ldap_rcode_t rlm_ldap_modify(const ldap_instance_t *inst, REQUEST *request, ldap
  *
  * This potentially allows for all authorization and authentication checks to be performed in one ldap search
  * operation, which is a big bonus given the number of crappy, slow *cough*AD*cough* LDAP directory servers out there.
- * 
+ *
  * @param[in] inst rlm_ldap configuration.
  * @param[in] request Current request.
  * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
@@ -787,34 +864,34 @@ ldap_rcode_t rlm_ldap_modify(const ldap_instance_t *inst, REQUEST *request, ldap
  * @param[out] rcode The status of the operation, one of the RLM_MODULE_* codes.
  * @return The user's DN or NULL on error.
  */
-const char *rlm_ldap_find_user(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
-                              const char *attrs[], int force, LDAPMessage **result, rlm_rcode_t *rcode)
+char const *rlm_ldap_find_user(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+                              char const *attrs[], int force, LDAPMessage **result, rlm_rcode_t *rcode)
 {
-       static const char *tmp_attrs[] = { NULL };
-       
+       static char const *tmp_attrs[] = { NULL };
+
        ldap_rcode_t    status;
        VALUE_PAIR      *vp = NULL;
        LDAPMessage     *tmp_msg = NULL, *entry = NULL;
        int             ldap_errno;
        char            *dn = NULL;
-       char            filter[LDAP_MAX_FILTER_STR_LEN];        
+       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 (!result) {
                result = &tmp_msg;
-               freeit = TRUE;
+               freeit = true;
        }
        *result = NULL;
-       
+
        if (!attrs) {
                memset(&attrs, 0, sizeof(tmp_attrs));
        }
-       
+
        /*
         *      If the caller isn't looking for the result we can just return the current userdn value.
         */
@@ -826,33 +903,33 @@ const char *rlm_ldap_find_user(const ldap_instance_t *inst, REQUEST *request, ld
                        return vp->vp_strvalue;
                }
        }
-       
+
        /*
         *      Perform all searches as the admin user.
         */
        if ((*pconn)->rebound) {
-               status = rlm_ldap_bind(inst, request, pconn, inst->admin_dn, inst->password, TRUE);
+               status = rlm_ldap_bind(inst, request, pconn, inst->admin_dn, inst->password, true);
                if (status != LDAP_PROC_SUCCESS) {
                        *rcode = RLM_MODULE_FAIL;
                        return NULL;
                }
 
                rad_assert(*pconn);
-               
-               (*pconn)->rebound = FALSE;
+
+               (*pconn)->rebound = false;
        }
-       
-       if (!radius_xlat(filter, sizeof(filter), inst->userobj_filter, request, rlm_ldap_escape_func, NULL)) {
-               RDEBUGE("Unable to create filter");
-               
+
+       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), inst->userobj_base_dn, request, rlm_ldap_escape_func, NULL)) {
-               RDEBUGE("Unable to create base_dn");
-               
+       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;
        }
 
@@ -860,44 +937,46 @@ const char *rlm_ldap_find_user(const ldap_instance_t *inst, REQUEST *request, ld
        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;
        }
-       
+
        rad_assert(*pconn);
 
        entry = ldap_first_entry((*pconn)->handle, *result);
        if (!entry) {
                ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
-               RDEBUGE("Failed retrieving entry: %s", 
+               REDEBUG("Failed retrieving entry: %s",
                        ldap_err2string(ldap_errno));
-                        
+
                goto finish;
        }
 
        dn = ldap_get_dn((*pconn)->handle, entry);
        if (!dn) {
                ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
-                               
-               RDEBUGE("Retrieving object DN from entry failed: %s",
+
+               REDEBUG("Retrieving object DN from entry failed: %s",
                        ldap_err2string(ldap_errno));
-                      
+
                goto finish;
        }
-       
+
        RDEBUG("User object found at DN \"%s\"", dn);
        vp = pairmake(request, &request->config_items, "LDAP-UserDN", dn, T_OP_EQ);
-       if (vp) {       
+       if (vp) {
                *rcode = RLM_MODULE_OK;
        }
-       
+
        finish:
        ldap_memfree(dn);
-       
+
        if ((freeit || (*rcode != RLM_MODULE_OK)) && *result) {
                ldap_msgfree(*result);
                *result = NULL;
@@ -914,18 +993,22 @@ const char *rlm_ldap_find_user(const ldap_instance_t *inst, REQUEST *request, ld
  * @param[in] entry retrieved by rlm_ldap_find_user or rlm_ldap_search.
  * @return RLM_MODULE_USERLOCK if the user was denied access, else RLM_MODULE_OK.
  */
-rlm_rcode_t rlm_ldap_check_access(const ldap_instance_t *inst, REQUEST *request,
-                                 const ldap_handle_t *conn, LDAPMessage *entry)
+rlm_rcode_t rlm_ldap_check_access(ldap_instance_t const *inst, REQUEST *request,
+                                 ldap_handle_t const *conn, LDAPMessage *entry)
 {
        rlm_rcode_t rcode = RLM_MODULE_OK;
        char **vals = NULL;
 
        vals = ldap_get_values(conn->handle, entry, inst->userobj_access_attr);
        if (vals) {
-               if (inst->access_positive && (strncmp(vals[0], "FALSE", 5) == 0)) {
-                       RDEBUG("\"%s\" attribute exists but is set to 'false' - user locked out");
-                       rcode = RLM_MODULE_USERLOCK;
-               } else {
+               if (inst->access_positive) {
+                       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 if (strncasecmp(vals[0], "false", 5) != 0) {
                        RDEBUG("\"%s\" attribute exists - user locked out", inst->userobj_access_attr);
                        rcode = RLM_MODULE_USERLOCK;
                }
@@ -946,7 +1029,7 @@ rlm_rcode_t rlm_ldap_check_access(const ldap_instance_t *inst, REQUEST *request,
  * @param inst rlm_ldap configuration.
  * @param request Current request.
  */
-void rlm_ldap_check_reply(const ldap_instance_t *inst, REQUEST *request)
+void rlm_ldap_check_reply(ldap_instance_t const *inst, REQUEST *request)
 {
        /*
        *       More warning messages for people who can't be bothered to read the documentation.
@@ -960,9 +1043,9 @@ void rlm_ldap_check_reply(const ldap_instance_t *inst, REQUEST *request)
                    !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)) {
-                       RDEBUGW("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");
-                       RDEBUGW("PAP authentication will *NOT* work with Active Directory (if that is what you "
+                       RWDEBUG("PAP authentication will *NOT* work with Active Directory (if that is what you "
                                "were trying to configure)");
                }
        }
@@ -983,51 +1066,79 @@ static int rlm_ldap_rebind(LDAP *handle, LDAP_CONST char *url, UNUSED ber_tag_t
                           void *ctx)
 {
        ldap_rcode_t status;
-       ldap_handle_t *conn = ctx;
-       
+       ldap_handle_t *conn = talloc_get_type_abort(ctx, ldap_handle_t);
+
        int ldap_errno;
 
-       conn->referred = TRUE;
-       conn->rebound = TRUE;   /* not really, but oh well... */
+       conn->referred = true;
+       conn->rebound = true;   /* not really, but oh well... */
        rad_assert(handle == conn->handle);
 
        DEBUG("rlm_ldap (%s): Rebinding to URL %s", conn->inst->xlat_name, url);
 
-       status = rlm_ldap_bind(conn->inst, NULL, &conn, conn->inst->admin_dn, conn->inst->password, FALSE);
+       status = rlm_ldap_bind(conn->inst, NULL, &conn, conn->inst->admin_dn, conn->inst->password, false);
        if (status != LDAP_PROC_SUCCESS) {
                ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
-                       
+
                return ldap_errno;
        }
-       
+
 
        return LDAP_SUCCESS;
 }
 #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 rlm_ldap instance.
+ * @param ctx to allocate connection handle memory in.
+ * @param instance rlm_ldap instance.
  * @return A new connection handle or NULL on error.
  */
-void *rlm_ldap_conn_create(void *ctx)
+void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
 {
        ldap_rcode_t status;
-       
+
        int ldap_errno, ldap_version;
        struct timeval tv;
-       
-       ldap_instance_t *inst = ctx;
-       LDAP *handle = NULL;
-       ldap_handle_t *conn = NULL;
+
+       ldap_instance_t *inst = instance;
+       ldap_handle_t *conn;
+
+       /*
+        *      Allocate memory for the handle.
+        */
+       conn = talloc_zero(ctx, ldap_handle_t);
+       if (!conn) return NULL;
+       talloc_set_destructor(conn, _mod_conn_free);
+
+       conn->inst = inst;
+       conn->rebound = false;
+       conn->referred = false;
 
 #ifdef HAVE_LDAP_INITIALIZE
        if (inst->is_url) {
                DEBUG("rlm_ldap (%s): Connecting to %s", inst->xlat_name, inst->server);
-               
-               ldap_errno = ldap_initialize(&handle, inst->server);
+
+               ldap_errno = ldap_initialize(&conn->handle, inst->server);
                if (ldap_errno != LDAP_SUCCESS) {
                        LDAP_ERR("ldap_initialize failed: %s", ldap_err2string(ldap_errno));
                        goto error;
@@ -1037,12 +1148,13 @@ void *rlm_ldap_conn_create(void *ctx)
        {
                DEBUG("rlm_ldap (%s): Connecting to %s:%d", inst->xlat_name, inst->server, inst->port);
 
-               handle = ldap_init(inst->server, inst->port);
-               if (!handle) {
+               conn->handle = ldap_init(inst->server, inst->port);
+               if (!conn->handle) {
                        LDAP_ERR("ldap_init() failed");
                        goto error;
                }
        }
+       DEBUG3("rlm_ldap: New libldap handle %p", conn->handle);
 
        /*
         *      We now have a connection structure, but no actual TCP connection.
@@ -1050,28 +1162,39 @@ void *rlm_ldap_conn_create(void *ctx)
         *      Set a bunch of LDAP options, using common code.
         */
 #define do_ldap_option(_option, _name, _value) \
-       if (ldap_set_option(handle, _option, _value) != LDAP_OPT_SUCCESS) { \
-               ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno); \
+       if (ldap_set_option(conn->handle, _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)); \
+       }
+
+#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));
        }
 
        /*
         *      Leave "chase_referrals" unset to use the OpenLDAP default.
         */
-       if (inst->chase_referrals != 2) {
+       if (!inst->chase_referrals_unset) {
                if (inst->chase_referrals) {
                        do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals", LDAP_OPT_ON);
-                       
-                       if (inst->rebind == 1) {
+
+                       if (inst->rebind == true) {
 #if LDAP_SET_REBIND_PROC_ARGS == 3
-                               ldap_set_rebind_proc(handle, rlm_ldap_rebind, inst);
-#else
-                               DEBUGW("The flag 'rebind = yes' is not supported by the system LDAP library. "
-                                      "Ignoring.");
+                               ldap_set_rebind_proc(conn->handle, rlm_ldap_rebind, conn);
 #endif
                        }
                } else {
@@ -1079,10 +1202,14 @@ void *rlm_ldap_conn_create(void *ctx)
                }
        }
 
-       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));
 
@@ -1112,36 +1239,47 @@ void *rlm_ldap_conn_create(void *ctx)
 #  define maybe_ldap_option(_option, _name, _value) \
        if (_value) do_ldap_option(_option, _name, _value)
 
-       maybe_ldap_option(LDAP_OPT_X_TLS_CACERTFILE, "cacertfile", inst->tls_cacertfile);
-       maybe_ldap_option(LDAP_OPT_X_TLS_CACERTDIR, "cacertdir", inst->tls_cacertdir);
+       maybe_ldap_option(LDAP_OPT_X_TLS_CACERTFILE, "ca_file", inst->tls_ca_file);
+       maybe_ldap_option(LDAP_OPT_X_TLS_CACERTDIR, "ca_path", inst->tls_ca_path);
 
-#  ifdef HAVE_LDAP_INT_TLS_CONFIG
-       if (ldap_int_tls_config(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, inst->tls_require_cert) != LDAP_OPT_SUCCESS) {
-               ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
-               
-               LDAP_ERR("Could not set LDAP_OPT_X_TLS_REQUIRE_CERT option to %s: %s", inst->tls_require_cert,
-                        ldap_err2string(ldap_errno));
+
+       /*
+        *      Set certificate options
+        */
+       maybe_ldap_option(LDAP_OPT_X_TLS_CERTFILE, "certificate_file", inst->tls_certificate_file);
+       maybe_ldap_option(LDAP_OPT_X_TLS_KEYFILE, "private_key_file", inst->tls_private_key_file);
+       maybe_ldap_option(LDAP_OPT_X_TLS_RANDOM_FILE, "random_file", inst->tls_random_file);
+
+#  ifdef LDAP_OPT_X_TLS_NEVER
+       if (inst->tls_require_cert_str) {
+               do_ldap_option(LDAP_OPT_X_TLS_REQUIRE_CERT, "require_cert", &inst->tls_require_cert);
        }
 #  endif
 
        /*
-        *      Set certificate options
+        *      Counter intuitively the TLS context appears to need to be initialised
+        *      after all the TLS options are set on the handle.
         */
-       maybe_ldap_option(LDAP_OPT_X_TLS_CERTFILE, "certfile", inst->tls_certfile);
-       maybe_ldap_option(LDAP_OPT_X_TLS_KEYFILE, "keyfile", inst->tls_keyfile);
-       maybe_ldap_option(LDAP_OPT_X_TLS_RANDOM_FILE, "randfile", inst->tls_randfile);
+#  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) {
-                       DEBUGW("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");
                }
-               
-               if (ldap_start_tls_s(handle, NULL, NULL) != LDAP_SUCCESS) {
-                       ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
+
+               if (ldap_start_tls_s(conn->handle, NULL, NULL) != LDAP_SUCCESS) {
+                       ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
 
                        LDAP_ERR("Could not start TLS: %s", ldap_err2string(ldap_errno));
                        goto error;
@@ -1149,69 +1287,29 @@ void *rlm_ldap_conn_create(void *ctx)
        }
 #endif /* HAVE_LDAP_START_TLS */
 
-       /*
-        *      Allocate memory for the handle.
-        */
-       conn = talloc_zero(ctx, ldap_handle_t);
-       conn->inst = inst;
-       conn->handle = handle;
-       conn->rebound = FALSE;
-       conn->referred = FALSE;
-
-       status = rlm_ldap_bind(inst, NULL, &conn, inst->admin_dn, inst->password, FALSE);
+       status = rlm_ldap_bind(inst, NULL, &conn, inst->admin_dn, inst->password, false);
        if (status != LDAP_PROC_SUCCESS) {
                goto error;
        }
 
        return conn;
-       
-       error:
-       if (handle) ldap_unbind_s(handle);
-       if (conn) 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 ctx unused.
- * @param connection to destroy.
- * @return always indicates success.
- */
-int rlm_ldap_conn_delete(UNUSED void *ctx, void *connection)
-{
-       ldap_handle_t *conn = connection;
-
-       ldap_unbind_s(conn->handle);
+error:
        talloc_free(conn);
 
-       return 0;
+       return NULL;
 }
 
-
 /** 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.
+ * @param request Current request (may be NULL).
  */
-ldap_handle_t *rlm_ldap_get_socket(const ldap_instance_t *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) {
-               RDEBUGE("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
@@ -1222,7 +1320,7 @@ ldap_handle_t *rlm_ldap_get_socket(const ldap_instance_t *inst, REQUEST *request
  * @param inst rlm_ldap configuration.
  * @param conn to release.
  */
-void rlm_ldap_release_socket(const ldap_instance_t *inst, ldap_handle_t *conn)
+void rlm_ldap_release_socket(ldap_instance_t const *inst, ldap_handle_t *conn)
 {
        /*
         *      Could have already been free'd due to a previous error.