From: Arran Cudbard-Bell Date: Fri, 30 Nov 2012 17:11:40 +0000 (+0000) Subject: Switch to using new config based attribute map X-Git-Tag: release_3_0_0_beta1~1466 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2a8c11646b660b0c52eab9fa8e9e8cff3603f262;p=thirdparty%2Ffreeradius-server.git Switch to using new config based attribute map --- diff --git a/raddb/ldap.attrmap b/raddb/ldap.attrmap deleted file mode 100644 index 1aa7592c72f..00000000000 --- a/raddb/ldap.attrmap +++ /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 diff --git a/raddb/mods-available/ldap b/raddb/mods-available/ldap index c4ae0ec72bc..875b0f71835 100644 --- a/raddb/mods-available/ldap +++ b/raddb/mods-available/ldap @@ -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: + # + # + # Where: + # : The destination RADIUS attribute + # with any valid list and request qualifiers. + # : Is any assignment 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. diff --git a/src/modules/rlm_ldap/Makefile.in b/src/modules/rlm_ldap/Makefile.in index 058ef3bb524..13f540aa5d0 100644 --- a/src/modules/rlm_ldap/Makefile.in +++ b/src/modules/rlm_ldap/Makefile.in @@ -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@ diff --git a/src/modules/rlm_ldap/all.mk.in b/src/modules/rlm_ldap/all.mk.in index a0720527305..591728e52cd 100644 --- a/src/modules/rlm_ldap/all.mk.in +++ b/src/modules/rlm_ldap/all.mk.in @@ -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 index 6649c8619cb..00000000000 --- a/src/modules/rlm_ldap/mapping.c +++ /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 - */ - -#include -RCSID("$Id$") - -#include -#include -#include - -#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 - * ) - * - *****************************************************************************/ -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 index 29b5f14ee88..00000000000 --- a/src/modules/rlm_ldap/mapping.h +++ /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 - */ - -#include -#include - -/* 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); diff --git a/src/modules/rlm_ldap/rlm_ldap.c b/src/modules/rlm_ldap/rlm_ldap.c index 1334e01f376..4db15a0932f 100644 --- a/src/modules/rlm_ldap/rlm_ldap.c +++ b/src/modules/rlm_ldap/rlm_ldap.c @@ -31,48 +31,54 @@ RCSID("$Id$") #include #include -#include "mapping.h" +#include +#include +#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);