diff mbox series

Cannot lookup EAP user on reauthentication (PEAP/TTLS)

Message ID 14af5472f127935334978f086128119e6b5adb9a.camel@gmail.com
State RFC
Headers show
Series Cannot lookup EAP user on reauthentication (PEAP/TTLS) | expand

Commit Message

James Prestwood May 26, 2022, 10:21 p.m. UTC
Hi,

For tunneled methods like PEAP/TTLS, on a reauthentication request,
hostapd uses the phase2 identity stored in the sm but hard codes the
phase to 0. This happens in eap_sm_Policy_getDecision().

The reason for this is PEAP/TTLS overwrite sm->identity with the phase2
identity and the phase1 identity is lost forever. The code in
eap_sm_Policy_getDecision() assumes sm->identity is phase one and hard
codes '0' to the phase parameter, causing the lookup to fail.

I'm not sure how you want this fixed, either save the phase1 identity,
or add some flag which tunneled methods can set to signify phase2 has
completed and set the 'phase2' argument to eap_user_get() dependent on
this flag? Maybe the eap_sm already has some value which can hint at
the correct phase value?

I have a patch below which hopefully lines out the issue better. I
don't expect this to get merged, its just (hopefully) showing the
problem better than I can explain it.

1 : 0;
                /*
                 * Allow Identity method to be started once to allow
identity
                 * selection hint to be sent from the authentication
server,
@@ -1755,7 +1762,8 @@ static int eap_sm_Policy_getDecision(struct
eap_sm *sm)
                    sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
                    sm->user->methods[0].method == EAP_TYPE_IDENTITY)
                        id_req = 1;
-               if (eap_user_get(sm, sm->identity, sm->identity_len, 0)
!= 0) {
+
+               if (eap_user_get(sm, sm->identity, sm->identity_len,
phase) != 0) {
                        wpa_printf(MSG_DEBUG, "EAP: getDecision: user
not "
                                   "found from database -> FAILURE");
                        return DECISION_FAILURE;

Comments

Alan DeKok May 27, 2022, 1:54 p.m. UTC | #1
On May 26, 2022, at 6:21 PM, James Prestwood <prestwoj@gmail.com> wrote:
> For tunneled methods like PEAP/TTLS, on a reauthentication request,
> hostapd uses the phase2 identity stored in the sm but hard codes the
> phase to 0. This happens in eap_sm_Policy_getDecision().

  The outer identity should be the same for both the initial authentication, and any resumption.  For details, see:

https://datatracker.ietf.org/doc/html/rfc9190#section-2.1.3

   When NAI reuse can be
   done without privacy implications, it is RECOMMENDED to use the same
   NAI in the resumption as was used in the original full handshake
   [RFC7542]
  
  Changing outer identities for resumption seems wrong.

  Alan DeKok.
James Prestwood May 27, 2022, 4:22 p.m. UTC | #2
Hi Alan,

On Fri, 2022-05-27 at 09:54 -0400, Alan DeKok wrote:
> On May 26, 2022, at 6:21 PM, James Prestwood <prestwoj@gmail.com>
> wrote:
> > For tunneled methods like PEAP/TTLS, on a reauthentication request,
> > hostapd uses the phase2 identity stored in the sm but hard codes the
> > phase to 0. This happens in eap_sm_Policy_getDecision().
> 
>   The outer identity should be the same for both the initial
> authentication, and any resumption.  For details, see:
> 
> https://datatracker.ietf.org/doc/html/rfc9190#section-2.1.3
> 
>    When NAI reuse can be
>    done without privacy implications, it is RECOMMENDED to use the same
>    NAI in the resumption as was used in the original full handshake
>    [RFC7542]
>   
>   Changing outer identities for resumption seems wrong.

I'm not sure I follow, EAP-TLS doesn't suffer this issue since it
doesn't have two phases. TTLS/PEAP use an anonymous/outer identity and
the real identity for phase2 which is encrypted. Using the same
identities for both phases removes any privacy from the real identity.

Several example configurations for wpa_supplicant even uses two
separate identities e.g.

# EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for
the
# unencrypted use. Real identity is sent only within an encrypted TLS
tunnel.
network={
	ssid="example"
	key_mgmt=WPA-EAP
	eap=TTLS
	identity="user@example.com"
	anonymous_identity="anonymous@example.com"
	password="foobar"
	ca_cert="/etc/cert/ca.pem"
	priority=2
}

The hostapd tests avoid this issue it seems by allowing any outer
identity to be accepted:

*		TTLS,TLS,PEAP,FAST,TEAP,SIM,AKA',AKA

Maybe this is standard practice for all authentication servers? But
from what I can gather there is no requirement they two identities must
be the same.

Thanks,
James

> 
>   Alan DeKok.
>
Alan DeKok May 27, 2022, 4:35 p.m. UTC | #3
On May 27, 2022, at 12:22 PM, James Prestwood <prestwoj@gmail.com> wrote:
> On Fri, 2022-05-27 at 09:54 -0400, Alan DeKok wrote:
>> 
>> Changing outer identities for resumption seems wrong.
> 
> I'm not sure I follow, EAP-TLS doesn't suffer this issue since it
> doesn't have two phases.

  I referenced the EAP-TLS document because the updated PEAP / TTLS / PEAP RFC will have similar requirements.  Unfortunately, it's not done yet, so there's only a draft document available.

> TTLS/PEAP use an anonymous/outer identity and
> the real identity for phase2 which is encrypted. Using the same
> identities for both phases removes any privacy from the real identity.

  I didn't say anything about using the same identity for both phases.

  I said that the same identity should be used for the initial authentication, and for resumption.

  The draft document I mentioned explicitly covers the issue of using the same identity for both inner and outer methods.  In short, it's not recommended.

https://datatracker.ietf.org/doc/html/draft-ietf-emu-tls-eap-types-06#section-3.1

> Maybe this is standard practice for all authentication servers? But
> from what I can gather there is no requirement they two identities must
> be the same.

  No one said that they should be the same.

  Your original message seemed to be saying that wpa_supplicant would use the phase2 identity on resumption.  This is wrong.

  The "outer" identity should be the same for both the initial authentication, and for resumption, unless the server has sent a new PSK identity for resumptions.  See Section 2.1.3 of RFC 9190.

  Again, there is no requirement that the inner identity be the same as the outer one, and no one is proposing that.

  Alan DeKok.
James Prestwood May 27, 2022, 5:06 p.m. UTC | #4
On Fri, 2022-05-27 at 12:35 -0400, Alan DeKok wrote:
> 
> On May 27, 2022, at 12:22 PM, James Prestwood <prestwoj@gmail.com>
> wrote:
> > On Fri, 2022-05-27 at 09:54 -0400, Alan DeKok wrote:
> > > 
> > > Changing outer identities for resumption seems wrong.
> > 
> > I'm not sure I follow, EAP-TLS doesn't suffer this issue since it
> > doesn't have two phases.
> 
>   I referenced the EAP-TLS document because the updated PEAP / TTLS /
> PEAP RFC will have similar requirements.  Unfortunately, it's not
> done yet, so there's only a draft document available.
> 
> > TTLS/PEAP use an anonymous/outer identity and
> > the real identity for phase2 which is encrypted. Using the same
> > identities for both phases removes any privacy from the real
> > identity.
> 
>   I didn't say anything about using the same identity for both
> phases.

>   I said that the same identity should be used for the initial
> authentication, and for resumption.

Yes I misinterpreted what you said. But from what I can tell the
supplicant isn't even involved at the point when hostapd fails to look
up the user (the supplicant hasn't even received an identity request).

In my test I issue the EAPOL_REAUTH command to hostapd which triggers
the lookup based on the eap_sm's saved identity. This identity is
phase2 since TTLS/PEAP overwrite sm->identity during the initial
authentication.

I could care less what identity hostapd wants to use to lookup the
session, but since sm->identity is used for both phases there needs to
be some logic to determine what phase the identity goes with. Hard
coding to phase1 in all cases is wrong if sm->identity is for phase2.


Thanks,
James
Alan DeKok May 27, 2022, 6:09 p.m. UTC | #5
On May 27, 2022, at 1:06 PM, James Prestwood <prestwoj@gmail.com> wrote:
> Yes I misinterpreted what you said. But from what I can tell the
> supplicant isn't even involved at the point when hostapd fails to look
> up the user (the supplicant hasn't even received an identity request).

  I'm less familiar with the internals of hostap, so I'll reply from the point of view of a RADIUS server author, which may not be exactly the same as what hostap does.

> In my test I issue the EAPOL_REAUTH command to hostapd which triggers
> the lookup based on the eap_sm's saved identity. This identity is
> phase2 since TTLS/PEAP overwrite sm->identity during the initial
> authentication.

  See RFC 9190 Section 5.7 for discussion of what needs to be cached for resumption.  While the RFC is for EAP-TLS, the same requirements apply to PEAP / TTLS / TEAP.

> I could care less what identity hostapd wants to use to lookup the
> session, but since sm->identity is used for both phases there needs to
> be some logic to determine what phase the identity goes with. Hard
> coding to phase1 in all cases is wrong if sm->identity is for phase2.

  Resumption SHOULD use the same outer identity as the original authentication.

  The server SHOULD cache a set of information about the original session, including things like inner identity, MAC addresses, etc.   This cache is typically based off of the TLS session key, because nothing else is really relevant.

  IIRC hostap keeps information about ports it controls.  So that it can detect re-authentication, etc. for a port.  This includes the "real" identity of the user.

  If the supplicant hasn't received an identity request, them I don't see why the identity matters.  What, precisely is the issue?  Your original message discussed why internal hostap data structures didn't have the values that you expected.

  OK... what's the problem?  What user-visible issues are there?

  Alan DeKok.
James Prestwood May 27, 2022, 8:09 p.m. UTC | #6
On Fri, 2022-05-27 at 14:09 -0400, Alan DeKok wrote:
> On May 27, 2022, at 1:06 PM, James Prestwood <prestwoj@gmail.com>
> wrote:
> > Yes I misinterpreted what you said. But from what I can tell the
> > supplicant isn't even involved at the point when hostapd fails to
> > look
> > up the user (the supplicant hasn't even received an identity
> > request).
> 
>   I'm less familiar with the internals of hostap, so I'll reply from
> the point of view of a RADIUS server author, which may not be exactly
> the same as what hostap does.

This problem is really about the internals of hostapd itself, not
RADIUS or the spec.

> 
> > In my test I issue the EAPOL_REAUTH command to hostapd which
> > triggers
> > the lookup based on the eap_sm's saved identity. This identity is
> > phase2 since TTLS/PEAP overwrite sm->identity during the initial
> > authentication.
> 
>   See RFC 9190 Section 5.7 for discussion of what needs to be cached
> for resumption.  While the RFC is for EAP-TLS, the same requirements
> apply to PEAP / TTLS / TEAP.
> 
> > I could care less what identity hostapd wants to use to lookup the
> > session, but since sm->identity is used for both phases there needs
> > to
> > be some logic to determine what phase the identity goes with. Hard
> > coding to phase1 in all cases is wrong if sm->identity is for
> > phase2.
> 
>   Resumption SHOULD use the same outer identity as the original
> authentication.

I think this is what hostapd is _trying_ to do as the lookup call will
always look for a phase1 identity, i.e. outer identity (this is the
integer argument 'phase2=0'). But for the identity value it uses
whatever sm->identity is, which was set by the EAP method. In the case
of tunneled methods, this is the inner identity as thats what is stored
last.

So we try looking up a phase2 identity but require that any matches be
a phase1 identity. And of course this fails unless:
a) The identities are the same or
b) The configuration uses a wildcard for phase1.

> 
>   The server SHOULD cache a set of information about the original
> session, including things like inner identity, MAC addresses, etc.  
> This cache is typically based off of the TLS session key, because
> nothing else is really relevant.
> 
>   IIRC hostap keeps information about ports it controls.  So that it
> can detect re-authentication, etc. for a port.  This includes the
> "real" identity of the user.

yep, and this is the identity being used for the lookup. But with the
assumption that its a phase1 identity, which it is not.

> 
>   If the supplicant hasn't received an identity request, them I don't
> see why the identity matters.  What, precisely is the issue?  Your
> original message discussed why internal hostap data structures didn't
> have the values that you expected.
> 
>   OK... what's the problem?  What user-visible issues are there?

If hostapd tries to reauth, users will get EAP-FAILURE/disconnected and
have to reauthenticate. Unless the server is configured for wildcard
phase1 identities of course.


> 
>   Alan DeKok.
>
Alan DeKok May 27, 2022, 8:35 p.m. UTC | #7
On May 27, 2022, at 4:09 PM, James Prestwood <prestwoj@gmail.com> wrote:
>> Resumption SHOULD use the same outer identity as the original
>> authentication.
> 
> I think this is what hostapd is _trying_ to do as the lookup call will
> always look for a phase1 identity, i.e. outer identity (this is the
> integer argument 'phase2=0'). But for the identity value it uses
> whatever sm->identity is, which was set by the EAP method. In the case
> of tunneled methods, this is the inner identity as thats what is stored
> last.

  I don't see why it's looking up any identity.  The identity in the EAP packet doesn't matter.  And your first message said that hostap was doing lookups before it had any identity from the supplicant.

> So we try looking up a phase2 identity but require that any matches be
> a phase1 identity. And of course this fails unless:
> a) The identities are the same or
> b) The configuration uses a wildcard for phase1.

  That seems wrong on a number of levels.  The identities shouldn't be the same, of course.  But I don't see why hostap needs the identity before it gets any packets from the supplicant.

> yep, and this is the identity being used for the lookup. But with the
> assumption that its a phase1 identity, which it is not.

  My point was that any cached data should be looked up based on the TLS session key.

>> If the supplicant hasn't received an identity request, them I don't
>> see why the identity matters.  What, precisely is the issue?  Your
>> original message discussed why internal hostap data structures didn't
>> have the values that you expected.
>> 
>> OK... what's the problem?  What user-visible issues are there?
> 
> If hostapd tries to reauth, users will get EAP-FAILURE/disconnected and
> have to reauthenticate. Unless the server is configured for wildcard
> phase1 identities of course.

  Again, the phase1 identities don't matter for TTLS / PEAP  etc.  The server should just allow for re-authentication.  Then, the server should use the TLS session data to look up in a cache to see what was in the users original session.

  I think you're looking at it from what hostap does right now, and trying to find a way to poke it to "make it work".  I think that's the wrong approach.

  hostap may need to key sessions off of identities for non-TLS EAP types.  But for TLS, there's just no point.  As you've seen here, it causes issues.  Instead, it should track TLS sessions, and then compare the cached session data to what it gets from the current EAP re-authentication attempt.

  That way a user can authenticate on ethernet port 1, and then resume the session on ethernet port 2.  The user can also use an anonymous outer identity to authenticate TTLS (e.g. "@example.com") and then resume the session using a PSK identity "PSK-4924727497@example.com".

  None of the port / identity changes matter.  What matters is that the user has the correct cryptographic keys / data.  That data ties the resumed session to the original one.

  That data can then be used to determine the original inner && outer identity.  Having a cache keyed off of identity just won't work with TLS.

  Alan DeKok.
James Prestwood May 28, 2022, 1:09 a.m. UTC | #8
On Fri, 2022-05-27 at 16:35 -0400, Alan DeKok wrote:
> On May 27, 2022, at 4:09 PM, James Prestwood <prestwoj@gmail.com>
> wrote:
> > > Resumption SHOULD use the same outer identity as the original
> > > authentication.
> > 
> > I think this is what hostapd is _trying_ to do as the lookup call
> > will
> > always look for a phase1 identity, i.e. outer identity (this is the
> > integer argument 'phase2=0'). But for the identity value it uses
> > whatever sm->identity is, which was set by the EAP method. In the
> > case
> > of tunneled methods, this is the inner identity as thats what is
> > stored
> > last.
> 
>   I don't see why it's looking up any identity.  The identity in the
> EAP packet doesn't matter.  And your first message said that hostap
> was doing lookups before it had any identity from the supplicant.

I believe its looking it up directly from the reauth command:

 - STA authenticates, everything is well.
 - At some point hostapd is told to reauthenticate: EAPOL_REAUTH <sta-
addr>
 - Hostapd gets this message and the only context it has is the station
address. So it first looks up this station object by address.
 - The found station object then points to an eapol_sm object which has
the EAP identity stored in it. This is the identity used to find the
EAP user/session which probably contains the TLS session key you're
talking about.

And you're right, I'm not sure why it needs to look up the identity at
this point. It could just send an identity request to the station, wait
for whatever identity is sent back, and use that for the lookup. This
would put the burden on the station to send the correct identity. But
in any case, this is what it does.

I'm betting there is more to it, because this seems like a lot of
additional work rather than just sending an identity request.

> 
> > So we try looking up a phase2 identity but require that any matches
> > be
> > a phase1 identity. And of course this fails unless:
> > a) The identities are the same or
> > b) The configuration uses a wildcard for phase1.
> 
>   That seems wrong on a number of levels.  The identities shouldn't
> be the same, of course.  But I don't see why hostap needs the
> identity before it gets any packets from the supplicant.

No idea either.

> 
> > yep, and this is the identity being used for the lookup. But with
> > the
> > assumption that its a phase1 identity, which it is not.
> 
>   My point was that any cached data should be looked up based on the
> TLS session key.
> 
> > > If the supplicant hasn't received an identity request, them I
> > > don't
> > > see why the identity matters.  What, precisely is the issue? 
> > > Your
> > > original message discussed why internal hostap data structures
> > > didn't
> > > have the values that you expected.
> > > 
> > > OK... what's the problem?  What user-visible issues are there?
> > 
> > If hostapd tries to reauth, users will get EAP-FAILURE/disconnected
> > and
> > have to reauthenticate. Unless the server is configured for
> > wildcard
> > phase1 identities of course.
> 
>   Again, the phase1 identities don't matter for TTLS / PEAP  etc. 
> The server should just allow for re-authentication.  Then, the server
> should use the TLS session data to look up in a cache to see what was
> in the users original session.
> 
>   I think you're looking at it from what hostap does right now, and
> trying to find a way to poke it to "make it work".  I think that's
> the wrong approach.

I don't agree. But I'll keep an open mind :) Maybe there is something
I'm not seeing as to why the lookup is done so early in the first
place, and why its hard coded to use the phase1 identity.


> 
>   hostap may need to key sessions off of identities for non-TLS EAP
> types.  But for TLS, there's just no point.  As you've seen here, it
> causes issues.  Instead, it should track TLS sessions, and then
> compare the cached session data to what it gets from the current EAP
> re-authentication attempt.
> 
>   That way a user can authenticate on ethernet port 1, and then
> resume the session on ethernet port 2.  The user can also use an
> anonymous outer identity to authenticate TTLS (e.g. "@example.com")
> and then resume the session using a PSK identity
> "PSK-4924727497@example.com".
> 
>   None of the port / identity changes matter.  What matters is that
> the user has the correct cryptographic keys / data.  That data ties
> the resumed session to the original one.
> 
>   That data can then be used to determine the original inner && outer
> identity.  Having a cache keyed off of identity just won't work with
> TLS.
> 
>   Alan DeKok.
>
Alan DeKok May 30, 2022, 1:06 p.m. UTC | #9
On May 27, 2022, at 9:09 PM, James Prestwood <prestwoj@gmail.com> wrote:
> I believe its looking it up directly from the reauth command:

  OK, so that's the piece which was missing.

  The reauth command could just re-authenticate a particular port.  In which case (IIRC) t only needs to know the MAC which was authenticated.

> And you're right, I'm not sure why it needs to look up the identity at
> this point. It could just send an identity request to the station, wait
> for whatever identity is sent back, and use that for the lookup. This
> would put the burden on the station to send the correct identity. But
> in any case, this is what it does.

  That seems correct.  If the supplicant sends the same identity, the previously cached one will be found.  If the supplicant sends a different identity, then they have to do a full re-authentication.

  Alan DeKok.
James Prestwood June 10, 2022, 9:19 p.m. UTC | #10
Just a ping, seeing if anyone has looked at this. There was a
discussion with Alan about it, and it was agreed (from my perspective)
the lookup directly from EAPOL_REAUTH and hard coding phase1 seems
incorrect, but still no resolution on how it should be fixed.

On Thu, 2022-05-26 at 15:21 -0700, James Prestwood wrote:
> Hi,
> 
> For tunneled methods like PEAP/TTLS, on a reauthentication request,
> hostapd uses the phase2 identity stored in the sm but hard codes the
> phase to 0. This happens in eap_sm_Policy_getDecision().
> 
> The reason for this is PEAP/TTLS overwrite sm->identity with the
> phase2
> identity and the phase1 identity is lost forever. The code in
> eap_sm_Policy_getDecision() assumes sm->identity is phase one and
> hard
> codes '0' to the phase parameter, causing the lookup to fail.
> 
> I'm not sure how you want this fixed, either save the phase1
> identity,
> or add some flag which tunneled methods can set to signify phase2 has
> completed and set the 'phase2' argument to eap_user_get() dependent
> on
> this flag? Maybe the eap_sm already has some value which can hint at
> the correct phase value?
> 
> I have a patch below which hopefully lines out the issue better. I
> don't expect this to get merged, its just (hopefully) showing the
> problem better than I can explain it.
> 
> diff --git a/src/eap_server/eap_server.c
> b/src/eap_server/eap_server.c
> index 0b7a5b98c..7c2d33b51 100644
> --- a/src/eap_server/eap_server.c
> +++ b/src/eap_server/eap_server.c
> @@ -1744,6 +1744,13 @@ static int eap_sm_Policy_getDecision(struct
> eap_sm *sm)
>  
>         if ((sm->user == NULL || sm->update_user) && sm->identity &&
>             !sm->start_reauth) {
> +               /*
> +                * sm->identity may contain a phase2 identity since
> PEAP/TTLS
> +                * overwrite the phase1 identity. In this case the
> lookup should
> +                * actually be for phase2 (1) rather than phase1 (0).
> +                */
> +               int phase = ((sm->currentMethod == EAP_TYPE_PEAP ||
> +                               sm->currentMethod == EAP_TYPE_TTLS))
> ?
> 1 : 0;
>                 /*
>                  * Allow Identity method to be started once to allow
> identity
>                  * selection hint to be sent from the authentication
> server,
> @@ -1755,7 +1762,8 @@ static int eap_sm_Policy_getDecision(struct
> eap_sm *sm)
>                     sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
>                     sm->user->methods[0].method == EAP_TYPE_IDENTITY)
>                         id_req = 1;
> -               if (eap_user_get(sm, sm->identity, sm->identity_len,
> 0)
> != 0) {
> +
> +               if (eap_user_get(sm, sm->identity, sm->identity_len,
> phase) != 0) {
>                         wpa_printf(MSG_DEBUG, "EAP: getDecision: user
> not "
>                                    "found from database -> FAILURE");
>                         return DECISION_FAILURE;
> 
>
diff mbox series

Patch

diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index 0b7a5b98c..7c2d33b51 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -1744,6 +1744,13 @@  static int eap_sm_Policy_getDecision(struct
eap_sm *sm)
 
        if ((sm->user == NULL || sm->update_user) && sm->identity &&
            !sm->start_reauth) {
+               /*
+                * sm->identity may contain a phase2 identity since
PEAP/TTLS
+                * overwrite the phase1 identity. In this case the
lookup should
+                * actually be for phase2 (1) rather than phase1 (0).
+                */
+               int phase = ((sm->currentMethod == EAP_TYPE_PEAP ||
+                               sm->currentMethod == EAP_TYPE_TTLS)) ?