From: Arran Cudbard-Bell Date: Sat, 25 Nov 2023 02:37:39 +0000 (-0600) Subject: Switch LDAP map compilation to using call envs too X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ffad03d9cb245b568f3e3cc59155026b03508be5;p=thirdparty%2Ffreeradius-server.git Switch LDAP map compilation to using call envs too --- diff --git a/src/modules/rlm_cache/rlm_cache.c b/src/modules/rlm_cache/rlm_cache.c index 45be49e0a7d..1b9e3245b5f 100644 --- a/src/modules/rlm_cache/rlm_cache.c +++ b/src/modules/rlm_cache/rlm_cache.c @@ -1244,41 +1244,37 @@ static int cache_verify(map_t *map, void *uctx) static int cache_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, fr_dict_t const *namespace, CONF_ITEM *ci, UNUSED call_env_parser_t const *rule) { - map_list_t *maps; CONF_SECTION *update = cf_item_to_section(ci); - call_env_parsed_t *call_env_parsed; - - MEM(call_env_parsed = call_env_parsed_add(ctx, out, - &(call_env_parser_t){ FR_CALL_ENV_PARSE_ONLY_OFFSET("update", FR_TYPE_VOID, 0, cache_call_env_t, maps)})); + call_env_parsed_t *parsed; + map_list_t *maps; - MEM(maps = talloc_zero(call_env_parsed, map_list_t)); + tmpl_rules_t parse_rules = { + .attr = { + .dict_def = namespace, + .list_def = request_attr_request, + .allow_wildcard = true, + .allow_foreign = true /* Because we don't know where we'll be called */ + } + }; - /* - * Make sure the users don't screw up too badly. - */ - { - tmpl_rules_t parse_rules = { - .attr = { - .dict_def = namespace, - .list_def = request_attr_request, - .allow_wildcard = true, - .allow_foreign = true /* Because we don't know where we'll be called */ - } - }; + MEM(parsed = call_env_parsed_add(ctx, out, + &(call_env_parser_t){ FR_CALL_ENV_PARSE_ONLY_OFFSET("update", FR_TYPE_VOID, 0, cache_call_env_t, maps)})); + MEM(maps = talloc_zero(parsed, map_list_t)); - map_list_init(maps); - if (map_afrom_cs(maps, maps, update, - &parse_rules, &parse_rules, cache_verify, NULL, MAX_ATTRMAP) < 0) { - return -1; - } + map_list_init(maps); + if (map_afrom_cs(maps, maps, update, + &parse_rules, &parse_rules, cache_verify, NULL, MAX_ATTRMAP) < 0) { + error: + call_env_parsed_free(out, parsed); + return -1; } if (map_list_empty(maps)) { cf_log_err(update, "Update section must not be empty"); - return -1; + goto error; } - call_env_parsed_set_data(call_env_parsed, maps); + call_env_parsed_set_data(parsed, maps); return 0; } diff --git a/src/modules/rlm_ldap/rlm_ldap.c b/src/modules/rlm_ldap/rlm_ldap.c index 42050b1c8e6..f9113a2e2ba 100644 --- a/src/modules/rlm_ldap/rlm_ldap.c +++ b/src/modules/rlm_ldap/rlm_ldap.c @@ -50,7 +50,7 @@ USES_APPLE_DEPRECATED_API typedef struct { fr_value_box_t password; - tmpl_t *password_tmpl; + tmpl_t const *password_tmpl; fr_value_box_t user_base; fr_value_box_t user_filter; fr_value_box_t user_sasl_mech; @@ -68,8 +68,11 @@ typedef struct { */ typedef struct { fr_value_box_t profile_filter; //!< Filter to use when searching for users. + map_list_t *profile_map; //!< List of maps to apply to the profile. } ldap_xlat_profile_call_env_t; +static int ldap_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, fr_dict_t const *namespace, CONF_ITEM *ci, 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) }, @@ -190,9 +193,21 @@ static const call_env_method_t authenticate_method_env = { } }; +/** Parameters to allow ldap_update_section_parse to be reused + */ +typedef struct { + size_t map_offset; + ssize_t expect_password_offset; +} ldap_update_rules_t; + static const call_env_method_t authorize_method_env = { FR_CALL_ENV_METHOD_OUT(ldap_autz_call_env_t), .env = (call_env_parser_t[]) { + { FR_CALL_ENV_SUBSECTION_FUNC("update", CF_IDENT_ANY, CALL_ENV_FLAG_NONE, ldap_update_section_parse), + .uctx = &(ldap_update_rules_t){ + .map_offset = offsetof(ldap_autz_call_env_t, user_map), + .expect_password_offset = offsetof(ldap_autz_call_env_t, expect_password) + } }, { FR_CALL_ENV_SUBSECTION("user", NULL, CALL_ENV_FLAG_REQUIRED, ((call_env_parser_t[]) { USER_CALL_ENV_COMMON(ldap_autz_call_env_t), @@ -247,6 +262,11 @@ static const call_env_method_t xlat_memberof_method_env = { static const call_env_method_t xlat_profile_method_env = { FR_CALL_ENV_METHOD_OUT(ldap_xlat_profile_call_env_t), .env = (call_env_parser_t[]) { + { FR_CALL_ENV_SUBSECTION_FUNC("update", CF_IDENT_ANY, CALL_ENV_FLAG_NONE, ldap_update_section_parse), + .uctx = &(ldap_update_rules_t){ + .map_offset = offsetof(ldap_xlat_profile_call_env_t, profile_map), + .expect_password_offset = -1 + } }, { FR_CALL_ENV_SUBSECTION("profile", NULL, CALL_ENV_FLAG_NONE, ((call_env_parser_t[]) { { FR_CALL_ENV_OFFSET("filter", FR_TYPE_STRING, CALL_ENV_FLAG_CONCAT, ldap_xlat_profile_call_env_t, profile_filter), @@ -1035,7 +1055,7 @@ static xlat_action_t ldap_profile_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor /* * Synchronous expansion of maps (fixme!) */ - if (fr_ldap_map_expand(xlat_ctx, &xlat_ctx->expanded, request, &inst->user_map, inst->valuepair_attr) < 0) goto error; + if (fr_ldap_map_expand(xlat_ctx, &xlat_ctx->expanded, request, env_data->profile_map, inst->valuepair_attr) < 0) goto error; ttrunk = fr_thread_ldap_trunk_get(t, host_url, handle_config->admin_identity, handle_config->admin_password, request, handle_config); if (host) ldap_memfree(host); @@ -1644,13 +1664,13 @@ static unlang_action_t mod_authorize_resume(rlm_rcode_t *p_result, UNUSED int *p FALL_THROUGH; case LDAP_AUTZ_MAP: - if (!map_list_empty(&inst->user_map) || inst->valuepair_attr) { + if (!map_list_empty(call_env->user_map) || inst->valuepair_attr) { RDEBUG2("Processing user attributes"); RINDENT(); if (fr_ldap_map_do(request, inst->valuepair_attr, &autz_ctx->expanded, autz_ctx->entry) > 0) rcode = RLM_MODULE_UPDATED; REXDENT(); - rlm_ldap_check_reply(&(module_ctx_t){.inst = autz_ctx->dlinst}, request, autz_ctx->ttrunk); + rlm_ldap_check_reply(request, autz_ctx->dlinst->name, call_env->expect_password->vb_bool, autz_ctx->ttrunk); } } @@ -1698,7 +1718,7 @@ static unlang_action_t CC_HINT(nonnull) mod_authorize(rlm_rcode_t *p_result, mod * for many things besides searching for users. */ - if (fr_ldap_map_expand(autz_ctx, expanded, request, &inst->user_map, inst->valuepair_attr) < 0) { + if (fr_ldap_map_expand(autz_ctx, expanded, request, call_env->user_map, inst->valuepair_attr) < 0) { fail: talloc_free(autz_ctx); RETURN_MODULE_FAIL; @@ -2259,6 +2279,85 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx) return 0; } +static int ldap_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, fr_dict_t const *namespace, CONF_ITEM *ci, UNUSED call_env_parser_t const *rule) +{ + map_list_t *maps; + CONF_SECTION *update = cf_item_to_section(ci); + ldap_update_rules_t const *ur = rule->uctx; + + bool expect_password; + + /* + * Build the attribute map + */ + { + map_t const *map = NULL; + tmpl_attr_t const *ar; + call_env_parsed_t *parsed; + tmpl_rules_t parse_rules = { + .attr = { + .dict_def = namespace, + .list_def = request_attr_request, + } + }; + + MEM(parsed = call_env_parsed_add(ctx, out, + &(call_env_parser_t){ + .name = "update", + .flags = CALL_ENV_FLAG_PARSE_ONLY, + .pair = { + .parsed = { + .offset = ur->map_offset, + .type = CALL_ENV_PARSE_TYPE_VOID + } + } + })); + MEM(maps = talloc_zero(parsed, map_list_t)); + + if (map_afrom_cs(maps, maps, update, &parse_rules, &parse_rules, fr_ldap_map_verify, NULL, LDAP_MAX_ATTRMAP) < 0) { + call_env_parsed_free(out, parsed); + return -1; + } + /* + * Check map to see if a password is being retrieved. + * fr_ldap_map_verify ensures that all maps have attributes on the LHS. + * All passwords have a common parent attribute of attr_password + */ + while ((map = map_list_next(maps, map))) { + ar = tmpl_attr_tail(map->lhs); + if (ar->da->parent == attr_password) { + expect_password = true; + break; + } + } + } + + /* + * Write out whether we expect a password to be returned from the ldap data + */ + if (ur->expect_password_offset >= 0) { + call_env_parsed_t *parsed; + fr_value_box_t *vb; + + MEM(parsed = call_env_parsed_add(ctx, out, + &(call_env_parser_t){ + .name = "expect_password", + .flags = CALL_ENV_FLAG_PARSE_ONLY, + .pair = { + .parsed = { + .offset = ur->expect_password_offset, + .type = CALL_ENV_PARSE_TYPE_VALUE_BOX + } + } + })); + MEM(vb = fr_value_box_alloc(parsed, FR_TYPE_BOOL, NULL)); + vb->vb_bool = expect_password; + call_env_parsed_set_value(parsed, vb); + } + + return 0; +} + /** Instantiate the module * * Creates a new instance of the module reading parameters from a configuration section. @@ -2272,12 +2371,10 @@ static int mod_instantiate(module_inst_ctx_t const *mctx) { size_t i; - CONF_SECTION *options, *update; + CONF_SECTION *options; rlm_ldap_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_ldap_t); CONF_SECTION *conf = mctx->inst->conf; - map_list_init(&inst->user_map); - options = cf_section_find(conf, "options", NULL); if (!options || !cf_pair_find(options, "chase_referrals")) { inst->handle_config.chase_referrals_unset = true; /* use OpenLDAP defaults */ @@ -2457,40 +2554,6 @@ static int mod_instantiate(module_inst_ctx_t const *mctx) } } - /* - * Build the attribute map - */ - { - map_t const *map = NULL; - tmpl_attr_t const *ar; - tmpl_rules_t parse_rules = { - .attr = { - .list_def = request_attr_request, - .allow_foreign = true /* Because we don't know where we'll be called */ - } - }; - - update = cf_section_find(conf, "update", NULL); - if (update && (map_afrom_cs(inst, &inst->user_map, update, - &parse_rules, &parse_rules, fr_ldap_map_verify, NULL, - LDAP_MAX_ATTRMAP) < 0)) { - return -1; - } - - /* - * Check map to see if a password is being retrieved. - * fr_ldap_map_verify ensures that all maps have attributes on the LHS. - * All passwords have a common parent attribute of attr_password - */ - while ((map = map_list_next(&inst->user_map, map))) { - ar = tmpl_attr_tail(map->lhs); - if (ar->da->parent == attr_password) { - inst->expect_password = true; - break; - } - } - } - return 0; error: diff --git a/src/modules/rlm_ldap/rlm_ldap.h b/src/modules/rlm_ldap/rlm_ldap.h index 9ffca4c46c5..c5c85bfeb69 100644 --- a/src/modules/rlm_ldap/rlm_ldap.h +++ b/src/modules/rlm_ldap/rlm_ldap.h @@ -24,14 +24,6 @@ typedef struct { } ldap_acct_section_t; typedef struct { - bool expect_password; //!< True if the user_map included a mapping between an LDAP - //!< attribute and one of our password reference attributes. - - /* - * RADIUS attribute to LDAP attribute maps - */ - map_list_t user_map; //!< Attribute map applied to users and profiles. - /* * Options */ @@ -141,6 +133,11 @@ typedef struct { //!< No value should be set if profiles are not being used //!< as there is an associated performance penalty. fr_value_box_t profile_filter; //!< Filter to use when searching for profiles. + + map_list_t *user_map; //!< Attribute map applied to users and profiles. + + fr_value_box_t const *expect_password; //!< True if the user_map included a mapping between an LDAP + //!< attribute and one of our password reference attributes. } ldap_autz_call_env_t; /** Call environment used in group membership xlat @@ -252,7 +249,7 @@ int rlm_ldap_find_user_async(TALLOC_CTX *ctx, rlm_ldap_t const *inst, request_t ldap_access_state_t rlm_ldap_check_access(rlm_ldap_t const *inst, request_t *request, LDAPMessage *entry); -void rlm_ldap_check_reply(module_ctx_t const *mctx, request_t *request, fr_ldap_thread_trunk_t const *ttrunk); +void rlm_ldap_check_reply(request_t *request, char const *inst_name, bool expect_password, fr_ldap_thread_trunk_t const *ttrunk); /* * groups.c - Group membership functions. diff --git a/src/modules/rlm_ldap/user.c b/src/modules/rlm_ldap/user.c index f763e3cf924..4292625697b 100644 --- a/src/modules/rlm_ldap/user.c +++ b/src/modules/rlm_ldap/user.c @@ -239,22 +239,21 @@ ldap_access_state_t rlm_ldap_check_access(rlm_ldap_t const *inst, request_t *req * * Checks to see if after the LDAP to RADIUS mapping has been completed that a reference password. * - * @param[in] mctx rlm_ldap configuration. - * @param[in] request Current request. - * @param[in] ttrunk the connection thread trunk. + * @param[in] request Current request. + * @param[in] expect_password Whether we should be expecting a password. + * @param[in] ttrunk the connection thread trunk. */ -void rlm_ldap_check_reply(module_ctx_t const *mctx, request_t *request, fr_ldap_thread_trunk_t const *ttrunk) +void rlm_ldap_check_reply(request_t *request, char const *inst_name, bool expect_password, fr_ldap_thread_trunk_t const *ttrunk) { - rlm_ldap_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_ldap_t); - fr_pair_t *parent; + fr_pair_t *parent; - /* + /* * More warning messages for people who can't be bothered to read the documentation. * * Expect_password is set when we process the mapping, and is only true if there was a mapping between * an LDAP attribute and a password reference attribute in the control list. */ - if (!inst->expect_password || !RDEBUG_ENABLED2) return; + if (!expect_password || !RDEBUG_ENABLED2) return; parent = fr_pair_find_by_da_nested(&request->control_pairs, NULL, attr_password); if (!parent) parent = request->control_ctx; @@ -271,9 +270,9 @@ void rlm_ldap_check_reply(module_ctx_t const *mctx, request_t *request, fr_ldap_ RWDEBUG2("!!! - Configure authentication via ntlm_auth (mschapv2 only)"); RWDEBUG2("!!! - Configure authentication via wbclient (mschapv2 only)"); RWDEBUG2("!!! - Bind as the user by listing %s in the authenticate section, and", - mctx->inst->name); + inst_name); RWDEBUG2("!!! setting attribute &control.Auth-Type := '%s' in the authorize section", - mctx->inst->name); + inst_name); RWDEBUG2("!!! (pap only)"); break; @@ -285,9 +284,9 @@ void rlm_ldap_check_reply(module_ctx_t const *mctx, request_t *request, fr_ldap_ RWDEBUG2("!!! - Set 'edir = yes' and enable the universal password feature on your"); RWDEBUG2("!!! eDir server (recommended)"); RWDEBUG2("!!! - Bind as the user by listing %s in the authenticate section, and", - mctx->inst->name); + inst_name); RWDEBUG2("!!! setting attribute &control.Auth-Type := '%s' in the authorize section", - mctx->inst->name); + inst_name); RWDEBUG("!!! (pap only)"); break; @@ -300,9 +299,9 @@ void rlm_ldap_check_reply(module_ctx_t const *mctx, request_t *request, fr_ldap_ RWDEBUG2("!!! \"%s\" has permission to read that password attribute (recommended)", ttrunk->config.admin_identity); RWDEBUG2("!!! - Bind as the user by listing %s in the authenticate section, and", - mctx->inst->name); + inst_name); RWDEBUG2("!!! setting attribute &control.Auth-Type := '%s' in the authorize section", - mctx->inst->name); + inst_name); RWDEBUG2("!!! (pap only)"); } else { RWDEBUG2("!!! No \"known good\" password added"); @@ -312,9 +311,9 @@ void rlm_ldap_check_reply(module_ctx_t const *mctx, request_t *request, fr_ldap_ RWDEBUG2("!!! 'identity' is set to the DN of an account that has permission to read"); RWDEBUG2("!!! that password attribute"); RWDEBUG2("!!! - Bind as the user by listing %s in the authenticate section, and", - mctx->inst->name); + inst_name); RWDEBUG2("!!! setting attribute &control.Auth-Type := '%s' in the authorize section", - mctx->inst->name); + inst_name); RWDEBUG2("!!! (pap only)"); } break;