]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Switch LDAP map compilation to using call envs too
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 25 Nov 2023 02:37:39 +0000 (20:37 -0600)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 25 Nov 2023 02:37:57 +0000 (20:37 -0600)
src/modules/rlm_cache/rlm_cache.c
src/modules/rlm_ldap/rlm_ldap.c
src/modules/rlm_ldap/rlm_ldap.h
src/modules/rlm_ldap/user.c

index 45be49e0a7d03a1532c2d9d631ffdb7835f2daf9..1b9e3245b5faa6125f187633f6621c38c0032c0d 100644 (file)
@@ -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;
 }
index 42050b1c8e67239a17564fa12aaf8d8fc7027eae..f9113a2e2ba1d3fcde3fd3043ab0c56c4d20b299 100644 (file)
@@ -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:
index 9ffca4c46c5ea9df40f11ea1c9e807fe87db42b0..c5c85bfeb6929696e643030813c82eb1505685d1 100644 (file)
@@ -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.
index f763e3cf924b7847c0d3b4e2a23009606d78b874..4292625697b21083684f55d0183dd4e8384bb401 100644 (file)
@@ -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;