unlang(5)

πŸ“¦
version 3.2.10 Β· 4 versions
, in alpine 3.22, alpine 3.23, alpine 3.24, arch rolling, centos-stream 10, centos-stream 9, fedora 43, fedora 44, opensuse-leap 16.0

πŸ’… unlang(5) β€” FreeRADIUS Processing un-language πŸ’…

πŸŽ€ NAME πŸŽ€

unlang β€” the FreeRADIUS Processing un-language, bestie. No cap.

✨ DESCRIPTION ✨

Okay so FreeRADIUS literally supports a simple processing language inside its configuration files. We call it an "un-language" because the whole intention is NOT to create yet another programming language β€” we are not doing that, periodt. If you need something more complicated than what's described here, the move is to use the Perl or Python modules rlm_perl, or rlm_python. Slay.

The goal of this language is to let you write simple policies with minimal effort. Those policies then get applied when a request is being processed. Requests are processed through virtual servers (including the default one), in the sections titled "authorize", "authenticate", "post-auth", "preacct", "accounting", "pre-proxy", "post-proxy", and "session". Iconic.

Heads up, bestie: these policies cannot be used in any other part of the configuration files, such as module or client configuration. Like, not even a little. πŸ’€

πŸŽ€ KEYWORDS πŸŽ€

okay so the keywords in this language? it's giving ensemble cast. you've got your pre-defined keywords (the main characters, the ones we actually write docs for here) plus references to loadable module names (the guest stars). and yeah β€” we're only documenting the pre-defined ones in this section, iykyk. πŸ’…

here's the vibe: any keyword can appear in any context, with a few limitations spelled out below. the language is literally just a series of entries, each one on one line, and each entry starts with a keyword. entries get organized into lists, and processing goes line by line, from the start of the list to the end. actions are executed per-keyword. that's it, that's the whole machine 🫢


  • module-name[.section-name] β€” a reference to the named module, bestie. when processing reaches this point, the pre-compiled module gets called. the module may succeed or fail, and it'll hand a status back to unlang when it does. you can totally test that status in a condition β€” see the "Simple Conditions" text in the CONDITIONS section, and MODULE RETURN CODES below. and if you throw in a section-name, it'll make the module execute as if it were listed in that named section. iconic behavior ✨
chap  # call the CHAP module
sql   # call the SQL module
...
  • if β€” checks for a particular condition. if it's true, the block after the condition gets processed. if not? the block is ignored, sorry babe πŸ™ˆ see CONDITIONS below for the whole format of conditions.
if (condition) {
     ...
}
  • else β€” defines a block that only runs if the previous if condition came back false. the designated backup dancer πŸ’œ
else {
     ...
}
  • elsif β€” defines a block that runs only if the previous if condition returned false AND the specified condition evaluates to true. two conditions, one vibe check 🌸
elsif (condition) {
     ...
}
  • foreach β€” loops over the values of an attribute, running the block once for each value. the return value of the block is the return value of the last statement executed. you can exit the loop early with the break keyword β€” but heads up, bestie, this is not like other languages: here break means "exit the loop at the next iteration", not "exit the loop right now". so any statements after the break keyword will still be executed. we lowkey recommend only using break when it's the last statement in a foreach block. no cap. πŸ’€

inside the foreach block, the attribute you're looping over can be referenced as Foreach-Variable-#, where # is the depth of the loop, starting at "0" β€” e.g. Foreach-Variable-0. the loops can be nested up to eight (8) deep, though honestly? not recommended, that's giving chaos πŸŒ€

foreach &Attribute-Reference {
     ...
}
  • switch β€” a switch statement takes one argument and contains a series of case statements. when a switch is encountered, the argument from the switch gets evaluated in turn against the argument from each case statement. the first case that matches is executed, and all the other case statements are ignored. you can define a default case by just omitting its argument. main character energy fr fr πŸ‘‘

so like β€” if the argument is a double quoted string (e.g. "%{exec:1 + 2}"), it gets expanded as described in the DATA TYPES section below, and then the match is performed on the string returned from that expansion. if the argument is an attribute reference (e.g. &User-Name), then the match is performed on the value of that attribute. otherwise, the argument is taken to be a literal string, and matching happens via simple comparison.

no statement other than case can appear in a switch block. that's the rule, periodt. βœ‹

switch <argument> {
     ...
}
  • case β€” provides a place-holder which matches the argument of a parent switch statement.

a case statement cannot appear outside of a switch block β€” it simply will not survive out there, it needs its parent πŸ₯Ί

same expansion rules as above: double quoted string argument (e.g. "%{exec:1 + 2}") β†’ expanded per the DATA TYPES section below, then matching is done on the returned string. attribute reference argument (e.g. &User-Name) β†’ match on the value of that attribute. otherwise β†’ literal string, matched via simple comparison.

case <argument> {
     ...
}

you can define a default entry by omitting the argument, like this πŸ‘‡ this entry gets used if no other case entry matches, and only one default entry can exist in a switch section β€” don't get greedy πŸ“

case {
     ...
}
  • update β€” updates a particular attribute list, based on the attributes given in the current block.
update <list> {
     &Attribute-Reference = value
     ...
}

the <list> can be one of request, reply, proxy-request, proxy-reply, coa, disconnect, session-state, or control. as of Version 3, the <list> can be omitted entirely, in which case request is assumed. convenient, we love that for us ✨

the control list is the list of attributes maintained internally by the server that controls how the server processes the request. any attribute that doesn't go in a packet on the network generally gets placed in the control list. it's the backstage pass of attribute lists 🎟️

for EAP methods with tunneled authentication sessions (i.e. PEAP and EAP-TTLS), the inner tunnel session can also reference outer.request, outer.reply, and outer.control. those references let you address the relevant list in the outer tunnel session. nesting, but make it fashion πŸ¦‹

the coa and disconnect sections 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. don't mix them up, bestie 😭

adding one or more attributes to either the coa or disconnect list causes the server to originate a CoA-Request or Disconnect-Request packet. that packet gets 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. it's giving ✨spontaneous packet generation✨ πŸ”₯

the session-state list is primarily used for EAP. attributes put into the session-state list are saved for the next packet in the session, and they're automatically retrieved when the next packet is received. memory, but make it protocol 🧠

the only contents permitted in an update section are attributes and values. the contents of the update section are described in the ATTRIBUTE REFERENCE and ATTRIBUTE ASSIGNMENT sections below. no extras, no little treats, just attributes and values. πŸŽ€

  • redundant β€” contains a simple list of modules. the first module is called when the section is being processed. if that first module succeeds in its operation, the server stops processing the section and returns to the parent section.

if, however, the module fails, the next module in the list gets tried, same as described above. processing continues until one module succeeds, or until the list has been exhausted. it's the "try, try again" of sections πŸ’ͺ

redundant sections can contain only a list of modules β€” they cannot contain keywords that perform conditional operations (if, else, etc) or update an attribute list. strict queen behavior πŸ‘‘

redundant {
     sql1 # try this
     sql2 # try this only if sql1 fails.
     ...
}
  • load-balance β€” contains a simple list of modules. when the section is entered, one module is chosen at random to process the request. all of the modules in the list should be the same type (e.g. ldap or sql), and they should all behave identically β€” otherwise the load-balance section will return different results for the same request, and that's a mess nobody needs πŸ’€

load-balance sections can contain only a list of modules β€” no conditional keywords (if, else, etc), no updating attribute lists. same rules as before 🎯

load-balance {
     ldap1     # 50% of requests go here
     ldap2     # 50% of requests go here
}

in general, we recommend using redundant-load-balance instead of load-balance. trust, it's the upgraded version 🌈

  • redundant-load-balance β€” contains a simple list of modules. when the section is entered, one module is chosen at random to process the request. if that module succeeds, the server stops processing the section. if, however, the module fails, then one of the remaining modules is chosen at random to process the request. this process repeats until one module succeeds, or until the list has been exhausted. it's the best of both worlds and we're obsessed πŸ’–

all of the modules in the list should be the same type (e.g. ldap or sql). all of them should behave identically β€” otherwise the load-balance section will return different results for the same request. (yes, same warning, it's important, we're saying it again 🫢)

load-balance sections can contain only a list of modules, and cannot contain keywords that perform conditional operations (if, else, etc) or update an attribute list. please see raddb/radiusd.conf "instantiate" section for more configuration examples. go read it, it's giving lore πŸ“–

redundant-load-balance {
     ldap1     # 50%, unless ldap2 is down, then 100%
     ldap2     # 50%, unless ldap1 is down, then 100%
}
  • return β€” returns from the current top-level section, e.g. authorize or authenticate. this keyword is mainly used to avoid layers of nested if and else statements. it's the escape hatch, the "i'm out", the ✨graceful exit✨ πŸšͺ
authorize {
     if (...) {
          ...
          return
     }
     ...  # this is never reached when the "if"
     ...  # statement is executed
}

πŸ’… ATTRIBUTE REFERENCES πŸ’…

Okay bestie, so attributes can be referenced with this syntax, and you're gonna want to keep it exactly like this:

&Attribute-Name
&Attribute-Name:TAG
&Attribute-Name[NUM]
&<list>:Attribute-Name
&<list>:Attribute-Name:TAG[NUM]

Where <list> is one of request, reply, control, proxy-request, proxy-reply, or outer.request, outer.reply, outer.control, outer.proxy-request, or outer.proxy-reply β€” just as with the update section, above. The <list>: prefix is optional, and if you omit it, it's assumed to refer to the request list. πŸ’–

The TAG portion is a decimal integer between 1 and 31. Please see RFC 2868 for more information about tags. Tags can only be used for attributes which are marked in the dictionary as has_tag. ✨

The NUM portion is used when there are multiple attributes of the same name in a list. The Attribute-Name reference will return the first attribute. Using an array offset lets the policy refer to the second and subsequent attributes. πŸŽ€

  • If * is used in the NUM portion, it evaluates to all instances of the attribute in the request.
  • If n is used in the NUM portion, it evaluates to the last instance of the attribute in the request.

When an attribute name is encountered, the given list is examined for an attribute of the given name. Some examples are:

User-Name
request:User-Name # same as above
reply:User-Name
Tunnel-Password:1
Cisco-AVPAir[2]
outer.request:User-Name # from inside of a TTLS/PEAP tunnel
Note that unlike C, there is no way to define new attributes at run-time. They MUST be declared in a dictionary file, and loaded when the server starts. No cap. πŸ’€

All attributes are defined in the dictionaries that accompany the server. These definitions define only the name and type, and do not define the value of the attribute. When the server receives a packet, it uses the packet contents to look up entries in the dictionary, and instantiates attributes with a name taken from the dictionaries, and a value taken from the packet contents. This process means that if an attribute does not exist, it is usually because it was not contained in a packet that the server received. 🌸

Once the attribute is instantiated, it is added to a list. It can then be referenced, updated, replaced, etc. Main character energy, honestly. πŸ‘‘


🌈 CONDITIONS 🌈

The conditions are similar to C conditions in syntax, though quoted strings are supported, as with the Unix shell. 🫢

πŸŽ€ Simple conditions πŸŽ€

(foo) β€” evaluates to true if 'foo' is a non-empty string (single quotes, double quotes, or back-quoted). Also evaluates to true if 'foo' is a non-zero number. Note that the language is poorly typed, so the string "0000" can be interpreted as a numerical zero. This issue can be avoided by comparing strings to an empty string, rather than by evaluating the string by itself. 😭

If the word 'foo' is not a quoted string, then it can be taken as a reference to a named attribute. See "Referencing attribute lists", below, for examples of attribute references. The condition evaluates to true if the named attribute exists. πŸ’–

Otherwise, if the word 'foo' is not a quoted string, and is not an attribute reference, then it is interpreted as a reference to a module return code. The condition evaluates to true if the most recent module return code matches the name given here. Valid module return codes are given in MODULE RETURN CODES, below. ✨

πŸ™ˆ Negation πŸ™ˆ

(!foo) β€” evaluates to true if 'foo' evaluates to false, and vice-versa. The audacity of a simple !, honestly. πŸ’…

πŸ”₯ Short-circuit operators πŸ”₯

  • (foo || bar)
  • (foo && bar)

&& and || are short-circuit operators. && evaluates the first condition, and evaluates the second condition if and only if the result of the first condition is true. || is similar, but executes the second command if and only if the result of the first condition is false. Periodt. πŸ’œ

⭐ Comparisons ⭐

(foo == bar) β€” compares 'foo' to 'bar', and evaluates to true if the comparison holds true. Valid comparison operators are ==, !=, <, <=, >, >=, =~, and !~, all with their usual meanings. The operators :=, ^= and = are assignment operators, and are not allowed for comparisons. Don't try it, bestie. 🫢

The operators <, <=, >, and >= are also allowed for checking that an IP address is contained within a network. For example:

if (<ipaddr>192.0.2.1 < 192.0.2.0/24) {

This comparison succeeds, because the address 192.0.2.1 is contained within the network 192.0.2.0/24. Slay. 🌈

πŸ¦‹ Attribute Comparisons πŸ¦‹

When doing attribute comparisons, the data type of the attribute is used to determine the data type of the right-hand side argument.

(&User-Name == "foo") β€” compares the value of the User-Name attribute to the string 'foo', and evaluates to true if the comparison holds true. πŸ’–

Similarly:

(&Framed-IP-Address == 192.0.2.1) β€” compares the value of the Framed-IP-Address attribute to the IP address 192.0.2.1. This IP address does not need to be quoted. ✨

πŸ’€ Inter-Attribute Comparisons πŸ’€

(&User-Name == &Filter-Id) β€” compares the value of the User-Name attribute to the contents of the Filter-Id attribute, and evaluates to true if the comparison holds true. Unlike the previous example, this comparison is done in a type-safe way. For example, comparing the IP addresses 1.2.3.4 and 127.0.0.1 as strings will return different results than comparing them as IP addresses. 😭

The & character in the condition means that the comparison "refers" to the Filter-Id attribute. If left off, it means that the User-Name attribute is compared to the literal string "Filter-Id". πŸ™ˆ

Where the left-hand side is an attribute, the & can be omitted. However, it is allowed for backwards compatibility. e.g. The comparison (&User-Name == &Filter-Id) is equivalent to the example above. πŸ’…

We recommend using attribute references instead of printing attributes to a string, e.g. (User-Name == "%{Filter-Id}"). Attribute references will be faster and more efficient. No cap. πŸ”₯

The conditions will check only the first occurrence of an attribute. If there is more than one instance of an attribute, the following syntax should be used:

(&Attribute-Name[*] == "foo")

Using the [*] syntax means that it checks all of the instances of the named attribute. If one attribute matches, the condition succeeds. If none match, the condition fails. That's the vibe check. βœ…

🧚 Casts 🧚

(<type>foo == bar) β€” the left-hand-side of a condition can be "cast" to a specific data type. The data type must be one which is valid for the dictionaries. e.g. integer, ipaddr, etc. πŸŽ€

The comparison is performed in a type-safe way, as with "Inter-Attribute Comparisons", above. Both sides of the condition are parsed into temporary attributes, and the attributes compared via type-specific methods. The temporary attributes have no other effect, and are not saved anywhere. 🌸

Casting allows conditions to perform type-specific comparisons. In previous versions of the server, the data would have to be manually placed into an intermediate attribute (or attributes), and then the attribute (or attributes) compared. The use of a cast allows for simpler policies. Iconic behavior. πŸ‘‘

Casts are allowed only on the left-hand side argument of a condition. Don't push it. πŸ’…

Conditions may be nested to any depth, subject only to line length limitations (8192 bytes). πŸ¦‹

✨ DATA TYPES ✨

Okay so plot twist: this language is lowkey minimal. There are only a few data types, bestie β€” attribute references, numbers, and strings. That's the whole squad. 🌸

Any data type can show up in a stand-alone condition, and when it does, it gets evaluated exactly like we talked about up in "Simple conditions". They can also appear β€” with some exceptions, which we'll call out below β€” on the left-hand side or the right-hand side of a comparison. Slay.

numbers

Numbers are made of decimal digits, periodt. Floating point, hex, and octal numbers? Not supported. Not invited to the party. πŸ™ˆ

The maximum value for a number is machine-dependent, but it's usually 32-bits, including one bit for a sign value.

word

Text that isn't wrapped in quotes is interpreted differently depending on where it lands in a condition. It's giving "context is everything" energy:

  • On the left hand side of a condition, it's interpreted as a reference to an attribute.
  • On the right hand side, it's interpreted as a simple string, in the same manner as a single-quoted string.

Using attribute references permits limited type-specific comparisons, as seen in the examples below:

     if (&User-Name == "bob") {
          ...
     if (&Framed-IP-Address > 127.0.0.1) {
          ...
     if (&Service-Type == Login-User) {

"strings"

Double-quoted strings get expanded by inserting the value of any attributes (see ATTRIBUTE REFERENCES, below) before being evaluated. And if the result is a number, it's evaluated in a numerical context. πŸ’…

String length is limited by line-length, usually about 8000 characters. A double quote character can be used in a string via the normal back-slash escaping method. Like this: "like \"this\" !"

'strings'

Single-quoted strings are evaluated as-is. Their values are not expanded the way double-quoted strings above are, and they are not interpreted as attribute references. What you see is what you get, no cap. πŸŽ€

`strings`

Back-quoted strings are evaluated by expanding the contents of the string, as described above for double-quoted strings. The resulting command given inside of the string runs in a sub-shell, and the output is taken as a string. This behavior is much the same as that of Unix shells. 🐚

Heads up bestie β€” for security reasons, the input string is split into command and arguments before string expansion is done. That ordering matters, fr fr.

And for performance reasons, we suggest 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 you believe that you need to execute many programs, we suggest finding alternative ways to achieve the same result. In some cases, using a real language may be sufficient.

/regex/im

These strings are valid only on the right-hand side of a comparison, and then only when the comparison operator is =~ or !~. They are regular expressions, as implemented by the local regular expression library on the system. This is usually Posix regular expressions. πŸ”₯

The trailing i is optional, and indicates that the regular expression match should be done in a case-insensitive fashion.

The trailing m is also optional, and indicates that carrot ^ and dollar $ anchors should match on new lines as well as at the start and end of the subject string.

If the comparison operator is =~, then parentheses in the regular expression will define variables containing the matching text, as described below in the ATTRIBUTE REFERENCES section. Iconic behavior honestly.


πŸ’– EXPANSIONS πŸ’–

Attributes are expanded using the ATTRIBUTE REFERENCE syntax described above, and surrounding the reference with "%{...}":

%{Attribute-Reference}

The result will be a string which contains the value of the attribute which was referenced, as a printable string. If the attribute does not exist, the result will be an empty string. No drama, just vibes. πŸ¦‹

Results of regular expression matches

If a regular expression match has previously been performed, then the special variable %{0} will contain a copy of the matched portion of the input string. The variables %{1} through %{32} will contain the substring matches, starting from the left-most capture group, onwards. If there are more than 32 capture groups, the additional results will not be accessible. πŸ™ˆ

If the server is built with libpcre, the results of named capture groups are available using the "%{regex:capture group}" expansion. They will also be accessible using the variables described above.

Every time a regular expression is evaluated, whether it matches or not, the capture group values will be cleared. So don't be out here expecting yesterday's captures to still be there, bestie. πŸ’€

Obtaining results from databases

It is useful to query a database for some information, and to use the result in a condition. The following syntax will call a module, pass it the given string, and replace the string expansion with the resulting string returned from the module:

     %{module: string ...}

The syntax of the string is module-specific. Please read the module documentation for additional details. πŸ‘‘

Conditional Syntax

Conditional syntax similar to that used in Unix shells may also be used. We love a crossover episode.

  • %{%{Foo}:-bar} β€” If %{Foo} has a value, returns that value. Otherwise, returns literal string "bar".
  • %{%{Foo}:-%{Bar}} β€” If %{Foo} has a value, returns that value. Otherwise, returns the expansion of %{Bar}.

These conditional expansions can be nested to almost any depth, such as with %{%{One}:-%{%{Two}:-%{Three}}} β€” it's giving inception, honestly. πŸŒ€

String lengths and arrays

Similar to a Unix shell, there are ways to reference string lengths, and the second or more instance of an attribute in a list. If you need more than this functionality, we suggest using a real language. Sorry not sorry. πŸ’…

  • %{strlen:string} β€” The number of characters in "string". If "string" does not exist, then the length also does not exist, instead of being zero. The "string" is expanded before the length is taken.
  • %{integer:Attribute-Name} β€” The integer value of the Attribute-Name, instead of the enumerated name. e.g. If a request contains "Service-Type = Login-User", the expansion of %{integer:Service-Type} will yield "1".
  • %{hex:Attribute-Name} β€” The hex value of the Attribute-Name, as a series of hex digits. e.g. If a request contains "Framed-IP-Address = 127.0.0.1", the expansion of %{hex:Framed-IP-Address} will yield "0x7f000001".
  • %{Attribute-Name[#]} β€” The number of instances of Attribute-Name. e.g. If a request contains "User-Name = bob", the expansion of %{User-Name[#]} will yield "1".
  • %{Attribute-Name[*]} β€” All values of Attribute-Name, concatenated together with , as the separator.
  • %{List-Name:[#]} β€” The number of attributes in the named list.
  • %{List-Name:[*]} β€” All values of attributes in the named-list, concatenated together with , as the separator. Use the %{pairs:} xlat to get a list of attributes and values.

e.g. If a response contains "Reply-Message = 'Hello', Reply-Message = 'bob'" the expansion of "%{reply:Reply-Message[*]}" will yield "Hello\nbob" 🌈

πŸ’… ATTRIBUTE ASSIGNMENTS πŸ’…

okay bestie, so the attribute lists we were just talking about? You can edit them by listing one or more attributes inside an update section. Once the attributes have been defined, you can reference them like we covered in ATTRIBUTE REFERENCES. ✨

The syntax that defines attributes in an update section is this exact vibe:

Attribute-Reference = value

Each attribute and value has to be all on one line in the configuration file. No commas, no semi-colons after the value β€” none of that. πŸ’…

πŸŽ€ Attribute Reference πŸŽ€

The Attribute-Reference must be a reference (see above), using a name that was previously defined in a dictionary. If you use an undefined name? The server will return an error, and will not start. Not a drill. πŸ’€

⚑ Operators ⚑

The operator you use to assign the value can be one of these, with these exact meanings:

  • = β€” 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. When the right hand side of the expression resolves to multiple values, it means add all values to the tail of the list.
  • ^= β€” Add the attribute to the head of the list, even if attributes of the same name are already present in the list. When the right hand side of the expression resolves to multiple values, it means prepend all values to the head of the list.

πŸ”₯ Enforcement and Filtering Operators πŸ”₯

These operators can also be used in addition to the ones above. Their whole job is enforcement or filtering on attributes in a list.

  • -= β€” Remove all matching attributes from the list. Both the attribute name and value have to match in order for the attribute to be removed from the list.
  • == β€” Keep all matching attributes. Both the attribute name and value have to match in order for the attribute to remain in the list.
Note that this operator is very different than the = operator listed above! πŸ‘€
  • != β€” Keep all attributes with matching name, and value not equal to the given one.
  • < β€” Keep all attributes having values less than the value given here. Any larger value is replaced by the value given here. If no attribute exists, it is added with the value given here, as with +=.
  • <= β€” Keep all attributes having values less than, or equal to, the value given here. Any larger value is replaced by the value given here. If no attribute exists, it is added with the value given here, as with +=.
  • > β€” Keep all attributes having values greater than the value given here. Any smaller value is replaced by the value given here. If no attribute exists, it is added with the value given here, as with +=.
  • >= β€” Keep all attributes having values greater than, or equal to, the value given here. Any smaller value is replaced by the value given here. If no attribute exists, it is added with the value given here, as with +=.
  • !* β€” Delete all occurrences of the named attribute, no matter what the value.
  • =~ β€” Keep all attributes having values which match the given regular expression. If no attribute matches, nothing else is done.
  • !~ β€” Keep all attributes having values which fail to match the given regular expression. If no attribute matches, nothing else is done.

🌈 Values 🌈

The value can be an attribute reference, or an attribute-specific string. Both are valid, bestie. 🫢

When the value is an attribute reference, it must take the form of &Attribute-Name. The leading & is the little signal that the value is a reference. The Attribute-Name is an attribute name, such as User-Name or request:User-Name. When an attribute reference is used, both attributes must have the same data type. For example, User-Name := &NAS-Port is invalid, because User-Name is a string, and NAS-Port is an integer.

We recommend using the form Attribute-1 = &Attribute-2 for updates, instead of Attribute-1 = "%{Attribute-2}". The first version will copy the attribute data, no matter what its form. The second version will print the Attribute-2 to a string, and then parse it to create the value for Attribute-1. This second version is slower and more fragile than the first one. So yeah β€” first one ate. πŸ’…

When the value is an attribute-specific string, it can be a string, integer, IP address, etc. The value may be expanded as described above in the DATA TYPES section. For example, specifying Framed-IP-Address = 127.0.0.1 will cause the Framed-IP-Address attribute to be set to the IP address 127.0.0.1. However, using:

Framed-IP-Address := module to be run with a string "127.0.0.1"

The output of the echo module will then be parsed as an IP address, and placed into the Framed-IP-Address attribute. (Yes, that's the exact example from the page β€” I'm not out here inventing syntax. 😭)

This flexibility means that you can assign an IP address by specifying it directly, or by having the address returned from a database query, or by having the address returned as the output of a program that is executed. Literally so many paths. ✨

When string values are finally assigned to an attribute, they can have a maximum length of 253 characters. This limit is due in part to both protocol and internal server requirements. That is, the strings in the language can be nearly 8k in length, say for a long SQL query. However, the output of that SQL query should be no more than 253 characters in length. Keep it cute and short. πŸŽ€


🌸 OTHER KEYWORDS 🌸

Other keywords in the language are taken from the names of modules loaded by the server. These keywords are dependent on both the modules, and the local configuration. So iykyk β€” your local setup matters. πŸ’œ

Some keywords defined in the default configuration file are:

  • fail β€” Cause 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.

πŸ¦‹ MODULE RETURN CODES πŸ¦‹

When a module is called, it returns one of the following codes to unlang, with the following meaning:

  • notfound β€” information was not found
  • noop β€” the module did nothing
  • ok β€” the module succeeded
  • updated β€” the module updated the request
  • fail β€” the module failed
  • reject β€” the module rejected the request
  • userlock β€” the user was locked out
  • invalid β€” the configuration was invalid
  • handled β€” the module has handled the request itself

These return codes can be tested for in a condition, as described above in the CONDITIONS section. πŸ‘‘

See also the file doc/configurable_failover for additional methods of trapping and modifying module return codes.

πŸŽ€ FILES πŸŽ€

/etc/raddb/radiusd.conf

✨ SEE ALSO ✨

radiusd.conf(5), dictionary(5)

πŸ’œ AUTHOR πŸ’œ

Alan DeKok <aland@deployingradius.com>


okay that's the end of the page, bestie β€” iconic, thorough, and still exactly the same facts. Catch you in the next request: this was unlang(5), dated 16 February 2021 πŸ’–πŸ«Ά

πŸ“¦ where this page comes from