]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Switch to using new config based attribute map
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 30 Nov 2012 17:11:40 +0000 (17:11 +0000)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 1 Dec 2012 12:07:10 +0000 (12:07 +0000)
raddb/ldap.attrmap [deleted file]
raddb/mods-available/ldap
src/modules/rlm_ldap/Makefile.in
src/modules/rlm_ldap/all.mk.in
src/modules/rlm_ldap/mapping.c [deleted file]
src/modules/rlm_ldap/mapping.h [deleted file]
src/modules/rlm_ldap/rlm_ldap.c

diff --git a/raddb/ldap.attrmap b/raddb/ldap.attrmap
deleted file mode 100644 (file)
index 1aa7592..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-#
-# Mapping of RADIUS dictionary attributes to LDAP directory attributes
-# to be used by LDAP authentication and authorization module (rlm_ldap)
-#
-# Format:
-#   ItemType   RADIUS-Attribute-Name           ldapAttributeName  [operator]
-#
-# Where:
-#   ItemType              = checkItem or replyItem 
-#   RADIUS-Attribute-Name = attribute name in RADIUS dictionary
-#   ldapAttributeName     = attribute name in LDAP schema
-#   operator              = optional, and may not be present.
-#                          If not present, defaults to "==" for checkItems,
-#                          and "=" for replyItems.
-#                          If present, the operator here should be one
-#                          of the same operators as defined in the "users"3
-#                          file ("man users", or "man 5 users").
-#                          If an operator is present in the value of the
-#                          LDAP entry (i.e. ":=foo"), then it over-rides
-#                          both the default, and any operator given here.
-#
-# If $GENERIC$ is specified as RADIUS-Attribute-Name, the line specifies
-# a LDAP attribute which can be used to store any RADIUS 
-# attribute/value-pair in LDAP directory.
-#
-# You should edit this file to suit it to your needs.
-#
-
-checkItem      $GENERIC$                       radiusCheckItem
-replyItem      $GENERIC$                       radiusReplyItem
-
-checkItem      Auth-Type                       radiusAuthType
-checkItem      Simultaneous-Use                radiusSimultaneousUse
-checkItem      Called-Station-Id               radiusCalledStationId
-checkItem      Calling-Station-Id              radiusCallingStationId
-checkItem      LM-Password                     lmPassword
-checkItem      NT-Password                     ntPassword
-checkItem      LM-Password                     sambaLmPassword
-checkItem      NT-Password                     sambaNtPassword
-checkItem      LM-Password                     dBCSPwd
-checkitem      Password-With-Header            userPassword
-checkItem      SMB-Account-CTRL-TEXT           acctFlags
-checkItem      Expiration                      radiusExpiration
-checkItem      NAS-IP-Address                  radiusNASIpAddress
-checkItem      Password-With-Header            userPassword
-
-replyItem      Service-Type                    radiusServiceType
-replyItem      Framed-Protocol                 radiusFramedProtocol
-replyItem      Framed-IP-Address               radiusFramedIPAddress
-replyItem      Framed-IP-Netmask               radiusFramedIPNetmask
-replyItem      Framed-Route                    radiusFramedRoute
-replyItem      Framed-Routing                  radiusFramedRouting
-replyItem      Filter-Id                       radiusFilterId
-replyItem      Framed-MTU                      radiusFramedMTU
-replyItem      Framed-Compression              radiusFramedCompression
-replyItem      Login-IP-Host                   radiusLoginIPHost
-replyItem      Login-Service                   radiusLoginService
-replyItem      Login-TCP-Port                  radiusLoginTCPPort
-replyItem      Callback-Number                 radiusCallbackNumber
-replyItem      Callback-Id                     radiusCallbackId
-replyItem      Framed-IPX-Network              radiusFramedIPXNetwork
-replyItem      Class                           radiusClass
-replyItem      Session-Timeout                 radiusSessionTimeout
-replyItem      Idle-Timeout                    radiusIdleTimeout
-replyItem      Termination-Action              radiusTerminationAction
-replyItem      Login-LAT-Service               radiusLoginLATService
-replyItem      Login-LAT-Node                  radiusLoginLATNode
-replyItem      Login-LAT-Group                 radiusLoginLATGroup
-replyItem      Framed-AppleTalk-Link           radiusFramedAppleTalkLink
-replyItem      Framed-AppleTalk-Network        radiusFramedAppleTalkNetwork
-replyItem      Framed-AppleTalk-Zone           radiusFramedAppleTalkZone
-replyItem      Port-Limit                      radiusPortLimit
-replyItem      Login-LAT-Port                  radiusLoginLATPort
-replyItem      Reply-Message                   radiusReplyMessage
-replyItem      Tunnel-Type                     radiusTunnelType
-replyItem      Tunnel-Medium-Type              radiusTunnelMediumType
-replyItem      Tunnel-Private-Group-Id         radiusTunnelPrivateGroupId
index c4ae0ec72bc676e31d55e3e278878908d3bbe57c..875b0f71835f80c5389cd099f48c5760b3c73bba 100644 (file)
@@ -24,9 +24,34 @@ ldap {
 #      basedn = "ou=people,dc=example,dc=org"
 #      filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
 
-       # Mapping of RADIUS dictionary attributes to LDAP
-       # directory attributes.
-#      dictionary_mapping = ${confdir}/ldap.attrmap
+       #
+       #  Mapping of RADIUS dictionary attributes to LDAP directory attributes.
+       #
+       #  WARNING: Although this format is almost identical to the unlang 
+       #  update section format, it does *NOT* mean that you can use other
+       #  unlang constructs in module configuration files.
+       #
+       #  Configuration items are in the format:
+       #       <radius attribute> <op> <ldap attribute>
+       # 
+       #  Where:
+       #       <radius attribute>:     The destination RADIUS attribute
+       #                       with any valid list and request qualifiers.
+       #       <op>:           Is any assignment attribute (=, :=, +=, -=).
+       #       <ldap attribute>:       The attribute in the associated 
+       #                       with user or profile objects in the LDAP 
+       #                       directory. If the attribute name is wrapped in 
+       #                       double quotes it will be xlat expanded.
+       # 
+       #  Request and list qualifiers may also be placed after the section
+       #  name to set defaults for unqualified RADIUS attributes.
+       update reply {
+#              control:NT-Password     := ntPassword
+#              Reply-Message           := radiusReplyMessage
+#              Tunnel-Type             := radiusTunnelType
+#              Tunnel-Medium-Type      := radiusTunnelMediumType
+#              Tunnel-Private-Group-ID := radiusTunnelPrivategroupId
+       }
 
        # Set to "no" to disable the "no \"known good\" password" warning,
        # if you're not using LDAP to retrieve password values.
@@ -70,14 +95,14 @@ ldap {
        #  When doing checks for LDAP-Group = foo"
        #
        group {
-             # Check for "cn=foo"
-#            name_attribute = cn
+               # Check for "cn=foo"
+#              name_attribute = cn
 
-             # Filter to get the list of groups that a user belongs to.
-#            membership_filter = "(|(&(objectClass=GroupOfNames)(member=%{control:Ldap-UserDn}))(&(objectClass=GroupOfUniqueNames)(uniquemember=%{control:Ldap-UserDn})))"
+               # Filter to get the list of groups that a user belongs to.
+#              membership_filter = "(|(&(objectClass=GroupOfNames)(member=%{control:Ldap-UserDn}))(&(objectClass=GroupOfUniqueNames)(uniquemember=%{control:Ldap-UserDn})))"
 
-             # If the filter returns nothing
-             membership_attribute = radiusGroupName
+               # If the filter returns nothing
+               membership_attribute = radiusGroupName
        }
 
        #  LDAP connection-specific options.
index 058ef3bb52456d451b2597fa24e1358de5c07af8..13f540aa5d04a5fc9470679bc06e42a7ce87831f 100644 (file)
@@ -3,8 +3,7 @@
 #
 
 TARGET     = @targetname@
-SRCS       = rlm_ldap.c mapping.c
-HEADERS    = mapping.h
+SRCS       = rlm_ldap.c
 RLM_CFLAGS = @ldap_cflags@
 RLM_LIBS   = @ldap_ldflags@
 
index a072052730520eb9a9a4776dc3e11936646011aa..591728e52cde9f385c82714646c0fb8d2fcad0d9 100644 (file)
@@ -4,7 +4,7 @@ ifneq "$(TARGETNAME)" ""
 TARGET         := $(TARGETNAME).a
 endif
 
-SOURCES                := $(TARGETNAME).c mapping.c
+SOURCES                := $(TARGETNAME).c
 
 SRC_CFLAGS     := @ldap_cflags@
 TGT_LDLIBS     := @ldap_ldflags@
diff --git a/src/modules/rlm_ldap/mapping.c b/src/modules/rlm_ldap/mapping.c
deleted file mode 100644 (file)
index 6649c86..0000000
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * mapping.c   LDAP attribute to RADIUS attribute mappings
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   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
- *
- *   Copyright 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,
- *            2009,2010,2011,1012 The FreeRADIUS Server Project.
- *
- *   Copyright 2012 Alan DeKok <aland@freeradius.org>
- */
-
-#include <freeradius-devel/ident.h>
-RCSID("$Id$")
-
-#include       <freeradius-devel/radiusd.h>
-#include       <freeradius-devel/modules.h>
-#include       <freeradius-devel/rad_assert.h>
-
-#include       "mapping.h"
-
-#define GENERIC_ATTRIBUTE_ID "$GENERIC$"
-#define MAX_ARGV 5
-
-void rlm_ldap_map_free(TLDAP_RADIUS **map)
-{
-       TLDAP_RADIUS *t, *next;
-
-       if (!map || !*map) return;
-
-       for (t = *map; t != NULL; t = next) {
-               next = t->next;
-               free(t);
-       }
-
-       *map = NULL;
-}
-
-int rlm_ldap_map_read(const char *xlat_name,
-                     const char *filename,
-                     TLDAP_RADIUS **check_map,
-                     TLDAP_RADIUS **reply_map,
-                     int offset,
-                     char *attrs[MAX_ATTRMAP])
-{
-       FILE *fp;
-       int total;
-       int lineno, argc;
-       FR_TOKEN operator;
-       TLDAP_RADIUS *pair;
-       char *argv[MAX_ARGV];
-       char *p, buffer[1024];
-
-       fp = fopen(filename, "r");
-       if (!fp) {
-               radlog(L_ERR, "%s: Failed opening %s: %s",
-                      xlat_name, filename, strerror(errno));
-               return -1;
-       }
-
-       lineno = 0;
-       total = offset;
-
-       while (fgets(buffer, sizeof(buffer), fp) != NULL) {
-               size_t ldap_size, radius_size;
-
-               lineno++;
-
-               p = strchr(buffer, '#');
-               if (p) *p = 0;
-
-               argc = str2argv(buffer, argv, MAX_ARGV);
-               if (argc == 0) continue;
-
-               if ((argc < 3) || (argc > 4)) {
-                       radlog(L_ERR, "%s: Invalid format in file %s line %d",
-                              xlat_name, filename, lineno);
-               error:
-                       fclose(fp);
-                       rlm_ldap_map_free(check_map);
-                       rlm_ldap_map_free(reply_map);
-                       return -1;
-               }
-
-               if (argc == 3) {
-                       operator = T_OP_INVALID; /* use defaults */
-               } else {
-                       operator = fr_str2int(fr_tokens, argv[3], T_OP_INVALID);
-                       if ((operator < T_OP_ADD) || (operator > T_OP_CMP_EQ)) {
-                               radlog(L_ERR, "%s: Invalid operator '%s' in file %s line %d",
-                                      xlat_name, argv[3],
-                                      filename, lineno);
-                               goto error;
-                       }
-               }
-
-               /*
-                *      Sanity check checkItem or replyItem
-                */
-               if ((strcasecmp(argv[0], "checkItem") != 0) &&
-                   (strcasecmp(argv[0], "replyItem") != 0)) {
-                       radlog(L_ERR, "%s: Entry does not have \"checkItem\" or \"replyItem\" in column 0 of file %s line %d",
-                              xlat_name, filename, lineno);
-                       goto error;
-               }
-
-               /*
-                *      Sanity check RADIUS attribute.
-                *      Allow generic name, too.
-                */
-               if ((strcmp(argv[1], GENERIC_ATTRIBUTE_ID) != 0) &&
-                   !dict_attrbyname(argv[1])) {
-                       radlog(L_ERR, "%s: Unknown RADIUS attribute \"%s\" in file %s line %d",
-                              xlat_name, argv[1], filename, lineno);
-                       goto error;
-               }
-
-               /* create new TLDAP_RADIUS list node */
-               radius_size = strlen(argv[1]);
-               ldap_size = strlen(argv[2]);
-
-               /*
-                *      Doing tons of strcmp() at run-time is bad.
-                *      Instead, just set the radius field to be ""
-                */
-               if (strcmp(argv[1], GENERIC_ATTRIBUTE_ID) == 0) {
-                       argv[1] += radius_size;
-                       radius_size = 0;
-               }
-
-               pair = rad_malloc(sizeof(*pair) + radius_size + ldap_size + 2);
-               pair->ldap_attr = ((char *) pair) + sizeof(*pair);
-               pair->radius_attr = pair->ldap_attr + ldap_size + 1;
-
-               memcpy(pair->radius_attr, argv[1], radius_size + 1);
-               memcpy(pair->ldap_attr, argv[2], ldap_size + 1);
-               pair->operator = operator;
-
-               /*
-                *      Place it in the correct list.
-                */
-               if (*argv[0] == 'c') {
-                       pair->next = *check_map;
-                       *check_map = pair;
-               } else {
-                       pair->next = *reply_map;
-                       *reply_map = pair;
-               }
-
-               attrs[total++] = pair->ldap_attr;
-
-               if (total >= MAX_ATTRMAP) {
-                       radlog(L_ERR, "%s: ERROR Too many entries (%d) in %s",
-                              xlat_name, total, filename);
-                       goto error;
-               }
-
-               DEBUG("       %s: %s --> %s",
-                     xlat_name, pair->ldap_attr,
-                     *pair->radius_attr ? pair->radius_attr : GENERIC_ATTRIBUTE_ID);
-       }
-
-       fclose(fp);
-
-       attrs[total] = NULL;
-       return 0; /* success */
-}
-
-static VALUE_PAIR *ldap2vp(const char *xlat_name, TLDAP_RADIUS *pair,
-                          const char *value, int is_check)
-{
-       int do_xlat;
-       const char *p;
-       VALUE_PAIR *vp;
-       FR_TOKEN    token, operator;
-       char buffer[1024];
-
-       p = value;
-
-       if (!*pair->radius_attr) {
-               FR_TOKEN eol;
-
-               vp = pairread(&p, &eol);
-               goto done;      /* I hate indentation */
-       }
-
-       /*
-        *      This is a one-to-one-mapped attribute
-        */
-       operator = gettoken(&p, buffer, sizeof(buffer));
-       if (operator < T_EQSTART || operator > T_EQEND) {
-               if (pair->operator != T_OP_INVALID) {
-                       operator = pair->operator;
-               } else if (is_check) {
-                       operator = T_OP_CMP_EQ;
-               } else { 
-                       operator = T_OP_EQ;
-               }
-       } else {                /* skip the operator */
-               value = p;
-       }
-
-       if (!*value) {
-       empty_string:
-               DEBUG("  [%s] FAILED parsing %s -> empty string",
-                     xlat_name, pair->ldap_attr);
-               return NULL;
-       }
-       
-       p = value;
-       token = gettoken(&p, buffer, sizeof(buffer));
-       switch (token) {
-       case T_BARE_WORD:
-       case T_SINGLE_QUOTED_STRING:
-       case T_DOUBLE_QUOTED_STRING:
-               do_xlat = FALSE;
-               break;
-                       
-               /* the value will be xlat'ed later */
-       case T_BACK_QUOTED_STRING:
-               do_xlat = TRUE;
-               break;
-
-       case T_OP_INVALID:
-       default:
-               DEBUG("  [%s] FAILED Parsing %s -> %s",
-                     xlat_name, pair->ldap_attr, value);
-               return NULL;
-
-       }
-
-       if (!*buffer) goto empty_string;
-
-       /*
-        *      Create the pair.
-        */
-       if (do_xlat) {
-               vp = pairmake_xlat(pair->radius_attr, buffer, operator);
-       } else {
-               vp = pairmake(pair->radius_attr, buffer, operator);
-       }
-
-done:
-       if (!vp) {
-               DEBUG("  [%s] FAILED parsing %s -> %s",
-                     xlat_name, pair->ldap_attr, value);
-               return NULL;
-       }
-       
-       if (fr_debug_flag) {
-               vp_prints(buffer, sizeof(buffer), vp);
-               DEBUG("  [%s] %s -> %s",
-                     xlat_name, pair->ldap_attr, buffer);
-       }
-       
-       return vp;
-}
-
-/*****************************************************************************
- *     Get RADIUS attributes from LDAP object
- *     ( according to draft-adoba-radius-05.txt
- *       <http://www.ietf.org/internet-drafts/draft-adoba-radius-05.txt> )
- *
- *****************************************************************************/
-VALUE_PAIR *rlm_ldap_pairget(const char *xlat_name,
-                            LDAP *ld, LDAPMessage *entry,
-                            TLDAP_RADIUS *map,
-                            VALUE_PAIR **vps, int is_check)
-{
-       char          **vals;
-       int             count;
-       int             i;
-       TLDAP_RADIUS   *pair;
-       VALUE_PAIR     *head, **tail;
-
-       head = NULL;
-       tail = &head;
-
-       /*
-        *      check if there is a mapping from this LDAP attribute
-        *      to a RADIUS attribute
-        */
-       for (pair = map; pair != NULL; pair = pair->next) {
-               /*
-                *      No mapping found, skip it.
-                */
-               vals = ldap_get_values(ld, entry, pair->ldap_attr);
-               if (!vals) continue;
-
-               /*
-                *      Find out how many values there are for the
-                *      attribute and extract all of them.
-                */
-               count = ldap_count_values(vals);
-
-               /*
-                *      FIXME: The old code DELETES all references
-                *      to pair->radius_attr from the vps list.
-                *      Why?
-                */
-
-               for (i = 0; i < count; i++) {
-                       VALUE_PAIR *vp;
-
-                       vp = ldap2vp(xlat_name, pair, vals[i], is_check);
-                       if (!vp) continue;
-
-                       /*
-                        *      FIXME: if i==0, delete from vps?
-                        */
-
-                       *tail = vp;
-                       tail = &vp->next;
-               }
-
-               ldap_value_free(vals);
-       }
-
-       return head;
-}
diff --git a/src/modules/rlm_ldap/mapping.h b/src/modules/rlm_ldap/mapping.h
deleted file mode 100644 (file)
index 29b5f14..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * mapping.h   LDAP attribute to RADIUS attribute mappings
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   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
- *
- *   Copyright 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,
- *            2009,2010,2011,1012 The FreeRADIUS Server Project.
- *
- *   Copyright 2012 Alan DeKok <aland@freeradius.org>
- */
-
-#include       <lber.h>
-#include        <ldap.h>
-
-/* linked list of mappings between RADIUS attributes and LDAP attributes */
-typedef struct TLDAP_RADIUS TLDAP_RADIUS;
-
-struct TLDAP_RADIUS {
-       char            *ldap_attr;
-       char            *radius_attr;
-       FR_TOKEN        operator;
-       int             is_check;
-       TLDAP_RADIUS    *next;
-};
-
-#define MAX_ATTRMAP (256)
-
-int rlm_ldap_map_read(const char *xlat_name,
-                     const char *filename,
-                     TLDAP_RADIUS **check_map,
-                     TLDAP_RADIUS **reply_map,
-                     int offset,
-                     char *attrs[MAX_ATTRMAP]);
-void rlm_ldap_map_free(TLDAP_RADIUS **map);
-
-VALUE_PAIR *rlm_ldap_pairget(const char *xlat_name,
-                            LDAP *ld, LDAPMessage *entry,
-                            TLDAP_RADIUS *map,
-                            VALUE_PAIR **vps, int is_check);
index 1334e01f376bbe81fdc64c7367433ec27257eb53..4db15a0932f1de6c133d5a6fda2eab7ece52d60f 100644 (file)
@@ -31,48 +31,54 @@ RCSID("$Id$")
 #include       <stdarg.h>
 #include       <ctype.h>
 
-#include       "mapping.h"
+#include       <lber.h>
+#include       <ldap.h>
 
+#define MAX_ATTRMAP            128
+#define MAX_ATTR_STR_LEN       256
 #define MAX_FILTER_STR_LEN     1024
 
 typedef struct {
-       CONF_SECTION   *cs;
+       CONF_SECTION    *cs;
        fr_connection_pool_t *pool;
-       char           *server;
-       int             port;
+       
+       char            *server;
+       int             port;
 
-       char           *login;
-       char           *password;
+       char            *login;
+       char            *password;
 
-       char           *filter;
-       char           *basedn;
+       char            *filter;
+       char            *basedn;
 
        int             chase_referrals;
        int             rebind;
 
-       int             ldap_debug; /* Debug flag for LDAP SDK */
+       int             ldap_debug; /* Debug flag for LDAP SDK */
+
        const char      *xlat_name; /* name used to xlat */
 
-       const char      *map_file;
        int             expect_password;
-       TLDAP_RADIUS    *check_map;
-       TLDAP_RADIUS    *reply_map;
-       char            **attrs;
+       
+       /*
+        *      RADIUS attribute to LDAP attribute maps
+        */
+       VALUE_PAIR_MAP  *user_map;      /* Applied to user object, and profiles */
 
        int             do_xlat;
 
        /*
         *      Access related configuration
         */
-       char           *access_attr;
+       char            *access_attr;
        int             positive_access_attr;
 
        /*
         *      Profiles
         */
-       char           *base_filter;
-       char           *default_profile;
-       char           *profile_attr;
+       char            *base_filter;
+       char            *default_profile;
+       char            *profile_attr;
 
        /*
         *      Group checking.
@@ -85,7 +91,7 @@ typedef struct {
         *      TLS items.  We should really normalize these with the
         *      TLS code in 3.0.
         */
-       int             tls_mode;
+       int             tls_mode;
        int             start_tls;
        char            *tls_cacertfile;
        char            *tls_cacertdir;
@@ -97,7 +103,7 @@ typedef struct {
        /*
         *      Options
         */
-       int             timelimit;
+       int             timelimit;
        int             net_timeout;
        int             timeout;
        int             is_url;
@@ -245,10 +251,6 @@ static const CONF_PARSER module_config[] = {
        {"filter", PW_TYPE_STRING_PTR,
         offsetof(ldap_instance,filter), NULL, "(uid=%u)"},
 
-       /* file with mapping between LDAP and RADIUS attributes */
-       {"dictionary_mapping", PW_TYPE_FILENAME,
-        offsetof(ldap_instance, map_file), NULL, NULL},
-       
        /* turn off the annoying warning if we don't expect a password */
        {"expect_password", PW_TYPE_BOOLEAN,
         offsetof(ldap_instance,expect_password), NULL, "yes"},
@@ -273,18 +275,28 @@ static const CONF_PARSER module_config[] = {
 };
 
 typedef struct ldap_conn {
-       LDAP    *ld;
+       LDAP    *handle;
        int     rebound;
        int     referred;
        ldap_instance *inst;
 } LDAP_CONN;
 
+typedef struct xlat_attrs {
+       const VALUE_PAIR_MAP *maps;
+       const char *attrs[MAX_ATTRMAP];
+} xlat_attrs_t;
+
+typedef struct rlm_ldap_result {
+       char    **values;
+       int     count;
+} rlm_ldap_result_t;
+
 
 #if LDAP_SET_REBIND_PROC_ARGS == 3
 /*
  *     Rebind && chase referral stuff
  */
-static int ldap_rebind(LDAP *ld, LDAP_CONST char *url,
+static int ldap_rebind(LDAP *handle, LDAP_CONST char *url,
                       UNUSED ber_tag_t request, UNUSED ber_int_t msgid,
                       void *ctx )
 {
@@ -292,10 +304,10 @@ static int ldap_rebind(LDAP *ld, LDAP_CONST char *url,
 
        conn->referred = TRUE;
        conn->rebound = TRUE;   /* not really, but oh well... */
-       rad_assert(ld == conn->ld);
+       rad_assert(handle == conn->handle);
 
        DEBUG("  [%s] rebind to URL %s", conn->inst->xlat_name, url);
-       return ldap_bind_s(ld, conn->inst->login, conn->inst->password,
+       return ldap_bind_s(handle, conn->inst->login, conn->inst->password,
                           LDAP_AUTH_SIMPLE);
 }
 #endif
@@ -314,10 +326,11 @@ static int ldap_bind_wrapper(LDAP_CONN **pconn, const char *user,
        struct timeval tv;
 
 redo:
-       ldap_errno = ldap_bind(conn->ld, user, password, LDAP_AUTH_SIMPLE);
+       ldap_errno = ldap_bind(conn->handle, user, password, LDAP_AUTH_SIMPLE);
        if (ldap_errno < 0) {
        get_error:
-               ldap_get_option(conn->ld, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
+               ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
+                               &ldap_errno);
                error_string = ldap_err2string(ldap_errno);
 
                if (do_rebind && !reconnect) {
@@ -351,7 +364,7 @@ redo:
 
        tv.tv_sec = inst->timeout;
        tv.tv_usec = 0;
-       rcode = ldap_result(conn->ld, ldap_errno, 1, &tv, &result);
+       rcode = ldap_result(conn->handle, ldap_errno, 1, &tv, &result);
        if (rcode < 0) goto get_error;
 
        if (rcode == 0) {
@@ -359,7 +372,7 @@ redo:
                goto print_error;
        }
 
-       ldap_errno = ldap_result2error(conn->ld, result, 1);
+       ldap_errno = ldap_result2error(conn->handle, result, 1);
        switch (ldap_errno) {
        case LDAP_SUCCESS:
                break;
@@ -390,7 +403,7 @@ static void *ldap_conn_create(void *ctx)
        int ldap_errno, ldap_version;
        struct timeval tv;
        ldap_instance *inst = ctx;
-       LDAP *ld = NULL;
+       LDAP *handle = NULL;
        LDAP_CONN *conn = NULL;
        const char *error;
 
@@ -398,7 +411,7 @@ static void *ldap_conn_create(void *ctx)
        if (inst->is_url) {
                DEBUG("  [%s] Connect to %s", inst->xlat_name, inst->server);
 
-               ldap_errno = ldap_initialize(&ld, inst->server);
+               ldap_errno = ldap_initialize(&handle, inst->server);
 
                if (ldap_errno != LDAP_SUCCESS) {
                        radlog(L_ERR, "  [%s] ldap_initialize() failed: %s",
@@ -411,11 +424,11 @@ static void *ldap_conn_create(void *ctx)
                DEBUG("  [%s] Connect to %s:%d", inst->xlat_name,
                      inst->server, inst->port);
 
-               ld = ldap_init(inst->server, inst->port);
-               if (!ld) {
+               handle = ldap_init(inst->server, inst->port);
+               if (!handle) {
                        radlog(L_ERR, "  [%s] ldap_init() failed", inst->xlat_name);
                conn_fail:
-                       if (ld) ldap_unbind_s(ld);
+                       if (handle) ldap_unbind_s(handle);
                        return NULL;
                }
        }
@@ -426,8 +439,8 @@ static void *ldap_conn_create(void *ctx)
         *      Set a bunch of LDAP options, using common code.
         */
 
-#define do_ldap_option(_option, _name, _value) if (ldap_set_option(ld, _option, _value) != LDAP_OPT_SUCCESS) { \
-               ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ldap_errno); \
+#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, "  [%s] Could not set %s: %s", inst->xlat_name, _name, ldap_err2string(ldap_errno)); \
        }
                
@@ -445,7 +458,7 @@ static void *ldap_conn_create(void *ctx)
                        
 #if LDAP_SET_REBIND_PROC_ARGS == 3
                        if (inst->rebind == 1) {
-                               ldap_set_rebind_proc(ld, ldap_rebind, inst);
+                               ldap_set_rebind_proc(handle, ldap_rebind, inst);
                        }
 #endif
                } else {
@@ -492,7 +505,7 @@ static void *ldap_conn_create(void *ctx)
 #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(ld, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
+               ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
                radlog(L_ERR, "  [%s] could not set ", 
                       "LDAP_OPT_X_TLS_REQUIRE_CERT option to %s: %s",
                       inst->xlat_name, 
@@ -512,9 +525,9 @@ static void *ldap_conn_create(void *ctx)
         *      And finally start the TLS code.
         */
        if (inst->start_tls && (inst->port != 636)) {
-               ldap_errno = ldap_start_tls_s(ld, NULL, NULL);
+               ldap_errno = ldap_start_tls_s(handle, NULL, NULL);
                if (ldap_errno != LDAP_SUCCESS) {
-                       ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
+                       ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER,
                                        &ldap_errno);
                        radlog(L_ERR, "  [%s] could not start TLS: %s",
                               inst->xlat_name,
@@ -526,7 +539,7 @@ static void *ldap_conn_create(void *ctx)
 
        conn = rad_malloc(sizeof(*conn));
        conn->inst = inst;
-       conn->ld = ld;
+       conn->handle = handle;
        conn->rebound = FALSE;
        conn->referred = FALSE;
 
@@ -557,7 +570,7 @@ static int ldap_conn_delete(UNUSED void *ctx, void *connection)
 {
        LDAP_CONN *conn = connection;
 
-       ldap_unbind_s(conn->ld);
+       ldap_unbind_s(conn->handle);
        free(conn);
 
        return 0;
@@ -683,14 +696,22 @@ static size_t ldap_escape_func(UNUSED REQUEST *request, char *out,
  *
  *************************************************************************/
 static int perform_search(ldap_instance *inst, LDAP_CONN **pconn,
-                         char *search_basedn, int scope, char *filter,
-                         char **attrs, LDAPMessage **presult)
+                         const char *search_basedn, int scope,
+                         const char *filter, const char *attrs[],
+                         LDAPMessage **presult)
 {
        int             ldap_errno;
        int             reconnect = FALSE;
        LDAP_CONN       *conn = *pconn;
        struct timeval  tv;
 
+       /*
+        *      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;
 
        /*
@@ -715,8 +736,9 @@ static int perform_search(ldap_instance *inst, LDAP_CONN **pconn,
               search_basedn ? search_basedn : "(null)" , filter);
 
 retry:
-       ldap_errno = ldap_search_st(conn->ld, search_basedn, scope, filter,
-                                   attrs, 0, &tv, presult);
+       ldap_errno = ldap_search_ext_s(conn->handle, search_basedn, scope,
+                                      filter, search_attrs, 0, NULL, NULL,
+                                      &tv, 0, presult);
        switch (ldap_errno) {
        case LDAP_SUCCESS:
        case LDAP_NO_SUCH_OBJECT:
@@ -759,20 +781,22 @@ retry:
                 *      Reconnect.  There's an issue with the socket
                 *      or LDAP server.
                 */
-               ldap_get_option(conn->ld, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
-               radlog(L_ERR, "  [%s] ldap_search() failed: %s", inst->xlat_name,
-                      ldap_err2string(ldap_errno));
+               ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
+                               &ldap_errno);
+               radlog(L_ERR, "  [%s] ldap_search() failed: %s",
+                      inst->xlat_name, ldap_err2string(ldap_errno));
                goto do_reconnect;
 
        default:
-               ldap_get_option(conn->ld, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
-               radlog(L_ERR, "  [%s] ldap_search() failed: %s", inst->xlat_name,
-                      ldap_err2string(ldap_errno));
+               ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER,
+                               &ldap_errno);
+               radlog(L_ERR, "  [%s] ldap_search() failed: %s",
+                      inst->xlat_name, ldap_err2string(ldap_errno));
                ldap_msgfree(*presult);
                return -1;
        }
 
-       ldap_errno = ldap_count_entries(conn->ld, *presult);
+       ldap_errno = ldap_count_entries(conn->handle, *presult);
        if (ldap_errno == 0) {
                ldap_msgfree(*presult);
                DEBUG("  [%s] object not found", inst->xlat_name);
@@ -781,7 +805,8 @@ retry:
 
        if (ldap_errno != 1) {
                ldap_msgfree(*presult);
-               DEBUG("  [%s] got ambiguous search result (%d results)", inst->xlat_name, ldap_errno);
+               DEBUG("  [%s] got ambiguous search result (%d results)",
+                     inst->xlat_name, ldap_errno);
                return -2;
        }
 
@@ -804,10 +829,11 @@ static size_t ldap_xlat(void *instance, REQUEST *request, const char *fmt,
        ldap_instance *inst = instance;
        LDAPURLDesc *ldap_url;
        LDAPMessage *result = NULL;
-       LDAPMessage *msg = NULL;
+       LDAPMessage *entry = NULL;
        char **vals;
        LDAP_CONN *conn;
        const char *url;
+       const char **attrs;
        char buffer[MAX_FILTER_STR_LEN];
 
        if (strchr(fmt, '%') != NULL) {
@@ -852,8 +878,10 @@ static size_t ldap_xlat(void *instance, REQUEST *request, const char *fmt,
        conn = ldap_get_socket(inst);
        if (!conn) goto free_urldesc;
 
+       memcpy(&attrs, &ldap_url->lud_attrs, sizeof(attrs));
+       
        rcode = perform_search(inst, &conn, ldap_url->lud_dn, ldap_url->lud_scope,
-                              ldap_url->lud_filter, ldap_url->lud_attrs, &result);
+                              ldap_url->lud_filter, attrs, &result);
        if (rcode < 0) {
                if (rcode == -2) {
                        DEBUG("  [%s] Search returned not found", inst->xlat_name);
@@ -863,13 +891,13 @@ static size_t ldap_xlat(void *instance, REQUEST *request, const char *fmt,
                goto free_socket;
        }
 
-       msg = ldap_first_entry(conn->ld, result);
-       if (!msg) {
+       entry = ldap_first_entry(conn->handle, result);
+       if (!entry) {
                DEBUG("  [%s] ldap_first_entry() failed", inst->xlat_name);
                goto free_result;
        }
 
-       vals = ldap_get_values(conn->ld, msg, ldap_url->lud_attrs[0]);
+       vals = ldap_get_values(conn->handle, entry, ldap_url->lud_attrs[0]);
        if (!vals) {
                DEBUG("  [%s] ldap_get_values failed", inst->xlat_name);
                goto free_result;
@@ -900,10 +928,11 @@ static char *get_userdn(LDAP_CONN **pconn, REQUEST *request, int *module_rcode)
 {
        int             rcode;
        VALUE_PAIR      *vp;
-       ldap_instance   *inst = (*pconn)->inst;
-       LDAPMessage    *result, *msg;
+       ldap_instance   *inst = (*pconn)->inst;
+       LDAPMessage     *result, *entry;
        static char     firstattr[] = "uid";
-       char           *user_dn, *attrs[] = {firstattr, NULL};
+       char            *user_dn;
+       const char      *attrs[] = {firstattr, NULL};
         char            filter[MAX_FILTER_STR_LEN];    
         char            basedn[MAX_FILTER_STR_LEN];    
 
@@ -936,12 +965,12 @@ static char *get_userdn(LDAP_CONN **pconn, REQUEST *request, int *module_rcode)
                return NULL;
        }
 
-       if ((msg = ldap_first_entry((*pconn)->ld, result)) == NULL) {
+       if ((entry = ldap_first_entry((*pconn)->handle, result)) == NULL) {
                ldap_msgfree(result);
                return NULL;
        }
 
-       if ((user_dn = ldap_get_dn((*pconn)->ld, msg)) == NULL) {
+       if ((user_dn = ldap_get_dn((*pconn)->handle, entry)) == NULL) {
                ldap_msgfree(result);
                return NULL;
        }
@@ -974,17 +1003,17 @@ static int ldap_groupcmp(void *instance, REQUEST *request,
         ldap_instance   *inst = instance;
         int             i, rcode, found;
         LDAPMessage     *result = NULL;
-        LDAPMessage     *msg = NULL;
+        LDAPMessage     *entry = NULL;
        static char     firstattr[] = "dn";
-       char            *attrs[] = {firstattr,NULL};
+       const char      *attrs[] = {firstattr, NULL};
        char            **vals;
-       char            *group_attrs[] = {inst->groupmemb_attr,NULL};
+       const char      *group_attrs[] = {inst->groupmemb_attr, NULL};
        LDAP_CONN       *conn;
        char            *user_dn;
        int             module_rcode;
-        char            gr_filter[MAX_FILTER_STR_LEN];
-        char            filter[MAX_FILTER_STR_LEN];
-        char            basedn[MAX_FILTER_STR_LEN];
+       char            gr_filter[MAX_FILTER_STR_LEN];
+       char            filter[MAX_FILTER_STR_LEN];
+       char            basedn[MAX_FILTER_STR_LEN];
        
        if (check->length == 0) {
                 RDEBUG("Cannot do comparison: group name is empty");
@@ -1074,15 +1103,15 @@ check_attr:
                return 1;
        }
 
-       msg = ldap_first_entry(conn->ld, result);
-       if (!msg) {
+       entry = ldap_first_entry(conn->handle, result);
+       if (!entry) {
                RDEBUG("First entry failed for group attrs");
                ldap_release_socket(inst, conn);
                ldap_msgfree(result);
                return 1;
        }
 
-       vals = ldap_get_values(conn->ld, msg, inst->groupmemb_attr);
+       vals = ldap_get_values(conn->handle, entry, inst->groupmemb_attr);
        if (!vals) {
                RDEBUG("Get values failed for group attrs");
                ldap_release_socket(inst, conn);
@@ -1139,6 +1168,74 @@ check_attr:
         return 0;
 }
 
+/*
+ *     Verify that the ldap update section makes sense, and add attribute names
+ *     to array of attributes for efficient querying later.
+ */
+static VALUE_PAIR_MAP *build_attrmap(CONF_SECTION *cs)
+{
+       const char *cs_list, *p;
+
+       request_refs_t request_def = REQUEST_CURRENT;
+       pair_lists_t list_def = PAIR_LIST_REQUEST;
+
+       CONF_ITEM *ci = cf_sectiontoitem(cs);
+       CONF_PAIR *cp;
+
+       unsigned int total = 0;
+       VALUE_PAIR_MAP **tail, *map, *head;
+       head = NULL;
+       tail = &head;
+       
+       if (!cs) return NULL;
+       
+       cs_list = p = cf_section_name2(cs);
+       if (cs_list) {
+               request_def = radius_request_name(&p, REQUEST_UNKNOWN);
+               if (request_def == REQUEST_UNKNOWN) {
+                       cf_log_err(ci, "rlm_ldap: Default request specified "
+                                  "in mapping section is invalid");
+                       return NULL;
+               }
+               
+               list_def = fr_str2int(pair_lists, p, PAIR_LIST_UNKNOWN);
+               if (list_def == PAIR_LIST_UNKNOWN) {
+                       cf_log_err(ci, "rlm_ldap: Default list specified "
+                                  "in mapping section is invalid");
+                       return NULL;
+               }
+       }
+
+       for (ci = cf_item_find_next(cs, NULL);
+            ci != NULL;
+            ci = cf_item_find_next(cs, ci)) {
+               if (total++ == MAX_ATTRMAP) {
+                       cf_log_err(ci, "rlm_ldap: Attribute map size exceeded");
+                       goto error;
+               }
+               
+               if (!cf_item_is_pair(ci)) {
+                       cf_log_err(ci, "rlm_ldap: Entry is not in \"attribute ="
+                                      " ldap-attribute\" format");
+                       goto error;
+               }
+       
+               cp = cf_itemtopair(ci);
+               map = radius_cp2map(cp, REQUEST_CURRENT, list_def);
+               if (!map) {
+                       goto error;
+               }
+               
+               *tail = map;
+               tail = &(map->next);
+       }
+
+       return head;
+       
+       error:
+               radius_mapfree(&head);
+               return NULL;
+}
 
 /*****************************************************************************
  *
@@ -1150,11 +1247,9 @@ static int ldap_detach(void *instance)
        ldap_instance *inst = instance;
 
        fr_connection_pool_delete(inst->pool);
-
-       if (inst->map_file) {
-               rlm_ldap_map_free(&inst->check_map);
-               rlm_ldap_map_free(&inst->reply_map);
-               free(inst->attrs);
+       
+       if (inst->user_map) {
+               radius_mapfree(&inst->user_map);
        }
 
        free(inst);
@@ -1173,6 +1268,7 @@ static int ldap_detach(void *instance)
 static int ldap_instantiate(CONF_SECTION * conf, void **instance)
 {
        ldap_instance *inst;
+       CONF_SECTION *cs;
 
        inst = rad_malloc(sizeof *inst);
        if (!inst) {
@@ -1205,7 +1301,8 @@ static int ldap_instantiate(CONF_SECTION * conf, void **instance)
                inst->is_url = 1;
                inst->port = 0;
 #else
-               radlog(L_ERR, "rlm_ldap: 'server' directive is in URL form but ldap_initialize() is not available.");
+               radlog(L_ERR, "rlm_ldap: 'server' directive is in URL form but "
+                      "ldap_initialize() is not available.");
                ldap_detach(inst);
                return -1;
 #endif
@@ -1228,39 +1325,21 @@ static int ldap_instantiate(CONF_SECTION * conf, void **instance)
         *      variable for the username, password, etc.
         */
        if (inst->rebind == 1) {
-               radlog(L_ERR, "%s: Cannot use 'rebind' directive as this version of libldap does not support the API that we need.", inst->xlat-name);
+               radlog(L_ERR, "%s: Cannot use 'rebind' directive as this "
+                      "version of libldap does not support the API that "
+                      "we need.", inst->xlat-name);
                ldap_detach(inst);
                return -1;
        }
 #endif
 
-       if (inst->map_file) {
-               int offset = 0;
-
-               inst->attrs = rad_malloc(sizeof(inst->attrs[0]) * MAX_ATTRMAP);
-               inst->attrs[0] = NULL;
-
-               if (inst->profile_attr) inst->attrs[offset++] = inst->profile_attr;
-               if (inst->access_attr) inst->attrs[offset++] = inst->access_attr;
-
-               if (rlm_ldap_map_read(inst->xlat_name, inst->map_file,
-                                     &inst->check_map, &inst->reply_map,
-                                     offset,
-                                     inst->attrs) < 0) {
-                       ldap_detach(inst);
-                       return -1;
-               }
-
-               if (!inst->check_map || !inst->reply_map) {
-                       radlog(L_ERR, "%s: Empty file: %s",
-                              inst->xlat_name, inst->map_file);
-                       ldap_detach(inst);
-                       return -1;
-               }
-
-       } else {
-               if (inst->default_profile || inst->profile_attr) {
-                       radlog(L_ERR, "%s: You MUST specify \"dictionary_mapping\" to use the profile attributes", inst->xlat_name);
+       /*
+        *      Build the attribute map
+        */
+       cs = cf_section_sub_find(conf, "update");
+       if (cs) {       
+               inst->user_map = build_attrmap(cs);
+               if (!inst->user_map) {
                        ldap_detach(inst);
                        return -1;
                }
@@ -1327,12 +1406,12 @@ static void module_failure_msg(VALUE_PAIR **vps, const char *fmt, ...)
 }
 
 
-static int check_access(ldap_instance *inst, LDAP_CONN *conn, LDAPMessage *msg)
+static int check_access(ldap_instance *inst, LDAP_CONN *conn, LDAPMessage *entry)
 {
        int rcode = -1;
        char **vals = NULL;
 
-       vals = ldap_get_values(conn->ld, msg, inst->access_attr);
+       vals = ldap_get_values(conn->handle, entry, inst->access_attr);
        if (vals) {
                if (inst->positive_access_attr) {
                        if (strncmp(vals[0], "FALSE", 5) == 0) {
@@ -1360,70 +1439,156 @@ static int check_access(ldap_instance *inst, LDAP_CONN *conn, LDAPMessage *msg)
 }
 
 
-static void apply_profile(ldap_instance *inst, LDAP_CONN **pconn,
-                         REQUEST *request, char *profile)
+static VALUE_PAIR *ldap_getvalue(REQUEST *request, VALUE_PAIR_TMPL *dst,
+                                void *ctx)
 {
-       int rcode;
-       LDAPMessage     *result, *msg;
-       VALUE_PAIR      *check_tmp, *reply_tmp;
-       char            filter[MAX_FILTER_STR_LEN];
+       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(dst->da);
+               rad_assert(vp);
 
-       if (!profile || !*profile) return;
+               pairparsevalue(vp, self->values[i]);
+               
+               *tail = vp;
+               tail = &(vp->next);
+       }
+       
+       return head;            
+}
 
-       strlcpy(filter,inst->base_filter,sizeof(filter));
 
-       rcode = perform_search(inst, pconn, profile, LDAP_SCOPE_BASE, filter,
-                              inst->attrs, &result);
-       if (rcode < 0) {
-               RDEBUG("FAILED Searching profile %s", profile);
-               return;
+static void xlat_attrsfree(const xlat_attrs_t *expanded)
+{
+       const VALUE_PAIR_MAP *map;
+       unsigned int total = 0;
+       
+       char *name;
+       
+       for (map = expanded->maps; map != NULL; map = map->next)
+       {
+               memcpy(&name, &(expanded->attrs[total++]), sizeof(name));
+               
+               if (!name) return;
+               
+               if (map->src.do_xlat) {
+                       free(name);
+               }
        }
+}
 
-       msg = ldap_first_entry((*pconn)->ld, result);
-       if (!msg) goto free_result;
 
-       check_tmp = rlm_ldap_pairget(inst->xlat_name, (*pconn)->ld, msg,
-                                    inst->check_map, &request->config_items, 1);
-       if (check_tmp) {
-               pairfree(&check_tmp);
-       }
+static int xlat_attrs(REQUEST *request, const VALUE_PAIR_MAP *maps,
+                     xlat_attrs_t *expanded)
+{
+       const VALUE_PAIR_MAP *map;
+       unsigned int total = 0;
+       
+       size_t len;
+       char *buffer;
 
-       reply_tmp = rlm_ldap_pairget(inst->xlat_name, (*pconn)->ld, msg,
-                                    inst->reply_map, &request->packet->vps, 0);
-       if (reply_tmp) {
-               pairfree(&reply_tmp);
+       for (map = maps; map != NULL; map = map->next)
+       {
+               if (map->src.do_xlat) {
+                       buffer = rad_malloc(MAX_ATTR_STR_LEN);
+                       len = radius_xlat(buffer, MAX_ATTR_STR_LEN,
+                                         map->src.name, request, NULL, NULL);
+                                         
+                       if (!len) {
+                               DEBUG2("WARNING: Expansion of LDAP attribute "
+                                      "\"%s\" failed", map->src.name);
+                                      
+                               expanded->attrs[total] = NULL;
+                               
+                               xlat_attrsfree(expanded);
+                               
+                               return -1;
+                       }
+                       
+                       expanded->attrs[total++] = buffer;
+               } else {
+                       expanded->attrs[total++] = map->src.name;
+               }
        }
-
-free_result:
-       ldap_msgfree(result);
+       
+       expanded->attrs[total] = NULL;
+       expanded->maps = maps;
+       
+       return 0;
 }
 
 
-static void do_one_map(ldap_instance *inst, LDAP *ld, REQUEST *request,
-                      LDAPMessage *msg, TLDAP_RADIUS *map, VALUE_PAIR **vps,
-                      int is_check)
+/** 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)
 {
-       VALUE_PAIR *vp;
+       const VALUE_PAIR_MAP    *map;
+       unsigned int            total = 0;
+       
+       rlm_ldap_result_t       result;
 
-       vp = rlm_ldap_pairget(inst->xlat_name, ld, msg, map, vps, is_check);
-       if (vp) {
-               if (inst->do_xlat) {
-                       pairxlatmove(request, vps, &vp);
-                       pairfree(&vp);
+       REQUEST                 *update_request;
+       const char              *name;
 
-               } else {
-                       pairadd(vps, vp);
+       for (map = expanded->maps; map != NULL; map = map->next)
+       {
+               update_request = request;
+               
+               name = expanded->attrs[total++];
+               
+               result.values = ldap_get_values(handle, entry, name);
+               if (!result.values) {
+                       DEBUG2("WARNING: 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:
+               
+               ldap_value_free(result.values);
        }
-
 }
 
-static void do_check_reply(ldap_instance *inst, LDAP *ld, REQUEST *request,
-                          LDAPMessage *msg)
-{
-       do_one_map(inst, ld, request, msg, inst->check_map, &request->config_items, 1);
-       do_one_map(inst, ld, request, msg, inst->reply_map, &request->reply->vps, 0);
 
+static void do_check_reply(ldap_instance *inst, REQUEST *request)
+{
        /*
        *       More warning messages for people who can't be bothered
        *       to read the documentation.
@@ -1439,6 +1604,37 @@ static void do_check_reply(ldap_instance *inst, LDAP *ld, REQUEST *request,
        }
 }
 
+
+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;
+       char            filter[MAX_FILTER_STR_LEN];
+
+       if (!profile || !*profile) return;
+
+       strlcpy(filter, inst->base_filter, sizeof(filter));
+
+       rcode = perform_search(inst, pconn, profile, LDAP_SCOPE_BASE,
+                              filter, expanded->attrs, &result);
+               
+       if (rcode < 0) {
+               RDEBUG("FAILED Searching profile %s", profile);
+               goto free_result;
+       }
+
+       entry = ldap_first_entry((*pconn)->handle, result);
+       if (!entry) goto free_result;
+
+       do_attrmap(inst, request, (*pconn)->handle, expanded, entry);
+
+free_result:
+       ldap_msgfree(result);
+}
+
+
 /******************************************************************************
  *
  *      Function: ldap_authorize
@@ -1455,10 +1651,11 @@ static int ldap_authorize(void *instance, REQUEST * request)
        char            **vals;
        VALUE_PAIR      *vp;
        LDAP_CONN       *conn;
-       LDAPMessage     *result, *msg;
+       LDAPMessage     *result, *entry;
        char            filter[MAX_FILTER_STR_LEN];
        char            basedn[MAX_FILTER_STR_LEN];
-
+       xlat_attrs_t    expanded; /* faster that mallocing every time */
+       
        if (!request->username) {
                RDEBUG2("attribute \"User-Name\" is required for authorization.\n");
                return RLM_MODULE_NOOP;
@@ -1486,9 +1683,14 @@ static int ldap_authorize(void *instance, REQUEST * request)
 
        conn = ldap_get_socket(inst);
        if (!conn) return RLM_MODULE_FAIL;
-
+       
+       if (xlat_attrs(request, inst->user_map, &expanded) < 0) {
+               return RLM_MODULE_FAIL;
+       }
+       
        rcode = perform_search(inst, &conn, basedn, LDAP_SCOPE_SUBTREE, filter,
-                              inst->attrs, &result);
+                              expanded.attrs, &result);
+       
        if (rcode < 0) {
                if (rcode == -2) {
                        module_failure_msg(&request->packet->vps,
@@ -1502,13 +1704,13 @@ static int ldap_authorize(void *instance, REQUEST * request)
                goto free_socket;
        }
 
-       msg = ldap_first_entry(conn->ld, result);
-       if (!msg) {
+       entry = ldap_first_entry(conn->handle, result);
+       if (!entry) {
                RDEBUG2("ldap_first_entry() failed");
                goto free_result;
        }
 
-       user_dn = ldap_get_dn(conn->ld, msg);
+       user_dn = ldap_get_dn(conn->handle, entry);
        if (!user_dn) {
                RDEBUG2("ldap_get_dn() failed");
                goto free_result;
@@ -1525,7 +1727,7 @@ static int ldap_authorize(void *instance, REQUEST * request)
         *      Check for access.
         */
        if (inst->access_attr) {
-               if (check_access(inst, conn, msg) < 0) {
+               if (check_access(inst, conn, entry) < 0) {
                        module_rcode = RLM_MODULE_USERLOCK;
                        goto free_result;
                }
@@ -1540,29 +1742,31 @@ static int ldap_authorize(void *instance, REQUEST * request)
 
                if (vp) profile = vp->vp_strvalue;
 
-               apply_profile(inst, &conn, request, profile);
+               apply_profile(inst, request, &conn, profile, &expanded);
        }
 
        /*
         *      Apply a SET of user profiles.
         */
        if (inst->profile_attr &&
-           (vals = ldap_get_values(conn->ld, msg, inst->profile_attr)) != NULL) {
+           (vals = ldap_get_values(conn->handle, entry, inst->profile_attr)) != NULL) {
 
                int i;
 
                for (i = 0; (vals[i] != NULL) && (*vals[i] != '\0'); i++) {
-                       apply_profile(inst, &conn, request, vals[i]);
+                       apply_profile(inst, request, &conn, vals[i], &expanded);
                }
 
                ldap_value_free(vals);
        }
 
-       if (inst->map_file) {
-               do_check_reply(inst, conn->ld, request, msg);
+       if (inst->user_map) {
+               do_attrmap(inst, request, conn->handle, &expanded, entry);
+               do_check_reply(inst, request);
        }
        
 free_result:
+       xlat_attrsfree(&expanded);
        ldap_msgfree(result);
 free_socket:
        ldap_release_socket(inst, conn);