Pass a threadsafe ctx into fr_connection_pool create callback
[freeradius.git] / src / modules / rlm_ldap / ldap.c
index 764ed9f..6fe2473 100644 (file)
  * @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
  *
@@ -266,7 +264,7 @@ static ldap_rcode_t rlm_ldap_result(ldap_instance_t const *inst, ldap_handle_t c
        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.
@@ -341,8 +339,7 @@ static ldap_rcode_t rlm_ldap_result(ldap_instance_t const *inst, ldap_handle_t c
                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)) {
@@ -368,7 +365,7 @@ static ldap_rcode_t rlm_ldap_result(ldap_instance_t const *inst, ldap_handle_t c
                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;
 
@@ -443,7 +440,7 @@ static ldap_rcode_t rlm_ldap_result(ldap_instance_t const *inst, ldap_handle_t c
 
                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);
@@ -489,9 +486,7 @@ static ldap_rcode_t rlm_ldap_result(ldap_instance_t const *inst, ldap_handle_t c
                ldap_memfree(part_dn);
        }
 
-       if (our_err) {
-               talloc_free(our_err);
-       }
+       talloc_free(our_err);
 
        if ((lib_errno || srv_errno) && *result) {
                ldap_msgfree(*result);
@@ -514,93 +509,106 @@ static ldap_rcode_t rlm_ldap_result(ldap_instance_t const *inst, ldap_handle_t c
  * @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.
@@ -633,6 +641,9 @@ ldap_rcode_t rlm_ldap_search(ldap_instance_t const *inst, REQUEST *request, ldap
        char const      *error = NULL;
        char            *extra = NULL;
 
+       int             i;
+
+
        rad_assert(*pconn && (*pconn)->handle);
 
        /*
@@ -656,8 +667,13 @@ ldap_rcode_t rlm_ldap_search(ldap_instance_t const *inst, REQUEST *request, ldap
                (*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
@@ -665,14 +681,21 @@ ldap_rcode_t rlm_ldap_search(ldap_instance_t const *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);
 
-       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) {
@@ -680,7 +703,7 @@ retry:
 
                                talloc_free(extra); /* don't leak debug info */
 
-                               goto retry;
+                               continue;
                        }
 
                        status = LDAP_PROC_ERROR;
@@ -691,6 +714,16 @@ retry:
                        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);
@@ -708,10 +741,8 @@ retry:
                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
@@ -750,6 +781,8 @@ ldap_rcode_t rlm_ldap_modify(ldap_instance_t const *inst, REQUEST *request, ldap
        char const      *error = NULL;
        char            *extra = NULL;
 
+       int             i;
+
        rad_assert(*pconn && (*pconn)->handle);
 
        /*
@@ -766,40 +799,51 @@ ldap_rcode_t rlm_ldap_modify(ldap_instance_t const *inst, REQUEST *request, ldap
                (*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;
 }
 
@@ -833,7 +877,7 @@ char const *rlm_ldap_find_user(ldap_instance_t const *inst, REQUEST *request, ld
        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;
@@ -877,15 +921,15 @@ char const *rlm_ldap_find_user(ldap_instance_t const *inst, REQUEST *request, ld
 
        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;
        }
 
@@ -893,9 +937,11 @@ char const *rlm_ldap_find_user(ldap_instance_t const *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;
@@ -956,12 +1002,13 @@ rlm_rcode_t rlm_ldap_check_access(ldap_instance_t const *inst, REQUEST *request,
        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;
                }
@@ -996,7 +1043,7 @@ void rlm_ldap_check_reply(ldap_instance_t const *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)) {
-                       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)");
@@ -1041,14 +1088,32 @@ static int rlm_ldap_rebind(LDAP *handle, LDAP_CONST char *url, UNUSED ber_tag_t
 }
 #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;
 
@@ -1056,15 +1121,16 @@ void *mod_conn_create(void *instance)
        struct timeval tv;
 
        ldap_instance_t *inst = instance;
-       LDAP *handle = NULL;
        ldap_handle_t *conn;
 
        /*
         *      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->handle = handle;
        conn->rebound = false;
        conn->referred = false;
 
@@ -1072,7 +1138,7 @@ void *mod_conn_create(void *instance)
        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;
@@ -1082,12 +1148,13 @@ void *mod_conn_create(void *instance)
        {
                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.
@@ -1095,25 +1162,39 @@ void *mod_conn_create(void *instance)
         *      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 == true) {
 #if LDAP_SET_REBIND_PROC_ARGS == 3
-                               ldap_set_rebind_proc(handle, rlm_ldap_rebind, conn);
+                               ldap_set_rebind_proc(conn->handle, rlm_ldap_rebind, conn);
 #endif
                        }
                } else {
@@ -1121,10 +1202,14 @@ void *mod_conn_create(void *instance)
                }
        }
 
-       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));
 
@@ -1147,16 +1232,6 @@ void *mod_conn_create(void *instance)
        /*
         *      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));
        }
@@ -1182,16 +1257,29 @@ void *mod_conn_create(void *instance)
 #  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");
                }
 
-               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;
@@ -1206,34 +1294,12 @@ void *mod_conn_create(void *instance)
 
        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 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);
+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).
@@ -1241,21 +1307,11 @@ int mod_conn_delete(UNUSED void *instance, void *handle)
  * @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.