]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add "caller" keyword
authorAlan T. DeKok <aland@freeradius.org>
Mon, 21 Sep 2020 18:12:49 +0000 (14:12 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 22 Sep 2020 12:10:31 +0000 (08:10 -0400)
this currently only does one level of checks.  It doesn't track
nested "caller" sections.  That requires updates to the
parse_rules structure

doc/antora/modules/unlang/nav.adoc
doc/antora/modules/unlang/pages/caller.adoc [new file with mode: 0644]
doc/antora/modules/unlang/pages/keywords.adoc
src/lib/unlang/all.mk
src/lib/unlang/base.c
src/lib/unlang/caller.c [new file with mode: 0644]
src/lib/unlang/compile.c
src/lib/unlang/unlang_priv.h

index 5a4f89034767711620487ff82fda4807d67a1b92..7e1525a9fa3f69318c0148408e27266596f93f0a 100644 (file)
@@ -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 (file)
index 0000000..61c3d24
--- /dev/null
@@ -0,0 +1,56 @@
+= The caller Statement
+
+.Syntax
+[source,unlang]
+----
+caller <protocol> {
+    [ 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.
+
+<protocol>::  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.
index 02b0a6553e24c51d76050b9ca9f38c3c928a2544..a255a599c7641eb2f6ea4fb9afcb7b261eb5d929 100644 (file)
@@ -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
index 5e4b046f78e7236862bdc37e6a55e4a7cb9d93fc..4ee493350892c5d8e65b5909efaf5317fe663b70 100644 (file)
@@ -2,6 +2,7 @@ TARGET          := libfreeradius-unlang.a
 
 SOURCES        :=      base.c \
                call.c \
+               caller.c \
                compile.c \
                condition.c \
                foreach.c \
index 27529834218c267fed74dc26e794e440ad25ab2e..fec50c7fcfc521b43c8df29cb17f1ef72c60f6a3 100644 (file)
@@ -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 (file)
index 0000000..198219e
--- /dev/null
@@ -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 <freeradius-devel/server/state.h>
+#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
+                          });
+}
index 6664324973782f1766d5061f7ed9281f14e209c1..f34a0235159bbbeb60444d9d8dd707ef92e98fc7 100644 (file)
@@ -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 <protocol> { ... }'");
+               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 },
index 712bd73c916f14f0802237860a50ace7c71ecd33..304f4b29553c481bd1004a1b50625c4d2cf73dae 100644 (file)
@@ -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);