= ODBC Driver (rlm_sql_unixodbc)

FreeRADIUS supports connections to ODBC data sources by interfacing with the
unixODBC framework together with a backend driver.

TIP: Where FreeRADIUS provides a specific SQL driver for a particular database
server, using the specific driver is usually preferable since less indirection
often leads to better performance and stability. Nevertheless, you may have a
reason to use ODBC.

ODBC is a library specification for accessing multiple data sources using a
common API. FreeRADIUS uses the unixODBC implementation of ODBC and the data
source is normally hosted by a database server. The ODBC implementation is
seperate from FreeRADIUS with a discrete configuration that must be configured
and tested first.

Since FreeRADIUS is not interfacing directly with the backend database the
configuration is necessarily more involved than for a module for a native
driver. The database connection details are not only present in the usual
`mods-available/sql` file but are included in configuration files required by
the ODBC library:

`/etc/odbc.ini`:: Data sources are typically defined here. Each entry has a
name ("DSN") that is provided in the `server` parameter of the `rlm_sql`
instance.

`/etc/odbcinst.ini`:: Backend drivers must be installed and then registered
here. Each entry in `odbc.ini` will normally have a reference in its `Driver`
property to one of the entries in `odbcinst.ini`, unless the driver's library
is specified directly in `odbc.ini`.

TIP: The locations of the various ODBC configuration files can be determined
from the output of running `odbcinst -j`.

Configuring FreeRADIUS to connect to a data source using ODBC requires the
following steps:

. Test connectivity directly using a native database client, if possible
. Install a backend database driver for ODBC
. Register the backend driver
. Configure a DSN for the data source
. Test the ODBC connection using the DSN
. Configure an instance of the `rlm_sql` module to use the `rlm_sql_unixodbc`
  driver to connect to the DSN
. Test FreeRADIUS connectivity in debug mode (`-X`)

NOTE: Not all errors arising from ODBC issues are reported to FreeRADIUS in a
meaningful way. In the event that you have ODBC-related problems connecting the
a data source you should first consult the documentation for unixODBC (or ODBC
generally) and/or backend driver. For some issues you should consider
enabling ODBC tracing as described later in this document.


== Worked example: FreeRADIUS with MS SQL Server over ODBC

The following procedure has been demonstrated to work in this environment:

* CentOS 7
* FreeRADIUS from the Network RADIUS package repository
* unixODBC 2.3 (from the Linux distribution)
* Microsoft ODBC driver for SQL Server 17 from Microsoft MSSQL-Release repository

It will likely also work with other operating systems and package versions but
the details will need to be modified accordingly.

TIP: For connecting to Microsoft SQL Server it may be better to use a native
driver such as `rlm_sql_freetds`, where possible.


=== Prepare: Configure the required package repositories

It is recommended that you use Network RADIUS supplied packages for FreeRADIUS
which include the `rlm_unixodbc` module. If your operating system provides
FreeRADIUS packages that include the `rlm_unixodbc` module then these may also
work, however they are likely to be out of date.

Follow the instructions for installing the Network RADIUS repository:
https://networkradius.com/freeradius-packages/

Follow the instructions for installing the Microsoft MSSQL-Release repository
for RHEL/CentOS 7 from here:
https://docs.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver15#redhat17

The specific step that is required is as follows, but the remainder of the
instructions are worth reviewing:

[source,shell]
----
curl https://packages.microsoft.com/config/rhel/7/prod.repo \
    > /etc/yum.repos.d/mssql-release.repo
----


=== Test native database connectivity

Install the MS SQL Tools package:

[source,shell]
----
yum install mssql-tools
----

Verify that non-ODBC connectivity to the database server functions correctly
using `sqlcmd`:

[source,shell]
----
/opt/mssql-tools/bin/sqlcmd -S tcp:192.0.2.1,1433 \
    -U radius -P radPass_123 -d radius -Q 'SELECT * FROM sys.schemas'
----

The above command assumes a pre-existing database `radius` on the server
`192.0.2.1` that can be accessed via `TCP/1433` using the username `radius` and
password `radPass_123`.

In the event of difficulties connecting using the native tools check server
reachability, firewalling, credentials, database permissions, etc.


.Example of deploying a schema and application-specific user via CLI
================================================================================================

Configuring a production MS SQL Server to permit the required access and
deploying a database is beyond the scope of this document, however if the
FreeRADIUS schema and user do not already exist and the server is accessible
with a database administrator account (e.g. `sa`) then the schema and user can
be deployed as follows:

[source,shell]
----
sqlcmd -S tcp:192.0.2.1,1433 -U sa -P 'Admin123!'                     \
    -Q "CREATE DATABASE radius"
sqlcmd -S tcp:192.0.2.1,1433 -U sa -P 'Admin123!' -d radius           \
    -Q 'CREATE LOGIN radius WITH password='"'"'radPass_123'"'"''
sqlcmd -S tcp:192.0.2.1,1433 -U sa -P 'Admin123!' -d radius           \
    -Q "CREATE USER radius"
sqlcmd -S tcp:192.0.2.1,1433 -U sa -P 'Admin123!' -d radius           \
    -Q "GRANT CONTROL TO radius"
sqlcmd -S tcp:192.0.2.1,1433 -U radius -P radPass_123 -d radius -e    \
    -i /etc/raddb/mods-config/sql/main/mssql/schema.sql
----

================================================================================================


WARNING: Do not proceed until the above non-ODBC test is known to work using
the database and credentials that will be used by FreeRADIUS.


=== Install the ODBC backend driver

Install the package for Microsoft ODBC Driver for SQL Server:

[source,shell]
----
yum install msodbcsql17 unixodbc
----


=== Register the driver

The Microsoft ODBC Driver for SQL Server package will normally register itself
as a ODBC driver. This means that the /etc/odbcinst.ini should include a config
section such as the following:

.Example driver definition in `/etc/odbcinit.ini`
================================================================================================

 [ODBC Driver 17 for SQL Server]
 Description=Microsoft ODBC Driver 17 for SQL Server
 Driver=/opt/microsoft/msodbcsql17/lib64/libmsodbcsql-17.5.so.2.1
 UsageCount=1

================================================================================================

An entry such as the above should be created manually if it does not already
exist. In this case it is important to ensure that the backend driver
referenced by `Driver` has been installed correctly:

[source,shell]
----
$ ldd /opt/microsoft/msodbcsql17/lib64/libmsodbcsql-17.5.so.2.1
       linux-vdso.so.1 =>  (0x00007fff2bb12000)
       libdl.so.2 => /lib64/libdl.so.2 (0x00007f25f0459000)
       librt.so.1 => /lib64/librt.so.1 (0x00007f25f0251000)
       libodbcinst.so.2 => /lib64/libodbcinst.so.2 (0x00007f25f003f000)
...
----

NOTE: The config section name in `[square brackets]` is the ODBC name of the
database driver. It is an arbitrary name but must be referenced exactly in the
DSN definitions within `/etc/odbc.ini`.

You can verify that the ODBC driver definition can be successfully found by
name with:

[source,shell]
----
odbcinst -q -d -n "ODBC Driver 17 for SQL Server"
----


=== Configure a DSN for the data source

Create a DSN for the database server, referencing the ODBC driver by its exact
config section name.

.Example data source definition in `/etc/odbc.ini`
================================================================================================

 [MSSQLdb]
 Driver = ODBC Driver 17 for SQL Server
 Description = My DSN for the FreeRADIUS database
 Server = tcp:192.0.2.1,1443
 Database = radius

================================================================================================

Replace the values for `Server` and `Database` with your own. The config must
include a `Database` parameter since this is not indicated by the rlm_sql
configuration.

NOTE: The config section name in `[square brackets]` is the name of the DSN
which is the lookup key for the connection.  It is an arbitrary name but it
must be referenced identically in connection strings such as in the FreeRADIUS
`rlm_sql` module configuration.


=== Test the ODBC connection

unixODBC includes a basic tool called `isql` that can be used to make ODBC
connections a data source using its DSN.

Run the following which is the ODBC equivalent of the native test performed
earlier:

[source,shell]
----
echo 'SELECT * FROM sys.schemas' | isql -b -v MSSQLdb radius radPass_123
----

If `isql` fails then double check the `odbc.ini` and `odbcinst.ini` entries. If
the failure isn't obvious then ODBC tracing can be enabled by adding an
additional entry to odbcinst.ini, as follows:

.Example `/etc/odbcinst.ini` entry to enable tracing
================================================================================================

 [ODBC]
 Trace = yes
 TraceFile = /tmp/odbc_trace.log

================================================================================================

WARNING: Do not proceed with testing FreeRADIUS until the above ODBC test is
known to work.


=== Configure an instance of rlm_sql to use rlm_sql_unixodbc

The `rlm_sql` module can be configured as follows:

.Example configuration for an ODBC connection in /etc/raddb/mods-available/sql
============================================================

 sql {
     dialect = "mssql"
     driver = "unixodbc"
     server = "MSSQLdb"           # The exact "[DSN]" from odbc.ini
     login = "radius"
     password = "radPass_123"
 #   radius_db = "radius"         # Ignored! Database is set in odbc.ini
 ...
 }

============================================================

The `rlm_sql` module should be enabled as follows:

[source,shell]
----
cd /etc/raddb/mods-enabled
ln -s ../mods-available/sql
----


=== Test FreeRADIUS in debug mode

Start FreeRADIUS in debug mode:

[source,shell]
----
radiusd -X
----

Look for the following lines which indicate that FreeRADIUS has successfully
made a connection to the database.

.Example FreeRADIUS debug output for successful ODBC connections
================================================================================================

 rlm_sql (sql): Opening additional connection (0), 1 of 8 pending slots used
 rlm_sql (sql): Opening additional connection (1), 1 of 8 pending slots used
 rlm_sql (sql): Opening additional connection (2), 1 of 8 pending slots used

================================================================================================

If there is a problem then FreeRADIUS will usually terminate with a descriptive
error message identifying the issue, such as the following:

.Example FreeRADIUS debug output showing errors from rlm_sql_unixodbc
================================================================================================

 rlm_sql (sql): Opening additional connection (0), 1 of 8 pending slots used
 rlm_sql_unixodbc: 28000 [unixODBC][Microsoft][ODBC Driver for SQL Server]
                         [SQL Server]Login failed for user 'radius'.
 rlm_sql_unixodbc: Connection failed
 rlm_sql_unixodbc: Socket destructor called, closing socket
 rlm_sql (sql): Opening connection failed (0)
 rlm_sql (sql): Removing connection pool
 /etc/raddb/mods-enabled/sql[1]: Instantiation failed for module "sql"

================================================================================================

If you need a test user then you may be able to add one as follows if you
permissions permit this:

[source,shell]
----
sqlcmd -S tcp:192.0.2.1,1433 -U radius -P radPass_123 -d radius   \
    -Q "INSERT INTO radcheck (username, attribute, op, value)     \
        VALUES ('bob', 'Password.Cleartext', ':=', 'radpass')"
----

Finally, attempt an authentication:

.Example successful authentication
================================================================================================

 $ radtest bob test 127.0.0.1 0 testing123
 Sent Access-Request Id 53 from 0.0.0.0:12345 to 127.0.0.1:1812
        Password.Cleartext = "test"
        User-Name = "bob"
        User-Password = "test"
        NAS-IP-Address = 192.0.2.10
        NAS-Port = 0
        Message-Authenticator = 0x00
 Received Access-Accept Id 53 from 127.0.0.1:1812 to 0.0.0.0:12345
        User-Name = "bob"

================================================================================================

CAUTION: If ODBC tracing has been enabled during testing then you should
remember to disable this before moving into production.
