diff --git a/raddb/modules/ldap b/raddb/modules/ldap index 5351a81..f8a64dc 100644 --- a/raddb/modules/ldap +++ b/raddb/modules/ldap @@ -123,11 +123,16 @@ ldap { edir_account_policy_check = no # - # Group membership checking. Disabled by default. + # Group membership checking. Disabled by default. + # + # A user may be a member of at most 1 group, use + # groupmembership_limit to increase this value, or 0 + # for no limit. # # groupname_attribute = cn # groupmembership_filter = "(|(&(objectClass=GroupOfNames)(member=%{control:Ldap-UserDn}))(&(objectClass=GroupOfUniqueNames)(uniquemember=%{control:Ldap-UserDn})))" # groupmembership_attribute = radiusGroupName + # groupmembership_limit = 1 # compare_check_items = yes # do_xlat = yes diff --git a/src/modules/rlm_ldap/rlm_ldap.c b/src/modules/rlm_ldap/rlm_ldap.c index e91f02e..53c213f 100644 --- a/src/modules/rlm_ldap/rlm_ldap.c +++ b/src/modules/rlm_ldap/rlm_ldap.c @@ -152,6 +152,7 @@ typedef struct { char *dictionary_mapping; char *groupname_attr; char *groupmemb_filt; + int groupmemb_limit; char *groupmemb_attr; char **atts; TLDAP_RADIUS *check_item_map; @@ -319,6 +320,8 @@ static const CONF_PARSER module_config[] = { offsetof(ldap_instance,groupmemb_filt), NULL, "(|(&(objectClass=GroupOfNames)(member=%{Ldap-UserDn}))(&(objectClass=GroupOfUniqueNames)(uniquemember=%{Ldap-UserDn})))"}, {"groupmembership_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance,groupmemb_attr), NULL, NULL}, + {"groupmembership_limit", PW_TYPE_INTEGER, + offsetof(ldap_instance,groupmemb_limit), NULL, "1"}, /* file with mapping between LDAP and RADIUS attributes */ {"dictionary_mapping", PW_TYPE_FILENAME, @@ -827,7 +830,7 @@ read_mappings(ldap_instance* inst) static int perform_search(void *instance, LDAP_CONN *conn, char *search_basedn, int scope, char *filter, - char **attrs, LDAPMessage ** result) + char **attrs, int limit, LDAPMessage ** result) { int res = RLM_MODULE_OK; int ldap_errno = 0; @@ -923,11 +926,11 @@ retry: } ldap_errno = ldap_count_entries(conn->ld, *result); - if (ldap_errno != 1) { + if (ldap_errno < 1 || (limit && ldap_errno > limit)) { if (ldap_errno == 0) { DEBUG(" [%s] object not found", inst->xlat_name); - } else { - DEBUG(" [%s] got ambiguous search result (%d results)", inst->xlat_name, ldap_errno); + } else if (limit && ldap_errno > limit) { + DEBUG(" [%s] got ambiguous search result (%d results exceeds %d)", inst->xlat_name, ldap_errno, limit); } res = RLM_MODULE_NOTFOUND; ldap_msgfree(*result); @@ -1041,7 +1044,7 @@ static int ldap_groupcmp(void *instance, REQUEST *req, return 1; } if ((res = perform_search(inst, conn, basedn, LDAP_SCOPE_SUBTREE, - filter, attrs, &result)) != RLM_MODULE_OK){ + filter, attrs, 1, &result)) != RLM_MODULE_OK){ DEBUG("rlm_ldap::ldap_groupcmp: search failed"); ldap_release_conn(conn_id,inst); return 1; @@ -1091,7 +1094,8 @@ static int ldap_groupcmp(void *instance, REQUEST *req, } if ((res = perform_search(inst, conn, basedn, LDAP_SCOPE_SUBTREE, - filter, attrs, &result)) == RLM_MODULE_OK) { + filter, attrs, inst->groupmemb_limit, + &result)) == RLM_MODULE_OK) { DEBUG("rlm_ldap::ldap_groupcmp: User found in group %s", (char *)check->vp_strvalue); ldap_msgfree(result); @@ -1122,7 +1126,7 @@ static int ldap_groupcmp(void *instance, REQUEST *req, return 1; } if ((res = perform_search(inst, conn, vp_user_dn->vp_strvalue, - LDAP_SCOPE_BASE, filter, group_attrs, + LDAP_SCOPE_BASE, filter, group_attrs, 1, &result)) != RLM_MODULE_OK) { DEBUG("rlm_ldap::ldap_groupcmp: Search returned error"); ldap_release_conn(conn_id, inst); @@ -1149,7 +1153,7 @@ static int ldap_groupcmp(void *instance, REQUEST *req, (char *)check->vp_strvalue); if ((res = perform_search(inst, conn, vals[i], LDAP_SCOPE_BASE, filter, - attrs, &gr_result)) != RLM_MODULE_OK){ + attrs, 1, &gr_result)) != RLM_MODULE_OK){ if (res != RLM_MODULE_NOTFOUND) { DEBUG("rlm_ldap::ldap_groupcmp: Search returned error"); ldap_value_free(vals); @@ -1243,7 +1247,8 @@ static size_t ldap_xlat(void *instance, REQUEST *request, char *fmt, ldap_free_urldesc(ldap_url); return 0; } - 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){ + if ((res = perform_search(inst, conn, ldap_url->lud_dn, ldap_url->lud_scope, + ldap_url->lud_filter, ldap_url->lud_attrs, 1, &result)) != RLM_MODULE_OK){ if (res == RLM_MODULE_NOTFOUND){ DEBUG(" [%s] Search returned not found", inst->xlat_name); ldap_free_urldesc(ldap_url); @@ -1371,7 +1376,7 @@ static int ldap_authorize(void *instance, REQUEST * request) radlog(L_ERR, " [%s] All ldap connections are in use", inst->xlat_name); return RLM_MODULE_FAIL; } - if ((res = perform_search(instance, conn, basedn, LDAP_SCOPE_SUBTREE, filter, inst->atts, &result)) != RLM_MODULE_OK) { + if ((res = perform_search(instance, conn, basedn, LDAP_SCOPE_SUBTREE, filter, inst->atts, 1, &result)) != RLM_MODULE_OK) { RDEBUG("search failed"); if (res == RLM_MODULE_NOTFOUND){ snprintf(module_fmsg,sizeof(module_fmsg)," [%s] User not found", inst->xlat_name); @@ -1401,7 +1406,7 @@ static int ldap_authorize(void *instance, REQUEST * request) ldap_memfree(user_dn); - /* Remote access is controled by attribute of the user object */ + /* Remote access is controlled by attribute of the user object */ if (inst->access_attr) { if ((vals = ldap_get_values(conn->ld, msg, inst->access_attr)) != NULL) { if (inst->default_allow){ @@ -1456,7 +1461,7 @@ static int ldap_authorize(void *instance, REQUEST * request) if (profile && *profile){ if ((res = perform_search(instance, conn, profile, LDAP_SCOPE_BASE, - filter, inst->atts, &def_result)) == RLM_MODULE_OK){ + filter, inst->atts, 1, &def_result)) == RLM_MODULE_OK){ if ((def_msg = ldap_first_entry(conn->ld,def_result))){ if ((check_tmp = ldap_pairget(conn->ld,def_msg,inst->check_item_map,check_pairs,1, inst))) { if (inst->do_xlat){ @@ -1483,7 +1488,7 @@ static int ldap_authorize(void *instance, REQUEST * request) /* * Check for the profile attribute. If it exists, we assume that it - * contains the DN of an entry containg a profile for the user. That + * contains the DN of an entry containing a profile for the user. That * way we can have different general profiles for various user groups * (students,faculty,staff etc) */ @@ -1495,7 +1500,7 @@ static int ldap_authorize(void *instance, REQUEST * request) while(vals[i] && *vals[i]){ if ((res = perform_search(instance, conn, vals[i], LDAP_SCOPE_BASE, - filter, inst->atts, &def_attr_result)) == RLM_MODULE_OK){ + filter, inst->atts, 1, &def_attr_result)) == RLM_MODULE_OK){ if ((def_attr_msg = ldap_first_entry(conn->ld,def_attr_result))){ if ((check_tmp = ldap_pairget(conn->ld,def_attr_msg,inst->check_item_map,check_pairs,1, inst))) { if (inst->do_xlat){ @@ -1886,7 +1891,7 @@ static int ldap_authenticate(void *instance, REQUEST * request) radlog(L_ERR, " [%s] All ldap connections are in use", inst->xlat_name); return RLM_MODULE_FAIL; } - if ((res = perform_search(instance, conn, basedn, LDAP_SCOPE_SUBTREE, filter, attrs, &result)) != RLM_MODULE_OK) { + if ((res = perform_search(instance, conn, basedn, LDAP_SCOPE_SUBTREE, filter, attrs, 1, &result)) != RLM_MODULE_OK) { if (res == RLM_MODULE_NOTFOUND){ snprintf(module_fmsg,sizeof(module_fmsg)," [%s] User not found", inst->xlat_name); module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);