From: Alan T. DeKok Date: Mon, 21 Sep 2020 18:12:49 +0000 (-0400) Subject: add "caller" keyword X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ec2b23d23a39bc2489b113288fbadeab62b7a838;p=thirdparty%2Ffreeradius-server.git add "caller" keyword this currently only does one level of checks. It doesn't track nested "caller" sections. That requires updates to the parse_rules structure --- diff --git a/doc/antora/modules/unlang/nav.adoc b/doc/antora/modules/unlang/nav.adoc index 5a4f8903476..7e1525a9fa3 100644 --- a/doc/antora/modules/unlang/nav.adoc +++ b/doc/antora/modules/unlang/nav.adoc @@ -7,6 +7,7 @@ ** xref:keywords.adoc[Keywords] *** xref:break.adoc[break] *** xref:call.adoc[call] +*** xref:caller.adoc[caller] *** xref:case.adoc[case] *** xref:detach.adoc[detach] *** xref:else.adoc[else] diff --git a/doc/antora/modules/unlang/pages/caller.adoc b/doc/antora/modules/unlang/pages/caller.adoc new file mode 100644 index 00000000000..61c3d246ff4 --- /dev/null +++ b/doc/antora/modules/unlang/pages/caller.adoc @@ -0,0 +1,56 @@ += The caller Statement + +.Syntax +[source,unlang] +---- +caller { + [ statements ] +} +---- + +The `caller` keyword checks the parent request to see if it matches a +particular protocol. If so, it executes the given _[statements]_, If +the parent does not exist, or the parent protocol does not match, the +statements are ignored. + +:: The name of the protocol used by the virtual server which `call`ed this policy. + +[ statements ]:: If the protocol matches, the _[ +statements]_ from inside of the `caller` section will be executed. +These statements will be executed in the context of the current request. + +A virtual server may the the target of multiple xref:call.adoc[call] +keywords, each of which uses a different protocol. For example, a +policy which handles RADIUS `Access-Request` packets may create a DHCP +`Discover` packet in order to perform IP address allocation. The DHCP +`Discover` packet will typically be processed through `DHCPv4` virtual +server. However, that virtual server may need to handle requests +differently based on whether the original packet was RADIUS, or +DHCPv4. + +The `caller` keyword allows a virtual server to check where it is +called from, and to apply different policies based on that +information. The `caller` keywords is in some senses the counter-part +to the xref:call.adoc[call] keyword. + +It may have been possible to use `if` conditions to perform this +check. However, doing so would involve creating additional syntax +which would not have been as clear as using `caller`. + +.Example + +This example copies the `DHCP-Client-Identifer` from the parent DHCPv4 +request to the current RADIUS reply packet, as the contents of the +`Filter-Id` attribute. + +[source,unlang] +---- +caller dhcpv4 { + update reply { + &Filter-Id := &parent.request:DHCP-Client-Identifier + } +} +---- + +// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0. +// Development of this documentation was sponsored by Network RADIUS SAS. diff --git a/doc/antora/modules/unlang/pages/keywords.adoc b/doc/antora/modules/unlang/pages/keywords.adoc index 02b0a6553e2..a255a599c76 100644 --- a/doc/antora/modules/unlang/pages/keywords.adoc +++ b/doc/antora/modules/unlang/pages/keywords.adoc @@ -81,6 +81,7 @@ impossible to create before. |===== | Keyword | Description | xref:call.adoc[call] | Call one virtual server from another +| xref:caller.adoc[caller] | Check if the request is from a parent `call` | xref:detach.adoc[detach] | Detach a child from a parent | xref:parallel.adoc[parallel] | Create multiple parallel child requests | xref:subrequest.adoc[subrequest] | Create a child request diff --git a/src/lib/unlang/all.mk b/src/lib/unlang/all.mk index 5e4b046f78e..4ee49335089 100644 --- a/src/lib/unlang/all.mk +++ b/src/lib/unlang/all.mk @@ -2,6 +2,7 @@ TARGET := libfreeradius-unlang.a SOURCES := base.c \ call.c \ + caller.c \ compile.c \ condition.c \ foreach.c \ diff --git a/src/lib/unlang/base.c b/src/lib/unlang/base.c index 27529834218..fec50c7fcfc 100644 --- a/src/lib/unlang/base.c +++ b/src/lib/unlang/base.c @@ -90,6 +90,7 @@ int unlang_init(void) if (unlang_subrequest_op_init() < 0) return -1; unlang_switch_init(); unlang_call_init(); + unlang_caller_init(); unlang_tmpl_init_shallow(); return 0; diff --git a/src/lib/unlang/caller.c b/src/lib/unlang/caller.c new file mode 100644 index 00000000000..198219ea166 --- /dev/null +++ b/src/lib/unlang/caller.c @@ -0,0 +1,65 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * + * @file unlang/caller.c + * @brief Unlang "caller" keyword evaluation. Used for setting allowed parent protocols + * + * @copyright 2020 The FreeRADIUS server project + */ +RCSID("$Id$") + +#include +#include "unlang_priv.h" +#include "group_priv.h" + +static unlang_action_t unlang_caller(REQUEST *request, rlm_rcode_t *presult) +{ + unlang_stack_t *stack = request->stack; + unlang_stack_frame_t *frame = &stack->frame[stack->depth]; + unlang_t *instruction = frame->instruction; + + unlang_group_t *g; + + g = unlang_generic_to_group(instruction); + fr_assert(g->num_children > 0); /* otherwise the compilation is broken */ + + /* + * No parent, or the dictionaries don't match. Ignore it. + */ + if (!request->parent || (request->parent->dict != g->dict)) { + RDEBUG2("..."); + return UNLANG_ACTION_EXECUTE_NEXT; + } + + /* + * The dictionary matches. Go recurse into its' children. + */ + return unlang_group(request, presult); +} + + +void unlang_caller_init(void) +{ + unlang_register(UNLANG_TYPE_CALLER, + &(unlang_op_t){ + .name = "caller", + .interpret = unlang_caller, + .debug_braces = true + }); +} diff --git a/src/lib/unlang/compile.c b/src/lib/unlang/compile.c index 66643249737..f34a0235159 100644 --- a/src/lib/unlang/compile.c +++ b/src/lib/unlang/compile.c @@ -711,6 +711,7 @@ static void unlang_dump(unlang_t *instruction, int depth) break; case UNLANG_TYPE_CALL: + case UNLANG_TYPE_CALLER: case UNLANG_TYPE_CASE: case UNLANG_TYPE_FOREACH: case UNLANG_TYPE_ELSE: @@ -2865,6 +2866,50 @@ static unlang_t *compile_call(unlang_t *parent, unlang_compile_t *unlang_ctx, CO } +static unlang_t *compile_caller(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs) +{ + unlang_t *c; + unlang_group_t *g; + fr_token_t type; + char const *name; + fr_dict_t const *dict; + + name = cf_section_name2(cs); + if (!name) { + cf_log_err(cs, "You MUST specify a protocol name for 'caller { ... }'"); + return NULL; + } + + type = cf_section_name2_quote(cs); + if (type != T_BARE_WORD) { + cf_log_err(cs, "The argument to 'caller' cannot be a quoted string or a dynamic value"); + return NULL; + } + + dict = fr_dict_by_protocol_name(name); + if (!dict) { + cf_log_err(cs, "Unknown protocol '%s'", name); + return NULL; + } + + c = compile_section(parent, unlang_ctx, cs, UNLANG_TYPE_CALLER); + if (!c) return NULL; + + /* + * Set the virtual server name, which tells unlang_call() + * which virtual server to call. + */ + g = unlang_generic_to_group(c); + g->dict = dict; + + if (!g->num_children) { + talloc_free(c); + return UNLANG_IGNORE; + } + + return c; +} + static unlang_t *compile_function(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM *ci, CONF_SECTION *subcs, rlm_components_t component, bool policy) @@ -3110,6 +3155,7 @@ typedef unlang_t *(*unlang_op_compile_t)(unlang_t *parent, unlang_compile_t *unl static fr_table_ptr_sorted_t unlang_section_keywords[] = { { L("call"), (void *) compile_call }, + { L("caller"), (void *) compile_caller }, { L("case"), (void *) compile_case }, { L("else"), (void *) compile_else }, { L("elsif"), (void *) compile_elsif }, diff --git a/src/lib/unlang/unlang_priv.h b/src/lib/unlang/unlang_priv.h index 712bd73c916..304f4b29553 100644 --- a/src/lib/unlang/unlang_priv.h +++ b/src/lib/unlang/unlang_priv.h @@ -75,6 +75,7 @@ typedef enum { UNLANG_TYPE_SUBREQUEST, //!< create a child subrequest UNLANG_TYPE_DETACH, //!< detach a child UNLANG_TYPE_CALL, //!< call another virtual server + UNLANG_TYPE_CALLER, //!< conditionally check parent dictionary type UNLANG_TYPE_POLICY, //!< Policy section. UNLANG_TYPE_XLAT, //!< Represents one level of an xlat expansion. UNLANG_TYPE_TMPL, //!< asynchronously expand a tmpl_t @@ -354,6 +355,8 @@ REQUEST *unlang_io_subrequest_alloc(REQUEST *parent, fr_dict_t const *namespace */ void unlang_call_init(void); +void unlang_caller_init(void); + void unlang_condition_init(void); void unlang_foreach_init(void);