1

What I have: freeradius 2.1.10 on debian, configured to use a database.

How it works now: There are many devices on the network and users, the users log on devices to configure them and so on. The users can log on to anything. For example some devices (ciscos) the user account on radius comes along with privileges (cisco-avpair attribute) that restrict what that individual can and cannot do on cisco devices.

What I want freeradius to do in addition, for a specific special case: There's a special device that only select users should be able to authenticate into. Everyone else should be just denied access. (so, more strict than the cisco example above).

What I think I should be doing is first define my own attribute for example company-special-privilege so that I can set that for some users in the database with a value of 0 or 1. Then define some policy in policy.conf.

What I'm asking: Never done policies and I don't understand where they're applied in the rest of the freeradius config (where should I put my special_access policy). Also unsure how to formulate it but the following pseudocode should represent what I want:

special_access {
    if (request to log in $special_device_ip) {
        if ($username company-special-privilege) {
            reject #
        }
    }
}

From the above I don't know how to get the device IP or the attribute that the user is set with. I'm not doing a == 1 on the attribute in the second condition in case the user doesn't have the attribute as I don't know what that would imply but any user that doesn't have 1 must be rejected.

Recct
  • 360
  • 1
  • 3
  • 20

2 Answers2

4

You should put it in raddb/policy.conf inside the policy {} stanza. Then they can be referenced (by their name) as you would a normal module, in authorize, authenticate, post-auth etc...

Policies in FreeRADIUS are essentially macros, they're not functions, they don't take arguments.

Defining a special attribute to control policy decisions is fine, do it in raddb/dictionary unless you have an IANA number and want to spin your own custom dictionary. An easier way may just be to query SQL directly.

I'm not sure what you want to do specifically, but here's an example that may help... Modify as necessary

special_access {
    if ("%{Called-Station-ID}" == 'special device') {
        if ("%{sql:SELECT special_priv FROM table WHERE username = '%{User-Name}'}" != '1') {
            reject
        }
    }
}
Arran Cudbard-Bell
  • 1,514
  • 1
  • 9
  • 18
  • I assume then that I should have `special_access` in `authenticate`? I don't know what the csid will be reported as, will that hold the IP of said special device? I've seen some weird values filled in the accounting table but for other types of radius account.. – Recct Apr 12 '15 at 20:33
  • It's best to stick to the standard AAA model. That is, policies for authorizing users go in authorize, policies for authenticating credentials provided by the user go in authenticate. If the user is authenticating with credentials, then you'd list ``special_access`` and the module that could deal with those credentials, like pap, in ``authorize {}``, and then use pap again in ``authenticate {}``. – Arran Cudbard-Bell Apr 12 '15 at 20:40
  • If that doesn't make sense, this page might help: http://wiki.freeradius.org/guide/Concepts – Arran Cudbard-Bell Apr 12 '15 at 20:41
  • Oh and it's impossible to know what the Calling-Station-Id will be, it's a text field, the NAS can stick anything it likes in there. It was just an example of something that'd identify the device. – Arran Cudbard-Bell Apr 13 '15 at 01:38
3

Thanks to some info I found on http://linotp.org/doc/2.6/part-installation/integration/index.html you can use the following configuration if you use the nas table in the MySQL database. in the nas table you have IP ranges per device group, so if you device is in the specific IP range, the policy will be used:

mysql> SELECT * FROM nas WHERE is = '1'; 
+----+------------------+-------------+-------+------------+----------------------------------+
| id | nasip            | shortname   | type  | secret     | description                      |
+----+------------------+-------------+-------+------------+----------------------------------+
|  1 | 192.168.1.0/24   | restricted  | other | *********  | only some users have access      |
+----+------------------+-------------+-------+------------+----------------------------------+

In the file sites-enabled/default you refer to the policy;

authorize {
 ...
 update request {
     FreeRADIUS-Client-Shortname = "%{Client-Shortname}"
 }
 my_policy
 ...
}

and in the policy.conf you write your policy. This policy check first the nas table for the restricted prefix and then checks if the user has sufficient rights

...
  my_policy {
    if (FreeRadius-Client-Shortname =~ /^restricted/) {
      if ("%{sql:SELECT moreaccess FROM radusergroup WHERE username = '%{User-Name}'}" != '1') {
        update reply {
          Cisco-AVPair := "shell:priv-lvl=1"
        }
      }
    }
  }
...

And of course in your radusergroup table where you're referring to in your SQL query you add an extra column where you allow specific users with extra rights.

mysql> SELECT * FROM radusergroup WHERE username like 'peter%';
+------------------+--------------+----------+------------+
| username         | groupname    | priority | moreaccess |
+------------------+--------------+----------+------------+
| peter            | super-rights |        1 |       NULL |
| peter1           | super-rights |        1 |          1 |
| peter2           | super-rights |        1 |          0 |
+------------------+--------------+----------+------------+

In this case I wrote the policy like this;

  • Normally already get the high privilege: Cisco-AVPair := "shell:priv-lvl=15"
  • So if the user has a '1' in this case peter1 is just getting the normal rights.
  • If the user doesn't have a '1' (!= '1'), it will get the right mentioned in the policy.
    • instead of using update reply you can use reject to completely block access for this (peter and peter2) users.

it could get quiet complicated, but this works for me.