diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 14:11:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 14:11:00 +0000 |
commit | af754e596a8dbb05ed8580c342e7fe02e08b28e0 (patch) | |
tree | b2f334c2b55ede42081aa6710a72da784547d8ea /doc/antora/modules/unlang | |
parent | Initial commit. (diff) | |
download | freeradius-b95e0cd7685e6bbc2465b1f4efe1884df2f4ef01.tar.xz freeradius-b95e0cd7685e6bbc2465b1f4efe1884df2f4ef01.zip |
Adding upstream version 3.2.3+dfsg.upstream/3.2.3+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
51 files changed, 3440 insertions, 0 deletions
diff --git a/doc/antora/modules/unlang/.gitignore b/doc/antora/modules/unlang/.gitignore new file mode 100644 index 0000000..c5722d7 --- /dev/null +++ b/doc/antora/modules/unlang/.gitignore @@ -0,0 +1 @@ +!*.adoc diff --git a/doc/antora/modules/unlang/nav.adoc b/doc/antora/modules/unlang/nav.adoc new file mode 100644 index 0000000..77be328 --- /dev/null +++ b/doc/antora/modules/unlang/nav.adoc @@ -0,0 +1,51 @@ +* xref:index.adoc[Unlang Policy Language] + +** xref:list.adoc[Attribute Lists] +** xref:attr.adoc[Attribute References] +** xref:return_codes.adoc[Return Codes] + +** xref:keywords.adoc[Keywords] +*** xref:break.adoc[break] +*** xref:case.adoc[case] +*** xref:else.adoc[else] +*** xref:elsif.adoc[elsif] +*** xref:foreach.adoc[foreach] +*** xref:group.adoc[group] +*** xref:if.adoc[if] +*** xref:load-balance.adoc[load-balance] +*** xref:redundant-load-balance.adoc[redundant-load-balance] +*** xref:redundant.adoc[redundant] +*** xref:return.adoc[return] +*** xref:switch.adoc[switch] +*** xref:update.adoc[update] + +** xref:module.adoc[Modules] +*** xref:module_method.adoc[Module Methods] +*** xref:module_builtin.adoc[Built-in Modules] + +** xref:type/index.adoc[Data Types] +*** xref:type/index.adoc[List of Data Types] +*** xref:type/ip.adoc[IP Addresses] +*** xref:type/numb.adoc[Numbers] +*** xref:type/string/single.adoc[Single Quoted Strings] +*** xref:type/string/double.adoc[Double Quoted Strings] +*** xref:type/string/backticks.adoc[Backtick-quoted string] +*** xref:type/string/unquoted.adoc[Unquoted Strings] + +** xref:condition/index.adoc[Conditional Expressions] +*** xref:condition/cmp.adoc[Comparisons] +*** xref:condition/operands.adoc[Operands] +*** xref:condition/return_code.adoc[The Return Code Operator] +*** xref:condition/eq.adoc[The '==' Operator] +*** xref:condition/and.adoc[The '&&' Operator] +*** xref:condition/or.adoc[The '||' Operator] +*** xref:condition/not.adoc[The '!' Operator] +*** xref:condition/para.adoc[The '( )' Operator] +*** xref:condition/regex.adoc[Regular Expressions] + +** xref:xlat/index.adoc[String Expansion] +*** xref:xlat/alternation.adoc[Alternation Syntax] +*** xref:xlat/builtin.adoc[Built-in Expansions] +*** xref:xlat/character.adoc[Single Letter Expansions] +*** xref:xlat/attribute.adoc[Attribute References] +*** xref:xlat/module.adoc[Module References] diff --git a/doc/antora/modules/unlang/pages/attr.adoc b/doc/antora/modules/unlang/pages/attr.adoc new file mode 100644 index 0000000..70afce4 --- /dev/null +++ b/doc/antora/modules/unlang/pages/attr.adoc @@ -0,0 +1,77 @@ += &Attribute References + +.Syntax +[source,unlang] +---- +[&]Attribute-Name +---- + +The `&Attribute-Name` operator returns a reference to the named +attribute. + +When used as an existence check in a condition, the condition +evaluates to `true` if the attribute exists. Otherwise, the condition +evaluates to `false`. + +When used elsewhere, such as in xref:switch.adoc[switch], it returns +the value of the named attribute. + +.Examples +[source,unlang] +---- +&User-Name +&NAS-IP-Address +---- + +== Lists + +The attribute reference can also be qualified with a +xref:list.adoc[list] reference. When no list is given, the server +looks in the input packet list for the named attribute. + +.Examples + +[source,unlang] +---- +&request:User-Name +&reply:NAS-IP-Address +---- + +== Array References + +.Syntax +[source,unlang] +---- +&Attribute-Name[<integer>] +---- + +When an attribute appears multiple times in a list, this syntax allows +you to address the attributes as if they were array entries. The +`<integer>` value defines which attribute to address. The `[0]` value +refers to the first attributes, `[1]` refers to the second attribute, +etc. + +.Examples +[source,unlang] +---- +&EAP-Message[1] +&reply:NAS-IP-Address[2] +---- + +== Removing ambuguity from the configuration files + +The server does not use the `&` character to distinguish attribute names +from other strings. + +Without the `&`, it is more difficult to parse the configuration file +clearly. You could interpret a string as `hello-there` +either as a literal string "hello-there", or as a reference to an +attribute named `hello-there`. + +Adding the leading `&` character means that attribute references are +now easily distinguishable from literal strings. The use of the leading +`&` character is highly recommended. + + +// 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/break.adoc b/doc/antora/modules/unlang/pages/break.adoc new file mode 100644 index 0000000..01783ea --- /dev/null +++ b/doc/antora/modules/unlang/pages/break.adoc @@ -0,0 +1,28 @@ += The break statement + +.Syntax +[source,unlang] +---- +break +---- + +The `break` statement is used to exit an enclosing +xref:foreach.adoc[foreach] loop. The `break` statement only be used +inside of a xref:foreach.adoc[foreach] loop. + +.Example +[source,unlang] +---- +foreach &Class { + if (&Foreach-Variable-0 == 0xabcdef) { + break + } + + update reply { + Reply-Message += "Contains %{Foreach-Variable-0}" + } +} +---- + +// 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/case.adoc b/doc/antora/modules/unlang/pages/case.adoc new file mode 100644 index 0000000..ba2b5fe --- /dev/null +++ b/doc/antora/modules/unlang/pages/case.adoc @@ -0,0 +1,44 @@ += The case Statement + +.Syntax +[source,unlang] +---- +case [ <match> ] { + [ statements ] +} +---- + +The `case` statement is used to match data inside of a +xref:switch.adoc[switch] statement. The `case` statement cannot be used +outside of a xref:switch.adoc[switch] statement. + + +The `<match>` text can be an attribute reference such as `&User-Name`, +or it can be a xref:type/string/index.adoc[string]. If the match +text is a dynamically expanded string, then the match is performed on +the output of the string expansion. + +If no `<match>` text is given, it means that the `case` statement is +the "default" and will match all which is not matched by another +`case` statement inside of the same xref:switch.adoc[switch]. + +.Example +[source,unlang] +---- +switch &User-Name { + case "bob" { + reject + } + + case &Filter-Id { + reject + } + + case { + ok + } +} +---- + +// 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/condition/and.adoc b/doc/antora/modules/unlang/pages/condition/and.adoc new file mode 100644 index 0000000..50b3deb --- /dev/null +++ b/doc/antora/modules/unlang/pages/condition/and.adoc @@ -0,0 +1,21 @@ += The && Operator + +.Syntax +[source,unlang] +---- +(condition-1 && condition-2) +---- + +The `&&` operator performs a short-circuit "and" evaluation of the +two conditions. This operator evaluates _condition-1_ and returns +`false` if _condition-1_ returns `false`. Only if _condition-1_ +returns `true` is _condition-2_ evaluated and its result returned. + +.Examples +[source,unlang] +---- +if (&User-Name && &EAP-Message) { ... +---- + +// 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/condition/cmp.adoc b/doc/antora/modules/unlang/pages/condition/cmp.adoc new file mode 100644 index 0000000..4138b86 --- /dev/null +++ b/doc/antora/modules/unlang/pages/condition/cmp.adoc @@ -0,0 +1,104 @@ += Comparisons + +.Syntax +[source,unlang] +---- +lhs OP rhs +---- + +The most common use-case for conditions is to perform comparisons. +The `lhs` and `rhs` of a conditional comparison can be +xref:attr.adoc[&Attribute-Name] or xref:type/index.adoc[data]. The +the `OP` is an operator, commonly `==` or `\<=`. It is used to +control how the two other portions of the condition are compared. + +== The Comparison Operators + +The comparison operators are given below. + +[options="header"] +|===== +| Operator | Description +| < | less than +| \<= | less than or equals +| == | equals +| != | not equals +| >= | greater than or equals +| > | greater than +| xref:condition/regex.adoc[=~] | regular expression matches +| xref:condition/regex.adoc[!~] | regular expression does not match +|===== + +The comparison operators perform _type-specific_ comparisons. The +only exceptions are the xref:condition/regex.adoc[regular expression] operators, +which interpret the `lhs` as a printable string, and the `rhs` as a +regular expression. + +== IP Address Comparisons + +The type-specific comparisons operate as expected for most data types. +The only exception is data types that are IP addresses or IP address +prefixes. For those data types, the comparisons are done via the +following rules: + +* Any unqualified IP address is assumed to have a /32 prefix (IPv4) + or a /128 prefix (IPv6). + +* If the prefixes of the left and right sides are equal, then the comparisons + are performed on the IP address portion. + +* If the prefixes of the left and right sides are not equal, then the + comparisons are performed as _set membership checks_. + +The syntax allows conditions such as `192.0.2.1 < 192.0.2/24`. This +condition will return `true`, as the IP address `192.0.2.1' is within +the network `192.0.2/24`. + +== Casting + +In some situations, it is useful to force the left side to be +interpreted as a particular data type. + +[NOTE] +The data types used by the cast *must* be a type taken from the RADIUS +dictionaries, e.g., `ipaddr`, `integer`, etc. These types are not the +same as the xref:type/index.adoc[data types] used in the +configuration files. + +.Syntax +[source,unlang] +---- +<cast>lhs OP rhs +---- + +The `cast` text can be any one of the standard RADIUS dictionary data +types, as with the following example: + +.Example +[source,unlang] +---- +<ipaddr>&Class == 127.0.0.1 +---- + +In this example, the `Class` attribute is treated as if it was an IPv4 +address and is compared to the address `127.0.0.1` + +Casting is most useful when the left side of a comparison is a +dynamically expanded string. The cast ensures that the comparison is +done in a type-safe manner, instead of performing a string comparison. + +.Example +[source,unlang] +---- +<integer>`/bin/echo 00` == 0 +---- + +In this example, the string output of the `echo` program is interpreted as an +integer. It is then compared to the right side via integer +comparisons. Since the integer `00` is equivalent to the integer `0`, +the comparison will match. If the comparison had been performed via +string equality checks, then the comparison would fail, because the +strings `00` and `0` are different. + +// 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/condition/eq.adoc b/doc/antora/modules/unlang/pages/condition/eq.adoc new file mode 100644 index 0000000..d9e51f3 --- /dev/null +++ b/doc/antora/modules/unlang/pages/condition/eq.adoc @@ -0,0 +1,30 @@ += The == Operator + +.Syntax +`(data-1 == data-2)` + +The `==` operator compares the result of evaluating `data-1` and +`data-2`. As discussed in xref:type/index.adoc[Data Types], the `data-1` +field may be interpreted as a reference to an attribute. + +The `data-2` field is interpreted in a type-specific manner. For +example, if `data-1` refers to an attribute of type `ipaddr`, then +`data-2` is evaluated as an IP address. If `data-1` refers to an +attribute of type `integer`, then `data-2` is evaluated as an integer +or as a named enumeration defined by a `VALUE` statement in a +dictionary. Similarly, if `data-1` refers to an attribute of type +`date`, then `data-2` will be interpreted as a date string. + +If the resulting data evaluates to be the same, then the operator +returns `true`; otherwise, it returns `false`. + +.Example +[source,unlang] +---- +if (User-Name == "bob") { + ... +} +---- + +// 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/condition/index.adoc b/doc/antora/modules/unlang/pages/condition/index.adoc new file mode 100644 index 0000000..b9d9d5f --- /dev/null +++ b/doc/antora/modules/unlang/pages/condition/index.adoc @@ -0,0 +1,85 @@ += Conditional Expressions + +Conditions are evaluated when parsing xref:if.adoc[if] and +xref:elsif.adoc[elsif] statements. These conditions allow the server to +make complex decisions based on one of a number of possible criteria. + +.Syntax +[source,unlang] +---- +if ( condition ) { ... + +elsif ( condition ) { ... +---- + +Conditions are expressed using the following syntax: + +[options="header"] +|===== +| Syntax | Description +| xref:attr.adoc[&Attribute-Name] | Check for attribute existence. +| xref:condition/return_code.adoc[rcode] | Check return code of a previous module. +| xref:condition/operands.adoc[data] | Check value of data. +| xref:condition/cmp.adoc[lhs OP rhs] | Compare two kinds of data. +| xref:condition/para.adoc[( condition )] | Check sub-condition +| xref:condition/not.adoc[! condition] | Negate a conditional check +| xref:condition/and.adoc[( condition ) && ...] | Check a condition AND the next one +| xref:condition/or.adoc[( condition ) \|\| ...] | Check a condition OR the next one +|===== + + +.Examples +[source,unlang] +---- +if ( &User-Name == "bob" ) { + ... +} + +if ( &Framed-IP-Address == 127.0.0.1 ) { + ... +} + +if ( &Calling-Station-Id == "%{sql:SELECT ...}" ) { + ... +} +---- + +== Load-time Syntax Checks + +The server performs a number of checks when it loads the configuration +files. Unlike version 2, all of the conditions are syntax checked +when the server loads. This checking greatly aids in creating +configurations that are correct. Where the configuration is +incorrect, a descriptive error is produced. + +This error contains the filename and line number of the syntax error. +In addition, it will print out a portion of the line that caused the +error and will point to the exact character where the error was seen. +These descriptive messages mean that most errors are easy to find and fix. + +== Load-time Optimizations + +The server performs a number of optimizations when it loads the +configuration files. Conditions that have static values are +evaluated and replaced with the result of the conditional comparison. + +.Example +[source,unlang] +---- +if ( 0 == 1 ) { + ... +} +---- + +The condition `0 == 1` is static and will evaluate to `false`. Since +it evaluates to `false`, the configuration inside of the `if` +statement is ignored. Any modules referenced inside of the `if` +statement will not be loaded. + +This optimization is most useful for creating configurations that +selectively load (or not) certain policies. If the condition above +was used in version 2, then the configuration inside of the `if` statement +would be loaded, even though it would never be used. + +// 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/condition/not.adoc b/doc/antora/modules/unlang/pages/condition/not.adoc new file mode 100644 index 0000000..bde038e --- /dev/null +++ b/doc/antora/modules/unlang/pages/condition/not.adoc @@ -0,0 +1,19 @@ += The ! Operator + +.Syntax +[source,unlang] +---- +! condition +---- + +The `!` operator negates the result of the following condition. It +returns `true` when _condition_ returns `false`. It returns `false` +when _condition_ returns `true`. + +.Examples + +`(! (foo == bar))` + +`! &User-Name` + +// 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/condition/operands.adoc b/doc/antora/modules/unlang/pages/condition/operands.adoc new file mode 100644 index 0000000..4a2d00b --- /dev/null +++ b/doc/antora/modules/unlang/pages/condition/operands.adoc @@ -0,0 +1,37 @@ += Operands + +.Syntax +[source,unlang] +---- +string +integer +"double-quoted string" +'single-quoted string' +`back-quoted string` +---- + +Any text not matching xref:attr.adoc[&Attribute-Name] or +xref:condition/return_code.adoc[Return Code] is interpreted as a value for a +particular xref:type/index.adoc[data type]. + +Double-quoted strings and back-quoted strings are dynamically expanded +before the condition is evaluated. Single-quoted strings are static +literals and are not dynamically expanded. + +When used as an existence check, the condition evaluates to `true` if +the data is non-zero. Otherwise, the condition evaluates to `false`. + +For integer existence checks, `0` is `false`; all other values are `true`. + +For string existence checks, an empty string is `false`. All other +strings are `true`. + +All other data types are disallowed in existence checks. + +.Examples + +`"hello there"` + +`5` + +// 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/condition/or.adoc b/doc/antora/modules/unlang/pages/condition/or.adoc new file mode 100644 index 0000000..80c2cb4 --- /dev/null +++ b/doc/antora/modules/unlang/pages/condition/or.adoc @@ -0,0 +1,21 @@ += The || Operator + +.Syntax +[source,unlang] +---- +(expression-1 || expression-2) +---- + +The `||` operator performs a short-circuit "or" evaluation of the two +expressions. This operator evaluates _condition-1_ and returns `true` +if _condition-1_ returns true. Only if _condition-1_ returns `false` +is _condition-2_ evaluated and its result returned. + +.Examples +[source,unlang] +---- +if (&User-Name || &NAS-IP-Address) { ... +---- + +// 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/condition/para.adoc b/doc/antora/modules/unlang/pages/condition/para.adoc new file mode 100644 index 0000000..bdb3f01 --- /dev/null +++ b/doc/antora/modules/unlang/pages/condition/para.adoc @@ -0,0 +1,19 @@ += The ( ) Operator + +.Syntax +[source,unlang] +---- +( condition ) +---- + +The `( )` operator returns the result of evaluating the given +`condition`. It is used to clarify policies or to explicitly define +conditional precedence. + +.Examples + +`(foo)` + +`(bar || (baz && dub))` + +// 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/condition/regex.adoc b/doc/antora/modules/unlang/pages/condition/regex.adoc new file mode 100644 index 0000000..038faa6 --- /dev/null +++ b/doc/antora/modules/unlang/pages/condition/regex.adoc @@ -0,0 +1,180 @@ += Regular Expressions + +.Syntax +==== +[source,unlang] +---- +(<subject> =~ /<pattern>/) +(<subject> =~ /<pattern>/[imsux]) + +(<subject> !~ /<pattern>/) +(<subject> !~ /<pattern>/[imsux]) +---- +==== + +== Matching +The regular expression operators perform regular expression matching +on the data. The `<subject>` field can be an attribute reference or data, +as with the other xref:condition/cmp.adoc[comparison] operators. The `/<pattern>/` +field must be a valid regular expression. + +The `=~` operator evaluates to `true` when `data` matches the +`/<pattern>/`. Otherwise, it evaluates to `false`. + +The `!~` operator evaluates to `true` when `data` does not match the +`/<pattern>/`. Otherwise, it evaluates to `true`. + +The regular expression comparison is performed on the _string representation_ +of the left side of the comparison. That is, if the left side is an +xref:type/numb.adoc[integer], the regular expression will behave is if the +value `0` was the literal string `"0"`. Similarly, if the left side is an +xref:attr.adoc[&Attribute-Name], then the regular expression will behave is if +the attribute was printed to a string, and the match was performed on the +resulting string. + +.Checking if the `User-Name` attribute contains a realm of example.com +==== +[source,unlang] +---- +if (&User-Name =~ /@example\.com$/) { + ... +} +---- +==== + +== Dialects + +The syntax of the regular expression is defined by the regular +expression library available on the local system. + +FreeRADIUS currently supports: + +* link:https://www.pcre.org/original/doc/html/[libpcre] and +link:https://www.pcre.org/current/doc/html/[libpcre2] both of which +provide +link:https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions[Perl +Compatible Regular expressions]. +* Regex support provided by the local libc implementation, usually +link:http://en.wikipedia.org/wiki/Regular_expression#POSIX_basic_and_extended[ +Posix regular expressions]. + +[TIP] +==== +Use the output of `radiusd -Xxv` to determine which regular expression library is in use. + +.... +... +Debug : regex-pcre : no +Debug : regex-pcre2 : yes +Debug : regex-posix : no +Debug : regex-posix-extended : no +Debug : regex-binsafe : yes +... +Debug : pcre2 : 10.33 (2019-04-16) - retrieved at build time +.... +==== + +[WARNING] +==== +Depending on the regular expression library or libc implementation the server +was built against, the pattern matching function available may not be binary +safe (see `regex-binsafe` in the output of `radiusd -Xxv`). + +If a binary safe regex match function is not available, and a match is +attempted against a subject that contains one or more `NUL` ('\0') bytes, the +match will be aborted, any condition that uses the result will evaluate to false, +and a warning will be emitted. +==== + +== Flags + +The regular expression `/<pattern>/` may be followed by one or more flag +characters. Again, which flags are available depends on the regular expression +library the server was built with. Multiple flags may be specified per +`/pattern/`. + +.Flags and their uses + +[options="header"] +|===== +| Flag Character | Available with | Effect +| `i` | All | Enable case-insensitive matching. +| `m` | All | '^' and '$' match newlines within the subject. +| `s` | libpcre[2] | '.' matches anything, including newlines. +| `u` | libpcre[2] | Treats subjects as UTF8. Invalid UTF8 + sequences will result in the match failing. + |`x` | libpcre[2] | Allows comments in expressions by ignoring + whitespace, and text between '#' and the next + newline character. +|===== + +== Subcapture groups + +When the `=~` or `!~` operators are used, then parentheses in the regular +expression will sub capture groups, which contain part of the subject string. + +The special expansion `%{0}` expands to the portion of the subject that +matched. The expansions + +`%{1}`..`%{32}` expand to the contents of any subcapture groups. + +When using libpcre[2], named capture groups may also be accessed using the +built-in expansion + +`%{regex:<named capture group>}`. + +Please see the xref:xlat/builtin.adoc#_0_32[xlat documentation] for +more information on regular expression matching. + +.Extracting the 'user' portion of a realm qualified string +==== +[source,unlang] +---- +if (&User-Name =~ /^(.*)@example\.com$/) { + update reply { + Reply-Message := "Hello %{1}" + } +} +---- +==== + +== Pre-Compiled vs Runtime Compiled + +When the server starts any regular expressions comparisons it finds will be +pre-compiled, and if support is available, JIT'd (converted to machine code) +to ensure fast execution. + +If a pattern contains a xref:xlat/index.adoc[string expansion], the pattern +cannot be compiled on startup, and will be compiled at runtime each time the +expression is evaluated. The server will also turn off JITing for runtime +compiled expressions, as the overhead is greater than the time that would be +saved during evaluation. + +.A runtime compiled regular expression +==== +[source,unlang] +---- +if (&User-Name =~ /^@%{Tmp-String-0}$/) { + ... +} +---- +==== + +To ensure optimal performance you should limit the number of patterns +containing xref:xlat/index.adoc[string expansions], and if using PCRE, combine +multiple expressions operating on the same subject into a single expression +using the PCRE alternation '|' operator. + +.Using multiple string expansions and the PCRE alternation operator +==== +[source,unlang] +---- +if (&User-Name =~ /^@(%{Tmp-String-0}|%{Tmp-String-1})$/) { + ... +} +---- +==== + + +// Licenced under CC-by-NC 4.0. +// Copyright (C) 2020 Network RADIUS SAS. +// Copyright (C) 2019 Arran Cudbard-Bell <a.cudbardb@freeradius.org> +// Development of this documentation was sponsored by Network RADIUS SAS. diff --git a/doc/antora/modules/unlang/pages/condition/return_codes.adoc b/doc/antora/modules/unlang/pages/condition/return_codes.adoc new file mode 100644 index 0000000..ebc49ed --- /dev/null +++ b/doc/antora/modules/unlang/pages/condition/return_codes.adoc @@ -0,0 +1,35 @@ += The return code Operator + +.Syntax +[source,unlang] +---- +rcode +---- + +The Unlang interpreter tracks the return code of any module, string expansion +or keyword that has been called. + +This return code can be checked in any condition. If the saved return code +matches the `code` given here, then the condition evaluates to `true`. +Otherwise, it evaluates to `false`. + +rcodes cannot be set in a condition. rcodes cannot be compared with anything else. + +The list of valid return codes is as follows: + +.Return Codes + +include::partial$rcode_table.adoc[] + +.Examples + +[source,unlang] +---- +sql +if (notfound) { + ... +} +---- + +// 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/default.adoc b/doc/antora/modules/unlang/pages/default.adoc new file mode 100644 index 0000000..3b298f6 --- /dev/null +++ b/doc/antora/modules/unlang/pages/default.adoc @@ -0,0 +1,47 @@ += The case Statement + +.Syntax +[source,unlang] +---- +case [ <match> ] { + [ statements ] +} +---- + +The `case` statement is used to match data inside of a +xref:switch.adoc[switch] statement. The `case` statement cannot be used +outside of a xref:switch.adoc[switch] statement. + + +The `<match>` text can be an attribute reference such as `&User-Name`, +or it can be a xref:type/string/index.adoc[string]. If the match +text is a dynamically expanded string, then the match is performed on +the output of the string expansion. + +The keyword `default` can be used to specify the default action to +take inside of a xref:switch.adoc[switch] statement. + +If no `<match>` text is given, it means that the `case` statement is +the "default" and will match all which is not matched by another +`case` statement inside of the same xref:switch.adoc[switch]. + +.Example +[source,unlang] +---- +switch &User-Name { + case "bob" { + reject + } + + case &Filter-Id { + reject + } + + default { + ok + } +} +---- + +// 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/else.adoc b/doc/antora/modules/unlang/pages/else.adoc new file mode 100644 index 0000000..a795d0e --- /dev/null +++ b/doc/antora/modules/unlang/pages/else.adoc @@ -0,0 +1,30 @@ += The else Statement + +.Syntax +[source,unlang] +---- +if (condition) { + [ statements ] +} +else { + [ statements ] +} +---- + +An xref:if.adoc[if] statement can have an `else` clause. If _condition_ +evaluates to `false`, the statements in the xref:if.adoc[if] subsection are skipped +and the statements within the `else` subsection are executed. + +.Example +[source,unlang] +---- +if (&User-Name == "bob") { + reject +} +else { + ok +} +---- + +// 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/elsif.adoc b/doc/antora/modules/unlang/pages/elsif.adoc new file mode 100644 index 0000000..ff5799c --- /dev/null +++ b/doc/antora/modules/unlang/pages/elsif.adoc @@ -0,0 +1,43 @@ += The elsif Statement + +.Syntax +[source,unlang] +---- +if (condition-1) { + [ statements-1 ] +} +elsif (condition-2) { + [ statements-2 ] +} +else { + [ statements-3 ] +} +---- + +An `elsif` statement is used to evaluate a subsequent +xref:condition/index.adoc[condition] after a preceding xref:if.adoc[if] statement +evaluates to `false`. In the example above, when _condition-1_ +evaluates to false, then _statements-1_ are skipped and _condition-2_ +is checked. When _condition-2_ evaluates true, then _statements-2_ +are executed. When _condition-2_ evaluates false, then +_statements-2_ are skipped and _statements-3_ are executed. + +As with xref:if.adoc[if], an `elsif` clause does not need to be followed by +an xref:else.adoc[else] statement. However, any xref:else.adoc[else] statement +must be the last statement in an `elsif` chain. An arbitrary number of +`elsif` statements can be chained together to create a series of +conditional checks and statements. + +.Example +[source,unlang] +---- +if (&User-Name == "bob") { + reject +} +elsif (&User-Name == "doug") { + ok +} +---- + +// 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/foreach.adoc b/doc/antora/modules/unlang/pages/foreach.adoc new file mode 100644 index 0000000..6ed3ddf --- /dev/null +++ b/doc/antora/modules/unlang/pages/foreach.adoc @@ -0,0 +1,40 @@ += The foreach Statement + +.Syntax +[source,unlang] +---- +foreach <attribute-reference> { + [ statements ] +} +---- + +The `foreach` statement loops over a set of attributes as given by +`<attribute-reference>`. The loop can be exited early by using the +xref:break.adoc[break] keyword. + +<attribute-reference>:: + +The xref:attr.adoc[attribute reference] which will will be looped +over. The reference can be to one attribute, to an array, a child, or +be a subset. + +Inside of the `foreach` block, the attribute that is being looped over +can be referenced as `Foreach-Variable-0`, through +`Foreach-Variable-9`. The last digit is the depth of the loop, +starting at "0". The loops can be nested up to eight (8) deep, though +this is not recommended. + +The attributes being looped over cannot be modified or deleted. + +.Example +[source,unlang] +---- +foreach &Class { + update reply { + Reply-Message += "Contains %{Foreach-Variable-0}" + } +} +---- + +// 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/group.adoc b/doc/antora/modules/unlang/pages/group.adoc new file mode 100644 index 0000000..98801fd --- /dev/null +++ b/doc/antora/modules/unlang/pages/group.adoc @@ -0,0 +1,39 @@ += The group Statement + +.Syntax +[source,unlang] +---- +group { + [ statements ] +} +---- + +The `group` statement collects a series of statements into a list. +The default processing sections of the server (`authorize`, +`accounting`, etc.) are also `group` statements. Those sections are +given different name for management reasons, but they behave +internally exactly like a `group`. + +[ statements ]:: The `unlang` commands which will be executed. + +All of the statements inside of the `group` are executed in sequence. +The `group` statement is not normally used, as the statements within +it can just be placed inside of the enclosing section. However, the +`group` statement is included in the `unlang` syntax for completeness. + +.Examples + +[source,unlang] +---- +group { + sql + ldap + file + if (updated) { + ... + } +} +---- + +// 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/if.adoc b/doc/antora/modules/unlang/pages/if.adoc new file mode 100644 index 0000000..ea549ef --- /dev/null +++ b/doc/antora/modules/unlang/pages/if.adoc @@ -0,0 +1,29 @@ += The if Statement + +.Syntax +[source,unlang] +---- +if (condition) { + [ statements ] +} +---- + +.Description +The `if` statement evaluates a xref:condition/index.adoc[condition]. When the +_condition_ evaluates to `true`, the statements within the subsection +are executed. When the _condition_ evaluates to `false`, those +statements are skipped. + +An `if` statement can optionally be followed by an xref:else.adoc[else] or +an xref:elsif.adoc[elsif] statement. + +.Example +[source,unlang] +---- +if (&User-Name == "bob") { + reject +} +---- + +// 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/index.adoc b/doc/antora/modules/unlang/pages/index.adoc new file mode 100644 index 0000000..fc812f8 --- /dev/null +++ b/doc/antora/modules/unlang/pages/index.adoc @@ -0,0 +1,162 @@ += Unlang Policy Language + +The server supports a simple processing language called "Unlang", +which is short for "un-language". The original intention of using an +"un-language" was to avoid creating yet another programming language. +Instead, the `unlang` syntax allows for simple _if / then / else_ +checks, and attribute editing. It does not support recursion, +subroutines, or more complex flow controls. + +Where more complicated functionality is required, we recommend using +the `lua`, `perl` or `python` modules. Those modules allow the insertion of +full-featured scripts at any point in the packet processing. + +NOTE: The documentation is this directory is _reference_ +documentation. That is, it describes the syntax of `unlang` keywords, +using minimal examples. The reference documentation does not, +however, describe _when_ to use the keywords, or _how_ to create +policies. Please see the xref:howto:index.adoc[howto] directory for +more in-depth "how to" guides. + +The documentation is organized so that each item is on its own page. +The page beings with a description of the item, followed by some text +explaining what the item does. The page concludes with one or more +examples of using the item in `unlang` policies. + +The `unlang` processing can be split into some high-level concepts. + +== Keywords + +xref:keywords.adoc[Keywords], which are the basic statements of the +language, e.g., xref:load-balance.adoc[load-balance], +xref:if.adoc[if], xref:else.adoc[else], etc. + +.Example +[source,unlang] +---- +load-balance { + sql1 + sql2 + sql3 +} +---- + +== Conditional Expressions + +xref:condition/index.adoc[Conditional expressions], which are used to check +if conditions evaluate to _true_ or _false_. Conditional expressions +can be used to control the flow of processing. + +.Example +[source,unlang] +---- +if ((&User-Name == "bob") && (&Calling-Station-Id == "00:01:03:04:05")) { + ... +} +---- + +== Update Statements + +xref:update.adoc[update] statements are used to edit attributes and +lists of attributes. + +Most request packets will result in reply packets that contain +additional information for the requestor. The `update` section allows +policies to add attributes to requests, replies, or to other places. + +.Example +[source,unlang] +---- +update reply { + &Session-Timeout := 3600 + &Framed-IP-Address := 192.0.2.4 +} +---- + +== String Expansions + +xref:xlat/index.adoc[String expansion] Using `%{...}` to perform dynamic +string expansions. (also known as xref:xlat/index.adoc[xlat]) + +String expansions are usually performed in order to get additional +information which is not immediately available to the policy. This +information can be taken from almost any source, including other +attributes, databases, and scripts. + +.Example +[source,unlang] +---- +update reply { + &Framed-IP-Address := "%{sql:SELECT static_ip from table WHERE user = '%{User-Name}'}" +} +---- + +== Data Types + +Each attribute used by the server has an associated +xref:type/index.adoc[data type]. The `unlang` interpreter enforces +restrictions on assignments, so that only valid data types can be +assigned to an attribute. Invalid assignments result in a run-time +error. + +.Example +[source,unlang] +---- +update reply { + &Framed-IP-Address := 192.0.2.4 + &Session-Timeout := 5 + &Reply-Message := "hello" +} +---- + +== Design Goals of Unlang + +The goal of `unlang` is to allow simple policies to be written with +minimal effort. Conditional checks can be performed by the policies, +which can then update the request or response attributes based on the +results of those checks. `unlang` can only be used in a processing +section, it cannot be used anywhere else, including in configuration +sections for a client or a module. The reason for this limitation is +that the language is intended to perform specific actions on requests +and responses. The client and module sections contain definitions for +a client or module; they do not define how a request is processed. + +`unlang` uses the same the basic syntax as the configuration files. +The syntax of the configuration file for lines, comments, sections, +sub-section, etc., all apply to `unlang`. + +Where `unlang` differs from the basic configuration file format is in +complexity and operation. The normal configuration files are +_declarative_ and they are _static_. That is, they declare variables +and values for those variables. Those values do not change when the +server is running. + +In contrast, `unlang` performs run-time processing of requests. +Conditional statements such as xref:if.adoc[if] are evaluated for every +packet that is received. Attribute editing statements such as +xref:update.adoc[update] can be used to edit attribute contents or lists +of attributes. + +.Example +[source,unlang] +---- +# First, the keyword 'if' + +# followed by condition which checks that the User-Name +# attribute has value "bob" + +if (&User-Name == "bob") { + # keyword "update" + + # followed by instructions to add the Reply-Message + # attribute to the "reply" list, with contents + # "Hello, bob" + + update reply { + Reply-Message := "Hello, bob" + } +} +---- + +// 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 new file mode 100644 index 0000000..e6411ea --- /dev/null +++ b/doc/antora/modules/unlang/pages/keywords.adoc @@ -0,0 +1,78 @@ += Keywords + +The following tables list the keywords used in `Unlang`. These keywords +implement the "flow control" of the policies. + +== Flow Control Keywords + +The _flow control_ keywords allow _if / then / else_ checks, simple +looping, etc. + +.Flow Control +[options="header"] +[cols="30%,70%"] +|===== +| Keyword | Description +| xref:break.adoc[break] | Exit early from a `foreach` loop. +| xref:case.adoc[case] | Match inside of a `switch`. +| xref:else.adoc[else] | Do something when an `if` does not match. +| xref:elsif.adoc[elsif] | Check for condition when a previous `if` does not match. +| xref:foreach.adoc[foreach] | Loop over a list of attributes. +| xref:if.adoc[if] | Check for a condition, and execute a sub-policy if it matches. +| xref:return.adoc[return] | Immediately stop processing a section. +| xref:switch.adoc[switch] | Check for multiple values. +|===== + +== Attribute Editing Keywords + +The _attribute editing_ keywords allow policies to add, delete, and +modify attributes in any list or packet. + +.Attribute Editing +[options="header"] +[cols="30%,70%"] +|===== +| Keyword | Description +| xref:update.adoc[update] | Add or filter attributes to a list +|===== + +== Grouping Keywords + +The _grouping_ keywords allow policies to be organized into groups, +including load-balancing. + +.Grouping +[options="header"] +[cols="30%,70%"] +|===== +| Keyword | Description +| xref:group.adoc[group] | Group a series of statements. +| xref:load-balance.adoc[load-balance] | Define a load balancing group without fail-over. +| xref:redundant.adoc[redundant] | Define a redundant group with fail-over. +| xref:redundant-load-balance.adoc[redundant-load-balance] | Define a redundant group with fail-over and load balancing. +|===== + +== Module Keywords + +The _module_ keywords refer pre-packaged libraries that implement +specific functionality, such as connecting to SQL, LDAP, etc. The +name used here is not the literal string `module`. Instead, it is the +name of an instance of a pre-packaged module such as `sql`, or `ldap`, or +`eap`. + +The documentation below describes how to reference modules. That is, +how to use `sql`, etc. in the policies. Please see +`raddb/mods-available/` for configuration examples for each module. + +.Modules +[options="header"] +[cols="30%,70%"] +|===== +| Keyword | Description +| xref:module.adoc[<module>] | Execute a named module, e.g., `sql`. +| xref:module_method.adoc[<module>.<method>] | Execute a particular method of a named module, e.g., `pap.authorize` +| xref:module_builtin.adoc[reject] | Built-in modules, e.g., `reject`, or `ok`, etc. +|===== + +// 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/list.adoc b/doc/antora/modules/unlang/pages/list.adoc new file mode 100644 index 0000000..a55a54f --- /dev/null +++ b/doc/antora/modules/unlang/pages/list.adoc @@ -0,0 +1,72 @@ += Attribute Lists + +An attribute list contains a set of attributes. The allowed lists +are: + +`request`:: Attributes in the incoming request packet. + +`reply`:: Attributes in the outgoing reply packet. + +`control`:: Attributes in the internal "control" list that is +associated with the request. ++ +The `control` attributes are used to manage how the request is +processed. These attributes are never sent in any packet. + +`session-state`:: Attributes which are maintained across multi-packet +exchanges. + +`proxy-request`:: Attributes in the proxied request packet to a home server. + +`proxy-reply`:: Attributes in the reply packet from the home server. + +`coa`:: Attributes in a CoA-Request packet which is sent to a home server. + +`disconnect`:: Attributes in a Disconnect-Request packet which is sent to a home server. + +There must be a colon `:` after the list name and before the attribute name. +This syntax helps the server to distinguish between list names and attribute +names. + +With the exception of `session-state`, all of the above lists are +ephemeral. That is, they exist for one packet exchange, and only one +packet exchange. When a reply is sent for a request, the above lists +and all attributes are deleted. There is no way to reference an +attribute from a previous packet. We recommend using a database to +track complex state. + +The `coa` and `disconnect` lists can only be used when the server +receives an Access-Request or Accounting-Request. Use `request` and +`reply` instead of `coa` when the server receives a CoA-Request or +Disconnect-Request packet. + +Adding one or more attributes to either of the `coa` or `disconnect` +list causes server to originate a CoA-Request or Disconnect-Request +packet. That packet is sent when the current Access-Request or +Accounting-Request has been finished, and a reply sent to the NAS. +See `raddb/sites-available/originate-coa` for additional information. + +In some cases, requests are associated a multi-packet exchange. For +those situations, the `session-state` list is automatically saved when +a reply is sent, and it is automatically restored when the next packet +in sequence comes in. Once the packet exchange has been finished, the +`session-state` list is deleted. + +In some cases, there is a parent-child relationship between requests. +In those situations, it is possible for the policy rules in the child +to refer to attributes in the parent. This reference can be made by +prefixing the _<list>_ name with the `parent` qualifier. The key word +`outer` is also a synonym for `parent`. If there are multiple +parent-child relationships, the `parent` qualifier can be repeated. + +There is, however, no way for the parent to refer to the child. When +the child is running, the parent is suspended. Once the child +finishes, it is deleted, and is no longer accessible to the parent. + +.Examples +`&parent.request.User-Name` + +`&parent.reply.Reply-Message` + +`&parent.parent.session-state.Filter-Id` + +// 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/load-balance.adoc b/doc/antora/modules/unlang/pages/load-balance.adoc new file mode 100644 index 0000000..d64b161 --- /dev/null +++ b/doc/antora/modules/unlang/pages/load-balance.adoc @@ -0,0 +1,32 @@ += The load-balance Statement + +.Syntax +[source,unlang] +---- +load-balance { + [ statements ] +} +---- + +The `load-balance` section is similar to the `redundant` section +except that only one module in the subsection is ever called. + +In general, the +xref:redundant-load-balance.adoc[redundant-load-balance] statement is +more useful than this one. + +[ statements ]:: One or more `unlang` commands. Only one of the +statements is executed. + +.Examples + +[source,unlang] +---- +load-balance &User-Name { + sql1 + sql2 +} +---- + +// 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/module.adoc b/doc/antora/modules/unlang/pages/module.adoc new file mode 100644 index 0000000..fd18f2f --- /dev/null +++ b/doc/antora/modules/unlang/pages/module.adoc @@ -0,0 +1,86 @@ += Modules + +.Syntax +[source,unlang] +---- +<module> +---- + +The `<module>` statement is a reference to the named module. Common +module names include `pap`, `chap`, `files`, `eap`, and `sql`. The +`modules { ... }` subsection of `radiusd.conf` contains all of the +valid modules. + +When processing reaches a named module, the pre-compiled module is +called. The module may succeed or fail and will return a status code +to the `unlang` interpreter detailing success or failure. + +.Example +[source,unlang] +---- +chap +sql +---- + +== Module Return Codes + +When a module is called, it returns one of the following codes to +the interpreter; the meaning of each code is detailed to the right of +the source, below: + +.Module Return Codes + +The below table describes the purpose of the rcodes that may be returned +by a module call. In this case the 'operation' referenced in the table is +the current module call. + +include::partial$rcode_table.adoc[] + +These return codes can be used in a subsequent +xref:condition/index.adoc[conditional expression] thus allowing policies to +perform different actions based on the behaviour of the modules. + +.Example +[source,unlang] +---- +sql +if (notfound) { + update reply { + Reply-Message = "We don't know who you are" + } + reject +} +---- + +== Module Return Code priority overrides + +In the case of modules, rcodes can be modified on a per-call basis. + +Module priority overrides are specified in a block inline with the module call. +The format of an override is `<rcode> = (0+|<rcode>|return)` - That is, +a number greater than or equal to 0, the priority of another rcode, or the special +priority `return` which causes the current section to immediately exit. + +[source, unlang] +---- +ldap { <1> + fail = 1 <2> + ok = handled <3> + reject = return <4> +} +---- + +<1> Call to the `ldap` module. +<2> Sets the priority of the `fail` rcode to be `1`. If the priority + of the rcode for the request is `0`, then the request request rcode + will be set to `fail` if the module returns `fail`. +<3> Sets the priority of the `ok` rcode to be whatever the default is for + `handled` in this section. As the default for `handled` is usually + `return`. If `ok` is returned, the current section will immediately + exit. +<4> Sets the priority of `reject` to be `return`. If the module returns + `reject`, the current section will immediately exit. + + +// 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/module_builtin.adoc b/doc/antora/modules/unlang/pages/module_builtin.adoc new file mode 100644 index 0000000..f21a128 --- /dev/null +++ b/doc/antora/modules/unlang/pages/module_builtin.adoc @@ -0,0 +1,42 @@ += Built-in Modules + +In some cases, it is useful to reject a request immediately or perform another +action on it. The built-in modules can be used to perform these actions. These +modules are named for the return codes given in the xref:module.adoc[module] +section. + +In practice, these modules are implemented by the `always` module and +exist so that a success or failure can be forced during the processing +of a policy. + +The names and behaviours of these modules are given below: + +`fail`:: +Causes the request to be treated as if a database failure had +occurred. + +`noop`:: +Do nothing. This also serves as an instruction to the +configurable failover tracking that nothing was done in the current +section. + +`ok`:: +Instructs the server that the request was processed properly. This keyword can be used to over-ride earlier failures if the local +administrator determines that the failures are not catastrophic. + +`reject`:: +Causes the request to be immediately rejected. + +.Example +[source,unlang] +---- +if (!&User-Name) { + update reply { + Reply-Message := "We don't know who you are" + } + reject +} +---- + +// 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/module_method.adoc b/doc/antora/modules/unlang/pages/module_method.adoc new file mode 100644 index 0000000..98cd375 --- /dev/null +++ b/doc/antora/modules/unlang/pages/module_method.adoc @@ -0,0 +1,27 @@ += Module methods + +.Syntax +[source,unlang] +---- +<module>.<method> +---- + +This variant of xref:module.adoc[<module>] is used in one processing +section. It calls a module using the method of another processing +section. For example, it can be used to call a module's `authorize` +method while processing the `post-auth` section. + +The `<module>` portion must refer to an existing module; the +`<method>` portion must refer to processing method supported by that +module. Typically, the names of the processing method will be the +same as the processing sections. + +.Example +[source,unlang] +---- +sql.recv.Accounting-Request +files.recv.Access-Request +---- + +// 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/redundant-load-balance.adoc b/doc/antora/modules/unlang/pages/redundant-load-balance.adoc new file mode 100644 index 0000000..2322f72 --- /dev/null +++ b/doc/antora/modules/unlang/pages/redundant-load-balance.adoc @@ -0,0 +1,39 @@ += The redundant-load-balance Statement + +.Syntax +[source,unlang] +---- +redundant-load-balance { + [ statements ] +} +---- + +The `redundant-load-balance` section operates as a combination of the +xref:redundant.adoc[redundant] and xref:load-balance.adoc[load-balance] +sections. + +[ statements ]:: One or more `unlang` commands. ++ +If the selected statement succeeds, then the server stops processing +the `redundant-load-balance` section. If, however, that statement fails, +then the next statement in the list is chosen (wrapping around to the +top). This process continues until either one statement succeeds or all +of the statements have failed. ++ +All of the statements in the list should be modules, and of the same +type (e.g., `ldap` or `sql`). All of the statements in the list should +behave identically, otherwise different requests will be processed +through different modules and will give different results. + +.Example +[source,unlang] +---- +redundant-load-balance &User-Name { + sql1 + sql2 + sql3 +} +---- + +// 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/redundant.adoc b/doc/antora/modules/unlang/pages/redundant.adoc new file mode 100644 index 0000000..e837d1f --- /dev/null +++ b/doc/antora/modules/unlang/pages/redundant.adoc @@ -0,0 +1,42 @@ += The redundant Statement + +.Syntax +[source,unlang] +---- +redundant { + [ statements ] +} +---- + +The `redundant` section executes a series of statements in sequence. +As soon as one statement succeeds, the rest of the section is skipped. + +[ statements ]:: One or more `unlang` commands. Processing starts +from the first statement in the list. ++ +If the selected statement succeeds, then the server stops processing +the `redundant` section. If, however, that statement fails, then the +next statement in the list is chosen. This process continues until +either one statement succeeds or all of the statements have failed. ++ +All of the statements in the list should be modules, and of the same +type (e.g., `ldap` or `sql`). All of the statements in the list should +behave identically, otherwise different requests will be processed +through different modules and will give different results. + +In general, we recommend using the +xref:redundant-load-balance.adoc[redundant-load-balance] statement +instead of `redundant`. + +.Example +[source,unlang] +---- +redundant { + sql1 + sql2 + sql3 +} +---- + +// 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/return.adoc b/doc/antora/modules/unlang/pages/return.adoc new file mode 100644 index 0000000..aea1bc2 --- /dev/null +++ b/doc/antora/modules/unlang/pages/return.adoc @@ -0,0 +1,36 @@ += The return Statement + +.Syntax +[source,unlang] +---- +return +---- + +The `return` statement is used to exit a processing section such as +`authorize`. It behaves similarly to the +xref:break.adoc[break] statement, except that it is not limited to +being used inside of a xref:foreach.adoc[foreach] loop. + +The `return` statement is not strictly necessary. It is mainly used +to simplify the layout of `unlang` policies. If the `return` +statement did not exist, then every xref:if.adoc[if] statement might need +to have a matching xref:else.adoc[else] statement. + +The `return` statement will also exit a policy which is defined in the +`policy { ... } ` subsection. This behavior allows policies to be +treated as a function call. Any `return` inside of the policy section +will only return from that policy. The `return` will _not_ return +from the enclosing processing section which called the policy. + +.Example +[source,unlang] +---- +sql +if (&reply.Filter-Id == "hello") { + return +} +... +---- + +// 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/return_codes.adoc b/doc/antora/modules/unlang/pages/return_codes.adoc new file mode 100644 index 0000000..3b09c2d --- /dev/null +++ b/doc/antora/modules/unlang/pages/return_codes.adoc @@ -0,0 +1,17 @@ += Return codes + +Many operations in the server produce a return code (rcode). +The different rcodes give a course indication of whether a particular operation +(a module call, string expansion, or keyword) was successful. + +Unlike return values in other languages, FreeRADIUS' rcodes are are always taken +from a fixed compiled-in set. + +include::partial$rcode_table.adoc[] + +Return codes propagate through nested unlang sections based on their priority. +If a rcode returned by an operation has a higher priority than the current rcode +associated with the request, then the request rcode is overwritten. + +Return code priorities are assigned by the section the module call, expansion or +keyword was used in. diff --git a/doc/antora/modules/unlang/pages/switch.adoc b/doc/antora/modules/unlang/pages/switch.adoc new file mode 100644 index 0000000..deff703 --- /dev/null +++ b/doc/antora/modules/unlang/pages/switch.adoc @@ -0,0 +1,83 @@ += The switch Statement + +.Syntax +[source,unlang] +---- +switch <expansion> { + case <match-1> { + [ statements-1 ] + } + case <match-2> { + [ statements-2 ] + } + case { + [ statements-3 ] + } +} +---- + +A `switch` statement causes the server to evaluate _expansion_, which +can be an xref:attr.adoc[&Attribute-Name] or +xref:condition/operands.adoc[data]. The result is compared against _match-1_ +and _match-2_ to find a match. If no string matches, then the server +looks for the default xref:case.adoc[case] statement, which has no +associated match. + +The matching is done via equality. The `switch` statement is mostly +syntactic sugar and is used to simplify the visual form of the +configuration. It is mostly equivalent to the following use of +xref:if.adoc[if] statements: + +.Nearly equivalent syntax +[source,unlang] +---- +if (<expansion> == <match-1>) { + [ statements-1 ] +} +elsif (<expansion> == <match-2>) { + [ statements-2 ] +} +else { + [ statements-3 ] +} +---- + +The only difference between the two forms is that for a `switch` +statement, the _expansion_ is evaluated only once. For the equivalent +xref:if.adoc[if] statement, the _expansion_ is evaluated again for every +xref:if.adoc[if]. + +If a matching xref:case.adoc[case] is found, the statements within +that xref:case.adoc[case] are evaluated. If no matching +xref:case.adoc[case] is found, the `case` section with no "match" is +evaluated. If there is no such `case { ...}` subsection, then the +`switch` statement behaves as if the `case {...}` section was empty. + +//// +For compatibility with version 3, and empty `case` statement can also +be used instead of `default`. +//// + +The _match_ text for the xref:case.adoc[case] statement can be an +xref:attr.adoc[&Attribute-Name] or xref:type/index.adoc[data]. + +No statement other than xref:case.adoc[case] can appear in a `switch` +statement, and the xref:case.adoc[case] statement cannot appear outside of a +`switch` statement. + +.Example +[source,unlang] +---- +switch &User-Name { + case "bob" { + reject + } + + case { + ok + } +} +---- + +// 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/type/all_types.adoc b/doc/antora/modules/unlang/pages/type/all_types.adoc new file mode 100644 index 0000000..0bace01 --- /dev/null +++ b/doc/antora/modules/unlang/pages/type/all_types.adoc @@ -0,0 +1,80 @@ += List of Data Types + +The server support a wide range of data types, both in `unlang` and in +the dictionaries. This page outlines the names and functionality of +those data types. + +== Basic Data Types + +There are a number of "basic" data types. These data types are +fixed-size, and encapsulate simple concepts such as "integer" or "IP +address". + +Basic data types can be used in `unlang`, as they contain simple +values which can be compared, or assigned to one attribute. In most +cases, it is not necessary to know the name of the data type. It is +possible to write values in the format you expect, The server will do +"the right thing" when interpreting the values. + +.Basic Data Types +[options="header"] +[cols="15%,85%"] +|===== +| Data Type | Description +| bool | boolean +| date | calendar date +| ethernet | Ethernet address +| float32 | 32-bit floating point number +| float64 | 64-bit floating point number +| ifid | interface ID +| int8 | 8-bit signed integer +| int16 | 16-bit signed integer +| int32 | 32-bit signed integer +| int64 | 64-bit signed integer +| ipaddr | IPv4 address +| ipv6addr | IPv6 address +| ipv4prefix | IPv4 network with address and prefix length +| ipv6prefix | IPv6 network with address and prefix length +| octets | raw binary, printed as hex strings +| string | printable strings +| time_delta | difference between two calendar dates +| uint8 | 8-bit unsigned integer +| uint16 | 16-bit unsigned integer +| uint32 | 32-bit unsigned integer +| uint64 | 64-bit unsigned integer +|===== + +=== Structural Data Types + +The following data types are "structural", in that they form +parent-child relationships between attributes. These data types can +only be used in the dictionaries. They cannot be used in `unlang` +statements. + +.Structural Data Types +[options="header"] +[cols="15%,85%"] +|===== +| Data Type | Description +| struct | structure which contains fixed-width fields +| tlv | type-length-value which contains other attributes +| vsa | Encapsulation of vendor-specific attributes +|===== + +=== Protocol-Specific Data Types + +The following data types are used only in certain protocols. These +data types can be used only in the dictionaries. They cannot be used +in `unlang` statements. + +.Protocol Specific Data Types +[options="header"] +[cols="15%,15%,70%"] +|===== +| Data Type | Protocol | Description +| abinary | RADIUS | Ascend binary filters +| extended | RADIUS | attributes which "extend" the number space +|===== + +// 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/type/double.adoc b/doc/antora/modules/unlang/pages/type/double.adoc new file mode 100644 index 0000000..6c3e708 --- /dev/null +++ b/doc/antora/modules/unlang/pages/type/double.adoc @@ -0,0 +1,39 @@ += Double-Quoted Strings + +.Syntax +`"string"` + +A double-quoted string is interpreted via the usual rules in +programming languages for double quoted strings. The double-quote +character can be placed in a string by escaping it with a backslash. +Carriage returns and line-feeds can also be used via the usual `\r` and +`\n` syntax. + +The main difference between the single and double quoted strings is +that the double quoted strings can be dynamically expanded. The syntax +`${...}` is used for parse-time expansion and `%{...}` is used for +run-time expansion. The difference between the two methods is that the +`${...}` form is expanded when the server loads the configuration +files and is valid anywhere in the configuration files. The `%{...}` +link:xlat.adoc[string expansion] form is valid only in conditional +expressions and attribute assignments. + +The output of the dynamic expansion can be interpreted as a string, +a number, or an IP address, depending on its context. + +Note that the interpretation of text _strongly_ depends on the +context. The text `"0000"` can be interpreted as a data type +"integer", having value zero, or a data type "string", having value +`"0000"`. In general when a particular piece of text is used, it is +used with the context of a known attribute. That attribute has a +link:data.adoc[data type], and the text will be interpreted as that +data type. + +.Examples + +`"word"` + +`"a string"` + +`"this has embedded\ncharacters"` + +// 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/type/index.adoc b/doc/antora/modules/unlang/pages/type/index.adoc new file mode 100644 index 0000000..7d0d70f --- /dev/null +++ b/doc/antora/modules/unlang/pages/type/index.adoc @@ -0,0 +1,117 @@ += Data Types + +Unlang supports a number of data types. These data types are used in +conditional expressions or when assigning a value to an attribute. + +== Using Data Types + +The server support a wide range of data types, as given in the +xref:unlang/type/all_types.adoc[list of data types] page. The choice +of which data type applies is determined by the context in which that +data type is used. This context is usually taken from an attribute +which is being assigned a value. + +The `unlang` interpreter uses pre-defined attributes which are defined +in dictionaries. The dictionaries define both a name, and a data type +for the attributes. In the interpreter, then, attributes can be +assigned a value or compared to a value, without specifying the data +type. The interpreter knows how to parse the value by using the data +type assigned to the attribute. + +The result is that in most cases, it is not necessary to know the name +of the data types. It is possible to write values in the format you +expect, and he server will do "the right thing" when interpreting the +values. + +.Attributes with Different Data Types +[source,unlang] +---- +Framed-IP-Address = 192.0.2.1 +Framed-IPv6-Address = 2001:db8:: +Reply-Message = "This is a reply" +Port-Limit = 5 +Boolean = true +Octets-Thing = 0xabcdef0102030405 +MAC-Address = 00:01:02:03:04:05 +---- + +== Parsing Data Types + +The interpreter is flexible when parsing data types. So long as the +value can be parsed as the given data type without error, the value +will be accepted. + +For example, a particular attribute may be of data type `ipaddr` in +order to store IPv4 addresses. The interpreter will then accept the +following strings as valid IPv4 addresses: + +`192.168.0.2`:: xref:type/string/unquoted.adoc[Unquoted text], interpreted as the data type + +`'192.168.0.2'`:: xref:type/string/single.adoc[Single-quoted string], the contents of the string are parsed as the data type. ++ +The single-quoted string form is most useful when the data type +contains special characters that may otherwise confuse the parser. + +`"192.168.0.2"`:: xref:type/string/double.adoc[Double-quoted string]. ++ +The contents of the string are dynamically expanded as described in +the xref:unlang/xlat/index.adoc[dynamic expansion] page. The +resulting output is then interpreted as the given data type. + +`{backtick}/bin/echo 192.168.0.2{backtick}`:: xref:type/string/backticks.adoc[backtick-quoted string]. +Run a script, and parse the resulting string as the data type. + +Similar processing rules are applied when parsing assignments and +comparisons, for all attributes and data types. + +=== Casting Data Types + +In some cases, it is necessary to parse values which do not refer to +attributes. This situation usually occurs when two values need to be +compared, as in the following example: + +[source,unlang] +---- +if ("%{sql:SELECT ipaddress FROM table WHERE user=%{User-Name}}" == 192.0.2.1) } + .... +} +---- + +Since there is no attribute on either side of the `==` operator, the +interpreter has no way of knowing that the string `192.0.2.1` is an IP +address. There is unfortunately no way of automatically parsing +strings in order to determine the data type to use. Any such +automatic parsing would work most of the time, but it would have +error cases where the parsing was incorrect. + +The solution is to resolve these ambiguities by allowing the values to +be cast to a particular type. Casting a value to a type tells the +interpreter how that value should be parsed. Casting is done by +prefixing a value with the type name, surrounded by angle brackets; +`<...>`. + +.Syntax +---- +<...>value +---- + +We can add a cast to the above example, as follows: + +[source,unlang] +---- +if ("%{sql:SELECT ipaddress FROM table WHERE user=%{User-Name}}" == <ipaddr>192.0.2.1) } + .... +} +---- + +In this example, we prefix the IP address with the string `<ipaddr>`. +The interpreter then knows that the value `192.0.2.` should be +interpreted as the data type `ipaddr`, and not as the literal string +`"192.0.2."`. + +For a full list of data types which can be used in a cast, please see +the xref:unlang/type/all_types.adoc[list of data types] page, and the +"Basic Type Types" section. + +// 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/type/ip.adoc b/doc/antora/modules/unlang/pages/type/ip.adoc new file mode 100644 index 0000000..fc25ae8 --- /dev/null +++ b/doc/antora/modules/unlang/pages/type/ip.adoc @@ -0,0 +1,15 @@ += IP Addresses + +.Examples + +`192.0.2.16` + +`::1` + +`example.com` + +Depending on the context, a "simple word", as above, may be +interpreted as an IPv4 or an IPv6 address. This interpretation is +usually done when the string is used in the context of an attribute, +or to compare two addresses or assign an address to an attribute. + +// 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/type/numb.adoc b/doc/antora/modules/unlang/pages/type/numb.adoc new file mode 100644 index 0000000..284cf81 --- /dev/null +++ b/doc/antora/modules/unlang/pages/type/numb.adoc @@ -0,0 +1,11 @@ += Numbers + +.Examples + +`0` + +`563` + +Numbers are unsigned integers that are composed of decimal digits. + +// 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/type/string/backticks.adoc b/doc/antora/modules/unlang/pages/type/string/backticks.adoc new file mode 100644 index 0000000..9372b4c --- /dev/null +++ b/doc/antora/modules/unlang/pages/type/string/backticks.adoc @@ -0,0 +1,38 @@ += Backtick-quoted string + +.Syntax +`{backtick}string{backtick}` + +The backtick operator is used to perform a run-time expansion +similar to what is done with the Unix shell. The contents of the string +are split into one or more sub-strings, based on intermediate +whitespace. Each substring is then expanded as described above for +double quoted strings. The resulting set of strings is used to execute a +program with the associated arguments. + +The output of the program is recorded, and the resulting data is +used in place of the input string value. Where the output is composed of +multiple lines, any carriage returns and line feeds are replaced by +spaces. + +For safety reasons, the full path to the executed program should be +given. In addition, the string is split into arguments _before_ the +substrings are dynamically expanded. This step is done both to allow +the substrings to contain spaces, and to prevent spaces in the +expanded substrings from affecting the number of command-line +arguments. + +For performance reasons, we recommend that the use of back-quoted +strings be kept to a minimum. Executing external programs is +relatively expensive, and executing a large number of programs for +every request can quickly use all of the CPU time in a server. If many +programs need to be executed, it is suggested that alternative ways to +achieve the same result be found. In some cases, using a real +programming language such as `lua`, `perl` or `python` may be better. + +.Examples + +`{backtick}/bin/echo hello{backtick}` + +// 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/type/string/double.adoc b/doc/antora/modules/unlang/pages/type/string/double.adoc new file mode 100644 index 0000000..ea87bc5 --- /dev/null +++ b/doc/antora/modules/unlang/pages/type/string/double.adoc @@ -0,0 +1,68 @@ += Double Quoted Strings + +.Syntax +`"string"` + +A double-quoted string allows escape sequences and xref:xlat/index.adoc[dynamic +expansions]. As with xref:type/string/single.adoc[single-quoted strings], text +within double quotes can include spaces. + +The main difference between the single and double quoted strings is +that the double quoted strings can be dynamically expanded. The syntax +`${...}` is used for parse-time expansion and `%{...}` is used for +run-time expansion. The difference between the two methods is that the +`${...}` form is expanded when the server loads the configuration +files and is valid anywhere in the configuration files. The `%{...}` +xref:xlat/index.adoc[string expansion] form is valid only in conditional +expressions and attribute assignments. + +The output of the dynamic expansion can be interpreted as a string, +a number, or an IP address, depending on its context. + +Note that the interpretation of text _strongly_ depends on the +context. The text `"0000"` can be interpreted as a data type +"integer", having value zero, or a data type "string", having value +`"0000"`. In general when a particular piece of text is used, it is +used with the context of a known attribute. That attribute has a +xref:type/index.adoc[data type], and the text will be interpreted as that +data type. + +NOTE: Most values retrieved from external datastores will be treated implicitly +as double-quoted strings. + +== Escape sequences + +Escape sequences allow the inclusion of characters that may be difficult to +represent in datastores, or the FreeRADIUS configuration files. + +.Escape sequences and their descriptions +[options="header", cols="15%,85%"] +|===== +| Escape sequence | Character represented +| `\\` | Literal backslash (0x5c) +| `\r` | Carriage return (0x0d) +| `\n` | Line feed (0x0a) +| `\t` | Horizontal tab (0x09) +| `\"` | Double quote (0x22) +| `\x<hex><hex>` | A byte whose numerical value is given by `<hex><hex>` interpreted as a hexadecimal number. +| `\x<oct><oct><oct>` | A byte whose numerical value is given by `<oct><oct><oct>` interpreted as an octal number. +|===== + +.Examples + +`"word"` + +`"a string"' + +`"foo\"bar\""` + +`"this is a long string"` + +`"this has embedded\ncharacters"` + +`"attribute\tvalue\nusername\t%{User-Name}\nreply-message\t%{reply.Reply-Message}"` +`"The result of 'SELECT * FROM foo WHERE 1' is: %{sql:SELECT * FROM foo WHERE 1}"` + +// Licenced under CC-by-NC 4.0. +// Copyright (C) 2019 Arran Cudbard-Bell <a.cudbardb@freeradius.org> +// Copyright (C) 2019 The FreeRADIUS project. +// Copyright (C) 2020 Network RADIUS SAS. + + + + diff --git a/doc/antora/modules/unlang/pages/type/string/escaping.adoc b/doc/antora/modules/unlang/pages/type/string/escaping.adoc new file mode 100644 index 0000000..e63a498 --- /dev/null +++ b/doc/antora/modules/unlang/pages/type/string/escaping.adoc @@ -0,0 +1,14 @@ += Character Escaping + +The quotation characters in the above string data types can be +escaped by using the backslash, or `\,` character. The backslash +character itself can be created by using `\\`. Carriage returns and +line feeds can be created by using `\n` and `\r`. + +.Examples + +`"I say \"hello\" to you"` + +`"This is split\nacross two lines"` + +// 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/type/string/single.adoc b/doc/antora/modules/unlang/pages/type/string/single.adoc new file mode 100644 index 0000000..fa2ac05 --- /dev/null +++ b/doc/antora/modules/unlang/pages/type/string/single.adoc @@ -0,0 +1,19 @@ += Single Quoted Strings + +.Syntax +`'string'` + +A single-quoted string is interpreted without any dynamic string +expansion. The quotes allow the string to contain spaces, among other +special characters. The single quote character can be placed in such a +string by escaping it with a backslash. + +.Examples + +`'hello'` + +`'foo bar`' + +`'foo\\'bar'` + +`'this is a long string'` + +// 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/type/string/unquoted.adoc b/doc/antora/modules/unlang/pages/type/string/unquoted.adoc new file mode 100644 index 0000000..9dd6e55 --- /dev/null +++ b/doc/antora/modules/unlang/pages/type/string/unquoted.adoc @@ -0,0 +1,21 @@ += Unquoted Strings + +Where a series of characters cannot be parsed as a decimal number, +they are interpreted as a simple string composed of one word. This +word is delimited by spaces, or by other tokens, such as `)` in +conditional expressions. + +This unquoted text is interpreted as simple strings and are generally +equivalent to placing the string in single quotes. + +The interpretation of the text depends on the context, which is +usually defined by an attribute which has a xref:type/index.adoc[data type]. + +.Examples + +`Hello` + +`192.168.0.1` + +`00:01:02:03:04:05` + +// 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/update.adoc b/doc/antora/modules/unlang/pages/update.adoc new file mode 100644 index 0000000..645f4d8 --- /dev/null +++ b/doc/antora/modules/unlang/pages/update.adoc @@ -0,0 +1,160 @@ += The update Statement + +.Syntax +[source,unlang] +---- +update [ <list> ] { + <server-attribute> <op> <value> + ... +} +---- + +The `update` statement adds attributes to, or edits the attributes in, +the named _<list>_. + +The `update` statement consists of the following syntax elements: + +<list>:: The attribute list which will be updated. The list is +usually `request`, `reply`, or `control`. ++ +If the _<list>_ qualifier is omitted, then each entry inside of the +`update` section *must* be prefixed with a list name. For example, +`&request.User-Name ...` + +<server-attribute>:: The server attribute which is assigned the +_<value>_. + +<op>:: The operator such as `=`, `:=`, etc. + +<value>:: The value which is assigned to the attribute. If the field +is a double-quoted string, it undergoes xref:xlat/index.adoc[string +expansion], and the resulting value is assigned to the attribute. + +The update process is atomic, in that either all of the attributes are +modified, or none of them are modified. If the `update` fails for any +reason, then all of the results are discarded, and the `update` does +not affect any server attributes. + +.Example +[source,unlang] +---- +update reply { + &Reply-Message := "Hello!" + &Framed-IP-Address := 192.0.2.4 +} +---- + +== Lists + +The _<list>_ field sets the attribute list that will be updated. If +the _<list>_ qualifier is omitted, then each entry inside of the +`update` section *must* be prefixed with a list name. For example, +`&request.User-Name ...` + +Please see the xref:list.adoc[list] page for valid list names. + +== Server Attributes + +The _<server-attribute>_ field is an attribute name, such as +`&Reply-Message`. The attribute name may also be prefixed with a +_<list>_ qualifier, which overrides the _<list>_ given at the start +of the `update` section. + +NOTE: In version 3, the leading `&` is optional but recommended. + +== Editing Operators + +The `<op>` field is used to define how the attribute is processed. +Different operators allow attributes to be added, deleted, or +replaced, as defined below. + +.Editing Operators +[options="header"] +[cols="10%,90%"] +|===== +| Operator | Description +| = | Add the attribute to the list, if and only if an attribute of +the same name is not already present in that list. +| := | Add the attribute to the list. If any attribute of the same +name is already present in that list, its value is replaced with the +value of the current attribute. +| += | Add the attribute to the tail of the list, even if attributes +of the same name are already present in the list. +| ^= | Add the attribute to the head of the list, even if attributes +of the same name are already present in the list. +| -= | Remove all attributes from the list that match _<value>_. +| !* | Delete all occurances of the attribute, no matter what the value. +|===== + +== Filtering Operators + +The following operators may also be used in addition to the ones +listed above. These operators use the _<server-attribute>_ and +_<value>_ fields to enforce limits on all attributes in the given +_<list>_, and to edit attributes which have a matching +_<server-attribute>_ name. All other attributes are ignored. + +.Filtering Operators +[options="header] +[cols="10%,90%"] +|===== +| Operator | Description +| == | Keep only the attributes in the list that match _<value>_ +| < | Keep only the attributes in the list that have values less than _<value>_. +| \<= | Keep only the attributes in the list that have values less than or equal to _<value>_. +| > | Keep only the attributes in the list that have values greater than _<value>_. +| >= | Keep only the attributes in the list that have values greater than or equal to _<value>_. +| =~ | Keep only the attributes in the list which match the regular expression given in _<value>_. +| !~ | Keep only the attributes in the list which do not match the regular expression given in _<value>_. +|===== + +The `==` operator is very different from the `=` operator listed +above. The `=` operator is used to add new attributes to the list, +while the `==` operator removes all attributes that do not match the +given value. + +The comparison operators `<`, `<=`, `>`, and `>=` have some additional +side effects. Any non-matching value is replaced by the _<value>_ +given here. If no attribute exists, it is created with the given +_<value>_. + +For IP addresses, the operators `>`, `>=`, `<`, and `\<=` check for +membership in a network. The _<value>_ field should then be a IP +network, given in `address/mask` format. + +.Example +[source,unlang] +---- +update reply { + &Session-timeout := 86400 +} +---- + +.Example +[source,unlang] +---- +update reply { + &Reply-Message += "Rejected: Also, realm does not end with ac.uk" +} +---- + +== Values + +The _<value>_ field is the value which is assigned to the +_<server-attribute>_. The interpretation of the _<value>_ field +depends on the data type of the contents. For example, if the string +`"192.0.2.1"` is assigned to an attribute of the `string` data type, +then the result is an ASCII string containing that value. However, if +the same string is assigned to an attribute of the `ipaddr` data type, +then the result is a 32-bit IPv4 address, with binary value `0xc0000201`. + +.Example +[source,unlang] +---- +update reply { + &Session-Timeout <= 3600 +} +---- + +// 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/xlat/alternation.adoc b/doc/antora/modules/unlang/pages/xlat/alternation.adoc new file mode 100644 index 0000000..adb7604 --- /dev/null +++ b/doc/antora/modules/unlang/pages/xlat/alternation.adoc @@ -0,0 +1,24 @@ += Alternation Syntax + +Alternation syntax similar to that used in Unix shells may also be +used: + +`%{%{Foo}:-bar}` + +This code returns the value of `%{Foo}`, if it has a value. +Otherwise, it returns a literal string bar. + +`%{%{Foo}:-%{Bar}}` + +This code returns the value of `%{Foo}`, if it has a value. +Otherwise, it returns the expansion of `%{Bar}`. + +These conditional expansions can be nested to almost any depth, such +as with `%{%{One}:-%{%{Two}:-%{Three}}}`. + +.Examples +`%{%{Stripped-User-Name}:-%{User-Name}}` + +`%{%{Framed-IP-Address}:-<none>}` + +// 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/xlat/attribute.adoc b/doc/antora/modules/unlang/pages/xlat/attribute.adoc new file mode 100644 index 0000000..a3ee29b --- /dev/null +++ b/doc/antora/modules/unlang/pages/xlat/attribute.adoc @@ -0,0 +1,54 @@ += Attribute References + +Attributes in a list may be referenced via one of the following two +syntaxes: + +`%{Attribute-Name}` + +`%{<list>:Attribute-Name}` + +The `<list>:` prefix is optional. If given, it must be a valid +reference to an xref:list.adoc[attribute list]. + +If the `<list>:` prefix is omitted, then the `request` list is +assumed. + +For EAP methods with tunneled authentication sessions (i.e. PEAP and +EAP-TTLS), the inner tunnel session can refer to a list for the outer +session by prefixing the list name with `outer.` ; for example, +`outer.request`. + +When a reference is encountered, the given list is examined for an +attribute of the given name. If found, the variable reference in the +string is replaced with the value of that attribute. Otherwise, the +reference is replacedd with an empty string. + +.Examples + +`%{User-Name}` + +`%{request.User-Name} # same as above` + +`%{reply.User-Name}` + +`%{outer.request.User-Name} # from inside of a TTLS/PEAP tunnel` + +Examples of using references inside of a string: + +`"Hello %{User-Name}"` + +`"You, %{User-Name} are not allowed to use %{NAS-IP-Address}"` + +== Additional Variations + +`%{Attribute-Name[#]}`:: +Returns an integer containing the number of named attributes + +`%{Attribute-Name[0]}`:: + +When an attribute appears multiple times in a list, this syntax allows +you to address the attributes as with array entries. `[0]` refers to +the first attributes, `[1]` refers to the second attribute, etc. + +`%{Attribute-Name[*]}`:: + +Returns a comma-separated string containing all values for the named +attributes. + +// 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/xlat/builtin.adoc b/doc/antora/modules/unlang/pages/xlat/builtin.adoc new file mode 100644 index 0000000..f236a57 --- /dev/null +++ b/doc/antora/modules/unlang/pages/xlat/builtin.adoc @@ -0,0 +1,891 @@ += Built-In Expansions + +In addition to storing attribute references, the server has a number +of built-in expansions. These expansions act largely as functions +which operate on inputs, and produce an output. + + + +== Attribute Manipulation + +=== %{length: ... } + +The `length` expansion returns the size of the input as an integer. +When the input is a string, then the output is identical to the +`strlen` expansion. + +When the input is an attribute reference, the output is the size of +the attributes data as encoded "on the wire". + +.Return: _size_ + +.Determining the length of fixed and variable length attributes +==== +[source,unlang] +---- +update control { + &Tmp-String-0 := "Caipirinha" + &Framed-IP-Address := 192.0.2.1 +} + +update reply { + &Reply-Message := "The length of %{control:Tmp-String-0} is %{length:&control:Tmp-String-0}" + &Reply-Message += "The length of %{control:Framed-IP-Address} is %{length:&control:Framed-IP-Address}" +} +---- + +.Output +.... +The length of Caipirinha is 10 +The length of 192.168.0.2 is 4 +.... +==== + +`length` is built in to the server core. + + + +=== %{integer:<&ref>} + +Print the value of the attribute an integer. + +In normal operation, `integer` attributes are printed using the name +given by a `VALUE` statement in a dictionary. Similarly, date +attributes are printed as dates, i.e., "January 1 2010. + +The `integer` expansion applies only to attributes which can be +converted to an integer. For all other inputs, it returns `0`. + +A common usage is to find the difference between two dates. + +For example, if a request contains `Service-Type = Login-User`, the +expansion of `%{integer:&Service-Type}` will yield `1`, which is the +value associated with the `Login-User` name. Using +`%{integer:&Event-Timestamp}` will return the event timestamp as an +unsigned 32-bit number. + +.Return: _string_ + +.Determining the integer value of an enumerated attribute +==== +[source,unlang] +---- +update { + &control:Service-Type := Login-User +} +update reply { + &Reply-Message := "The value of Service-Type is %{integer:&control:Service-Type}" +} +---- + +.Output + +``` +The value of Service-Type is 1 +``` +==== + +`integer` is built in to the server core. + + + +=== %{rand:<number>} + +Generate random number from `0` to `<number>-1`. + +.Return: _uint64_ + +.Generating a random number between 0 and 511 +==== +[source,unlang] +---- +update reply { + &Reply-Message := "The random number is %{rand:512}" +} +---- + +.Output + +``` +The random number is 347 +``` +==== + +`rand` is provided by the `rlm_expr` module. + + + +=== %{tag:<attribute ref>} + +CAUTION: This expansion is deprecated and will likely be removed. + +Returns a list of tags for any attributes found using ``<attribute ref>``. + +.Return: _int8_ + +.Determining the tag value of the second instance of the `radius.Tunnel-Server-Endpoint` attribute +==== +[source,unlang] +---- +update request { + &Tunnel-Server-Endpoint := '192.0.1.1' + &Tunnel-Server-Endpoint:1 := '192.0.5.2' + &Tunnel-Server-Endpoint:1 += '192.0.3.8' + &Tunnel-Server-Endpoint:2 := '192.0.2.1' + &Tunnel-Server-Endpoint:2 += '192.0.3.4' +} + +update reply { + &Reply-Message := "The tag value of the second instance of Tunnel-Server-Enpoint is %{request:Tunnel-Server-Endpoint[1]}" +} +---- + +.Output + +``` +The tag value of the second instance of Tunnel-Server-Enpoint is 192.0.5.2 +``` +==== + +`tag` is built in to the server core. + + + +=== %{string:<data>} + +Convert input to a string (if possible). For _octets_ type attributes, this +means interpreting the data as a UTF8 string, and inserting octal escape +sequences where appropriate. + +For other types, this means printing the value in its _presentation_ format, +i.e. dotted quads for IPv4 addresses, link:https://en.wikipedia.org/wiki/ISO_8601[ISO 8601] +time for date types, enumeration values for attributes such as `radius.Service-Type` etc. + +.Return: _string_ + +.String interpolation using the raw octets value of Tmp-Octets-0, and the stringified version +==== +[source,unlang] +---- +update control { + &Tmp-Octets-0 := 0x7465737431 +} +update reply { + &Reply-Message := "The string value of %{control:Tmp-Octets-0} is %{string:%{control:Tmp-Octets-0}}" +} +---- +==== + +.Output + +``` +The string value of 0x7465737431 is test1 +``` + +`string` is built in to the server core. + + + +== Server Manipulation + +=== %{config:<key>} + +Refers to a variable in the configuration file. See the documentation +on configuration file references. + +.Return: _string_ + +.Example + +[source,unlang] +---- +"Server installed in %{config:prefix}" +"Module rlm_exec.shell_escape = %{config:modules.exec.shell_escape}" +---- + +.Output + +``` +Server installed in /opt/freeradius +Module rlm_exec.shell_escape = yes +``` + +`config` is built in to the server core. + + + +=== %{client:<key>} + +Refers to a variable that was defined in the client section for the +current client. See the sections `client { ... }` in `clients.conf`. + +.Return: _string_ + +.Example + +[source,unlang] +---- +"The client ipaddr is %{client:ipaddr}" +---- + +.Output + +``` +The client ipaddr is 192.168.5.9 +``` + +`client` is built in to the server core. + + + +=== %{debug:<level>} + +Dynamically change the debug level to something high, recording the old level. + +.Return: _string_ + +.Example + +[source,unlang] +---- +authorize { + if (&request:User-Name == "bob") { + "%{debug:4}" + } else { + "%{debug:0}" + } + ... +} +---- + +.Output (_extra informations only for that condition_) + +``` +... +(0) authorize { +(0) if (&request:User-Name == "bob") { +(0) EXPAND %{debug:4} +(0) --> 2 +(0) } # if (&request:User-Name == "bob") (...) +(0) filter_username { +(0) if (&State) { +(0) ... +(0) } +... +``` + +`debug` is built in to the server core. + + + +=== %{debug_attr:<list:[index]>} + +Print to debug output all instances of current attribute, or all attributes in a list. +expands to a zero-length string. + +.Return: _string_ + +.Example + +[source,unlang] +---- +authorize { + if (&request:User-Name == "bob") { + "%{debug_attr:request[*]}" + } + ... +} +---- + +.Output + +``` +... +(0) authorize { +(0) if (&request:User-Name == "bob") { +(0) Attributes matching "request[*]" +(0) &request:User-Name = bob +(0) &request:User-Password = hello +(0) &request:NAS-IP-Address = 127.0.1.1 +(0) &request:NAS-Port = 1 +(0) &request:Message-Authenticator = 0x9210ee447a9f4c522f5300eb8fc15e14 +(0) EXPAND %{debug_attr:request[*]} +(0) } # if (&request:User-Name == "bob") (...) +... +``` + +`debug_attr` is built in to the server core. + + + +== String manipulation + +=== %{lpad:<&ref> <val> <char>} + +Left-pad a string. + +.Return: _string_ + +.Example + +[source,unlang] +---- +update control { + &Tmp-String-0 := "123" +} +update reply { + &Reply-Message := "Maximum should be %{lpad:&control:Tmp-String-0 11 0}" +} +---- + +.Output + +``` +Maximum should be 00000000123 +``` + +`lpad` is provided by the `rlm_expr` module. + + + +=== %{rpad:<&ref> <val> <char>} + +Right-pad a string. + +.Return: _string_ + +.Example + +[source,unlang] +---- +update control { + &Tmp-String-0 := "123" +} +update reply { + &Reply-Message := "Maximum should be %{rpad:&control:Tmp-String-0 11 0}" +} +---- + +.Output + +``` +Maximum should be 12300000000 +``` + +`rpad` is provided by the `rlm_expr` module. + + + +=== %{pairs:<&list:[*]>} + +Serialize attributes as comma-delimited string. + +.Return: _string_ + +.Example + +[source,unlang] +---- +update { + &control:Tmp-String-0 := "This is a string" + &control:Tmp-String-0 += "This is another one" +} + +update reply { + &Reply-Message := "Serialize output: %{pairs:&control[*]}" +} +---- + +.Output + +``` +Serialize output: Tmp-String-0 = \"This is a string\"Tmp-String-0 = \"This is another one\" +``` + +`pairs` is provided by the `rlm_expr` module. + + + +=== %{randstr: ...} + +Get random string built from character classes. + +.Return: _string_ + +.Example + +[source,unlang] +---- +update reply { + &Reply-Message := "The random string output is %{randstr:aaaaaaaa}" +} +---- + +.Output + +``` +The random string output is 4Uq0gPyG +``` + +`randstr` is provided by the `rlm_expr` module. + + + +=== %{strlen: ... } + +Length of given string. + +.Return: _integer_ + +.Example + +[source,unlang] +---- +update control { + &Tmp-String-0 := "Caipirinha" +} +update reply { + &Reply-Message := "The length of %{control:Tmp-String-0} is %{strlen:&control:Tmp-String-0}" +} +---- + +.Output + +``` +The length of Caipirinha is 21 +``` + +`strlen` is built in to the server core. + + + +=== %{tolower: ... } + +Dynamically expands the string and returns the lowercase version of +it. This definition is only available in version 2.1.10 and later. + +.Return: _string_ + +.Example + +[source,unlang] +---- +update control { + &Tmp-String-0 := "CAIPIRINHA" +} +update reply { + &Reply-Message := "tolower of %{control:Tmp-String-0} is %{tolower:%{control:Tmp-String-0}}" +} +---- + +.Output + +``` +tolower of CAIPIRINHA is caipirinha +``` + +`tolower` is provided by the `rlm_expr` module. + + + +=== %{toupper: ... } + +Dynamically expands the string and returns the uppercase version of +it. This definition is only available in version 2.1.10 and later. + +.Return: _string_ + +.Example + +[source,unlang] +---- +update control { + &Tmp-String-0 := "caipirinha" +} +update reply { + &Reply-Message := "toupper of %{control:Tmp-String-0} is %{toupper:%{control:Tmp-String-0}}" +} +---- + +.Output + +``` +toupper of caipirinha is CAIPIRINHA +``` + +`toupper` is provided by the `rlm_expr` module. + + + +== String Conversion + +=== %{base64: ... } + +Encode a string using Base64. + +.Return: _string_ + +.Example + +[source,unlang] +---- +update control { + &Tmp-String-0 := "Caipirinha" +} +update reply { + &Reply-Message := "The base64 of %{control:Tmp-String-0} is %{base64:%{control:Tmp-String-0}}" +} +---- + +.Output + +``` +The base64 of foo is Q2FpcGlyaW5oYQ== +``` + +`base64` is provided by the `rlm_expr` module. + + + +=== %{base64tohex: ... } + +Decode a base64 string (e.g. previously encoded using `base64`) to +hex. + +.Return: _string_ + +.Example + +[source,unlang] +---- +update control { + &Tmp-String-0 := "Q2FpcGlyaW5oYQ==" +} +update reply { + &Reply-Message := "The base64tohex of %{control:Tmp-String-0} is %{base64tohex:%{control:Tmp-String-0}}" +} +---- + +.Output + +``` +The base64decode of Q2FpcGlyaW5oYQ== is 436169706972696e6861 +``` + +`base64tohex` is provided by the `rlm_expr` module. + + + +=== %{hex: ... } + +Convert to hex. + +.Return: _string_ + +.Example + +[source,unlang] +---- +update control { + &Tmp-String-0 := "12345" +} +update reply { + &Reply-Message := "The value of %{control:Tmp-String-0} in hex is %{hex:%{control:Tmp-String-0}}" +} +---- + +.Output + +``` +The value of 12345 in hex is 3132333435 +``` + +`hex` is built in to the server core. + + + +=== %{urlquote: ... } + +Quote URL special characters. + +.Return: _string_. + +.Example + +[source,unlang] +---- +update { + &control:Tmp-String-0 := "http://example.org/" +} +update reply { + &Reply-Message += "The urlquote of %{control:Tmp-String-0} is %{urlquote:%{control:Tmp-String-0}}" +} +---- + +.Output + +``` +The urlquote of http://example.org/ is http%3A%2F%2Fexample.org%2F +``` + +`urlquote` is provided by the `rlm_expr` module. + + + +=== %{urlunquote: ... } + +Unquote URL special characters. + +.Return: _string_. + +.Example + +[source,unlang] +---- +update { + &control:Tmp-String-0 := "http%%3A%%2F%%2Fexample.org%%2F" # Attention for the double %. +} +update reply { + &Reply-Message += "The urlunquote of %{control:Tmp-String-0} is %{urlunquote:%{control:Tmp-String-0}}" +} +---- + +.Output + +``` +The urlunquote of http%3A%2F%2Fexample.org%2F is http://example.org/ +``` + +`urlunquote` is provided by the `rlm_expr` module. + + + +== Hashing and Encryption + +=== %{hmacmd5:<shared_key> <string>} + +Generate `HMAC-MD5` of string. + +.Return: _octal_ + +.Example + +[source,unlang] +---- +update { + &control:Tmp-String-0 := "mykey" + &control:Tmp-String-1 := "Caipirinha" +} +update { + &control:Tmp-Octets-0 := "%{hmacmd5:%{control:Tmp-String-0} %{control:Tmp-String-1}}" +} + +update reply { + &Reply-Message := "The HMAC-MD5 of %{control:Tmp-String-1} in octets is %{control:Tmp-Octets-0}" + &Reply-Message += "The HMAC-MD5 of %{control:Tmp-String-1} in hex is %{hex:control:Tmp-Octets-0}" +} +---- + +.Output + +``` +The HMAC-MD5 of Caipirinha in octets is \317}\264@K\216\371\035\304\367\202,c\376\341\203 +The HMAC-MD5 of Caipirinha in hex is 636f6e74726f6c3a546d702d4f63746574732d30 +``` + +`hmacmd5` is provided by the `rlm_expr` module. + + + +=== %{hmacsha1:<shared_key> <string>} + +Generate `HMAC-SHA1` of string. + +.Return: _octal_ + +.Example + +[source,unlang] +---- +update { + &control:Tmp-String-0 := "mykey" + &control:Tmp-String-1 := "Caipirinha" +} +update { + &control:Tmp-Octets-0 := "%{hmacsha1:%{control:Tmp-String-0} %{control:Tmp-String-1}}" +} + +update reply { + &Reply-Message := "The HMAC-SHA1 of %{control:Tmp-String-1} in octets is %{control:Tmp-Octets-0}" + &Reply-Message += "The HMAC-SHA1 of %{control:Tmp-String-1} in hex is %{hex:control:Tmp-Octets-0}" +} +---- + +.Output + +``` +The HMAC-SHA1 of Caipirinha in octets is \311\007\212\234j\355\207\035\225\256\372ʙ>R\"\341\351O) +The HMAC-SHA1 of Caipirinha in hex is 636f6e74726f6c3a546d702d4f63746574732d30 +``` + +`hmacsha1` is provided by the `rlm_expr` module. + + + +=== %{md5: ... } + +Dynamically expands the string and performs an MD5 hash on it. The +result is binary data. + +.Return: _binary data_ + +.Example + +[source,unlang] +---- +update control { + &Tmp-String-0 := "Caipirinha" +} +update reply { + &Reply-Message := "md5 of %{control:Tmp-String-0} is octal=%{md5:%{control:Tmp-String-0}}" + &Reply-Message := "md5 of %{control:Tmp-String-0} is hex=%{hex:%{md5:%{control:Tmp-String-0}}}" +} +---- + +.Output + +``` +md5 of Caipirinha is octal=\024\204\013md||\230\243\3472\3703\330n\251 +md5 of Caipirinha is hex=14840b6d647c7c98a3e732f833d86ea9 +``` + +`md5` is provided by the `rlm_expr` module. + + + +== Miscellaneous Expansions + +=== +%{0}+..+%{32}+ + +`%{0}` expands to the portion of the subject that matched the last regular +expression evaluated. `%{1}`..`%{32}` expand to the contents of any capture +groups in the pattern. + +Every time a regular expression is evaluated, whether it matches or not, +the numbered capture group values will be cleared. + + + +=== +%{regex:<named capture group>}+ + +Return named subcapture value from the last regular expression evaluated. + +Results of named capture groups are available using the `%{regex:<named capture +group>}` expansion. They will also be accessible using the numbered expansions +described xref:builtin.adoc#_0_32[above]. + +Every time a regular expression is evaluated, whether it matches or not, +the named capture group values will be cleared. + +[NOTE] +==== +This expansion is only available if the server is built with libpcre or libpcre2. +Use the output of `radiusd -Xxv` to determine which regular expression library in use. + +.... +... +Debug : regex-pcre : no +Debug : regex-pcre2 : yes +Debug : regex-posix : no +Debug : regex-posix-extended : no +Debug : regex-binsafe : yes +... +Debug : pcre2 : 10.33 (2019-04-16) - retrieved at build time +.... +==== + +`regex` is built in to the server core. + + + +=== +%{nexttime:<time>}+ + +Calculate number of seconds until next n hour(`s`), day(`s`), week(`s`), year(`s`). + +.Return: _string_ + +.Example: + +With the current time at 16:18, `%{nexttime:1h}` will expand to `2520`. + +[source,unlang] +---- +update reply { + &Reply-Message := "You should wait for %{nexttime:1h}s" +} +---- + +.Output + +``` +You should wait for 2520s +``` + +`nexttime` is provided by the `rlm_expr` module. + + + +=== +%{Packet-Type}+ + +The packet type (`Access-Request`, etc.) + + + +=== +%{Packet-Src-IP-Address} and %{Packet-Src-IPv6-Address}+ + +The source IPv4 or IPv6 address of the packet. See also the expansions +`%{client:ipaddr}` and `%{client:ipv6addr}`. The two expansions +should be identical, unless `%{client:ipaddr}` contains a DNS hostname. + + + +=== +%{Packet-Dst-IP-Address} and %{Packet-Dst-IPv6-Address}+ + +The destination IPv4 or IPv6 address of the packet. See also the +expansions `%{listen:ipaddr}` and `%{listen:ipv6addr}`. If the socket +is listening on a "wildcard" address, then these two expansions will be +different, as follows: the `%{listen:ipaddr}` will be the wildcard +address and `%{Packet-DST-IP-Address}` will be the unicast address to +which the packet was sent. + + + +=== +%{Packet-Src-Port} and %{Packet-Dst-Port}+ + +The source/destination ports associated with the packet. + +.Return: _string_. + +.Example + +[source,unlang] +---- +update control { + &Tmp-String-0 := "user@example.com" +} + +if (&control:Tmp-String-0 =~ /^(?<login>(.*))@(?<domain>(.*))$/) { + update reply { + &Reply-Message := "The %{control:Tmp-String-0} { login=%{regex:login}, domain=%{regex:domain} }" + } +} +---- + +.Output + +``` +The user@example.com { login=user, domain=example.com } +``` + +// 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/xlat/character.adoc b/doc/antora/modules/unlang/pages/xlat/character.adoc new file mode 100644 index 0000000..84a148c --- /dev/null +++ b/doc/antora/modules/unlang/pages/xlat/character.adoc @@ -0,0 +1,80 @@ += Single Letter Expansions + +The following are single letter expansions. These expansions do not +use the typical `%{...}` format. Instead, they are short-cuts for +simple, common cases. + +== Miscellaneous + +`%%`:: + +Returns `%`. + + +== Current Time + +`%c`:: + +The current Unix epoch time in seconds. This is an unsigned decimal number. +It should be used with time-based calculations. + +`%C`:: + +The microsecond component of the current epoch time. This is an unsigned +decimal number. It should be used with time-based calculations. + + +== Request Time + +`%l`:: + +The Unix timestamp of when the request was received. This is an unsigned +decimal number. It should be used with time-based calculations. + +`%Y`:: + +Four-digit year when the request was received. + +`%m`:: + +Numeric month when the request was received. + +`%d`:: + +Numeric day of the month when the request was received. + +`%H`:: + +Hour of the day when the request was received. + +`%G`:: + +Minute component of the time when the request was received. + +`%e`:: + +Second component of the time when the request was received. + +`%M`:: + +Microsecond component of the time when the request was received. + +`%D`:: + +Request date in the format `YYYYMMDD`. + +`%S`:: + +Request timestamp in SQL format, `YYYY-mmm-ddd HH:MM:SS`. + +`%t`:: + +Request timestamp in _ctime_ format, `Www Mmm dd HH:MM:SS YYYY`. + +`%T`:: + +Request timestamp in ISO format, `YYYY-mm-ddTHH:MM:SS.000`. + + +// 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/xlat/index.adoc b/doc/antora/modules/unlang/pages/xlat/index.adoc new file mode 100644 index 0000000..b42f725 --- /dev/null +++ b/doc/antora/modules/unlang/pages/xlat/index.adoc @@ -0,0 +1,56 @@ += String Expansion + +String expansion is a feature that allows strings to dynamically +define their value at run time. For historical reasons, these string +expansions are called "xlats". + +String expansion is performed via the following syntax: + +`%{...}` + +Where the `%{` signals the start of a dynamic expansion, and `}` +signals the end of the dynamic expansion. The contents of the +expansion can be many things: + +.String Expansions +[options="header"] +|===== +| Keyword | Description +| xref:xlat/attribute.adoc[attributes] | Expand the value of a named attribute. +| xref:xlat/character.adoc[single character] | Single character expansions. +| xref:xlat/module.adoc[modules] | Pass a string to a module such as `sql`. +| xref:xlat/alternation.adoc[condition] | Conditionally expand a string. +| xref:xlat/builtin.adoc[built-in expansions] | Such as string length, tolower, etc... +|===== + +This feature is used to create policies which refer to concepts rather +than to specific values. For example, a policy can be created that +refers to the User-Name in a request, via: + +`%{User-Name}` + +This string expansion is done only for double-quoted strings and for +the back-tick operator. + +== Caveats + +Unlike other languages, there is no way to define new variables. All +of the string expansions must refer to attributes that already exist, +or to modules that will return a string value. + +== Character Escaping + +Some characters need to be escaped within a dynamically expanded +string `%{...}`. The `%` character is used for variable expansion, so a +literal `%` character can be created by using `%%`. + +Other than within a dynamically expanded string, very little +character escaping is needed. The rules of the enclosing string context +determine whether or not a space or " character needs to be escaped. + +.Example + +`Reply-Message := "%{User-Name} with a literal %%` + +// 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/xlat/module.adoc b/doc/antora/modules/unlang/pages/xlat/module.adoc new file mode 100644 index 0000000..3ce4322 --- /dev/null +++ b/doc/antora/modules/unlang/pages/xlat/module.adoc @@ -0,0 +1,18 @@ += Module References + +Individual modules may be referenced via the following syntax: + +`%{module:string}` + +These references are allowed only by a small number of modules that +usually perform database lookups. The module name is the actual name of +the module, as described earlier. The string portion is specific to each +module and is not documented here. It is, however, usually dynamically +expanded to allow for additional flexibility. + +.Examples + +`%{sql:SELECT name FROM mytable WHERE username = %{User-Name}}` + +// 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/partials/rcode_table.adoc b/doc/antora/modules/unlang/partials/rcode_table.adoc new file mode 100644 index 0000000..e114e74 --- /dev/null +++ b/doc/antora/modules/unlang/partials/rcode_table.adoc @@ -0,0 +1,39 @@ +[options="header"] +[cols="15%,85%"] +|===== +| Return code | Description +| `fail` | The operation failed. Usually as a result of an + external dependency like a database being unavailable + or an internal error. +| `handled` | The request has been "handled", no further policies + in the current section should be called, and the section + should immediately exit. +| `invalid` | The request, or operation, was invalid. In the case of + requests this usually indicates absent or malformed + attribute values. +| `noop` | The operation did nothing. +| `notfound` | A 'lookup' operation returned no results. +| `ok` | Operation completed successfully but did not change any + attributes in the request. +| `reject` | The operation indicates the current request should be + 'rejected'. What this actually means is different from + protocol to protocol. It usually means that access to + the requested resource should be denied, or that the + current request should be NAKd. Usually returned when + provided credentials were invalid. +| `updated` | The operation completed successfully and updated one + or more attributes in the request. +| `disallow` | Access to a particular resource is + denied. This is similar to `reject` but is the result + of an authorizational check failing, as opposed to + credentials being incorrect. +| `yield` | Returned by an operation when execution of a request should + be suspended. +|===== + +[NOTE] +==== +In versions ≤ v3.0.x the `disallow` rcode was called `userlock`. `disallow` and +`userlock` have an identical meaning. `disallow` will be returned in any +instance where `userlock` was returned in v3.0.x +==== |