Best way to deny users not matching any groups in the SQL DB

Alan DeKok aland at
Mon Feb 22 14:25:48 CET 2016

On Feb 22, 2016, at 8:00 AM, Sylvain Munaut <s.munaut at> wrote:
> I have a bunch of services like VPN server, Wifi SSID, Wired port
> auth, ...  some are on the same NAS (like several SSID on the same
> AP), some are on other NAS ( like VPN server, .. ).
> For each service, I have a bunch of checks to make sure the request is
> for that particular service, for instances things like :
> wired_private | NAS-IP-Address  | == |
> wired_private | NAS-Port        | <= | 39
> wired_private | NAS-Port        | >= | 20
> wired_private | NAS-Port-Type   | == | Ethernet
> wifi_private  | NAS-IP-Address  | <= |
> wifi_private  | NAS-IP-Address  | >= |
> wifi_private  | NAS-Port-Type   | == | Wireless-802.11
> wifi_private  | Called-Station-SSID | == | Private

  I'm not sure why this level of detail is necessary.  If these are all RADIUS clients on your network, you can know in advance what function each one offers.  All you need to do is to match source IP address of the packet to a "name" for that service.  You could even put that name into the "client" section:

client foo {
	type = auth
	ipaddr =
	secret = ...

	# local extensions!
	local_type = "wifi_private"

  Then, check that via "unlang:

	switch "%{client:local_type}" {
		case wifi_private {
			update reply {
				 | Cisco-AVPair  += ssid=Private


  You could also put the service information into a special SQL table, keyed by client source IP.

  There's really no need to do additional checks for NAS-Port, etc.  You can add them if you wish... but it makes things more complicated.

  This is about separation of functionality.  Mixing & matching NAS rules with user rules is a bad idea.  Write down all of the rules for the NAS, including replies.  Get those implemented.  Then, add user rules on top of that.

> Then I have users that authenticate using either EAP-MD5 / EAP-PEAP /
> EAP-MSCHAPv2 / EAP-TLS  (depends on the user and depend on the
> service, some only have certs, some only have passwords).
> And for each of theses users, I have a list of the services they must
> be able to access.

  Then use a separate SQL table to match user to service, as I suggested earlier.

	if (! "%{sql:SELECT username from service_table WHERE username = '%{User-Name}' AND service = "%{client:local_type}"}") {

  Simple.  The *logic* is in FreeRADIUS.  The *data* is in a database.

> If the user tries to access a service he's allowed to, he must be allowed.
> If the user tries to access a service he's not allowed to, he must be rejected.

  See above.  A 3 line "unlang" policy does this.

> If the user doesn't even exist (for instance, he has a EAP-TLS cert
> but he's not appearing in the DB at all), then he must be rejected as
> well.

  The 3 line unlang policy does this.

> (and wrt to EAP-TLS, yes, I'm already checking the User-Name matches
> the cert CN, the 'check-eap-tls' virtual server was pretty clear about
> what to do for this).

  That's good.

> Huh ... I know the functional requirements.
> But I don't know every detail of every messages for every
> authentication protocol and how they could possibly be manipulated by
> a malicious party to make things behave weirdly.

  Don't worry about it.  Honestly.  You control the NAS, so the NAS can't lie to you about it's source IP, etc.

  You don't control the user, but you *do* control who gets authenticated.  Rely on the authentication protocols to do their job, and to accept only authenticated users.

  You're not going to look into the hundreds of pages of TLS specs, or the hundreds of thousands of lines of OpenSSL code to see what weird things people can do.  You're not an expert.  Don't even try.  Rely on the experts to do things correctly.

> For instance, currently I assume that if I don't set a
> Cleartext-Password, a user can't possibly authenitcate with MSCHAPv2.
> But is that true ?

  That's just ridiculous.  Stop wasting your time, and don't worry about it.

> Or is not setting a password equivalent to an empty
> string ? Not something I can even test easily as most clients don't
> let you try an empty string as the password to attempt connection.

  That's also ridiculous.

  People get authenticated when the authentication protocol says they're authenticated.  Trust that.

  Or, look at the MS-CHAP code, and all of the MS-CHAP RFCs to see how it works.  Spend weeks (no, months) trying to break MS-CHAP security.  And TLS.  And EAP.  And RADIUS.  And IP.  It's a never-ending circle of paranoia.

  But I think the process of trusting people works a whole lot better.

> Testing that something works when it's supposed to work is easy.
> Testing that something indeed fails for every case where it's supposed
> to fail is pretty much impossible (to be 100%). Even if I could
> somehow physically test everything I can think of ... that still
> leaves whatever I can't think of now.

  It's a waste of time to worry about that.  Don't bother.

> Which is why I'm trying to find the solution that modify the _least_
> the default files that come with Freeradius because I'm relying on the
> fact the defaults have been written by people who know a whole lot
> more than me about Radius, the authentication protocols it supports
> and Freeradius codebase itself. And that's also consistent with the
> wiki that recommends to edit as little as possible.

  See?  You trust people.  Keep that up.

  Alan DeKok.

More information about the Freeradius-Users mailing list