]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Reorganise rlm_ldap
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 19 Mar 2013 19:33:21 +0000 (15:33 -0400)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 19 Mar 2013 19:33:21 +0000 (15:33 -0400)
src/modules/rlm_ldap/all.mk.in
src/modules/rlm_ldap/attrmap.c [new file with mode: 0644]
src/modules/rlm_ldap/edir.c
src/modules/rlm_ldap/ldap.c [new file with mode: 0644]
src/modules/rlm_ldap/ldap.h [new file with mode: 0644]
src/modules/rlm_ldap/rlm_ldap.c
src/modules/rlm_pap/rlm_pap.c

index 1459ab3dd08021a89c2432788b3551d08cb7f73d..ed7f72e5e003949e9b88185cb434518c379f73da 100644 (file)
@@ -4,7 +4,7 @@ ifneq "$(TARGETNAME)" ""
 TARGET         := $(TARGETNAME).a
 endif
 
-SOURCES                := $(TARGETNAME).c @edir@
+SOURCES                := $(TARGETNAME).c attrmap.c ldap.c @edir@
 
 SRC_CFLAGS     := @ldap_cflags@
 TGT_LDLIBS     := @ldap_ldflags@
diff --git a/src/modules/rlm_ldap/attrmap.c b/src/modules/rlm_ldap/attrmap.c
new file mode 100644 (file)
index 0000000..89d95d8
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License, version 2 if the
+ *   License as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+/**
+ * $Id$
+ * @file ldap.c
+ * @brief Functions for mapping between LDAP and FreeRADIUS attributes.
+ *
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2013 Network RADIUS SARL <info@networkradius.com>
+ * @copyright 2013 The FreeRADIUS Server Project.
+ */
+
+#include <freeradius-devel/rad_assert.h>
+#include "ldap.h"
+
+static VALUE_PAIR *rlm_ldap_map_getvalue(REQUEST *request, const value_pair_map_t *map, void *ctx)
+{
+       rlm_ldap_result_t *self = ctx;
+       VALUE_PAIR *head, **tail, *vp;
+       int i;
+       
+       request = request;
+       
+       head = NULL;
+       tail = &head;
+       
+       /*
+        *      Iterate over all the retrieved values,
+        *      don't try and be clever about changing operators
+        *      just use whatever was set in the attribute map. 
+        */
+       for (i = 0; i < self->count; i++) {
+               vp = pairalloc(NULL, map->dst->da);
+               rad_assert(vp);
+
+               pairparsevalue(vp, self->values[i]);
+               
+               *tail = vp;
+               tail = &(vp->next);
+       }
+       
+       return head;            
+}
+
+int rlm_ldap_map_verify(ldap_instance_t *inst, value_pair_map_t **head)
+{
+       value_pair_map_t *map;
+       
+       if (radius_attrmap(inst->cs, head, PAIR_LIST_REPLY,
+                          PAIR_LIST_REQUEST, LDAP_MAX_ATTRMAP) < 0) {
+               return -1;
+       }
+       /*
+        *      Attrmap only performs some basic validation checks, we need
+        *      to do rlm_ldap specific checks here.
+        */
+       for (map = *head; map != NULL; map = map->next) {
+               if (map->dst->type != VPT_TYPE_ATTR) {
+                       cf_log_err(map->ci, "Left operand must be an attribute ref");
+                       
+                       return -1;
+               }
+               
+               if (map->src->type == VPT_TYPE_LIST) {
+                       cf_log_err(map->ci, "Right operand must not be a list");
+               
+                       return -1;
+               }
+               
+               /*
+                *      Be smart about whether we warn the user about missing passwords.
+                *      If there are no password attributes in the mapping, then the user's either an idiot
+                *      and has no idea what they're doing, or they're authenticating the user using a different
+                *      method.
+                */
+               if (!inst->expect_password && map->dst->da && (map->dst->type == VPT_TYPE_ATTR)) {
+                       switch (map->dst->da->attr) {
+                       case PW_CLEARTEXT_PASSWORD:
+                       case PW_NT_PASSWORD:
+                       case PW_USER_PASSWORD:
+                       case PW_PASSWORD_WITH_HEADER:
+                       case PW_CRYPT_PASSWORD:
+                               /*
+                                *      Because you just know someone is going to map NT-Password to the
+                                *      request list, and then complain it's not working...
+                                */
+                               if (map->dst->list != PAIR_LIST_CONTROL) {
+                                       LDAP_DBGW("Mapping LDAP (%s) attribute to password \"reference\" attribute "
+                                                 "(%s) in %s list. This is probably *NOT* the correct list, "
+                                                 "you should prepend \"control:\" to \"reference\" attribute "
+                                                 "(control:%s)",
+                                                 map->src->name, map->dst->da->name,
+                                                 fr_int2str(pair_lists, map->dst->list, "<invalid>"),
+                                                 map->dst->da->name);
+                               }
+                               
+                               inst->expect_password = TRUE;
+                       default:
+                               break;  
+                       }
+               }
+               
+               switch (map->src->type) {
+               /*
+                *      Only =, :=, += and -= operators are supported for
+                *      cache entries.
+                */
+               case VPT_TYPE_LITERAL:
+               case VPT_TYPE_XLAT:
+               case VPT_TYPE_ATTR:
+                       switch (map->op) {
+                       case T_OP_SET:
+                       case T_OP_EQ:
+                       case T_OP_SUB:
+                       case T_OP_ADD:
+                               break;
+               
+                       default:
+                               cf_log_err(map->ci, "Operator \"%s\" not allowed for %s values",
+                                          fr_int2str(fr_tokens, map->op, "¿unknown?"),
+                                          fr_int2str(vpt_types, map->src->type, "¿unknown?"));
+                               return -1;
+                       }
+               default:
+                       break;
+               }
+       }
+       return 0;
+}
+
+/** Free attribute map values
+ *
+ */
+void rlm_ldap_map_xlat_free(const rlm_ldap_map_xlat_t *expanded)
+{
+       const value_pair_map_t *map;
+       unsigned int total = 0;
+       
+       const char *name;
+       
+       for (map = expanded->maps; map != NULL; map = map->next) {
+               name = expanded->attrs[total++];
+               if (!name) return;
+               
+               switch (map->src->type) {
+               case VPT_TYPE_XLAT:             
+               case VPT_TYPE_ATTR:
+                       rad_cfree(name);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+/** Expand values in an attribute map where needed
+ *
+ */
+int rlm_ldap_map_xlat(REQUEST *request, const value_pair_map_t *maps, rlm_ldap_map_xlat_t *expanded)
+{
+       const value_pair_map_t *map;
+       unsigned int total = 0;
+       
+       size_t len;
+       char *buffer;
+
+       VALUE_PAIR *found, **from = NULL;
+       REQUEST *context;
+
+       for (map = maps; map != NULL; map = map->next) {
+               switch (map->src->type) {
+               case VPT_TYPE_XLAT:
+                       buffer = rad_malloc(LDAP_MAX_ATTR_STR_LEN);
+                       len = radius_xlat(buffer, LDAP_MAX_ATTR_STR_LEN, map->src->name, request, NULL, NULL);
+                                         
+                       if (len <= 0) {
+                               RDEBUG("Expansion of LDAP attribute \"%s\" failed", map->src->name);
+                                      
+                               goto error;
+                       }
+                       
+                       expanded->attrs[total++] = buffer;
+                       break;
+
+               case VPT_TYPE_ATTR:
+                       context = request;
+                       
+                       if (radius_request(&context, map->src->request) == 0) {
+                               from = radius_list(context, map->src->list);
+                       }
+                       if (!from) continue;
+                       
+                       found = pairfind(*from, map->src->da->attr,
+                                        map->src->da->vendor, TAG_ANY);
+                       if (!found) continue;
+                       
+                       buffer = rad_malloc(LDAP_MAX_ATTR_STR_LEN);
+                       strlcpy(buffer, found->vp_strvalue, LDAP_MAX_ATTR_STR_LEN);
+                       
+                       expanded->attrs[total++] = buffer;
+                       break;
+                       
+               case VPT_TYPE_LITERAL:
+                       expanded->attrs[total++] = map->src->name;
+                       break;
+               default:
+                       rad_assert(0);
+               error:
+                       expanded->attrs[total] = NULL;
+                       
+                       rlm_ldap_map_xlat_free(expanded);
+                       
+                       return -1;
+               }
+                       
+       }
+       
+       expanded->attrs[total] = NULL;
+       expanded->maps = maps;
+       
+       return 0;
+}
+
+
+/** Convert attribute map into valuepairs
+ *
+ * Use the attribute map built earlier to convert LDAP values into valuepairs and insert them into whichever 
+ * list they need to go into.
+ *
+ * This is *NOT* atomic, but there's no condition in which we should error out...
+ */
+void rlm_ldap_map_do(UNUSED const ldap_instance_t *inst, REQUEST *request, LDAP *handle,
+                    const rlm_ldap_map_xlat_t *expanded, LDAPMessage *entry)
+{
+       const value_pair_map_t  *map;
+       unsigned int            total = 0;
+       
+       rlm_ldap_result_t       result;
+       const char              *name;
+
+       for (map = expanded->maps; map != NULL; map = map->next) {
+               name = expanded->attrs[total++];
+               
+               result.values = ldap_get_values(handle, entry, name);
+               if (!result.values) {
+                       RDEBUG2("Attribute \"%s\" not found in LDAP object", name);
+                               
+                       goto next;
+               }
+               
+               /*
+                *      Find out how many values there are for the
+                *      attribute and extract all of them.
+                */
+               result.count = ldap_count_values(result.values);
+               
+               /*
+                *      If something bad happened, just skip, this is probably
+                *      a case of the dst being incorrect for the current
+                *      request context
+                */
+               if (radius_map2request(request, map, name, rlm_ldap_map_getvalue, &result) < 0) {
+                       goto next;
+               }
+               
+               next:
+               
+               ldap_value_free(result.values);
+       }
+}
index da55f0ec27767519e57fb5788dd91b1553477ff0..32688822f185e58c0fe8a12a6d3142ed9706b696 100644 (file)
@@ -34,17 +34,17 @@ RCSID("$Id$")
 #include <lber.h>
 
 /* NMAS error codes */
-#define NMAS_E_BASE             (-1600)
+#define NMAS_E_BASE    (-1600)
 
-#define NMAS_SUCCESS           0
+#define NMAS_SUCCESS   0
 
-#define NMAS_E_FRAG_FAILURE        (NMAS_E_BASE-31)     /* -1631 0xFFFFF9A1 */
-#define NMAS_E_BUFFER_OVERFLOW      (NMAS_E_BASE-33)     /* -1633 0xFFFFF99F */
-#define NMAS_E_SYSTEM_RESOURCES     (NMAS_E_BASE-34)     /* -1634 0xFFFFF99E */
-#define NMAS_E_INSUFFICIENT_MEMORY  (NMAS_E_BASE-35)     /* -1635 0xFFFFF99D */
-#define NMAS_E_NOT_SUPPORTED   (NMAS_E_BASE-36)     /* -1636 0xFFFFF99C */
-#define NMAS_E_INVALID_PARAMETER    (NMAS_E_BASE-43)     /* -1643 0xFFFFF995 */
-#define NMAS_E_INVALID_VERSION      (NMAS_E_BASE-52)     /* -1652 0xFFFFF98C */
+#define NMAS_E_FRAG_FAILURE            (NMAS_E_BASE-31)     /* -1631 0xFFFFF9A1 */
+#define NMAS_E_BUFFER_OVERFLOW         (NMAS_E_BASE-33)     /* -1633 0xFFFFF99F */
+#define NMAS_E_SYSTEM_RESOURCES                (NMAS_E_BASE-34)     /* -1634 0xFFFFF99E */
+#define NMAS_E_INSUFFICIENT_MEMORY     (NMAS_E_BASE-35)     /* -1635 0xFFFFF99D */
+#define NMAS_E_NOT_SUPPORTED           (NMAS_E_BASE-36)     /* -1636 0xFFFFF99C */
+#define NMAS_E_INVALID_PARAMETER       (NMAS_E_BASE-43)     /* -1643 0xFFFFF995 */
+#define NMAS_E_INVALID_VERSION         (NMAS_E_BASE-52)     /* -1652 0xFFFFF98C */
 
 /* OID of LDAP extenstion calls to read Universal Password */
 #define NMASLDAP_GET_PASSWORD_REQUEST     "2.16.840.1.113719.1.39.42.100.13"
diff --git a/src/modules/rlm_ldap/ldap.c b/src/modules/rlm_ldap/ldap.c
new file mode 100644 (file)
index 0000000..d7ed802
--- /dev/null
@@ -0,0 +1,1207 @@
+/*
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License, version 2 if the
+ *   License as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+/**
+ * $Id$
+ * @file ldap.c
+ * @brief LDAP module library functions.
+ *
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @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"
+
+
+
+/** Converts "bad" strings into ones which are safe for LDAP
+ *
+ * This is a callback for xlat operations.
+ *
+ * Will escape any characters in input strings that would cause the string to be interpreted as part of a DN and or
+ * filter. Escape sequence is \<hex><hex>.
+ *
+ * @param request The current request.
+ * @param out Pointer to output buffer.
+ * @param outlen Size of the output buffer.
+ * @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)
+{
+       static const char encode[] = ",+\"\\<>;*=()";
+       static const char hextab[] = "0123456789abcdef";
+       size_t left = outlen;
+       
+       if (*in && ((*in == ' ') || (*in == '#'))) {
+               goto encode;
+       }
+       
+       while (*in) {
+               /*
+                *      Encode unsafe characters.
+                */
+               if (memchr(encode, *in, sizeof(encode) - 1)) {
+                       encode:
+
+                       /*
+                        *      Only 3 or less bytes available.
+                        */
+                       if (left <= 3) break;
+
+                       *out++ = '\\';
+                       *out++ = hextab[(*in >> 4) & 0x0f];
+                       *out++ = hextab[*in & 0x0f];
+                       in++;
+                       left -= 3;
+
+                       continue;
+               }
+
+               if (left <= 1) break;
+
+               /*
+                *      Doesn't need encoding
+                */
+               *out++ = *in++;
+               left--;
+       }
+       
+       *out = '\0';
+       
+       return outlen - left;
+}
+
+/** Check whether a string is a DN
+ *
+ * @param str to check.
+ * @return true if string is a DN, else false.
+ */
+int rlm_ldap_is_dn(const char *str)
+{
+       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.
+ * @param 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)
+{
+       size_t f_len, p_len, i;
+       
+       if (!full) {
+               return -1;
+       }
+       
+       f_len = strlen(full);
+       
+       if (!part) {
+               return f_len;
+       }
+       
+       p_len = strlen(part);
+       if (!p_len) {
+               return f_len;
+       }
+       
+       if ((f_len < p_len) || !f_len) {
+               return -1; 
+       }
+
+
+       for (i = 0; i < p_len; i++) {
+               if (part[p_len - i] != full[f_len - i]) {
+                       return -1; 
+               }
+       }
+
+       return f_len - p_len;
+}
+
+/** 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 
+ * to retrieve and parse the result.
+ *
+ * Will also produce extended error output including any messages the server sent, and information about partial 
+ * DN matches.
+ *
+ * @param[in] inst of LDAP module.
+ * @param[in] conn Current connection.
+ * @param[in] msgid returned from last operation.
+ * @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 
+ *     (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)
+{
+       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.
+       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.
+       
+       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;
+       }
+       
+       /*
+        *      Check if there was an error sending the request
+        */
+       ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
+                       &lib_errno);
+       if (lib_errno != LDAP_SUCCESS) {
+               goto process_error;
+       }
+       
+       tv.tv_sec = inst->timeout;
+       tv.tv_usec = 0;
+
+       /*
+        *      Now retrieve the result and check for errors
+        *      ldap_result returns -1 on error, and 0 on timeout
+        */
+       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
+        */
+       lib_errno = ldap_parse_result(conn->handle, *result,
+                                     &srv_errno,
+                                     extra ? &part_dn : NULL,
+                                     extra ? extra : NULL,
+                                     NULL, NULL, freeit);
+                                     
+       if (lib_errno != LDAP_SUCCESS) {
+               ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
+                               &lib_errno);
+               goto 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 object wasn't found, check basedn and admin dn";
+               
+               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, part_dn, part_dn ? part_dn : "");
+
+               break;
+
+       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);
+               
+               *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);
+               
+               *error = "Time limit exceeded";
+               /* FALL-THROUGH */
+
+       case LDAP_BUSY:
+       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
+                */
+               p = talloc_strdup(conn, "");
+               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,
+                                                  ldap_err2string(srv_errno), srv_errno);
+                       if (!a) {
+                               talloc_free(p);
+                               break;
+                       }
+                       
+                       p = a;
+               }
+
+               if (our_err) {
+                       a = talloc_asprintf_append_buffer(p,". %s", our_err);
+                       if (!a) {
+                               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);
+       }
+       
+       if ((lib_errno || srv_errno) && *result) {
+               ldap_msgfree(*result);
+               *result = NULL;
+       }
+       
+       return status;
+}
+
+/** Bind to the LDAP directory as a user
+ *
+ * Performs a simple bind to the LDAP directory, and handles any errors that
+ * occur.
+ *
+ * @param request Current request, this may be NULL, in which case all debug logging is done with radlog.
+ * @param pconn to use. May change as this function auto re-connects. Caller must check that pconn is not NULL after 
+ *     calling this function.
+ * @param dn The DN of the user, may be NULL to bind anonymously.
+ * @param password May be NULL if no password is specified.
+ * @param retry if the server is down.
+ * @return one of the RLM_MODULE_* values.
+ */
+rlm_rcode_t rlm_ldap_bind(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn, const char *dn,
+                         const char *password, int retry)
+{
+       rlm_rcode_t     rcode = RLM_MODULE_OK;
+       ldap_rcode_t    status;
+       
+       int             msgid;
+       
+       const char      *error = NULL;
+       char            *extra = NULL;
+
+       /*
+        *      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);
+               }
+       }
+
+       status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
+       switch (status) {
+       case LDAP_PROC_SUCCESS:
+               break;
+       case LDAP_PROC_NOT_PERMITTED:
+               rcode = RLM_MODULE_USERLOCK;
+               
+               LDAP_ERR_REQ("Bind was not permitted: %s", error);
+               LDAP_EXT_REQ();
+               
+               break;
+
+       case LDAP_PROC_REJECT:
+               rcode = RLM_MODULE_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 */
+                               
+                               goto retry;
+                       }
+               };
+               
+               /*
+                *      Were not allowed to retry, or there are no more
+                *      sockets, treat this as a hard failure.
+                */
+               goto error;
+       default:
+error:
+               rcode = RLM_MODULE_FAIL;
+#ifdef HAVE_LDAP_INITIALIZE
+               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_EXT_REQ();
+               
+               break;
+       }
+
+       if (extra) talloc_free(extra);
+
+       return rcode; /* caller closes the connection */
+}
+
+
+/** Search for something in the LDAP directory
+ *
+ * Binds as the administrative user and performs a search, dealing with any
+ * errors.
+ *
+ * @param request Current request.
+ * @param pconn to use. May change as this function auto re-connects. Caller must check that pconn is not NULL 
+ *     after calling this function.
+ * @param dn to use.
+ * @param scope to use.
+ * @param filter to use.
+ * @param attrs to retrieve.
+ * @param result Where to store the result. Must be freed with ldap_msgfree.
+ *     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,
+                            LDAPMessage **result)
+{
+       ldap_rcode_t    status;
+       
+       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            *extra = NULL;
+
+       /*
+        *      OpenLDAP library doesn't declare attrs array as const, but
+        *      it really should be *sigh*.
+        */
+       char **search_attrs;
+       memcpy(&search_attrs, &attrs, sizeof(attrs));
+
+       /*
+        *      Do all searches as the admin user.
+        */
+       if ((*pconn)->rebound) {
+               if (rlm_ldap_bind(inst, request, pconn, inst->login, inst->password, TRUE) != RLM_MODULE_OK) {
+                       return LDAP_PROC_ERROR;
+               }
+
+               rad_assert(*pconn);
+               
+               (*pconn)->rebound = FALSE;
+       }
+
+       RDEBUG2("Performing search in '%s' with filter '%s'", dn, filter);
+
+       /*
+        *      If LDAP search produced an error it should also be logged
+        *      to the ld. result should pick it up without us
+        *      having to pass it explicitly.
+        */
+       tv.tv_sec = inst->timeout;
+       tv.tv_usec = 0;
+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) {
+               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);
+                               
+                               talloc_free(extra); /* don't leak debug info */
+                               
+                               goto retry;
+                       }
+                       
+                       status = LDAP_PROC_ERROR;
+                       
+                       /* FALL-THROUGH */
+               default:
+                       RDEBUGE("Failed performing search: %s", error);
+                       RDEBUGE("%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;
+               }
+       }
+       
+       finish:
+       if (extra) talloc_free(extra);
+
+       return status;
+}
+
+/** Modify something in the LDAP directory
+ *
+ * Binds as the administrative user and attempts to modify an LDAP object.
+ *
+ * @param request Current request.
+ * @param pconn to use. May change as this function auto re-connects. Caller must check that pconn is not NULL after 
+ *     calling this function.
+ * @param dn to modify.
+ * @param mods to make.
+ * @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    status;
+       
+       int             msgid;          //!< Message id returned by
+                                       //!< ldap_search_ext.
+       
+       const char      *error = NULL;
+       char            *extra = NULL;                     
+
+       /*
+        *      Perform all modifications as the admin user.
+        */
+       if ((*pconn)->rebound) {
+               if (rlm_ldap_bind(inst, request, pconn, inst->login, inst->password, TRUE) != RLM_MODULE_OK) {
+                       return LDAP_PROC_ERROR;
+               }
+
+               rad_assert(*pconn);
+               
+               (*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);
+
+       return status;
+}
+
+/** Retrieve the DN of a user object
+ *
+ * Retrieves the DN of a user and adds it to the control list as LDAP-UserDN. Will also retrieve any attributes
+ * passed and return the result in *result.
+ *
+ * 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] request Current request.
+ * @param[in,out] pconn to use. May change as this function auto re-connects. Caller must check that pconn is not NULL 
+ *     after calling this function.
+ * @param[in] attrs Additional attributes to retrieve, may be NULL.
+ * @param[out] result Where to write the result, may be NULL in which case result is discarded.
+ * @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[], LDAPMessage **result, rlm_rcode_t *rcode)
+{
+       static const char *tmp_attrs[] = { NULL };
+       
+       ldap_rcode_t    status;
+       VALUE_PAIR      *vp = NULL;
+       LDAPMessage     *tmp_msg = NULL, *entry = NULL;
+       int             ldap_errno;
+       char            *dn;
+       char            filter[LDAP_MAX_FILTER_STR_LEN];        
+       char            basedn[LDAP_MAX_FILTER_STR_LEN];
+       
+       int freeit = FALSE;                                     //!< Whether the message should
+                                                               //!< be freed after being processed.
+
+       *rcode = RLM_MODULE_FAIL;
+
+       if (!result) {
+               result = &tmp_msg;
+               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.
+        */
+       if (!result) {
+               vp = pairfind(request->config_items, PW_LDAP_USERDN, 0, TAG_ANY);
+               if (vp) {
+                       *rcode = RLM_MODULE_OK;
+                       return vp->vp_strvalue;
+               }
+       }
+       
+       /*
+        *      Perform all searches as the admin user.
+        */
+       if ((*pconn)->rebound) {
+               if (rlm_ldap_bind(inst, request, pconn, inst->login, inst->password, TRUE) != RLM_MODULE_OK) {
+                       *rcode = RLM_MODULE_FAIL;
+                       return NULL;
+               }
+
+               rad_assert(*pconn);
+               
+               (*pconn)->rebound = FALSE;
+       }
+
+       
+       if (!radius_xlat(filter, sizeof(filter), inst->filter, request, rlm_ldap_escape_func, NULL)) {
+               RDEBUGE("Unable to create filter");
+               
+               *rcode = RLM_MODULE_INVALID;
+               return NULL;
+       }
+
+       if (!radius_xlat(basedn, sizeof(basedn), inst->basedn, request, rlm_ldap_escape_func, NULL)) {
+               RDEBUGE("Unable to create basedn");
+               
+               *rcode = RLM_MODULE_INVALID;
+               return NULL;
+       }
+
+       status = rlm_ldap_search(inst, request, pconn, basedn, LDAP_SCOPE_SUBTREE, filter, attrs, result);
+       switch (status) {
+               case LDAP_PROC_SUCCESS:
+                       break;
+               case LDAP_PROC_NO_RESULT:
+                       *rcode = RLM_MODULE_NOTFOUND;
+                       return NULL;
+               default:
+                       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", 
+                       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",
+                       ldap_err2string(ldap_errno));
+                      
+               goto finish;
+       }
+       
+       vp = pairmake("LDAP-UserDn", dn, T_OP_EQ);
+       if (vp) {       
+               pairadd(&request->config_items, vp);
+               *rcode = RLM_MODULE_OK;
+       }
+       
+       finish:
+       ldap_memfree(dn);
+       
+       if ((freeit || (*rcode != RLM_MODULE_OK)) && *result) {
+               ldap_msgfree(*result);
+               *result = NULL;
+       }
+
+       return vp ? vp->vp_strvalue : NULL;
+}
+
+rlm_rcode_t rlm_ldap_apply_profile(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
+                                  const char *profile, const rlm_ldap_map_xlat_t *expanded)
+{
+       rlm_rcode_t     rcode = RLM_MODULE_OK;
+       ldap_rcode_t    status;
+       LDAPMessage     *result = NULL, *entry = NULL;
+       int             ldap_errno;
+       LDAP            *handle = (*pconn)->handle;
+       char            filter[LDAP_MAX_FILTER_STR_LEN];
+
+       if (!profile || !*profile) {
+               return RLM_MODULE_NOOP;
+       }
+       strlcpy(filter, inst->base_filter, sizeof(filter));
+
+       status = rlm_ldap_search(inst, request, pconn, profile, LDAP_SCOPE_BASE, filter, expanded->attrs, &result);
+       switch (status) {
+               case LDAP_PROC_SUCCESS:
+                       break;
+               case LDAP_PROC_NO_RESULT:
+                       RDEBUG("Profile \"%s\" not found", profile);
+                       return RLM_MODULE_NOTFOUND;
+               default:
+                       return RLM_MODULE_FAIL;
+       }
+       
+       rad_assert(*pconn);
+       rad_assert(result);
+       
+       entry = ldap_first_entry(handle, result);
+       if (!entry) {
+               ldap_get_option(handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
+               RDEBUGE("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
+               
+               rcode = RLM_MODULE_NOTFOUND;
+               
+               goto free_result;
+       }
+       
+       rlm_ldap_map_do(inst, request, handle, expanded, entry);
+
+free_result:
+       ldap_msgfree(result);
+       
+       return rcode;
+}
+
+/** Check for presence of access attribute in result
+ *
+ * @param inst rlm_ldap configuration.
+ * @param request Current request.
+ * @param conn used to retrieve entry.
+ * @param 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 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 {
+                       RDEBUG("\"%s\" attribute exists - user locked out", inst->userobj_access_attr);
+                       rcode = RLM_MODULE_USERLOCK;
+               }
+
+               ldap_value_free(vals);
+       } else if (inst->access_positive) {
+               RDEBUG("No \"%s\" attribute - user locked out", inst->userobj_access_attr);
+               rcode = RLM_MODULE_USERLOCK;
+       }
+
+       return rcode;
+}
+
+/** Verify we got a password from the search
+ *
+ * Checks to see if after the LDAP to RADIUS mapping has been completed that a reference password.
+ *
+ * @param inst rlm_ldap configuration.
+ * @param request Current request.
+ */
+void rlm_ldap_check_reply(const ldap_instance_t *inst, REQUEST *request)
+{
+       /*
+       *       More warning messages for people who can't be bothered to read the documentation.
+       *
+       *       Expect_password is set when we process the mapping, and is only true if there was a mapping between
+       *       an LDAP attribute and a password reference attribute in the control list.
+       */
+       if (inst->expect_password && (debug_flag > 1)) {
+               if (!pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY) &&
+                   !pairfind(request->config_items, PW_NT_PASSWORD, 0, TAG_ANY) &&
+                   !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 "
+                               "read the password attribute");
+                       RDEBUGW("PAP authentication will *NOT* work with Active Directory (if that is what you "
+                               "were trying to configure)");
+               }
+       }
+}
+
+#if LDAP_SET_REBIND_PROC_ARGS == 3
+/** Callback for OpenLDAP to rebind and chase referrals
+ *
+ * Called by OpenLDAP when it receives a referral and has to rebind.
+ *
+ * @param handle to rebind.
+ * @param url to bind to.
+ * @param request that triggered the rebind.
+ * @param msgid that triggered the rebind.
+ * @param ctx rlm_ldap configuration.
+ */
+static int rlm_ldap_rebind(LDAP *handle, LDAP_CONST char *url, UNUSED ber_tag_t request, UNUSED ber_int_t msgid,
+                          void *ctx)
+{
+       rlm_rcode_t rcode;
+       ldap_handle_t *conn = ctx;
+       
+       int ldap_errno;
+
+       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);
+
+       rcode = rlm_ldap_bind(conn->inst, NULL, &conn, conn->inst->login, conn->inst->password, FALSE);
+       
+       if (rcode == RLM_MODULE_OK) {
+               return LDAP_SUCCESS;
+       }
+       
+       ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
+                       
+       return ldap_errno;
+}
+#endif
+
+/** 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.
+ * @return A new connection handle or NULL on error.
+ */
+void *rlm_ldap_conn_create(void *ctx)
+{
+       rlm_rcode_t rcode;
+       
+       int ldap_errno, ldap_version;
+       struct timeval tv;
+       
+       ldap_instance_t *inst = ctx;
+       LDAP *handle = NULL;
+       ldap_handle_t *conn = NULL;
+
+#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);
+               if (ldap_errno != LDAP_SUCCESS) {
+                       LDAP_ERR("ldap_initialize failed: %s", ldap_err2string(ldap_errno));
+                       goto error;
+               }
+       } else
+#endif
+       {
+               DEBUG("rlm_ldap (%s): Connecting to %s:%d", inst->xlat_name, inst->server, inst->port);
+
+               handle = ldap_init(inst->server, inst->port);
+               if (!handle) {
+                       LDAP_ERR("ldap_init() failed");
+                       goto error;
+               }
+       }
+
+       /*
+        *      We now have a connection structure, but no actual TCP connection.
+        *
+        *      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); \
+               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));
+       }
+
+       /*
+        *      Leave "chase_referrals" unset to use the OpenLDAP default.
+        */
+       if (inst->chase_referrals != 2) {
+               if (inst->chase_referrals) {
+                       do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals", LDAP_OPT_ON);
+                       
+                       if (inst->rebind == 1) {
+#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.");
+#endif
+                       }
+               } else {
+                       do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals", LDAP_OPT_OFF);
+               }
+       }
+
+       tv.tv_sec = inst->net_timeout;
+       tv.tv_usec = 0;
+       do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout", &tv);
+
+       do_ldap_option(LDAP_OPT_TIMELIMIT, "timelimit", &(inst->timelimit));
+
+       ldap_version = LDAP_VERSION3;
+       do_ldap_option(LDAP_OPT_PROTOCOL_VERSION, "ldap_version", &ldap_version);
+
+#ifdef LDAP_OPT_X_KEEPALIVE_IDLE
+       do_ldap_option(LDAP_OPT_X_KEEPALIVE_IDLE, "keepalive idle", &(inst->keepalive_idle));
+#endif
+
+#ifdef LDAP_OPT_X_KEEPALIVE_PROBES
+       do_ldap_option(LDAP_OPT_X_KEEPALIVE_PROBES, "keepalive probes", &(inst->keepalive_probes));
+#endif
+
+#ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL
+       do_ldap_option(LDAP_OPT_X_KEEPALIVE_INTERVAL, "keepalive interval", &(inst->keepalive_interval));
+#endif
+
+#ifdef HAVE_LDAP_START_TLS
+       /*
+        *      Set all of the TLS options
+        */
+       if (inst->tls_mode) {
+               do_ldap_option(LDAP_OPT_X_TLS, "tls_mode", &(inst->tls_mode));
+       }
+
+#  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);
+
+#  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));
+       }
+#  endif
+
+       /*
+        *      Set certificate options
+        */
+       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);
+
+       /*
+        *      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 "
+                              "configuration");
+               }
+               
+               if (ldap_start_tls_s(handle, NULL, NULL) != LDAP_SUCCESS) {
+                       ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
+
+                       LDAP_ERR("Could not start TLS: %s", ldap_err2string(ldap_errno));
+                       goto error;
+               }
+       }
+#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;
+
+       rcode = rlm_ldap_bind(inst, NULL, &conn, inst->login, inst->password, FALSE);
+       if (rcode != RLM_MODULE_OK) {
+               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);
+       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.
+ */
+ldap_handle_t *rlm_ldap_get_socket(const ldap_instance_t *inst, 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;
+}
+
+/** Frees an LDAP socket back to the connection pool
+ *
+ * If the socket was rebound chasing a referral onto another server then we destroy it.
+ * If the socket was rebound to another user on the same server, we let the next caller rebind it.
+ *
+ * @param inst rlm_ldap configuration.
+ * @param conn to release.
+ */
+void rlm_ldap_release_socket(const ldap_instance_t *inst, ldap_handle_t *conn)
+{
+       /*
+        *      Could have already been free'd due to a previous error.
+        */
+       if (!conn) return;
+
+       /*
+        *      We chased a referral to another server.
+        *
+        *      This connection is no longer part of the pool which is connected to and bound to the configured server.
+        *      Close it.
+        *
+        *      Note that we do NOT close it if it was bound to another user.  Instead, we let the next caller do the
+        *      rebind.
+        */
+       if (conn->referred) {
+               fr_connection_del(inst->pool, conn);
+               return;
+       }
+
+       fr_connection_release(inst->pool, conn);
+       return;
+}
diff --git a/src/modules/rlm_ldap/ldap.h b/src/modules/rlm_ldap/ldap.h
new file mode 100644 (file)
index 0000000..28a30e2
--- /dev/null
@@ -0,0 +1,247 @@
+/**
+ * $Id$
+ * @file ldap.
+ * @brief LDAP authorization and authentication module headers.
+ *
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2013 Network RADIUS SARL<info@networkradius.com>
+ * @copyright 2013 The FreeRADIUS Server Project.
+ */
+#ifndef _RLM_LDAP_H
+#define _RLM_LDAP_H
+
+#include       <freeradius-devel/radiusd.h>
+#include       <freeradius-devel/modules.h>
+#include       <ldap.h>
+
+#define LDAP_MAX_ATTRMAP       128
+#define LDAP_MAX_ATTR_STR_LEN  256
+#define LDAP_MAX_FILTER_STR_LEN        1024
+
+/*
+ *     The default setting for TLS Certificate Verification
+ */
+#define TLS_DEFAULT_VERIFY "allow"
+
+typedef struct ldap_acct_section {
+       CONF_SECTION    *cs;
+       
+       const char *reference;
+} ldap_acct_section_t;
+
+typedef struct ldap_instance {
+       CONF_SECTION    *cs;
+       fr_connection_pool_t *pool;
+
+       char            *server;
+       int             port;
+
+       char            *login;
+       char            *password;
+
+       char            *filter;
+       char            *basedn;
+
+       int             chase_referrals;
+       int             rebind;
+
+       int             ldap_debug;             //!< Debug flag for the SDK.
+
+       const char      *xlat_name;             //!< Instance name.
+
+       int             expect_password;
+       
+       /*
+        *      RADIUS attribute to LDAP attribute maps
+        */
+       value_pair_map_t *user_map;             //!< Attribute map applied 
+                                               //!< to users and profiles.
+       
+       /*
+        *      User object attributes and filters
+        */
+       const char      *userobj_filter;        //!< Filter to retrieve only
+                                               //!< user objects.
+       const char      *userobj_membership_attr;       //!< Attribute that
+                                                       //!< describes groups
+                                                       //!< the user is a
+                                                       //!< member of.
+       char            *userobj_access_attr;   //!< Attribute to check to see
+                                               //!< if the user should be 
+                                               //!< locked out.
+       int             access_positive;        //!< If true the presence of 
+                                               //!< the attribute will allow
+                                               //!< access, else it will
+                                               //!< deny access.
+
+       /*
+        *      Group object attributes and filters
+        */
+       const char      *groupobj_name_attr;    //!< The name of the group.
+       const char      *groupobj_membership_filter;    //!< Filter to only
+                                                       //!< retrieve groups
+                                                       //!< which contain
+                                                       //!< the user as a 
+                                                       //!< member.
+       
+       /*
+        *      Profiles
+        */
+       const char      *base_filter;           //!< Base filter combined with
+                                               //!< all other filters.
+       const char      *default_profile;
+       const char      *profile_attr;
+       
+
+       /*
+        *      Accounting
+        */
+       ldap_acct_section_t *postauth;
+       ldap_acct_section_t *accounting;
+
+       /*
+        *      TLS items.  We should really normalize these with the
+        *      TLS code in 3.0.
+        */
+       int             tls_mode;
+       int             start_tls;
+       char            *tls_cacertfile;
+       char            *tls_cacertdir;
+       char            *tls_certfile;
+       char            *tls_keyfile;
+       char            *tls_randfile;
+       char            *tls_require_cert;
+
+       /*
+        *      Options
+        */
+       int             timelimit;
+       int             net_timeout;
+       int             timeout;
+       int             is_url;
+
+#ifdef WITH_EDIR
+       /*
+        *      eDir support
+        */
+       int             edir;
+       int             edir_autz;
+#endif
+       /*
+        *      For keep-alives.
+        */
+#ifdef LDAP_OPT_X_KEEPALIVE_IDLE
+       int             keepalive_idle;
+#endif
+#ifdef LDAP_OPT_X_KEEPALIVE_PROBES
+       int             keepalive_probes;
+#endif
+#ifdef LDAP_OPT_ERROR_NUMBER
+       int             keepalive_interval;
+#endif
+
+} ldap_instance_t;
+
+typedef struct ldap_handle {
+       LDAP            *handle;        //!< LDAP LD handle.
+       int             rebound;        //!< Whether the connection has been rebound to something other than the admin
+                                       //!< user.
+       int             referred;       //!< Whether the connection is now established a server other than the
+                                       //!< configured one.
+       ldap_instance_t *inst;          //!< rlm_ldap configuration.
+} ldap_handle_t;
+
+typedef struct rlm_ldap_map_xlat {
+       const value_pair_map_t *maps;
+       const char *attrs[LDAP_MAX_ATTRMAP];
+} rlm_ldap_map_xlat_t;
+
+typedef struct rlm_ldap_result {
+       char    **values;
+       int     count;
+} rlm_ldap_result_t;
+
+typedef enum {
+       LDAP_PROC_SUCCESS = 0,          //!< Operation was successfull.
+       LDAP_PROC_ERROR = -1,           //!< Unrecoverable library/server error.
+       LDAP_PROC_RETRY = -2,           //!< Transitory error, caller should
+                                       //!< retry the operation with a new
+                                       //!< connection.
+       LDAP_PROC_NOT_PERMITTED = -3,   //!< Operation was not permitted, 
+                                       //!< either current user was locked out
+                                       //!< in the case of binds, or has
+                                       //!< insufficient access.
+       LDAP_PROC_REJECT = -4,          //!< Bind failed, user was rejected.
+       LDAP_PROC_BAD_DN = -5,          //!< Specified an invalid object in a
+                                       //!< bind or search DN.
+       LDAP_PROC_NO_RESULT = -6        //!< Got no results.
+} ldap_rcode_t;
+
+/*
+ *     Some functions may be called with a NULL request structure, this
+ *     simplifies switching certain messages from the request log to
+ *     the main log.
+ */
+#define LDAP_INFO(fmt, ...) radlog(L_INFO, "rlm_ldap (%s): " fmt, inst->xlat_name, ##__VA_ARGS__)
+#define LDAP_DBGW(fmt, ...) radlog(L_DBG_WARN, "rlm_ldap (%s): " fmt, inst->xlat_name, ##__VA_ARGS__)
+#define LDAP_DBGW_REQ(fmt, ...) do { if (request) {RDEBUGW(fmt, ##__VA_ARGS__);} else {LDAP_DBGW(fmt, ##__VA_ARGS__);}} while (0)
+
+#define LDAP_ERR(fmt, ...) radlog(L_ERR, "rlm_ldap (%s): " fmt, inst->xlat_name, ##__VA_ARGS__)
+#define LDAP_ERR_REQ(fmt, ...) do { if (request) {RDEBUGE(fmt, ##__VA_ARGS__);} else {LDAP_ERR(fmt, ##__VA_ARGS__);}} while (0)
+
+#define LDAP_EXT() if (extra) LDAP_ERR(extra)
+#define LDAP_EXT_REQ() do { if (extra) { if (request) RDEBUGE("%s", extra); else LDAP_ERR("%s", extra); }} while (0)
+
+/*
+ *     ldap.c - Wrappers arounds OpenLDAP functions.
+ */
+size_t rlm_ldap_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, const char *in, UNUSED void *arg);
+
+int rlm_ldap_is_dn(const char *str);
+
+rlm_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_search(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
+                            const char *dn, int scope, const char *filter, const char * const *attrs,
+                            LDAPMessage **result);
+                            
+ldap_rcode_t rlm_ldap_modify(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
+                            const char *dn, LDAPMod *mods[]);
+
+rlm_rcode_t rlm_ldap_apply_profile(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
+                                  const char *profile, const rlm_ldap_map_xlat_t *expanded);
+                                  
+const char *rlm_ldap_find_user(const ldap_instance_t *inst, REQUEST *request, ldap_handle_t **pconn,
+                              const char *attrs[], LDAPMessage **result, rlm_rcode_t *rcode);
+                              
+rlm_rcode_t rlm_ldap_check_access(const ldap_instance_t *inst, REQUEST *request, const ldap_handle_t *conn,
+                                 LDAPMessage *entry);
+                                 
+void rlm_ldap_check_reply(const ldap_instance_t *inst, REQUEST *request);
+
+/*
+ *     ldap.c - Callbacks for the connection pool API.
+ */
+void *rlm_ldap_conn_create(void *ctx);
+
+int rlm_ldap_conn_delete(UNUSED void *ctx, void *connection);
+
+ldap_handle_t *rlm_ldap_get_socket(const ldap_instance_t *inst, REQUEST *request);
+
+void rlm_ldap_release_socket(const ldap_instance_t *inst, ldap_handle_t *conn);
+
+/*
+ *     attrmap.c - Attribute mapping code.
+ */
+int rlm_ldap_map_verify(ldap_instance_t *inst, value_pair_map_t **head);
+
+void rlm_ldap_map_xlat_free(const rlm_ldap_map_xlat_t *expanded);
+
+int rlm_ldap_map_xlat(REQUEST *request, const value_pair_map_t *maps, rlm_ldap_map_xlat_t *expanded);
+
+void rlm_ldap_map_do(const ldap_instance_t *inst, REQUEST *request, LDAP *handle,
+                    const rlm_ldap_map_xlat_t *expanded, LDAPMessage *entry);
+
+#endif
index 81f1df4ce5e9f0954fd865648eca2f0088637828..d466e97d402105041a63d4b04247e0a5aae517e4 100644 (file)
  * @file rlm_ldap.c
  * @brief LDAP authorization and authentication module.
  *
- * @copyright 1999-2013 The FreeRADIUS Server Project.
- * @copyright 2012 Alan DeKok <aland@freeradius.org>
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @author Alan DeKok <aland@freeradius.org>
+ *
+ * @copyright 2013 Network RADIUS SARL <info@networkradius.com>
  * @copyright 2012-2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2012 Alan DeKok <aland@freeradius.org>
+ * @copyright 1999-2013 The FreeRADIUS Server Project.
  */
 #include <freeradius-devel/ident.h>
 RCSID("$Id$")
 
-#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>
-
-#define MAX_ATTRMAP            128
-#define MAX_ATTR_STR_LEN       256
-#define MAX_FILTER_STR_LEN     1024
-
-#ifdef WITH_EDIR
-extern int nmasldap_get_password(LDAP *ld,char *objectDN, char *pwd, size_t *pwdSize);
-
-#endif
-
-typedef struct ldap_acct_section {
-       CONF_SECTION    *cs;
-       
-       const char *reference;
-} ldap_acct_section_t;
-
-
-typedef struct {
-       CONF_SECTION    *cs;
-       fr_connection_pool_t *pool;
-
-       char            *server;
-       int             port;
-
-       char            *login;
-       char            *password;
-
-       char            *filter;
-       char            *basedn;
-
-       int             chase_referrals;
-       int             rebind;
-
-       int             ldap_debug; //!< Debug flag for the SDK.
-
-       const char      *xlat_name; //!< Instance name.
-
-       int             expect_password;
-       
-       /*
-        *      RADIUS attribute to LDAP attribute maps
-        */
-       value_pair_map_t *user_map; //!< Attribute map applied to users and
-                                   //!< profiles.
-       
-       /*
-        *      Access related configuration
-        */
-       char            *access_attr;
-       int             positive_access_attr;
-
-       /*
-        *      Profiles
-        */
-       char            *base_filter;
-       char            *default_profile;
-       char            *profile_attr;
-
-       /*
-        *      Group checking.
-        */
-       char            *groupname_attr;
-       char            *groupmemb_filter;
-       char            *groupmemb_attr;
-       
-       /*
-        *      Accounting
-        */
-       ldap_acct_section_t *postauth;
-       ldap_acct_section_t *accounting;
-
-       /*
-        *      TLS items.  We should really normalize these with the
-        *      TLS code in 3.0.
-        */
-       int             tls_mode;
-       int             start_tls;
-       char            *tls_cacertfile;
-       char            *tls_cacertdir;
-       char            *tls_certfile;
-       char            *tls_keyfile;
-       char            *tls_randfile;
-       char            *tls_require_cert;
-
-       /*
-        *      Options
-        */
-       int             timelimit;
-       int             net_timeout;
-       int             timeout;
-       int             is_url;
+#include       "ldap.h"
 
 #ifdef WITH_EDIR
-       /*
-        *      eDir support
-        */
-       int             edir;
-       int             edir_autz;
-#endif
-       /*
-        *      For keep-alives.
-        */
-#ifdef LDAP_OPT_X_KEEPALIVE_IDLE
-       int             keepalive_idle;
-#endif
-#ifdef LDAP_OPT_X_KEEPALIVE_PROBES
-       int             keepalive_probes;
+extern int nmasldap_get_password(LDAP *ld, char *objectDN, char *pwd,
+                                size_t *pwdSize);
 #endif
-#ifdef LDAP_OPT_ERROR_NUMBER
-       int             keepalive_interval;
-#endif
-
-}  ldap_instance;
-
-/* The default setting for TLS Certificate Verification */
-#define TLS_DEFAULT_VERIFY "allow"
 
 /*
  *     TLS Configuration
  */
 static CONF_PARSER tls_config[] = {
-       {"start_tls", PW_TYPE_BOOLEAN,
-        offsetof(ldap_instance,start_tls), NULL, "no"},
-       {"cacertfile", PW_TYPE_FILENAME,
-        offsetof(ldap_instance,tls_cacertfile), NULL, NULL},
-       {"cacertdir", PW_TYPE_FILENAME,
-        offsetof(ldap_instance,tls_cacertdir), NULL, NULL},
-       {"certfile", PW_TYPE_FILENAME,
-        offsetof(ldap_instance,tls_certfile), NULL, NULL},
-       {"keyfile", PW_TYPE_FILENAME,
-        offsetof(ldap_instance,tls_keyfile), NULL, NULL},
-       {"randfile", PW_TYPE_STRING_PTR, /* OK if it changes on HUP */
-        offsetof(ldap_instance,tls_randfile), NULL, NULL},
-       {"require_cert", PW_TYPE_STRING_PTR,
-        offsetof(ldap_instance,tls_require_cert), NULL, TLS_DEFAULT_VERIFY},
+       {"start_tls", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, start_tls), NULL, "no"},
+       {"cacertfile", PW_TYPE_FILENAME, offsetof(ldap_instance_t, tls_cacertfile), NULL, NULL},
+       {"cacertdir", PW_TYPE_FILENAME, offsetof(ldap_instance_t, tls_cacertdir), NULL, NULL},
+       {"certfile", PW_TYPE_FILENAME, offsetof(ldap_instance_t, tls_certfile), NULL, NULL},
+       {"keyfile", PW_TYPE_FILENAME, offsetof(ldap_instance_t, tls_keyfile), NULL, NULL}, // OK if it changes on HUP
+       {"randfile", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, tls_randfile), NULL, NULL},
+       {"require_cert",PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, tls_require_cert), NULL, TLS_DEFAULT_VERIFY},
        { NULL, -1, 0, NULL, NULL }
 };
 
-
+/*
+ *     Access limitations
+ */
 static CONF_PARSER attr_config[] = {
-       /*
-        *      Access limitations
-        */
        /* LDAP attribute name that controls remote access */
-       {"access_attr", PW_TYPE_STRING_PTR,
-        offsetof(ldap_instance,access_attr), NULL, NULL},
-       {"positive_access_attr", PW_TYPE_BOOLEAN,
-        offsetof(ldap_instance,positive_access_attr), NULL, "yes"},
-
-       {"base_filter", PW_TYPE_STRING_PTR,
-        offsetof(ldap_instance,base_filter), NULL,
-        "(objectclass=radiusprofile)"},
-       {"default_profile", PW_TYPE_STRING_PTR,
-        offsetof(ldap_instance,default_profile), NULL, NULL},
-       {"profile_attribute", PW_TYPE_STRING_PTR,
-        offsetof(ldap_instance,profile_attr), NULL, NULL},
+       {"userobj_access_attr", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,userobj_access_attr), NULL, NULL},
+       {"access_positive", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,access_positive), NULL, "yes"},
+       {"base_filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,base_filter), NULL, "(objectclass=radiusprofile)"},
+       {"default_profile", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,default_profile), NULL, NULL},
+       {"profile_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,profile_attr), NULL, NULL},
 
        { NULL, -1, 0, NULL, NULL }
 };
 
-
 /*
  *     Group configuration
  */
 static CONF_PARSER group_config[] = {
        /*
-        *      Group checks.  These could probably be done
-        *      via dynamic xlat's.
+        *      Group checks.  These could probably be done via dynamic xlat's.
         */
-       {"name_attribute", PW_TYPE_STRING_PTR,
-        offsetof(ldap_instance,groupname_attr), NULL, "cn"},
-       {"membership_filter", PW_TYPE_STRING_PTR,
-        offsetof(ldap_instance,groupmemb_filter), NULL,
-        "(|(&(objectClass=GroupOfNames)(member=%{Ldap-UserDn}))"
-        "(&(objectClass=GroupOfUniqueNames)(uniquemember=%{Ldap-UserDn})))"},
-       {"membership_attribute", PW_TYPE_STRING_PTR,
-        offsetof(ldap_instance,groupmemb_attr), NULL, NULL},
-
-
+       {"name_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,groupobj_name_attr), NULL, "cn"},
+       {"membership_filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,groupobj_membership_filter), NULL,
+        "(|(&(objectClass=GroupOfNames)(member=%{Ldap-UserDn}))(&(objectClass=GroupOfUniqueNames)"
+        "(uniquemember=%{Ldap-UserDn})))"},
+       {"membership_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,userobj_membership_attr), NULL, NULL},
        { NULL, -1, 0, NULL, NULL }
 };
 
@@ -220,8 +88,7 @@ static CONF_PARSER group_config[] = {
  *     Reference for accounting updates
  */
 static const CONF_PARSER acct_section_config[] = {
-       {"reference", PW_TYPE_STRING_PTR,
-         offsetof(ldap_acct_section_t, reference), NULL, "."},
+       {"reference", PW_TYPE_STRING_PTR, offsetof(ldap_acct_section_t, reference), NULL, "."},
        {NULL, -1, 0, NULL, NULL}
 };
 
@@ -234,78 +101,58 @@ static CONF_PARSER option_config[] = {
        /*
         *      Debugging flags to the server
         */
-       {"ldap_debug", PW_TYPE_INTEGER,
-        offsetof(ldap_instance,ldap_debug), NULL, "0x0000"},
+       {"ldap_debug", PW_TYPE_INTEGER, offsetof(ldap_instance_t,ldap_debug), NULL, "0x0000"},
 
-       {"chase_referrals", PW_TYPE_BOOLEAN,
-        offsetof(ldap_instance,chase_referrals), NULL, NULL},
+       {"chase_referrals", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,chase_referrals), NULL, NULL},
 
-       {"rebind", PW_TYPE_BOOLEAN,
-        offsetof(ldap_instance,rebind), NULL, NULL},
+       {"rebind", PW_TYPE_BOOLEAN,offsetof(ldap_instance_t,rebind), NULL, NULL},
 
        /* timeout on network activity */
-       {"net_timeout", PW_TYPE_INTEGER,
-        offsetof(ldap_instance,net_timeout), NULL, "10"},
+       {"net_timeout", PW_TYPE_INTEGER, offsetof(ldap_instance_t,net_timeout), NULL, "10"},
 
        /* timeout for search results */
-       {"timeout", PW_TYPE_INTEGER,
-        offsetof(ldap_instance,timeout), NULL, "20"},
+       {"timeout", PW_TYPE_INTEGER, offsetof(ldap_instance_t,timeout), NULL, "20"},
 
        /* allow server unlimited time for search (server-side limit) */
-       {"timelimit", PW_TYPE_INTEGER,
-        offsetof(ldap_instance,timelimit), NULL, "20"},
+       {"timelimit", PW_TYPE_INTEGER, offsetof(ldap_instance_t,timelimit), NULL, "20"},
 
 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
-       {"idle", PW_TYPE_INTEGER,
-        offsetof(ldap_instance,keepalive_idle), NULL, "60"},
+       {"idle", PW_TYPE_INTEGER, offsetof(ldap_instance_t,keepalive_idle), NULL, "60"},
 #endif
 #ifdef LDAP_OPT_X_KEEPALIVE_PROBES
-       {"probes", PW_TYPE_INTEGER,
-        offsetof(ldap_instance,keepalive_probes), NULL, "3"},
+       {"probes", PW_TYPE_INTEGER, offsetof(ldap_instance_t,keepalive_probes), NULL, "3"},
 #endif
 #ifdef LDAP_OPT_ERROR_NUMBER
-       {"interval", PW_TYPE_INTEGER, 
-        offsetof(ldap_instance,keepalive_interval), NULL, "30"},
+       {"interval", PW_TYPE_INTEGER,  offsetof(ldap_instance_t,keepalive_interval), NULL, "30"},
 #endif
        { NULL, -1, 0, NULL, NULL }
 };
 
 
 static const CONF_PARSER module_config[] = {
-       {"server", PW_TYPE_STRING_PTR,
-        offsetof(ldap_instance,server), NULL, "localhost"},
-       {"port", PW_TYPE_INTEGER,
-        offsetof(ldap_instance,port), NULL, "389"},
+       {"server", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,server), NULL, "localhost"},
+       {"port", PW_TYPE_INTEGER, offsetof(ldap_instance_t,port), NULL, "389"},
 
-       {"password", PW_TYPE_STRING_PTR,
-        offsetof(ldap_instance,password), NULL, ""},
-       {"identity", PW_TYPE_STRING_PTR,
-        offsetof(ldap_instance,login), NULL, ""},
+       {"password", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,password), NULL, ""},
+       {"identity", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,login), NULL, ""},
 
        /*
         *      DN's and filters.
         */
-       {"basedn", PW_TYPE_STRING_PTR,
-        offsetof(ldap_instance,basedn), NULL, "o=notexist"},
+       {"basedn", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,basedn), NULL, "o=notexist"},
 
-       {"filter", PW_TYPE_STRING_PTR,
-        offsetof(ldap_instance,filter), NULL, "(uid=%u)"},
+       {"filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,userobj_filter), NULL, "(uid=%u)"},
 
-       /* turn off the annoying warning if we don't expect a password */
-       {"expect_password", PW_TYPE_BOOLEAN,
-        offsetof(ldap_instance,expect_password), NULL, "yes"},
-        
 #ifdef WITH_EDIR
        /* support for eDirectory Universal Password */
        {"edir", PW_TYPE_BOOLEAN,
-        offsetof(ldap_instance,edir), NULL, NULL}, /* NULL defaults to "no" */
+        offsetof(ldap_instance_t,edir), NULL, NULL}, /* NULL defaults to "no" */
 
        /*
         * Attempt to bind with the Cleartext password we got from eDirectory
         * Universal password for additional authorization checks.
         */
-       {"edir_autz", PW_TYPE_BOOLEAN,
-        offsetof(ldap_instance,edir_autz), NULL, NULL}, /* NULL defaults to "no" */
+       {"edir_autz", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,edir_autz), NULL, NULL}, /* NULL defaults to "no" */
 #endif
 
        /*
@@ -323,636 +170,28 @@ static const CONF_PARSER module_config[] = {
        {NULL, -1, 0, NULL, NULL}
 };
 
-typedef struct ldap_conn {
-       LDAP    *handle;
-       int     rebound;
-       int     referred;
-       ldap_instance *inst;
-} LDAP_CONN;
-
-typedef struct xlat_attrs {
-       const value_pair_map_t *maps;
-       const char *attrs[MAX_ATTRMAP];
-} xlat_attrs_t;
-
-typedef struct rlm_ldap_result {
-       char    **values;
-       int     count;
-} rlm_ldap_result_t;
-
-typedef enum {
-       LDAP_PROC_SUCCESS = 0,
-       LDAP_PROC_ERROR = -1,
-       LDAP_PROC_RETRY = -2,
-       LDAP_PROC_NOTPERMITTED = -3,
-       LDAP_PROC_REJECT = -4
-} ldap_rcode_t;
-
-static ldap_rcode_t process_ldap_errno(ldap_instance *inst, 
-                                      const LDAP_CONN *conn, 
-                                      const char **error)
-{
-       int ldap_errno;
-       
-       *error = NULL;
-       
-       ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
-                       &ldap_errno);
-       switch (ldap_errno) {
-       case LDAP_SUCCESS:
-       case LDAP_NO_SUCH_OBJECT:
-               return LDAP_PROC_SUCCESS;
-
-       case LDAP_INSUFFICIENT_ACCESS:
-               *error = "Insufficient access. Check the identity and password "
-                        "configuration directive";
-               
-               return LDAP_PROC_NOTPERMITTED;
-               
-       case LDAP_UNWILLING_TO_PERFORM:
-               *error = "Server was unwilling to perform";
-       
-               return LDAP_PROC_NOTPERMITTED;
-               
-       case LDAP_TIMEOUT:
-               exec_trigger(NULL, inst->cs, "modules.ldap.timeout", TRUE);
-               
-               *error = "Timed out while waiting for server to respond";
-                      
-               return LDAP_PROC_ERROR;
-
-       case LDAP_FILTER_ERROR:
-               *error = "Bad search filter";
-
-               return LDAP_PROC_ERROR;
-
-       case LDAP_TIMELIMIT_EXCEEDED:
-               exec_trigger(NULL, inst->cs, "modules.ldap.timeout", TRUE);
-               
-               *error = "Time limit exceeded";
-               /* FALL-THROUGH */
-
-       case LDAP_BUSY:
-       case LDAP_UNAVAILABLE:
-               /*
-                *      Reconnect.  There's an issue with the socket
-                *      or LDAP server.
-                */
-               *error = ldap_err2string(ldap_errno);
-               
-       case LDAP_SERVER_DOWN:
-               return LDAP_PROC_RETRY;
-               
-       case LDAP_INVALID_CREDENTIALS:
-       case LDAP_CONSTRAINT_VIOLATION:
-               *error = ldap_err2string(ldap_errno);
-       
-               return LDAP_PROC_REJECT;
-
-       case LDAP_OPERATIONS_ERROR:
-               *error = "Please set 'chase_referrals=yes' and 'rebind=yes'. "
-                        "See the ldap module configuration for details.";
-               /* FALL-THROUGH */
-
-       default:
-               *error = ldap_err2string(ldap_errno);
-
-               return LDAP_PROC_ERROR;
-       }
-}
-
-
-static int ldap_bind_wrapper(REQUEST *request, LDAP_CONN **pconn, 
-                            const char *user, const char *password,
-                            int retry)
-{
-       int             rcode, msg_id;
-       int             module_rcode = RLM_MODULE_OK;
-       LDAP_CONN       *conn = *pconn;
-       ldap_instance   *inst = conn->inst;
-       LDAPMessage     *result = NULL;
-       const char      *error = NULL;
-       char            *ext_error = NULL;
-       struct timeval tv;
-
-retry:
-       msg_id = ldap_bind(conn->handle, user, password, LDAP_AUTH_SIMPLE);
-       if (msg_id < 0) goto get_error;
-
-       DEBUG3("rlm_ldap (%s): Waiting for bind result...", inst->xlat_name);
-
-       tv.tv_sec = inst->timeout;
-       tv.tv_usec = 0;
-
-       rcode = ldap_result(conn->handle, msg_id, 1, &tv, &result);
-       if (rcode > 0) {
-               rcode = ldap_parse_result(conn->handle, result, NULL, NULL,
-                                         &ext_error, NULL, NULL, 1);
-       }
-
-get_error:
-       switch (process_ldap_errno(inst, conn, &error))
-       {
-       case LDAP_PROC_SUCCESS:
-               break;
-       case LDAP_PROC_NOTPERMITTED:
-               if (request) {
-                       RDEBUGE("Bind was not permitted (%s): %s", error,
-                               ext_error ? ext_error : 
-                               "no additional information");
-               } else {
-                       radlog(L_ERR, "rlm_ldap (%s): Bind was not permitted "
-                              "(%s): %s", inst->xlat_name, 
-                              error, ext_error ? ext_error : 
-                              "no additional information");            
-               }
-               
-               module_rcode = RLM_MODULE_USERLOCK;
-               
-               break;
-       case LDAP_PROC_REJECT:  
-               if (request) {
-                       RDEBUGE("Bind credentials incorrect (%s): %s", error,
-                               ext_error ? ext_error : 
-                               "no additional information");
-               } else {
-                       radlog(L_ERR, "rlm_ldap (%s): Bind credentials "
-                              "incorrect (%s): %s", inst->xlat_name, 
-                              error, ext_error ? ext_error : 
-                              "no additional information");
-               }
-                      
-               module_rcode = RLM_MODULE_REJECT;
-               
-               break;
-       case LDAP_PROC_ERROR:
-               module_rcode = RLM_MODULE_FAIL;
-error:
-#ifdef HAVE_LDAP_INITIALIZE
-               if (inst->is_url) {
-                       radlog(L_ERR, "rlm_ldap (%s): bind "
-                              "with %s to %s failed: %s",
-                              inst->xlat_name, user,
-                              inst->server, error);
-               } else
-#endif
-               {
-                       radlog(L_ERR, "rlm_ldap (%s): bind "
-                              "with %s to %s:%d failed: %s",
-                              inst->xlat_name, user,
-                              inst->server, inst->port, error);
-               }
-
-               break;
-       case LDAP_PROC_RETRY:
-               if (retry) {
-                       radlog(L_ERR, "rlm_ldap (%s): bind "
-                              "with %s to %s:%d failed, reconnecting: %s",
-                              inst->xlat_name, user,
-                              inst->server, inst->port, error);
-                       ldap_memfree(ext_error);
-                       ext_error = NULL;
-                       
-                       *pconn = fr_connection_reconnect(inst->pool, *pconn);
-                       if (*pconn) goto retry;
-               } else goto error;
-               
-               module_rcode = RLM_MODULE_FAIL;
-               break;
-       }       
-
-       ldap_memfree(ext_error);
-
-       return module_rcode; /* caller closes the connection */
-}
-
-#if LDAP_SET_REBIND_PROC_ARGS == 3
-/*
- *     Rebind && chase referral stuff
- */
-static int ldap_rebind(LDAP *handle, LDAP_CONST char *url,
-                      UNUSED ber_tag_t request, UNUSED ber_int_t msgid,
-                      void *ctx )
-{
-       int rcode, ldap_errno;
-       LDAP_CONN *conn = ctx;
-
-       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);
-       
-
-       rcode = ldap_bind_wrapper(NULL, &conn, conn->inst->login,
-                                 conn->inst->password, FALSE);
-       
-       if (rcode == RLM_MODULE_OK) {
-               return LDAP_SUCCESS;
-       }
-       
-       ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
-                       
-       return ldap_errno;
-}
-#endif
-
-/** Create and return a new connection
- * This function is probably too big.
- */
-static void *ldap_conn_create(void *ctx)
-{
-       int module_rcode;
-       int ldap_errno, ldap_version;
-       struct timeval tv;
-       ldap_instance *inst = ctx;
-       LDAP *handle = NULL;
-       LDAP_CONN *conn = NULL;
-
-#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);
-               if (ldap_errno != LDAP_SUCCESS) {
-                       radlog(L_ERR, "rlm_ldap (%s): ldap_initialize() "
-                              "failed: %s",
-                              inst->xlat_name, ldap_err2string(ldap_errno));
-                       goto conn_fail;
-               }
-       } else
-#endif
-       {
-               DEBUG("rlm_ldap (%s): Connecting to %s:%d", inst->xlat_name,
-                     inst->server, inst->port);
-
-               handle = ldap_init(inst->server, inst->port);
-               if (!handle) {
-                       radlog(L_ERR, "rlm_ldap (%s): ldap_init() failed",
-                              inst->xlat_name);
-               conn_fail:
-                       if (handle) ldap_unbind_s(handle);
-                       return NULL;
-               }
-       }
-
-       /*
-        *      We now have a connection structure, but no actual TCP connection.
-        *
-        *      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); \
-               radlog(L_ERR, "rlm_ldap (%s): Could not set %s: %s", \
-                      inst->xlat_name, _name, ldap_err2string(ldap_errno)); \
-       }
-               
-       if (inst->ldap_debug) {
-               do_ldap_option(LDAP_OPT_DEBUG_LEVEL, "ldap_debug",
-                              &(inst->ldap_debug));
-       }
-
-       /*
-        *      Leave "chase_referrals" unset to use the OpenLDAP
-        *      default.
-        */
-       if (inst->chase_referrals != 2) {
-               if (inst->chase_referrals) {
-                       do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals",
-                                      LDAP_OPT_ON);
-                       
-                       if (inst->rebind == 1) {
-#if LDAP_SET_REBIND_PROC_ARGS == 3
-                               ldap_set_rebind_proc(handle, ldap_rebind, inst);
-#else
-                               DEBUGW("The flag 'rebind = yes' is not supported by the system LDAP library.  Ignoring.");
-#endif
-                       }
-               } else {
-                       do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals",
-                                      LDAP_OPT_OFF);
-               }
-       }
-
-       tv.tv_sec = inst->net_timeout;
-       tv.tv_usec = 0;
-       do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout", &tv);
-
-       do_ldap_option(LDAP_OPT_TIMELIMIT, "timelimit", &(inst->timelimit));
-
-       ldap_version = LDAP_VERSION3;
-       do_ldap_option(LDAP_OPT_PROTOCOL_VERSION, "ldap_version",
-                      &ldap_version);
-
-#ifdef LDAP_OPT_X_KEEPALIVE_IDLE
-       do_ldap_option(LDAP_OPT_X_KEEPALIVE_IDLE, "keepalive idle",
-                      &(inst->keepalive_idle));
-#endif
-
-#ifdef LDAP_OPT_X_KEEPALIVE_PROBES
-       do_ldap_option(LDAP_OPT_X_KEEPALIVE_PROBES, "keepalive probes",
-                      &(inst->keepalive_probes));
-#endif
-
-#ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL
-       do_ldap_option(LDAP_OPT_X_KEEPALIVE_INTERVAL, "keepalive interval",
-                      &(inst->keepalive_interval));
-#endif
-
-#ifdef HAVE_LDAP_START_TLS
-       /*
-        *      Set all of the TLS options
-        */
-       if (inst->tls_mode) {
-               do_ldap_option(LDAP_OPT_X_TLS, "tls_mode", &(inst->tls_mode));
-       }
-
-#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);
-
-#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);
-               radlog(L_ERR, "rlm_ldap (%s): could not set "
-                      "LDAP_OPT_X_TLS_REQUIRE_CERT option to %s: %s",
-                      inst->xlat_name, 
-                      inst->tls_require_cert,
-                      ldap_err2string(ldap_errno));
-       }
-#endif
-
-       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);
-
-       /*
-        *      And finally start the TLS code.
-        */
-       if (inst->start_tls && (inst->port != 636)) {
-               ldap_errno = ldap_start_tls_s(handle, NULL, NULL);
-               if (ldap_errno != LDAP_SUCCESS) {
-                       ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER,
-                                       &ldap_errno);
-                       radlog(L_ERR, "rlm_ldap (%s): could not start TLS: %s",
-                              inst->xlat_name,
-                              ldap_err2string(ldap_errno));
-                       goto conn_fail;
-               }
-       }
-#endif /* HAVE_LDAP_START_TLS */
-
-       conn = talloc_zero(ctx, LDAP_CONN);
-       conn->inst = inst;
-       conn->handle = handle;
-       conn->rebound = FALSE;
-       conn->referred = FALSE;
-
-       module_rcode = ldap_bind_wrapper(NULL, &conn, inst->login,
-                                        inst->password, FALSE);
-       if (module_rcode != RLM_MODULE_OK) {
-               goto conn_fail;
-       }
-
-       return conn;
-}
-
-
-/** Close and delete a connection
- *
- */
-static int ldap_conn_delete(UNUSED void *ctx, void *connection)
-{
-       LDAP_CONN *conn = connection;
-
-       ldap_unbind_s(conn->handle);
-       talloc_free(conn);
-
-       return 0;
-}
-
-
-/** Gets an LDAP socket from the connection pool
- *
- */
-static LDAP_CONN *ldap_get_socket(ldap_instance *inst)
-{
-       LDAP_CONN *conn;
-
-       conn = fr_connection_get(inst->pool);
-       if (!conn) {
-               radlog(L_ERR, "rlm_ldap (%s): all ldap connections are in use",
-                      inst->xlat_name);
-               return NULL;
-       }
-
-       return conn;
-}
-
-/** Frees an LDAP socket back to the connection pool
- *
- */
-static void ldap_release_socket(ldap_instance *inst, LDAP_CONN *conn)
-{
-       /*
-        *      Could have already been free'd due to a previous error.
-        */
-       if (!conn) return;
-
-       /*
-        *      We chased a referral to another server.
-        *
-        *      This connection is no longer part of the pool which is
-        *      connected to and bound to the configured server.
-        *      Close it.
-        *
-        *      Note that we do NOT close it if it was bound to
-        *      another user.  Instead, we let the next caller do the
-        *      rebind.
-        */
-       if (conn->referred) {
-               fr_connection_del(inst->pool, conn);
-               return;
-       }
-
-       fr_connection_release(inst->pool, conn);
-       return;
-}
-
-
-/* Converts "bad" strings into ones which are safe for LDAP
- *
- */
-static size_t ldap_escape_func(UNUSED REQUEST *request, char *out,
-                              size_t outlen, const char *in, UNUSED void *arg)
-{
-       static const char encode[] = ",+\"\\<>;*=()";
-       static const char hextab[] = "0123456789abcdef";
-       size_t left = outlen;
-       
-       if (*in && ((*in == ' ') || (*in == '#'))) {
-               goto encode;
-       }
-       
-       while (*in) {
-               /*
-                *      Encode unsafe characters.
-                */
-               if (memchr(encode, *in, sizeof(encode) - 1)) {
-                       encode:
-
-                       /*
-                        *      Only 3 or less bytes available.
-                        */
-                       if (left <= 3) break;
-
-                       *out++ = '\\';
-                       *out++ = hextab[(*in >> 4) & 0x0f];
-                       *out++ = hextab[*in & 0x0f];
-                       in++;
-                       left -= 3;
-
-                       continue;
-               }
-
-               if (left <= 1) break;
-
-               /*
-                *      Doesn't need encoding
-                */
-               *out++ = *in++;
-               left--;
-       }
-       
-       *out = '\0';
-       
-       return outlen - left;
-}
-
-/** Do a search and get a response
- *
- */
-static int perform_search(ldap_instance *inst, REQUEST *request,
-                         LDAP_CONN **pconn, const char *search_basedn,
-                         int scope, const char *filter, 
-                         const char * const *attrs, LDAPMessage **presult)
-{
-       int             ldap_errno;
-       int             count = 0;
-       struct timeval  tv;
-       const char      *error = NULL;
-
-       /*
-        *      OpenLDAP library doesn't declare attrs array as const, but
-        *      it really should be *sigh*.
-        */
-       char **search_attrs;
-       memcpy(&search_attrs, &attrs, sizeof(attrs));
-
-       *presult = NULL;
-
-       /*
-        *      Do all searches as the default admin user.
-        */
-       if ((*pconn)->rebound) {
-               ldap_errno = ldap_bind_wrapper(request, pconn, inst->login,
-                                              inst->password, TRUE);
-               if (ldap_errno != RLM_MODULE_OK) {
-                       return -1;
-               }
-
-               rad_assert(*pconn);
-               (*pconn)->rebound = FALSE;
-       }
-
-       tv.tv_sec = inst->timeout;
-       tv.tv_usec = 0;
-       RDEBUG2("Performing search in '%s' with filter '%s'",
-               search_basedn ? search_basedn : "(null)" ,
-               filter);
-
-retry:
-       ldap_errno = ldap_search_ext_s((*pconn)->handle, search_basedn, scope,
-                                      filter, search_attrs, 0, NULL, NULL,
-                                      &tv, 0, presult);
-       if (ldap_errno != LDAP_SUCCESS) {
-               ldap_msgfree(*presult);
-               switch (process_ldap_errno(inst, *pconn, &error))
-               {
-                       case LDAP_PROC_SUCCESS:
-                               break;
-                       case LDAP_PROC_RETRY:
-                               radlog(L_ERR, "rlm_ldap (%s): Failed "
-                                      "performing search, reconnecting: %s",
-                                      inst->xlat_name, error);
-                                      
-                               *pconn = fr_connection_reconnect(inst->pool,
-                                                                *pconn);
-                               if (*pconn) goto retry;
-                               
-                               return -1;
-                       default:
-                               radlog(L_ERR, "rlm_ldap (%s): Failed "
-                                      "performing search: %s",
-                                      inst->xlat_name, error);
-
-                               return -1;
-               }
-       }
-               
-       count = ldap_count_entries((*pconn)->handle, *presult);
-       if (count == 0) {
-               ldap_msgfree(*presult);
-               RDEBUGE("Search returned no results");
-               
-               return -2;
-       }
-
-       if (count != 1) {
-               ldap_msgfree(*presult);
-               RDEBUGE("Got ambiguous search result (%d results)", count);
-                     
-               return -2;
-       }
-
-       return 0;
-}
-
 /** Expand an LDAP URL into a query, and return a string result from that query.
  *
  */
 static size_t ldap_xlat(void *instance, REQUEST *request, const char *fmt,
                        char *out, size_t freespace)
 {
-       int rcode;
+       ldap_rcode_t status;
        size_t length = 0;
-       ldap_instance *inst = instance;
+       ldap_instance_t *inst = instance;
        LDAPURLDesc *ldap_url;
        LDAPMessage *result = NULL;
        LDAPMessage *entry = NULL;
        char **vals;
-       LDAP_CONN *conn;
+       ldap_handle_t *conn;
        int ldap_errno;
        const char *url;
        const char **attrs;
-       char buffer[MAX_FILTER_STR_LEN];
+       char buffer[LDAP_MAX_FILTER_STR_LEN];
 
        if (strchr(fmt, '%') != NULL) {
-               if (!radius_xlat(buffer, sizeof(buffer), fmt, request,
-                                ldap_escape_func, NULL)) {
-                       radlog(L_ERR,
-                              "rlm_ldap (%s): Unable to create LDAP URL", 
-                              inst->xlat_name);
+               if (!radius_xlat(buffer, sizeof(buffer), fmt, request, rlm_ldap_escape_func, NULL)) {
+                       RDEBUGE("Unable to create LDAP URL");
                        return 0;
                }
                url = buffer;
@@ -961,14 +200,12 @@ static size_t ldap_xlat(void *instance, REQUEST *request, const char *fmt,
        }
 
        if (!ldap_is_ldap_url(url)) {
-               radlog(L_ERR, "rlm_ldap (%s): String passed does not look "
-                      "like an LDAP URL", inst->xlat_name);
+               RDEBUGE("String passed does not look like an LDAP URL");
                return 0;
        }
 
        if (ldap_url_parse(url, &ldap_url)){
-               radlog(L_ERR, "rlm_ldap (%s): Parsing LDAP URL failed",
-                      inst->xlat_name);
+               RDEBUGE("Parsing LDAP URL failed");
                return 0;
        }
 
@@ -979,55 +216,50 @@ static size_t ldap_xlat(void *instance, REQUEST *request, const char *fmt,
            !*ldap_url->lud_attrs[0] ||
            (strcmp(ldap_url->lud_attrs[0], "*") == 0) ||
            ldap_url->lud_attrs[1]) {
-               radlog(L_ERR, "rlm_ldap (%s): Bad attributes list in LDAP "
-                      "URL. URL must specify exactly one attribute to "
-                      "retrieve",
-                      inst->xlat_name);
+               RDEBUGE("Bad attributes list in LDAP URL. "
+                       "URL must specify exactly one attribute to "
+                       "retrieve");
                       
                goto free_urldesc;
        }
 
-       if (ldap_url->lud_host &&
-           ((strncmp(inst->server, ldap_url->lud_host,
-                     strlen(inst->server)) != 0) ||
+       if (ldap_url->lud_host && 
+           ((strncmp(inst->server, ldap_url->lud_host, strlen(inst->server)) != 0) ||
             (ldap_url->lud_port != inst->port))) {
-               RDEBUG("Requested server/port is \"%s:%i\"", ldap_url->lud_host,
-                      inst->port);
+               RDEBUG("Requested server/port is \"%s:%i\"", ldap_url->lud_host, inst->port);
                
                goto free_urldesc;
        }
 
-       conn = ldap_get_socket(inst);
+       conn = rlm_ldap_get_socket(inst, request);
        if (!conn) goto free_urldesc;
 
        memcpy(&attrs, &ldap_url->lud_attrs, sizeof(attrs));
        
-       rcode = perform_search(inst, request, &conn, ldap_url->lud_dn, 
-                              ldap_url->lud_scope, ldap_url->lud_filter, attrs,
-                              &result);
-       if (rcode < 0) {
-               if (rcode == -2) {
-                       RDEBUG("Search returned not found", inst->xlat_name);
+       status = rlm_ldap_search(inst, request, &conn, ldap_url->lud_dn, ldap_url->lud_scope, ldap_url->lud_filter,
+                                attrs, &result);
+       switch (status) {
+               case LDAP_PROC_SUCCESS:
+                       break;
+               case LDAP_PROC_NO_RESULT:
+                       RDEBUG("Search returned not found");
+               default:
                        goto free_socket;
-               }
-
-               goto free_socket;
        }
 
+       rad_assert(conn);
+       rad_assert(result);
+
        entry = ldap_first_entry(conn->handle, result);
        if (!entry) {
-               ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE,
-                               &ldap_errno);
-               radlog(L_ERR, "rlm_ldap (%s): Failed retrieving entry: %s", 
-                      inst->xlat_name,
-                      ldap_err2string(ldap_errno));
+               ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
+               RDEBUGE("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
                goto free_result;
        }
 
        vals = ldap_get_values(conn->handle, entry, ldap_url->lud_attrs[0]);
        if (!vals) {
-               RDEBUG("No \"%s\" attributes found in specified object",
-                      inst->xlat_name, ldap_url->lud_attrs[0]);
+               RDEBUG("No \"%s\" attributes found in specified object", ldap_url->lud_attrs[0]);
                goto free_result;
        }
 
@@ -1044,125 +276,44 @@ free_vals:
 free_result:
        ldap_msgfree(result);
 free_socket:
-       ldap_release_socket(inst, conn);
+       rlm_ldap_release_socket(inst, conn);
 free_urldesc:
        ldap_free_urldesc(ldap_url);
 
        return length;
 }
 
-
-static char *get_userdn(LDAP_CONN **pconn, REQUEST *request,
-                       rlm_rcode_t *module_rcode)
-{
-       int             rcode;
-       VALUE_PAIR      *vp;
-       ldap_instance   *inst = (*pconn)->inst;
-       LDAPMessage     *result, *entry;
-       int             ldap_errno;
-       static char     firstattr[] = "uid";
-       char            *user_dn;
-       const char      *attrs[] = {firstattr, NULL};
-       char            filter[MAX_FILTER_STR_LEN];     
-       char            basedn[MAX_FILTER_STR_LEN];     
-
-       *module_rcode = RLM_MODULE_FAIL;
-
-       vp = pairfind(request->config_items, PW_LDAP_USERDN, 0, TAG_ANY);
-       if (vp) {
-               *module_rcode = RLM_MODULE_OK;
-               return vp->vp_strvalue;
-       }
-       
-       if (!radius_xlat(filter, sizeof(filter), inst->filter,
-                        request, ldap_escape_func, NULL)) {
-               radlog(L_ERR, "rlm_ldap (%s): Unable to create filter",
-                      inst->xlat_name);
-               *module_rcode = RLM_MODULE_INVALID;
-               return NULL;
-       }
-
-       if (!radius_xlat(basedn, sizeof(basedn), inst->basedn,
-                        request, ldap_escape_func, NULL)) {
-               radlog(L_ERR, "rlm_ldap (%s): Unable to create basedn",
-                      inst->xlat_name);
-               *module_rcode = RLM_MODULE_INVALID;
-               return NULL;
-       }
-
-       rcode = perform_search(inst, request, pconn, basedn, LDAP_SCOPE_SUBTREE,
-                              filter, attrs, &result);
-       if (rcode < 0) {
-               if (rcode == -2) {
-                       *module_rcode = RLM_MODULE_NOTFOUND;
-               }
-
-               return NULL;
-       }
-
-       if ((entry = ldap_first_entry((*pconn)->handle, result)) == NULL) {
-               ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE,
-                               &ldap_errno);
-               radlog(L_ERR, "rlm_ldap (%s): Failed retrieving entry: %s", 
-                      inst->xlat_name,
-                      ldap_err2string(ldap_errno));
-               ldap_msgfree(result);
-               return NULL;
-       }
-
-       if ((user_dn = ldap_get_dn((*pconn)->handle, entry)) == NULL) {
-               ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE,
-                               &ldap_errno);
-               radlog(L_ERR, "rlm_ldap (%s): ldap_get_dn() failed: %s",
-                      inst->xlat_name,
-                      ldap_err2string(ldap_errno));
-                      
-               ldap_msgfree(result);
-               return NULL;
-       }
-
-       vp = pairmake("LDAP-UserDn", user_dn, T_OP_EQ);
-       if (!vp) {
-               ldap_memfree(user_dn);
-               ldap_msgfree(result);
-               return NULL;
-       }
-       
-       *module_rcode = RLM_MODULE_OK;
-       
-       pairadd(&request->config_items, vp);
-       ldap_memfree(user_dn);
-       ldap_msgfree(result);
-
-       return vp->vp_strvalue;
-}
-
-
 /** Perform LDAP-Group comparison checking
  *
+ * Attempts to match users to groups using a variety of methods.
+ *
+ * @param instance of the rlm_ldap module.
+ * @param request Current request.
+ * @param thing Unknown.
+ * @param check Which group to check for user membership.
+ * @param check_pairs Unknown.
+ * @param reply_pairs Unknown.
+ * @return 1 on failure (or if the user is not a member), else 0.
  */
-static int ldap_groupcmp(void *instance, REQUEST *request,
-                        UNUSED VALUE_PAIR *thing, VALUE_PAIR *check,
-                        UNUSED VALUE_PAIR *check_pairs,
-                        UNUSED VALUE_PAIR **reply_pairs)
+static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *thing, VALUE_PAIR *check,
+                            UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
 {
-       ldap_instance   *inst = instance;
-       int             i, rcode, found;
-       rlm_rcode_t     module_rcode;
+       ldap_instance_t   *inst = instance;
+       rlm_rcode_t     rcode;
+       ldap_rcode_t    status;
+       int             i, found;
        LDAPMessage     *result = NULL;
        LDAPMessage     *entry = NULL;
        int             ldap_errno;
        int             check_is_dn = FALSE, value_is_dn = FALSE;
-       static char     firstattr[] = "dn";
-       const char      *attrs[] = {firstattr, NULL};
        char            **vals;
-       const char      *group_attrs[] = {inst->groupmemb_attr, NULL};
-       LDAP_CONN       *conn;
-       char            *user_dn;
+       const char      *group_attrs[] = {inst->userobj_membership_attr, NULL};
+       ldap_handle_t   *conn;
+       const char      *user_dn;
 
-       char            gr_filter[MAX_FILTER_STR_LEN];
-       char            filter[MAX_FILTER_STR_LEN];
-       char            basedn[MAX_FILTER_STR_LEN];
+       char            gr_filter[LDAP_MAX_FILTER_STR_LEN];
+       char            filter[LDAP_MAX_FILTER_STR_LEN];
+       char            basedn[LDAP_MAX_FILTER_STR_LEN];
 
        RDEBUG("Searching for user in group \"%s\"", check->vp_strvalue);
 
@@ -1171,116 +322,109 @@ static int ldap_groupcmp(void *instance, REQUEST *request,
                return 1;
        }
 
-       conn = ldap_get_socket(inst);
+       conn = rlm_ldap_get_socket(inst, request);
        if (!conn) return 1;
 
        /*
         *      This is used in the default membership filter.
         */
-       user_dn = get_userdn(&conn, request, &module_rcode);
+       user_dn = rlm_ldap_find_user(inst, request, &conn, NULL, NULL, &rcode);
        if (!user_dn) {
-               ldap_release_socket(inst, conn);
+               rlm_ldap_release_socket(inst, conn);
                return 1;
        }
-
-       if (!inst->groupmemb_filter) goto check_attr;
+       
+       rad_assert(conn);
+       
+       if (!inst->groupobj_membership_filter) goto check_attr;
 
        if (!radius_xlat(gr_filter, sizeof(gr_filter),
-                        inst->groupmemb_filter, request, ldap_escape_func,
+                        inst->groupobj_membership_filter, request, rlm_ldap_escape_func,
                         NULL)) {
-               radlog(L_ERR, "rlm_ldap (%s): Failed creating group filter",
-                      inst->xlat_name);
+               RDEBUGE("Failed creating group filter");
+
                return 1;
        }
 
        /*
         *      If it's a DN, use that.
         */
-       check_is_dn = strchr(check->vp_strvalue,',') == NULL ? FALSE : TRUE;
-       
+       check_is_dn = rlm_ldap_is_dn(check->vp_strvalue);
        if (check_is_dn) {
                strlcpy(filter, gr_filter, sizeof(filter));
                strlcpy(basedn, check->vp_strvalue, sizeof(basedn));    
        } else {
                snprintf(filter, sizeof(filter), "(&(%s=%s)%s)",
-                        inst->groupname_attr,
+                        inst->groupobj_name_attr,
                         check->vp_strvalue, gr_filter);
 
                /*
-                *      get_userdn does this, too.  Oh well.
+                *      rlm_ldap_find_user does this, too.  Oh well.
                 */
-               if (!radius_xlat(basedn, sizeof(basedn), inst->basedn,
-                                request, ldap_escape_func, NULL)) {
-                       radlog(L_ERR, "rlm_ldap (%s): Failed creating basedn",
-                              inst->xlat_name);
+               if (!radius_xlat(basedn, sizeof(basedn), inst->basedn, request, rlm_ldap_escape_func, NULL)) {
+                       RDEBUGE("Failed creating basedn");
+                       
                        return 1;
                }
        }
 
-       rcode = perform_search(inst, request, &conn, basedn, LDAP_SCOPE_SUBTREE,
-                              filter, attrs, &result);
-       if (rcode == 0) {
-               ldap_release_socket(inst, conn);
-               ldap_msgfree(result);
-                       
-               RDEBUG("User found in group object");
-               
-               return 0;
-       }
-
-       if (rcode == -1) {
-               ldap_release_socket(inst, conn);
-               return 1;
+       status = rlm_ldap_search(inst, request, &conn, basedn, LDAP_SCOPE_SUBTREE, filter, NULL, NULL);
+       switch (status) {
+               case LDAP_PROC_SUCCESS:
+                       RDEBUG("User found in group object");
+                       found = TRUE;
+                       goto finish;
+               case LDAP_PROC_NO_RESULT:
+                       RDEBUG("Search returned not found");
+                       goto check_attr;
+               default:
+                       goto finish;
        }
-
-       /* else the search returned -2, for "not found" */
+       
+       rad_assert(conn);
+       rad_assert(result);
 
        /*
         *      Else the search returned NOTFOUND.  See if we're
         *      configured to search for group membership using user
         *      object attribute.
         */
-       if (!inst->groupmemb_attr) {
-               ldap_release_socket(inst, conn);
-               RDEBUG("Group object \"%s\" not found, or user is not a member",
-                      check->vp_strvalue);
+       if (!inst->userobj_membership_attr) {
+               rlm_ldap_release_socket(inst, conn);
+               RDEBUG("Group object \"%s\" not found, or user is not a member", check->vp_strvalue);
                return 1;
        }
 
 check_attr:
-       RDEBUG2("Checking user object membership (%s) attributes",
-               inst->groupmemb_attr);
+       RDEBUG2("Checking user object membership (%s) attributes", inst->userobj_membership_attr);
 
        snprintf(filter ,sizeof(filter), "(objectclass=*)");
 
-       rcode = perform_search(inst, request, &conn, user_dn, LDAP_SCOPE_BASE,
-                              filter, group_attrs, &result);
-       if (rcode < 0) {
-               if (rcode == -2) {
-                       RDEBUG("Can't check membership attributes, user object "
-                              "not found");
-               }
-               ldap_release_socket(inst, conn);
-               return 1;
+       status = rlm_ldap_search(inst, request, &conn, user_dn, LDAP_SCOPE_BASE, filter, group_attrs, &result);
+       switch (status) {
+               case LDAP_PROC_SUCCESS:
+                       break;
+               case LDAP_PROC_NO_RESULT:
+                       RDEBUG("Can't check membership attributes, user object not found");
+               default:
+                       rlm_ldap_release_socket(inst, conn);
+                       return 1;
        }
 
        entry = ldap_first_entry(conn->handle, result);
        if (!entry) {
-               ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE,
-                               &ldap_errno);
-               radlog(L_ERR, "rlm_ldap (%s): Failed retrieving entry: %s", 
-                      inst->xlat_name,
-                      ldap_err2string(ldap_errno));
+               ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
+               RDEBUGE("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
                               
-               ldap_release_socket(inst, conn);
+               rlm_ldap_release_socket(inst, conn);
                ldap_msgfree(result);
                return 1;
        }
 
-       vals = ldap_get_values(conn->handle, entry, inst->groupmemb_attr);
+       vals = ldap_get_values(conn->handle, entry, inst->userobj_membership_attr);
        if (!vals) {
                RDEBUG("No group membership attribute(s) found in user object");
-               ldap_release_socket(inst, conn);
+               rlm_ldap_release_socket(inst, conn);
                ldap_msgfree(result);
                return 1;
        }
@@ -1291,9 +435,7 @@ check_attr:
         */
        found = FALSE;
        for (i = 0; i < ldap_count_values(vals); i++) {
-               LDAPMessage *gr_result = NULL;
-               
-               value_is_dn = strchr(vals[i], ',') == NULL ? FALSE : TRUE;
+               value_is_dn = rlm_ldap_is_dn(vals[i]);
                
                RDEBUG2("Processing group membership value \"%s\"", vals[i]);
 
@@ -1302,8 +444,7 @@ check_attr:
                 */
                if (!check_is_dn && !value_is_dn) {
                        if (strcmp(vals[i], check->vp_strvalue) == 0){
-                               RDEBUG("User found (membership value matches "
-                                      "check value)");
+                               RDEBUG("User found (membership value matches check value)");
                               
                                found = TRUE;
                                break;
@@ -1317,8 +458,7 @@ check_attr:
                 */
                if (check_is_dn && value_is_dn) {
                        if (strcasecmp(vals[i], check->vp_strvalue) == 0){
-                               RDEBUG("User found (membership DN matches "
-                                      "check DN)");
+                               RDEBUG("User found (membership DN matches check DN)");
                               
                                found = TRUE;
                                break;
@@ -1336,48 +476,34 @@ check_attr:
                /*
                 *      We have a value which is a DN, and a check item which
                 *      specifies the name of a group, search using the value
-                *      DN for the group, and see if it has a groupname_attr
+                *      DN for the group, and see if it has a groupobj_name_attr
                 *      which matches our check val.
                 */
                RDEBUG2("Searching with membership DN and group name");
 
-               snprintf(filter,sizeof(filter), "(%s=%s)",
-                        inst->groupname_attr, check->vp_strvalue);
+               snprintf(filter,sizeof(filter), "(%s=%s)", inst->groupobj_name_attr, check->vp_strvalue);
 
-               rcode = perform_search(inst, request, &conn, vals[i],
-                                      LDAP_SCOPE_BASE, filter, attrs,
-                                      &gr_result);
-                                      
-               ldap_msgfree(gr_result);
-
-               /* Error occurred */
-               if (rcode == -1) {
-                       ldap_value_free(vals);
-                       ldap_msgfree(result);
-                       ldap_release_socket(inst, conn);
-                       return 1;
-               }
-               
-               /*
-                *      Either the group DN wasn't found, or it didn't have the
-                *      correct name. Continue looping over the attributes.
-                */
-               if (rcode == -2) {
-                       ldap_msgfree(gr_result);
-                       continue;
+               status = rlm_ldap_search(inst, request, &conn, vals[i], LDAP_SCOPE_BASE, filter, NULL, NULL);       
+               switch (status) {
+                       case LDAP_PROC_SUCCESS:
+                               found = TRUE;
+                               RDEBUG("User found (group name in membership DN matches check value)");
+                               
+                               break;
+                       case LDAP_PROC_NO_RESULT:
+                               continue;
+                       default:
+                               goto finish;
                }
 
-               found = TRUE;
-
-               RDEBUG("User found (group name in membership DN matches check "
-                      "value)");
-
                break;
        }
 
+       finish:
+       
        ldap_value_free(vals);
        ldap_msgfree(result);
-       ldap_release_socket(inst, conn);
+       rlm_ldap_release_socket(inst, conn);
 
        if (!found) {
                RDEBUG("User is not a member of specified group");
@@ -1392,7 +518,7 @@ check_attr:
  */
 static int ldap_detach(void *instance)
 {
-       ldap_instance *inst = instance;
+       ldap_instance_t *inst = instance;
        
        fr_connection_pool_delete(inst->pool);
 
@@ -1403,9 +529,14 @@ static int ldap_detach(void *instance)
        return 0;
 }
 
-static int parse_sub_section(CONF_SECTION *parent, 
-                            ldap_instance *inst,
-                            ldap_acct_section_t **config,
+/** Parse an accounting sub section.
+ *
+ * Allocate a new ldap_acct_section_t and write the config data into it.
+ *
+ * @param[in] parent of the config section.
+ * @param[out] 
+ */
+static int parse_sub_section(ldap_instance_t *inst, CONF_SECTION *parent, ldap_acct_section_t **config,
                             rlm_components_t comp)
 {
        CONF_SECTION *cs;
@@ -1414,81 +545,21 @@ static int parse_sub_section(CONF_SECTION *parent,
        
        cs = cf_section_sub_find(parent, name);
        if (!cs) {
-               radlog(L_INFO, "rlm_ldap (%s): Couldn't find configuration for "
-                      "%s, will return NOOP for calls from this section",
-                      inst->xlat_name, name);
+               radlog(L_INFO, "rlm_ldap (%s): Couldn't find configuration for %s, will return NOOP for calls "
+                      "from this section", inst->xlat_name, name);
                
                return 0;
-       }
-       
-       *config = talloc_zero(inst, ldap_acct_section_t);
-       if (cf_section_parse(cs, *config, acct_section_config) < 0) {
-               radlog(L_ERR, "rlm_ldap (%s): Failed parsing configuration for "
-                      "section %s", inst->xlat_name, name);
-               return -1;
-       }
-               
-       (*config)->cs = cs;
-
-       return 0;
-}
-
-static int ldap_map_verify(ldap_instance *inst, value_pair_map_t **head)
-{
-       value_pair_map_t *map;
+       }
        
-       if (radius_attrmap(inst->cs, head, PAIR_LIST_REPLY,
-                          PAIR_LIST_REQUEST, MAX_ATTRMAP) < 0) {
+       *config = talloc_zero(inst, ldap_acct_section_t);
+       if (cf_section_parse(cs, *config, acct_section_config) < 0) {
+               LDAP_ERR("Failed parsing configuration for section %s", name);
+               
                return -1;
        }
-       /*
-        *      Attrmap only performs some basic validation checks, we need
-        *      to do rlm_ldap specific checks here.
-        */
-       for (map = *head; map != NULL; map = map->next) {
-               if (map->dst->type != VPT_TYPE_ATTR) {
-                       cf_log_err(map->ci, "Left operand must be an "
-                                    "attribute ref");
-                       
-                       return -1;
-               }
-               
-               if (map->src->type == VPT_TYPE_LIST) {
-                       cf_log_err(map->ci, "Right operand must not be "
-                                    "a list");
-                       
-                       return -1;
-               }
-               
-               switch (map->src->type) 
-               {
-               /*
-                *      Only =, :=, += and -= operators are supported for
-                *      cache entries.
-                */
-               case VPT_TYPE_LITERAL:
-               case VPT_TYPE_XLAT:
-               case VPT_TYPE_ATTR:
-                       switch (map->op) {
-                       case T_OP_SET:
-                       case T_OP_EQ:
-                       case T_OP_SUB:
-                       case T_OP_ADD:
-                               break;
                
-                       default:
-                               cf_log_err(map->ci, "Operator \"%s\" not "
-                                          "allowed for %s values",
-                                          fr_int2str(fr_tokens, map->op,
-                                                     "¿unknown?"),
-                                          fr_int2str(vpt_types, map->src->type,
-                                                     "¿unknown?"));
-                               return -1;
-                       }
-               default:
-                       break;
-               }
-       }
+       (*config)->cs = cs;
+
        return 0;
 }
 
@@ -1498,9 +569,9 @@ static int ldap_map_verify(ldap_instance *inst, value_pair_map_t **head)
  */
 static int ldap_instantiate(CONF_SECTION * conf, void **instance)
 {
-       ldap_instance *inst;
+       ldap_instance_t *inst;
 
-       *instance = inst = talloc_zero(conf, ldap_instance);
+       *instance = inst = talloc_zero(conf, ldap_instance_t);
        if (!inst) return -1;
 
        inst->cs = conf;
@@ -1517,26 +588,21 @@ static int ldap_instantiate(CONF_SECTION * conf, void **instance)
         *      If the configuration parameters can't be parsed, then fail.
         */
        if ((cf_section_parse(conf, inst, module_config) < 0) ||
-           (parse_sub_section(conf, inst,
-                              &inst->accounting,
-                              RLM_COMPONENT_ACCT) < 0) ||
-           (parse_sub_section(conf, inst,
-                              &inst->postauth,
-                              RLM_COMPONENT_POST_AUTH) < 0)) {
-               radlog(L_ERR, "rlm_ldap (%s): Failed parsing configuration",
-                      inst->xlat_name);
+           (parse_sub_section(inst, conf, &inst->accounting, RLM_COMPONENT_ACCT) < 0) ||
+           (parse_sub_section(inst, conf, &inst->postauth, RLM_COMPONENT_POST_AUTH) < 0)) {
+               LDAP_ERR("Failed parsing configuration");
+               
                goto error;
        }
 
-       if (inst->server == NULL) {
-               radlog(L_ERR, "rlm_ldap (%s): Missing 'server' directive",
-                      inst->xlat_name);
+       if (!inst->server) {
+               LDAP_ERR("Missing 'server' directive");
+               
                goto error;
        }
 
        /*
-        *      Check for URLs.  If they're used and the library doesn't
-        *      support them, then complain.
+        *      Check for URLs.  If they're used and the library doesn't support them, then complain.
         */
        inst->is_url = 0;
        if (ldap_is_ldap_url(inst->server)) {
@@ -1544,14 +610,14 @@ static int ldap_instantiate(CONF_SECTION * conf, void **instance)
                inst->is_url = 1;
                inst->port = 0;
 #else
-               radlog(L_ERR, "rlm_ldap (%s): 'server' directive is in URL "
-                      "form but ldap_initialize() is not available",
-                      inst->xlat_name);
+               LDAP_ERR("'server' directive is in URL form but ldap_initialize() is not available");
                goto error;
 #endif
        }
 
-       /* workaround for servers which support LDAPS but not START TLS */
+       /*
+        *      Workaround for servers which support LDAPS but not START TLS
+        */
        if (inst->port == LDAPS_PORT || inst->tls_mode) {
                inst->tls_mode = LDAP_OPT_X_TLS_HARD;
        } else {
@@ -1560,14 +626,13 @@ static int ldap_instantiate(CONF_SECTION * conf, void **instance)
 
 #if LDAP_SET_REBIND_PROC_ARGS != 3
        /*
-        *      The 2-argument rebind doesn't take an instance
-        *      variable.  Our rebind function needs the instance
+        *      The 2-argument rebind doesn't take an instance variable.  Our rebind function needs the instance
         *      variable for the username, password, etc.
         */
        if (inst->rebind == 1) {
-               radlog(L_ERR, "rlm_ldap (%s): Cannot use 'rebind' directive "
-                      "as this version of libldap does not support the API "
-                      "that we need", inst->xlat-name);
+               LDAP_ERR("Cannot use 'rebind' directive as this version of libldap does not support the API "
+                        "that we need");
+                        
                goto error;
        }
 #endif
@@ -1575,14 +640,14 @@ static int ldap_instantiate(CONF_SECTION * conf, void **instance)
        /*
         *      Build the attribute map
         */
-       if (ldap_map_verify(inst, &(inst->user_map)) < 0) {
+       if (rlm_ldap_map_verify(inst, &(inst->user_map)) < 0) {
                goto error;
        }
 
        /*
         *      Group comparison checks.
         */
-       paircompare_register(PW_LDAP_GROUP, PW_USER_NAME, ldap_groupcmp, inst); 
+       paircompare_register(PW_LDAP_GROUP, PW_USER_NAME, rlm_ldap_groupcmp, inst);     
        if (cf_section_name2(conf)) {
                const DICT_ATTR *da;
                ATTR_FLAGS flags;
@@ -1595,13 +660,12 @@ static int ldap_instantiate(CONF_SECTION * conf, void **instance)
                dict_addattr(buffer, -1, 0, PW_TYPE_STRING, flags);
                da = dict_attrbyname(buffer);
                if (!da) {
-                       radlog(L_ERR, "rlm_ldap (%s): Failed creating "
-                              "attribute %s", inst->xlat_name, buffer);
+                       LDAP_ERR("Failed creating attribute %s", buffer);
+                       
                        goto error;
                }
 
-               paircompare_register(da->attr, PW_USER_NAME, ldap_groupcmp,
-                                    inst);
+               paircompare_register(da->attr, PW_USER_NAME, rlm_ldap_groupcmp, inst);
        }
 
        xlat_register(inst->xlat_name, ldap_xlat, inst);
@@ -1609,10 +673,7 @@ static int ldap_instantiate(CONF_SECTION * conf, void **instance)
        /*
         *      Initialize the socket pool.
         */
-       inst->pool = fr_connection_pool_init(inst->cs, inst,
-                                            ldap_conn_create,
-                                            NULL,
-                                            ldap_conn_delete);
+       inst->pool = fr_connection_pool_init(inst->cs, inst, rlm_ldap_conn_create, NULL, rlm_ldap_conn_delete);
        if (!inst->pool) {
                ldap_detach(inst);
                return -1;
@@ -1625,304 +686,97 @@ error:
        return -1;
 }
 
-static int check_access(ldap_instance *inst, REQUEST* request, LDAP_CONN *conn,
-                       LDAPMessage *entry)
+/** Check the user's password against ldap database
+ *
+ */
+static rlm_rcode_t ldap_authenticate(void *instance, REQUEST *request)
 {
-       int rcode = -1;
-       char **vals = NULL;
-
-       vals = ldap_get_values(conn->handle, entry, inst->access_attr);
-       if (vals) {
-               if (inst->positive_access_attr) {
-                       if (strncmp(vals[0], "FALSE", 5) == 0) {
-                               RDEBUG("Dialup access disabled");
-
-                       } else {
-                               rcode = 0;
-                       }
-
-               } else {
-                       RDEBUG("\"%s\" attribute exists - access denied by"
-                              " default", inst->access_attr);
-               }
-
-               ldap_value_free(vals);
-
-       } else if (inst->positive_access_attr) {
-               RDEBUG("No %s attribute - access denied by default",
-                      inst->access_attr);
-
-       } else {
-               rcode = 0;
-       }
-
-       return rcode;
-}
-
+       rlm_rcode_t     rcode;
+       const char      *user_dn;
+       ldap_instance_t *inst = instance;
+       ldap_handle_t   *conn;
 
-static VALUE_PAIR *ldap_getvalue(REQUEST *request, const value_pair_map_t *map,
-                                void *ctx)
-{
-       rlm_ldap_result_t *self = ctx;
-       VALUE_PAIR *head, **tail, *vp;
-       int i;
-       
-       request = request;
-       
-       head = NULL;
-       tail = &head;
-       
        /*
-        *      Iterate over all the retrieved values,
-        *      don't try and be clever about changing operators
-        *      just use whatever was set in the attribute map. 
+        * Ensure that we're being passed a plain-text password, and not
+        * anything else.
         */
-       for (i = 0; i < self->count; i++) {
-               vp = pairalloc(NULL, map->dst->da);
-               rad_assert(vp);
-
-               pairparsevalue(vp, self->values[i]);
-               
-               *tail = vp;
-               tail = &(vp->next);
-       }
-       
-       return head;            
-}
-
-
-static void xlat_attrsfree(const xlat_attrs_t *expanded)
-{
-       const value_pair_map_t *map;
-       unsigned int total = 0;
-       
-       const char *name;
-       
-       for (map = expanded->maps; map != NULL; map = map->next)
-       {
-               name = expanded->attrs[total++];
-               if (!name) return;
-               
-               switch (map->src->type)
-               {
-               case VPT_TYPE_XLAT:             
-               case VPT_TYPE_ATTR:
-                       rad_cfree(name);
-                       break;
-               default:
-                       break;
-               }
-       }
-}
-
-
-static int xlat_attrs(REQUEST *request, const value_pair_map_t *maps,
-                     xlat_attrs_t *expanded)
-{
-       const value_pair_map_t *map;
-       unsigned int total = 0;
-       
-       size_t len;
-       char *buffer;
 
-       VALUE_PAIR *found, **from = NULL;
-       REQUEST *context;
-
-       for (map = maps; map != NULL; map = map->next)
-       {
-               switch (map->src->type)
-               {
-               case VPT_TYPE_XLAT:
-                       buffer = rad_malloc(MAX_ATTR_STR_LEN);
-                       len = radius_xlat(buffer, MAX_ATTR_STR_LEN,
-                                         map->src->name, request, NULL, NULL);
-                                         
-                       if (len <= 0) {
-                               RDEBUG("Expansion of LDAP attribute "
-                                      "\"%s\" failed", map->src->name);
-                                      
-                               goto error;
-                       }
-                       
-                       expanded->attrs[total++] = buffer;
-                       break;
+       if (!request->username) {
+               RDEBUGE("Attribute \"User-Name\" is required for authentication");
 
-               case VPT_TYPE_ATTR:
-                       context = request;
-                       
-                       if (radius_request(&context, map->src->request) == 0) {
-                               from = radius_list(context, map->src->list);
-                       }
-                       if (!from) continue;
-                       
-                       found = pairfind(*from, map->src->da->attr,
-                                        map->src->da->vendor, TAG_ANY);
-                       if (!found) continue;
-                       
-                       buffer = rad_malloc(MAX_ATTR_STR_LEN);
-                       strlcpy(buffer, found->vp_strvalue, MAX_ATTR_STR_LEN);
-                       
-                       expanded->attrs[total++] = buffer;
-                       break;
-                       
-               case VPT_TYPE_LITERAL:
-                       expanded->attrs[total++] = map->src->name;
-                       break;
-               default:
-                       rad_assert(0);
-               error:
-                       expanded->attrs[total] = NULL;
-                       
-                       xlat_attrsfree(expanded);
-                       
-                       return -1;
-               }
-                       
+               return RLM_MODULE_INVALID;
        }
-       
-       expanded->attrs[total] = NULL;
-       expanded->maps = maps;
-       
-       return 0;
-}
 
-
-/** Convert attribute map into valuepairs
- *
- * Use the attribute map built earlier to convert LDAP values into valuepairs
- * and insert them into whichever list they need to go into.
- *
- * This is *NOT* atomic, but there's no condition in which we should error
- * out...
- */
-static void do_attrmap(UNUSED ldap_instance *inst, REQUEST *request,
-                      LDAP *handle, const xlat_attrs_t *expanded,
-                      LDAPMessage *entry)
-{
-       const value_pair_map_t  *map;
-       unsigned int            total = 0;
-       
-       rlm_ldap_result_t       result;
-       const char              *name;
-
-       for (map = expanded->maps; map != NULL; map = map->next)
-       {
-               name = expanded->attrs[total++];
+       if (!request->password ||
+           (request->password->da->attr != PW_USER_PASSWORD)) {
+               RDEBUGW("  You have set \"Auth-Type := LDAP\" somewhere.");
+               RDEBUGW("  *********************************************");
+               RDEBUGW("  * THAT CONFIGURATION IS WRONG.  DELETE IT.   ");
+               RDEBUGW("  * YOU ARE PREVENTING THE SERVER FROM WORKING.");
+               RDEBUGW("  *********************************************");
                
-               result.values = ldap_get_values(handle, entry, name);
-               if (!result.values) {
-                       RDEBUG2("Attribute \"%s\" not found in LDAP object",
-                               name);
-                               
-                       goto next;
-               }
-               
-               /*
-                *      Find out how many values there are for the
-                *      attribute and extract all of them.
-                */
-               result.count = ldap_count_values(result.values);
-               
-               /*
-                *      If something bad happened, just skip, this is probably
-                *      a case of the dst being incorrect for the current
-                *      request context
-                */
-               if (radius_map2request(request, map, name, ldap_getvalue,
-                                      &result) < 0) {
-                       goto next;
-               }
-               
-               next:
+               RDEBUGE("Attribute \"User-Password\" is required for authentication.");
                
-               ldap_value_free(result.values);
+               return RLM_MODULE_INVALID;
        }
-}
-
-
-static void do_check_reply(ldap_instance *inst, REQUEST *request)
-{
-       /*
-       *       More warning messages for people who can't be bothered
-       *       to read the documentation.
-       */
-       if (inst->expect_password && (debug_flag > 1)) {
-               if (!pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY) &&
-                   !pairfind(request->config_items, PW_NT_PASSWORD, 0, TAG_ANY) &&
-                   !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 \"known good\" password "
-                              "was found in LDAP.  Are you sure that "
-                               "the user is configured correctly?");
-               }
-       }
-}
-
 
-static void apply_profile(ldap_instance *inst, REQUEST *request,
-                         LDAP_CONN **pconn, const char *profile,
-                         const xlat_attrs_t *expanded)
-{
-       int rcode;
-       LDAPMessage     *result, *entry;
-       int             ldap_errno;
-       LDAP            *handle = (*pconn)->handle;
-       char            filter[MAX_FILTER_STR_LEN];
+       if (request->password->length == 0) {
+               RDEBUGE("Empty password supplied");
+               
+               return RLM_MODULE_INVALID;
+       }
 
-       if (!profile || !*profile) return;
+       RDEBUG("Login attempt by \"%s\" with password \"%s\"", request->username->vp_strvalue,
+              request->password->vp_strvalue);
 
-       strlcpy(filter, inst->base_filter, sizeof(filter));
+       conn = rlm_ldap_get_socket(inst, request);
+       if (!conn) return RLM_MODULE_FAIL;
 
-       rcode = perform_search(inst, request, pconn, profile, LDAP_SCOPE_BASE,
-                              filter, expanded->attrs, &result);
+       /*
+        *      Get the DN by doing a search.
+        */
+       user_dn = rlm_ldap_find_user(inst, request, &conn, NULL, NULL, &rcode);
+       if (!user_dn) {
+               rlm_ldap_release_socket(inst, conn);
                
-       if (rcode < 0) {
-               if (rcode == -2) {
-                       RDEBUG("Profile \"%s\" not found", profile);
-               }
-               goto free_result;
+               return rcode;
        }
 
-       entry = ldap_first_entry(handle, result);
-       if (!entry) {
-               ldap_get_option(handle, LDAP_OPT_RESULT_CODE,
-                               &ldap_errno);
-               radlog(L_ERR, "rlm_ldap (%s): Failed retrieving entry: %s", 
-                      inst->xlat_name,
-                      ldap_err2string(ldap_errno));
-                      
-               goto free_result;
+       /*
+        *      Bind as the user
+        */
+       conn->rebound = TRUE;
+       rcode = rlm_ldap_bind(inst, request, &conn, user_dn, request->password->vp_strvalue, TRUE);
+       if (rcode == RLM_MODULE_OK) {
+               RDEBUG("Bind as user \"%s\" was successful", user_dn);
        }
-       
-       do_attrmap(inst, request, handle, expanded, entry);
 
-free_result:
-       ldap_msgfree(result);
+       rlm_ldap_release_socket(inst, conn);
+       
+       return rcode;
 }
 
-
 /** Check if user is authorized for remote access
  *
  */
 static rlm_rcode_t ldap_authorize(void *instance, REQUEST *request)
 {
-       int rcode;
-       int module_rcode = RLM_MODULE_OK;
-       ldap_instance   *inst = instance;
+       ldap_rcode_t status;
+       rlm_rcode_t rcode = RLM_MODULE_OK;
+       ldap_instance_t *inst = instance;
        char            *user_dn = NULL;
        char            **vals;
        VALUE_PAIR      *vp;
-       LDAP_CONN       *conn;
+       ldap_handle_t   *conn;
        LDAPMessage     *result, *entry;
        int             ldap_errno;
-       char            filter[MAX_FILTER_STR_LEN];
-       char            basedn[MAX_FILTER_STR_LEN];
-       xlat_attrs_t    expanded; /* faster that mallocing every time */
+       char            filter[LDAP_MAX_FILTER_STR_LEN];
+       char            basedn[LDAP_MAX_FILTER_STR_LEN];
+       rlm_ldap_map_xlat_t     expanded; /* faster that mallocing every time */
        
        if (!request->username) {
-               RDEBUG2("Attribute \"User-Name\" is required for "
-                       "authorization.");
+               RDEBUG2("Attribute \"User-Name\" is required for authorization.");
+               
                return RLM_MODULE_NOOP;
        }
 
@@ -1931,73 +785,60 @@ static rlm_rcode_t ldap_authorize(void *instance, REQUEST *request)
         */
        if (request->username->length == 0) {
                RDEBUG2("Zero length username not permitted");
+               
                return RLM_MODULE_INVALID;
        }
 
-       if (!radius_xlat(filter, sizeof(filter), inst->filter,
-                        request, ldap_escape_func, NULL)) {
-               radlog(L_ERR, "rlm_ldap (%s): Failed creating filter",
-                      inst->xlat_name);
+       if (!radius_xlat(filter, sizeof(filter), inst->filter, request, rlm_ldap_escape_func, NULL)) {
+               RDEBUGE("Failed creating filter");
+               
                return RLM_MODULE_INVALID;
        }
 
-       if (!radius_xlat(basedn, sizeof(basedn), inst->basedn,
-                        request, ldap_escape_func, NULL)) {
-               radlog(L_ERR, "rlm_ldap (%s): Failed creating basedn",
-                      inst->xlat_name);
+       if (!radius_xlat(basedn, sizeof(basedn), inst->basedn, request, rlm_ldap_escape_func, NULL)) {
+               RDEBUGE("Failed creating basedn");
+               
                return RLM_MODULE_INVALID;
        }
        
-       if (xlat_attrs(request, inst->user_map, &expanded) < 0) {
+       if (rlm_ldap_map_xlat(request, inst->user_map, &expanded) < 0) {
                return RLM_MODULE_FAIL;
        }
        
-
-       conn = ldap_get_socket(inst);
+       conn = rlm_ldap_get_socket(inst, request);
        if (!conn) return RLM_MODULE_FAIL;
        
-       rcode = perform_search(inst, request, &conn, basedn,
-                              LDAP_SCOPE_SUBTREE, filter, expanded.attrs,
-                              &result);
-       
-       if (rcode < 0) {
-               if (rcode == -2) {
+       status = rlm_ldap_search(inst, request, &conn, basedn, LDAP_SCOPE_SUBTREE, filter, expanded.attrs, &result);
+       switch (status) {
+               case LDAP_PROC_SUCCESS:
+                       break;
+               case LDAP_PROC_NO_RESULT:
+                       rcode = RLM_MODULE_NOTFOUND;
                        RDEBUGE("User object not found");
-                       module_rcode = RLM_MODULE_NOTFOUND;
-                       
+               default:
                        goto free_socket;
-               }
-
-               goto free_socket;
        }
+       
+       rad_assert(conn);
 
        entry = ldap_first_entry(conn->handle, result);
        if (!entry) {
-               ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE,
-                               &ldap_errno);
-               radlog(L_ERR, "rlm_ldap (%s): Failed retrieving entry: %s", 
-                      inst->xlat_name,
-                      ldap_err2string(ldap_errno));
+               ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
+               RDEBUGE("Failed retrieving entry: %s",
+                       ldap_err2string(ldap_errno));
                       
                goto free_result;
        }
 
        user_dn = ldap_get_dn(conn->handle, entry);
        if (!user_dn) {
-               ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE,
-                               &ldap_errno);
-               radlog(L_ERR, "rlm_ldap (%s): ldap_get_dn() failed: %s",
-                      inst->xlat_name,
-                      ldap_err2string(ldap_errno));
+               ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
+               RDEBUGE("ldap_get_dn() failed: %s", ldap_err2string(ldap_errno));
+
                goto free_result;
        }
        
        RDEBUG2("User found at DN \"%s\"", user_dn);
-       /*
-        *      Adding attribute containing the Users' DN.
-        */
-       pairadd(&request->config_items,
-               pairmake("Ldap-UserDn", user_dn, T_OP_EQ));
 
 #ifdef WITH_EDIR
        /*
@@ -2018,8 +859,7 @@ static rlm_rcode_t ldap_authorize(void *instance, REQUEST *request)
                bufsize = sizeof(buffer);
 
                /* retrive universal password */
-               res = nmasldap_get_password(conn->handle, user_dn,
-                                           buffer, &bufsize);
+               res = nmasldap_get_password(conn->handle, user_dn, buffer, &bufsize);
                if (res != 0) {
                        RDEBUGW("Failed to retrieve eDirectory password");
                        module_rcode = RLM_MODULE_NOOP;
@@ -2028,25 +868,19 @@ static rlm_rcode_t ldap_authorize(void *instance, REQUEST *request)
                }
 
                /* Add Cleartext-Password attribute to the request */
-               vp = radius_paircreate(request, &request->config_items,
-                                      PW_CLEARTEXT_PASSWORD, 0);
+               vp = radius_paircreate(request, &request->config_items, PW_CLEARTEXT_PASSWORD, 0);
                strlcpy(vp->vp_strvalue, buffer, sizeof(vp->vp_strvalue));
                vp->length = strlen(vp->vp_strvalue);
                
-               RDEBUG2("Added eDirectory password in check items as %s = %s",
-                       vp->da->name, vp->vp_strvalue);
+               RDEBUG2("Added eDirectory password in check items as %s = %s", vp->da->name, vp->vp_strvalue);
                        
                if (inst->edir_autz) {
-                       RDEBUG2("Binding as user for eDirectory authorization "
-                               "checks");
+                       RDEBUG2("Binding as user for eDirectory authorization checks");
                        /*
                         *      Bind as the user
                         */
                        conn->rebound = TRUE;
-                       module_rcode = ldap_bind_wrapper(request, &conn,
-                                                        user_dn,
-                                                        vp->vp_strvalue,
-                                                        TRUE);
+                       module_rcode = ldap_bind_user(request, &conn, user_dn, vp->vp_strvalue, TRUE);
                        if (module_rcode != RLM_MODULE_OK) {
                                goto free_result;
                        }
@@ -2061,9 +895,9 @@ skip_edir:
        /*
         *      Check for access.
         */
-       if (inst->access_attr) {
-               if (check_access(inst, request, conn, entry) < 0) {
-                       module_rcode = RLM_MODULE_USERLOCK;
+       if (inst->userobj_access_attr) {
+               rcode = rlm_ldap_check_access(inst, request, conn, entry);
+               if (rcode != RLM_MODULE_OK) {
                        goto free_result;
                }
        }
@@ -2073,11 +907,11 @@ skip_edir:
         */
        vp = pairfind(request->config_items, PW_USER_PROFILE, 0, TAG_ANY);
        if (vp || inst->default_profile) {
-               char *profile = inst->default_profile;
+               const char *profile = inst->default_profile;
 
                if (vp) profile = vp->vp_strvalue;
 
-               apply_profile(inst, request, &conn, profile, &expanded);
+               rlm_ldap_apply_profile(inst, request, &conn, profile, &expanded);
        }
 
        /*
@@ -2088,10 +922,8 @@ skip_edir:
                if (vals != NULL) {
                        int i;
        
-                       for (i = 0; (vals[i] != NULL) && (*vals[i] != '\0');
-                            i++) {
-                               apply_profile(inst, request, &conn, vals[i],
-                                             &expanded);
+                       for (i = 0; (vals[i] != NULL) && (*vals[i] != '\0'); i++) {
+                               rlm_ldap_apply_profile(inst, request, &conn, vals[i], &expanded);
                        }
        
                        ldap_value_free(vals);
@@ -2099,126 +931,48 @@ skip_edir:
        }
 
        if (inst->user_map) {
-               do_attrmap(inst, request, conn->handle, &expanded, entry);
-               do_check_reply(inst, request);
+               rlm_ldap_map_do(inst, request, conn->handle, &expanded, entry);
+               rlm_ldap_check_reply(inst, request);
        }
        
 free_result:
        if (user_dn) ldap_memfree(user_dn);
-       xlat_attrsfree(&expanded);
+       rlm_ldap_map_xlat_free(&expanded);
        ldap_msgfree(result);
 free_socket:
-       ldap_release_socket(inst, conn);
-
-       return module_rcode;
-}
-
-
-/** Check the user's password against ldap database
- *
- */
-static rlm_rcode_t ldap_authenticate(void *instance, REQUEST *request)
-{
-       rlm_rcode_t     module_rcode;
-       const char      *user_dn;
-       ldap_instance   *inst = instance;
-       LDAP_CONN       *conn;
-
-       /*
-        * Ensure that we're being passed a plain-text password, and not
-        * anything else.
-        */
-
-       if (!request->username) {
-               radlog(L_AUTH, "rlm_ldap (%s): Attribute \"User-Name\" is "
-                      "required for authentication", inst->xlat_name);
-               return RLM_MODULE_INVALID;
-       }
-
-       if (!request->password) {
-               radlog(L_AUTH, "rlm_ldap (%s): Attribute \"User-Password\" "
-                      "is required for authentication.", inst->xlat_name);
-               RDEBUG2("  You have set \"Auth-Type := LDAP\" somewhere.");
-               RDEBUG2("  *********************************************");
-               RDEBUG2("  * THAT CONFIGURATION IS WRONG.  DELETE IT.   ");
-               RDEBUG2("  * YOU ARE PREVENTING THE SERVER FROM WORKING.");
-               RDEBUG2("  *********************************************");
-               return RLM_MODULE_INVALID;
-       }
-
-       if (request->password->da->attr != PW_USER_PASSWORD) {
-               radlog(L_AUTH, "rlm_ldap (%s): Attribute \"User-Password\" "
-                      "is required for authentication. Cannot use \"%s\".",
-                      inst->xlat_name, request->password->da->name);
-               return RLM_MODULE_INVALID;
-       }
-
-       if (request->password->length == 0) {
-               RDEBUGE("Empty password supplied");
-               
-               return RLM_MODULE_INVALID;
-       }
-
-       RDEBUG("Login attempt by \"%s\" with password \"%s\"",
-              request->username->vp_strvalue, request->password->vp_strvalue);
-
-       conn = ldap_get_socket(inst);
-       if (!conn) return RLM_MODULE_FAIL;
+       rlm_ldap_release_socket(inst, conn);
 
-       /*
-        *      Get the DN by doing a search.
-        */
-       user_dn = get_userdn(&conn, request, &module_rcode);
-       if (!user_dn) {
-               ldap_release_socket(inst, conn);
-               return module_rcode;
-       }
-
-       /*
-        *      Bind as the user
-        */
-       conn->rebound = TRUE;
-       module_rcode = ldap_bind_wrapper(request, &conn, user_dn,
-                                        request->password->vp_strvalue,
-                                        TRUE);
-       if (module_rcode == RLM_MODULE_OK) {
-               RDEBUG("Bind as user \"%s\" was successful", user_dn);
-       }
-
-       ldap_release_socket(inst, conn);
-       return module_rcode;
+       return rcode;
 }
 
 /** Modify user's object in LDAP
  *
+ * Process a modifcation map to update a user object in the LDAP directory.
+ *
+ * @param inst rlm_ldap instance.
+ * @param request Current request.
+ * @param section that holds the map to process.
+ * @return one of the RLM_MODULE_* values.
  */
-static rlm_rcode_t user_modify(ldap_instance *inst, REQUEST *request,
-                              ldap_acct_section_t *section)
+static rlm_rcode_t user_modify(ldap_instance_t *inst, REQUEST *request, ldap_acct_section_t *section)
 {
-       rlm_rcode_t     module_rcode = RLM_MODULE_OK;
-       int             ldap_errno, rcode, msg_id;
-       LDAPMessage     *result = NULL;
+       rlm_rcode_t     rcode = RLM_MODULE_OK;
        
-       LDAP_CONN       *conn = NULL;
+       ldap_handle_t   *conn = NULL;
        
-       LDAPMod         *mod_p[MAX_ATTRMAP + 1], mod_s[MAX_ATTRMAP];
+       LDAPMod         *mod_p[LDAP_MAX_ATTRMAP + 1], mod_s[LDAP_MAX_ATTRMAP];
        LDAPMod         **modify = mod_p;
        
-       char            *passed[MAX_ATTRMAP * 2];
+       char            *passed[LDAP_MAX_ATTRMAP * 2];
        int             i, total = 0, last_pass = 0;
        
-       char            *expanded[MAX_ATTRMAP];
+       char            *expanded[LDAP_MAX_ATTRMAP];
        int             last_exp = 0;
        
-       struct timeval  tv;
-       
        const char      *attr;
        const char      *value;
        
-       const char      *user_dn;
-       
-       const char      *error = NULL;
-
+       const char      *dn;
        /*
         *      Build our set of modifications using the update sections in
         *      the config.
@@ -2240,8 +994,7 @@ static rlm_rcode_t user_modify(ldap_instance *inst, REQUEST *request,
                *p++ = '.';
        }
        
-       if (!radius_xlat(p, (sizeof(path) - (p - path)) - 1,
-                        section->reference, request, NULL, NULL)) {
+       if (!radius_xlat(p, (sizeof(path) - (p - path)) - 1, section->reference, request, NULL, NULL)) {
                goto error;     
        }
 
@@ -2251,17 +1004,14 @@ static rlm_rcode_t user_modify(ldap_instance *inst, REQUEST *request,
        }
        
        if (!cf_item_is_section(ci)){
-               radlog(L_ERR, "rlm_ldap (%s): Reference must resolve to a "
-                      "section", inst->xlat_name);
+               RDEBUGE("Reference must resolve to a section");
                
                goto error;     
        }
        
        cs = cf_section_sub_find(cf_itemtosection(ci), "update");
        if (!cs) {
-               radlog(L_ERR, "rlm_ldap (%s): Section must contain 'update' "
-                      "subsection",
-                      inst->xlat_name);
+               RDEBUGE("Section must contain 'update' subsection");
                
                goto error;
        }
@@ -2269,22 +1019,17 @@ static rlm_rcode_t user_modify(ldap_instance *inst, REQUEST *request,
        /*
         *      Iterate over all the pairs, building our mods array
         */
-       for (ci = cf_item_find_next(cs, NULL);
-            ci != NULL;
-            ci = cf_item_find_next(cs, ci)) {
+       for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) {
                int do_xlat = FALSE;
                
-               if (total == MAX_ATTRMAP) {
-                       radlog(L_ERR, "rlm_ldap (%s): Modify map size exceeded",
-                              inst->xlat_name);
+               if (total == LDAP_MAX_ATTRMAP) {
+                       RDEBUGE("Modify map size exceeded");
        
                        goto error;
                }
                
                if (!cf_item_is_pair(ci)) {
-                       radlog(L_ERR, "rlm_ldap (%s): Entry is not in "
-                              "\"ldap-attribute = value\" format",
-                              inst->xlat_name);
+                       RDEBUGE("Entry is not in \"ldap-attribute = value\" format");
                               
                        goto error;
                }
@@ -2297,9 +1042,8 @@ static rlm_rcode_t user_modify(ldap_instance *inst, REQUEST *request,
                attr = cf_pair_attr(cp);
                op = cf_pair_operator(cp);
                
-               if ((value == NULL) || (*value == '\0')) {
-                       RDEBUG("empty value string, "
-                              "skipping attribute \"%s\"", attr);
+               if (!value || (*value == '\0')) {
+                       RDEBUG("Empty value string, skipping attribute \"%s\"", attr);
                        
                        continue;
                }
@@ -2323,8 +1067,7 @@ static rlm_rcode_t user_modify(ldap_instance *inst, REQUEST *request,
                } else if (do_xlat) {
                        p = rad_malloc(1024);
                        if (radius_xlat(p, 1024, value, request, NULL, NULL) <= 0) {
-                               RDEBUG("xlat failed or empty value string, "
-                                      "skipping attribute \"%s\"", attr);
+                               RDEBUG("xlat failed or empty value string, skipping attribute \"%s\"", attr);
                                       
                                free(p);
                                
@@ -2337,8 +1080,7 @@ static rlm_rcode_t user_modify(ldap_instance *inst, REQUEST *request,
                 *      Static strings
                 */
                } else {
-                       memcpy(&(passed[last_pass]), &value,
-                              sizeof(passed[last_pass]));
+                       memcpy(&(passed[last_pass]), &value, sizeof(passed[last_pass]));
                }
                
                passed[last_pass + 1] = NULL;
@@ -2372,10 +1114,8 @@ static rlm_rcode_t user_modify(ldap_instance *inst, REQUEST *request,
                        break;
 #endif
                default:
-                       radlog(L_ERR, "rlm_ldap (%s): Operator '%s' "
-                              "is not supported for LDAP modify "
-                              "operations", inst->xlat_name,
-                              fr_int2str(fr_tokens, op, "¿unknown?"));
+                       RDEBUGE("Operator '%s' is not supported for LDAP modify operations",
+                               fr_int2str(fr_tokens, op, "¿unknown?"));
                               
                        goto error;
                }
@@ -2384,112 +1124,46 @@ static rlm_rcode_t user_modify(ldap_instance *inst, REQUEST *request,
                 *      Now we know the value is ok, copy the pointers into
                 *      the ldapmod struct.
                 */
-               memcpy(&(mod_s[total].mod_type), &(attr), 
-                      sizeof(mod_s[total].mod_type));
+               memcpy(&(mod_s[total].mod_type), &(attr), sizeof(mod_s[total].mod_type));
                
                mod_p[total] = &(mod_s[total]);
                total++;
        }
        
        if (total == 0) {
-               module_rcode = RLM_MODULE_NOOP;
+               rcode = RLM_MODULE_NOOP;
                goto release;
        }
        
        mod_p[total] = NULL;
        
-       conn = ldap_get_socket(inst);
+       conn = rlm_ldap_get_socket(inst, request);
        if (!conn) return RLM_MODULE_FAIL;
-       
-       /*
-        *      Perform all modifications as the default admin user.
-        */
-       if (conn->rebound) {
-               ldap_errno = ldap_bind_wrapper(request, &conn, inst->login,
-                                              inst->password, TRUE);
-               if (ldap_errno != RLM_MODULE_OK) {
-                       goto error;
-               }
 
-               rad_assert(conn != NULL);
-               conn->rebound = FALSE;
-       }
 
-       user_dn = get_userdn(&conn, request, &module_rcode);
-       if (!user_dn) {
-               module_rcode = RLM_MODULE_NOTFOUND;
-               goto release;
-       }
-       
-       RDEBUG2("Modifying user object with DN \"%s\"", user_dn);
-       retry:
-       ldap_errno = ldap_modify_ext(conn->handle, user_dn, modify, NULL, NULL,
-                                    &msg_id);
-       if (ldap_errno != LDAP_SUCCESS) {
-               switch (process_ldap_errno(inst, conn, &error))
-               {
-                       case LDAP_PROC_SUCCESS:
-                               break;
-                       case LDAP_PROC_RETRY:
-                               radlog(L_ERR, "rlm_ldap (%s): Failed "
-                                      "modifying object, reconnecting: %s",
-                                      inst->xlat_name, error);         
-                               goto retry;
-                       default:
-                               radlog(L_ERR, "rlm_ldap (%s): Failed "
-                                      "modifying object: %s",
-                                      inst->xlat_name, error);
-                               goto error;
-               }
+       dn = rlm_ldap_find_user(inst, request, &conn, NULL, NULL, &rcode);
+       if (!dn || (rcode = RLM_MODULE_OK)) {
+               goto error;
        }
-                            
-       DEBUG3("rlm_ldap (%s): Waiting for modify result...", inst->xlat_name);
-
-       tv.tv_sec = inst->timeout;
-       tv.tv_usec = 0;
        
-       result:
-       rcode = ldap_result(conn->handle, msg_id, 1, &tv, &result);
-       ldap_msgfree(result);
-       if (rcode <= 0) {
-               switch (process_ldap_errno(inst, conn, &error))
-               {
-                       case LDAP_PROC_SUCCESS:
-                               break;
-                       case LDAP_PROC_RETRY:
-                               radlog(L_ERR, "rlm_ldap (%s): Failed "
-                                      "modifying object, reconnecting: %s",
-                                      inst->xlat_name, error);
-                               goto result;
-                       default:
-                               error:
-                               radlog(L_ERR, "rlm_ldap (%s): Failed "
-                                      "modifying object: %s",
-                                      inst->xlat_name, error);
-                               module_rcode = RLM_MODULE_FAIL;
-                               goto release;
-               }
-       }
-               
-       RDEBUG2("Modification successful!");
+       rcode = rlm_ldap_modify(inst, request, &conn, dn, modify);
        
        release:
+       error:
        /*
         *      Free up any buffers we allocated for xlat expansion
         */     
        for (i = 0; i < last_exp; i++) {
                free(expanded[i]);
        }
-       
 
-       ldap_release_socket(inst, conn);
+       rlm_ldap_release_socket(inst, conn);
        
-       return module_rcode;
+       return rcode;
 }
 
-
 static rlm_rcode_t ldap_accounting(void *instance, REQUEST * request) {
-       ldap_instance *inst = instance;         
+       ldap_instance_t *inst = instance;               
 
        if (inst->accounting) {
                return user_modify(inst, request, inst->accounting); 
@@ -2504,7 +1178,7 @@ static rlm_rcode_t ldap_accounting(void *instance, REQUEST * request) {
  */
 static rlm_rcode_t ldap_postauth(void *instance, REQUEST * request)
 {
-       ldap_instance   *inst = instance;
+       ldap_instance_t *inst = instance;
 
        if (inst->postauth) {
                return user_modify(inst, request, inst->postauth); 
index f708586faf344c107d2878ad663bbf5cce912448..d3e5909a9015293da980d6822557c69dd79ea79b 100644 (file)
@@ -211,7 +211,7 @@ static rlm_rcode_t pap_authorize(void *instance, REQUEST *request)
                                 *      that instead of this one.
                                 */
                                if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) {
-                                       RDEBUG("Config already contains \"known good\" password.  Ignoring Password-With-Header");
+                                       RDEBUG("Config already contains \"reference\" password.  Ignoring Password-With-Header");
                                        break;
                                }
 
@@ -337,8 +337,8 @@ static rlm_rcode_t pap_authorize(void *instance, REQUEST *request)
                        return RLM_MODULE_NOOP;
                }
 
-               RDEBUGW("No \"known good\" password found for the user.  Not setting Auth-Type.");
-               RDEBUGW("Authentication will fail unless a \"known good\" password is available.");
+               RDEBUGW("No \"reference\" password found for the user.  Not setting Auth-Type.");
+               RDEBUGW("Authentication will fail unless a \"reference\" password is available.");
                return RLM_MODULE_NOOP;
        }