f18f156ef14946497ee34bce4c9dbaca1fac797f
[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, which in turn was
6  * based mostly on a Mysql+Cistron patch from oyarzun@wilmington.net
7  *
8  * 17 Jan 2000: OpenLDAP SDK porting, basic TLS support, LDAP authorization,
9  *            fault tolerance with multiple LDAP server support done by Adrian
10  *            Pavlykevych <pam@polynet.lviv.ua>
11  * 24 May 2000: converting to new configuration file format, futher improvements
12  *              in fault tolerance, threaded operation
13  *              Adrian Pavlykevych <pam@polynet.lviv.ua>
14  */
15 static const char rcsid[] = "$Id$";
16
17 #include "autoconf.h"
18
19 #include        <sys/types.h>
20 #include        <sys/socket.h>
21 #include        <sys/time.h>
22 #include        <netinet/in.h>
23
24 #include        <stdio.h>
25 #include        <stdlib.h>
26 #include        <netdb.h>
27 #include        <pwd.h>
28 #include        <time.h>
29 #include        <ctype.h>
30 #include        <string.h>
31
32 #include        <lber.h>
33 #include        <ldap.h>
34
35 #include        <errno.h>
36 #include        <unistd.h>
37 #include        <pthread.h>
38
39 #include        "radiusd.h"
40 #include        "conffile.h"
41 #include        "modules.h"
42
43
44 #define MAX_AUTH_QUERY_LEN      256
45 #define TIMELIMIT 5
46
47 typedef struct {
48   char* attr;
49   char* radius_attr;
50 } TLDAP_RADIUS;
51
52 static char     *make_filter(char *, char *);
53 #ifdef FIELDCPY 
54 static void     fieldcpy(char *, char **);
55 #endif
56 static char             *dn_base(char *);
57 static VALUE_PAIR       *ldap_pairget(LDAP *, LDAPMessage *, TLDAP_RADIUS *);
58 static LDAP             *rlm_ldap_connect(const char *, const char *, int, int *);
59 void                    *conn_maint(void *);
60
61 #define MAX_SERVER_LINE 1024
62 /*
63  *      These should really be in a module-specific data structure,
64  *      which is passed to the module with every request.
65  */
66 static char     *ldap_server = NULL;
67 static int      ldap_port = 389;
68 static int      ldap_timelimit = TIMELIMIT;
69 /* wait forever on network activity */
70 static struct timeval ldap_net_timeout = { -1, 0 };
71 /* wait forever for search results */
72 static struct timeval ldap_timeout = { -1, 0 };
73 static struct timeval *timeout = NULL;
74 static int      ldap_cache_size = 0; /* cache size limited only by TTL */
75 static int      ldap_cache_ttl = 30 ;/* cache objects TTL 30 secs */    
76 static int      ldap_debug = 0; /* 0x0005; */
77 static int      ldap_tls_mode = LDAP_OPT_X_TLS_TRY;
78 static char     *ldap_login    = NULL;
79 static char     *ldap_password = NULL;
80 static char     *ldap_filter   = NULL;
81 static char     *ldap_basedn   = NULL;
82 static char     *group_basedn  = NULL;
83 static char     *ldap_radius_group = NULL;
84
85 static LDAP     *ld = NULL;
86 static pthread_t conn_thread = 0; /*ID of thread, which performs LDAP connect*/
87 /* signal from module thread to conn_maint thread to perform LDAP
88  * connection operation (bind/unbind) */
89 static pthread_cond_t conn_cv = PTHREAD_COND_INITIALIZER; 
90 /* signal from conn_maint to module threads to proceed afer (re)connect */
91 static pthread_cond_t go_ahead_cv = PTHREAD_COND_INITIALIZER;
92 /* signal from rlm_detach() to conn_maint thread to close connection */
93 static pthread_mutex_t sig_mutex = PTHREAD_MUTEX_INITIALIZER;
94 static pthread_mutex_t conn_mutex = PTHREAD_MUTEX_INITIALIZER;
95 static pthread_mutex_t mnt_mutex = PTHREAD_MUTEX_INITIALIZER;
96
97 static int bound;
98 static int cleanup;
99 static int readers; /* number of threads performing LDAP lookups */ 
100
101 static CONF_PARSER module_config[] = {
102   { "server",           PW_TYPE_STRING_PTR, &ldap_server,             NULL },
103   { "port",             PW_TYPE_INTEGER,    &ldap_port,               "389" },
104   { "net_timeout",      PW_TYPE_INTEGER,    &ldap_net_timeout.tv_sec, "-1" },
105   { "timeout",          PW_TYPE_INTEGER,    &ldap_timeout.tv_sec,     "-1" },
106
107   { "identity",         PW_TYPE_STRING_PTR, &ldap_login,              NULL },
108   { "password",         PW_TYPE_STRING_PTR, &ldap_password,           NULL },
109   { "basedn",           PW_TYPE_STRING_PTR, &ldap_basedn,             NULL },
110   { "filter",           PW_TYPE_STRING_PTR, &ldap_filter,             NULL },
111   { "access_group",     PW_TYPE_STRING_PTR, &ldap_radius_group,       NULL },
112
113   { "cache_size",       PW_TYPE_INTEGER,    &ldap_cache_size,         "0" },
114   { "cache_ttl",        PW_TYPE_INTEGER,    &ldap_cache_ttl,          "30" },
115   
116   { NULL, -1, NULL, NULL }
117 };
118
119 /* LDAP attribute name that controls remote access */
120 #define LDAP_RADIUSACCESS "dialupAccess"
121
122 /* Fallback to default settings if no basic attributes defined in object
123  * (An ugly hack to be replaced by profiles and policies)
124  */
125 #define DEFAULT_CONF
126
127 #ifdef DEFAULT_CONF
128 #define DEFAULT_SERVICE_TYPE "Framed-User"
129 #define DEFAULT_FRAMED_PROTOCOL "PPP"
130 #define DEFAULT_FRAMED_MTU "576"
131 #define DEFAULT_FRAMED_COMPRESSION "Van-Jacobson-TCP-IP"
132 #define DEFAULT_IDLE_TIMEOUT "240"
133 #define DEFAULT_SIMULTANEOUS_USE "1"
134 #endif
135
136 #define ld_valid                ld_options.ldo_valid
137 #define LDAP_VALID_SESSION      0x2
138 #define LDAP_VALID(ld)  ( (ld)->ld_valid == LDAP_VALID_SESSION )
139
140 /*
141  *      Mappings of LDAP radius* attributes to RADIUS attributes
142  *
143  *      Hmm... these should really be read in from the configuration file
144  */
145 static TLDAP_RADIUS check_item_map[] = {
146         { "radiusAuthType", "Auth-Type" },
147         { "npSessionsAllowed", "Simultaneous-Use" },
148         {NULL, NULL}
149 };
150 static TLDAP_RADIUS reply_item_map[] = {
151         { "radiusServiceType", "Service-Type" },
152         { "radiusFramedProtocol", "Framed-Protocol" },
153         { "radiusFramedIPAddress", "Framed-IP-Address" },
154         { "radiusFramedIPNetmask", "Framed-IP-Netmask" },
155         { "radiusFramedRoute", "Framed-Route" },
156         { "radiusFramedRouting", "Framed-Routing" },
157         { "radiusFilterId", "Filter-Id" },
158         { "radiusFramedMTU", "Framed-MTU" },
159         { "radiusFramedCompression", "Framed-Compression" },
160         { "radiusLoginIPHost", "Login-IP-Host" },
161         { "radiusLoginService", "Login-Service" },
162         { "radiusLoginTCPPort", "Login-TCP-Port" },
163         { "radiusCallbackNumber", "Callback-Number" },
164         { "radiusCallbackId", "Callback-Id" },
165         { "radiusFramedRoute", "Framed-Route" },
166         { "radiusFramedIPXNetwork", "Framed-IPX-Network" },
167         { "radiusClass", "Class" },
168         { "radiusSessionTimeout", "Session-Timeout" },
169         { "radiusIdleTimeout", "Idle-Timeout" },
170         { "radiusTerminationAction", "Termination-Action" },
171         { "radiusCalledStationId", "Called-Station-Id" },
172         { "radiusCallingStationId", "Calling-Station-Id" },
173         { "radiusLoginLATService", "Login-LAT-Service" },
174         { "radiusLoginLATNode", "Login-LAT-Node" },
175         { "radiusLoginLATGroup", "Login-LAT-Group" },
176         { "radiusFramedAppleTalkLink", "Framed-AppleTalk-Link" },
177         { "radiusFramedAppleTalkNetwork", "Framed-AppleTalk-Network" },
178         { "radiusFramedAppleTalkZone", "Framed-AppleTalk-Zone" },
179         { "radiusPortLimit", "Port-Limit" },
180         { "radiusLoginLATPort", "Login-LAT-Port" },
181         {NULL, NULL}
182 };
183
184 /*************************************************************************
185  *
186  *      Function: rlm_ldap_instantiate
187  *
188  *      Purpose: Uses section of radiusd config file passed as parameter
189  *               to create an instance ofthe module.
190  *      Note:    Currently multiple instances are not supported, lots of
191  *               data structures restructuring should be done to support it 
192  *
193  *************************************************************************/
194 static int rlm_ldap_instantiate(CONF_SECTION *conf, void **instance)
195 {
196   int i;
197   cf_section_parse(conf, module_config);
198
199   if(ldap_radius_group != NULL)
200     group_basedn = dn_base(ldap_radius_group);
201   if(ldap_net_timeout.tv_sec != -1)
202     timeout = &ldap_net_timeout;
203   if(ldap_timeout.tv_sec != -1 )
204          timeout = &ldap_timeout;
205
206   bound = 0;
207   cleanup = 0;
208   readers = 0;
209   
210   DEBUG("rlm_ldap_instantiate() using:");
211   for(i = 0; module_config[i].name != NULL; i++) {
212     switch(module_config[i].type){
213       case PW_TYPE_STRING_PTR:
214         DEBUG( "%s = %s",module_config[i].name, (char *)*(int *)(module_config[i].data));
215         break;
216       case PW_TYPE_INTEGER:
217         DEBUG( "%s = %d",module_config[i].name, *((int *)module_config[i].data));
218         break;
219       default:
220         DEBUG("ERROR!!!");
221     }
222   }
223   if(timeout != NULL)
224     DEBUG("timeout: %ld.%ld", timeout->tv_sec,timeout->tv_usec);
225
226   DEBUG("Starting connection thread");
227   pthread_mutex_lock(&mnt_mutex);
228   pthread_create(&conn_thread,NULL,conn_maint,NULL);
229 /* Block until maintainer is created and initialized */
230   pthread_mutex_lock(&mnt_mutex);
231   pthread_mutex_unlock(&mnt_mutex);
232   DEBUG("Connection thread started");
233   
234   return 0;
235 }
236
237 void *conn_maint(void *arg)
238 {
239   int res;
240   int conn_state = 0;
241    
242   pthread_mutex_unlock(&mnt_mutex);
243   while(1){
244     DEBUG("rlm_ldap: connection maintainer obtaining lock ...");
245     pthread_mutex_lock(&sig_mutex);
246     DEBUG("rlm_ldap: maintainer got lock.");
247     while((bound && !cleanup) || readers) {
248       DEBUG("rlm_ldap: maintainer waits for wakeup signal, %d readers active", readers);
249       pthread_cond_wait(&conn_cv,&sig_mutex);
250     }
251     DEBUG("rlm_ldap: connection maintainer woke up");
252     if(cleanup){
253       DEBUG("rlm_ldap: maintainer #%p closing connection", pthread_self());
254       ldap_unbind_s(ld);
255       pthread_mutex_unlock(&sig_mutex);
256       pthread_exit(0);
257     }
258     if((ld = rlm_ldap_connect(ldap_login, ldap_password, 0, &res)) == NULL) {
259       conn_state = 0;
260       sleep(1);
261     } else {
262       conn_state = 1;
263     }
264 //    pthread_mutex_lock(&sig_mutex);
265     bound = conn_state;
266     DEBUG("rlm_ldap: maintainer reconnecton attempt succeeded? (%d)", bound);
267     if(bound && readers) { 
268       DEBUG("rlm_ldap: maintainer waking workers");
269       pthread_cond_broadcast(&go_ahead_cv);
270       DEBUG("rlm_ldap: worker wakeup done.");
271     }
272     pthread_mutex_unlock(&sig_mutex);
273   }
274 }
275
276 static int perform_search(char *ldap_basedn, char *filter, char **attrs, LDAPMessage **result)
277 {
278   int msgid;
279   int res = RLM_MODULE_OK;
280   int rc;
281
282   DEBUG2("rlm_ldap: thread #%p trying to get lock", pthread_self());
283   pthread_mutex_lock(&sig_mutex);
284   readers++;
285 /*
286  * Wake up connection maintenance thread, if there is no established LDAP
287  * connection 
288  */
289   DEBUG2("rlm_ldap: thread #%p got lock", pthread_self());
290   if(!bound){
291     DEBUG("rlm_ldap: worker thread #%p signaling for LDAP reconnection", pthread_self());
292     pthread_cond_signal(&conn_cv);
293     DEBUG("rlm_ldap: worker thread #%p waiting for LDAP connection", pthread_self());
294   }
295   DEBUG2("rlm_ldap: thread #%p bind loop", pthread_self());
296   while(!bound)
297     pthread_cond_wait(&go_ahead_cv, &sig_mutex);
298   DEBUG2("rlm_ldap: thread #%p unlocking", pthread_self());
299   pthread_mutex_unlock(&sig_mutex);
300
301   DEBUG("rlm_ldap: thread #%p performing search", pthread_self());
302   pthread_mutex_lock(&conn_mutex);
303   msgid = ldap_search(ld,ldap_basedn,LDAP_SCOPE_SUBTREE,filter,attrs,0);
304   pthread_mutex_unlock(&conn_mutex);
305   if(msgid == -1) {
306     radlog(L_ERR,"rlm_ldap: thread #%p ldap_search() API failed\n", pthread_self());
307     goto fail;
308   }
309   
310   pthread_mutex_lock(&conn_mutex);
311   rc = ldap_result(ld, msgid, 1, timeout, result);
312   pthread_mutex_unlock(&conn_mutex);
313   
314   if(rc < 1) {
315     ldap_perror( ld, "rlm_ldap: ldap_result()" );
316     radlog(L_ERR,"rlm_ldap: thread #%p ldap_result() failed - %s\n", pthread_self(), strerror(errno));
317     goto fail;
318   }
319   
320   switch(ldap_result2error(ld, *result, 0)) {
321     case LDAP_SUCCESS:
322       break;
323
324     case LDAP_TIMELIMIT_EXCEEDED:
325       radlog(L_ERR, "rlm_ldap: thread #%p Warning timelimit exceeded, using partial results\n", pthread_self());
326       break;
327
328     default:
329       DEBUG("rlm_ldap: thread #%p ldap_search() failed", pthread_self());
330        ldap_msgfree(*result);
331       goto fail;
332   }
333
334   if ((ldap_count_entries(ld, *result)) != 1) {
335     DEBUG("rlm_ldap: thread #%p user object not found or got ambiguous search result", pthread_self());
336      ldap_msgfree(*result);
337     res = RLM_MODULE_NOTFOUND;
338   }
339   
340   DEBUG2("rlm_ldap: thread #%p locking connection flag ...", pthread_self());
341   pthread_mutex_lock(&sig_mutex);
342   DEBUG2("rlm_ldap: done. thread #%p Clearing flag, active workers: %d", pthread_self(), readers-1);
343   readers--;
344   pthread_mutex_unlock(&sig_mutex);
345   return res;
346
347 fail:  
348   DEBUG2("rlm_ldap: thread #%p locking connection flag ...", pthread_self());
349   pthread_mutex_lock(&sig_mutex);
350   DEBUG2("rlm_ldap: thread #%p done. Clearing flag, active workers: %d", pthread_self(), readers-1);
351   bound = 0; readers--;
352   DEBUG("rlm_ldap: thread #%p sending signal to maintainer...", pthread_self());
353   pthread_cond_signal(&conn_cv);
354   DEBUG2("rlm_ldap: thread #%p done", pthread_self());
355   pthread_mutex_unlock(&sig_mutex);
356   return RLM_MODULE_FAIL;
357 }
358
359 /******************************************************************************
360  *
361  *      Function: rlm_ldap_authorize
362  *
363  *      Purpose: Check if user is authorized for remote access 
364  *
365  *****************************************************************************/
366 static int rlm_ldap_authorize(void *instance, REQUEST *request)
367 {
368     LDAPMessage *result, *msg, *gr_result, *gr_msg;
369     char *filter, *name, *user_dn,
370         *attrs[]        = { "*", NULL },
371         *group_attrs[]  = { "member", NULL      },
372         **vals;
373     VALUE_PAIR      *check_tmp;
374     VALUE_PAIR      *reply_tmp;
375     int  i;
376     int  res;
377     VALUE_PAIR **check_pairs, **reply_pairs;
378
379     check_pairs = &request->config_items;
380     reply_pairs = &request->reply->vps;
381
382     DEBUG("rlm_ldap: thread #%p - authorize", pthread_self());
383     name = request->username->strvalue;
384
385     /*
386      *      Check for valid input, zero length names not permitted
387      */
388     if (name[0] == 0) {
389           radlog(L_ERR, "rlm_ldap: zero length username not permitted\n");
390           return RLM_MODULE_INVALID;
391     }
392
393 /*  Unfortunately LDAP queries are case insensitive, so in order to provide
394     unique names for simultaneous logins verification, we need to lowercase
395     USERNAME attribute value
396 */
397     for(i=0; name[i] != 0; i++) 
398         name[i] = tolower(name[i]);
399
400     DEBUG("rlm_ldap: performing user authorization for %s", name);
401
402     filter = make_filter(ldap_filter, name);
403
404     if((res = perform_search(ldap_basedn, filter, attrs, &result)) != 0)
405         return(res);
406
407     if((msg = ldap_first_entry(ld,result)) == NULL) {
408       DEBUG("rlm_ldap: thread #%p ldap_first_entry() failed", pthread_self());
409        ldap_msgfree(result);
410       return RLM_MODULE_FAIL;
411     }
412
413     if ((user_dn = ldap_get_dn(ld,msg)) == NULL) {
414       DEBUG("rlm_ldap: thread #%p ldap_get_dn() failed", pthread_self());
415        ldap_msgfree(result);
416       return RLM_MODULE_FAIL;
417     }
418     
419 #ifdef DIALUP_ACCESS
420 /* Remote access is controled by LDAP_RADIUSACCESS attribute of user object */ 
421     if((vals = ldap_get_values(ld, msg, LDAP_RADIUSACCESS)) != NULL ) {
422       if(!strncmp(vals[0],"FALSE",5)) {
423         DEBUG("rlm_ldap: thread #%p dialup access disabled", pthread_self());
424          ldap_msgfree(result);
425         return RLM_MODULE_REJECT;
426       }
427     } else {
428       DEBUG("rlm_ldap: thread #%p no %s attribute - access denied by default", pthread_self(), LDAP_RADIUSACCESS);
429        ldap_msgfree(result);
430       return RLM_MODULE_REJECT;
431     }
432 #endif
433
434 /* Remote access controled by group membership attribute of user object */ 
435     if(ldap_radius_group != NULL) {
436       int found = 0;
437       
438       DEBUG("rlm_ldap: thread #%p checking user membership in dialup-enabling group %s", pthread_self(), ldap_radius_group);
439       if((res = perform_search(group_basedn, ldap_radius_group, group_attrs, &gr_result)))
440         return(res);
441       
442       if((gr_msg = ldap_first_entry(ld, gr_result)) == NULL) {
443         DEBUG("rlm_ldap: thread #%p ldap_first_entry() failed", pthread_self);
444          ldap_msgfree(result);
445         return RLM_MODULE_FAIL;
446       }
447
448       if((vals = ldap_get_values(ld, gr_msg, "member")) != NULL ) {
449         int valno;
450         for(valno = 0;
451             (vals[valno] != NULL) 
452             && !(found = !strncmp(vals[valno],user_dn,strlen(user_dn)));
453             valno++) {
454         }
455         ldap_value_free(vals);
456       }
457       ldap_msgfree(gr_result);
458       if(!found){
459         DEBUG("rlm_ldap: thread #%p user does not belong to dialup-enabling group", pthread_self());
460         ldap_msgfree(result);
461         return RLM_MODULE_REJECT;
462       }
463     }
464
465
466     DEBUG("rlm_ldap: thread #%p looking for check items in directory...", pthread_self()); 
467     if((check_tmp = ldap_pairget(ld, msg, check_item_map)) != (VALUE_PAIR *)0) {
468         pairadd(check_pairs, check_tmp);
469     }
470
471 /* Module should default to LDAP authentication if no Auth-Type specified */
472     if(pairfind(*check_pairs, PW_AUTHTYPE) == NULL){
473         pairadd(check_pairs, pairmake("Auth-Type", "LDAP", T_OP_CMP_EQ));
474     }
475 /* 
476  * Adding new attribute containing DN for LDAP object associated with given
477  * username 
478  */
479     pairadd(&request->packet->vps, pairmake("Ldap-UserDn", user_dn, T_OP_EQ));
480
481     DEBUG("rlm_ldap: thread #%p looking for reply items in directory...", pthread_self()); 
482     if((reply_tmp = ldap_pairget(ld,msg, reply_item_map)) != (VALUE_PAIR *)0) {
483         pairadd(reply_pairs, reply_tmp);
484     }
485
486 #ifdef DEFAULT_CONF
487     if(pairfind(*reply_pairs, PW_SERVICE_TYPE) == NULL){
488         pairadd(reply_pairs, pairmake("Service-Type", DEFAULT_SERVICE_TYPE, T_OP_EQ));
489     }
490     if(pairfind(*reply_pairs, PW_FRAMED_PROTOCOL) == NULL){
491         pairadd(reply_pairs, pairmake("Framed-Protocol", DEFAULT_FRAMED_PROTOCOL, T_OP_EQ));
492     }
493     if(pairfind(*reply_pairs, PW_FRAMED_MTU) == NULL){
494         pairadd(reply_pairs, pairmake("Framed-MTU", DEFAULT_FRAMED_MTU, T_OP_EQ));
495     }
496     if(pairfind(*reply_pairs, PW_FRAMED_COMPRESSION) == NULL){
497         pairadd(reply_pairs, pairmake("Framed-Compression", DEFAULT_FRAMED_COMPRESSION, T_OP_EQ));
498     }
499     if(pairfind(*reply_pairs, PW_IDLE_TIMEOUT) == NULL){
500         pairadd(reply_pairs, pairmake("Idle-Timeout", DEFAULT_IDLE_TIMEOUT, T_OP_EQ));       
501     }
502     if(pairfind(*check_pairs, PW_SIMULTANEOUS_USE) == NULL){
503         pairadd(check_pairs, pairmake("Simultaneous-Use", DEFAULT_SIMULTANEOUS_USE, T_OP_EQ));       
504     }
505
506 #endif
507
508     DEBUG("rlm_ldap: thread #%p user %s authorized to use remote access", pthread_self(), name);
509      ldap_msgfree(result);
510     return RLM_MODULE_OK;
511 }
512
513 /******************************************************************************
514  *
515  *      Function: rlm_ldap_authenticate
516  *
517  *      Purpose: Check the user's password against ldap database 
518  *
519  *****************************************************************************/
520 static int rlm_ldap_authenticate(void *instance, REQUEST *request);
521 {
522     LDAP *ld_user;
523     LDAPMessage *result, *msg;
524     char *filter, *passwd, *user_dn, *name,
525          *attrs[] = { "uid", NULL };
526     int  res;
527     VALUE_PAIR *vp_user_dn;
528
529     DEBUG("rlm_ldap: thread #%p - authenticate", pthread_self());
530     /*
531      * Ensure that we're being passed a plain-text password,
532      *  and not anything else. 
533      */
534     if(request->password->attribute != PW_PASSWORD) {
535       radlog(L_AUTH, "rlm_ldap: Attribute \"Password\" is required for authentication.  Cannot use \"%s\".", request->password->name);
536       return RLM_MODULE_INVALID;
537     }
538
539     name = request->username->strvalue;
540     passwd = request->password->strvalue;
541
542     if(strlen(passwd) == 0) {
543         radlog(L_ERR, "rlm_ldap: empty password supplied");
544         return RLM_MODULE_INVALID;
545     }
546
547     DEBUG("rlm_ldap: thread #%p login attempt by \"%s\" with password \"%s\"", pthread_self(), name, passwd);
548     filter = make_filter(ldap_filter, name);
549
550     if((vp_user_dn = pairfind(request->packet->vps, LDAP_USERDN)) == NULL){
551       if((res = perform_search(ldap_basedn, filter, attrs, &result)))
552         return(res);
553
554       if ((msg = ldap_first_entry(ld,result)) == NULL) {
555          ldap_msgfree(result);
556
557         return RLM_MODULE_FAIL;
558       }
559
560       if ((user_dn = ldap_get_dn(ld,msg)) == NULL) {
561         DEBUG("rlm_ldap: thread #%p ldap_get_dn() failed", pthread_self());
562          ldap_msgfree(result);
563
564         return RLM_MODULE_FAIL;
565       }
566       pairadd(&request->packet->vps,pairmake("Ldap-UserDn",user_dn,T_OP_EQ));
567       ldap_msgfree(result);
568
569     } else {    
570
571       user_dn = vp_user_dn->strvalue;
572     }
573
574     DEBUG("rlm_ldap: thread #%p user DN: %s", pthread_self(), user_dn);
575
576     if((ld_user = rlm_ldap_connect(user_dn, passwd, 1, &res)) == NULL)
577         return (res);
578
579     DEBUG("rlm_ldap: thread #%p user %s authenticated succesfully", pthread_self(), name);
580     ldap_unbind_s(ld_user);
581     return RLM_MODULE_OK;
582 }
583
584 static LDAP *rlm_ldap_connect(const char *dn, const char *password, int auth, int *result)
585 {
586     LDAP *ld;
587     int msgid, rc;
588     LDAPMessage  *res;
589
590     DEBUG("rlm_ldap: thread #%p (re)connect, authentication %d", pthread_self(), auth);
591     if ((ld = ldap_init(ldap_server,ldap_port)) == NULL){
592       radlog(L_ERR, "rlm_ldap: ldap_init() failed");        
593       *result = RLM_MODULE_FAIL;
594       return(NULL);
595     }
596     
597     if (timeout != NULL && ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, (void *)timeout) != LDAP_OPT_SUCCESS) {
598       radlog(L_ERR, "rlm_ldap: Could not set LDAP_OPT_NETWORK_TIMEOUT %d.%d", timeout->tv_sec, timeout->tv_usec);
599     }
600    
601     if (ldap_timelimit != -1 && ldap_set_option(ld, LDAP_OPT_TIMELIMIT, (void *) &ldap_timelimit) != LDAP_OPT_SUCCESS ){
602         radlog(L_ERR, "rlm_ldap: Could not set LDAP_OPT_TIMELIMIT %d", ldap_timelimit );
603     }
604     
605     if(ldap_debug && ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &ldap_debug ) != LDAP_OPT_SUCCESS ) {
606         radlog(L_ERR, "rlm_ldap: Could not set LDAP_OPT_DEBUG_LEVEL %d", ldap_debug);
607     }
608 #ifdef HAVE_TLS
609     if (ldap_tls_mode && ldap_set_option(ld, LDAP_OPT_X_TLS,(void *) &ldap_tls_mode) != LDAP_OPT_SUCCESS ){
610         radlog(L_ERR, "rlm_ldap: Could not set LDAP_OPT_X_TLS_TRY");
611     }
612 #endif
613
614     if (!auth)
615       if (ldap_enable_cache(ld, ldap_cache_ttl, ldap_cache_size) != LDAP_SUCCESS)
616         radlog(L_ERR,"rlm_ldap: ldap_enable_cache failed");
617
618     msgid = ldap_bind(ld, dn, password, LDAP_AUTH_SIMPLE);
619     if(msgid == -1) {
620         ldap_perror(ld, "rlm_ldap: rlm_ldap_connect()" );
621         *result = RLM_MODULE_FAIL;
622         ldap_unbind_s(ld);
623         return(NULL);
624     }
625
626     DEBUG("rlm_ldap: thread #%p rlm_ldap_connect() waiting for bind result ...", pthread_self());
627
628     rc = ldap_result(ld, msgid, 1, timeout, &res);
629     if(rc < 1){
630         ldap_perror( ld, "rlm_ldap: ldap_result()" );
631         *result = RLM_MODULE_FAIL;
632         ldap_unbind_s(ld);
633         return(NULL);
634     }
635     DEBUG("rlm_ldap: thread #%p rlm_ldap_connect() bind finished", pthread_self());
636     switch(ldap_result2error(ld, res, 1)) {
637         case LDAP_SUCCESS:
638                 *result = RLM_MODULE_OK;
639                 break;
640
641         case LDAP_INVALID_CREDENTIALS:
642                 if(auth){
643                         *result = RLM_MODULE_REJECT;
644                         break;
645                 }
646         default:
647                 DEBUG("rlm_ldap: thread #%p LDAP FAILURE", pthread_self());
648                 *result = RLM_MODULE_FAIL;
649     } 
650     if(*result != RLM_MODULE_OK) {
651         ldap_unbind_s(ld);
652         ld = NULL;
653     }
654     return ld;
655 }
656
657 /*****************************************************************************
658  * 
659  *      Detach from the LDAP server and cleanup internal state.
660  *      
661  *****************************************************************************/
662 static int rlm_ldap_detach(void *instance)
663 {
664   pthread_mutex_lock(&sig_mutex);
665   cleanup = 1;
666   pthread_cond_signal(&conn_cv);
667   pthread_mutex_unlock(&sig_mutex);
668   pthread_join(conn_thread, NULL);
669   return 0;
670 }
671
672 /*****************************************************************************
673  * 
674  * This function takes DN as parameter and returns RDN and BASEDN for search.
675  * Has anyone better idea about getting object attributes based on its DN?
676  *
677  *****************************************************************************/
678 char *dn_base(char *dn)
679 {
680   char *ptr;
681
682   if((ptr = (char *)strchr(dn, ',')) == NULL) {
683     DEBUG("Invalid DN syntax: no ',' in the string %s, maibe it's a CN? Returning default base", dn);  
684     return(ldap_basedn);
685   }
686   ptr[0]='\0';
687   if(++ptr == NULL) {
688     DEBUG("Invalid DN syntax: ',' is the last symbol in the string");  
689     return(NULL);
690   }
691   return(ptr);
692 }
693
694 /*****************************************************************************
695  *      Replace %<whatever> in a string.
696  *
697  *      %u   User name
698  *
699  *****************************************************************************/
700 static char *make_filter(char *str, char *name)
701 {
702         static char buf[MAX_AUTH_QUERY_LEN];
703         int i = 0, c;
704         char *p;
705
706         for(p = str; *p; p++) {
707                 c = *p;
708                 if (c != '%' && c != '\\') {
709                         buf[i++] = *p;
710                         continue;
711                 }
712                 if (*++p == 0) break;
713                 if (c == '%') switch(*p) {
714                         case '%':
715                                 buf[i++] = *p;
716                                 break;
717                         case 'u': /* User name */
718                                 if (name != NULL)
719                                         strcpy(buf + i, name);
720                                 else
721                                         strcpy(buf + i, " ");
722                                 i += strlen(buf + i);
723                                 break;
724                         default:
725                                 buf[i++] = '%';
726                                 buf[i++] = *p;
727                                 break;
728                 }
729                 if (c == '\\') switch(*p) {
730                         case 'n':
731                                 buf[i++] = '\n';
732                                 break;
733                         case 'r':
734                                 buf[i++] = '\r';
735                                 break;
736                         case 't':
737                                 buf[i++] = '\t';
738                                 break;
739                         default:
740                                 buf[i++] = '\\';
741                                 buf[i++] = *p;
742                                 break;
743                 }
744         }
745         if (i >= MAX_AUTH_QUERY_LEN)
746                 i = MAX_AUTH_QUERY_LEN - 1;
747         buf[i++] = 0;
748         return buf;
749 }
750
751 #ifdef FIELDCPY 
752 static  void fieldcpy(char *string, char **uptr)
753 {
754         char    *ptr;
755
756         ptr = *uptr;
757         while (*ptr == ' ' || *ptr == '\t') {
758               ptr++;
759         }
760         if(*ptr == '"') {
761                 ptr++;
762                 while(*ptr != '"' && *ptr != '\0' && *ptr != '\n') {
763                         *string++ = *ptr++;
764                 }
765                 *string = '\0';
766                 if(*ptr == '"') {
767                         ptr++;
768                 }
769                 *uptr = ptr;
770                 return;
771         }
772
773         while(*ptr != ' ' && *ptr != '\t' && *ptr != '\0' && *ptr != '\n' &&
774                                                 *ptr != '=' && *ptr != ',') {
775                         *string++ = *ptr++;
776         }
777         *string = '\0';
778         *uptr = ptr;
779         return;
780 }
781 #endif
782 /*****************************************************************************
783  *      Get RADIUS attributes from LDAP object
784  *      ( according to draft-adoba-radius-05.txt 
785  *        <http://www.ietf.org/internet-drafts/draft-adoba-radius-05.txt> )
786  *
787  *****************************************************************************/
788
789 static VALUE_PAIR *ldap_pairget(LDAP *ld, LDAPMessage *entry,
790                                 TLDAP_RADIUS *item_map)
791 {
792   BerElement *berptr;
793   char *attr;
794   char **vals;
795   char *ptr;
796   TLDAP_RADIUS *element;
797   int token;
798   char value[64];
799   VALUE_PAIR *pairlist;
800   VALUE_PAIR *newpair = NULL;
801   pairlist = NULL;
802   if((attr = ldap_first_attribute(ld, entry, &berptr)) == (char *)0) {
803         DEBUG("rlm_ldap: thread #%p Object has no attributes", pthread_self());
804         return NULL;
805   }
806   
807   do {
808     for(element=item_map; element->attr != NULL; element++) {
809       if(!strncasecmp(attr,element->attr,strlen(element->attr))) {
810         if(((vals = ldap_get_values(ld, entry, attr)) == NULL) ||
811             (ldap_count_values(vals) > 1)) {
812           DEBUG("rlm_ldap: thread #%p Attribute %s has multiple values", pthread_self(), attr);
813           break;
814         }
815         ptr = vals[0];
816         token = gettoken(&ptr, value, sizeof(value));
817         if (token < T_EQSTART || token > T_EQEND) {
818           token = T_OP_EQ;      
819         } else {
820           gettoken(&ptr, value, sizeof(value));
821         }
822         if (value[0] == 0) {
823           DEBUG("rlm_ldap: thread #%p Attribute %s has no value", pthread_self(), attr);
824           break;
825         }
826         DEBUG("rlm_ldap: thread #%p Adding %s as %s, value %s & op=%d", pthread_self(), attr, element->radius_attr, value, token);
827         if((newpair = pairmake(element->radius_attr, value, token)) == NULL)
828           continue;
829         pairadd(&pairlist, newpair);
830         ldap_value_free(vals);
831       }
832     }
833   } while ((attr = ldap_next_attribute(ld, entry, berptr)) != NULL);
834
835         ber_free(berptr, 0);
836         return(pairlist);
837 }
838
839 /* globally exported name */
840 module_t rlm_ldap = {
841   "LDAP",
842   0,                            /* type: reserved */
843   NULL,                         /* initialization */
844   rlm_ldap_instantiate,         /* instantiation */
845   rlm_ldap_authorize,           /* authorization */
846   rlm_ldap_authenticate,        /* authentication */
847   NULL,                         /* preaccounting */
848   NULL,                         /* accounting */
849   rlm_ldap_detach,              /* detach */
850   NULL,                         /* destroy */
851 };