size_t fr_ldap_uri_unescape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
CC_HINT(nonnull(2,4));
-ssize_t fr_ldap_xlat_filter(request_t *request, char const **sub, size_t sublen, char *out, size_t outlen);
-
char const *fr_ldap_error_str(fr_ldap_connection_t const *conn);
fr_ldap_rcode_t fr_ldap_search_async(int *msgid, request_t *request,
int fr_ldap_box_escape(fr_value_box_t *vb, UNUSED void *uctx);
+int fr_ldap_filter_to_tmpl(TALLOC_CTX *ctx, tmpl_rules_t const *t_rules, char const **sub, size_t sublen,
+ tmpl_t **out) CC_HINT(nonnull());
+
/*
* referral.c - Handle LDAP referrals
*/
return f_len - p_len;
}
-/** Combine and expand filters
+/** Combine filters and tokenize to a tmpl
*
- * @param request Current request.
- * @param out Where to write the expanded string.
- * @param outlen Length of output buffer.
- * @param sub Array of subfilters (may contain NULLs).
- * @param sublen Number of potential subfilters in array.
- * @return length of expanded data.
+ * @param ctx To allocate combined filter in
+ * @param request Current request.
+ * @param sub Array of subfilters (may contain NULLs).
+ * @param sublen Number of potential subfilters in array.
+ * @param out Where to write a pointer to the resulting tmpl.
+ * @return length of combined data.
*/
-ssize_t fr_ldap_xlat_filter(request_t *request, char const **sub, size_t sublen, char *out, size_t outlen)
+int fr_ldap_filter_to_tmpl(TALLOC_CTX *ctx, tmpl_rules_t const *t_rules, char const **sub, size_t sublen, tmpl_t **out)
{
- char buffer[LDAP_MAX_FILTER_STR_LEN + 1];
- char const *in = NULL;
- char *p = buffer;
+ char *buffer = NULL;
+ char const *in = NULL;
+ ssize_t len = 0;
+ size_t i;
+ int cnt = 0;
+ tmpl_t *parsed;
- ssize_t len = 0;
-
- unsigned int i;
- int cnt = 0;
+ *out = NULL;
/*
* Figure out how many filter elements we need to integrate
if (sub[i] && *sub[i]) {
in = sub[i];
cnt++;
+ len += strlen(sub[i]);
}
}
- if (!cnt) {
- out[0] = '\0';
- return 0;
- }
+ if (!cnt) return 0;
if (cnt > 1) {
- if (outlen < 3) {
- goto oob;
- }
-
- p[len++] = '(';
- p[len++] = '&';
+ /*
+ * Allocate a buffer large enough, allowing for (& ... ) plus trailing '\0'
+ */
+ buffer = talloc_array(ctx, char, len + 4);
+ strcpy(buffer, "(&");
for (i = 0; i < sublen; i++) {
if (sub[i] && (*sub[i] != '\0')) {
- len += strlcpy(p + len, sub[i], outlen - len);
-
- if ((size_t) len >= outlen) {
- oob:
- REDEBUG("Out of buffer space creating filter");
-
- return -1;
- }
+ strcat(buffer, sub[i]);
}
}
-
- if ((outlen - len) < 2) {
- goto oob;
- }
-
- p[len++] = ')';
- p[len] = '\0';
-
+ strcat(buffer, ")");
in = buffer;
}
- len = xlat_eval(out, outlen, request, in, fr_ldap_uri_escape_func, NULL);
- if (len < 0) {
- REDEBUG("Failed creating filter");
+ len = tmpl_afrom_substr(ctx, &parsed, &FR_SBUFF_IN(in, strlen(in)), T_DOUBLE_QUOTED_STRING, NULL, t_rules);
+
+ talloc_free(buffer);
+ if (len < 0) {
+ EMARKER(in, -len, fr_strerror());
return -1;
}
- return len;
+ *out = parsed;
+ return 0;
}
/** Check that a particular attribute is included in an attribute list
rlm_ldap_t const *inst; //!< Module instance.
fr_value_box_t *base_dn; //!< The base DN to search for groups in.
fr_ldap_thread_trunk_t *ttrunk; //!< Trunk on which to perform additional queries.
- char filter[LDAP_MAX_FILTER_STR_LEN + 1]; //!< Filter used to search for groups.
+ tmpl_t *filter_tmpl; //!< Tmpl to expand into LDAP filter.
+ fr_value_box_list_t expanded_filter; //!< Values produced by expanding filter xlat.
char const *attrs[2]; //!< For retrieving the group name.
fr_ldap_query_t *query; //!< Current query performing group lookup.
void *uctx; //!< Optional context for use in results parsing.
* @param[in] uctx Group lookup context.
* @return One of the RLM_MODULE_* values.
*/
-static unlang_action_t ldap_cacheable_groupobj_start(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, request_t *request,
+static unlang_action_t ldap_cacheable_groupobj_start(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request,
void *uctx)
{
ldap_group_groupobj_ctx_t *group_ctx = talloc_get_type_abort(uctx, ldap_group_groupobj_ctx_t);
rlm_ldap_t const *inst = group_ctx->inst;
+ fr_value_box_t *filter;
+
+ filter = fr_value_box_list_head(&group_ctx->expanded_filter);
+
+ if (filter->type != FR_TYPE_STRING) RETURN_MODULE_FAIL;
group_ctx->attrs[0] = inst->group.obj_name_attr;
return fr_ldap_trunk_search(group_ctx, &group_ctx->query, request, group_ctx->ttrunk,
group_ctx->base_dn->vb_strvalue, inst->group.obj_scope,
- group_ctx->filter, group_ctx->attrs, NULL, NULL);
+ filter->vb_strvalue, group_ctx->attrs, NULL, NULL);
}
/** Cancel a pending group object lookup.
{
rlm_ldap_t const *inst = autz_ctx->inst;
ldap_group_groupobj_ctx_t *group_ctx;
- char const *filters[] = { inst->group.obj_filter, inst->group.obj_membership_filter };
if (!inst->group.obj_membership_filter) {
RDEBUG2("Skipping caching group objects as directive 'group.membership_filter' is not set");
group_ctx->inst = inst;
group_ctx->ttrunk = autz_ctx->ttrunk;
group_ctx->base_dn = &autz_ctx->call_env->group_base;
-
- if (fr_ldap_xlat_filter(request, filters, NUM_ELEMENTS(filters),
- group_ctx->filter, sizeof(group_ctx->filter)) < 0) {
- talloc_free(group_ctx);
- RETURN_MODULE_INVALID;
- }
+ fr_value_box_list_init(&group_ctx->expanded_filter);
if (unlang_function_push(request, ldap_cacheable_groupobj_start, ldap_cacheable_groupobj_resume,
ldap_group_groupobj_cancel, ~FR_SIGNAL_CANCEL, UNLANG_SUB_FRAME, group_ctx) < 0) {
+ error:
talloc_free(group_ctx);
RETURN_MODULE_FAIL;
}
+ if (unlang_tmpl_push(group_ctx, &group_ctx->expanded_filter, request, autz_ctx->call_env->group_filter, NULL) < 0) goto error;
+
return UNLANG_ACTION_PUSHED_CHILD;
}
{
rlm_ldap_t const *inst = xlat_ctx->inst;
ldap_group_groupobj_ctx_t *group_ctx;
- int ret;
MEM(group_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), ldap_group_groupobj_ctx_t));
*group_ctx = (ldap_group_groupobj_ctx_t) {
.ttrunk = xlat_ctx->ttrunk,
.uctx = xlat_ctx
};
+ fr_value_box_list_init(&group_ctx->expanded_filter);
if (fr_ldap_util_is_dn(xlat_ctx->group->vb_strvalue, xlat_ctx->group->vb_length)) {
- char const *filters[] = { inst->group.obj_filter, inst->group.obj_membership_filter };
-
- RINDENT();
- ret = fr_ldap_xlat_filter(request,
- filters, NUM_ELEMENTS(filters),
- group_ctx->filter, sizeof(group_ctx->filter));
- REXDENT();
-
- if (ret < 0) {
- invalid:
- talloc_free(group_ctx);
- RETURN_MODULE_INVALID;
- }
-
+ group_ctx->filter_tmpl = xlat_ctx->env_data->group_filter;
group_ctx->base_dn = xlat_ctx->group;
} else {
- char name_filter[LDAP_MAX_FILTER_STR_LEN];
- char const *filters[] = { name_filter, inst->group.obj_filter, inst->group.obj_membership_filter };
+ char name_filter[LDAP_MAX_FILTER_STR_LEN];
+ char const *filters[] = { name_filter, inst->group.obj_filter, inst->group.obj_membership_filter };
+ tmpl_rules_t t_rules;
if (!inst->group.obj_name_attr) {
REDEBUG("Told to search for group by name, but missing 'group.name_attribute' "
"directive");
-
- goto invalid;
+ invalid:
+ talloc_free(group_ctx);
+ RETURN_MODULE_INVALID;
}
+ t_rules = (tmpl_rules_t){
+ .attr = {
+ .dict_def = request->dict,
+ .list_def = request_attr_request,
+ },
+ .xlat = {
+ .runtime_el = unlang_interpret_event_list(request),
+ },
+ .at_runtime = true,
+ .escape.func = fr_ldap_box_escape,
+ .escape.safe_for = (fr_value_box_safe_for_t)fr_ldap_box_escape,
+ .escape.mode = TMPL_ESCAPE_PRE_CONCAT,
+ .literals_safe_for = (fr_value_box_safe_for_t)fr_ldap_box_escape,
+ .cast = FR_TYPE_STRING,
+ };
+
snprintf(name_filter, sizeof(name_filter), "(%s=%s)",
inst->group.obj_name_attr, xlat_ctx->group->vb_strvalue);
- RINDENT();
- ret = fr_ldap_xlat_filter(request,
- filters, NUM_ELEMENTS(filters),
- group_ctx->filter, sizeof(group_ctx->filter));
- REXDENT();
- if (ret < 0) goto invalid;
+
+ if (fr_ldap_filter_to_tmpl(group_ctx, &t_rules, filters, NUM_ELEMENTS(filters),
+ &group_ctx->filter_tmpl) < 0) goto invalid;
fr_assert(xlat_ctx->env_data);
group_ctx->base_dn = &xlat_ctx->env_data->group_base;
if (unlang_function_push(request, ldap_cacheable_groupobj_start, ldap_check_groupobj_resume,
ldap_group_groupobj_cancel, ~FR_SIGNAL_CANCEL,
- UNLANG_SUB_FRAME, group_ctx) < 0) goto invalid;
+ UNLANG_SUB_FRAME, group_ctx) < 0) {
+ error:
+ talloc_free(group_ctx);
+ RETURN_MODULE_FAIL;
+ }
+
+ if (unlang_tmpl_push(group_ctx, &group_ctx->expanded_filter, request, group_ctx->filter_tmpl, NULL) < 0) goto error;
return UNLANG_ACTION_PUSHED_CHILD;
}
static int ldap_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, UNUSED call_env_parser_t const *rule);
+static int ldap_group_filter_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, void const *data, UNUSED call_env_parser_t const *rule);
+
static const call_env_parser_t sasl_call_env[] = {
{ FR_CALL_ENV_OFFSET("mech", FR_TYPE_STRING, CALL_ENV_FLAG_NONE, ldap_auth_call_env_t, user_sasl_mech) },
{ FR_CALL_ENV_OFFSET("authname", FR_TYPE_STRING, CALL_ENV_FLAG_NONE, ldap_auth_call_env_t, user_sasl_authname) },
{ FR_CALL_ENV_SUBSECTION("group", NULL, CALL_ENV_FLAG_NONE,
((call_env_parser_t[]) {
{ FR_CALL_ENV_OFFSET("base_dn", FR_TYPE_STRING, CALL_ENV_FLAG_CONCAT, ldap_autz_call_env_t, group_base) },
+ { FR_CALL_ENV_PARSE_ONLY_OFFSET("membership_filter", FR_TYPE_STRING, CALL_ENV_FLAG_CONCAT, ldap_autz_call_env_t, group_filter),
+ .pair.func = ldap_group_filter_parse,
+ .pair.escape = {
+ .func = fr_ldap_box_escape,
+ .safe_for = (fr_value_box_safe_for_t)fr_ldap_box_escape,
+ .mode = TMPL_ESCAPE_PRE_CONCAT
+ },
+ .pair.literals_safe_for = (fr_value_box_safe_for_t)fr_ldap_box_escape,
+ },
CALL_ENV_TERMINATOR
})) },
{ FR_CALL_ENV_SUBSECTION("profile", NULL, CALL_ENV_FLAG_NONE,
{ FR_CALL_ENV_SUBSECTION("group", NULL, CALL_ENV_FLAG_NONE,
((call_env_parser_t[]) {
{ FR_CALL_ENV_OFFSET("base_dn", FR_TYPE_STRING, CALL_ENV_FLAG_CONCAT, ldap_xlat_memberof_call_env_t, group_base) },
+ { FR_CALL_ENV_PARSE_ONLY_OFFSET("membership_filter", FR_TYPE_STRING, CALL_ENV_FLAG_CONCAT, ldap_xlat_memberof_call_env_t, group_filter),
+ .pair.func = ldap_group_filter_parse,
+ .pair.escape = {
+ .func = fr_ldap_box_escape,
+ .safe_for = (fr_value_box_safe_for_t)fr_ldap_box_escape,
+ .mode = TMPL_ESCAPE_PRE_CONCAT
+ },
+ .pair.literals_safe_for = (fr_value_box_safe_for_t)fr_ldap_box_escape,
+ },
CALL_ENV_TERMINATOR
})) },
CALL_ENV_TERMINATOR
return 0;
}
+static int ldap_group_filter_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, UNUSED CONF_ITEM *ci,
+ void const *data, UNUSED call_env_parser_t const *rule)
+{
+ rlm_ldap_t const *inst = talloc_get_type_abort_const(data, rlm_ldap_t);
+ char const *filters[] = { inst->group.obj_filter, inst->group.obj_membership_filter };
+ tmpl_t *parsed;
+
+ if (fr_ldap_filter_to_tmpl(ctx, t_rules, filters, NUM_ELEMENTS(filters), &parsed) < 0) return -1;
+
+ *(void **)out = parsed;
+ return 0;
+}
+
/** Instantiate the module
*
* Creates a new instance of the module reading parameters from a configuration section.
fr_value_box_t user_base; //!< Base DN in which to search for users.
fr_value_box_t user_filter; //!< Filter to use when searching for users.
fr_value_box_t group_base; //!< Base DN in which to search for groups.
+ tmpl_t *group_filter; //!< tmpl to expand as group membership filter.
fr_value_box_t default_profile; //!< If this is set, we will search for a profile object
//!< with this name, and map any attributes it contains.
//!< No value should be set if profiles are not being used
fr_value_box_t user_base; //!< Base DN in which to search for users.
fr_value_box_t user_filter; //!< Filter to use when searching for users.
fr_value_box_t group_base; //!< Base DN in which to search for groups.
+ tmpl_t *group_filter; //!< tmpl to expand as group membership filter.
} ldap_xlat_memberof_call_env_t;
/** State list for resumption of authorization