/** Query the LDAP directory to check if a user object is a member of a group * * @param[in] inst rlm_ldap configuration. * @param[in] request Current request. * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. * @param[in] dn of user object. * @param[in] check vp containing the group value (name or dn). * @return One of the RLM_MODULE_* values. */ rlm_rcode_t rlm_ldap_check_userobj_dynamic(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn, VALUE_PAIR *check) { rlm_rcode_t rcode = RLM_MODULE_NOTFOUND, ret; ldap_rcode_t status; int name_is_dn = false, value_is_dn = false; LDAPMessage *result = NULL; LDAPMessage *entry = NULL; char **vals = NULL; char const *name = check->vp_strvalue; char const *attrs[] = { inst->userobj_membership_attr, NULL }; int i, count, ldap_errno; RDEBUG2("Checking user object membership (%s) attributes", inst->userobj_membership_attr); status = rlm_ldap_search(inst, request, pconn, dn, LDAP_SCOPE_BASE, NULL, attrs, &result); switch (status) { case LDAP_PROC_SUCCESS: break; case LDAP_PROC_NO_RESULT: RDEBUG("Can't check membership attributes, user object not found"); rcode = RLM_MODULE_NOTFOUND; /* FALL-THROUGH */ default: goto finish; } entry = ldap_first_entry((*pconn)->handle, result); if (!entry) { ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno); REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno)); rcode = RLM_MODULE_FAIL; goto finish; } vals = ldap_get_values((*pconn)->handle, entry, inst->userobj_membership_attr); if (!vals) { RDEBUG("No group membership attribute(s) found in user object"); goto finish; } /* * Loop over the list of groups the user is a member of, * looking for a match. */ name_is_dn = rlm_ldap_is_dn(name); count = ldap_count_values(vals); for (i = 0; i < count; i++) { value_is_dn = rlm_ldap_is_dn(vals[i]); RDEBUG2("Processing group membership value \"%s\"", vals[i]); /* * Both literal group names, do case sensitive comparison */ if (!name_is_dn && !value_is_dn) { if (strcmp(vals[i], name) == 0){ RDEBUG("User found. Comparison between membership: name, check: name]"); rcode = RLM_MODULE_OK; goto finish; } continue; } /* * Both DNs, do case insensitive comparison */ if (name_is_dn && value_is_dn) { if (strcasecmp(vals[i], name) == 0){ RDEBUG("User found. Comparison between membership: dn, check: dn"); rcode = RLM_MODULE_OK; goto finish; } continue; } /* * If the value is not a DN, and the name we were given is a dn * convert the value to a DN and do a comparison. */ if (!value_is_dn && name_is_dn) { char *resolved; int eq; ret = rlm_ldap_group_dn2name(inst, request, pconn, name, &resolved); if (ret != RLM_MODULE_OK) { rcode = ret; goto finish; } eq = strcmp(vals[i], resolved); talloc_free(resolved); if (eq == 0){ RDEBUG("User found. Comparison between membership: name, check: name " "(resolved from DN)"); rcode = RLM_MODULE_OK; goto finish; } continue; } /* * We have a value which is a DN, and a check item which specifies the name of a group, * convert the value to a name so we can do a comparison. */ if (value_is_dn && !name_is_dn) { char *resolved; int eq; ret = rlm_ldap_group_dn2name(inst, request, pconn, vals[i], &resolved); if (ret != RLM_MODULE_OK) { rcode = ret; goto finish; } eq = strcmp(resolved, name); talloc_free(resolved); if (eq == 0){ RDEBUG("User found. Comparison between membership: name (resolved from DN), " "check: name"); rcode = RLM_MODULE_OK; goto finish; } continue; } rad_assert(0); } finish: if (vals) { ldap_value_free(vals); } if (result) { ldap_msgfree(result); } return rcode; }
/** Perform LDAP-Group comparison checking * * Attempts to match users to groups using a variety of methods. * * @param instance of the rlm_ldap module. * @param request Current request. * @param thing Unknown. * @param check Which group to check for user membership. * @param check_pairs Unknown. * @param reply_pairs Unknown. * @return 1 on failure (or if the user is not a member), else 0. */ static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *thing, VALUE_PAIR *check, UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs) { ldap_instance_t *inst = instance; rlm_rcode_t rcode; bool found = false; bool check_is_dn; ldap_handle_t *conn = NULL; char const *user_dn; rad_assert(inst->groupobj_base_dn); RDEBUG("Searching for user in group \"%s\"", check->vp_strvalue); if (check->vp_length == 0) { REDEBUG("Cannot do comparison (group name is empty)"); return 1; } /* * Check if we can do cached membership verification */ check_is_dn = rlm_ldap_is_dn(check->vp_strvalue, check->vp_length); if (check_is_dn) { char *norm; MEM(norm = talloc_memdup(check, check->vp_strvalue, talloc_array_length(check->vp_strvalue))); rlm_ldap_normalise_dn(norm, check->vp_strvalue); pairstrsteal(check, norm); } if ((check_is_dn && inst->cacheable_group_dn) || (!check_is_dn && inst->cacheable_group_name)) { switch (rlm_ldap_check_cached(inst, request, check)) { case RLM_MODULE_NOTFOUND: found = false; goto finish; case RLM_MODULE_OK: found = true; goto finish; /* * Fallback to dynamic search on failure */ case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: default: break; } } conn = mod_conn_get(inst, request); if (!conn) return 1; /* * This is used in the default membership filter. */ user_dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode); if (!user_dn) { mod_conn_release(inst, conn); return 1; } rad_assert(conn); /* * Check groupobj user membership */ if (inst->groupobj_membership_filter) { switch (rlm_ldap_check_groupobj_dynamic(inst, request, &conn, check)) { case RLM_MODULE_NOTFOUND: break; case RLM_MODULE_OK: found = true; default: goto finish; } } rad_assert(conn); /* * Check userobj group membership */ if (inst->userobj_membership_attr) { switch (rlm_ldap_check_userobj_dynamic(inst, request, &conn, user_dn, check)) { case RLM_MODULE_NOTFOUND: break; case RLM_MODULE_OK: found = true; default: goto finish; } } rad_assert(conn); finish: if (conn) mod_conn_release(inst, conn); if (!found) { RDEBUG("User is not a member of \"%s\"", check->vp_strvalue); return 1; } return 0; }
/** Convert group membership information into attributes * * @param[in] inst rlm_ldap configuration. * @param[in] request Current request. * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. * @param[in] entry retrieved by rlm_ldap_find_user or rlm_ldap_search. * @param[in] attr membership attribute to look for in the entry. * @return One of the RLM_MODULE_* values. */ rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, LDAPMessage *entry, char const *attr) { rlm_rcode_t rcode = RLM_MODULE_OK; char **vals; char *group_name[LDAP_MAX_CACHEABLE + 1]; char **name_p = group_name; char *group_dn[LDAP_MAX_CACHEABLE + 1]; char **dn_p; char *name; int is_dn, i; rad_assert(entry); rad_assert(attr); /* * Parse the membership information we got in the initial user query. */ vals = ldap_get_values((*pconn)->handle, entry, attr); if (!vals) { RDEBUG2("No cacheable group memberships found in user object"); return RLM_MODULE_OK; } for (i = 0; (vals[i] != NULL) && (i < LDAP_MAX_CACHEABLE); i++) { is_dn = rlm_ldap_is_dn(vals[i]); if (inst->cacheable_group_dn) { /* * The easy case, were caching DNs and we got a DN. */ if (is_dn) { pairmake(request, &request->config_items, inst->cache_da->name, vals[i], T_OP_ADD); RDEBUG("Added %s with value \"%s\" to control list", inst->cache_da->name, vals[i]); /* * We were told to cache DNs but we got a name, we now need to resolve * this to a DN. Store all the group names in an array so we can do one query. */ } else { *name_p++ = vals[i]; } } if (inst->cacheable_group_name) { /* * The easy case, were caching names and we got a name. */ if (!is_dn) { pairmake(request, &request->config_items, inst->cache_da->name, vals[i], T_OP_ADD); RDEBUG("Added %s with value \"%s\" to control list", inst->cache_da->name, vals[i]); /* * We were told to cache names but we got a DN, we now need to resolve * this to a name. * Only Active Directory supports filtering on DN, so we have to search * for each individual group. */ } else { rcode = rlm_ldap_group_dn2name(inst, request, pconn, vals[i], &name); if (rcode != RLM_MODULE_OK) { ldap_value_free(vals); return rcode; } pairmake(request, &request->config_items, inst->cache_da->name, name, T_OP_ADD); RDEBUG("Added %s with value \"%s\" to control list", inst->cache_da->name, name); talloc_free(name); } } } *name_p = NULL; rcode = rlm_ldap_group_name2dn(inst, request, pconn, group_name, group_dn, sizeof(group_dn)); ldap_value_free(vals); if (rcode != RLM_MODULE_OK) { return rcode; } dn_p = group_dn; while(*dn_p) { pairmake(request, &request->config_items, inst->cache_da->name, *dn_p, T_OP_ADD); RDEBUG("Added %s with value \"%s\" to control list", inst->cache_da->name, *dn_p); ldap_memfree(*dn_p); dn_p++; } return rcode; }
/** Query the LDAP directory to check if a group object includes a user object as a member * * @param[in] inst rlm_ldap configuration. * @param[in] request Current request. * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. * @param[in] check vp containing the group value (name or dn). * @return One of the RLM_MODULE_* values. */ rlm_rcode_t rlm_ldap_check_groupobj_dynamic(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, VALUE_PAIR *check) { ldap_rcode_t status; char base_dn[LDAP_MAX_DN_STR_LEN + 1]; char filter[LDAP_MAX_FILTER_STR_LEN + 1]; char const *dn = base_dn; char const *name = check->vp_strvalue; rad_assert(inst->groupobj_base_dn); RDEBUG2("Checking for user in group objects"); if (rlm_ldap_is_dn(name)) { char const *filters[] = { inst->groupobj_filter, inst->groupobj_membership_filter }; if (rlm_ldap_xlat_filter(request, filters, sizeof(filters) / sizeof(*filters), filter, sizeof(filter)) < 0) { return RLM_MODULE_INVALID; } dn = name; } else { char name_filter[LDAP_MAX_FILTER_STR_LEN]; char const *filters[] = { name_filter, inst->groupobj_filter, inst->groupobj_membership_filter }; if (!inst->groupobj_name_attr) { REDEBUG("Told to search for group by name, but missing 'group.name_attribute' " "directive"); return RLM_MODULE_INVALID; } snprintf(name_filter, sizeof(name_filter), "(%s=%s)", inst->groupobj_name_attr, name); if (rlm_ldap_xlat_filter(request, filters, sizeof(filters) / sizeof(*filters), filter, sizeof(filter)) < 0) { return RLM_MODULE_INVALID; } /* * rlm_ldap_find_user does this, too. Oh well. */ if (radius_xlat(base_dn, sizeof(base_dn), request, inst->groupobj_base_dn, rlm_ldap_escape_func, NULL) < 0) { REDEBUG("Failed creating base_dn"); return RLM_MODULE_INVALID; } } status = rlm_ldap_search(inst, request, pconn, dn, inst->groupobj_scope, filter, NULL, NULL); switch (status) { case LDAP_PROC_SUCCESS: RDEBUG("User found in group object"); break; case LDAP_PROC_NO_RESULT: RDEBUG("Search returned not found"); return RLM_MODULE_NOTFOUND; default: return RLM_MODULE_FAIL; } return RLM_MODULE_OK; }
/** Perform LDAP-Group comparison checking * * Attempts to match users to groups using a variety of methods. * * @param instance of the rlm_ldap module. * @param request Current request. * @param thing Unknown. * @param check Which group to check for user membership. * @param check_pairs Unknown. * @param reply_pairs Unknown. * @return 1 on failure (or if the user is not a member), else 0. */ static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *thing, VALUE_PAIR *check, UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs) { ldap_instance_t *inst = instance; rlm_rcode_t rcode; int found = false; int check_is_dn; ldap_handle_t *conn = NULL; char const *user_dn; if (!inst->groupobj_base_dn) { REDEBUG("Directive 'group.base_dn' must be set'"); return 1; } RDEBUG("Searching for user in group \"%s\"", check->vp_strvalue); if (check->length == 0) { RDEBUG("Cannot do comparison (group name is empty)"); return 1; } /* * Check if we can do cached membership verification */ check_is_dn = rlm_ldap_is_dn(check->vp_strvalue); if ((check_is_dn && inst->cacheable_group_dn) || (!check_is_dn && inst->cacheable_group_name)) { switch(rlm_ldap_check_cached(inst, request, check)) { case RLM_MODULE_NOTFOUND: break; case RLM_MODULE_OK: found = true; default: goto finish; } } conn = rlm_ldap_get_socket(inst, request); if (!conn) return 1; /* * This is used in the default membership filter. */ user_dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode); if (!user_dn) { rlm_ldap_release_socket(inst, conn); return 1; } rad_assert(conn); /* * Check groupobj user membership */ if (inst->groupobj_membership_filter) { switch(rlm_ldap_check_groupobj_dynamic(inst, request, &conn, check)) { case RLM_MODULE_NOTFOUND: break; case RLM_MODULE_OK: found = true; default: goto finish; } } rad_assert(conn); /* * Check userobj group membership */ if (inst->userobj_membership_attr) { switch(rlm_ldap_check_userobj_dynamic(inst, request, &conn, user_dn, check)) { case RLM_MODULE_NOTFOUND: break; case RLM_MODULE_OK: found = true; default: goto finish; } } rad_assert(conn); finish: if (conn) { rlm_ldap_release_socket(inst, conn); } if (!found) { RDEBUG("User is not a member of specified group"); return 1; } return 0; }
/** Query the LDAP directory to check if a user object is a member of a group * * @param[in] inst rlm_ldap configuration. * @param[in] request Current request. * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. * @param[in] dn of user object. * @param[in] check vp containing the group value (name or dn). * @return One of the RLM_MODULE_* values. */ rlm_rcode_t rlm_ldap_check_userobj_dynamic(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn, VALUE_PAIR *check) { rlm_rcode_t rcode = RLM_MODULE_NOTFOUND, ret; ldap_rcode_t status; bool name_is_dn = false, value_is_dn = false; LDAPMessage *result = NULL; LDAPMessage *entry = NULL; struct berval **values = NULL; char const *attrs[] = { inst->userobj_membership_attr, NULL }; int i, count, ldap_errno; RDEBUG2("Checking user object's %s attributes", inst->userobj_membership_attr); RINDENT(); status = rlm_ldap_search(inst, request, pconn, dn, LDAP_SCOPE_BASE, NULL, attrs, &result); REXDENT(); switch (status) { case LDAP_PROC_SUCCESS: break; case LDAP_PROC_NO_RESULT: RDEBUG("Can't check membership attributes, user object not found"); rcode = RLM_MODULE_NOTFOUND; /* FALL-THROUGH */ default: goto finish; } entry = ldap_first_entry((*pconn)->handle, result); if (!entry) { ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno); REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno)); rcode = RLM_MODULE_FAIL; goto finish; } values = ldap_get_values_len((*pconn)->handle, entry, inst->userobj_membership_attr); if (!values) { RDEBUG("No group membership attribute(s) found in user object"); goto finish; } /* * Loop over the list of groups the user is a member of, * looking for a match. */ name_is_dn = rlm_ldap_is_dn(check->vp_strvalue, check->vp_length); count = ldap_count_values_len(values); for (i = 0; i < count; i++) { value_is_dn = rlm_ldap_is_dn(values[i]->bv_val, values[i]->bv_len); RDEBUG2("Processing %s value \"%.*s\" as a %s", inst->userobj_membership_attr, (int)values[i]->bv_len, values[i]->bv_val, value_is_dn ? "DN" : "group name"); /* * Both literal group names, do case sensitive comparison */ if (!name_is_dn && !value_is_dn) { if ((check->vp_length == values[i]->bv_len) && (memcmp(values[i]->bv_val, check->vp_strvalue, values[i]->bv_len) == 0)) { RDEBUG("User found in group \"%s\". Comparison between membership: name, check: name", check->vp_strvalue); rcode = RLM_MODULE_OK; goto finish; } continue; } /* * Both DNs, do case insensitive, binary safe comparison */ if (name_is_dn && value_is_dn) { if (check->vp_length == values[i]->bv_len) { int j; for (j = 0; j < (int)values[i]->bv_len; j++) { if (tolower(values[i]->bv_val[j]) != tolower(check->vp_strvalue[j])) break; } if (j == (int)values[i]->bv_len) { RDEBUG("User found in group DN \"%s\". " "Comparison between membership: dn, check: dn", check->vp_strvalue); rcode = RLM_MODULE_OK; goto finish; } } continue; } /* * If the value is not a DN, and the name we were given is a dn * convert the value to a DN and do a comparison. */ if (!value_is_dn && name_is_dn) { char *resolved; bool eq = false; RINDENT(); ret = rlm_ldap_group_dn2name(inst, request, pconn, check->vp_strvalue, &resolved); REXDENT(); if (ret != RLM_MODULE_OK) { rcode = ret; goto finish; } if (((talloc_array_length(resolved) - 1) == values[i]->bv_len) && (memcmp(values[i]->bv_val, resolved, values[i]->bv_len) == 0)) eq = true; talloc_free(resolved); if (eq) { RDEBUG("User found in group \"%.*s\". Comparison between membership: name, check: name " "(resolved from DN \"%s\")", (int)values[i]->bv_len, values[i]->bv_val, check->vp_strvalue); rcode = RLM_MODULE_OK; goto finish; } continue; } /* * We have a value which is a DN, and a check item which specifies the name of a group, * convert the value to a name so we can do a comparison. */ if (value_is_dn && !name_is_dn) { char *resolved; char *value; bool eq = false; value = rlm_ldap_berval_to_string(request, values[i]); RINDENT(); ret = rlm_ldap_group_dn2name(inst, request, pconn, value, &resolved); REXDENT(); talloc_free(value); if (ret != RLM_MODULE_OK) { rcode = ret; goto finish; } if (((talloc_array_length(resolved) - 1) == check->vp_length) && (memcmp(check->vp_strvalue, resolved, check->vp_length) == 0)) eq = true; talloc_free(resolved); if (eq) { RDEBUG("User found in group \"%s\". Comparison between membership: name " "(resolved from DN \"%s\"), check: name", check->vp_strvalue, value); rcode = RLM_MODULE_OK; goto finish; } continue; } rad_assert(0); } finish: if (values) ldap_value_free_len(values); if (result) ldap_msgfree(result); return rcode; }
/** Query the LDAP directory to check if a group object includes a user object as a member * * @param[in] inst rlm_ldap configuration. * @param[in] request Current request. * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. * @param[in] check vp containing the group value (name or dn). * @return One of the RLM_MODULE_* values. */ rlm_rcode_t rlm_ldap_check_groupobj_dynamic(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, VALUE_PAIR *check) { ldap_rcode_t status; char base_dn[LDAP_MAX_DN_STR_LEN + 1]; char filter[LDAP_MAX_FILTER_STR_LEN + 1]; char const *dn = base_dn; int ret; rad_assert(inst->groupobj_base_dn); switch (check->op) { case T_OP_CMP_EQ: case T_OP_CMP_FALSE: case T_OP_CMP_TRUE: case T_OP_REG_EQ: case T_OP_REG_NE: break; default: REDEBUG("Operator \"%s\" not allowed for LDAP group comparisons", fr_int2str(fr_tokens, check->op, "<INVALID>")); return 1; } RDEBUG2("Checking for user in group objects"); if (rlm_ldap_is_dn(check->vp_strvalue, check->vp_length)) { char const *filters[] = { inst->groupobj_filter, inst->groupobj_membership_filter }; RINDENT(); ret = rlm_ldap_xlat_filter(request, filters, sizeof(filters) / sizeof(*filters), filter, sizeof(filter)); REXDENT(); if (ret < 0) return RLM_MODULE_INVALID; dn = check->vp_strvalue; } else { char name_filter[LDAP_MAX_FILTER_STR_LEN]; char const *filters[] = { name_filter, inst->groupobj_filter, inst->groupobj_membership_filter }; if (!inst->groupobj_name_attr) { REDEBUG("Told to search for group by name, but missing 'group.name_attribute' " "directive"); return RLM_MODULE_INVALID; } snprintf(name_filter, sizeof(name_filter), "(%s=%s)", inst->groupobj_name_attr, check->vp_strvalue); RINDENT(); ret = rlm_ldap_xlat_filter(request, filters, sizeof(filters) / sizeof(*filters), filter, sizeof(filter)); REXDENT(); if (ret < 0) return RLM_MODULE_INVALID; /* * rlm_ldap_find_user does this, too. Oh well. */ RINDENT(); ret = radius_xlat(base_dn, sizeof(base_dn), request, inst->groupobj_base_dn, rlm_ldap_escape_func, NULL); REXDENT(); if (ret < 0) { REDEBUG("Failed creating base_dn"); return RLM_MODULE_INVALID; } } RINDENT(); status = rlm_ldap_search(inst, request, pconn, dn, inst->groupobj_scope, filter, NULL, NULL); REXDENT(); switch (status) { case LDAP_PROC_SUCCESS: RDEBUG("User found in group object \"%s\"", dn); break; case LDAP_PROC_NO_RESULT: return RLM_MODULE_NOTFOUND; default: return RLM_MODULE_FAIL; } return RLM_MODULE_OK; }
/** Convert group membership information into attributes * * @param[in] inst rlm_ldap configuration. * @param[in] request Current request. * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. * @param[in] entry retrieved by rlm_ldap_find_user or rlm_ldap_search. * @param[in] attr membership attribute to look for in the entry. * @return One of the RLM_MODULE_* values. */ rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, LDAPMessage *entry, char const *attr) { rlm_rcode_t rcode = RLM_MODULE_OK; struct berval **values; size_t value_len = 0; TALLOC_CTX *value_pool; char *group_name[LDAP_MAX_CACHEABLE + 1]; char **name_p = group_name; char *group_dn[LDAP_MAX_CACHEABLE + 1]; char **dn_p; char *name; VALUE_PAIR *vp, **vps; TALLOC_CTX *ctx; vp_cursor_t cursor; int is_dn, i, count; rad_assert(entry); rad_assert(attr); /* * Parse the membership information we got in the initial user query. */ values = ldap_get_values_len((*pconn)->handle, entry, attr); if (!values) { RDEBUG2("No cacheable group memberships found in user object"); return RLM_MODULE_OK; } count = ldap_count_values_len(values); vps = radius_list(request, PAIR_LIST_CONTROL); ctx = radius_list_ctx(request, PAIR_LIST_CONTROL); fr_cursor_init(&cursor, vps); /* * Avoid allocing buffers for each value. * * The old code used ldap_get_values, which was likely doing * a very similar thing internally to produce \0 terminated * buffers from bervalues. */ for (i = 0; (i < LDAP_MAX_CACHEABLE) && (i < count); i++) value_len += values[i]->bv_len + 1; value_pool = talloc_pool(request, value_len); for (i = 0; (i < LDAP_MAX_CACHEABLE) && (i < count); i++) { is_dn = rlm_ldap_is_dn(values[i]->bv_val, values[i]->bv_len); if (inst->cacheable_group_dn) { /* * The easy case, we're caching DNs and we got a DN. */ if (is_dn) { MEM(vp = pairalloc(ctx, inst->cache_da)); pairstrncpy(vp, values[i]->bv_val, values[i]->bv_len); fr_cursor_insert(&cursor, vp); RDEBUG("Added %s with value \"%s\" to control list", inst->cache_da->name, vp->vp_strvalue); /* * We were told to cache DNs but we got a name, we now need to resolve * this to a DN. Store all the group names in an array so we can do one query. */ } else { *name_p++ = rlm_ldap_berval_to_string(value_pool, values[i]); } } if (inst->cacheable_group_name) { /* * The easy case, we're caching names and we got a name. */ if (!is_dn) { MEM(vp = pairalloc(ctx, inst->cache_da)); pairstrncpy(vp, values[i]->bv_val, values[i]->bv_len); fr_cursor_insert(&cursor, vp); RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, vp->vp_strvalue); /* * We were told to cache names but we got a DN, we now need to resolve * this to a name. * Only Active Directory supports filtering on DN, so we have to search * for each individual group. */ } else { char *dn; dn = rlm_ldap_berval_to_string(value_pool, values[i]); rcode = rlm_ldap_group_dn2name(inst, request, pconn, dn, &name); talloc_free(dn); if (rcode != RLM_MODULE_OK) { ldap_value_free_len(values); talloc_free(value_pool); return rcode; } MEM(vp = pairalloc(ctx, inst->cache_da)); pairstrncpy(vp, name, talloc_array_length(name) - 1); fr_cursor_insert(&cursor, vp); RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, name); talloc_free(name); } } } *name_p = NULL; rcode = rlm_ldap_group_name2dn(inst, request, pconn, group_name, group_dn, sizeof(group_dn)); ldap_value_free_len(values); talloc_free(value_pool); if (rcode != RLM_MODULE_OK) return rcode; dn_p = group_dn; while (*dn_p) { MEM(vp = pairalloc(ctx, inst->cache_da)); pairstrcpy(vp, *dn_p); fr_cursor_insert(&cursor, vp); RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, *dn_p); ldap_memfree(*dn_p); dn_p++; } return rcode; }
/** Convert group membership information into attributes * * @param[in] inst rlm_ldap configuration. * @param[in] request Current request. * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. * @param[in] entry retrieved by rlm_ldap_find_user or rlm_ldap_search. * @param[in] attr membership attribute to look for in the entry. * @return One of the RLM_MODULE_* values. */ rlm_rcode_t rlm_ldap_cacheable_userobj(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, LDAPMessage *entry, char const *attr) { rlm_rcode_t rcode = RLM_MODULE_OK; struct berval **values; char *group_name[LDAP_MAX_CACHEABLE + 1]; char **name_p = group_name; char *group_dn[LDAP_MAX_CACHEABLE + 1]; char **dn_p; char *name; VALUE_PAIR *vp, **list, *groups = NULL; TALLOC_CTX *list_ctx, *value_ctx; vp_cursor_t list_cursor, groups_cursor; int is_dn, i, count; rad_assert(entry); rad_assert(attr); /* * Parse the membership information we got in the initial user query. */ values = ldap_get_values_len((*pconn)->handle, entry, attr); if (!values) { RDEBUG2("No cacheable group memberships found in user object"); return RLM_MODULE_OK; } count = ldap_count_values_len(values); list = radius_list(request, PAIR_LIST_CONTROL); list_ctx = radius_list_ctx(request, PAIR_LIST_CONTROL); /* * Simplifies freeing temporary values */ value_ctx = talloc_new(request); /* * Temporary list to hold new group VPs, will be merged * once all group info has been gathered/resolved * successfully. */ fr_cursor_init(&groups_cursor, &groups); for (i = 0; (i < LDAP_MAX_CACHEABLE) && (i < count); i++) { is_dn = rlm_ldap_is_dn(values[i]->bv_val, values[i]->bv_len); if (inst->cacheable_group_dn) { /* * The easy case, we're caching DNs and we got a DN. */ if (is_dn) { MEM(vp = fr_pair_afrom_da(list_ctx, inst->cache_da)); fr_pair_value_bstrncpy(vp, values[i]->bv_val, values[i]->bv_len); fr_cursor_insert(&groups_cursor, vp); /* * We were told to cache DNs but we got a name, we now need to resolve * this to a DN. Store all the group names in an array so we can do one query. */ } else { *name_p++ = rlm_ldap_berval_to_string(value_ctx, values[i]); } } if (inst->cacheable_group_name) { /* * The easy case, we're caching names and we got a name. */ if (!is_dn) { MEM(vp = fr_pair_afrom_da(list_ctx, inst->cache_da)); fr_pair_value_bstrncpy(vp, values[i]->bv_val, values[i]->bv_len); fr_cursor_insert(&groups_cursor, vp); /* * We were told to cache names but we got a DN, we now need to resolve * this to a name. * Only Active Directory supports filtering on DN, so we have to search * for each individual group. */ } else { char *dn; dn = rlm_ldap_berval_to_string(value_ctx, values[i]); rcode = rlm_ldap_group_dn2name(inst, request, pconn, dn, &name); talloc_free(dn); if (rcode != RLM_MODULE_OK) { ldap_value_free_len(values); talloc_free(value_ctx); fr_pair_list_free(&groups); return rcode; } MEM(vp = fr_pair_afrom_da(list_ctx, inst->cache_da)); fr_pair_value_bstrncpy(vp, name, talloc_array_length(name) - 1); fr_cursor_insert(&groups_cursor, vp); talloc_free(name); } } } *name_p = NULL; rcode = rlm_ldap_group_name2dn(inst, request, pconn, group_name, group_dn, sizeof(group_dn)); ldap_value_free_len(values); talloc_free(value_ctx); if (rcode != RLM_MODULE_OK) return rcode; fr_cursor_init(&list_cursor, list); RDEBUG("Adding cacheable user object memberships"); RINDENT(); if (RDEBUG_ENABLED) { for (vp = fr_cursor_first(&groups_cursor); vp; vp = fr_cursor_next(&groups_cursor)) { RDEBUG("&control:%s += \"%s\"", inst->cache_da->name, vp->vp_strvalue); } } fr_cursor_merge(&list_cursor, groups); for (dn_p = group_dn; *dn_p; dn_p++) { MEM(vp = fr_pair_afrom_da(list_ctx, inst->cache_da)); fr_pair_value_strcpy(vp, *dn_p); fr_cursor_insert(&list_cursor, vp); RDEBUG("&control:%s += \"%s\"", inst->cache_da->name, vp->vp_strvalue); ldap_memfree(*dn_p); } REXDENT(); return rcode; }