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