10. Extensions

The use of extension language allows extending the functionality of
GNU Radius without having to modify its source code. The two extension
languages supported are Rewrite and Scheme. Use of Rewrite is always
enabled. Use of Scheme requires Guile version 1.4 or higher.

10.1.1 Getting Acquainted with Filters

Suppose we wish to implement an authentication method based on the
user name and the user's calling station ID. We have a
database of user names with valid IDs, and the new
method should authenticate a user only if the combination
{user_name, id} is found in this database.

We write a filter program that reads its standard input line by line.
Each input line must consist of exactly two words: the user name
and the calling station ID. For each input line, the
program prints 0 if the {user_name, id} is found in the
database and 1 otherwise. Let's suppose for the sake of example
that the database is a plaintext file and the filter is written in
a shell programming language. Then it will look like

10.1.5 Accounting Filters

Let's suppose we further modify our filter to also handle
accounting requests. To discern between the authentication and
accounting requests we'll prefix each authentication request
with the word ‘auth’ and each accounting request with
the word ‘acct’. Furthermore, the input line for accounting
requests will contain a timestamp.

10.2 Rewrite

Rewrite is the GNU Radius extension language. Its name reflects the
fact that it was originally designed to rewrite the broken request packets
so they could be processed as usual (see section Rewriting Incoming Requests). Beside this basic use, however, Rewrite functions are used
to control various aspects of GNU Radius functionality, such as
verifying the activity of user sessions, controlling the amount of
information displayed in log messages, etc (see section Interaction with Radius).

10.2.1 Syntax Overview

The syntax of Rewrite resembles that of C. Rewrite has two basic data types:
integer and string. It does not have global variables; all variables are
automatic. The only exceptions are the A/V pairs from the incoming request,
which are accessible to Rewrite functions via the special notation
%[attr].

10.2.2 Quick Start

The function takes an integer argument and returns the string
‘the number is odd’ or ‘the number is even’, depending on the
value of i. This illustrates the fact that in Rewrite the
addition operator is defined on the string type. The result of
such operation is the concatenation of operands.

Another example is a function that adds a prefix to the User-Name
attribute:

integer
px_add()
{
%[User-Name] = "pfx-" + %[User-Name];
return 0;
}

This function manipulates the contents of the incoming request; its
return value has no special meaning.

10.2.4 Rewriting Incoming Requests

The need for rewriting the incoming requests arises from the fact that
some NASes are very particular about the information they send with
the requests. There are cases when the information they send
is hardly usable or even completely unusable. For example, a
Cisco AS5300 terminal server used as a voice-over IP router packs
a lot of information into its Acct-Session-Id attribute. Though
the information stored there is otherwise relevant, it makes proper
accounting impossible, since the Acct-Session-Id attributes
in the start and stop packets of the same session become different, and
thus Radius cannot determine the session start to which the given
session stop request corresponds (see section Acct-Session-Id).

In order to cope with such NASes, GNU Radius is able to invoke
a Rewrite function upon arrival of the packet and before
processing it further. This function can transform the packet so that
it obtains the form prescribed by RFCs and its further processing
becomes possible.

For example, in the case of the AS5300 router, a corresponding Rewrite
function parses the Acct-Session-Id attribute; breaks it
down into fields; stores them into proper attributes, creating
them if necessary; and finally replaces Acct-Session-Id with
its real value, which is the same for the start and stop records
corresponding to a single session. Thus all the information that
came with the packet is preserved, but the packet itself is made
usable for proper accounting.

A special attribute, Rewrite-Function, is used to trigger
invocation of a Rewrite function. Its value is a name of the
function to be invoked.

When used in a ‘naslist’ profile, the attribute causes the function
to be invoked when the incoming request matches the huntgroup
(see section Huntgroups). For example, to have a function fixup
invoked for each packet from the NAS10.10.10.11, the
following huntgroup rule may be used:

DEFAULT NAS-IP-Address = 11.10.10.11
Rewrite-Function = "fixup"

The Rewrite-Function attribute may also be used in a ‘hints’
rule. In this case, it will invoke the function if the request matches
the rule (see section Hints). For example, this ‘hints’ rule will
cause the function to be invoked for each request containing the user name
starting with ‘P’:

DEFAULT Prefix = "P"
Rewrite-Function = "fixup"

Note that in both cases the attribute can be used
either in LHS or in RHS pairs of a rule.

The packet rewrite function must be declared as having no arguments
and returning an integer value:

integer fixup()
{
}

The actual return value from such a function is ignored, the integer
return type is just a matter of convention.

The following subsection present some examples of packet rewrite
functions.

10.2.4.1 Examples of Various Rewrite Functions

The examples found in this chapter are working functions that can be
used with various existing NAS types. They are taken from the
‘rewrite’ file contained in distribution of GNU Radius.

1. Port rewriting for MAX Ascend terminal servers

Some MAX Ascend terminal servers pack additional information
into the NAS-Port-Id attribute. The port number is constructed as
XYYZZ, where X = 1 for digital, X = 2 for analog,
YY is the line number
(1 for first PRI/T1/E1, 2 for second, and so on), and ZZ is the
channel number
(on the PRI or channelized T1/E1).

The following rewrite functions are intended to compute the integer
port number in the range (1 .. portcnt), where portcnt
represents the real number of physical ports available on the NAS.
Such a port number can be used, for example, to create a dynamic pool
of IP addresses (see section Framed-IP-Address).

2. Session ID parsing for Cisco AS 5300 series

Cisco VOIP IOS encodes a lot of other information into its
Acct-Session-Id. The pieces of information are separated by
‘/’ characters. The part of Acct-Session-Id up to the first
‘/’ character is the actual session ID.

On the other hand, its accounting packets lack NAS-Port-Id,
though they may contain the vendor-specific pair with code 2
(vendor PEC 9), which is a string in the form ‘ISDN 9:D:999’
(‘9’ represents any decimal digit). The number after the last
‘:’ character can be used as a port number.

The following code parses Acct-Session-Id attribute and stores
the information it contains in various other attributes, generates a
normal Acct-Session-Id, and attempts to generate a
NAS-Port-Id attribute.

10.2.5 Login Verification Functions

A login verification function is invoked to process the output from the
NAS. This process is described in Multiple Login Checking.
The function to be invoked for given NAS is defined by
a function flag in the ‘raddb/nastypes’ or ‘raddb/naslist’
file (see section NAS Types — ‘raddb/nastypes’). It must be defined as follows:

Input string. If the query method is finger, this is the string
of output received from the NAS with trailing newline stripped off. If
the query method is snmp, it is the received variable value
converted to its string representation.

name

User name.

pid

Port ID of the session.

sid

Session ID.

The function should return non-0 if its arguments match the user's
session, and 0 otherwise.

10.2.5.1 Examples of Login Verification Functions

As an example, let's consider the function for analyzing a
line of output from a standard UNIXfinger
service. In each line
of finger output the first field contains the user name; the
third field, the
The function must return 1 if the three fields match the input
user name and port and session IDs:

10.2.6 Attribute Creation Functions

These are the functions used to create Radius reply attributes. An
attribute creation function can take any number of arguments. The type
of its return is determined by the type of Radius attribute the
value will be assigned to. To invoke the function, write its name
in the A/V pair of the RHS in the ‘raddb/users’ file, e.g.:

10.2.8.1 Rewrite Data Types

There are only two data types: integer and string,
the two being coercible to each other in the sense that a string
can be coerced to an integer if it contains a valid ASCII representation
of a decimal, octal, or hex number, and an integer can always be coerced
to a string, the result of such coercion being the ASCII string
that is the
decimal representation of the number.

These are: ‘==’, ‘!=’, ‘<’, ‘<=’, ‘>’,
‘>=’ with the same meaning they have in C. Special operators
are provided for regular-expression matching. The binary
operator ‘=~’ returns true if its left-hand-side operand
matches the regular expression on its right-hand side
(see section Regular Expressions). ‘!~’ returns true if its
left-hand-side operand does not match
the regexp on its right-hand side. The right-hand-side operand of
‘!~’ or ‘=~’ must be a literal string, i.e., the regular
expression must be known at compile time.

Unary operators

The unary operators are ‘-’ and ‘+’ for unary plus and minus,
‘!’ for boolean negation, and ‘*’ for testing for the
existence of an attribute.

Boolean operators

These are ‘&&’ and ‘||’.

Parentheses ‘(’ and ‘)’

These are used to change the precedence of operators, to introduce
type casts (type coercions), to declare functions, and to pass actual
arguments to functions.

Curly braces (‘{’ and ‘}’)

These are used to delimit blocks of code.

Numbers

Numbers follow the usual C convention for integers. A number consisting of
a sequence of digits is taken to be octal if it begins with ‘0’
(digit zero), and decimal otherwise. If the sequence of digits is
preceded by ‘0x’ or ‘0X’, it is taken to be a hexadecimal
integer.

IP Numbers

IP numbers are represented by a standard numbers-and-dots notation.
IP numbers do not constitute a separate data type, rather they are
in all respects similar to initeger numbers.

Characters

These follow the usual C convention for characters, i.e., they consist
either of
an ASCII character itself or of its value, enclosed in a pair of
singlequotes.
The character value begins with ‘\’ (backslash) and
consists either of three octal or of two hexadecimal digits.
A character does not form a special data type; it is represented
internally by an integer.

Quoted strings

These follow slightly modified C conventions for strings. A string is
a sequence of characters surrounded by double quotes, as in
‘"..."’. In a string, the double quote character ‘"’ must be
preceeded by a backslash ‘\’. A ‘\’ and an immediately following
newline are ignored. Following escape sequences have special meaning:

\a

Audible bell character (ASCII 7)

\b

Backspace (ASCII 8)

\e

Escape character (ASCII 27)

\f

Form feed (ASCII 12)

\n

Newline (ASCII 10)

\r

Carriage return (ASCII 13)

\t

Horizontal tab (ASCII 9)

\\

Backslash

\ooo

(‘o’ represents an octal digit)
A character whose ASCII value is represented by the octal number ‘ooo’.

\xHH

\XHH

(‘H’ represents a hex digit)
A character whose ASCII value is represented by the hex number ‘HH’.

\(

Two characters ‘\(’.

\)

Two characters ‘\)’.

If the character following the backslash is not one of those
specified, the backslash is ignored.

Attribute values

The incoming request is passed implicitly to functions invoked via the
Rewrite-Function attribute. It is kept as an associative array,
whose entries can be accessed using the following syntax:

This function strips from arg all leading components up to the
last slash character, and all trailing components after the last dot
character. It returns arg unaltered if it does not contain
slashes and dots. It is roughly analogous to the system basename
utility.

10.2.8.4 Rewrite Declarations

Function declarations

A Rewrite function is declared as follows:

typefunction-name (parameter-list)

where type specifies the return type of the function,
function-name declares the symbolic name of the function, and
parameter-list declares the formal parameters to the function.
It is a comma-separated list of declarations in the form

typeparm-name

type being the parameter type, and parm-name being its
symbolic name. Both function-name and parm-name should
be valid identifiers.

Variable declarations

There are no global variables in Rewrite. All variables are local.
The local variables are declared right after the opening curly brace
(‘{’) and before any executable statements. The declaration
syntax is

typeident_list ;

Here ident_list is either a valid Rewrite identifier or a
comma-separated list of such identifiers.

Note that, unlike in
C, no assignments are allowed in variable declarations.

10.2.8.5 Rewrite Statements

The Rewrite statements are: expressions, assignments, conditional
statements, and return statements. A statement is terminated by a semicolon.

Expressions

An expression is one of the following:

A variable identifier

A type coercion expression

An arithmetic expression

A boolean expression

An assignment

A function call

A delete statement

Type coercion

The type coercion is like a type cast in C. Its syntax is

‘(’ type ‘)’ ident

The result of type coercion is as follows:

type

Variable type

Resulting conversion

integer

integer

No conversion. This results in the same integer value.

integer

string

If the string value of the variable is a valid ASCII representation
of the integer number (either decimal, octal, or hex), it is converted to
the integer; otherwise the result of the conversion is undefined.

string

integer

The ASCII representation (in decimal) of the integer number.

string

string

No conversion. This results in the same string value.

Assignment

An assignment is

ident = expression ;

The variable ident is assigned the value of expression.

Function calls

These take the form

ident ( arg-list )

where ident is the identifier representing the function, and
arg-list is a comma-separated list of expressions supplying
actual arguments to the function. The number of the expressions
must correspond exactly to the number of formal parameters in the
function definition. The function that ident
references can be either a compiled function or a built-in
function.

‘delete’ statement

The ‘delete’ statement is used to delete an attribute or attributes
from the incoming request. Its syntax is:

delete attribute-name;
delete attribute-name(n);

The first variant deletes all the attributes of the given type.
The second variant deletes only the nth occurrence of the matching
attribute.

Replace all non-printable characters in string S by their
corresponding hex value preceeded by a percent sign. Return the
resulting string. Printable are alphabetical characters, decimal
digits and dash (‘-’). Other characters are considered non-printable.
For example:

qprn("a string/value") ⇒ "a%20string%2Fvalue"

Function: string quote_string (string str)

Replace all non-printable characters in string str by their
three-digit octal code prefixed with a backslash, or by their C
escape notation, as appropriate. Non-printable characters
depend on the locale settings. For example, suppose that the current
locale is set to ISO-8859-1 (a so called “Latin-1” character set)
and ∗ represents a tab character. Then:

Native Language Support

The native language support is provided via the functions described
below. These functions are interfaces to GNU gettext library.
For the information about general concepts and principles of
Native Language Support, please refer to
GNU gettext utilities: (gettext)Top section `gettext' in GNU gettext utilities.

The default current textual domain is ‘radius’.

Function: string textdomain (string domain)

Sets the new value for the current textual domain. This domain is used by
the functions gettext and ngettext.
Returns the name of the previously used domain.

Function: string gettext (string msgid)

Function: string _ (string msgid)

The function returns the translation of the string msgid if it
is available in the current domain. If it is not available, the
argument itself is returned.

The second form of this function provides a traditional shortcut
notation.

For a detailed description of the GNU gettext interface,
refer to (gettext)Interface to gettext section `Interface to gettext' in GNU gettext utilities.

Function: string dgettext (string domain, string msgid)

Returns the translation of the string msgid if it
is available in the domain domain. If it is not available, the
argument itself is returned.

The ngettext function is used to translate the messages that
have singular and plural forms. The msgid_singular parameter
must contain the singular form of the string to be converted. It is
also used as the key for the search in the catalog. The
msgid_plural parameter is the plural form. The parameter
number is used to determine the plural form. If no message
catalog is found msgid_singular is returned if
number == 1, otherwise msgid_plural.

For a detailed description of the GNU gettext interface for the
plural translation,
refer to (gettext)Plural forms section `Additional functions for plural forms' in GNU gettext utilities.

10.3 Guile

The name Guile stands for GNU's Ubiquitous Intelligent Language for
Extensions. It provides a Scheme interpreter conforming to the R4RS
language specification. This section describes use of Guile as an
extension language for GNU Radius. It assumes that the reader is
sufficiently familiar with the Scheme language. For information about
the language, refer to
(r4rs)Top section `Top' in Revised(4) Report on the Algorithmic Language Scheme.
For more information about Guile, see (guile)Top section `Overview' in The Guile Reference Manual.

Scheme procedures can be called for processing both authentication
and accounting requests. The invocation of a Scheme procedure for an
authentication request is triggered by the Scheme-Procedure
attribute; the invocation for an accounting request is triggered
by the Scheme-Acct-Procedure attribute. The following sections
address these issues in more detail.

10.3.2 Authentication with Scheme

The Scheme procedure used for authentication must be declared as
follows:

Function Template:auth-function request-list check-list reply-list

Its arguments are:

request-list

The list of A/V pairs from the incoming request

check-list

The list of A/V pairs from the LHS of the profile entry that matched
the request

reply-list

The list of A/V pairs from the RHS of the profile entry that matched
the request

The function return value determines whether the authentication will
succeed. The function must return either a boolean value or a pair.
The return of #t causes authentication to succeed. The return
of #f causes it to fail.

For a function to add something to the reply A/V pairs, it
should return a pair in the form

(cons return-codelist)

where return-code is a boolean value of the same meaning as
described above. list is a list of A/V pairs to be added
to the reply list. For example, the following function will always
deny the authentication, returning an appropriate message to the user: