[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.10 Controlling Authentication Probes

Authentication probe is an attempt of a user to use other user's account, by guessing his password. The obvious indication of an authentication probe is appearence of several consecutive authentication failures for the same user. Of course, if the intruder is given sufficient number of such probes he will sooner or later succeed in finding the actual password. The conventional method to prevent this from occurring is to keep failure counters for each user and to lock the account when its failure counter reaches a predefined limit. Notice that a legitimate user may fail (sometimes even several times in sequence) in entering his password, so two important points should always be observed. First, failure counters record the number of consecutive authentication failures and they are reset after each successive authentication. Secondly, the maximum number of allowed consecutive failures should be set sufficiently high.

The version 1.6 offers two ways for controlling authentication probes: using external programs and using special SQL queries.

To control authentication probes using external programs, use the combination of Exec-Program-Wait and Auth-Failure-Trigger. The program specified by Auth-Failure-Trigger is executed each time an authentication attempt failed. When both attributes are used together, the program invoked by Auth-Failure-Trigger can update the failure counter, and the one invoked by Exec-Program-Wait can compare the counter value with the predefined limit and reject authentication when both values become equal. Such approach is most useful in conjunction with BEGIN profile.

Let's suppose the program ‘/sbin/check_failure’ accepts a user name and returns 1 if the failure counter for this user has reached maximum allowed value. Otherwise it returns 0 and clears the counter. Another program, ‘/sbin/count_failure’ increases failure counter value for the given user name. Assuming our basic authentication type is ‘PAM’, the ‘raddb/users’ file will look as follows:

 
BEGIN   NULL
        Exec-Program-Wait = "/sbin/check_failure  %C{User-Name}",
        Auth-Failure-Trigger = "/sbin/count_failure %C{User-Name}",
                Fall-Through = Yes

DEFAULT Auth-Type = PAM
        Service-Type = Framed-User,
                Framed-Protocol = PPP

[… Other profiles …]                

The BEGIN profile will be executed before any other profile. It will add to the RHS Exec-Program-Wait and Auth-Failure-Trigger attributes and then radiusd will proceed to finding a matching profile (due to Fall-Through attribute). When such profile is found, the user will be authenticated according to the method set up by the profile's Auth-Type attribute. If authentication fails, ‘/sbin/count_failure’ will be called and the user name passed to it as the argument. Otherwise, ‘/sbin/check_failure’ will be invoked.

To complete the example, here are working versions of both programs. Failure counters for each user name are kept in separate file in ‘/var/log/radius/fails’ directory. Both programs are written in bash.

The /sbin/count_failure program

 
#! /bin/bash

test $# -eq 1 || exit 1

MAXFAIL=8
REGDIR=/var/log/radius/fails

if [ -r "$REGDIR/$1" ]; then
  read COUNT < "$REGDIR/$1"
  COUNT=$((COUNT+1))
else
  COUNT=1
fi
echo $COUNT > "$REGDIR/$1"      
# End of /sbin/count_failure

The /sbin/check_failure program

 
#! /bin/bash

test $# -eq 1 || exit 1

MAXFAIL=8
REGDIR=/var/log/radius/fails

if [ -r "$REGDIR/$1" ]; then
  read COUNT < "$REGDIR/$1"
  if [ $COUNT -ge $MAXFAIL ]; then
    echo "Reply-Message=\"Too many login failures. Your account is locked\""
    exit 1
  else
    rm "$REGDIR/$1"
  fi
fi
exit 0

# End of check_failure

Another way of controlling authentication probes is by using SQL database to store failure counters. Two queries are provided for this purpose in ‘raddb/sqlserver’ file: auth_success_query is executed upon each successful authentication, and auth_failure_query is executed upon each authentication failure. Both queries are not expected to return any values. One obvious purpose of auth_failure_query would be to update failure counters and that of auth_success_query would be to clear them. The auth_query or group_query should then be modified to take into account the number of authentication failures.

The default SQL configuration GNU Radius is shipped with provides a working example of using these queries. Let's consider this example.

First, we create a special table for keeping authentication failure counters for each user:

 
CREATE TABLE authfail (
  # User name this entry refers to
  user_name           varchar(32) binary default '' not null,
  # Number of successive authentication failures for this user
  count               int,
  # Timestamp when this entry was last updated
  time                datetime DEFAULT '1970-01-01 00:00:00' NOT NULL,
  # Create a unique index on user_name
  UNIQUE uname (user_name)
);

The query auth_fail_query will increment the value of count column for the user in question:

 
auth_failure_query UPDATE authfail \
                   SET count=count+1,time=now() \
                   WHERE user_name='%C{User-Name}'

The query auth_success_query will clear count:

 
auth_success_query UPDATE authfail \
                   SET count=0,time=now() \
                   WHERE user_name='%C{User-Name}'

Now, the question is: how to use this counter in authentication? The answer is quite simple. First, let's create a special group for all the users whose authentication failure counter has reached its maximum value. Let this group be called ‘*LOCKED_ACCOUNT*’. We'll add the following entry to ‘raddb/users’:

 
DEFAULT Group = "*LOCKED_ACCOUNT*",
                Auth-Type = Reject
        Reply-Message = "Your account is currently locked.\n\
Please, contact your system administrator\n"

which will reject all such users with an appropriate reply message.

The only thing left now is to rewrite group_query so that it returns ‘*LOCKED_ACCOUNT*’ when authfail.count reaches its maximum value. Let's say this maximum value is 8. Then the following query will do the job:

 
group_query       SELECT user_group FROM groups \
                  WHERE user_name='%u' \
                  UNION \
                  SELECT CASE WHEN (SELECT count > 8 FROM authfail \
                                                 WHERE user_name='%u')
                         THEN '*LOCKED_ACCOUNT*' END

The default configuration comes with these queries commented out. It is up to you to uncomment them if you wish to use SQL-based control over authentication failures.

Notice the following important points when using this approach:

  1. Your SQL server must support UNION. Earlier versions of MySQL lacked this support, so if you run MySQL make sure you run a reasonably new version (at least 4.0.18).
  2. Both auth_failure_query and auth_success_query assume the database already contains an entry for each user. So, when adding a new user to the database, make sure to insert an appropriate record into authfails table, e.g.
     
    INSERT INTO authfail VALUES('new-user',0,now());
    

[ < ] [ > ]   [ << ] [ Up ] [ >> ]

This document was generated by Sergey Poznyakoff on December, 6 2008 using texi2html 1.78.