2 * rlm_ldap.c LDAP authorization and authentication module.
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>
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
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
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 * - Remember to free inst->atts in ldap_detach()
70 static const char rcsid[] = "$Id$";
74 #include <sys/types.h>
75 #include <sys/socket.h>
77 #include <netinet/in.h>
94 #include "libradius.h"
98 #include "rad_assert.h"
101 #define MAX_AUTH_QUERY_LEN 256
104 /* linked list of mappings between RADIUS attributes and LDAP attributes */
105 struct TLDAP_RADIUS {
108 struct TLDAP_RADIUS* next;
110 typedef struct TLDAP_RADIUS TLDAP_RADIUS;
112 typedef struct ldap_conn {
115 pthread_mutex_t mutex;
118 #define MAX_SERVER_LINE 1024
124 struct timeval net_timeout;
125 struct timeval timeout;
138 char *default_profile;
144 char *dictionary_mapping;
145 char *groupname_attr;
146 char *groupmemb_filt;
148 TLDAP_RADIUS *check_item_map;
149 TLDAP_RADIUS *reply_item_map;
151 int ldap_debug; /* Debug flag for LDAP SDK */
152 char *xlat_name; /* name used to xlat */
155 static CONF_PARSER module_config[] = {
156 {"server", PW_TYPE_STRING_PTR, offsetof(ldap_instance,server), NULL, NULL},
157 {"port", PW_TYPE_INTEGER, offsetof(ldap_instance,port), NULL, "389"},
158 /* wait forever on network activity */
159 {"net_timeout", PW_TYPE_INTEGER, offsetof(ldap_instance,net_timeout.tv_sec), NULL, "10"},
160 /* wait forever for search results */
161 {"timeout", PW_TYPE_INTEGER, offsetof(ldap_instance,timeout.tv_sec), NULL, "20"},
162 /* allow server unlimited time for search (server-side limit) */
163 {"timelimit", PW_TYPE_INTEGER, offsetof(ldap_instance,timelimit), NULL, "20"},
164 {"ldap_cache_timeout", PW_TYPE_INTEGER, offsetof(ldap_instance,cache_timeout), NULL, "0"},
165 {"ldap_cache_size", PW_TYPE_INTEGER, offsetof(ldap_instance,cache_size), NULL, "0"},
166 {"identity", PW_TYPE_STRING_PTR, offsetof(ldap_instance,login), NULL, ""},
167 {"start_tls", PW_TYPE_BOOLEAN, offsetof(ldap_instance,start_tls), NULL, "no"},
168 {"password", PW_TYPE_STRING_PTR, offsetof(ldap_instance,password), NULL, ""},
169 {"basedn", PW_TYPE_STRING_PTR, offsetof(ldap_instance,basedn), NULL, NULL},
170 {"filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance,filter), NULL, "(uid=%u)"},
171 {"default_profile", PW_TYPE_STRING_PTR, offsetof(ldap_instance,default_profile), NULL, NULL},
172 {"profile_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance,profile_attr), NULL, NULL},
173 {"access_group", PW_TYPE_STRING_PTR, offsetof(ldap_instance,access_group), NULL, NULL},
174 {"password_header", PW_TYPE_STRING_PTR, offsetof(ldap_instance,passwd_hdr), NULL, NULL},
175 {"password_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance,passwd_attr), NULL, NULL},
176 /* LDAP attribute name that controls remote access */
177 {"access_attr", PW_TYPE_STRING_PTR, offsetof(ldap_instance,access_attr), NULL, NULL},
178 /* file with mapping between LDAP and RADIUS attributes */
179 {"groupname_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance,groupname_attr), NULL, "cn"},
180 {"groupmembership_filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance,groupmemb_filt), NULL, "(|(&(objectClass=GroupOfNames)(member=%{Ldap-UserDn}))(&(objectClass=GroupOfUniqueNames)(uniquemember=%{Ldap-UserDn})))"},
181 {"dictionary_mapping", PW_TYPE_STRING_PTR, offsetof(ldap_instance,dictionary_mapping), NULL, "${confdir}/ldap.attrmap"},
182 {"ldap_debug", PW_TYPE_INTEGER, offsetof(ldap_instance,ldap_debug), NULL, "0x0000"},
183 {"ldap_connections_number", PW_TYPE_INTEGER, offsetof(ldap_instance,num_conns), NULL, "5"},
184 {"compare_check_items", PW_TYPE_BOOLEAN, offsetof(ldap_instance,do_comp), NULL, "no"},
185 {"access_attr_used_for_allow", PW_TYPE_BOOLEAN, offsetof(ldap_instance,default_allow), NULL, "yes"},
187 {NULL, -1, 0, NULL, NULL}
190 #define ld_valid ld_options.ldo_valid
191 #define LDAP_VALID_SESSION 0x2
192 #define LDAP_VALID(ld) ( (ld)->ld_valid == LDAP_VALID_SESSION )
195 static void fieldcpy(char *, char **);
197 static VALUE_PAIR *ldap_pairget(LDAP *, LDAPMessage *, TLDAP_RADIUS *,VALUE_PAIR **);
198 static int ldap_groupcmp(void *, REQUEST *, VALUE_PAIR *, VALUE_PAIR *, VALUE_PAIR *, VALUE_PAIR **);
199 static int ldap_xlat(void *,REQUEST *, char *, char *,int, RADIUS_ESCAPE_STRING);
200 static LDAP *ldap_connect(void *instance, const char *, const char *, int, int *);
201 static int read_mappings(ldap_instance* inst);
203 static inline int ldap_get_conn(LDAP_CONN *conns,LDAP_CONN **ret,void *instance)
205 ldap_instance *inst = instance;
208 for(;i<inst->num_conns;i++){
209 if (pthread_mutex_trylock(&(conns[i].mutex)) == 0){
211 DEBUG("ldap_get_conn: Got Id: %d",i);
219 static inline void ldap_release_conn(int i, LDAP_CONN *conns)
221 DEBUG("ldap_release_conn: Release Id: %d",i);
222 pthread_mutex_unlock(&(conns[i].mutex));
225 /*************************************************************************
227 * Function: rlm_ldap_instantiate
229 * Purpose: Uses section of radiusd config file passed as parameter
230 * to create an instance of the module.
232 *************************************************************************/
234 ldap_instantiate(CONF_SECTION * conf, void **instance)
239 int reply_map_num = 0;
240 int check_map_num = 0;
241 int att_map[3] = {0,0,0};
245 inst = rad_malloc(sizeof *inst);
247 if (cf_section_parse(conf, inst, module_config) < 0) {
252 if (inst->server == NULL) {
253 radlog(L_ERR, "rlm_ldap: missing 'server' directive.");
258 inst->timeout.tv_usec = 0;
259 inst->net_timeout.tv_usec = 0;
260 inst->tls_mode = LDAP_OPT_X_TLS_TRY;
261 inst->reply_item_map = NULL;
262 inst->check_item_map = NULL;
265 paircompare_register(PW_LDAP_GROUP, PW_USER_NAME, ldap_groupcmp, inst);
266 DEBUG("conns: %p",inst->conns);
268 xlat_name = cf_section_name2(conf);
269 if (xlat_name == NULL) {
270 xlat_name = cf_section_name1(conf);
271 rad_assert(xlat_name != NULL); /* or all hell breaks loose */
273 inst->xlat_name = strdup(xlat_name);
274 xlat_register(xlat_name,ldap_xlat,inst);
276 if (inst->num_conns <= 0){
277 radlog(L_ERR, "rlm_ldap: Invalid ldap connections number passed.");
281 inst->conns = (LDAP_CONN *)malloc(sizeof(LDAP_CONN)*inst->num_conns);
282 if (inst->conns == NULL){
283 radlog(L_ERR, "rlm_ldap: Could not allocate memory. Aborting.");
287 for(;i<inst->num_conns;i++){
288 inst->conns[i].bound = 0;
289 inst->conns[i].ld = NULL;
290 pthread_mutex_init(&inst->conns[i].mutex, NULL);
293 if (read_mappings(inst) != 0) {
294 radlog(L_ERR, "rlm_ldap: Reading dictionary mappings from file %s failed",
295 inst->dictionary_mapping);
296 radlog(L_ERR, "rlm_ldap: Proceeding with no mappings");
299 pair = inst->check_item_map;
304 check_map_num = (atts_num - 1);
305 pair = inst->reply_item_map;
310 reply_map_num = (atts_num - 1);
311 if (inst->profile_attr)
313 if (inst->passwd_attr)
315 if (inst->access_attr)
317 inst->atts = (char **)malloc(sizeof(char *)*(atts_num + 1));
318 if (inst->atts == NULL){
319 radlog(L_ERR, "rlm_ldap: Could not allocate memory. Aborting.");
323 pair = inst->check_item_map;
324 for(i=0;i<atts_num;i++){
325 if (i <= check_map_num ){
326 inst->atts[i] = pair->attr;
327 if (i == check_map_num)
328 pair = inst->reply_item_map;
332 else if (i <= reply_map_num){
333 inst->atts[i] = pair->attr;
337 if (inst->profile_attr && !att_map[0]){
338 inst->atts[i] = inst->profile_attr;
341 else if (inst->passwd_attr && !att_map[1]){
342 inst->atts[i] = inst->passwd_attr;
345 else if (inst->access_attr && !att_map[2]){
346 inst->atts[i] = inst->access_attr;
351 inst->atts[atts_num] = NULL;
353 DEBUG("conns: %p",inst->conns);
363 * read_mappings(...) reads a ldap<->radius mappings file to inst->reply_item_map and inst->check_item_map
366 #define MAX_LINE_LEN 160
367 #define GENERIC_ATTRIBUTE_ID "$GENERIC$"
370 read_mappings(ldap_instance* inst)
374 /* all buffers are of MAX_LINE_LEN so we can use sscanf without being afraid of buffer overflows */
375 char buf[MAX_LINE_LEN], itemType[MAX_LINE_LEN], radiusAttribute[MAX_LINE_LEN], ldapAttribute[MAX_LINE_LEN];
378 /* open the mappings file for reading */
380 filename = inst->dictionary_mapping;
381 DEBUG("rlm_ldap: reading ldap<->radius mappings from file %s", filename);
382 mapfile = fopen(filename, "r");
384 if (mapfile == NULL) {
385 radlog(L_ERR, "rlm_ldap: Opening file %s failed", filename);
386 return -1; /* error */
389 /* read file line by line. Note that if line length exceed MAX_LINE_LEN, line numbers will be mixed up */
393 while (fgets(buf, sizeof buf, mapfile)!=NULL) {
401 ptr = strchr(buf, '#');
405 if (buf[0] == 0) continue;
407 /* extract tokens from the string */
408 token_count = sscanf(buf, "%s %s %s", itemType, radiusAttribute, ldapAttribute);
410 if (token_count <= 0) /* no tokens */
413 if (token_count != 3) {
414 radlog(L_ERR, "rlm_ldap: Skipping %s line %i: %s", filename, linenumber, buf);
415 radlog(L_ERR, "rlm_ldap: Expected 3 tokens "
416 "(Item type, RADIUS Attribute and LDAP Attribute) but found only %i", token_count);
420 /* create new TLDAP_RADIUS list node */
421 pair = rad_malloc(sizeof(TLDAP_RADIUS));
423 pair->attr = strdup(ldapAttribute);
424 pair->radius_attr = strdup(radiusAttribute);
426 if ( (pair->attr == NULL) || (pair->radius_attr == NULL) ) {
427 radlog(L_ERR, "rlm_ldap: Out of memory");
428 if (pair->attr) free(pair->attr);
429 if (pair->radius_attr) free(pair->radius_attr);
435 /* push node to correct list */
436 if (strcasecmp(itemType, "checkItem") == 0) {
437 pair->next = inst->check_item_map;
438 inst->check_item_map = pair;
439 } else if (strcasecmp(itemType, "replyItem") == 0) {
440 pair->next = inst->reply_item_map;
441 inst->reply_item_map = pair;
443 radlog(L_ERR, "rlm_ldap: file %s: skipping line %i: unknown itemType %s",
444 filename, linenumber, itemType);
446 free(pair->radius_attr);
451 DEBUG("rlm_ldap: LDAP %s mapped to RADIUS %s",
452 pair->attr, pair->radius_attr);
457 return 0; /* success */
461 perform_search(void *instance, LDAP_CONN *conn, char *search_basedn, int scope, char *filter,
462 char **attrs, LDAPMessage ** result)
464 int res = RLM_MODULE_OK;
466 ldap_instance *inst = instance;
467 int search_retry = 0;
472 radlog(L_ERR, "rlm_ldap: NULL connection handle passed");
473 return RLM_MODULE_FAIL;
477 DEBUG2("rlm_ldap: attempting LDAP reconnection");
479 DEBUG2("rlm_ldap: closing existing LDAP connection");
480 if (inst->cache_timeout >0)
481 ldap_destroy_cache(conn->ld);
482 ldap_unbind_s(conn->ld);
484 if ((conn->ld = ldap_connect(instance, inst->login, inst->password, 0, &res)) == NULL) {
485 radlog(L_ERR, "rlm_ldap: (re)connection attempt failed");
486 return (RLM_MODULE_FAIL);
490 DEBUG2("rlm_ldap: performing search in %s, with filter %s", search_basedn ? search_basedn : "(null)" , filter);
491 switch (ldap_search_st(conn->ld, search_basedn, scope, filter, attrs, 0, &(inst->timeout), result)) {
494 case LDAP_SERVER_DOWN:
495 if (search_retry == 0){
496 DEBUG("rlm_ldap: LDAP connection lost. Attempting reconnect");
499 ldap_msgfree(*result);
503 ldap_get_option(conn->ld, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
504 radlog(L_ERR, "rlm_ldap: ldap_search() failed: %s", ldap_err2string(ldap_errno));
506 ldap_msgfree(*result);
507 return (RLM_MODULE_FAIL);
510 if ((ldap_count_entries(conn->ld, *result)) != 1) {
511 DEBUG("rlm_ldap: object not found or got ambiguous search result");
512 res = RLM_MODULE_NOTFOUND;
513 ldap_msgfree(*result);
520 * ldap_groupcmp(). Implement the Ldap-Group == "group" filter
523 static int ldap_groupcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
524 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
526 char filter[MAX_AUTH_QUERY_LEN];
529 LDAPMessage *result = NULL;
530 LDAPMessage *msg = NULL;
532 char *attrs[] = {"dn",NULL};
533 ldap_instance *inst = instance;
537 check_pairs = check_pairs;
538 reply_pairs = reply_pairs;
540 DEBUG("rlm_ldap: Entering ldap_groupcmp()");
542 if (check->strvalue == NULL || check->length == 0){
543 DEBUG("rlm_ldap::ldap_groupcmp: Illegal group name");
548 DEBUG("rlm_ldap::ldap_groupcmp: NULL request");
552 if (!radius_xlat(basedn, sizeof(basedn), inst->basedn, req, NULL)) {
553 DEBUG("rlm_ldap::ldap_groupcmp: unable to create basedn.");
557 if ((pairfind(req->packet->vps, LDAP_USERDN)) == NULL){
558 char *user_dn = NULL;
560 if (!radius_xlat(filter, MAX_AUTH_QUERY_LEN, inst->filter, req, NULL)) {
561 DEBUG("rlm_ldap::ldap_groupcmp: unable to create filter");
564 if ((conn_id = ldap_get_conn(inst->conns,&conn,inst)) == -1){
565 radlog(L_ERR, "rlm_ldap: All ldap connections are in use");
568 if ((res = perform_search(inst, conn, basedn, LDAP_SCOPE_SUBTREE, filter, attrs, &result)) != RLM_MODULE_OK) {
569 DEBUG("rlm_ldap::ldap_groupcmp: search failed");
570 ldap_release_conn(conn_id,inst->conns);
573 if ((msg = ldap_first_entry(conn->ld, result)) == NULL) {
574 DEBUG("rlm_ldap::ldap_groupcmp: ldap_first_entry() failed");
575 ldap_release_conn(conn_id,inst->conns);
576 ldap_msgfree(result);
579 if ((user_dn = ldap_get_dn(conn->ld, msg)) == NULL) {
580 DEBUG("rlm_ldap:ldap_groupcmp:: ldap_get_dn() failed");
581 ldap_release_conn(conn_id,inst->conns);
582 ldap_msgfree(result);
586 * Adding new attribute containing DN for LDAP object associated with
589 pairadd(&req->packet->vps, pairmake("Ldap-UserDn", user_dn, T_OP_EQ));
590 ldap_memfree(user_dn);
591 ldap_msgfree(result);
594 snprintf(filter,MAX_AUTH_QUERY_LEN - 1, "(%s=%s)",inst->groupname_attr,(char *)check->strvalue);
596 if (conn_id == -1 && (conn_id = ldap_get_conn(inst->conns,&conn,inst)) == -1){
597 radlog(L_ERR, "rlm_ldap: All ldap connections are in use");
601 if (inst->cache_timeout >0 && conn->ld != NULL)
602 ldap_enable_cache(conn->ld, inst->cache_timeout, inst->cache_size);
604 if ((res = perform_search(inst, conn, basedn, LDAP_SCOPE_SUBTREE, filter, attrs, &result)) != RLM_MODULE_OK){
605 if (inst->cache_timeout >0 && conn->ld != NULL)
606 ldap_disable_cache(conn->ld);
607 if (res == RLM_MODULE_NOTFOUND){
608 DEBUG("rlm_ldap::ldap_groupcmp: Group %s not found", (char *)check->strvalue);
609 ldap_release_conn(conn_id,inst->conns);
612 DEBUG("rlm_ldap::ldap_groupcmp: Search returned error");
613 ldap_release_conn(conn_id,inst->conns);
616 if ((msg = ldap_first_entry(conn->ld, result)) == NULL){
617 DEBUG("rlm_ldap::ldap_groupcmp: ldap_first_entry() failed");
618 if (inst->cache_timeout >0)
619 ldap_disable_cache(conn->ld);
620 ldap_release_conn(conn_id,inst->conns);
621 ldap_msgfree(result);
624 if ((group_dn = ldap_get_dn(conn->ld, msg)) == NULL){
625 DEBUG("rlm_ldap:ldap_groupcmp:: ldap_get_dn() failed");
626 if (inst->cache_timeout >0)
627 ldap_disable_cache(conn->ld);
628 ldap_release_conn(conn_id,inst->conns);
629 ldap_msgfree(result);
632 ldap_msgfree(result);
635 if(!radius_xlat(filter, MAX_AUTH_QUERY_LEN, inst->groupmemb_filt, req, NULL)){
636 DEBUG("rlm_ldap::ldap_groupcmp: unable to create filter.");
637 if (inst->cache_timeout >0)
638 ldap_disable_cache(conn->ld);
639 ldap_release_conn(conn_id,inst->conns);
640 ldap_memfree(group_dn);
644 if ((res = perform_search(inst, conn, group_dn, LDAP_SCOPE_BASE, filter, attrs, &result)) != RLM_MODULE_OK){
645 if (inst->cache_timeout >0 && conn->ld != NULL)
646 ldap_disable_cache(conn->ld);
647 ldap_release_conn(conn_id,inst->conns);
648 if (res == RLM_MODULE_NOTFOUND){
649 DEBUG("rlm_ldap::ldap_groupcmp: User not found in group %s",group_dn);
650 ldap_memfree(group_dn);
653 DEBUG("rlm_ldap::ldap_groupcmp: Search returned error");
654 ldap_memfree(group_dn);
658 DEBUG("rlm_ldap::ldap_groupcmp: User found in group %s",group_dn);
659 if (inst->cache_timeout >0)
660 ldap_disable_cache(conn->ld);
661 ldap_memfree(group_dn);
662 ldap_msgfree(result);
664 ldap_release_conn(conn_id,inst->conns);
672 * Do an xlat on an LDAP URL
675 static int ldap_xlat(void *instance, REQUEST *request, char *fmt, char *out, int freespace,
676 RADIUS_ESCAPE_STRING func)
678 char url[MAX_STRING_LEN];
681 ldap_instance *inst = instance;
682 LDAPURLDesc *ldap_url;
683 LDAPMessage *result = NULL;
684 LDAPMessage *msg = NULL;
689 DEBUG("rlm_ldap: - ldap_xlat");
690 if (!radius_xlat(url, sizeof(url), fmt, request, func)) {
691 radlog (L_ERR, "rlm_ldap: Unable to create LDAP URL.\n");
694 if (!ldap_is_ldap_url(url)){
695 radlog (L_ERR, "rlm_ldap: String passed does not look like an LDAP URL.\n");
698 if (ldap_url_parse(url,&ldap_url)){
699 radlog (L_ERR, "rlm_ldap: LDAP URL parse failed.\n");
702 if (ldap_url->lud_attrs == NULL || ldap_url->lud_attrs[0] == NULL || \
703 ( ldap_url->lud_attrs[1] != NULL || ( ! strlen(ldap_url->lud_attrs[0]) || \
704 ! strcmp(ldap_url->lud_attrs[0],"*") ) ) ){
705 radlog (L_ERR, "rlm_ldap: Invalid Attribute(s) request.\n");
706 ldap_free_urldesc(ldap_url);
709 if (ldap_url->lud_host){
710 if (strncmp(inst->server,ldap_url->lud_host,strlen(inst->server)) != 0 || \
711 ldap_url->lud_port != inst->port){
712 DEBUG("rlm_ldap: Requested server/port is not known to this module instance.");
713 ldap_free_urldesc(ldap_url);
717 if ((conn_id = ldap_get_conn(inst->conns,&conn,inst)) == -1){
718 radlog(L_ERR, "rlm_ldap: All ldap connections are in use");
721 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){
722 if (res == RLM_MODULE_NOTFOUND){
723 DEBUG("rlm_ldap: Search returned not found");
724 ldap_free_urldesc(ldap_url);
725 ldap_release_conn(conn_id,inst->conns);
728 DEBUG("rlm_ldap: Search returned error");
729 ldap_free_urldesc(ldap_url);
730 ldap_release_conn(conn_id,inst->conns);
733 if ((msg = ldap_first_entry(conn->ld, result)) == NULL){
734 DEBUG("rlm_ldap: ldap_first_entry() failed");
735 ldap_msgfree(result);
736 ldap_free_urldesc(ldap_url);
737 ldap_release_conn(conn_id,inst->conns);
740 if ((vals = ldap_get_values(conn->ld, msg, ldap_url->lud_attrs[0])) != NULL) {
741 ret = strlen(vals[0]);
742 if (ret > freespace){
743 DEBUG("rlm_ldap: Insufficient string space");
744 ldap_free_urldesc(ldap_url);
745 ldap_value_free(vals);
746 ldap_msgfree(result);
747 ldap_release_conn(conn_id,inst->conns);
750 DEBUG("rlm_ldap: Adding attribute %s, value: %s",ldap_url->lud_attrs[0],vals[0]);
751 strncpy(out,vals[0],ret);
752 ldap_value_free(vals);
757 ldap_msgfree(result);
758 ldap_free_urldesc(ldap_url);
759 ldap_release_conn(conn_id,inst->conns);
761 DEBUG("rlm_ldap: - ldap_xlat end");
767 /******************************************************************************
769 * Function: rlm_ldap_authorize
771 * Purpose: Check if user is authorized for remote access
773 ******************************************************************************/
775 ldap_authorize(void *instance, REQUEST * request)
777 LDAPMessage *result = NULL;
778 LDAPMessage *msg = NULL;
779 LDAPMessage *def_msg = NULL;
780 LDAPMessage *def_attr_msg = NULL;
781 LDAPMessage *gr_result = NULL;
782 LDAPMessage *def_result = NULL;
783 LDAPMessage *def_attr_result = NULL;
784 ldap_instance *inst = instance;
785 char *user_dn = NULL;
786 char filter[MAX_AUTH_QUERY_LEN];
788 VALUE_PAIR *check_tmp;
789 VALUE_PAIR *reply_tmp;
791 VALUE_PAIR **check_pairs, **reply_pairs;
793 VALUE_PAIR *module_fmsg_vp;
794 VALUE_PAIR *user_profile;
795 char module_fmsg[MAX_STRING_LEN];
799 DEBUG("rlm_ldap: - authorize");
801 if (!request->username){
802 radlog(L_AUTH, "rlm_ldap: Attribute \"User-Name\" is required for authentication.\n");
803 return RLM_MODULE_INVALID;
806 check_pairs = &request->config_items;
807 reply_pairs = &request->reply->vps;
810 * Check for valid input, zero length names not permitted
812 if (request->username->strvalue == 0) {
813 radlog(L_ERR, "rlm_ldap: zero length username not permitted\n");
814 return RLM_MODULE_INVALID;
816 DEBUG("rlm_ldap: performing user authorization for %s",
817 request->username->strvalue);
819 if (!radius_xlat(filter, sizeof(filter), inst->filter,
821 radlog (L_ERR, "rlm_ldap: unable to create filter.\n");
822 return RLM_MODULE_INVALID;
825 if (!radius_xlat(basedn, sizeof(basedn), inst->basedn,
827 radlog (L_ERR, "rlm_ldap: unable to create basedn.\n");
828 return RLM_MODULE_INVALID;
831 if ((conn_id = ldap_get_conn(inst->conns,&conn,inst)) == -1){
832 radlog(L_ERR, "rlm_ldap: All ldap connections are in use");
833 return RLM_MODULE_FAIL;
835 if ((res = perform_search(instance, conn, basedn, LDAP_SCOPE_SUBTREE, filter, inst->atts, &result)) != RLM_MODULE_OK) {
836 DEBUG("rlm_ldap: search failed");
837 if (res == RLM_MODULE_NOTFOUND){
838 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: User not found");
839 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
840 pairadd(&request->packet->vps, module_fmsg_vp);
842 ldap_release_conn(conn_id,inst->conns);
845 if ((msg = ldap_first_entry(conn->ld, result)) == NULL) {
846 DEBUG("rlm_ldap: ldap_first_entry() failed");
847 ldap_msgfree(result);
848 ldap_release_conn(conn_id,inst->conns);
849 return RLM_MODULE_FAIL;
851 if ((user_dn = ldap_get_dn(conn->ld, msg)) == NULL) {
852 DEBUG("rlm_ldap: ldap_get_dn() failed");
853 ldap_msgfree(result);
854 ldap_release_conn(conn_id,inst->conns);
855 return RLM_MODULE_FAIL;
858 * Adding new attribute containing DN for LDAP object associated with
861 pairadd(&request->packet->vps, pairmake("Ldap-UserDn", user_dn, T_OP_EQ));
862 ldap_memfree(user_dn);
865 /* Remote access is controled by attribute of the user object */
866 if (inst->access_attr) {
867 if ((vals = ldap_get_values(conn->ld, msg, inst->access_attr)) != NULL) {
868 if (inst->default_allow){
869 DEBUG("rlm_ldap: checking if remote access for %s is allowed by %s", request->username->strvalue, inst->access_attr);
870 if (!strncmp(vals[0], "FALSE", 5)) {
871 DEBUG("rlm_ldap: dialup access disabled");
872 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: Access Attribute denies access");
873 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
874 pairadd(&request->packet->vps, module_fmsg_vp);
875 ldap_msgfree(result);
876 ldap_value_free(vals);
877 ldap_release_conn(conn_id,inst->conns);
878 return RLM_MODULE_USERLOCK;
880 ldap_value_free(vals);
883 DEBUG("rlm_ldap: %s attribute exists - access denied by default", inst->access_attr);
884 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: Access Attribute denies access");
885 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
886 pairadd(&request->packet->vps, module_fmsg_vp);
887 ldap_msgfree(result);
888 ldap_value_free(vals);
889 ldap_release_conn(conn_id,inst->conns);
890 return RLM_MODULE_USERLOCK;
893 if (inst->default_allow){
894 DEBUG("rlm_ldap: no %s attribute - access denied by default", inst->access_attr);
895 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: Access Attribute denies access");
896 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
897 pairadd(&request->packet->vps, module_fmsg_vp);
898 ldap_msgfree(result);
899 ldap_release_conn(conn_id,inst->conns);
900 return RLM_MODULE_USERLOCK;
904 if (inst->cache_timeout >0)
905 ldap_enable_cache(conn->ld, inst->cache_timeout, inst->cache_size);
907 /* Remote access controled by group membership of the user object */
908 if (inst->access_group != NULL) {
909 static char group[MAX_AUTH_QUERY_LEN];
910 static char *attrs[] = {"dn", NULL};
912 DEBUG("rlm_ldap: checking user membership in dialup-enabling group %s", inst->access_group);
914 * uniquemember appears in Netscape Directory Server's groups
915 * since we have objectclass groupOfNames and
918 if(!radius_xlat(group, MAX_AUTH_QUERY_LEN, inst->access_group,
920 radlog (L_ERR, "rlm_ldap: unable to munge group.\n");
922 if(!radius_xlat(filter, MAX_AUTH_QUERY_LEN, inst->groupmemb_filt,
924 radlog (L_ERR, "rlm_ldap: unable to create filter.\n");
926 if ((res = perform_search(instance, conn, group, LDAP_SCOPE_BASE, filter, attrs, &gr_result)) != RLM_MODULE_OK) {
927 if (inst->cache_timeout >0 && conn->ld != NULL)
928 ldap_disable_cache(conn->ld);
929 ldap_msgfree(result);
930 ldap_release_conn(conn_id,inst->conns);
931 if (res == RLM_MODULE_NOTFOUND){
932 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: User is not an access group member");
933 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
934 pairadd(&request->packet->vps, module_fmsg_vp);
935 return (RLM_MODULE_USERLOCK);
940 ldap_msgfree(gr_result);
944 * Check for the default profile entry. If it exists then add the
945 * attributes it contains in the check and reply pairs
948 user_profile = pairfind(request->config_items, PW_USER_PROFILE);
949 if (inst->default_profile || user_profile){
950 char *profile = inst->default_profile;
952 strncpy(filter,"(objectclass=radiusprofile)",MAX_AUTH_QUERY_LEN);
954 profile = user_profile->strvalue;
955 if (profile && strlen(profile)){
956 if ((res = perform_search(instance, conn,
957 profile, LDAP_SCOPE_BASE,
958 filter, inst->atts, &def_result)) == RLM_MODULE_OK){
959 if ((def_msg = ldap_first_entry(conn->ld,def_result))){
960 if ((check_tmp = ldap_pairget(conn->ld,def_msg,inst->check_item_map,check_pairs)))
961 pairadd(check_pairs,check_tmp);
962 if ((reply_tmp = ldap_pairget(conn->ld,def_msg,inst->reply_item_map,reply_pairs)))
963 pairadd(reply_pairs,reply_tmp);
965 ldap_msgfree(def_result);
967 DEBUG("rlm_ldap: default_profile/user-profile search failed");
972 * Check for the profile attribute. If it exists, we assume that it
973 * contains the DN of an entry containg a profile for the user. That
974 * way we can have different general profiles for various user groups
975 * (students,faculty,staff etc)
978 if (inst->profile_attr){
979 if ((vals = ldap_get_values(conn->ld, msg, inst->profile_attr)) != NULL && strlen(vals[0])) {
980 strncpy(filter,"(objectclass=radiusprofile)",MAX_AUTH_QUERY_LEN);
981 if ((res = perform_search(instance, conn,
982 vals[0], LDAP_SCOPE_BASE,
983 filter, inst->atts, &def_attr_result)) == RLM_MODULE_OK){
984 if ((def_attr_msg = ldap_first_entry(conn->ld,def_attr_result))){
985 if ((check_tmp = ldap_pairget(conn->ld,def_attr_msg,inst->check_item_map,check_pairs)))
986 pairadd(check_pairs,check_tmp);
987 if ((reply_tmp = ldap_pairget(conn->ld,def_attr_msg,inst->reply_item_map,reply_pairs)))
988 pairadd(reply_pairs,reply_tmp);
990 ldap_msgfree(def_attr_result);
992 DEBUG("rlm_ldap: profile_attribute search failed");
993 ldap_value_free(vals);
996 if (inst->cache_timeout >0)
997 ldap_disable_cache(conn->ld);
998 if (inst->passwd_attr && strlen(inst->passwd_attr)){
999 VALUE_PAIR *passwd_item;
1001 if ((passwd_item = pairfind(request->config_items, PW_PASSWORD)) == NULL){
1003 char *passwd_val = NULL;
1006 if ((passwd_vals = ldap_get_values(conn->ld,msg,inst->passwd_attr)) != NULL){
1008 while(passwd_vals[i] != NULL){
1009 if (strlen(passwd_vals[i])){
1010 passwd_val = passwd_vals[i];
1012 if (inst->passwd_hdr && strlen(inst->passwd_hdr)){
1013 passwd_val = strstr(passwd_val,inst->passwd_hdr);
1014 if (passwd_val != NULL)
1015 passwd_val += strlen(inst->passwd_hdr);
1017 DEBUG("rlm_ldap: Password header not found in password %s for user %s", passwd_vals[0],request->username->strvalue);
1020 if ((passwd_item = paircreate(PW_PASSWORD,PW_TYPE_STRING)) == NULL){
1021 radlog(L_ERR|L_CONS, "no memory");
1022 ldap_value_free(passwd_vals);
1023 ldap_msgfree(result);
1024 ldap_release_conn(conn_id,inst->conns);
1025 return RLM_MODULE_FAIL;
1027 passwd_len = strlen(passwd_val);
1028 strncpy(passwd_item->strvalue,passwd_val,MAX_STRING_LEN - 1);
1029 passwd_item->length = (passwd_len > (MAX_STRING_LEN - 1)) ? (MAX_STRING_LEN - 1) : passwd_len;
1030 pairadd(&request->config_items,passwd_item);
1031 DEBUG("rlm_ldap: Added password %s in check items",passwd_item->strvalue);
1036 ldap_value_free(passwd_vals);
1043 DEBUG("rlm_ldap: looking for check items in directory...");
1045 if ((check_tmp = ldap_pairget(conn->ld, msg, inst->check_item_map,check_pairs)) != NULL)
1046 pairadd(check_pairs, check_tmp);
1049 DEBUG("rlm_ldap: looking for reply items in directory...");
1052 if ((reply_tmp = ldap_pairget(conn->ld, msg, inst->reply_item_map,reply_pairs)) != NULL)
1053 pairadd(reply_pairs, reply_tmp);
1055 if (inst->do_comp && paircmp(request,request->packet->vps,*check_pairs,reply_pairs) != 0){
1056 DEBUG("rlm_ldap: Pairs do not match. Rejecting user.");
1057 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: Pairs do not match");
1058 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
1059 pairadd(&request->packet->vps, module_fmsg_vp);
1061 return RLM_MODULE_REJECT;
1065 * Module should default to LDAP authentication if no Auth-Type
1068 if (pairfind(*check_pairs, PW_AUTHTYPE) == NULL)
1069 pairadd(check_pairs, pairmake("Auth-Type", "LDAP", T_OP_EQ));
1072 DEBUG("rlm_ldap: user %s authorized to use remote access",
1073 request->username->strvalue);
1074 ldap_msgfree(result);
1075 ldap_release_conn(conn_id,inst->conns);
1077 return RLM_MODULE_OK;
1080 /*****************************************************************************
1082 * Function: rlm_ldap_authenticate
1084 * Purpose: Check the user's password against ldap database
1086 *****************************************************************************/
1088 ldap_authenticate(void *instance, REQUEST * request)
1091 LDAPMessage *result, *msg;
1092 ldap_instance *inst = instance;
1093 char *user_dn, *attrs[] = {"uid", NULL};
1094 char filter[MAX_AUTH_QUERY_LEN];
1097 VALUE_PAIR *vp_user_dn;
1098 VALUE_PAIR *module_fmsg_vp;
1099 char module_fmsg[MAX_STRING_LEN];
1103 DEBUG("rlm_ldap: - authenticate");
1105 * Ensure that we're being passed a plain-text password, and not
1109 if (!request->username) {
1110 radlog(L_AUTH, "rlm_ldap: Attribute \"User-Name\" is required for authentication.\n");
1111 return RLM_MODULE_INVALID;
1114 if (!request->password){
1115 radlog(L_AUTH, "rlm_ldap: Attribute \"User-Password\" is required for authentication.");
1116 return RLM_MODULE_INVALID;
1119 if(request->password->attribute != PW_PASSWORD) {
1120 radlog(L_AUTH, "rlm_ldap: Attribute \"User-Password\" is required for authentication. Cannot use \"%s\".", request->password->name);
1121 return RLM_MODULE_INVALID;
1124 if (request->password->length == 0) {
1125 radlog(L_ERR, "rlm_ldap: empty password supplied");
1126 return RLM_MODULE_INVALID;
1129 DEBUG("rlm_ldap: login attempt by \"%s\" with password \"%s\"",
1130 request->username->strvalue, request->password->strvalue);
1132 while((vp_user_dn = pairfind(request->packet->vps, LDAP_USERDN)) == NULL) {
1133 if (!radius_xlat(filter, sizeof(filter), inst->filter,
1135 radlog (L_ERR, "rlm_ldap: unable to create filter.\n");
1136 return RLM_MODULE_INVALID;
1139 if (!radius_xlat(basedn, sizeof(basedn), inst->basedn,
1141 radlog (L_ERR, "rlm_ldap: unable to create basedn.\n");
1142 return RLM_MODULE_INVALID;
1145 if ((conn_id = ldap_get_conn(inst->conns,&conn,inst)) == -1){
1146 radlog(L_ERR, "rlm_ldap: All ldap connections are in use");
1147 return RLM_MODULE_FAIL;
1149 if ((res = perform_search(instance, conn, basedn, LDAP_SCOPE_SUBTREE, filter, attrs, &result)) != RLM_MODULE_OK) {
1150 if (res == RLM_MODULE_NOTFOUND){
1151 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: User not found");
1152 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
1153 pairadd(&request->packet->vps, module_fmsg_vp);
1155 ldap_release_conn(conn_id,inst->conns);
1158 if ((msg = ldap_first_entry(conn->ld, result)) == NULL) {
1159 ldap_msgfree(result);
1160 ldap_release_conn(conn_id,inst->conns);
1161 return RLM_MODULE_FAIL;
1163 if ((user_dn = ldap_get_dn(conn->ld, msg)) == NULL) {
1164 DEBUG("rlm_ldap: ldap_get_dn() failed");
1165 ldap_msgfree(result);
1166 ldap_release_conn(conn_id,inst->conns);
1167 return RLM_MODULE_FAIL;
1169 ldap_release_conn(conn_id,inst->conns);
1170 pairadd(&request->packet->vps, pairmake("Ldap-UserDn", user_dn, T_OP_EQ));
1171 ldap_memfree(user_dn);
1172 ldap_msgfree(result);
1175 user_dn = vp_user_dn->strvalue;
1177 DEBUG("rlm_ldap: user DN: %s", user_dn);
1179 ld_user = ldap_connect(instance, user_dn, request->password->strvalue,
1181 if (ld_user == NULL){
1182 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_ldap: Bind as user failed");
1183 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
1184 pairadd(&request->packet->vps, module_fmsg_vp);
1188 DEBUG("rlm_ldap: user %s authenticated succesfully",
1189 request->username->strvalue);
1190 ldap_unbind_s(ld_user);
1192 return RLM_MODULE_OK;
1196 ldap_connect(void *instance, const char *dn, const char *password, int auth, int *result)
1198 ldap_instance *inst = instance;
1200 int msgid, rc, ldap_version;
1204 DEBUG("rlm_ldap: (re)connect to %s:%d, authentication %d", inst->server, inst->port, auth);
1205 if ((ld = ldap_init(inst->server, inst->port)) == NULL) {
1206 radlog(L_ERR, "rlm_ldap: ldap_init() failed");
1207 *result = RLM_MODULE_FAIL;
1210 if (ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, (void *) &(inst->net_timeout)) != LDAP_OPT_SUCCESS) {
1211 radlog(L_ERR, "rlm_ldap: Could not set LDAP_OPT_NETWORK_TIMEOUT %ld.%ld", inst->net_timeout.tv_sec, inst->net_timeout.tv_usec);
1213 if (ldap_set_option(ld, LDAP_OPT_TIMELIMIT, (void *) &(inst->timelimit)) != LDAP_OPT_SUCCESS) {
1214 radlog(L_ERR, "rlm_ldap: Could not set LDAP_OPT_TIMELIMIT %d", inst->timelimit);
1216 if (inst->ldap_debug && ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &(inst->ldap_debug)) != LDAP_OPT_SUCCESS) {
1217 radlog(L_ERR, "rlm_ldap: Could not set LDAP_OPT_DEBUG_LEVEL %d", inst->ldap_debug);
1220 if (inst->tls_mode && ldap_set_option(ld, LDAP_OPT_X_TLS, (void *) &(inst->tls_mode)) != LDAP_OPT_SUCCESS) {
1221 radlog(L_ERR, "rlm_ldap: Could not set LDAP_OPT_X_TLS_TRY");
1225 #ifdef HAVE_LDAP_START_TLS
1226 if (inst->start_tls) {
1227 DEBUG("rlm_ldap: try to start TLS");
1228 ldap_version = LDAP_VERSION3;
1229 if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldap_version) == LDAP_SUCCESS) {
1230 rc = ldap_start_tls_s(ld, NULL, NULL);
1231 if (rc != LDAP_SUCCESS) {
1232 DEBUG("rlm_ldap: ldap_start_tls_s()");
1233 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
1234 radlog(L_ERR, "rlm_ldap: could not start TLS %s", ldap_err2string(ldap_errno));
1235 *result = RLM_MODULE_FAIL;
1241 #endif /* HAVE_LDAP_START_TLS */
1243 DEBUG("rlm_ldap: bind as %s/%s", dn, password);
1244 msgid = ldap_simple_bind(ld, dn, password);
1246 DEBUG("rlm_ldap: ldap_simple_bind()");
1247 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
1248 radlog(L_ERR, "rlm_ldap: %s bind failed: %s", dn, ldap_err2string(ldap_errno));
1249 *result = RLM_MODULE_FAIL;
1253 DEBUG("rlm_ldap: waiting for bind result ...");
1255 rc = ldap_result(ld, msgid, 1, &(inst->timeout), &res);
1258 DEBUG("rlm_ldap: ldap_result()");
1259 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
1260 radlog(L_ERR, "rlm_ldap: %s bind failed: %s", dn, (rc == 0) ? "timeout" : ldap_err2string(ldap_errno));
1261 *result = RLM_MODULE_FAIL;
1265 ldap_errno = ldap_result2error(ld, res, 1);
1266 switch (ldap_errno) {
1268 *result = RLM_MODULE_OK;
1271 case LDAP_INVALID_CREDENTIALS:
1273 *result = RLM_MODULE_REJECT;
1275 radlog(L_ERR, "rlm_ldap: LDAP login failed: check login, password settings in ldap section of radiusd.conf");
1276 *result = RLM_MODULE_FAIL;
1281 radlog(L_ERR,"rlm_ldap: %s bind failed %s", dn, ldap_err2string(ldap_errno));
1282 *result = RLM_MODULE_FAIL;
1285 if (*result != RLM_MODULE_OK) {
1292 /*****************************************************************************
1294 * Detach from the LDAP server and cleanup internal state.
1296 *****************************************************************************/
1298 ldap_detach(void *instance)
1300 ldap_instance *inst = instance;
1301 TLDAP_RADIUS *pair, *nextpair;
1304 free((char *) inst->server);
1306 free((char *) inst->login);
1308 free((char *) inst->password);
1310 free((char *) inst->basedn);
1311 if (inst->access_group)
1312 free((char *) inst->access_group);
1313 if (inst->dictionary_mapping)
1314 free(inst->dictionary_mapping);
1316 free((char *) inst->filter);
1317 if (inst->passwd_hdr)
1318 free((char *) inst->passwd_hdr);
1319 if (inst->passwd_attr)
1320 free((char *) inst->passwd_attr);
1321 if (inst->groupname_attr)
1322 free((char *) inst->groupname_attr);
1323 if (inst->groupmemb_filt)
1324 free((char *) inst->groupmemb_filt);
1328 for(;i<inst->num_conns;i++){
1329 if (inst->conns[i].ld){
1330 if (inst->cache_timeout >0)
1331 ldap_destroy_cache(inst->conns[i].ld);
1332 ldap_unbind_s(inst->conns[i].ld);
1334 pthread_mutex_destroy(&inst->conns[i].mutex);
1339 pair = inst->check_item_map;
1341 while (pair != NULL) {
1342 nextpair = pair->next;
1344 free(pair->radius_attr);
1349 pair = inst->reply_item_map;
1351 while (pair != NULL) {
1352 nextpair = pair->next;
1354 free(pair->radius_attr);
1362 while(inst->atts[i])
1363 free(inst->atts[i++]);
1367 paircompare_unregister(PW_LDAP_GROUP, ldap_groupcmp);
1368 xlat_unregister(inst->xlat_name,ldap_xlat);
1369 free(inst->xlat_name);
1378 fieldcpy(char *string, char **uptr)
1383 while (*ptr == ' ' || *ptr == '\t') {
1388 while (*ptr != '"' && *ptr != '\0' && *ptr != '\n') {
1398 while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0' && *ptr != '\n' &&
1399 *ptr != '=' && *ptr != ',') {
1407 /*****************************************************************************
1408 * Get RADIUS attributes from LDAP object
1409 * ( according to draft-adoba-radius-05.txt
1410 * <http://www.ietf.org/internet-drafts/draft-adoba-radius-05.txt> )
1412 *****************************************************************************/
1415 ldap_pairget(LDAP * ld, LDAPMessage * entry,
1416 TLDAP_RADIUS * item_map, VALUE_PAIR **pairs)
1422 TLDAP_RADIUS *element;
1424 int is_generic_attribute;
1426 VALUE_PAIR *pairlist = NULL;
1427 VALUE_PAIR *newpair = NULL;
1429 /* check if there is a mapping from this LDAP attribute to a RADIUS attribute */
1430 for (element = item_map; element != NULL; element = element->next) {
1431 if ((vals = ldap_get_values(ld,entry,element->attr)) != NULL){
1432 /* check whether this is a one-to-one-mapped ldap attribute or a generic
1433 attribute and set flag accordingly */
1435 if (strcasecmp(element->radius_attr, GENERIC_ATTRIBUTE_ID)==0)
1436 is_generic_attribute = 1;
1438 is_generic_attribute = 0;
1440 /* find out how many values there are for the attribute and extract all of them */
1442 vals_count = ldap_count_values(vals);
1444 for (vals_idx = 0; vals_idx < vals_count; vals_idx++) {
1445 ptr = vals[vals_idx];
1447 if (is_generic_attribute) {
1448 /* this is a generic attribute */
1449 LRAD_TOKEN dummy; /* makes pairread happy */
1451 /* not sure if using pairread here is ok ... */
1452 if ( (newpair = pairread(&ptr, &dummy)) != NULL) {
1453 DEBUG("rlm_ldap: extracted attribute %s from generic item %s",
1454 newpair->name, vals[vals_idx]);
1456 pairdelete(pairs,newpair->attribute);
1458 pairadd(&pairlist, newpair);
1460 radlog(L_ERR, "rlm_ldap: parsing %s failed: %s",
1461 element->attr, vals[vals_idx]);
1464 /* this is a one-to-one-mapped attribute */
1465 token = gettoken(&ptr, value, sizeof(value));
1466 if (token < T_EQSTART || token > T_EQEND) {
1469 gettoken(&ptr, value, sizeof(value));
1471 if (value[0] == 0) {
1472 DEBUG("rlm_ldap: Attribute %s has no value", element->attr);
1475 DEBUG("rlm_ldap: Adding %s as %s, value %s & op=%d", element->attr, element->radius_attr, value, token);
1476 if ((newpair = pairmake(element->radius_attr, value, token)) == NULL)
1479 pairdelete(pairs,newpair->attribute);
1481 pairadd(&pairlist, newpair);
1484 ldap_value_free(vals);
1491 /* globally exported name */
1492 module_t rlm_ldap = {
1494 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1495 NULL, /* initialization */
1496 ldap_instantiate, /* instantiation */
1498 ldap_authenticate, /* authentication */
1499 ldap_authorize, /* authorization */
1500 NULL, /* preaccounting */
1501 NULL, /* accounting */
1502 NULL /* checksimul */
1504 ldap_detach, /* detach */