summaryrefslogtreecommitdiffstats
path: root/doc/configuration
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:49:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:49:46 +0000
commit50b37d4a27d3295a29afca2286f1a5a086142cec (patch)
tree9212f763934ee090ef72d823f559f52ce387f268 /doc/configuration
parentInitial commit. (diff)
downloadfreeradius-50b37d4a27d3295a29afca2286f1a5a086142cec.tar.xz
freeradius-50b37d4a27d3295a29afca2286f1a5a086142cec.zip
Adding upstream version 3.2.1+dfsg.upstream/3.2.1+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'doc/configuration')
-rw-r--r--doc/configuration/acct_type.rst71
-rw-r--r--doc/configuration/autz_type.rst88
-rw-r--r--doc/configuration/configurable_failover.rst476
-rw-r--r--doc/configuration/dynamic_home_servers.md238
-rw-r--r--doc/configuration/load_balance.rst172
-rw-r--r--doc/configuration/post_auth_type44
-rw-r--r--doc/configuration/session_type10
-rw-r--r--doc/configuration/simultaneous_use173
-rw-r--r--doc/configuration/snmp58
-rw-r--r--doc/configuration/variables.rst151
10 files changed, 1481 insertions, 0 deletions
diff --git a/doc/configuration/acct_type.rst b/doc/configuration/acct_type.rst
new file mode 100644
index 0000000..e8abff4
--- /dev/null
+++ b/doc/configuration/acct_type.rst
@@ -0,0 +1,71 @@
+Acct-Type
+=========
+
+FreeRADIUS supports the Acct-Type attribute to select between
+accounting methods based on arbitrary attribute/value pairs contained
+in an accounting packet. Its use follows the same general configuration
+syntax as Auth-Type and Autz-Type. The main difference in configuration
+between Acct-Type and Auth/Autz-Type lies in where the Acct-Type
+method is assigned. With Auth/Autz-Type, the method is typically
+assigned in the 'users' file. The 'users' file, naturally, is not
+processed during the handling of the accounting {} section. However,
+part of the default files {} module is the 'acct_users' file, which
+serves the same purpose as the 'users' file, but applies to accounting
+packets.
+
+For example, a server administrator is responsible for handling the
+accounting data for two different realms, foo.com and bar.com, and
+wishes to use different instances of the SQL module for each. In
+addition, there is one RADIUS client sending accounting data that is
+to be logged only to a specific detail file. Everything else should
+use a third SQL instance.
+
+The acct_users file would look something like this::
+
+ DEFAULT Realm == "foo.com", Acct-Type := "SQLFOO"
+
+ DEFAULT Realm == "bar.com", Acct-Type := "SQLBAR"
+
+ DEFAULT Client-IP-Address == "10.0.0.1", Acct-Type := "OTHERNAS"
+
+And in radiusd.conf::
+
+ $INCLUDE ${confdir}/sql0.conf # Instance named 'sql0'.
+ $INCLUDE ${confdir}/sql1.conf # Instance named 'sql1'.
+ $INCLUDE ${confdir}/sql2.conf # Instance named 'sql2'.
+
+ detail othernas {
+ filename = ${radacctdir}/10.0.0.1/detail-%Y%m%d
+ }
+
+ preacct {
+ suffix # Add the Realm A/V pair.
+ files # Add the Acct-Type A/V pair based on the Realm A/V pair.
+ }
+
+ accounting {
+
+ # If Acct-Type is SQLFOO use the 'sql1' instance of the SQL module.
+
+ Acct-Type SQLFOO {
+ sql1
+ }
+
+ # If Acct-Type is SQLBAR, use the 'sql2' instance of the SQL module.
+
+ Acct-Type SQLBAR {
+ sql2
+ }
+
+ # If Acct-Type is OTHERNAS, use the 'othernas' instance of the detail
+ # module
+
+ Acct-Type OTHERNAS {
+ othernas
+ }
+
+ # If we've made it this far, we haven't matched an Acct-Type, so use
+ # the sql0 instance.
+
+ sql0
+ }
diff --git a/doc/configuration/autz_type.rst b/doc/configuration/autz_type.rst
new file mode 100644
index 0000000..a91e4e5
--- /dev/null
+++ b/doc/configuration/autz_type.rst
@@ -0,0 +1,88 @@
+Autz-Type
+=========
+
+Like Auth-Type for authentication method selection freeradius also
+supports the Autz-Type to select between authorization methods. The only
+problem is that authorization is the first thing to be called when an
+authentication request is handled. As a result we first have to call the
+authorize section without checking for Autz-Type. After that we check for
+Autz-Type and if it exists we call the corresponding subsection in the
+authorize section. In other words the authorize section in radiusd.conf
+should look like this::
+
+ authorize{
+ suffix
+ preprocess
+ # whatever other authorize modules here
+ Autz-Type Ldap{
+ ldap
+ }
+ Autz-Type SQL{
+ sql
+ }
+ files
+ }
+
+What happens is that the first time the authorize section is examined the
+suffix, preprocess and files modules are executed. If Autz-Type is set
+after that the server core checks for any matching Autz-Type subsection.
+If one is found it is called. The users file should look something
+like this::
+
+ DEFAULT Called-Station-Id == "123456789", Autz-Type := Ldap
+
+ DEFAULT Realm == "other.example.com", Autz-Type := SQL
+
+Autz-Type could also be used to select between multiple instances of
+a module (ie sql or ldap) which have been configured differently. For
+example based on the user realm different ldap servers (belonging to
+different companies) could be queried. If Auth-Type was also set then we
+could do both Authentication and Authorization with the user databases
+belonging to other companies. In detail:
+
+radiusd.conf::
+
+ authenticate{
+ Auth-Type customer1{
+ ldap1
+ }
+ Auth-Type customer2{
+ ldap2
+ }
+ }
+
+ authorize{
+ preprocess
+ suffix
+ Autz-Type customer1{
+ ldap1
+ }
+ Autz-Type customer2{
+ ldap2
+ }
+ files
+ }
+
+The users file::
+
+ DEFAULT Realm == "customer1", Autz-Type := customer1, Auth-Type := customer1
+
+ DEFAULT Realm == "customer2", Autz-Type := customer2, Auth-Type := customer2
+
+
+Apart from Autz-Type the server also supports the use of
+Acct-Type, Session-Type and Post-Auth-Type for the corresponding sections.
+The corresponding section names in the radiusd.conf file are the same. So for example:
+
+users file::
+
+ DEFAULT Called-Station-Id == "236473", Session-Type := SQL
+
+radiusd.conf::
+
+ session {
+ radutmp
+ Session-Type SQL {
+ sql
+ }
+ }
diff --git a/doc/configuration/configurable_failover.rst b/doc/configuration/configurable_failover.rst
new file mode 100644
index 0000000..4e21335
--- /dev/null
+++ b/doc/configuration/configurable_failover.rst
@@ -0,0 +1,476 @@
+Configurable Module Fail Over
+=============================
+
+Before configurable module failover, we had this kind of entry in
+``radiusd.conf``:
+
+::
+
+ #---
+ authorize {
+ preprocess
+ files
+ }
+ #---
+
+This entry instructed the ``authorize`` section to first process the
+request through the ``preprocess`` module, and if that returned success,
+to process it through ``files`` module. If that sequence returned
+success, then the ``authorize`` stage itself would then return success.
+Processing was strictly linear and if one module failed, the whole
+section would fail immediately.
+
+Configurable failover provides more flexibility. It takes advantage
+of the tree structure of radiusd.conf to support a configuration
+language that allows you to ``group`` modules that should work together
+in ways other than simple lists. You can control the flow of any
+stage (e.g. ``authorize``) to fit your needs, without touching C code,
+just by altering radiusd.conf.
+
+This configurable fail-over has a convenient short-hand, too.
+Administrators commonly want to say things like "try SQL1, if it's
+down, try SQL2, otherwise drop the request."
+
+For example:
+
+::
+
+ #---
+ modules {
+ sql sql1 {
+ # configuration to connect to SQL database one
+ }
+ sql sql2 {
+ # configuration to connect to SQL database two
+ }
+ always handled {
+ rcode = handled
+ }
+ }
+
+ # Handle accounting packets
+ accounting {
+ detail # always log to detail, stopping if it fails
+ redundant {
+ sql1 # try module sql1
+ sql2 # if that's down, try module sql2
+ handled # otherwise drop the request as
+ # it's been ``handled`` by the ``always``
+ # module (see doc/rlm_always)
+ }
+ }
+ #---
+
+The ``redundant`` section is a configuration directive which tells the
+server to process the second module if the first one fails. Any
+number of modules can be listed in a ``redundant`` section. The server
+will process each in turn, until one of the modules succeeds. It will then stop processing the ``redundant`` list.
+
+Rewriting results for single modules
+------------------------------------
+
+Normally, when a module fails, the entire section (``authorize``,
+``accounting``, etc.) stops being processed. In some cases, we may want
+to permit "soft failures". That is, we may want to tell the server
+that it is "ok" for a module to fail, and that the failure should not
+be treated as a fatal error.
+
+In this case, the module is treated as a "section", rather than just
+as a single lne in ``radiusd.conf``. The configuration entries for
+that section are taken from the ``configurable fail-over`` code, and not
+from the configuration information for that module.
+
+For example, the ``detail`` module normally returns ``fail`` if it is
+unable to write its information to the ``detail`` file. As a test, we
+can configure the server so that it continues processing the request,
+even if the ``detail`` module fails. The following example shows how:
+
+::
+
+ #--
+ # Handle accounting packets
+ accounting {
+ detail {
+ fail = 1
+ }
+ redundant {
+ sql1
+ sql2
+ handled
+ }
+ }
+ #--
+
+The ``fail = 1`` entry tells the server to remember the ``fail`` code,
+with priority ``1``. The normal configuration is ``fail = return``, which
+means ``if the detail module fails, stop processing the accounting
+section``.
+
+Fail-over configuration entries
+-------------------------------
+
+Modules normally return on of the following codes as their result:
+
++-----------+-----------------------------------------------------+
+|Code | Meaning |
++===========+=====================================================+
+|notfound | the user was not found |
++-----------+-----------------------------------------------------+
+|noop | the module did nothing |
++-----------+-----------------------------------------------------+
+|ok | the module succeeded |
++-----------+-----------------------------------------------------+
+|updated | the module updated information in the request |
++-----------+-----------------------------------------------------+
+|fail | the module failed |
++-----------+-----------------------------------------------------+
+|reject | the module rejected the user |
++-----------+-----------------------------------------------------+
+|userlock | the user was locked out |
++-----------+-----------------------------------------------------+
+|invalid | the user's configuration entry was invalid |
++-----------+-----------------------------------------------------+
+|handled | the module has done everything to handle the request|
++-----------+-----------------------------------------------------+
+
+In a configurable fail-over section, each of these codes may be
+listed, with a value. If the code is not listed, or a configurable
+fail-over section is not defined, then values that make sense for the
+requested ``group`` (group, redundant, load-balance, etc) are used.
+
+The special code ``default`` can be used to set all return codes to
+the specified value. This value will be used with a lower priority
+than ones that are explicitly set.
+
+The values for each code may be one of two things:
+
++---------+---------------------------------------------------------------+
+|Value | Meaning |
++=========+===============================================================+
+|<number> | Priority for this return code. |
++---------+---------------------------------------------------------------+
+|return | Stop processing this configurable fail-over list. |
++---------+---------------------------------------------------------------+
+|reject | Stop processing this configurable fail-over list and |
+| | immediately return a reject. |
++---------+---------------------------------------------------------------+
+
+The ``<number>`` used for a value may be any decimal number between 1
+and 99999. The number is used when processing a list of modules, to
+determine which code is returned from the list. For example, if
+``module1`` returns ``fail`` with priority ``1``, and a later ``module2``
+returns ``ok`` with priority ``3``, the return code from the list of
+modules will be ``ok``, because it has higher priority than ``fail``.
+
+This configurability allows the administrator to permit some modules
+to fail, so long as a later module succeeds.
+
+
+More Complex Configurations
+---------------------------
+
+The ``authorize`` section is normally a list of module names. We can
+create sub-lists by using the section name ``group``. The ``redundant``
+section above is just a short-hand for ``group``, with a set of default
+return codes, which are different than the normal ``stop processing the
+list on failure``.
+
+For example, we can configure two detail modules, and allow either
+to fail, so long as one of them succeeds.
+
+::
+
+ #--
+ # Handle accounting packets
+ accounting {
+ group {
+ detail1 {
+ fail = 1 # remember ``fail`` with priority 1
+ ok = return # if we succeed, don't do ``detail2``
+ }
+ detail2 {
+ fail = 1 # remember ``fail`` with priority 1
+ ok = return # if we succeed, return ``ok``
+ # if ``detail1`` returned ``fail``
+ }
+ } # returns ``fail`` only if BOTH modules returned ``fail``
+ redundant {
+ sql1
+ sql2
+ handled
+ }
+ }
+ #--
+
+This configuration says:
+
+ - log to ``detail1``, and stop processing the ``group`` list if ``detail1`` returned OK.
+
+ - If ``detail1`` returned ``fail``, then continue, but remember the ``fail`` code, with priority 1.
+
+ - If ``detail2`` fails, then remember ``fail`` with priority 1.
+
+ - If ``detail2`` returned ``ok``, return ``ok`` from the ``group``.
+
+The return code from the ``group`` is the return code which was either
+forced to return (e.g. ``ok`` for ``detail1``), or the highest priority
+return code found by processing the list.
+
+This process can be extended to any number of modules listed in a
+``group`` section.
+
+
+Virtual Modules
+---------------
+
+Some configurations may require using the same list of modules, in
+the same order, in multiple sections. For those systems, the
+configuration can be simplified through the use of ``virtual`` modules.
+These modules are configured as named sub-sections of the
+``instantiate`` section, as follows:
+
+::
+
+ instantiate {
+ ...
+
+ redundant sql1_or_2 {
+ sql1
+ sql2
+ }
+ }
+
+The name ``sql1_or_2`` can then be used in any other section, such as
+``authorize`` or ``accounting``. The result will be *exactly* as if that
+section was placed at the location of the ``sql1_or_2`` reference.
+
+These virtual modules are full-fledged objects in and of themselves.
+One virtual module can refer to another virtual module, and they can
+contain ``if`` conditions, or any other configuration permitted in a
+section.
+
+
+Redundancy and Load-Balancing
+-----------------------------
+
+See ``man unlang`` or ``doc/load-balance`` for information on simple
+redundancy (fail-over) and load balancing.
+
+
+The Gory Details
+-----------------
+
+The fundamental object is called a MODCALLABLE, because it is something that
+can be passed a specific radius request and returns one of the RLM_MODULE_*
+results. It is a function - if you can accept the fact that pieces of
+radiusd.conf are functions. There are two kinds of MODCALLABLEs: GROUPs and
+SINGLEs.
+
+A SINGLE is a reference to a module instance that was set up in the modules{}
+section of radiusd.conf, like ``preprocess`` or ``sql1``. When a SINGLE is
+called, the corresponding function in the rlm is invoked, and whichever
+RLM_MODULE_* it returns becomes the RESULT of the SINGLE.
+
+A GROUP is a section of radiusd.conf that includes some MODCALLABLEs.
+Examples of GROUPs above include ``authorize{...}``, which implements the C
+function module_authorize, and ``redundant{...}``, which contains two SINGLEs
+that refer to a couple of redundant databases. Note that a GROUP can contain
+other GROUPs - ``Auth-Type SQL{...}`` is also a GROUP, which implements the C
+function module_authenticate when Auth-Type is set to SQL.
+
+Now here's the fun part - what happens when a GROUP is called? It simply runs
+through all of its children in order, and calls each one, whether it is
+another GROUP or a SINGLE. It then looks at the RESULT of that child, and
+takes some ACTION, which is basically either ``return that RESULT immediately``
+or ``Keep going``. In the first example, any ``bad`` RESULT from the preprocess
+module causes an immediate return, and any ``good`` RESULT causes the
+authorize{...} GROUP to proceed to the files module.
+
+We can see the exact rules by writing them out the long way:
+
+::
+
+ authorize {
+ preprocess {
+ notfound = 1
+ noop = 2
+ ok = 3
+ updated = 4
+ fail = return
+ reject = return
+ userlock = return
+ invalid = return
+ handled = return
+ }
+ files {
+ notfound = 1
+ noop = 2
+ ok = 3
+ updated = 4
+ fail = return
+ reject = return
+ userlock = return
+ invalid = return
+ handled = return
+ }
+ }
+
+This is the same as the first example, with the behavior explicitly
+spelled out. Each SINGLE becomes its own section, containing a list of
+RESULTs that it may return and what ACTION should follow from them. So
+preprocess is called, and if it returns for example RLM_MODULE_REJECT,
+then the reject=return rule is applied, and the authorize{...} GROUP
+itself immediately returns RLM_MODULE_REJECT.
+
+If preprocess returns RLM_MODULE_NOOP, the corresponding ACTION is ``2``. An
+integer ACTION serves two purposes - first, it tells the parent GROUP to go
+on to the next module. Second, it is a hint as to how desirable this RESULT
+is as a candidate for the GROUP's own RESULT. So files is called... suppose
+it returns RLM_MODULE_NOTFOUND. The ACTION for notfound inside the files{...}
+block is ``1``. We have now reached the end of the authorize{...} GROUP and we
+look at the RESULTs we accumulated along the way - there is a noop with
+preference level 2, and a notfound with preference level 1, so the
+authorize{...} GROUP as a whole returns RLM_MODULE_NOOP, which makes sense
+because to say the user was not found at all would be a lie, since preprocess
+apparently found him, or else it would have returned RLM_MODULE_NOTFOUND too.
+
+We could use the ``default`` code to simplify the above example a
+little. The following two configurations are identical:
+
+::
+
+ files {
+ notfound = 1
+ noop = 2
+ ok = 3
+ updated = 4
+ default = return
+ }
+
+
+When putting the ``default`` first, later definitions over-ride it's
+return code:
+
+::
+
+ files {
+ default = return
+ notfound = 1
+ noop = 2
+ ok = 3
+ updated = 4
+ }
+
+[Take a deep breath - the worst is over]
+
+That RESULT preference/desirability stuff is pretty complex, but my hope is
+that it will be complex enough to handle the needs of everyone's real-world
+imperfect systems, while staying out of sight most of the time since the
+defaults will be right for the most common configurations.
+
+So where does redundant{...} fit in with all that? Well, redundant{...} is
+simply a group that changes the default ACTIONs to something like
+
+::
+
+ fail = 1
+ everythingelse = return
+
+so that when one module fails, we keep trying until we find one that doesn't
+fail, then return whatever it returned. And at the end, if they all failed,
+the redundant GROUP as a whole returns RLM_MODULE_FAIL, just as you'd want it
+to (I hope).
+
+There are two other kinds of grouping: ``group{...}`` which does not have any
+specialized default ACTIONs, and ``append{...}``, which should be used when you
+have separate but similarly structured databases that are guaranteed not to
+overlap.
+
+That's all that really needs to be said. But now a few random notes:
+
+GROUPs may have RESULT=ACTION
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+It would look like this:
+
+::
+
+ authorize {
+ preprocess
+ redundant {
+ sql1
+ sql2
+ notfound = return
+ }
+ files
+ }
+
+which would prevent ``files`` from being called if neither of the SQL
+instances could find the user.
+
+redundant{...} and append{...} are just shortcuts
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+You could write:
+
+::
+
+ group {
+ sql1 {
+ fail = 1
+ notfound = 2
+ noop = return
+ ok = return
+ updated = return
+ reject = return
+ userlock = return
+ invalid = return
+ handled = return
+ }
+ sql2 {
+ fail = 1
+ notfound = 2
+ noop = return
+ ok = return
+ updated = return
+ reject = return
+ userlock = return
+ invalid = return
+ handled = return
+ }
+ }
+ instead of
+ redundant {
+ sql1
+ sql2
+ }
+
+but the latter is just a whole lot easier to read.
+
+``authenticate{...}`` is not a GROUP
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+even though it contains a list of ``Auth-Type`` GROUPs, because its
+semantics are totally different - it uses ``Auth-Type`` to decide which of
+its members to call, and their order is irrelevant.
+
+The default rules are context-sensitive
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+For ``authorize``, the defaults are
+what you saw above - notfound, noop, ok, and updated are considered
+success, and anything else has an ACTION of ``return``. For authenticate, the
+default is to return on success *or* reject, and only try the second and
+following items if the first one fails. You can read all the default ACTIONs
+in modcall.c (int defaultactions[][][]), or just trust me. They do the right
+thing.
+
+There are some rules that can't be implemented in this language
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+things like ``notfound = 1-reject``, ``noop = 2-ok``, ``ok = 3-ok``, etc. But I don't feel
+justified adding that complexity in the first draft.
+There are already enough things here that may never see real-world usage.
+Like append{...}
+
+-- Pac. 9/18/2000
diff --git a/doc/configuration/dynamic_home_servers.md b/doc/configuration/dynamic_home_servers.md
new file mode 100644
index 0000000..7518f0a
--- /dev/null
+++ b/doc/configuration/dynamic_home_servers.md
@@ -0,0 +1,238 @@
+# Dynamic Home Servers in v3
+
+FreeRADIUS has some support for dynamic home servers, with certain
+limitations.
+
+
+## Configuration
+
+The configuration needs to have dynamic home servers enabled, by
+editing `proxy.conf`.
+
+```
+proxy server {
+ ...
+ dynamic = true
+ ...
+}
+```
+
+This configuration item causes the internal data structures to become
+thread-safe for updates. This change means that there will be more
+lock contention on the data structures holding home servers. As a
+result, high-load proxy may see slowdowns.
+
+Once dynamic home servers are enabled, they should be placed into
+a subdirectory. FreeRADIUS should be told which subdirectory the
+home servers are located in:
+
+```
+proxy server {
+ ...
+ dynamic = true
+ ...
+ directory = ${raddb}/home_servers/
+}
+```
+
+This directory should contain nothing other than definitions for
+dynamic home servers. These definitions are simply normal
+`home_server` definitions:
+
+```
+home_server example.com {
+ ...
+}
+```
+
+Each file in the directory should be named for the home server domain
+name. In the above example, the filename should be
+`${raddb}/home_servers/example.com`. The name of the home server in
+the file should be the same as the filename which contains the home
+server definition.
+
+Each file in the directory should have one, and only one,
+`home_server` definition.
+
+Home servers which use RadSec can `$INCLUDE tls.conf` in this
+directory to use a common site-local TLS configuration. The script
+`freeradius-naptr-to-home-server.sh` referenced below assumes that
+this file exists. If you are not using that file, it is safe to just
+replace it with an empty file.
+
+### The Control Socket
+
+The virtual server `sites-enabled/control` *must* be enabled for
+dynamic home servers to work. The `radmin` program *must* have
+read/write permission in order for dynamic home servers to work.
+
+Please see that `sites-enabled/control` file for information on
+configuring that virtual server.
+
+
+## Starting FreeRADIUS
+
+When FreeRADIUS starts, it will read each file in the
+`${raddb}/home_servers/` directory. The file will parsed in order to
+define a dynamic `home_server`.
+
+
+## Adding a new Home Server
+
+In order to add a new home server while FreeRADIUS is running, simply
+add a new `home_server` definition file to the
+`${raddb}/home_servers/` directory.
+
+Then, tell FreeRADIUS that the home server is available in a new file:
+
+```
+$ radmin -e "add home_server file /path/to/raddb/home_servers/example/com"
+```
+
+If all goes well, the home server will be added. If there are issues,
+`radmin` will print a descriptive error.
+
+Once a dynamic home server has been added, it can be used just like
+any other home server.
+
+
+## Deleting a Home Server
+
+Dynamically created home servers can be deleted via `radmin`. Note
+also that dynamic home servers which are loaded when FreeRADIUS starts
+can be deleted.
+
+```
+$ radmin -e "del home_server file <name> <type>"
+```
+
+Note that this command deletes the home server by name and type, not
+by filename. This difference from the `add home_server` command is
+due to internal limitations in the server core.
+
+```
+home_server <name> {
+ type = <type>
+}
+```
+
+
+## Listing a Home Server
+
+It is possible to list all home servers and know which is dynamic or no.
+
+```
+$ radmin -e "show home_server list all"
+```
+
+
+## Limitations
+
+Note that due to internal limitations, dynamic home servers are _not_
+freed. So repeatedly adding and deleted home servers _will_ cause
+FreeRADIUS to gradually use more memory.
+
+Other internal limitations means that it is impossible to add dynamic
+home servers to a `home_server_pool`. In short, dynamic home servers
+exist by themselves, with no associated realm, pool, or failover
+capability.
+
+
+## Proxying to a Home Server
+
+The new attribute `Home-Server-Name` controls proxying to a particular
+home server. The home server just has to exist, it does not need to
+be a dynamic home server.
+
+```
+authorize {
+ ...
+
+ update control {
+ Home-Server-Name := "example.com"
+ }
+ ...
+}
+```
+
+
+## Checking if a Dynamic Home Server exists
+
+You can see if a dynamic home server exists through the following
+dynamic string expansion:
+
+```
+%{home_server_dynamic:name}
+```
+
+This expansion looks up the home server by name, and returns whether
+or not the home server exists, and is dynamic.
+
+The return values are:
+
+* empty string - the home server does not exist.
+* `0` - the home server exists, and is statically defined.
+* `1` - the home server exists, and is dynamically defined
+
+```
+authorize {
+ ...
+ if (User-Name =~ /@(.*)$/) {
+ switch "%{home_server_dynamic:%{1}}" {
+ case "1" {
+ # Proxy to this one particular home server
+ update control {
+ &Home-Server-Name := "%{1}"
+ }
+ }
+
+ case "0" {
+ # Proxy with home server pool, failover, etc.
+ update control {
+ &Proxy-To-Realm := "%{1}"
+ }
+ }
+
+ case {
+ # no home server exists, ask DNS
+ update control {
+ # you can add a parameter for the NAPTR tag to look up, e.g. "aaa+auth:radius.tls.tcp" (RFC7585, OpenRoaming)
+ # if the third parameter is omitted, it defaults to "x-eduroam:radius.tls"
+ &Temp-Home-Server-String := `%{config:confdir}/mods-config/realm/freeradius-naptr-to-home-server.sh -d %{config:confdir} %{1}`
+ }
+ if ("%{control:Temp-Home-Server-String}" == "" ) {
+ reject
+ } else {
+ update control {
+ &Home-Server-Name := "%{1}"
+ }
+ }
+ }
+ }
+ }
+ ...
+}
+```
+
+## Maintenance of Dynamic Home Servers
+
+Dynamic home servers are discovered from DNS, and DNS has TTLs. These
+TTLs are not tracked by FreeRADIUS, as they are not available when
+using the standard DNS APIs.
+
+Dynamic realms should be regularly deleted, so that they can be
+recreated with updated information. The server should be restarted
+with an empty home_server directory regularly, for two reasons:
+
+* Entries in DNS may change over time, or be removed, and the server should learn this.
+ If the entries are not removed, the server will not discover any changes.
+* dynamic home servers are often RADIUS/TLS based with client and server certificates,
+ and the server should refresh CRL information regularly
+
+As a result, we recommend emptying the home_servers directory (except
+for the `tls.conf` file), refreshing CRLs and then restarting the server
+once per day. e.g.
+
+```
+rm -f $(ls -1 raddb/home_servers | egrep -v tls.conf)
+```
diff --git a/doc/configuration/load_balance.rst b/doc/configuration/load_balance.rst
new file mode 100644
index 0000000..7926444
--- /dev/null
+++ b/doc/configuration/load_balance.rst
@@ -0,0 +1,172 @@
+Load Balancing
+==============
+
+As of version 2.0.0, the load balance documentation is in the
+available in the "unlang" man page. The text below may not be up to
+date, and is here only for historical purposes.
+
+As of version 1.1.0, FreeRADIUS supports load balancing in module
+sections. Please see the "configurable_failover" file in this
+directory for a more complete description of module sections.
+
+The short summary is that you can use a "load-balance" section in
+any place where a module name may be used. The semantics of the
+"load-balance" section are that one of the modules in the section will
+be chosen at random, evenly spread over the modules in the list.
+
+An example is below::
+
+ accounting {
+ load-balance {
+ sql1
+ sql2
+ sql2
+ }
+ }
+
+In this case, 1/3 of the RADIUS requests will be processed by
+"sql1", one third by "sql2", and 1/3 by "sql3".
+
+The "load-balance" section can be nested in a "redundant" section,
+or vice-versa::
+
+ accounting {
+ load-balance { # between two redundant sections below
+ redundant {
+ sql1
+ sql2
+ }
+ redundant {
+ sql2
+ sql1
+ }
+ }
+ }
+
+This says "load balance between sql1 and sql2, but if sql1 is down,
+use sql2, and if sql2 is down, use sql1". That way, you can guarantee
+both that load balancing occurs, and that the requests are *always*
+logged to one of the databases::
+
+ accounting {
+ redundant {
+ load-balance {
+ sql1
+ sql2
+ }
+ detail
+ }
+ }
+
+This says "load balance between sql1 and sql2, but if the one being
+used is down, then log to detail".
+
+And finally::
+
+ accounting {
+ redundant { # between load-balance & detail
+ load-balance { # between two redundant sections
+ redundant {
+ sql1
+ sql2
+ }
+ redundant {
+ sql2
+ sql1
+ }
+ }
+ detail
+ }
+ }
+
+This says "try to load balance between sql1 and sql2; if sql1 is down,
+use sql2; if sql2 is down use sql1; if both sql1 and sql2 are down,
+then log to the detail file"
+
+
+More complicated scenarios
+--------------------------
+
+If you want to do redundancy and load-balancing among three
+modules, the configuration is quite complex::
+
+ load-balance {
+ redundant {
+ sql1
+ load-balance {
+ redundant {
+ sql2
+ sql3
+ }
+ redundant {
+ sql3
+ sql2
+ }
+ }
+ } # sql1, etc.
+ redundant {
+ sql2
+ load-balance {
+ redundant {
+ sql3
+ sql1
+ }
+ redundant {
+ sql1
+ sql3
+ }
+ }
+ } # sql2, etc.
+ redundant {
+ sql3
+ load-balance {
+ redundant {
+ sql1
+ sql2
+ }
+ redundant {
+ sql2
+ sql1
+ }
+ }
+ } # sql3, etc.
+ }
+
+For four or more modules, it quickly becomes unmanageable.
+
+The solution is to use the "redundant-load-balance" section, which
+combines the features of "load-balance", with "redundant" fail-over
+between members. The above complex configuration for three modules
+then becomes::
+
+ redundant-load-balance {
+ sql1
+ sql2
+ sql3
+ }
+
+
+Which means "load-balance evenly among all three servers. If the
+one picked for load-balancing is down, load-balance among the
+remaining two. If that one is down, pick the one remaining 'live'
+server".
+
+The "redundant-load-balance" section can contain any number of
+modules.
+
+
+Interaction with "if" and "else"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It's best to have "if" and "else" blocks contain "load-balance" or
+"redundant-load-balance" sections, rather than the other way around.
+The "else" and "elsif" sections cannot appear inside of a
+"load-balance" or "redundant-load-balance" section, because the "else"
+condition would be chose as one of the modules for load-balancing,
+which is not what you want.
+
+It's OK to have a plain "if" block inside of a "load-balance" or
+"redundant-load-balance" section. In that case, the "if" condition
+checks the return code of the module or group that executed just
+before the "load-balance" section. It does *not* check the return
+code of the previous module in the section.
diff --git a/doc/configuration/post_auth_type b/doc/configuration/post_auth_type
new file mode 100644
index 0000000..7492324
--- /dev/null
+++ b/doc/configuration/post_auth_type
@@ -0,0 +1,44 @@
+This is now called Post-Auth-Type, for consistency.
+
+O.INTRODUCTION
+
+ Post-Auth-Type is used to select between groupings of
+ modules in the post-auth stanza using arbitrary attributes.
+ It is functionally identical to Acct-Type, apart from
+ the name of the attribute and its dealing with rejected
+ requests.. This means that (unlike Autz-Type) the attribute
+ must be set before the stanza is run. Changes to
+ Post-Auth-Type during post-auth will have no effect.
+
+1.HOW IT WORKS
+
+ If a request has been rejected, the value of Post-Auth-Type
+ is overwritten with REJECT automatically, so anonymous
+ modules outside the REJECT substanza will not be run, only
+ modules within the appropriate substanza will be run.
+
+2.EXAMPLES
+
+ In the example below, when a request has been rejected, the
+ module my_ippool will not be run, only the module my_detail
+ will be run.
+ If the request is not rejected, the my_ippool module will be
+ run, but not the my_detail module
+
+ post-auth {
+ my_ippool
+ Post-Auth-Type REJECT {
+ my_detail
+ }
+ }
+
+ In the following example, 2 different sql modules are used
+ to store accepted requests and rejected requests.
+
+ post-auth {
+ my_sql_accept
+ Post-Auth-Type REJECT {
+ my_sql_reject
+ }
+ }
+
diff --git a/doc/configuration/session_type b/doc/configuration/session_type
new file mode 100644
index 0000000..9efcd7c
--- /dev/null
+++ b/doc/configuration/session_type
@@ -0,0 +1,10 @@
+Session-Type is used to select between groupings of
+modules in the session stanza using arbitrary attributes.
+It is functionally identical to Acct-Type, apart from
+the name of the attribute. This means that (unlike
+Autz-Type) the attribute must be set before the stanza
+is run. Changes to Session-Type during session will
+have no effect.
+
+This allows Simultaneous-Use checking behaviour to be very flexible.
+
diff --git a/doc/configuration/simultaneous_use b/doc/configuration/simultaneous_use
new file mode 100644
index 0000000..5639738
--- /dev/null
+++ b/doc/configuration/simultaneous_use
@@ -0,0 +1,173 @@
+
+ FreeRADIUS server and the Simultaneous-Use parameter.
+
+
+0. INTRODUCTION
+
+ Lots of people want to limit the number of times one user account can
+ login, usually to one. This is hard to do with the radius protocol;
+ the nature of the accounting stuff is such that the idea the radius server
+ has about the list of logged-in users might be different from the idea
+ the terminal server has about it.
+
+ However, most terminal servers have an alternative way to get a list
+ of logged-in users. Most support some way through telnet, some have
+ a finger-daemon builtin and a lot of them support SNMP. So if the
+ radius server thinks that someone is trying to login a second time,
+ it is possible to check on the terminal server itself if the first
+ login is indeed still active. Only then access is denied for the
+ second login.
+
+
+1. PREREQUISITES
+
+ You need to have perl installed.
+
+ For SNMP checks, you have 2 options. You can use the `snmpget' program
+ from the cmu-snmp tools. You can probably get precompiled ones,
+ maybe even packaged for your system (Debian/Linux, Redhat/Linux, FreeBSD
+ ports collection etc). The source code is at
+ http://www.net.cmu.edu/projects/snmp/snmpapps/. The Linux-specific
+ version of this is at http://www.gaertner.de/snmp/
+
+ The other option is to install the SNMP_Session and BER modules that
+ for example the well known `mrtg' package uses. This is recommended.
+ In that case you need no external snmpget program, checkrad will
+ speak SNMP directly. See http://www.switch.ch/misc/leinen/snmp/perl/
+
+ The checkroutine for USR/3Com Total Control racks uses the Net::Telnet
+ module from CPAN, at least version 3.00. If you need that, obtain it from
+ your local CPAN mirror (or see http://www.perl.com/CPAN/). The checkrad.pl
+ perl script will autodetect if that module is installed.
+
+2. USAGE.
+
+ It works by adding the `check' parameter "Simultaneous-Use" to the entry
+ for a users or DEFAULT in /etc/raddb/users. It should be at least one;
+ it defines the maximum number of users logged in with the same account name.
+ For example:
+
+ #
+ # Simultaneous use restrictions.
+ #
+ DEFAULT Group == "staff", Simultaneous-Use := 4
+ Fall-Through = 1
+ DEFAULT Group == "business", Simultaneous-Use := 2
+ Fall-Through = 1
+ DEFAULT Simultaneous-Use := 1
+ Fall-Through = 1
+
+
+ NOTE!!! The "Simultaneous-Use" parameter is in the "check" A/V pairs,
+ and not in the Reply A/V pairs (it _is_ a check).
+
+ For SQL, after creating and populating your schema, you should
+ execute the following statement (for MySQL, others may vary):
+
+ INSERT INTO radgroupcheck (GroupName, Attribute, op, Value) values("dialup", "Simultaneous-Use", ":=", "1");
+
+ Once that is done, your users should be limited to only one login at a time.
+
+3. IMPLEMENTATION
+
+ The server keeps a list of logged-in users in the /var/log/radutmp file.
+ This is also called "the session database". When you execute "radwho",
+ all that radwho really does is list the entries in this file in a pretty
+ format. Only when someone tries to login who _already_ has an active
+ session according to the radutmp file, the server executes the perl
+ script /usr/local/sbin/checkrad (or /usr/sbin/checkrad, it checks for
+ the presence of both and in that order). This script queries the terminal
+ server to see if the user indeed already has an active session.
+
+ The script uses SNMP for Livingston Portmasters and Ciscos, finger for
+ Portslave, Computone and Ascend, and Net::Telnet for USR/3Com TC.
+
+ Since the script has been witten in perl, it's easy to adjust for
+ any type of terminal server. There are implementations in the script for
+ checks using SNMP, finger, and telnet, so it should be easy to add
+ your own check routine if your terminal server is not supported yet.
+
+ You can find the script in the file src/checkrad.pl.
+
+ You need to set the correct type in the file /etc/raddb/naslist so that
+ checkrad KNOWS how it should interrogate the terminal server. At this
+ time you can define the following types:
+
+ type Vendor Uses method needs Need naspasswd
+ ==== ====== =========== ===== ==============
+ ascend Lucent SNMP SNMP No
+ bay Nortel finger finger command No
+ cisco Cisco SNMP SNMP Optional [1]
+ computone Computone finger finger command No
+ cvx Nortel SNMP SNMP No
+ digitro Digitro rusers rusers command No
+ livingston Livingston SNMP SNMP No [2]
+ max40xx Lucent finger finger command No
+ netserver USR/3com telnet CPAN Net::Telnet Yes
+ pathras Cyclades telnet CPAN Net::Telnet Yes
+ patton Patton SNMP SNMP No
+ portslave ? finger finger command No
+ pr3000 Cyclades SNMP snmpwalk command No
+ pr4000 Cyclades SNMP snmpwalk command No
+ tc USR/3com telnet CPAN Net::Telnet Yes
+ usrhyper USR/3com SNMP SNMP No [3]
+ versanet VersaNet SNMP SNMP No
+
+ other none N/A - No
+
+ [1] In naspasswd file: set username to SNMP, password is community.
+ [2] Needs at least ComOS 3.5, SNMP enabled.
+ [3] Set "Reported Port Density" to 256 (default)
+
+ "other" means "don't bother checking, I believe what radutmp says".
+ This really is not recommended, if a user has a "stuck" entry in the
+ session database she will not be able to login again - hence the
+ extra check that "checkrad" does.
+
+4. IF IT DOESN'T WORK
+
+ Note that you need to add the Simultaneous-Use parameter to the
+ check item (first line), not the reply item, using the ':=' operator.
+
+ You can edit the `checkrad' perl script and turn on debugging. Then
+ watch the debug file. The `radius.log' file also gives some hints.
+
+ You can also run the "checkrad" script manually, use the "-d"
+ switch to get debug output on standard output instead of in the log.
+
+ See also:
+
+ http://wrath.geoweb.ge/simult.html
+
+ which has a good discussion of the use of Simultaneous-Use.
+
+
+5. CAVEATS
+
+ This solution checks the radutmp file. This file is kept up-to-date from
+ the Accounting records the NAS sends. Since some NASes delay these records
+ for quite some time, it is possible to get a double login by logging in
+ twice at _exactly_ the same time (plus or minus the mentioned delay time),
+ since neither of the logins are registered yet.
+
+ The solution would be to create a small 1-minute cache of Authentication
+ records, that is also checked for double login attempts. Perhaps in the
+ next version.
+
+ When implementing this one thing was considered the most important: when
+ trying to detect double logins, we always try to err on the safe side. So
+ in rare cases, a double login is possible but we try never to limit access
+ for a legitimate login.
+
+6. PROBLEMS WITH DROPPED CONNECTIONS
+
+ Our PM3, with 2 ISDN-30 lines coming into it, had the habit of sometimes
+ dropping connections. In a few cases, the portmaster thought the session was
+ still alive so if the user tried to login again, he or she was denied access.
+ In our case, this problem was caused by a bad PRI line from the phone
+ company.
+
+ We tried to compensate this by setting the Idle-Timeout to 15 minutes. That
+ way, even if a user did get locked out the portmaster would clear the rogue
+ session within 15 minutes and the user could login again.
+
diff --git a/doc/configuration/snmp b/doc/configuration/snmp
new file mode 100644
index 0000000..713cb83
--- /dev/null
+++ b/doc/configuration/snmp
@@ -0,0 +1,58 @@
+INSTALL
+-------
+
+Installing the SNMP patch is straightforward:
+
+$ tar -zxf freeradius-server-2.1.11.tar.gz
+$ cd freeradius-server-2.1.11
+$ patch -p1 < ../snmp.patch
+$ ./configure --args....
+$ make
+$ make install
+
+MIB Installation
+----------------
+
+The traps *REQUIRE* that the files in the "mibs" directory be copied
+to the global mibs directory, usually /usr/share/snmp/mibs/.
+If this is not done, the "snmptrap" program has no idea what information
+to send, and will not work. The MIB installation is *NOT* done as
+part of the default installation, so that step *MUST* be done manually.
+
+The global MIB directory can be found by running the following command:
+
+ $ snmptranslate -Dinit_mib .1.3 2>&1 | grep MIBDIR | sed "s/' .*//;s/.* '//;s/.*://"
+
+Or maybe just:
+
+ $ snmptranslate -Dinit_mib .1.3 2>&1 | grep MIBDIR
+
+If you have copied the MIBs to that directory, you can test the
+FreeRADIUS MIBs by running the following command:
+
+ $ snmptranslate -m +FREERADIUS-NOTIFICATION-MIB -IR -On serverStart
+
+It should print out:
+
+ .1.3.6.1.4.1.11344.4.1.1
+
+As always, run the server in debugging mode after enabling the
+traps. You will see the "snmptrap" command being run, and it will
+print out any errors or issues that it encounters. Those need to
+be fixed before running the server in daemon mode.
+
+We also suggest running in debugging mode as the "radiusd" user, if
+you have "user/group" set in radiusd.conf. The "snmptrap" program
+may behave differently when run as "root" or as the "radiusd" user.
+
+You will also need to edit "radiusd.conf", and uncomment the line saying
+
+ # $INCLUDE trigger.conf
+
+That will enable the triggers.
+
+
+More Documentation
+------------------
+
+See raddb/trigger.conf for complete documentation.
diff --git a/doc/configuration/variables.rst b/doc/configuration/variables.rst
new file mode 100644
index 0000000..4a2f28c
--- /dev/null
+++ b/doc/configuration/variables.rst
@@ -0,0 +1,151 @@
+Run-time variables
+==================
+
+See "man unlang" for more complete documentation on the run-time
+variables. This file is here only for historical purposes.
+
+The above variable expansions also support the following
+meta-attributes. These are not normal RADIUS attributes, but are
+created by the server to be used like them, for ease of use. They can
+only be queried, and cannot be assigned.
+
++-----------------------+-------------------------------------------------+
+| Packet-Type | RADIUS packet type (Access-Request, etc.) |
++-----------------------+-------------------------------------------------+
+| Packet-Src-IP-Address | IP address from which the packet was sent |
++-----------------------+-------------------------------------------------+
+| Packet-Dst-IP-Address | IP address to which the packet was sent. |
+| | This may be "0.0.0.0", if the server |
+| | was configured with ``bind_address = *``. |
++-----------------------+-------------------------------------------------+
+| Packet-Src-Port | UDP port from which the packet was sent |
++-----------------------+-------------------------------------------------+
+| Packet-Dst-Port | UDP port to which the packet was sent. |
++-----------------------+-------------------------------------------------+
+
+``%{config:section.subsection.item}``
+ Corresponding value in ``radiusd.conf`` for the string value of that item.
+
+The ``%{config:...}`` variables should be used VERY carefully, as they
+may leak secret information from your RADIUS server, if you use them
+in reply attributes to the NAS!
+
+::
+
+ DEFAULT User-Name =~ "^([^@]+)@(.*)"
+ All-That-Matched = `%{0}`
+ Just-The-User-Name = `%{1}`
+ Just-The-Realm-Name = `%{2}`
+
+
+The variables are used in dynamically translated strings. Most of the
+configuration entries in ``radiusd.conf`` (and related files) will do
+dynamic string translation. To do the same dynamic translation in a
+RADIUS attribute (when pulling it from a database, or "users" file),
+you must put the string into an back-quoted string:
+
+::
+
+ Session-Timeout = `%{expr: 2 + 3}`
+
+To do the dynamic translation in the ``radiusd.conf`` (or some other
+configuration files), just use the variable as-is. See
+``radiusd.conf`` for examples.
+
+
+Attributes as environment variables in executed programs
+--------------------------------------------------------
+
+When calling an external program (e.g. from ``rlm_exec`` module), these
+variables can be passed on the command line to the program. In
+addition, the server places all of the attributes in the RADIUS
+request into environment variables for the external program. The
+variables are renamed under the following rules:
+
+ #. All letters are made upper-case.
+ #. All hyphens '-' are turned into underscores '_'
+
+so the attribute ``User-Name`` can be passed on the command line to the
+program as ``%{User-Name}``, or used inside the program as the environment
+variable ``USER_NAME`` (or ``$USER_NAME`` for shell scripts).
+
+If you want to see the list of all of the variables, try adding a line
+``printenv > /tmp/exec-program-wait`` to the script. Then look in the
+file for a complete list of variables.
+
+One-character variables
+-----------------------
+
+The following one-character variables were defined. They were duplicates of the
+previous general cases, and were only provided for backwards compatibility.
+They are in the process of being removed, this table documents the old variables
+and their new equivalents.
+(i.e. ``:-``, as described above.
+
++-----------+---------------------------+-----------------------+
+| Variable | Description | Proper Equivalent |
++===========+===========================+=======================+
+|%a |Protocol (SLIP/PPP) |%{Framed-Protocol} |
++-----------+---------------------------+-----------------------+
+|%c |Callback-Number |%{Callback-Number} |
++-----------+---------------------------+-----------------------+
+|%d |request day (DD) | |
++-----------+---------------------------+-----------------------+
+|%f |Framed IP address |%{Framed-IP-Address} |
++-----------+---------------------------+-----------------------+
+|%i |Calling Station ID |%{Calling-Station-Id} |
++-----------+---------------------------+-----------------------+
+|%l |request timestamp | |
++-----------+---------------------------+-----------------------+
+|%m |request month (MM) | |
++-----------+---------------------------+-----------------------+
+|%n |NAS IP address |%{NAS-IP-Address} |
++-----------+---------------------------+-----------------------+
+|%p |Port number |%{NAS-Port} |
++-----------+---------------------------+-----------------------+
+|%s |Speed (PW_CONNECT_INFO) |%{Connect-Info} |
++-----------+---------------------------+-----------------------+
+|%t |request in ctime format | |
++-----------+---------------------------+-----------------------+
+|%u |User name |%{User-Name} |
++-----------+---------------------------+-----------------------+
+|%A |radacct_dir |%{config:radacctdir} |
++-----------+---------------------------+-----------------------+
+|%C |clientname | |
++-----------+---------------------------+-----------------------+
+|%D |request date (YYYYMMDD) | |
++-----------+---------------------------+-----------------------+
+|%G |request minute | |
++-----------+---------------------------+-----------------------+
+|%H |request hour | |
++-----------+---------------------------+-----------------------+
+|%I |request ID | |
++-----------+---------------------------+-----------------------+
+|%L |radlog_dir |%{config:logdir} |
++-----------+---------------------------+-----------------------+
+|%M |MTU |%{Framed-MTU} |
++-----------+---------------------------+-----------------------+
+|%R |radius_dir |%{config:raddbdir} |
++-----------+---------------------------+-----------------------+
+|%S |request timestamp | |
+| |in SQL format | |
++-----------+---------------------------+-----------------------+
+|%T |request timestamp | |
+| |in database format | |
++-----------+---------------------------+-----------------------+
+|%U |Stripped User name |%{Stripped-User-Name} |
++-----------+---------------------------+-----------------------+
+|%V |Request-Authenticator | |
+| |(Verified/None) | |
++-----------+---------------------------+-----------------------+
+|%v |Server Version | |
++-----------+---------------------------+-----------------------+
+|%Y |request year (YYYY) | |
++-----------+---------------------------+-----------------------+
+|%Z |All request attributes | |
+| |except password | |
+| |(must have a big buffer) | |
++-----------+---------------------------+-----------------------+
+
+
+ $Id$