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