FreeRADIUS WikiMain Page | About | Help | FAQ | Special pages | Log in

Printable version | Disclaimers | Privacy policy

Modules2

From FreeRADIUS Wiki

Contents

Creating a Module (Version 2)

FreeRADIUS provides several different mechanisms to add site specific authentication and accounting procedures. The traditional approach was to create a Exec-Program-Wait script and include it in the users file. This approach is still available in FreeRADIUS but has been determined to be obsolete and may be removed in some future version. In addition there is the rlm_exec module provided by FreeRADIUS that permits a script or program to be included in some of the FreeRADIUS.conf sections. There have been a number of problems discussed with this approach as it does not always provide all the flexibility desired.

In any case, both of those approaches require that FreeRADIUS fork another process and incur all associated process startup overhead each time an authentication or accounting request is received. On a high volume server this overhead can become significant and impact response times. The approach that eliminates the overhead is the rlm approach where you create your own module and compile it directly into FreeRADIUS. While this sounds like a lot of work, its actually very straight forward. However, the rlm does have to be written in C. Converting shell scripts to C is not that big a deal. Modules in Version 2 are a bit different from Version 1. This page addresses adding modules to FreeRADIUS Version 2. For Version 1 see Modules.

Establishing the Environment

There are two ways to compile your module: inside FreeRADIUS or separately. I have not tried the inside approach with Version 2. However, it should be quite similar to that used with Version 1. This page realy addresses compiling the modules searately from FreeRADIUS.

The simple approach is to build your module outside of the FreeRADIUS environment and then move the resulting .so file into the library. To do that you need a Makefile. Here is one that works for a module named rlm_xxx:

    VERS = 2.0.5
    CFLAGS  =  -DNDEBUG -Wall -I/usr/include -I/usr/local/msql3/include \
                -I/usr/ports/net/freeradius2/work/freeradius-server-$(VERS)/src
    LIBS    =  -lc -L/usr/local/msql3/lib -lmsql
    ALL:    rlm_xxx.o rlm_xxx-$(VERS).so
    rlm_xxx.o:     rlm_xxx.c
            cc -g -fPIC -DPIC -c $(CFLAGS) rlm_xxx.c
    rlm_xxx-$(VERS).so:    rlm_xxx.o
            cc -g -shared -Wl,-soname,rlm_xxx-$(VERS).so \
                       -o rlm_xxx-$(VERS).so rlm_xxx.o $(LIBS)
    install:        ALL
       install rlm_xxx-$(VERS).so /usr/local/lib/freeradius-$(VERS)

ln -fs rlm_xxx-$(VERS).so /usr/local/lib/freeradius-$(VERS)/rlm_xxx.so

    clean:
            rm rlm_xxx*.o rlm_xxx*.so
            

The FreeRADIUS package is located at: /usr/local/ports/freeradius/2/work/freeradius-server-2.0.5. You will need to change that to your location.

Note, you need access to the various include files used by FreeRADIUS. Change the location above for the include directory to match where they are on your system. Note, in Version 2 the include structure is a bit unusual. You need to reference the src directory and not the more obvious include directory. The reason is that all the FreeRADIUS source modules use includes of the form "freeradius-dev/module" which is linked to the proper place. You need to follow that convention for things to work properly. The version numbering I am using for the module is that of FreeRADIUS but you can use any you want. This example shows how to link to a static library from the module. The second cc command is quite touchy about the ordering of items. There may be issues with libraries if they are not after the module source name.

Notice the -DNDEBUG in the CFLAGS definition above. This is a compile option in the base system. You need to be sure that this option is included if it was included when the base system was compiled. Likewise it should not be included if it was not included in the base system. If this option does not match the base system, radiusd will get a segment violation almost immediately. If you can not figure out how the base system was built, try it both ways. One of them should work.

Establishing the Module

Now start building rlm_xxx.c. Start by copying rlm_example.c and editing it to create your module. Note, there is an obscure note near the end of rlm_example.c that talks about global variables. What that really is telling you is that your rlm_xxx.c module must include all storage definitions within one of the modules defined in the module_t rlm_xxx definition. Data storage outside those modules will not work properly. Your rlm may compile and seem to run, but it will not do what you want.

Also, keep in mind that in general, rlm’s must be thread safe. There can be multiple threads using your module at any time. The RLM_TYPE_THREAD_SAFE definition tells FreeRADIUS that this is a thread-safe rlm. If you need to use one that cannot be made thread safe, then change RLM_TYPE_THREAD_SAFE to RLM_TYPE_THREAD_UNSAFE. FreeRADIUS will then only permit one instance of that rlm. Be advised that this will adversely affect performance and response times.

Setup struct rlm_xxx_t to hold data that needs to be accessed by all instances of the rlm. This data is not necessarily the same for each instance. There is a separate copy for each instance. For example, this is the place to store configuration variables that will be provided in FreeRADIUS.conf.

module_config is where the configuration variables from FreeRADIUS.conf are defined. There needs to be an entry here for each configuration variable. Do note there is a special entry for ending the list. That must be the last entry in this list. Each other entry has first a name for the configuration variable. The following is a sample entry:

{ "host",  PW_TYPE_STRING_PTR, offsetof(rlm_xxx_t,host), NULL,  "0"},

The first field is the string that will be used in the FreeRADIUS.conf entry for the variable. In this case the variable name is “host”. In the FreeRADIUS.conf file in the modules section for xxx you might find:

host = “my_host”

The second entry is the type of data the variable holds.

Entry Data Type
PW_TYPE_STRING_PTR Character string
PW_TYPE_INTEGER Integer
PW_TYPE_BOOLEAN Boolean
PW_TYPE_IPADDR IP Address

The third entry tells FreeRADIUS where to save the configuration variable. the host entry is the name of the variable in rlm_xxx_t. The fourth entry is unknown. I could only find NULL used here.

The last entry is an initial value for the configuration variable if nothing is specified in FreeRADIUS.conf. This value must have the proper type as established in the send entry and must be enclosed in double-quotes.

The xxx_instantiate module is called each time a new instance is started during the initial configuration process. Generally this module is used to establish the data for the instance that needs to be retained during the life of the instance. For example, reading the configuration variables. cf_section_parse(conf, data, module_config) is used to do this function. Note that the instantiate module is not called each time a new instantiation of the module is started during run time. The data established during the instantiate module is available to all instantiations during run time. If you need to store data that is associated with a particulare *request*, and is valid only for the lifetime of a request, see request_data_add(), and request_data_get().

The module_t rlm_xxx definition establishes the connection between FreeRADIUS and the available services your rlm can provide. Here is a sample definition which only includes instantiate, detach and authorize:

module_t rlm_xxx = {
	RLM_MODULE_INIT,
        "xxx",
        RLM_TYPE_THREAD_SAFE,           /* type */
        xxx_instantiate,                /* instantiation */
        xxx_detach,                    /* detach */
        {
                NULL,                   /* authentication */
                xxx_authorize,          /* authorization */
                NULL,                   /* preaccounting */
                NULL,                   /* accounting */
                NULL,                   /* checksimul */
                NULL,                   /* pre-proxy */
                NULL,                   /* post-proxy */
                NULL                    /* post-auth */
        },
};

This example only has one authorization module. You can have multiple modules for use in different parts of the FreeRADIUS processing. You can also make separate rlm’s for each module. There does not appear to be any significant difference in performance in using one or multiple rlm’s. Your specific needs will probably make one approach easier than the other.

The example above indicates one of the least understood aspects of FreeRADIUS’s architecture. Both the authentication and authorization modules are part of the radius authentication request processing. However, those two sections are handled quite differently. Authentication is the checking of the password (or other authentication token) provided by the user to insure the user is who they are claiming to be. Generally only one authentication module is used. Authorization is a policy decision - should this request be permitted. This is generally where local policy issues are addressed.

Module Return Codes

There are a number of return codes possible from each of the rlm modules:

Return Code Meaning
RLM_MODULE_REJECT This request is not permitted under local policy. FreeRADIUS responds immediately with a reject response.
RLM_MODULE_FAIL Processing of this request could not be completed. Something is not working properly. FreeRADIUS responds with a reject response.
RLM_MODULE_OK This request is permitted or this module has processed the request successfully. The FreeRADIUS response will be determined by processing of later modules. If this is the last module then the response will be an OK
RLM_MODULE_HANDLED The module handled the request, so stop
RLM_MODULE_INVALID The module considers the request invalid
RLM_MODULE_USERLOCK Reject the request (user is locked out)
RLM_MODULE_NOTFOUND User not found (Probably shouldn’t be used in a RLM)
RLM_MODULE_NOOP Module succeeded without doing anything
RLM_MODULE_UPDATED OK (pairs modified)
RLM_MODULE_NUMCODES How many return codes there are

Accessing Radius Request Attributes

Version 2 uses a very generalized data format. The details are provided in libradius.h. You will probably want to review that. The basics are provided here. Do note, that in Version 1 an IPv4 attribute value was returned as a character string (e.g., "xxx.xxx.xxx.xxx"). In Version 2 it is returned as the binary 4 byte value. You have to do the conversion if needed.

The following example shows how to access the Framed-IP-Address attribute:

int *ip;
pair =  pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS);
       if (pair) {
               ip =  pair->vp_octet;
               DEBUG("rlm_xxx: Found IP Address");
       }

The standard radius dictionary items are prefixed by PW_. Vendor specific dictionary entries can usually be accessed by prefixing the vendor name to the item. For example, ASCEND_DATA_RATE can be used to obtain the Ascend specific Data-Rate attribute.

The data types are used in the vp_type form. This is strongly recommended because the actual storage location for the value is in transition. Some of them have been moved into the value_pair_data structure and some have not yet been converted. The vp_type form points to the proper place so you do not have to be concerned if the specific field has been converted yet. See libradius.h for all the details. Here is a table of the available data types.

_}

Accessing Configuration Parameters

Configuration parameters are saved in the rlm_xxx_t data structure. They are accessed through the instance argument. For example the host parameter shown above would be accessed using instance->host

Adding a Radius Response Item

The following shows how to add a response item to the radius response:

             sprintf (auth_msg, "%d", sestime*60);
             VALUE_PAIR *timeout;
              timeout = pairmake("Session-Timeout",
                               auth_msg, T_OP_SET);
                       pairadd(&request->reply->vps, timeout);
       

The first argument to pairmake is the attribute name. The second argument is its value. Note the value must be a string. There is a way to set vendor specific attributes but I have not needed to do that.

Creating Debug Entries

FreeRADIUS has a very helpful debug capability. You can certainly use trace commands like ktrace, strace, or truss. However, those are limited in what the show you about how your module is working. It is much easier to add debug statements into your module. If your FreeRADIUS is compiled with enable-debug then you can start it with:

        /.../FreeRADIUS -X

and it will generate lots of debug entries on the console to show you what it is doing. To add debug information to your modules you can use the following command:

       DEBUG (“format string”, variables);

The format string and variable list are the same as for a printf statement. Use debug statements at critical places to show how the request is being processed.

Creating Log Entries

Log entries are created using the following: radlog (level, “format string”, variables); The level argument is the syslog level (e.g., L_ERR). These are the same as those in syslog.h except only L is used rather then LOG. The format string and variables are the same as for a printf statement. Log entries should be used for things that need to be retained in the log files.

Compiling Your Module

All you need to do when everything is correct is enter:

make
make install

Check errors very carefully. Getting the Makefile setup properly requires a lot of care.

Configuring the Module in radiusd.conf

You need to make 2 modifications to the FreeRadius configuratio files to add your rlm. While in Version 1 all the configuration was in radiusd.conf, in Version 2 it is distributed between a number of files, some of which are in sub-directories. First you need to create a module configuration file in the modules directory. See modules/rlm_example for a sample. The contents of the file should look something like:

# xxx module
xxx {
host = “myhost”
}

This establishes the configuration parameters for the module. Each module gets a separate file.

Next you need to add an entry in the appropriate instantiation section for xxx so that it will be called at the proper time. The instantiation section is now in the virtual servers configuration. You can have different modules for different virtual servers. If you are only running one virtual server then you can use the default file in the sites-available directory. In that file you would add your module to the appropriate section. For example in this case:

# Authorization. First preprocess (hints and huntgroups files),
authorize {
preprocess
files
xxx
}

Finally you need to check the sites-active directory and make sure there is a link to the sites-available file that includes your module. The link for default should already be present.

At this point you are ready to test the server. Make sure to start it with radiusd -X and check through the output to be sure the module loaded and initialized itself properly.

Available Modules

See Also

Data Type Content
vp_strvalue A character string with max size of MAX_STRING_LEN.
vp_octets An octet string with max size of MAX_STRING_LEN
vp_ip6addr An IPv6 address in struct in6_addr format
vp_ifld Some sort of structure - Check the source code for this one
vp_ipv6prefix Some sort of structure - Check the source code for this one
vp_filter A data type of filter[32]
vp_ether A 6 byte ethernet address in octet format (not printable)
vp_ipaddr An IPv4 address in struct in_addr format
vp_date A date value
vp_integer An integer value in octet format (not printable)

Retrieved from "http://wiki.freeradius.org/Modules2"

This page has been accessed 8,087 times. This page was last modified on 3 March 2010, at 18:48.


Find

Browse
Main Page
Community portal
Current events
Recent changes
Random page
Help
Edit
View source
Editing help
This page
Discuss this page
New section
Printable version
Context
Page history
What links here
Related changes
My pages
Log in / create account
Special pages
New pages
File list
Statistics
More…