Add a new configuration directive, do_xlat (default: yes). If set we use pairxlatmove
[freeradius.git] / src / modules / rlm_ldap / rlm_ldap.c
1 /*
2  * rlm_ldap.c   LDAP authorization and authentication module.
3  *
4  *   This program is free software; you can redistribute it and/or modify
5  *   it under the terms of the GNU General Public License as published by
6  *   the Free Software Foundation; either version 2 of the License, or
7  *   (at your option) any later version.
8  *
9  *   This program is distributed in the hope that it will be useful,
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *   GNU General Public License for more details.
13  *
14  *   You should have received a copy of the GNU General Public License
15  *   along with this program; if not, write to the Free Software
16  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  * This module is based on LDAP patch to Cistron radiusd by James Golovich 
19  * <james@wwnet.net>, which in turn was based mostly on a Mysql+Cistron patch 
20  * from <oyarzun@wilmington.net>
21  * 
22  * 17 Jan 2000, Adrian Pavlykevych <pam@polynet.lviv.ua>
23  *      - OpenLDAP SDK porting, basic TLS support, LDAP authorization,
24  *        fault tolerance with multiple LDAP server support 
25  * 24 May 2000, Adrian Pavlykevych <pam@polynet.lviv.ua> 
26  *      - Converting to new configuration file format, futher improvements
27  *        in fault tolerance, threaded operation
28  * 12 Dec 2000, Adrian Pavlykevych <pam@polynet.lviv.ua> 
29  *      - Added preliminary support for multiple instances
30  *      - moved all instance configuration into dynamicly allocated structure
31  *      - Removed connection maintenance thread and all attempts for multihreading
32  *        the module itself. OpenLDAP SDK is not thread safe when used with shared
33  *        LDAP connection.
34  *      - Added configuration option for defining LDAP attribute of user object,
35  *        which controls remote access.
36  * 16 Feb 2001, Hannu Laurila <hannu.laurila@japo.fi>
37  *      - LDAP<->RADIUS attribute mappings are now read from a file
38  *      - Support for generic RADIUS check and reply attribute.
39  * Jun 2001, Kostas Kalevras <kkalev@noc.ntua.gr>
40  *      - Fix: check and reply attributes from LDAP _replace_ existing ones
41  *      - Added "default_profile" directive, which points to radiusProfile 
42  *        object, which contains default values for RADIUS users
43  *      - Added "profile_attribute" directive, which specifies user object 
44  *        attribute pointing to radiusProfile object.
45  * Nov 2001, Kostas Kalevras <kkalev@noc.ntua.gr>
46  *      - Added support for adding the user password to the check. Based on
47  *        the password_header directive rlm_ldap will strip the
48  *        password header if needed. This will make support for CHAP much easier.
49  *      - Added module messages when we reject a user.
50  *      - Added ldap_groupcmp to allow searching for user group membership.
51  *      - Added ldap_xlat to allow ldap urls in xlat strings. Something like:
52  *        %{ldap:ldap:///dc=company,dc=com?cn?sub?uid=user}
53  * Nov 2001, Gordon Tetlow <gordont@gnf.org>
54  *      - Do an xlat on the access_group attribute.
55  * Dec 2001, Kostas Kalevras <kkalev@noc.ntua.gr>
56  *      - Added ldap caching for the default/regular profiles and group entries.
57  *      - Fixed a memory leak in ldap_xlat.
58  *      - Removed dict_attrbyname from ldap_pairget. They are not needed.
59  *      - Moved the radius_xlat's for filter and basedn in ldap_authenticate() to
60  *        the right place.
61  *      - Made the module thread safe. We create a connection pool and each thread
62  *        will call ldap_get_conn to lock one of the ldap connections and release with
63  *        a call to ldap_release_conn when it has finished.
64  *      - Request only the user attributes that interest us (radius attributes,regular
65  *        profile,user password and access attribute).
66  * Mar 2002, Kostas Kalevras <kkalev@noc.ntua.gr>
67  *      - Fixed a bug where the ldap server will kill the idle connections from the ldap
68  *        connection pool. We now check if ldap_search returns LDAP_SERVER_DOWN and try to
69  *        reconnect if it does. Bug noted by Dan Perik <dan_perik-work@ntm.org.pg>
70  * May 2002, Kostas Kalevras <kkalev@noc.ntua.gr>
71  *      - Instead of the Group attribute we now have the Ldap-Group attribute, to avoid
72  *        collisions with other modules
73  *      - If perform_search fails check the ld != NULL before using it. Based on a bug report
74  *        by John <jhogenmiller@pennswoods.net>
75  * Jun 2002, Kostas Kalevras <kkalev@noc.ntua.gr>
76  *      - Add the ability to do a paircmp on the check items. Add a compare_check_items boolean
77  *        configuration directive which defaults to no. If it is set then we will do a compare
78  *      - Add another configuration directive. access_attr_used_for_allow. If it is set to yes
79  *        then the access_attr will be used to allow user access. If it is set to no then it will
80  *        be used to deny user access.
81  *      - Remember to free inst->atts in ldap_detach()
82  *      - Add a forgotten ldap_free_urldesc in ldap_xlat()
83  *      - Add a variable locked in the LDAP_CONN structure. We use this to avoid deadlocks. The mutex
84  *        we are using is of type fast and can deadlock if the same thread tries to relock it. That
85  *        could happen in case of calls to xlat.
86  *      - When ldap_search returns NO_SUCH_OBJECT don't return fail but notfound
87  * Jul 2002, Kostas Kalevras <kkalev@noc.ntua.gr>
88  *      - Fix the logic when we get an LDAP_SERVER_DOWN or we have conn->ld == NULL in perform_search
89  *      - Try to minimize the penalty of having the ldap server go down. The comments before
90  *        MAX_FAILED_CONNS_* definitions should explain things.
91  *      - Check for a number of error codes from ldap_search and log corresponding error messages
92  *        We should only reconnect when that can help things.
93  *      - In ldap_groupcmp instead of first searching for the group object and then checking user
94  *        group membership combine them in one ldap search operation. That should make group
95  *        membership checks a lot faster.
96  *      - Remember to do ldap_release_conn and ldap_msgfree when we do paircmp and the result is reject
97  * Aug 2002, Kostas Kalevras <kkalev@noc.ntua.gr>
98  *      - Add support for group membership attribute inside the user entry in ldap_groupcmp. The attribute
99  *        can either contain the name or the DN of the group. Added the groupmembership_attribute
100  *        configuration directive
101  *      - Move the ldap_{get,release}_conn in ldap_groupcmp so that we hold a connection for the minimum time.
102  *      - Now that ldap_groupcmp is complete we really don't need access_group. Removed it.
103  *      - Remember to free groupmembership_attribute in ldap_detach
104  *      - Don't delete existing generic attributes in ldap_pairget when adding new ones. Since generic attributes
105  *        have operators we don't need to try to be cleaver.
106  * Sep 2002, Kostas Kalevras <kkalev@noc.ntua.gr>
107  *      - Fix a crash in ldap_pairget when the attribute value is larger than the buffer size
108  *        Bug report by Stefan Radovanovici <sra@rtsffm.com>
109  *      - If we add a check item then use the == operator. Based on an idea by Allister Maguire <amaguire@gnc.net.nz>
110  *      - Only add a failure message for bind as user failed in ldap_authenticate if the result of ldap_connect was
111  *        RLM_MODULE_REJECT
112  *      - Make tls_mode a configurable option. Patch from John <jhogenmiller@pennswoods.net>
113  *      - Allow multiple regular profiles for an entry
114  * Oct 2002, Kostas Kalevras <kkalev@noc.ntua.gr>
115  *      - Disable cache after searching for the default profile
116  *      - Use the MAX_FAILED_CONNS_* in ldap_authenticate() when calling ldap_connect()
117  * Nov 2002, Kostas Kalevras <kkalev@noc.ntua.gr>
118  *      - Set LDAP version to V3 before binding. Now freeradius should work with openldap21
119  * Dec 2002, Kostas Kalevras <kkalev@noc.ntua.gr>
120  *      - Set default values for the server and basedn parameters
121  * Feb 2003, Kostas Kalevras <kkalev@noc.ntua.gr>
122  *      - Add support for ldap_initialize. That way we can specify the server as an ldap url.
123  *        Based on ideas from Derrik Pates <dpates@dsdk12.net>
124  * Mar 2003, Kostas Kalevras <kkalev@noc.ntua.gr>
125  *      - Add an ldap_escape_func. Escape the * character from the filter so that we can avoid
126  *        the trivial DoS of username=*
127  *      - Remove the caching code. It does not exist in openldap21.
128  *        Based on a report from Mike Denka <mdenk@whidbey.net>
129  * May 2003, Kostas Kalevras <kkalev@noc.ntua.gr>
130  *      - Don't do a double free on the attribute maps. Bug noted by Derrik Pates <dpates@dsdk12.net>
131  *      - Apply a patch from Alexander M. Pravking <fduch@antar.bryansk.ru> to do an xlat on the
132  *        retrieved attributes.
133  * Aug 2003, Kostas Kalevras <kkalev@noc.ntua.gr>
134  *      - In case of a bad search filter, print out the corresponding filter
135  * Sep 2003, Kostas Kalevras <kkalev@noc.ntua.gr>
136  *      - Compile even if we don't have pthread's
137  * Oct 2003, Kostas Kalevras <kkalev@noc.ntua.gr>
138  *      - Add a new configuration directive, base_filter which is used for base scope searches
139  *        (When searching for the default/regular profiles for example)
140  * Nov 2003, Kostas Kalevras <kkalev@noc.ntua.gr>
141  *      - Add a new configuration directive, do_xlat (default: yes). If set we use pairxlatmove
142  *        on the radius attributes, else we fall back to the plain old pairadd. That way people
143  *        can fall back on the 0.8.1 behaviour without making changes to their ldap database or
144  *        gain a little performance by not using pairxlatmove
145  */
146 static const char rcsid[] = "$Id$";
147
148 #include "autoconf.h"
149
150 #include        <sys/types.h>
151 #include        <sys/socket.h>
152 #include        <sys/time.h>
153 #include        <netinet/in.h>
154
155 #include        <stdio.h>
156 #include        <stdlib.h>
157 #include        <netdb.h>
158 #include        <pwd.h>
159 #include        <time.h>
160 #include        <ctype.h>
161 #include        <string.h>
162
163 #include        <lber.h>
164 #include        <ldap.h>
165
166 #include        <errno.h>
167 #include        <unistd.h>
168 #include        <pthread.h>
169
170 #include        "libradius.h"
171 #include        "radiusd.h"
172 #include        "conffile.h"
173 #include        "modules.h"
174 #include        "rad_assert.h"
175
176 #ifndef HAVE_PTHREAD_H
177 /*
178  *      This is a lot simpler than putting ifdef's around
179  *      every use of the pthread functions.
180  */
181 #define pthread_mutex_lock(a)
182 #define pthread_mutex_unlock(a)
183 #define pthread_mutex_init(a,b)
184 #define pthread_mutex_destroy(a)
185 #endif
186
187
188 #define MAX_FILTER_STR_LEN      1024
189 #define TIMELIMIT 5
190
191 /*
192  * These are used in case ldap_search returns LDAP_SERVER_DOWN
193  * In that case we do conn->failed_conns++ and then check it:
194  * If conn->failed_conns <= MAX_FAILED_CONNS_START then we try
195  * to reconnect
196  * conn->failed_conns is also checked on entrance in perform_search:
197  * If conn->failed_conns > MAX_FAILED_CONNS_START then we don't
198  * try to do anything and we just do conn->failed_conns++ and
199  * return RLM_MODULE_FAIL
200  * if conn->failed_conns >= MAX_FAILED_CONNS_END then we give it
201  * another chance and we set it to MAX_FAILED_CONNS_RESTART and
202  * try to reconnect.
203  *
204  *
205  * We are assuming that the majority of the LDAP_SERVER_DOWN cases
206  * will either be an ldap connection timeout or a temporary ldap
207  * server problem.
208  * As a result we make a few attempts to reconnect hoping that the problem
209  * will soon go away. If it does not go away then we just return
210  * RLM_MODULE_FAIL on entrance in perform_search until conn->failed_conns
211  * gets to MAX_FAILED_CONNS_END. After that we give it one more chance by
212  * going back to MAX_FAILED_CONNS_RESTART
213  *
214  */
215
216 #define MAX_FAILED_CONNS_END            20
217 #define MAX_FAILED_CONNS_RESTART        4
218 #define MAX_FAILED_CONNS_START          5
219
220 /* linked list of mappings between RADIUS attributes and LDAP attributes */
221 struct TLDAP_RADIUS {
222         char*                 attr;
223         char*                 radius_attr;
224         struct TLDAP_RADIUS*  next;
225 };
226 typedef struct TLDAP_RADIUS TLDAP_RADIUS;
227
228 typedef struct ldap_conn {
229         LDAP            *ld;
230         char            bound;
231         char            locked;
232         int             failed_conns;
233         pthread_mutex_t mutex;
234 } LDAP_CONN;
235
236 typedef struct {
237         char           *server;
238         int             port;
239         int             timelimit;
240         struct timeval  net_timeout;
241         struct timeval  timeout;
242         int             debug;
243         int             tls_mode;
244         int             start_tls;
245         int             num_conns;
246         int             do_comp;
247         int             do_xlat;
248         int             default_allow;
249         int             failed_conns;
250         int             is_url;
251         char           *login;
252         char           *password;
253         char           *filter;
254         char           *base_filter;
255         char           *basedn;
256         char           *default_profile;
257         char           *profile_attr;
258         char           *access_attr;
259         char           *passwd_hdr;
260         char           *passwd_attr;
261         char           *dictionary_mapping;
262         char           *groupname_attr;
263         char           *groupmemb_filt;
264         char           *groupmemb_attr;
265         char            **atts;
266         TLDAP_RADIUS   *check_item_map;
267         TLDAP_RADIUS   *reply_item_map;
268         LDAP_CONN       *conns;
269         int             ldap_debug; /* Debug flag for LDAP SDK */
270         char            *xlat_name; /* name used to xlat */
271 }               ldap_instance;
272
273 static CONF_PARSER module_config[] = {
274         {"server", PW_TYPE_STRING_PTR, offsetof(ldap_instance,server), NULL, "localhost"},
275         {"port", PW_TYPE_INTEGER, offsetof(ldap_instance,port), NULL, "389"},
276         /* wait forever on network activity */
277         {"net_timeout", PW_TYPE_INTEGER, offsetof(ldap_instance,net_timeout.tv_sec), NULL, "10"},
278         /* wait forever for search results */
279         {"timeout", PW_TYPE_INTEGER, offsetof(ldap_instance,timeout.tv_sec), NULL, "20"},
280         /* allow server unlimited time for search (server-side limit) */
281         {"timelimit", PW_TYPE_INTEGER, offsetof(ldap_instance,timelimit), NULL, "20"},
282         {"identity", PW_TYPE_STRING_PTR, offsetof(ldap_instance,login), NULL, ""},
283         {"start_tls", PW_TYPE_BOOLEAN, offsetof(ldap_instance,start_tls), NULL, "no"},
284         {"password", PW_TYPE_STRING_PTR, offsetof(ldap_instance,password), NULL, ""},
285         {"basedn", PW_TYPE_STRING_PTR, offsetof(ldap_instance,basedn), NULL, "o=notexist"},
286         {"filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance,filter), NULL, "(uid=%u)"},
287         {"base_filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance,base_filter), NULL, "(objectclass=radiusprofile)"},
288         {"default_profile", PW_TYPE_STRING_PTR, offsetof(ldap_instance,default_profile), NULL, NULL},
289         {"profile_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance,profile_attr), NULL, NULL},
290         {"password_header", PW_TYPE_STRING_PTR, offsetof(ldap_instance,passwd_hdr), NULL, NULL},
291         {"password_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance,passwd_attr), NULL, NULL},
292         /* LDAP attribute name that controls remote access */
293         {"access_attr", PW_TYPE_STRING_PTR, offsetof(ldap_instance,access_attr), NULL, NULL},
294         /* file with mapping between LDAP and RADIUS attributes */
295         {"groupname_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance,groupname_attr), NULL, "cn"},
296         {"groupmembership_filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance,groupmemb_filt), NULL, "(|(&(objectClass=GroupOfNames)(member=%{Ldap-UserDn}))(&(objectClass=GroupOfUniqueNames)(uniquemember=%{Ldap-UserDn})))"},
297         {"groupmembership_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance,groupmemb_attr), NULL, NULL},
298         {"dictionary_mapping", PW_TYPE_STRING_PTR, offsetof(ldap_instance,dictionary_mapping), NULL, "${confdir}/ldap.attrmap"},
299         {"ldap_debug", PW_TYPE_INTEGER, offsetof(ldap_instance,ldap_debug), NULL, "0x0000"},
300         {"ldap_connections_number", PW_TYPE_INTEGER, offsetof(ldap_instance,num_conns), NULL, "5"},
301         {"compare_check_items", PW_TYPE_BOOLEAN, offsetof(ldap_instance,do_comp), NULL, "no"},
302         {"access_attr_used_for_allow", PW_TYPE_BOOLEAN, offsetof(ldap_instance,default_allow), NULL, "yes"},
303         {"do_xlat", PW_TYPE_BOOLEAN, offsetof(ldap_instance,do_xlat), NULL, "yes"},
304
305         {NULL, -1, 0, NULL, NULL}
306 };
307
308 #define ld_valid                ld_options.ldo_valid
309 #define LDAP_VALID_SESSION      0x2
310 #define LDAP_VALID(ld)  ( (ld)->ld_valid == LDAP_VALID_SESSION )
311
312 #ifdef FIELDCPY
313 static void     fieldcpy(char *, char **);
314 #endif
315 static VALUE_PAIR *ldap_pairget(LDAP *, LDAPMessage *, TLDAP_RADIUS *,VALUE_PAIR **,char);
316 static int ldap_groupcmp(void *, REQUEST *, VALUE_PAIR *, VALUE_PAIR *, VALUE_PAIR *, VALUE_PAIR **);
317 static int ldap_xlat(void *,REQUEST *, char *, char *,int, RADIUS_ESCAPE_STRING);
318 static LDAP    *ldap_connect(void *instance, const char *, const char *, int, int *);
319 static int     read_mappings(ldap_instance* inst);
320
321 static inline int ldap_get_conn(LDAP_CONN *conns,LDAP_CONN **ret,void *instance)
322 {
323         ldap_instance *inst = instance;
324         register int i = 0;
325
326         for(;i<inst->num_conns;i++){
327                 if (conns[i].locked == 0 && pthread_mutex_trylock(&(conns[i].mutex)) == 0){
328                         *ret = &conns[i];
329                         conns[i].locked = 1;
330                         DEBUG("ldap_get_conn: Got Id: %d",i);
331                         return i;
332                 }
333         }
334
335         return -1;
336 }
337         
338 static inline void ldap_release_conn(int i, LDAP_CONN *conns)
339 {
340         DEBUG("ldap_release_conn: Release Id: %d",i);
341         conns[i].locked = 0;
342         pthread_mutex_unlock(&(conns[i].mutex));
343 }
344
345 /*************************************************************************
346  *
347  *      Function: rlm_ldap_instantiate
348  *
349  *      Purpose: Uses section of radiusd config file passed as parameter
350  *               to create an instance of the module.
351  *
352  *************************************************************************/
353 static int 
354 ldap_instantiate(CONF_SECTION * conf, void **instance)
355 {
356         ldap_instance  *inst;
357         int i = 0;
358         int atts_num = 0;
359         int reply_map_num = 0;
360         int check_map_num = 0;
361         int att_map[3] = {0,0,0};
362         TLDAP_RADIUS *pair;
363         char *xlat_name;
364
365         inst = rad_malloc(sizeof *inst);
366         if (!inst) {
367                 return -1;
368         }
369         memset(inst, 0, sizeof(*inst));
370
371         if (cf_section_parse(conf, inst, module_config) < 0) {
372                 free(inst);
373                 return -1;
374         }
375
376         if (inst->server == NULL) {
377                 radlog(L_ERR, "rlm_ldap: missing 'server' directive.");
378                 free(inst);
379                 return -1;
380         }
381         inst->is_url = 0;
382 #ifdef HAVE_LDAP_INITIALIZE
383         if (ldap_is_ldap_url(inst->server)){
384                 inst->is_url = 1;
385                 inst->port = 0;
386         }
387 #endif
388  
389         inst->timeout.tv_usec = 0;
390         inst->net_timeout.tv_usec = 0;
391         /* workaround for servers which support LDAPS but not START TLS */
392         if(inst->port == LDAPS_PORT)
393                 inst->tls_mode = LDAP_OPT_X_TLS_HARD;
394         else
395                 inst->tls_mode = 0;
396         inst->reply_item_map = NULL;
397         inst->check_item_map = NULL;
398         inst->conns = NULL;
399         inst->failed_conns = 0;
400
401         paircompare_register(PW_LDAP_GROUP, PW_USER_NAME, ldap_groupcmp, inst);
402         DEBUG("conns: %p",inst->conns);
403
404         xlat_name = cf_section_name2(conf);
405         if (xlat_name == NULL) {
406                 xlat_name = cf_section_name1(conf);
407                 rad_assert(xlat_name != NULL); /* or all hell breaks loose */
408         }
409         inst->xlat_name = strdup(xlat_name);
410         xlat_register(xlat_name,ldap_xlat,inst);
411
412         if (inst->num_conns <= 0){
413                 radlog(L_ERR, "rlm_ldap: Invalid ldap connections number passed.");
414                 free(inst);
415                 return -1;
416         }
417         inst->conns = (LDAP_CONN *)malloc(sizeof(LDAP_CONN)*inst->num_conns);
418         if (inst->conns == NULL){
419                 radlog(L_ERR, "rlm_ldap: Could not allocate memory. Aborting.");
420                 free(inst);
421                 return -1;
422         }
423         for(;i<inst->num_conns;i++){
424                 inst->conns[i].bound = 0;
425                 inst->conns[i].locked = 0;
426                 inst->conns[i].failed_conns = 0;
427                 inst->conns[i].ld = NULL;
428                 pthread_mutex_init(&inst->conns[i].mutex, NULL);
429         }       
430
431         if (read_mappings(inst) != 0) {
432                 radlog(L_ERR, "rlm_ldap: Reading dictionary mappings from file %s failed",
433                        inst->dictionary_mapping);
434                 radlog(L_ERR, "rlm_ldap: Proceeding with no mappings");
435         }
436         
437         pair = inst->check_item_map;
438         while(pair != NULL){
439                 atts_num++;
440                 pair = pair->next;
441         }
442         check_map_num = (atts_num - 1);
443         pair = inst->reply_item_map;
444         while(pair != NULL){
445                 atts_num++;
446                 pair = pair->next;
447         }
448         reply_map_num = (atts_num - 1);
449         if (inst->profile_attr)
450                 atts_num++;
451         if (inst->passwd_attr)
452                 atts_num++;
453         if (inst->access_attr)
454                 atts_num++;
455         inst->atts = (char **)malloc(sizeof(char *)*(atts_num + 1));
456         if (inst->atts == NULL){
457                 radlog(L_ERR, "rlm_ldap: Could not allocate memory. Aborting.");
458                 free(inst);
459                 return -1;
460         }
461         pair = inst->check_item_map;
462         for(i=0;i<atts_num;i++){
463                 if (i <= check_map_num ){
464                         inst->atts[i] = pair->attr;
465                         if (i == check_map_num)
466                                 pair = inst->reply_item_map;
467                         else
468                                 pair = pair->next;
469                 }
470                 else if (i <= reply_map_num){
471                         inst->atts[i] = pair->attr;
472                         pair = pair->next;
473                 }
474                 else{
475                         if (inst->profile_attr && !att_map[0]){
476                                 inst->atts[i] = inst->profile_attr;
477                                 att_map[0] = 1;
478                         }
479                         else if (inst->passwd_attr && !att_map[1]){
480                                 inst->atts[i] = inst->passwd_attr;
481                                 att_map[1] = 1;
482                         }
483                         else if (inst->access_attr && !att_map[2]){
484                                 inst->atts[i] = inst->access_attr;
485                                 att_map[2] = 1;
486                         }
487                 }
488         }
489         inst->atts[atts_num] = NULL;            
490
491         DEBUG("conns: %p",inst->conns);
492
493         *instance = inst;
494
495
496         return 0;
497 }
498
499
500 /*
501  * read_mappings(...) reads a ldap<->radius mappings file to inst->reply_item_map and inst->check_item_map
502  */
503
504 #define MAX_LINE_LEN 160
505 #define GENERIC_ATTRIBUTE_ID "$GENERIC$"
506
507 static int
508 read_mappings(ldap_instance* inst)
509 {
510         FILE* mapfile;
511         char *filename;
512         /* all buffers are of MAX_LINE_LEN so we can use sscanf without being afraid of buffer overflows */
513         char buf[MAX_LINE_LEN], itemType[MAX_LINE_LEN], radiusAttribute[MAX_LINE_LEN], ldapAttribute[MAX_LINE_LEN];
514         int linenumber;
515
516         /* open the mappings file for reading */
517
518         filename = inst->dictionary_mapping;
519         DEBUG("rlm_ldap: reading ldap<->radius mappings from file %s", filename);
520         mapfile = fopen(filename, "r");
521
522         if (mapfile == NULL) {
523                 radlog(L_ERR, "rlm_ldap: Opening file %s failed", filename);
524                 return -1; /* error */
525         }
526
527         /* read file line by line. Note that if line length exceed MAX_LINE_LEN, line numbers will be mixed up */
528
529         linenumber = 0;
530
531         while (fgets(buf, sizeof buf, mapfile)!=NULL) {
532                 char* ptr;
533                 int token_count;
534                 TLDAP_RADIUS* pair;
535
536                 linenumber++;
537
538                 /* strip comments */
539                 ptr = strchr(buf, '#');
540                 if (ptr) *ptr = 0;
541                 
542                 /* empty line */
543                 if (buf[0] == 0) continue;
544                 
545                 /* extract tokens from the string */            
546                 token_count = sscanf(buf, "%s %s %s", itemType, radiusAttribute, ldapAttribute);
547
548                 if (token_count <= 0) /* no tokens */                   
549                         continue;
550
551                 if (token_count != 3) {
552                         radlog(L_ERR, "rlm_ldap: Skipping %s line %i: %s", filename, linenumber, buf);
553                         radlog(L_ERR, "rlm_ldap: Expected 3 tokens "
554                                "(Item type, RADIUS Attribute and LDAP Attribute) but found only %i", token_count);
555                         continue;
556                 }
557
558                 /* create new TLDAP_RADIUS list node */
559                 pair = rad_malloc(sizeof(TLDAP_RADIUS));
560
561                 pair->attr = strdup(ldapAttribute);
562                 pair->radius_attr = strdup(radiusAttribute);
563
564                 if ( (pair->attr == NULL) || (pair->radius_attr == NULL) ) {
565                         radlog(L_ERR, "rlm_ldap: Out of memory");
566                         if (pair->attr) free(pair->attr);
567                         if (pair->radius_attr) free(pair->radius_attr);
568                         free(pair);
569                         fclose(mapfile);
570                         return -1;
571                 }
572                         
573                 /* push node to correct list */
574                 if (strcasecmp(itemType, "checkItem") == 0) {
575                         pair->next = inst->check_item_map;
576                         inst->check_item_map = pair;
577                 } else if (strcasecmp(itemType, "replyItem") == 0) {
578                         pair->next = inst->reply_item_map;
579                         inst->reply_item_map = pair;
580                 } else {
581                         radlog(L_ERR, "rlm_ldap: file %s: skipping line %i: unknown itemType %s", 
582                                filename, linenumber, itemType);
583                         free(pair->attr);
584                         free(pair->radius_attr);
585                         free(pair);
586                         continue;
587                 }
588
589                 DEBUG("rlm_ldap: LDAP %s mapped to RADIUS %s",
590                       pair->attr, pair->radius_attr);
591         }
592         
593         fclose(mapfile);
594
595         return 0; /* success */
596 }
597
598 static int 
599 perform_search(void *instance, LDAP_CONN *conn, char *search_basedn, int scope, char *filter, 
600                 char **attrs, LDAPMessage ** result)
601 {
602         int             res = RLM_MODULE_OK;
603         int             ldap_errno = 0;
604         ldap_instance  *inst = instance;
605         int             search_retry = 0;
606
607         *result = NULL;
608
609         if (!conn){
610                 radlog(L_ERR, "rlm_ldap: NULL connection handle passed");
611                 return RLM_MODULE_FAIL;
612         }
613         if (conn->failed_conns > MAX_FAILED_CONNS_START){
614                 conn->failed_conns++;
615                 if (conn->failed_conns >= MAX_FAILED_CONNS_END){
616                         conn->failed_conns = MAX_FAILED_CONNS_RESTART;
617                         conn->bound = 0;
618                 }
619         }
620 retry:
621         if (!conn->bound || conn->ld == NULL) {
622                 DEBUG2("rlm_ldap: attempting LDAP reconnection");
623                 if (conn->ld){
624                         DEBUG2("rlm_ldap: closing existing LDAP connection");
625                         ldap_unbind_s(conn->ld);
626                 }
627                 if ((conn->ld = ldap_connect(instance, inst->login, inst->password, 0, &res)) == NULL) {
628                         radlog(L_ERR, "rlm_ldap: (re)connection attempt failed");
629                         if (search_retry == 0)
630                                 conn->failed_conns++;
631                         return (RLM_MODULE_FAIL);
632                 }
633                 conn->bound = 1;
634                 conn->failed_conns = 0;
635         }
636         DEBUG2("rlm_ldap: performing search in %s, with filter %s", search_basedn ? search_basedn : "(null)" , filter);
637         switch (ldap_search_st(conn->ld, search_basedn, scope, filter, attrs, 0, &(inst->timeout), result)) {
638         case LDAP_SUCCESS:
639         case LDAP_NO_SUCH_OBJECT:
640                 break;
641         case LDAP_SERVER_DOWN:
642                 radlog(L_ERR, "rlm_ldap: ldap_search() failed: LDAP connection lost.");
643                 conn->failed_conns++;
644                 if (search_retry == 0){
645                         if (conn->failed_conns <= MAX_FAILED_CONNS_START){
646                                 radlog(L_INFO, "rlm_ldap: Attempting reconnect");
647                                 search_retry = 1;
648                                 conn->bound = 0;
649                                 ldap_msgfree(*result);  
650                                 goto retry;
651                         }
652                 }
653                 ldap_msgfree(*result);
654                 return RLM_MODULE_FAIL;
655         case LDAP_INSUFFICIENT_ACCESS:
656                 radlog(L_ERR, "rlm_ldap: ldap_search() failed: Insufficient access. Check the identity and password configuration directives.");
657                 ldap_msgfree(*result);
658                 return RLM_MODULE_FAIL;
659         case LDAP_TIMEOUT:
660                 radlog(L_ERR, "rlm_ldap: ldap_search() failed: Timed out while waiting for server to respond. Please increase the timeout.");
661                 ldap_msgfree(*result);
662                 return RLM_MODULE_FAIL;
663         case LDAP_FILTER_ERROR:
664                 radlog(L_ERR, "rlm_ldap: ldap_search() failed: Bad search filter: %s",filter);
665                 ldap_msgfree(*result);
666                 return RLM_MODULE_FAIL;
667         case LDAP_TIMELIMIT_EXCEEDED:
668         case LDAP_BUSY:
669         case LDAP_UNAVAILABLE:
670                 /* We don't need to reconnect in these cases so we don't set conn->bound */
671                 ldap_get_option(conn->ld, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
672                 radlog(L_ERR, "rlm_ldap: ldap_search() failed: %s", ldap_err2string(ldap_errno));
673                 ldap_msgfree(*result);  
674                 return (RLM_MODULE_FAIL);
675         default:
676                 ldap_get_option(conn->ld, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
677                 radlog(L_ERR, "rlm_ldap: ldap_search() failed: %s", ldap_err2string(ldap_errno));
678                 conn->bound = 0;
679                 ldap_msgfree(*result);  
680                 return (RLM_MODULE_FAIL);
681         }
682
683         if ((ldap_count_entries(conn->ld, *result)) != 1) {
684                 DEBUG("rlm_ldap: object not found or got ambiguous search result");
685                 res = RLM_MODULE_NOTFOUND;
686                 ldap_msgfree(*result);  
687         }
688         return res;
689 }
690
691
692 /*
693  *      Translate the LDAP queries.
694  */
695 static int ldap_escape_func(char *out, int outlen, const char *in)
696 {
697         int len = 0;
698         
699         while (in[0]) {
700                 /*
701                  *  Only one byte left.
702                  */
703                 if (outlen <= 1) {
704                         break;
705                 }
706                 
707                 if (strchr("*", *in)) {
708                         in++;
709                         outlen--;
710                         continue;
711                 }
712                 
713                 /*
714                  *      Else it's a nice character.
715                  */
716                 *out = *in;
717                 out++;
718                 in++;
719                 outlen--;
720                 len++;
721         }
722         *out = '\0';
723         return len;
724 }
725
726 /*
727  * ldap_groupcmp(). Implement the Ldap-Group == "group" filter
728  */
729
730 static int ldap_groupcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
731                 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
732 {
733         char            filter[MAX_FILTER_STR_LEN];
734         char            gr_filter[MAX_FILTER_STR_LEN];
735         int             res;
736         LDAPMessage     *result = NULL;
737         LDAPMessage     *msg = NULL;
738         char            basedn[MAX_FILTER_STR_LEN];
739         char            *attrs[] = {"dn",NULL};
740         char            **vals;
741         ldap_instance   *inst = instance;
742         char            *group_attrs[] = {inst->groupmemb_attr,NULL};
743         LDAP_CONN       *conn;
744         int             conn_id = -1;
745         VALUE_PAIR      *vp_user_dn;
746         VALUE_PAIR      **request_pairs;
747
748         request_pairs = &req->packet->vps;
749
750         DEBUG("rlm_ldap: Entering ldap_groupcmp()");
751
752         if (check->strvalue == NULL || check->length == 0){
753                 DEBUG("rlm_ldap::ldap_groupcmp: Illegal group name");
754                 return 1;
755         }
756
757         if (req == NULL){
758                 DEBUG("rlm_ldap::ldap_groupcmp: NULL request");
759                 return 1;
760         }
761
762         if (!radius_xlat(basedn, sizeof(basedn), inst->basedn, req, NULL)) {
763                 DEBUG("rlm_ldap::ldap_groupcmp: unable to create basedn.");
764                 return 1;
765         }
766
767         while((vp_user_dn = pairfind(*request_pairs, PW_LDAP_USERDN)) == NULL){
768                 char            *user_dn = NULL;
769
770                 if (!radius_xlat(filter, sizeof(filter), inst->filter,
771                                         req, ldap_escape_func)){
772                         DEBUG("rlm_ldap::ldap_groupcmp: unable to create filter");
773                         return 1;
774                 }
775                 if ((conn_id = ldap_get_conn(inst->conns,&conn,inst)) == -1){
776                         radlog(L_ERR, "rlm_ldap: All ldap connections are in use");
777                         return 1;
778                 }
779                 if ((res = perform_search(inst, conn, basedn, LDAP_SCOPE_SUBTREE,
780                                         filter, attrs, &result)) != RLM_MODULE_OK){
781                         DEBUG("rlm_ldap::ldap_groupcmp: search failed");
782                         ldap_release_conn(conn_id,inst->conns);
783                         return 1;
784                 }
785                 if ((msg = ldap_first_entry(conn->ld, result)) == NULL) {
786                         DEBUG("rlm_ldap::ldap_groupcmp: ldap_first_entry() failed");
787                         ldap_release_conn(conn_id,inst->conns);
788                         ldap_msgfree(result);
789                         return 1;
790                 }
791                 if ((user_dn = ldap_get_dn(conn->ld, msg)) == NULL) {
792                         DEBUG("rlm_ldap:ldap_groupcmp:: ldap_get_dn() failed");
793                         ldap_release_conn(conn_id,inst->conns);
794                         ldap_msgfree(result);
795                         return 1;
796                 }
797                 ldap_release_conn(conn_id,inst->conns);
798                 /*
799                 * Adding new attribute containing DN for LDAP object associated with
800                 * given username
801                 */
802                 pairadd(request_pairs, pairmake("Ldap-UserDn", user_dn, T_OP_EQ));
803                 ldap_memfree(user_dn);
804                 ldap_msgfree(result);
805         }
806
807         if(!radius_xlat(gr_filter, sizeof(gr_filter), inst->groupmemb_filt, req, NULL)){
808                 DEBUG("rlm_ldap::ldap_groupcmp: unable to create filter.");
809                 return 1;
810         }
811
812         if (strchr((char *)check->strvalue,',') != NULL) {
813                 /* This looks like a DN */
814                 snprintf(filter,sizeof(filter), "%s",gr_filter);
815                 snprintf(basedn,sizeof(basedn), "%s",(char *)check->strvalue);
816         } else 
817                 snprintf(filter,sizeof(filter), "(&(%s=%s)%s)",inst->groupname_attr,(char *)check->strvalue,gr_filter);
818
819         if ((conn_id = ldap_get_conn(inst->conns,&conn,inst)) == -1){
820                 radlog(L_ERR, "rlm_ldap: All ldap connections are in use");
821                 return 1;
822         }
823         
824         if ((res = perform_search(inst, conn, basedn, LDAP_SCOPE_SUBTREE,
825                                 filter, attrs, &result)) == RLM_MODULE_OK){
826                 DEBUG("rlm_ldap::ldap_groupcmp: User found in group %s",
827                                 (char *)check->strvalue);
828                 ldap_msgfree(result);
829                 ldap_release_conn(conn_id,inst->conns);
830                 return 0;
831         }
832         
833         ldap_release_conn(conn_id,inst->conns);
834
835         if (res != RLM_MODULE_NOTFOUND ) {
836                 DEBUG("rlm_ldap::ldap_groupcmp: Search returned error");
837                 return 1;
838         }
839
840         if (inst->groupmemb_attr == NULL){
841                 /* search returned NOTFOUND and searching for membership 
842                  * using user object attributes is not specified in config
843                  * file
844                  */
845                 DEBUG("rlm_ldap::ldap_groupcmp: Group %s not found or user is not a member.",(char *)check->strvalue);
846                 return 1;
847         }
848
849         snprintf(filter,sizeof(filter), "(objectclass=*)");
850         if ((conn_id = ldap_get_conn(inst->conns,&conn,inst)) == -1){
851                 radlog(L_ERR, "rlm_ldap: Add ldap connections are in use");
852                 return 1;
853         }
854         if ((res = perform_search(inst, conn, vp_user_dn->strvalue, LDAP_SCOPE_BASE,
855                                         filter, group_attrs,&result)) != RLM_MODULE_OK){
856                 DEBUG("rlm_ldap::ldap_groupcmp: Search returned error");
857                 ldap_release_conn(conn_id, inst->conns);
858                 return 1;
859         }               
860
861         if ((msg = ldap_first_entry(conn->ld, result)) == NULL) {
862                 DEBUG("rlm_ldap::ldap_groupcmp: ldap_first_entry() failed");
863                 ldap_release_conn(conn_id,inst->conns);
864                 ldap_msgfree(result);
865                 return 1;
866         }
867         if ((vals = ldap_get_values(conn->ld, msg, inst->groupmemb_attr)) != NULL) {
868                 unsigned int i = 0;
869                 char found = 0;
870                 for (;i < ldap_count_values(vals);i++){
871                         if (strchr(vals[i],',') != NULL){ 
872                                 /* This looks like a DN */
873                                 LDAPMessage *gr_result = NULL;
874                                 snprintf(filter,sizeof(filter), "(%s=%s)",
875                                         inst->groupname_attr,
876                                         (char *)check->strvalue);
877                                 if ((res = perform_search(inst, conn, vals[i],
878                                                 LDAP_SCOPE_BASE, filter,
879                                                 attrs, &gr_result)) != RLM_MODULE_OK){
880                                         if (res != RLM_MODULE_NOTFOUND){
881                                                 DEBUG("rlm_ldap::ldap_groupcmp: \
882                                                         Search returned error");
883                                                 ldap_value_free(vals);
884                                                 ldap_msgfree(result);
885                                                 ldap_release_conn(conn_id,inst->conns);
886                                                 return 1;
887                                         }
888                                 } else {
889                                         ldap_msgfree(gr_result);
890                                         found = 1;
891                                         break;
892                                 }
893                         } else {
894                                 if (strcmp(vals[i],(char *)check->strvalue) == 0){
895                                         found = 1;
896                                         break;
897                                 }
898                         }
899                 }
900                 ldap_value_free(vals);
901                 ldap_msgfree(result);
902                 if (found == 0){
903                         DEBUG("rlm_ldap::groupcmp: Group %s not found \
904                                 or user not a member",
905                                 (char *)check->strvalue);
906                         ldap_release_conn(conn_id,inst->conns);
907                         return 1;
908                 }
909         } else {
910                         DEBUG("rlm_ldap::ldap_groupcmp: ldap_get_values() failed");
911                         ldap_msgfree(result);
912                         ldap_release_conn(conn_id,inst->conns);
913                         return 1;
914         }
915
916         DEBUG("rlm_ldap::ldap_groupcmp: User found in group %s",(char *)check->strvalue);
917         ldap_release_conn(conn_id,inst->conns);
918
919         return 0;
920 }
921
922 /*
923  * ldap_xlat()
924  * Do an xlat on an LDAP URL
925  */
926
927 static int ldap_xlat(void *instance, REQUEST *request, char *fmt, char *out, int freespace,
928                                 RADIUS_ESCAPE_STRING func)
929 {
930         char url[MAX_FILTER_STR_LEN];
931         int res;
932         int ret = 0;
933         ldap_instance *inst = instance;
934         LDAPURLDesc *ldap_url;
935         LDAPMessage *result = NULL;
936         LDAPMessage *msg = NULL;
937         char **vals;
938         int conn_id = -1;
939         LDAP_CONN *conn;
940
941         DEBUG("rlm_ldap: - ldap_xlat");
942         if (!radius_xlat(url, sizeof(url), fmt, request, func)) {
943                 radlog (L_ERR, "rlm_ldap: Unable to create LDAP URL.\n");
944                 return 0;
945         }
946         if (!ldap_is_ldap_url(url)){
947                 radlog (L_ERR, "rlm_ldap: String passed does not look like an LDAP URL.\n");
948                 return 0;
949         }
950         if (ldap_url_parse(url,&ldap_url)){
951                 radlog (L_ERR, "rlm_ldap: LDAP URL parse failed.\n");
952                 return 0;
953         }
954         if (ldap_url->lud_attrs == NULL || ldap_url->lud_attrs[0] == NULL || \
955                 ( ldap_url->lud_attrs[1] != NULL || ( ! strlen(ldap_url->lud_attrs[0]) || \
956                 ! strcmp(ldap_url->lud_attrs[0],"*") ) ) ){
957                 radlog (L_ERR, "rlm_ldap: Invalid Attribute(s) request.\n");
958                 ldap_free_urldesc(ldap_url);
959                 return 0;
960         }
961         if (ldap_url->lud_host){
962                 if (strncmp(inst->server,ldap_url->lud_host,strlen(inst->server)) != 0 || \
963                                 ldap_url->lud_port != inst->port){
964                         DEBUG("rlm_ldap: Requested server/port is not known to this module instance.");
965                         ldap_free_urldesc(ldap_url);
966                         return 0;
967                 }
968         }
969         if ((conn_id = ldap_get_conn(inst->conns,&conn,inst)) == -1){
970                 radlog(L_ERR, "rlm_ldap: All ldap connections are in use");
971                 ldap_free_urldesc(ldap_url);
972                 return 0;
973         }
974         if ((res = perform_search(inst, conn, ldap_url->lud_dn, ldap_url->lud_scope, ldap_url->lud_filter, ldap_url->lud_attrs, &result)) != RLM_MODULE_OK){
975                 if (res == RLM_MODULE_NOTFOUND){
976                         DEBUG("rlm_ldap: Search returned not found");
977                         ldap_free_urldesc(ldap_url);
978                         ldap_release_conn(conn_id,inst->conns);
979                         return 0;
980                 }
981                 DEBUG("rlm_ldap: Search returned error");
982                 ldap_free_urldesc(ldap_url);
983                 ldap_release_conn(conn_id,inst->conns);
984                 return 0;
985         }
986         if ((msg = ldap_first_entry(conn->ld, result)) == NULL){
987                 DEBUG("rlm_ldap: ldap_first_entry() failed");
988                 ldap_msgfree(result);
989                 ldap_free_urldesc(ldap_url);
990                 ldap_release_conn(conn_id,inst->conns);
991                 return 0;
992         }
993         if ((vals = ldap_get_values(conn->ld, msg, ldap_url->lud_attrs[0])) != NULL) {
994                 ret = strlen(vals[0]);
995                 if (ret > freespace){
996                         DEBUG("rlm_ldap: Insufficient string space");
997                         ldap_free_urldesc(ldap_url);
998                         ldap_value_free(vals);
999                         ldap_msgfree(result);
1000                         ldap_release_conn(conn_id,inst->conns);
1001                         return 0;
1002                 }
1003                 DEBUG("rlm_ldap: Adding attribute %s, value: %s",ldap_url->lud_attrs[0],vals[0]);
1004                 strncpy(out,vals[0],ret);
1005                 ldap_value_free(vals);
1006         }
1007         else
1008                 ret = 0;
1009
1010         ldap_msgfree(result);
1011         ldap_free_urldesc(ldap_url);
1012         ldap_release_conn(conn_id,inst->conns);
1013
1014         DEBUG("rlm_ldap: - ldap_xlat end");
1015
1016         return ret;
1017 }
1018
1019
1020 /******************************************************************************
1021  *
1022  *      Function: rlm_ldap_authorize
1023  *
1024  *      Purpose: Check if user is authorized for remote access
1025  *
1026  ******************************************************************************/
1027 static int 
1028 ldap_authorize(void *instance, REQUEST * request)
1029 {
1030         LDAPMessage     *result = NULL;
1031         LDAPMessage     *msg = NULL;
1032         LDAPMessage     *def_msg = NULL;
1033         LDAPMessage     *def_attr_msg = NULL;
1034         LDAPMessage     *def_result = NULL;
1035         LDAPMessage     *def_attr_result = NULL;
1036         ldap_instance   *inst = instance;
1037         char            *user_dn = NULL;
1038         char            filter[MAX_FILTER_STR_LEN];
1039         char            basedn[MAX_FILTER_STR_LEN];
1040         VALUE_PAIR      *check_tmp;
1041         VALUE_PAIR      *reply_tmp;
1042         int             res;
1043         VALUE_PAIR      **check_pairs, **reply_pairs;
1044         char            **vals;
1045         VALUE_PAIR      *module_fmsg_vp;
1046         VALUE_PAIR      *user_profile;
1047         char            module_fmsg[MAX_STRING_LEN];
1048         LDAP_CONN       *conn;
1049         int             conn_id = -1;
1050
1051         DEBUG("rlm_ldap: - authorize");
1052
1053         if (!request->username){
1054                 radlog(L_AUTH, "rlm_ldap: Attribute \"User-Name\" is required for authentication.\n");
1055                 return RLM_MODULE_INVALID;
1056         }
1057
1058         check_pairs = &request->config_items;
1059         reply_pairs = &request->reply->vps;
1060
1061         /*
1062          * Check for valid input, zero length names not permitted
1063          */
1064         if (request->username->strvalue == 0) {
1065                 radlog(L_ERR, "rlm_ldap: zero length username not permitted\n");
1066                 return RLM_MODULE_INVALID;
1067         }
1068         DEBUG("rlm_ldap: performing user authorization for %s",
1069                request->username->strvalue);
1070
1071         if (!radius_xlat(filter, sizeof(filter), inst->filter,
1072                          request, ldap_escape_func)) {
1073                 radlog (L_ERR, "rlm_ldap: unable to create filter.\n");
1074                 return RLM_MODULE_INVALID;
1075         }
1076
1077         if (!radius_xlat(basedn, sizeof(basedn), inst->basedn,
1078                          request, NULL)) {
1079                 radlog (L_ERR, "rlm_ldap: unable to create basedn.\n");
1080                 return RLM_MODULE_INVALID;
1081         }
1082
1083         if ((conn_id = ldap_get_conn(inst->conns,&conn,inst)) == -1){
1084                 radlog(L_ERR, "rlm_ldap: All ldap connections are in use");
1085                 return RLM_MODULE_FAIL;
1086         }
1087         if ((res = perform_search(instance, conn, basedn, LDAP_SCOPE_SUBTREE, filter, inst->atts, &result)) != RLM_MODULE_OK) {
1088                 DEBUG("rlm_ldap: search failed");
1089                 if (res == RLM_MODULE_NOTFOUND){
1090                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: User not found");
1091                         module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
1092                         pairadd(&request->packet->vps, module_fmsg_vp);
1093                 }
1094                 ldap_release_conn(conn_id,inst->conns);
1095                 return (res);
1096         }
1097         if ((msg = ldap_first_entry(conn->ld, result)) == NULL) {
1098                 DEBUG("rlm_ldap: ldap_first_entry() failed");
1099                 ldap_msgfree(result);
1100                 ldap_release_conn(conn_id,inst->conns);
1101                 return RLM_MODULE_FAIL;
1102         }
1103         if ((user_dn = ldap_get_dn(conn->ld, msg)) == NULL) {
1104                 DEBUG("rlm_ldap: ldap_get_dn() failed");
1105                 ldap_msgfree(result);
1106                 ldap_release_conn(conn_id,inst->conns);
1107                 return RLM_MODULE_FAIL;
1108         }
1109         /*
1110          * Adding new attribute containing DN for LDAP object associated with
1111          * given username
1112          */
1113         pairadd(&request->packet->vps, pairmake("Ldap-UserDn", user_dn, T_OP_EQ));
1114         ldap_memfree(user_dn);
1115
1116
1117         /* Remote access is controled by attribute of the user object */
1118         if (inst->access_attr) {
1119                 if ((vals = ldap_get_values(conn->ld, msg, inst->access_attr)) != NULL) {
1120                         if (inst->default_allow){
1121                                 DEBUG("rlm_ldap: checking if remote access for %s is allowed by %s", request->username->strvalue, inst->access_attr);
1122                                 if (!strncmp(vals[0], "FALSE", 5)) {
1123                                         DEBUG("rlm_ldap: dialup access disabled");
1124                                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: Access Attribute denies access");
1125                                         module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
1126                                         pairadd(&request->packet->vps, module_fmsg_vp);
1127                                         ldap_msgfree(result);
1128                                         ldap_value_free(vals);
1129                                         ldap_release_conn(conn_id,inst->conns);
1130                                         return RLM_MODULE_USERLOCK;
1131                                 }
1132                                 ldap_value_free(vals);
1133                         }
1134                         else{
1135                                 DEBUG("rlm_ldap: %s attribute exists - access denied by default", inst->access_attr);
1136                                 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: Access Attribute denies access");
1137                                 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
1138                                 pairadd(&request->packet->vps, module_fmsg_vp);
1139                                 ldap_msgfree(result);
1140                                 ldap_value_free(vals);
1141                                 ldap_release_conn(conn_id,inst->conns);
1142                                 return RLM_MODULE_USERLOCK;
1143                         }
1144                 } else {
1145                         if (inst->default_allow){
1146                                 DEBUG("rlm_ldap: no %s attribute - access denied by default", inst->access_attr);
1147                                 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: Access Attribute denies access");
1148                                 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
1149                                 pairadd(&request->packet->vps, module_fmsg_vp);
1150                                 ldap_msgfree(result);
1151                                 ldap_release_conn(conn_id,inst->conns);
1152                                 return RLM_MODULE_USERLOCK;
1153                         }
1154                 }
1155         }
1156
1157         /*
1158          * Check for the default profile entry. If it exists then add the 
1159          * attributes it contains in the check and reply pairs
1160          */
1161
1162         user_profile = pairfind(request->config_items, PW_USER_PROFILE);
1163         if (inst->default_profile || user_profile){
1164                 char *profile = inst->default_profile;
1165
1166                 strNcpy(filter,inst->base_filter,sizeof(filter));
1167                 if (user_profile)
1168                         profile = user_profile->strvalue;
1169                 if (profile && strlen(profile)){
1170                         if ((res = perform_search(instance, conn,
1171                                 profile, LDAP_SCOPE_BASE, 
1172                                 filter, inst->atts, &def_result)) == RLM_MODULE_OK){
1173                                 if ((def_msg = ldap_first_entry(conn->ld,def_result))){
1174                                         if ((check_tmp = ldap_pairget(conn->ld,def_msg,inst->check_item_map,check_pairs,1))) {
1175                                                 if (inst->do_xlat)
1176                                                         pairxlatmove(request, check_pairs, &check_tmp);
1177                                                 else
1178                                                         pairadd(check_pairs,check_tmp);
1179                                                 pairfree(&check_tmp);
1180                                         }
1181                                         if ((reply_tmp = ldap_pairget(conn->ld,def_msg,inst->reply_item_map,reply_pairs,0))) {
1182                                                 if (inst->do_xlat)
1183                                                         pairxlatmove(request, reply_pairs, &reply_tmp);
1184                                                 else
1185                                                         pairadd(reply_pairs,reply_tmp);
1186                                                 pairfree(&reply_tmp);
1187                                         }
1188                                 }
1189                                 ldap_msgfree(def_result);
1190                         } else 
1191                                 DEBUG("rlm_ldap: default_profile/user-profile search failed");
1192                 }
1193         }
1194
1195         /*
1196          * Check for the profile attribute. If it exists, we assume that it 
1197          * contains the DN of an entry containg a profile for the user. That
1198          * way we can have different general profiles for various user groups
1199          * (students,faculty,staff etc)
1200          */
1201
1202         if (inst->profile_attr){
1203                 if ((vals = ldap_get_values(conn->ld, msg, inst->profile_attr)) != NULL) {
1204                         unsigned int i=0;
1205                         strNcpy(filter,inst->base_filter,sizeof(filter));
1206                         while(vals[i] != NULL && strlen(vals[i])){
1207                                 if ((res = perform_search(instance, conn,
1208                                         vals[i], LDAP_SCOPE_BASE, 
1209                                         filter, inst->atts, &def_attr_result)) == RLM_MODULE_OK){
1210                                         if ((def_attr_msg = ldap_first_entry(conn->ld,def_attr_result))){
1211                                                 if ((check_tmp = ldap_pairget(conn->ld,def_attr_msg,inst->check_item_map,check_pairs,1))) {
1212                                                         if (inst->do_xlat)
1213                                                                 pairxlatmove(request, check_pairs, &check_tmp);
1214                                                         else
1215                                                                 pairadd(check_pairs,check_tmp);
1216                                                         pairfree(&check_tmp);
1217                                                 }
1218                                                 if ((reply_tmp = ldap_pairget(conn->ld,def_attr_msg,inst->reply_item_map,reply_pairs,0))) {
1219                                                         if (inst->do_xlat)
1220                                                                 pairxlatmove(request, reply_pairs, &reply_tmp);
1221                                                         else
1222                                                                 pairadd(reply_pairs,reply_tmp);
1223                                                         pairfree(&reply_tmp);
1224                                                 }
1225                                         }
1226                                         ldap_msgfree(def_attr_result);
1227                                 } else 
1228                                         DEBUG("rlm_ldap: profile_attribute search failed");
1229                                 i++;
1230                         }
1231                         ldap_value_free(vals);
1232                 }
1233         }
1234         if (inst->passwd_attr && strlen(inst->passwd_attr)){
1235                 VALUE_PAIR *passwd_item;
1236
1237                 if ((passwd_item = pairfind(request->config_items, PW_PASSWORD)) == NULL){
1238                         char **passwd_vals;
1239                         char *passwd_val = NULL;
1240                         int passwd_len;
1241
1242                         if ((passwd_vals = ldap_get_values(conn->ld,msg,inst->passwd_attr)) != NULL){
1243                                 unsigned int i=0;
1244                                 while(passwd_vals[i] != NULL){
1245                                         if (strlen(passwd_vals[i])){
1246                                                 passwd_val = passwd_vals[i];
1247
1248                                                 if (inst->passwd_hdr && strlen(inst->passwd_hdr)){
1249                                                         passwd_val = strstr(passwd_val,inst->passwd_hdr);
1250                                                         if (passwd_val != NULL)
1251                                                                 passwd_val += strlen(inst->passwd_hdr);
1252                                                         else
1253                                                                 DEBUG("rlm_ldap: Password header not found in password %s for user %s", passwd_vals[0],request->username->strvalue);
1254                                                 }
1255                                                 if (passwd_val){
1256                                                         if ((passwd_item = paircreate(PW_PASSWORD,PW_TYPE_STRING)) == NULL){
1257                                                                 radlog(L_ERR|L_CONS, "no memory");
1258                                                                 ldap_value_free(passwd_vals);
1259                                                                 ldap_msgfree(result);
1260                                                                 ldap_release_conn(conn_id,inst->conns);
1261                                                                 return RLM_MODULE_FAIL;
1262                                                         }
1263                                                         passwd_len = strlen(passwd_val);
1264                                                         strncpy(passwd_item->strvalue,passwd_val,MAX_STRING_LEN - 1);
1265                                                         passwd_item->length = (passwd_len > (MAX_STRING_LEN - 1)) ? (MAX_STRING_LEN - 1) : passwd_len;
1266                                                         pairadd(&request->config_items,passwd_item);
1267                                                         DEBUG("rlm_ldap: Added password %s in check items",passwd_item->strvalue);
1268                                                 }
1269                                         }
1270                                         i++;
1271                                 }
1272                                 ldap_value_free(passwd_vals);
1273                         }
1274                 }
1275         }
1276
1277
1278
1279         DEBUG("rlm_ldap: looking for check items in directory...");
1280
1281         if ((check_tmp = ldap_pairget(conn->ld, msg, inst->check_item_map,check_pairs,1)) != NULL) {
1282                 if (inst->do_xlat)
1283                         pairxlatmove(request, check_pairs, &check_tmp);
1284                 else
1285                         pairadd(check_pairs,check_tmp);
1286                 pairfree(&check_tmp);
1287         }
1288
1289
1290         DEBUG("rlm_ldap: looking for reply items in directory...");
1291
1292
1293         if ((reply_tmp = ldap_pairget(conn->ld, msg, inst->reply_item_map,reply_pairs,0)) != NULL) {
1294                 if (inst->do_xlat)
1295                         pairxlatmove(request, reply_pairs, &reply_tmp);
1296                 else
1297                         pairadd(reply_pairs,reply_tmp);
1298                 pairfree(&reply_tmp);
1299         }
1300
1301        if (inst->do_comp && paircmp(request,request->packet->vps,*check_pairs,reply_pairs) != 0){
1302                 DEBUG("rlm_ldap: Pairs do not match. Rejecting user.");
1303                 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: Pairs do not match");
1304                 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
1305                 pairadd(&request->packet->vps, module_fmsg_vp);
1306                 ldap_msgfree(result);
1307                 ldap_release_conn(conn_id,inst->conns);
1308
1309                 return RLM_MODULE_REJECT;
1310         }
1311
1312         /*
1313          * Module should default to LDAP authentication if no Auth-Type
1314          * specified
1315          */
1316         if (pairfind(*check_pairs, PW_AUTHTYPE) == NULL)
1317                 pairadd(check_pairs, pairmake("Auth-Type", "LDAP", T_OP_EQ));
1318
1319
1320         DEBUG("rlm_ldap: user %s authorized to use remote access",
1321               request->username->strvalue);
1322         ldap_msgfree(result);
1323         ldap_release_conn(conn_id,inst->conns);
1324
1325         return RLM_MODULE_OK;
1326 }
1327
1328 /*****************************************************************************
1329  *
1330  *      Function: rlm_ldap_authenticate
1331  *
1332  *      Purpose: Check the user's password against ldap database
1333  *
1334  *****************************************************************************/
1335 static int 
1336 ldap_authenticate(void *instance, REQUEST * request)
1337 {
1338         LDAP           *ld_user;
1339         LDAPMessage    *result, *msg;
1340         ldap_instance  *inst = instance;
1341         char           *user_dn, *attrs[] = {"uid", NULL};
1342         char            filter[MAX_FILTER_STR_LEN];
1343         char            basedn[MAX_FILTER_STR_LEN];
1344         int             res;
1345         VALUE_PAIR     *vp_user_dn;
1346         VALUE_PAIR      *module_fmsg_vp;
1347         char            module_fmsg[MAX_STRING_LEN];
1348         LDAP_CONN       *conn;
1349         int             conn_id = -1;
1350
1351         DEBUG("rlm_ldap: - authenticate");
1352
1353         /*
1354          * Ensure that we're being passed a plain-text password, and not
1355          * anything else.
1356          */
1357
1358         if (!request->username) {
1359                 radlog(L_AUTH, "rlm_ldap: Attribute \"User-Name\" is required for authentication.\n");
1360                 return RLM_MODULE_INVALID;
1361         }
1362
1363         if (!request->password){
1364                 radlog(L_AUTH, "rlm_ldap: Attribute \"User-Password\" is required for authentication.");
1365                 return RLM_MODULE_INVALID;
1366         }
1367
1368         if(request->password->attribute != PW_PASSWORD) {
1369                 radlog(L_AUTH, "rlm_ldap: Attribute \"User-Password\" is required for authentication. Cannot use \"%s\".", request->password->name);
1370                 return RLM_MODULE_INVALID;
1371         }
1372
1373         if (request->password->length == 0) {
1374                 radlog(L_ERR, "rlm_ldap: empty password supplied");
1375                 return RLM_MODULE_INVALID;
1376         }
1377
1378         /*
1379          * Check that we don't have any failed connections. If we do there's no real need
1380          * of runing. Also give it another chance if we have a lot of failed connections.
1381          */
1382         if (inst->failed_conns > MAX_FAILED_CONNS_END)
1383                 inst->failed_conns = 0;
1384         if (inst->failed_conns > MAX_FAILED_CONNS_START){
1385                 inst->failed_conns++;
1386                 return RLM_MODULE_FAIL;
1387         }
1388
1389
1390         DEBUG("rlm_ldap: login attempt by \"%s\" with password \"%s\"", 
1391                request->username->strvalue, request->password->strvalue);
1392
1393         while((vp_user_dn = pairfind(request->packet->vps, PW_LDAP_USERDN)) == NULL) {
1394                 if (!radius_xlat(filter, sizeof(filter), inst->filter,
1395                                 request, NULL)) {
1396                         radlog (L_ERR, "rlm_ldap: unable to create filter.\n"); 
1397                         return RLM_MODULE_INVALID;
1398                 }
1399
1400                 if (!radius_xlat(basedn, sizeof(basedn), inst->basedn,
1401                                 request, NULL)) {
1402                         radlog (L_ERR, "rlm_ldap: unable to create basedn.\n");
1403                         return RLM_MODULE_INVALID;
1404                 }
1405
1406                 if ((conn_id = ldap_get_conn(inst->conns,&conn,inst)) == -1){
1407                         radlog(L_ERR, "rlm_ldap: All ldap connections are in use");
1408                         return RLM_MODULE_FAIL;
1409                 }
1410                 if ((res = perform_search(instance, conn, basedn, LDAP_SCOPE_SUBTREE, filter, attrs, &result)) != RLM_MODULE_OK) {
1411                         if (res == RLM_MODULE_NOTFOUND){
1412                                 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: User not found");
1413                                 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
1414                                 pairadd(&request->packet->vps, module_fmsg_vp);
1415                         }
1416                         ldap_release_conn(conn_id,inst->conns);
1417                         return (res);
1418                 }
1419                 if ((msg = ldap_first_entry(conn->ld, result)) == NULL) {
1420                         ldap_msgfree(result);
1421                         ldap_release_conn(conn_id,inst->conns);
1422                         return RLM_MODULE_FAIL;
1423                 }
1424                 if ((user_dn = ldap_get_dn(conn->ld, msg)) == NULL) {
1425                         DEBUG("rlm_ldap: ldap_get_dn() failed");
1426                         ldap_msgfree(result);
1427                         ldap_release_conn(conn_id,inst->conns);
1428                         return RLM_MODULE_FAIL;
1429                 }
1430                 ldap_release_conn(conn_id,inst->conns);
1431                 pairadd(&request->packet->vps, pairmake("Ldap-UserDn", user_dn, T_OP_EQ));
1432                 ldap_memfree(user_dn);
1433                 ldap_msgfree(result);
1434         }
1435
1436         user_dn = vp_user_dn->strvalue;
1437
1438         DEBUG("rlm_ldap: user DN: %s", user_dn);
1439
1440         ld_user = ldap_connect(instance, user_dn, request->password->strvalue,
1441                                1, &res);
1442         if (ld_user == NULL){
1443                 if (res == RLM_MODULE_REJECT){
1444                         inst->failed_conns = 0;
1445                         snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: Bind as user failed");
1446                         module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
1447                         pairadd(&request->packet->vps, module_fmsg_vp);
1448                 }
1449                 if (res == RLM_MODULE_FAIL){
1450                         DEBUG("rlm_ldap: ldap_connect() failed");
1451                         inst->failed_conns++;
1452                 }
1453                 return (res);
1454         }
1455
1456         DEBUG("rlm_ldap: user %s authenticated succesfully",
1457               request->username->strvalue);
1458         ldap_unbind_s(ld_user);
1459         inst->failed_conns = 0;
1460
1461         return RLM_MODULE_OK;
1462 }
1463
1464 static LDAP    *
1465 ldap_connect(void *instance, const char *dn, const char *password, int auth, int *result)
1466 {
1467         ldap_instance  *inst = instance;
1468         LDAP           *ld = NULL;
1469         int             msgid, rc, ldap_version;
1470         int             ldap_errno = 0;
1471         LDAPMessage    *res;
1472
1473         if (inst->is_url){
1474 #ifdef HAVE_LDAP_INITIALIZE
1475                 DEBUG("rlm_ldap: (re)connect to %s, authentication %d", inst->server, auth);
1476                 if (ldap_initialize(&ld, inst->server) != LDAP_SUCCESS) {
1477                         radlog(L_ERR, "rlm_ldap: ldap_initialize() failed");
1478                         *result = RLM_MODULE_FAIL;
1479                         return (NULL);
1480                 }
1481 #endif
1482         }
1483         else{
1484                 DEBUG("rlm_ldap: (re)connect to %s:%d, authentication %d", inst->server, inst->port, auth);
1485                 if ((ld = ldap_init(inst->server, inst->port)) == NULL) {
1486                         radlog(L_ERR, "rlm_ldap: ldap_init() failed");
1487                         *result = RLM_MODULE_FAIL;
1488                         return (NULL);
1489                 }
1490         }
1491         if (ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, (void *) &(inst->net_timeout)) != LDAP_OPT_SUCCESS) {
1492                 radlog(L_ERR, "rlm_ldap: Could not set LDAP_OPT_NETWORK_TIMEOUT %ld.%ld", inst->net_timeout.tv_sec, inst->net_timeout.tv_usec);
1493         }
1494         if (ldap_set_option(ld, LDAP_OPT_TIMELIMIT, (void *) &(inst->timelimit)) != LDAP_OPT_SUCCESS) {
1495                 radlog(L_ERR, "rlm_ldap: Could not set LDAP_OPT_TIMELIMIT %d", inst->timelimit);
1496         }
1497         if (inst->ldap_debug && ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &(inst->ldap_debug)) != LDAP_OPT_SUCCESS) {
1498                 radlog(L_ERR, "rlm_ldap: Could not set LDAP_OPT_DEBUG_LEVEL %d", inst->ldap_debug);
1499         }
1500         ldap_version = LDAP_VERSION3;
1501         if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldap_version) != LDAP_OPT_SUCCESS) {
1502                 radlog(L_ERR, "rlm_ldap: Could not set LDAP version to V3");
1503         }
1504 #ifdef HAVE_LDAP_START_TLS
1505         if(inst->tls_mode) {
1506                 DEBUG("rlm_ldap: setting TLS mode to %d", inst->tls_mode);
1507                 if(ldap_set_option(ld, LDAP_OPT_X_TLS,
1508                            (void *) &(inst->tls_mode)) != LDAP_OPT_SUCCESS) {
1509                         ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
1510                         radlog(L_ERR, "rlm_ldap: could not set LDAP_OPT_X_TLS option %s", ldap_err2string(ldap_errno));
1511                 }
1512         }
1513         if (inst->start_tls) {
1514                 DEBUG("rlm_ldap: starting TLS");
1515                 rc = ldap_start_tls_s(ld, NULL, NULL);
1516                 if (rc != LDAP_SUCCESS) {
1517                         DEBUG("rlm_ldap: ldap_start_tls_s()");
1518                         ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
1519                         radlog(L_ERR, "rlm_ldap: could not start TLS %s", ldap_err2string(ldap_errno));
1520                         *result = RLM_MODULE_FAIL;
1521                         ldap_unbind_s(ld);
1522                         return (NULL);
1523                 }
1524         }
1525 #endif /* HAVE_LDAP_START_TLS */
1526
1527         if (inst->is_url){
1528                 DEBUG("rlm_ldap: bind as %s/%s to %s", dn, password, inst->server);
1529         }
1530         else{
1531                 DEBUG("rlm_ldap: bind as %s/%s to %s:%d", dn, password, inst->server, inst->port);
1532         }
1533         msgid = ldap_bind(ld, dn, password,LDAP_AUTH_SIMPLE);
1534         if (msgid == -1) {
1535                 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
1536                 if (inst->is_url)
1537                         radlog(L_ERR, "rlm_ldap: %s bind to %s failed: %s",
1538                                 dn, inst->server, ldap_err2string(ldap_errno));
1539                 else
1540                         radlog(L_ERR, "rlm_ldap: %s bind to %s:%d failed: %s",
1541                                 dn, inst->server, inst->port,
1542                                 ldap_err2string(ldap_errno));
1543                 *result = RLM_MODULE_FAIL;
1544                 ldap_unbind_s(ld);
1545                 return (NULL);
1546         }
1547         DEBUG("rlm_ldap: waiting for bind result ...");
1548
1549         rc = ldap_result(ld, msgid, 1, &(inst->timeout), &res);
1550
1551         if(rc < 1) {
1552                 DEBUG("rlm_ldap: ldap_result()");
1553                 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
1554                 if (inst->is_url)
1555                         radlog(L_ERR, "rlm_ldap: %s bind to %s failed: %s",
1556                                 dn, inst->server, (rc == 0) ? "timeout" : ldap_err2string(ldap_errno));
1557                 else
1558                         radlog(L_ERR, "rlm_ldap: %s bind to %s:%d failed: %s",
1559                                 dn, inst->server, inst->port,
1560                                 (rc == 0) ? "timeout" : ldap_err2string(ldap_errno));
1561                 *result = RLM_MODULE_FAIL;
1562                 ldap_unbind_s(ld);
1563                 return (NULL);
1564         }
1565         ldap_errno = ldap_result2error(ld, res, 1);
1566         switch (ldap_errno) {
1567         case LDAP_SUCCESS:
1568                 DEBUG("rlm_ldap: Bind was successful");
1569                 *result = RLM_MODULE_OK;
1570                 break;
1571
1572         case LDAP_INVALID_CREDENTIALS:
1573                 if (auth){ 
1574                         DEBUG("rlm_ldap: Bind failed with invalid credentials");
1575                         *result = RLM_MODULE_REJECT;
1576                 }
1577                 else {
1578                         radlog(L_ERR, "rlm_ldap: LDAP login failed: check login, password settings in ldap section of radiusd.conf");
1579                         *result = RLM_MODULE_FAIL;
1580                 }               
1581                 break;
1582                 
1583         default:
1584                 if (inst->is_url)
1585                         radlog(L_ERR,"rlm_ldap: %s bind to %s failed %s",
1586                                 dn, inst->server, ldap_err2string(ldap_errno));
1587                 else
1588                         radlog(L_ERR,"rlm_ldap: %s bind to %s:%d failed %s",
1589                                 dn, inst->server, inst->port,
1590                                 ldap_err2string(ldap_errno));
1591                 *result = RLM_MODULE_FAIL;
1592         }
1593
1594         if (*result != RLM_MODULE_OK) {
1595                 ldap_unbind_s(ld);
1596                 ld = NULL;
1597         }
1598         return ld;
1599 }
1600
1601 /*****************************************************************************
1602  *
1603  *      Detach from the LDAP server and cleanup internal state.
1604  *
1605  *****************************************************************************/
1606 static int 
1607 ldap_detach(void *instance)
1608 {
1609         ldap_instance  *inst = instance;
1610         TLDAP_RADIUS *pair, *nextpair;
1611
1612         if (inst->server)
1613                 free((char *) inst->server);
1614         if (inst->login)
1615                 free((char *) inst->login);
1616         if (inst->password)
1617                 free((char *) inst->password);
1618         if (inst->basedn)
1619                 free((char *) inst->basedn);
1620         if (inst->dictionary_mapping)
1621                 free(inst->dictionary_mapping);
1622         if (inst->filter)
1623                 free((char *) inst->filter);
1624         if (inst->base_filter)
1625                 free((char *) inst->base_filter);
1626         if (inst->passwd_hdr)
1627                 free((char *) inst->passwd_hdr);
1628         if (inst->passwd_attr)
1629                 free((char *) inst->passwd_attr);
1630         if (inst->groupname_attr)
1631                 free((char *) inst->groupname_attr);
1632         if (inst->groupmemb_filt)
1633                 free((char *) inst->groupmemb_filt);
1634         if (inst->groupmemb_attr)
1635                 free((char *) inst->groupmemb_attr);
1636         if (inst->access_attr)
1637                 free((char *) inst->access_attr);
1638         if (inst->profile_attr)
1639                 free((char *) inst->profile_attr);
1640         if (inst->conns){
1641                 int i=0;
1642
1643                 for(;i<inst->num_conns;i++){
1644                         if (inst->conns[i].ld){
1645                                 ldap_unbind_s(inst->conns[i].ld);
1646                         }
1647                         pthread_mutex_destroy(&inst->conns[i].mutex);
1648                 }
1649                 free(inst->conns);
1650         }
1651
1652         pair = inst->check_item_map;
1653         
1654         while (pair != NULL) {
1655                 nextpair = pair->next;
1656                 free(pair->attr);
1657                 free(pair->radius_attr);
1658                 free(pair);
1659                 pair = nextpair;
1660         }
1661
1662         pair = inst->reply_item_map;
1663
1664         while (pair != NULL) {
1665                 nextpair = pair->next;
1666                 free(pair->attr);
1667                 free(pair->radius_attr);
1668                 free(pair);
1669                 pair = nextpair;
1670         }
1671
1672         if (inst->atts)
1673                 free(inst->atts);
1674
1675         paircompare_unregister(PW_LDAP_GROUP, ldap_groupcmp);
1676         xlat_unregister(inst->xlat_name,ldap_xlat);
1677         free(inst->xlat_name);
1678
1679         free(inst);
1680
1681         return 0;
1682 }
1683
1684 #ifdef FIELDCPY
1685 static void 
1686 fieldcpy(char *string, char **uptr)
1687 {
1688         char           *ptr;
1689
1690         ptr = *uptr;
1691         while (*ptr == ' ' || *ptr == '\t') {
1692                 ptr++;
1693         }
1694         if (*ptr == '"') {
1695                 ptr++;
1696                 while (*ptr != '"' && *ptr != '\0' && *ptr != '\n') {
1697                         *string++ = *ptr++;
1698                 }
1699                 *string = '\0';
1700                 if (*ptr == '"') {
1701                         ptr++;
1702                 }
1703                 *uptr = ptr;
1704                 return;
1705         }
1706         while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0' && *ptr != '\n' &&
1707                *ptr != '=' && *ptr != ',') {
1708                 *string++ = *ptr++;
1709         }
1710         *string = '\0';
1711         *uptr = ptr;
1712         return;
1713 }
1714 #endif
1715 /*****************************************************************************
1716  *      Get RADIUS attributes from LDAP object
1717  *      ( according to draft-adoba-radius-05.txt
1718  *        <http://www.ietf.org/internet-drafts/draft-adoba-radius-05.txt> )
1719  *
1720  *****************************************************************************/
1721
1722 static VALUE_PAIR *
1723 ldap_pairget(LDAP * ld, LDAPMessage * entry,
1724              TLDAP_RADIUS * item_map, VALUE_PAIR **pairs,char is_check)
1725 {
1726         char          **vals;
1727         int             vals_count;
1728         int             vals_idx;
1729         char           *ptr;
1730         TLDAP_RADIUS   *element;
1731         LRAD_TOKEN      token;
1732         int             is_generic_attribute;
1733         char            value[256];
1734         VALUE_PAIR     *pairlist = NULL;
1735         VALUE_PAIR     *newpair = NULL;
1736
1737         /* check if there is a mapping from this LDAP attribute to a RADIUS attribute */
1738         for (element = item_map; element != NULL; element = element->next) {
1739         if ((vals = ldap_get_values(ld,entry,element->attr)) != NULL){
1740                         /* check whether this is a one-to-one-mapped ldap attribute or a generic
1741                            attribute and set flag accordingly */
1742
1743                         if (strcasecmp(element->radius_attr, GENERIC_ATTRIBUTE_ID)==0)
1744                                 is_generic_attribute = 1;
1745                         else
1746                                 is_generic_attribute = 0;
1747
1748                         /* find out how many values there are for the attribute and extract all of them */
1749
1750                         vals_count = ldap_count_values(vals);
1751
1752                         for (vals_idx = 0; vals_idx < vals_count; vals_idx++) {
1753                                 ptr = vals[vals_idx];
1754
1755                                 if (is_generic_attribute) {
1756                                         /* this is a generic attribute */
1757                                         LRAD_TOKEN dummy; /* makes pairread happy */
1758                                         
1759                                         /* not sure if using pairread here is ok ... */
1760                                         if ( (newpair = pairread(&ptr, &dummy)) != NULL) {
1761                                                 DEBUG("rlm_ldap: extracted attribute %s from generic item %s", 
1762                                                       newpair->name, vals[vals_idx]);
1763                                                 pairadd(&pairlist, newpair);
1764                                         } else {
1765                                                 radlog(L_ERR, "rlm_ldap: parsing %s failed: %s", 
1766                                                        element->attr, vals[vals_idx]);
1767                                         }
1768                                 } else {
1769                                         /* this is a one-to-one-mapped attribute */
1770                                         token = gettoken(&ptr, value, sizeof(value) - 1);
1771                                         if (token < T_EQSTART || token > T_EQEND) {
1772                                                 token = (is_check) ? T_OP_CMP_EQ : T_OP_EQ;
1773                                         } else {
1774                                                 gettoken(&ptr, value, sizeof(value) - 1);
1775                                         }
1776                                         if (value[0] == 0) {
1777                                                 DEBUG("rlm_ldap: Attribute %s has no value", element->attr);
1778                                                 break;
1779                                         }
1780                                         DEBUG("rlm_ldap: Adding %s as %s, value %s & op=%d", element->attr, element->radius_attr, value, token);
1781                                                 if ((newpair = pairmake(element->radius_attr, value, token)) == NULL)
1782                                                 continue;
1783                                         if (! vals_idx){
1784                                                 pairdelete(pairs,newpair->attribute);
1785                                         }
1786                                         pairadd(&pairlist, newpair);
1787                                 }
1788                         }
1789                         ldap_value_free(vals);
1790                 }
1791         }
1792
1793         return (pairlist);
1794 }
1795
1796 /* globally exported name */
1797 module_t        rlm_ldap = {
1798         "LDAP",
1799         RLM_TYPE_THREAD_SAFE,   /* type: reserved        */
1800         NULL,                   /* initialization        */
1801         ldap_instantiate,       /* instantiation         */
1802         {
1803                 ldap_authenticate,      /* authentication        */
1804                 ldap_authorize,         /* authorization         */
1805                 NULL,                   /* preaccounting         */
1806                 NULL,                   /* accounting            */
1807                 NULL,                   /* checksimul            */
1808                 NULL,                   /* pre-proxy             */
1809                 NULL,                   /* post-proxy            */
1810                 NULL                    /* post-auth             */
1811         },
1812         ldap_detach,            /* detach                */
1813         NULL,                   /* destroy               */
1814 };