RADIUS with LDAP: changing LDAP filter based on RADIUS request

Sylvain Robitaille syl at alcor.concordia.ca
Mon Jun 9 22:54:20 CEST 2008


Background:

   We're using FreeRADIUS-2.0.3 with an OpenLDAP backend, on two separate
systems for authentication and authorization of users to our wireless
network and a lesser-used VPN service.  I would like to be able to modify
the LDAP query filter based on (for example) which NAS device sent the
RADIUS request so that (again, for example) a different filter might be
used to search for wireless users than for VPN users.

In an effort to be able to setup a "dynamic" LDAP query filter, I have
tried adding a string-type attribute to our local dictionary file,
to hold the filter that would be updated on-the-fly:

  ATTRIBUTE     CU-LDAP-Filter          3000    string

The radiusd.conf file then has ldap module instances (one per LDAP
server) with the following reference to this attribute:

         ldap ldap_host1 {
                 server = "ldap_host1"
                 identity = "cn=...,ou=...,dc=concordia,dc=ca"
                 password = "**SANITIZED**"
                 basedn = "ou=people,dc=concordia,dc=ca"
                 filter = "%{CU-LDAP-Filter}"
                 base_filter = "(objectclass=ConcordiaPerson)"
                 ...
         }

         ldap ldap_host2 {
                 server = "ldap_host2"
                 identity = "cn=...,ou=...,dc=concordia,dc=ca"
                 password = "**SANITIZED**"
                 basedn = "ou=people,dc=concordia,dc=ca"
                 filter = "%{CU-LDAP-Filter}"
                 base_filter = "(objectclass=ConcordiaPerson)"
                 ...
         }

I'm presently testing with the radeapclient client (on localhost), so am
testing with the following configured into sites-enabled/default:

    authorize {
            ...
            switch "%{NAS-IP-Address}" {
                case 192.168.198.20 {
                    # test
                    update control {
                       CU-LDAP-Filter = "(&(cn=%{%{Stripped-User-Name}:-%{User-Name}})(***LDAP QUERY1 TRIMMED FOR BREVITY***))"
                    }
                }
                case 127.0.0.1 {
                    # test
                    update control {
                       CU-LDAP-Filter = "(&(cn=%{%{Stripped-User-Name}:-%{User-Name}})(***LDAP QUERY2 TRIMMED FOR BREVITY***))"
                    }
                }
                ...
                case {
                   # default
                   noop
                }
            }

            if ("%{CU-LDAP-Filter}" != "") {
                redundant-load-balance {
                   ldap_host1
                   ldap_host2
                }
            }
            ...
    }

The LDAP query filters, though not included above (these things are quite
long and site-specific anyway) are known to work, as they're copied from
our currently in-production configuration that is selecting the ldap
module instance based on the NAS-IP-Address (ie. if NAS-IP-Address is
the VPN server we call the ldap_vpn module which is configured with an
appropriate filter, else we call the ldap_wireless module which is also
appropriately configured).  The present setup works, but based on what
I'm reading it would give is a much more flexible and fault-tolerant
(and dare-I-say, more manageable?) setup if I could get the LDAP module
instances configured into a redundant-load-balance stanza, with a
dynamically determined LDAP query filter (so that if one LDAP server is
unavailable, one or more others could be tried instead, instead of
having a hard-coded query to specific LDAP servers for each service).

I hope I'm making sense so far ...

The following command is issued to test:

    radeapclient -x localhost:1815 auth testing123 << END_OF_EAP
    User-Name = "j_doe"
    Cleartext-Password = "**SANITIZED**"
    NAS-IP-Address = 192.168.198.20
    NAS-Port-Type = "Wireless-802.11"
    EAP-Code = Response
    EAP-Id = 210
    EAP-Type-Identity = "j_doe"
    Message-Authenticator = 0x00
    NAS-Port = 0
    END_OF_EAP

Radiusd, running in debug mode shows the following (config-file parsing
output skipped;  it is not showing any errors at all, so although the
configuration I show above may not be producing the intended result, it
at least seems syntactically correct):

    Listening on authentication address * port 1815
    Listening on accounting address * port 1816
    Listening on proxy address * port 1817
    Ready to process requests.
            User-Name = "j_doe"
            NAS-IP-Address = 192.168.198.20
            NAS-Port-Type = Wireless-802.11
            Message-Authenticator = 0x8499bae7c162d5f0ef2e587fdb9086a0
            NAS-Port = 0
            EAP-Message = 0x02d200080173796c
    +- entering group authorize
    ++[preprocess] returns ok
    ++[chap] returns noop
    ++[mschap] returns noop
        rlm_realm: No '@' in User-Name = "j_doe", looking up realm NULL
        rlm_realm: No such realm "NULL"
    ++[suffix] returns noop
      rlm_eap: EAP packet type response id 210 length 8
      rlm_eap: No EAP Start, assuming it's an on-going EAP conversation
    ++[eap] returns updated
    ++[unix] returns notfound
    ++[files] returns noop
            expand: %{NAS-IP-Address} -> 192.168.198.20
    ++- entering switch %{NAS-IP-Address}
    +++- entering case 192.168.198.20
            expand: %{Stripped-User-Name} ->
            expand: %{User-Name} -> j_doe
            expand: (&(cn=%{%{Stripped-User-Name}:-%{User-Name}})**LDAP QUERY1 FILTER TRIMMED**) -> (&(cn=j_doe)**LDAP QUERY1 FILTER TRIMMED**)
    ++++[control] returns noop
    +++- case 192.168.198.20 returns noop
    ++- switch %{NAS-IP-Address} returns noop
    ++? if ("%{CU-LDAP-Filter}" != "")
            expand: %{CU-LDAP-Filter} ->
    ? Evaluating ("%{CU-LDAP-Filter}" != "") -> FALSE
    ++? if ("%{CU-LDAP-Filter}" != "") -> FALSE
    ++[expiration] returns noop
    ++[logintime] returns noop
    rlm_pap: WARNING! No "known good" password found for the user.  Authentication may fail because of this.
    ++[pap] returns noop
      rad_check_password:  Found Auth-Type EAP
    auth: type "EAP"
    +- entering group authenticate
      rlm_eap: EAP Identity
      rlm_eap: processing type tls
      rlm_eap_tls: Initiate
      rlm_eap_tls: Start returned 1
      rlm_eap: RT Modif EAP-Type = 21 EAP-LENGTH = 1
    ++[eap] returns handled
            EAP-Message = 0x01d300061520
            Message-Authenticator = 0x00000000000000000000000000000000
            State = 0x1c870d031c541859b55d17dd8af5914a
    Finished request 0.
    Going to the next request
    Waking up in 4.9 seconds.
    Cleaning up request 0 ID 232 with timestamp +4
    Ready to process requests.


Note that of the above, I believe the following is relevant to my
question:

            expand: %{NAS-IP-Address} -> 192.168.198.20
    ++- entering switch %{NAS-IP-Address}
    +++- entering case 192.168.198.20
            expand: %{Stripped-User-Name} ->
            expand: %{User-Name} -> j_doe
            expand: (&(cn=%{%{Stripped-User-Name}:-%{User-Name}})**LDAP QUERY1 FILTER TRIMMED**) -> (&(cn=j_doe)**LDAP QUERY1 FILTER TRIMMED**)
    ++++[control] returns noop
    +++- case 192.168.198.20 returns noop
    ++- switch %{NAS-IP-Address} returns noop
    ++? if ("%{CU-LDAP-Filter}" != "")
            expand: %{CU-LDAP-Filter} ->
    ? Evaluating ("%{CU-LDAP-Filter}" != "") -> FALSE
    ++? if ("%{CU-LDAP-Filter}" != "") -> FALSE

This corresponds to the following from the "authorize" section in my
sites-enabled/default file:

            switch "%{NAS-IP-Address}" {
                case 192.168.198.20 {
                    # test
                    update control {
                       CU-LDAP-Filter = "(&(cn=%{%{Stripped-User-Name}:-%{User-Name}})(***LDAP QUERY TRIMMED FOR BREVITY***))"
                    }
                }
            }

We see that the line where I intend to assign a value to CU-LDAP-Filter
is indeed encountered and the variables referred to in that line are
expanded correctly, but it seems as though the assignment to
CU-LDAP-Filter isn't done:  if ("%{CU-LDAP-Filter}" != "") is evaluated
as "FALSE".

I suspect that my "update" statement is likely incorrect, since the only
mention of it in the debug output is:

    ++++[control] returns noop

Can anyone help me with this?  I've read as much of the relevant
documentation as I'm aware exists, especially "man unlang" without which
I certainly wouldn't have gotten even this far, but I'd be very grateful
if someone could point me to further documentation that would help with
what I'm trying to do ...

Thanks ...

-- 
----------------------------------------------------------------------
Sylvain Robitaille                              syl at alcor.concordia.ca

Systems and Network analyst                       Concordia University
Instructional & Information Technology        Montreal, Quebec, Canada
----------------------------------------------------------------------



More information about the Freeradius-Users mailing list