eap module returning 'updated' rather than 'ok'
I have an issue with inner versus outer identities. I can demonstrate this with the stock freeradius 3.0.12 config; just uncomment the "bob" and "steve" entries. I have also set "use_tunneled_reply = yes" in raddb/mods-available/eap (in both places) Now I create a config for eapol_test (from wpa_supplicant package) like this: ---- # # eapol_test -c peap-mschapv2.conf -s testing123 # network={ ssid="Cityfibre Admin" key_mgmt=WPA-EAP eap=PEAP identity="bob" anonymous_identity="steve" password="hello" phase2="autheap=MSCHAPV2" # # Uncomment the following to perform server certificate validation. # ca_cert="/etc/raddb/certs/ca.der" } ---- Note how I've chosen "steve" as the anonymous identity. What happens is that is in the second Access-Challenge response, steve's attributes are returned: (0) Received Access-Request Id 0 from 127.0.0.1:49950 to 127.0.0.1:1812 length 118 (0) User-Name = "steve" (0) NAS-IP-Address = 127.0.0.1 (0) Calling-Station-Id = "02-00-00-00-00-01" (0) Framed-MTU = 1400 (0) NAS-Port-Type = Wireless-802.11 (0) Connect-Info = "CONNECT 11Mbps 802.11b" (0) EAP-Message = 0x0200000a017374657665 (0) Message-Authenticator = 0xc31453b2556c5c5767691b67d9d1b1b8 (0) # Executing section authorize from file /etc/raddb/sites-enabled/default (0) authorize { (0) policy filter_username { (0) if (&User-Name) { (0) if (&User-Name) -> TRUE (0) if (&User-Name) { (0) if (&User-Name =~ / /) { (0) if (&User-Name =~ / /) -> FALSE (0) if (&User-Name =~ /@[^@]*@/ ) { (0) if (&User-Name =~ /@[^@]*@/ ) -> FALSE (0) if (&User-Name =~ /\.\./ ) { (0) if (&User-Name =~ /\.\./ ) -> FALSE (0) if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/)) { (0) if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/)) -> FALSE (0) if (&User-Name =~ /\.$/) { (0) if (&User-Name =~ /\.$/) -> FALSE (0) if (&User-Name =~ /@\./) { (0) if (&User-Name =~ /@\./) -> FALSE (0) } # if (&User-Name) = notfound (0) } # policy filter_username = notfound (0) [preprocess] = ok (0) [chap] = noop (0) [mschap] = noop (0) [digest] = noop (0) suffix: Checking for suffix after "@" (0) suffix: No '@' in User-Name = "steve", looking up realm NULL (0) suffix: No such realm "NULL" (0) [suffix] = noop (0) eap: Peer sent EAP Response (code 2) ID 0 length 10 (0) eap: EAP-Identity reply, returning 'ok' so we can short-circuit the rest of authorize (0) [eap] = ok (0) } # authorize = ok (0) Found Auth-Type = eap (0) # Executing group from file /etc/raddb/sites-enabled/default (0) authenticate { (0) eap: Peer sent packet with method EAP Identity (1) (0) eap: Calling submodule eap_md5 to process data (0) eap_md5: Issuing MD5 Challenge (0) eap: Sending EAP Request (code 1) ID 1 length 22 (0) eap: EAP session adding &reply:State = 0x74107434741170f2 (0) [eap] = handled (0) } # authenticate = handled (0) Using Post-Auth-Type Challenge (0) Post-Auth-Type sub-section not found. Ignoring. (0) # Executing group from file /etc/raddb/sites-enabled/default (0) Sent Access-Challenge Id 0 from 127.0.0.1:1812 to 127.0.0.1:49950 length 0 (0) EAP-Message = 0x0101001604103936abba88e393e0bfbb63a6f9636887 (0) Message-Authenticator = 0x00000000000000000000000000000000 (0) State = 0x74107434741170f2f863cec79136ac13 (0) Finished request Waking up in 4.9 seconds. (1) Received Access-Request Id 1 from 127.0.0.1:49950 to 127.0.0.1:1812 length 132 (1) User-Name = "steve" (1) NAS-IP-Address = 127.0.0.1 (1) Calling-Station-Id = "02-00-00-00-00-01" (1) Framed-MTU = 1400 (1) NAS-Port-Type = Wireless-802.11 (1) Connect-Info = "CONNECT 11Mbps 802.11b" (1) EAP-Message = 0x020100060319 (1) State = 0x74107434741170f2f863cec79136ac13 (1) Message-Authenticator = 0xbf4a7b7d48c99b11a68c98a2af96b7b6 (1) session-state: No cached attributes (1) # Executing section authorize from file /etc/raddb/sites-enabled/default (1) authorize { (1) policy filter_username { (1) if (&User-Name) { (1) if (&User-Name) -> TRUE (1) if (&User-Name) { (1) if (&User-Name =~ / /) { (1) if (&User-Name =~ / /) -> FALSE (1) if (&User-Name =~ /@[^@]*@/ ) { (1) if (&User-Name =~ /@[^@]*@/ ) -> FALSE (1) if (&User-Name =~ /\.\./ ) { (1) if (&User-Name =~ /\.\./ ) -> FALSE (1) if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/)) { (1) if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/)) -> FALSE (1) if (&User-Name =~ /\.$/) { (1) if (&User-Name =~ /\.$/) -> FALSE (1) if (&User-Name =~ /@\./) { (1) if (&User-Name =~ /@\./) -> FALSE (1) } # if (&User-Name) = notfound (1) } # policy filter_username = notfound (1) [preprocess] = ok (1) [chap] = noop (1) [mschap] = noop (1) [digest] = noop (1) suffix: Checking for suffix after "@" (1) suffix: No '@' in User-Name = "steve", looking up realm NULL (1) suffix: No such realm "NULL" (1) [suffix] = noop (1) eap: Peer sent EAP Response (code 2) ID 1 length 6 (1) eap: No EAP Start, assuming it's an on-going EAP conversation (1) [eap] = updated (1) files: users: Matched entry steve at line 73 (1) [files] = ok (1) [expiration] = noop (1) [logintime] = noop (1) pap: WARNING: Auth-Type already set. Not setting to PAP (1) [pap] = noop (1) } # authorize = updated (1) Found Auth-Type = eap (1) # Executing group from file /etc/raddb/sites-enabled/default (1) authenticate { (1) eap: Expiring EAP session with state 0x74107434741170f2 (1) eap: Finished EAP session with state 0x74107434741170f2 (1) eap: Previous EAP request found for state 0x74107434741170f2, released from the list (1) eap: Peer sent packet with method EAP NAK (3) (1) eap: Found mutually acceptable type PEAP (25) (1) eap: Calling submodule eap_peap to process data (1) eap_peap: Initiating new EAP-TLS session (1) eap_peap: Flushing SSL sessions (of #0) (1) eap_peap: [eaptls start] = request (1) eap: Sending EAP Request (code 1) ID 2 length 6 (1) eap: EAP session adding &reply:State = 0x7410743475126df2 (1) [eap] = handled (1) } # authenticate = handled (1) Using Post-Auth-Type Challenge (1) Post-Auth-Type sub-section not found. Ignoring. (1) # Executing group from file /etc/raddb/sites-enabled/default (1) Sent Access-Challenge Id 1 from 127.0.0.1:1812 to 127.0.0.1:49950 length 0 *(1) Service-Type = Framed-User** **(1) Framed-Protocol = PPP** **(1) Framed-IP-Address = 172.16.3.33** **(1) Framed-IP-Netmask = 255.255.255.0** **(1) Framed-Routing = Broadcast-Listen** **(1) Framed-Filter-Id = "std.ppp"** **(1) Framed-MTU = 1500** **(1) Framed-Compression = Van-Jacobson-TCP-IP** *(1) EAP-Message = 0x010200061920 (1) Message-Authenticator = 0x00000000000000000000000000000000 (1) State = 0x7410743475126df2f863cec79136ac13 (1) Finished request It's clearly wrong to return steve's authorization attributes, since we've not authenticated at all (and certainly not as steve) - although since this only an Access-Challenge, hopefully the NAS will ignore them. The EAP exchange does complete successfully. My other concern is that it does an unnecessary database lookup for "steve" - actually the live config which started this investigation is an LDAP one, which is how I noticed this. Now, the default site has in its authorize section: eap { ok = return } But at this step we're getting "updated". So it looks like it would be reasonable to change this to: eap { ok = return updated = return } ... and this does seem to work. But I wonder why it's done this way in the default config. Is this a mistake, or this there some subtle point I am missing? Under what circumstances does rlm_eap return "updated" instead of "ok"? I want to be sure that there's no security impact by dropping out of the authorize section at this point, for example if someone uses a non-tunneled version of EAP like EAP-TLS or EAP-PWD. Thanks, Brian.
On Oct 20, 2016, at 9:33 AM, Brian Candler <b.candler@pobox.com> wrote:
I have an issue with inner versus outer identities. I can demonstrate this with the stock freeradius 3.0.12 config; just uncomment the "bob" and "steve" entries. I have also set "use_tunneled_reply = yes" in raddb/mods-available/eap (in both places)
The inner and outer identities are largely unrelated. I have an outline of an RFC which attempts to deal with this.
Now I create a config for eapol_test (from wpa_supplicant package) like this:
----
# # eapol_test -c peap-mschapv2.conf -s testing123 # network={ ssid="Cityfibre Admin" key_mgmt=WPA-EAP eap=PEAP identity="bob" anonymous_identity="steve"
Those two identities don't have to be related in any way whatsoever.
Note how I've chosen "steve" as the anonymous identity.
"steve" isn't really an "anonymous" identity. For a discussion of anonymous identities, see my RFC: https://tools.ietf.org/html/rfc7542#section-2.4
What happens is that is in the second Access-Challenge response, steve's attributes are returned:
Which is what you told it to do.
It's clearly wrong to return steve's authorization attributes, since we've not authenticated at all (and certainly not as steve) - although since this only an Access-Challenge, hopefully the NAS will ignore them. The EAP exchange does complete successfully.
See the comments in the raddb/sites-available/default. Look for "Access-Challenge". The documentation describes the problem, and shows how to fix it.
My other concern is that it does an unnecessary database lookup for "steve" - actually the live config which started this investigation is an LDAP one, which is how I noticed this.
Now, the default site has in its authorize section:
eap { ok = return }
But at this step we're getting "updated". So it looks like it would be reasonable to change this to:
eap { ok = return updated = return }
It depends. Given the stable nature of 3.0, I'm inclined to leave it.
... and this does seem to work. But I wonder why it's done this way in the default config. Is this a mistake, or this there some subtle point I am missing?
Other peoples systems may behave differently from yours. So I'm inclined to leave the current configuration. We will be fixing all of this in v4. Not by accident, but by design.
Under what circumstances does rlm_eap return "updated" instead of "ok"? I want to be sure that there's no security impact by dropping out of the authorize section at this point, for example if someone uses a non-tunneled version of EAP like EAP-TLS or EAP-PWD.
That's what tests are for. The server comes with configuration files for eapol_test (see src/tests/eap*.conf). If it works for you, use it. Alan DeKok.
On 20/10/2016 15:12, Alan DeKok wrote:
Those two identities don't have to be related in any way whatsoever.
Exactly. So I'm intentionally demonstrating the case where they are different; a potential attacker can choose whichever outer identity they like.
Note how I've chosen "steve" as the anonymous identity. "steve" isn't really an "anonymous" identity. For a discussion of anonymous identities, see my RFC:
https://tools.ietf.org/html/rfc7542#section-2.4 Yes I know. An attacker can choose whichever identity they like. Even the stock Android client lets you enter whichever "anonymous" identity you like; no special tools required.
What happens is that is in the second Access-Challenge response, steve's attributes are returned: Which is what you told it to do. It's what the default config tells it to do. I was surprised. It does not seem like desirable default behaviour: specifically, an unauthorized user being able to generate RADIUS reply attributes which belong to some other user.
It's clearly wrong to return steve's authorization attributes, since we've not authenticated at all (and certainly not as steve) - although since this only an Access-Challenge, hopefully the NAS will ignore them. The EAP exchange does complete successfully. See the comments in the raddb/sites-available/default. Look for "Access-Challenge". The documentation describes the problem, and shows how to fix it.
I had read that already. It says that "older configurations" had this problem, and it is commented out. The implication is that the current configuration *doesn't* have this problem. But I see this behaviour with the current sample configuration which is supplied with freeradius 3.0.12.
My other concern is that it does an unnecessary database lookup for "steve" - actually the live config which started this investigation is an LDAP one, which is how I noticed this.
Now, the default site has in its authorize section:
eap { ok = return }
But at this step we're getting "updated". So it looks like it would be reasonable to change this to:
eap { ok = return updated = return } It depends. Given the stable nature of 3.0, I'm inclined to leave it.
Do you mean leave the sample config as-is, or leave the behaviour of the rlm_eap module as-is? Would you say rlm_eap is behaving as intended by returning 'updated' for this particular step of the exchange?
... and this does seem to work. But I wonder why it's done this way in the default config. Is this a mistake, or this there some subtle point I am missing? Other peoples systems may behave differently from yours. So I'm inclined to leave the current configuration. Well, obviously people's configs are different, but if they copy-pasted this part of eap config then they should behave the same.
This seems to be an under-documented area of freeradius. For example: http://wiki.freeradius.org/modules/Rlm_eap /usr/share/doc/freeradius-3.0.11/modules/rlm_eap I couldn't find any reference to return codes, or "ok" or "updated", in either of these.
We will be fixing all of this in v4. Not by accident, but by design.
Under what circumstances does rlm_eap return "updated" instead of "ok"? I want to be sure that there's no security impact by dropping out of the authorize section at this point, for example if someone uses a non-tunneled version of EAP like EAP-TLS or EAP-PWD. That's what tests are for. The server comes with configuration files for eapol_test (see src/tests/eap*.conf).
I can't see any tests which exercise the individual modules and check their return codes under different scenarios. I can see high-level tests exercise the general behaviour ("does radclient return 0?"). I'm not sure it actually checks the returned RADIUS attributes at each stage of the exchange, nor scenarios which require a reject rather than an accept. But that's just from a cursory scan of the scripts. Anyway, I'm not necessarily saying that the module behaviour is wrong. What I am saying is: * the module returns different return values at different parts of the EAP exchange, and this appears to be undocumented * the sample config doesn't take this into consideration, and ends up sometimes dropping through to later parts of the outer config part-way through the EAP exchange (but not every time) In fact, it explicitly says: # The EAP module returns "ok" if it is not yet ready to # authenticate the user. The configuration below checks for # that code, and stops processing the "authorize" section if # so. # # Any LDAP and/or SQL servers will not be queried for the # initial set of packets that go back and forth to set up # TTLS or PEAP. which means I think I was right to expect that eap { ok = return } would catch everything except the end of the exchange. * therefore, the sample config could be improved to match the description (or the rlm_eap behaviour changed to return "ok", but that's a matter for you) IMO freeradius's configurable failover mechanism is subtle at the best of times, so robust sample configs are especially helpful. Regards, Brian.
Hi,
Exactly. So I'm intentionally demonstrating the case where they are different; a potential attacker can choose whichever outer identity they like.
DONT trust the outerId. never base policy decisions on the outerID (in our case the policy is in the users file...and your server is using the 'files' module in the outer phase.
Yes I know. An attacker can choose whichever identity they like. Even the stock Android client lets you enter whichever "anonymous" identity you like; no special tools required.
this is all rather known about and old. the outerID is fake. never trust it. (see above)
It's what the default config tells it to do. I was surprised. It does not seem like desirable default behaviour: specifically, an unauthorized user being able to generate RADIUS reply attributes which belong to some other user.
this is down to a sites policy. no server I know of uses the default users file as shipped.....
But I see this behaviour with the current sample configuration which is supplied with freeradius 3.0.12.
and 2.2 and 1.1.3 and 0.9 alan
On Oct 20, 2016, at 10:53 AM, Brian Candler <b.candler@pobox.com> wrote:
Exactly. So I'm intentionally demonstrating the case where they are different; a potential attacker can choose whichever outer identity they like.
Yes... that's well known. What can the "attacker" do, in this case? Cause the server to send the "wrong" attributes in an Access-Challenge? And what, exactly, happens as a result of this "attack"? Nothing. Absolutely nothing.
It's what the default config tells it to do. I was surprised. It does not seem like desirable default behaviour: specifically, an unauthorized user being able to generate RADIUS reply attributes which belong to some other user.
Not "reply", Access-Challenge. And definitely not Access-Accept.
I had read that already. It says that "older configurations" had this problem, and it is commented out. The implication is that the current configuration *doesn't* have this problem.
But I see this behaviour with the current sample configuration which is supplied with freeradius 3.0.12.
Then (drum roll) *edit the configuration*. That's what the comments mean.
Do you mean leave the sample config as-is, or leave the behaviour of the rlm_eap module as-is?
I mean I'm not going to change 3.0 unless someone convinces me that a security problem exists.
Would you say rlm_eap is behaving as intended by returning 'updated' for this particular step of the exchange?
It works as written. I'd have to go over the whole module again to see what / why it does things. Since I'm busy with 4.0 re-design... addressing issues in 3.0 is less of a priority.
... and this does seem to work. But I wonder why it's done this way in the default config. Is this a mistake, or this there some subtle point I am missing? Other peoples systems may behave differently from yours. So I'm inclined to leave the current configuration. Well, obviously people's configs are different, but if they copy-pasted this part of eap config then they should behave the same.
Don't be obtuse. The default configuration is intended to work for the largest amount of people. Changing it *may* mean that it stops working for some people. People who likely won't test it before the change, and won't comment on the change, and likely won't know what happened when it suddenly stops working. *That* is what I meant by "I'm inclined to leave the current configuration".
This seems to be an under-documented area of freeradius. For example: http://wiki.freeradius.org/modules/Rlm_eap /usr/share/doc/freeradius-3.0.11/modules/rlm_eap
If only the Wiki was editable. And if only people could read the source, and update the Wiki.
I couldn't find any reference to return codes, or "ok" or "updated", in either of these.
As always, patches are welcome. Complaining is less useful than making a contribution.
I can't see any tests which exercise the individual modules and check their return codes under different scenarios.
Then submit some.
Anyway, I'm not necessarily saying that the module behaviour is wrong. What I am saying is:
* the module returns different return values at different parts of the EAP exchange, and this appears to be undocumented
Because (a) most modules don't have their return codes documented, (b) no one contributed documentation for that, and (c) it doesn't matter to 99.9% of the people using the module, and (d) it's documented in the source code, for people who really truly *need* to know.
* the sample config doesn't take this into consideration, and ends up sometimes dropping through to later parts of the outer config part-way through the EAP exchange (but not every time)
It's not random. Don't make it sound like it is. The code documents what it does, and why. I suggest reading the EAP module source.
In fact, it explicitly says:
# The EAP module returns "ok" if it is not yet ready to # authenticate the user. The configuration below checks for # that code, and stops processing the "authorize" section if # so. # # Any LDAP and/or SQL servers will not be queried for the # initial set of packets that go back and forth to set up # TTLS or PEAP.
which means I think I was right to expect that eap { ok = return } would catch everything except the end of the exchange.
Or, EAP is more complicated than you think. i.e. your change works for your tests, but will cause other EAP methods to break.
* therefore, the sample config could be improved to match the description (or the rlm_eap behaviour changed to return "ok", but that's a matter for you)
No. Just.... no. I am *not* going to change the code or the documentation because you didn't understand what it's doing, and therefore decided it was broken. That is *entirely* the wrong approach. If you want to change the code, then read the code, and explain why it's broken. If you want to change the documentation, then explain why the documentation doesn't match the behaviour of the server. If you want to change the default configuration in 3.0, then explain why the change doesn't cause problems for people.
IMO freeradius's configurable failover mechanism is subtle at the best of times, so robust sample configs are especially helpful.
I *really* hate passive-aggressive comments like this. I don't need to be told that sample configurations are helpful. I know, I've spent nearly 20 years writing code, documentation, and example configurations that *work*. Perhaps you could explain why the default configurations *don't work*. Explain why they're *broken*. That would be much more productive than making snide comments. You've convinced me (so far) that you've seen an "attack" where none exists, and that without understanding how EAP works you want to change it, and to make that change without understanding how it affects everyone else. Which is just not a helpful approach. Alan DeKok.
On 20/10/2016 20:01, Alan DeKok wrote:
I couldn't find any reference to return codes, or "ok" or "updated", in either of these. As always, patches are welcome.
Would this be an acceptable? diff --git a/raddb/sites-available/default b/raddb/sites-available/default index 0834075..f047979 100644 --- a/raddb/sites-available/default +++ b/raddb/sites-available/default @@ -347,9 +347,9 @@ authorize { # It also sets the EAP-Type attribute in the request # attribute list to the EAP type from the packet. # - # The EAP module returns "ok" if it is not yet ready to + # The EAP module returns "ok" or "updated" if it is not yet ready to # authenticate the user. The configuration below checks for - # that code, and stops processing the "authorize" section if + # those codes, and stops processing the "authorize" section if # so. # # Any LDAP and/or SQL servers will not be queried for the @@ -358,6 +358,7 @@ authorize { # eap { ok = return + updated = return } # If yes, I'll reformat as a pull request. I believe that is accurate. Looking at rlm_eap.c, the EAP responses are mapped to the following return values in the authorize handler: EAP_NOOP -> RLM_MODULE_NOOP EAP_FAIL -> RLM_MODULE_FAIL EAP_FOUND -> RLM_MODULE_HANDLED (if it gets this far, control:Auth-Type := eap is set) EAP_OK -> RLM_MODULE_OK anything else* -> RLM_MODULE_UPDATED *the remaining enum values are EAP_NOTFOUND, EAP_INVALID or EAP_VALID. Regards, Brian.
On Oct 21, 2016, at 8:12 AM, Brian Candler <b.candler@pobox.com> wrote:
On 20/10/2016 20:01, Alan DeKok wrote:
I couldn't find any reference to return codes, or "ok" or "updated", in either of these. As always, patches are welcome.
Would this be an acceptable?
I'd rather have the comments there, but the "updated = return" commented out by default. I've gone through the code, and I'm just not convinced that this change will have zero effect on all EAP types. We're planning on fixing this for v4. Not by figuring out the EAP module return codes, but fixing it by design. i.e. the ongoing conversations should be run through their own processing sections / virtual server, and NOT through the normal config. The path for "I received a packet, what do I do?" is just too different from the path taken for "I've received the third packet of an ongoing EAP exchange" Alan DeKok.
On 24/10/2016 16:43, Alan DeKok wrote:
I'd rather have the comments there, but the "updated = return" commented out by default.
OK. Moved to https://github.com/FreeRADIUS/freeradius-server/pull/1806
participants (3)
-
A.L.M.Buxey@lboro.ac.uk -
Alan DeKok -
Brian Candler