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