Freeradius: Request without User-Password attribut

2020-08-02 03:38发布

问题:

I'm trying to set up a radius server using a rest backend for authentication. I used the following project as example: https://github.com/fgsants/REST-API-FreeRADIUS

Everything is setup already, and everything works if I use the following command to simulated a login on radius:

radtest bar passwd 127.0.0.1 10 testing123

The rest backend receives /user/:username/mac?action=authorize and then /user/:username/mac?action=authenticate&password=passwd.

When I configure the AP to use this radius server, the request seems to be different than expected and the auth fails. This is the output of the radius server when trying to login through the AP:

(0) Received Access-Request Id 105 from 172.21.0.1:46358 to 172.21.0.3:1812 length 126
(0)   User-Name = "bar"
(0)   NAS-IP-Address = 192.168.0.21
(0)   NAS-Identifier = "RalinkAP1"
(0)   NAS-Port = 0
(0)   Called-Station-Id = "1A-0D-2C-1B-49-11"
(0)   Calling-Station-Id = "D4-9A-20-70-F4-0E"
(0)   Framed-MTU = 1400
(0)   NAS-Port-Type = Wireless-802.11
(0)   EAP-Message = 0x0201000801626172
(0)   Message-Authenticator = 0xbffda6639904c9026259be2a45b378c4
(0) # Executing section authorize from file /etc/freeradius/3.0/sites-enabled/default
(0)   authorize {
rlm_rest (rest): Reserved connection (0)
(0) rest: Expanding URI components
(0) rest: EXPAND http://rest:3000
(0) rest:    --> http://rest:3000
(0) rest: EXPAND /user/%{User-Name}/mac?action=authorize
(0) rest:    --> /user/bar/mac?action=authorize
(0) rest: Sending HTTP GET to "http://rest:3000/user/bar/mac?action=authorize"
(0) rest: Processing response header
(0) rest:   Status : 204 (No Content)
rlm_rest (rest): Released connection (0)
Need 5 more connections to reach 10 spares
rlm_rest (rest): Opening additional connection (5), 1 of 27 pending slots used
rlm_rest (rest): Connecting to "http://rest:3000"
(0)     [rest] = ok
(0)     if (ok) {
(0)     if (ok)  -> TRUE
(0)     if (ok)  {
(0)       update control {
(0)         Auth-Type := rest
(0)       } # update control = noop
(0)     } # if (ok)  = noop
(0)   } # authorize = ok
(0) Found Auth-Type = rest
(0) # Executing group from file /etc/freeradius/3.0/sites-enabled/default
(0)   Auth-Type rest {
(0) rest: ERROR: You set 'Auth-Type = REST' for a request that does not contain a User-Password attribute!
(0)     [rest] = invalid
(0)   } # Auth-Type rest = invalid
(0) Failed to authenticate the user
(0) Using Post-Auth-Type Reject
(0) Post-Auth-Type sub-section not found.  Ignoring.
(0) # Executing group from file /etc/freeradius/3.0/sites-enabled/default
(0) Delaying response for 1.000000 seconds
Waking up in 0.2 seconds.
Waking up in 0.7 seconds.
(0) (0) Discarding duplicate request from client ipv4 port 46358 - ID: 105 due to delayed response
(0) Sending delayed response
(0) Sent Access-Reject Id 105 from 172.21.0.3:1812 to 172.21.0.1:46358 length 20
Waking up in 3.9 seconds.
(0) Cleaning up request packet ID 105 with timestamp +15
Ready to process requests

The radius server don't seem to be receiving a password from the AP:

(0)   User-Name = "bar"
(0)   NAS-IP-Address = 192.168.0.21
(0)   NAS-Identifier = "RalinkAP1"
(0)   NAS-Port = 0
(0)   Called-Station-Id = "1A-0D-2C-1B-49-11"
(0)   Calling-Station-Id = "D4-9A-20-70-F4-0E"
(0)   Framed-MTU = 1400
(0)   NAS-Port-Type = Wireless-802.11
(0)   EAP-Message = 0x0201000801626172
(0)   Message-Authenticator = 0xbffda6639904c9026259be2a45b378c4

So later the authorization fails:

(0) rest: ERROR: You set 'Auth-Type = REST' for a request that does not contain a User-Password attribute!

This is my configuration file:

server default {

listen {
    type = auth
    ipaddr = *
    port = 0
    limit {
          max_connections = 16
          lifetime = 0
          idle_timeout = 30
    }
}

listen {
    ipaddr = *
    port = 0
    type = acct
    limit {
    }
}

authorize {
    rest
        if (ok) {
            update control {
                Auth-Type := rest
            }
        }
}

authenticate {
    Auth-Type rest {
        rest {
            updated = 1
        }
        if (updated) {
            ok
        }
    }
}

preacct {
    preprocess
    acct_unique
    suffix
}

accounting {
    detail
    rest
}

post-auth {
    update {
        &reply: += &session-state:
    }
}
}

What's missing? What needs to be done so I can receive the password from the access point?

Thanks

回答1:

Module Methods and Authentication Approaches

FreeRADIUS modules have multiple 'methods', each method handles a different aspect of processing RADIUS packets.

The 'authorize' method is called when the module is listed in the authorize section. The authorize method is used to gather additional information to authorize the user.

The 'authenticate' method is called when a module is listed in the authenticate section, and a module in the authorize section specified that this particular module be used to authenticate the user by setting control:Auth-Type = <module>.

There are generally two ways you can authenticate a user:

  1. Submit their credentials to an external authentication server (in this exmple by calling rest.authenticate).
  2. Retrieve a copy of their 'known good' password from an external server, and compare it with what the user submitted (in this example by calling rest.authorize).

You need to decide which of these approaches you're going to use.

If it's approach 1, then the user needs to send their password as plaintext so it can be submitted to the authentication server.

If it's approach 2, then you need to retrieve a copy of the user's password in plaintext from another server.

There are advantages and disadvantages to both approaches, and which approach you choose affects which EAP (Extensible Authentication Protocol) methods are available for you to use.

Wireless authentication

In your example, you're using WPA/2-Enterprise, in which case the authentication framework running between the AP and the Wireless client will be 802.1X.

With 802.1X the AP and Wireless client exchange authentication packets over a protocol called EAPOL (EAP Over Lan). 802.1X and EAPOL allow the Wireless client to submit credentials to an Authentication Server (usually a RADIUS server), before the encryption keys are exchanged (WPA four-way handshake), and before the wireless client can exchange normal (non-EAPOL) packets with the AP (such as DHCP packets).

With wireless authentication, EAPOL and RADIUS serve mainly as transports for EAP, and its EAP that carries the user's credentials during the authentication attempt.

EAP and FreeRADIUS

When the access point forwards EAP data in RADIUS packets it splits the EAP packets into 253-byte chunks and encapsulates those chunks in EAP-Message attributes.

When FreeRADIUS is performing the role of an Authentication Server, it needs to reassemble those 253-byte chunks and run the state machine of whichever EAP method it negotiates with the wireless client.

To simplify writing configurations that deal with EAP authentication, FreeRADIUS uses two virtual servers. The 'outer' server deals with the RADIUS portion of the authentication attempt, and the 'inner' server deals with the EAP portion of the authentication attempt.

Unless you're implementing complex policies the only module the outer server needs to call is 'eap'.

In your configuration remove everything from the authorize and authenticate sections leaving only the eap module:

authorize {
    eap
}

authenticate {
   eap
}

When the eap module is called in the authorize section, the eap module checks that one or more EAP-Message attributes are present in the request, and they are, it sets control:Auth-Type = EAP, so that the module called in the authenticate section is also eap.

When the eap module is called in the authenticate section, the eap module concatenates all the EAP-Message attributes back into a single packet, and runs the EAP state machine, calling EAP submodules as appropriate to run the different EAP methods.

Which EAP method runs first is determined by default_eap_type in raddb/mods-available/eap.

If you're using approach 1 (user submitting plaintext credentials), you need to use an EAP method like EAP-TTLS, which will wrap the plaintext credentials in a TLS wrapper (to protect them from harvesting).

Set default_eap_type = ttls if you want to use approach 1.

If you're using approach 2 (external server provides plaintext credentials), you can use a wider range of EAP methods. PEAP (Protected EAP) is the most widely implemented EAP method amongst consumer devices (phones, laptops, VoIP phones etc..). Like TTLS, PEAP uses a TLS wrapper to protect the credential exchange, but instead of sending the user's password in the clear uses MSCHAPv2 for credential exchange, which means the credentials are never sent over the wire in plaintext.

In truth, MSCHAPv2 only offers limited additional protection over TTLS as MSCHAPv2 is considered broken, but you may want to use PEAP if you have legacy clients running <= Windows 7, as EAP-TTLS support was only added in Windows 8.

Set default_eap_type = peap if you want to use approach 2.

Inner virtual server

FreeRADIUS submodules for TLS based EAP methods such as TTLS and PEAP run a synthetic request (generated internally) through a separate 'inner' virtual server.

The purpose of this request is to represent the decrypted and decoded contents of the TLS wrapper and to make that content available to other modules (such as rest, ldap, pap etc...) in a standard form that they can interpret.

There's an example inner virtual server that is included in the default config called sites-available/inner-tunnel. This virtual server is configured as the default in the default EAP module, and this is the one you should modify to set how the user's credentials are validated.

Approach 1

EAP-TTLS can actually run many different inner authentication methods, even another layer of EAP. This is because EAP-TTLS allows multiple attributes in the RADIUS attribute space to be sent within its TLS wrapper.

In general, however, most supplicants will default to running PAP as the inner authentication method, so when you see the synthetic request being sent to the inner virtual server, it'll likely only contain the User-Name and User-Password attributes.

Remove all existing text in the authorize and authenticate sections of sites-available/inner-tunnel.

Either copy the text from your original example into the authorize and authenticate sections, or... I'm not sure you need to call the REST module twice (honestly it's been a long time since I worked on v3), you may want to try:

authorize {
   if (&User-Password) {
      update control {
          Auth-Type = rest
      }
   }
}

authenticate {
   rest {
      update = 1
   }
   if (update) {
      ok
   }
}

Approach 2

With PEAP, the TLS wrapper contents represent another 'inner' EAP conversation. To process this conversation we need to first retrieve the user's password, and then call the inner-eap module.

Note: inner-eap is another instance of the EAP module, but with only EAP methods enabled that are appropriate to use inside another EAP conversation. inner-eap should be available in mods-available/eap_inner, you may need to symlink this file from mods-available to mods-enabled.

Remove all existing text in the authorize and authenticate sections of sites-available/inner-tunnel.

In the authorize section list the rest module, then the inner-eap module:

authorize {
    rest
    inner-eap
}

When the request is received by your API endpoint, you'll need to return a control:Cleartext-Password attribute, with the user's cleartext password.

In the authenticate section list only the inner-eap module:

authenticate {
    inner-eap
}


回答2:

I have spent an enormous amount of time, trying to figure out, how is this thing suppose to work, as I am a newbie, regarding freeradius. I am also using the nodejs rest api and was able to succeed with the second approach, but in order to do it, I had to add some more bits, inside the inner-tunnel file as follows:

authorize {
    update control {
        MS-CHAP-Use-NTLM-Auth := No
    }
    rest
    inner-eap
}

authenticate {
    inner-eap
    Auth-Type PAP {
        pap
    }
    Auth-Type MS-CHAP {
        mschap
    }
}


标签: freeradius