diff mbox

Remove data->phase2_success check for accessing EAP-TTLS key material.

Message ID 1473506939-6899-1-git-send-email-stapelberg@google.com
State Changes Requested
Headers show

Commit Message

Michael Stapelberg Sept. 10, 2016, 11:28 a.m. UTC
This fixes the 4-way handshake when joining a WPA-EAP wireless network which is
configured to return EAP-Success as soon as the inner EAP method is started
within an EAP-TTLS tunnel.

Such a wireless configuration is common at Chaos Computer Club events (e.g. the
yearly Chaos Communication Congress, or various smaller events which re-use the
same wireless network configuration), where 802.1x is merely used to select
VLANs and ensure individual users have different encryption keys, but NOT for
authentication.

data->phase2_success was checked in eap_ttls_isKeyAvailable and eap_ttls_getKey
since 2008 at least (commit 6fc6879bd55a394f807cbbe927df736c190cb8ab is the
earliest commit that is included in the git repository).

Commit 7f7bfba919a76bb03a7f762eab0ac00d4f5c3184 (2015-02-01) introduced the
allow_canned_success=1 configuration option, so I am assuming not removing
data->phase2_success was an oversight of that commit.

Debug log excerpt from before this commit:

[…]
wlp4s0: WPA: RX message 1 of 4-Way Handshake from 66:70:02:77:e2:70 (ver=2)
RSN: msg 1/4 key data - hexdump(len=22): […]
WPA: PMKID in EAPOL-Key - hexdump(len=22): […]
RSN: PMKID from Authenticator - hexdump(len=16): […]
wlp4s0: RSN: no matching PMKID found
EAPOL: EAP key not available
EAPOL: EAP key not available
wlp4s0: WPA: Failed to get master session key from EAPOL state machines - key handshake aborted
wlp4s0: RSN: no PMKSA entry found - trigger full EAP authentication
wlp4s0: RSN: Do not reply to msg 1/4 - requesting full EAP authentication
l2_packet_receive: src=66:70:02:77:e2:70 len=9
wlp4s0: RX EAPOL from 66:70:02:77:e2:70
RX EAPOL - hexdump(len=9): 02 00 00 05 01 7a 00 05 01
EAPOL: Received EAP-Packet frame
EAPOL: SUPP_PAE entering state RESTART
[…]

Debug log excerpt from after this commit:

[…]
wlp4s0: WPA: RX message 1 of 4-Way Handshake from 66:70:02:77:e2:70 (ver=2)
RSN: msg 1/4 key data - hexdump(len=22): […]
WPA: PMKID in EAPOL-Key - hexdump(len=22): […]
RSN: PMKID from Authenticator - hexdump(len=16): […]
wlp4s0: RSN: no matching PMKID found
EAPOL: Successfully fetched key (len=32)
EAPOL: Successfully fetched key (len=64)
WPA: PMK from EAPOL state machines - hexdump(len=32): […]
[…]

Signed-off-by: Michael Stapelberg <stapelberg@google.com>
---
 src/eap_peer/eap_ttls.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

Comments

Jouni Malinen Oct. 2, 2016, 6:47 p.m. UTC | #1
On Sat, Sep 10, 2016 at 07:28:59AM -0400, Michael Stapelberg wrote:
> This fixes the 4-way handshake when joining a WPA-EAP wireless network which is
> configured to return EAP-Success as soon as the inner EAP method is started
> within an EAP-TTLS tunnel.

How is the supplicant side network profile configured for this and how
does this terminate EAP-TTLS sequence? It should be noted that EAP-TTLS
has expectations on how the exchange is terminated and if wpa_supplicant
is configured with credentials that expect a specific response from the
server in Phase 2, the key should not really be used before that
response has been received.

EAP-TTLS does allow operations with Phase 2 skipped completely and
wpa_supplicant supports some of the possible cases for this. For this
specific use case, it might make sense to add new Phase 2 configuration
option that makes wpa_supplicant expect it to be skipped completely.
That would allow a clean setting of data->phase2_success = 1 in the
exchange to avoid having to make keys available in unexpected cases in a
manner than proposed in the patch here.

> Such a wireless configuration is common at Chaos Computer Club events (e.g. the
> yearly Chaos Communication Congress, or various smaller events which re-use the
> same wireless network configuration), where 802.1x is merely used to select
> VLANs and ensure individual users have different encryption keys, but NOT for
> authentication.

Would you be able to share a more detailed wpa_supplicant debug log with
-ddK on the command line (obviously without any private keys being used
here since -K will reveal them) showing how the protocol works? Or
alternatively describe what the supplicant side is expected to do after
the termination of Phase 1, i.e., when receiving TLS Finished message
from the server.

> data->phase2_success was checked in eap_ttls_isKeyAvailable and eap_ttls_getKey
> since 2008 at least (commit 6fc6879bd55a394f807cbbe927df736c190cb8ab is the
> earliest commit that is included in the git repository).

This was added with the commit message explicitly noting the addition in
2004:
http://w1.fi/cgit/hostap-history/commit/?id=0620112483fd93e8bc8a6f6248ff039f35cf501f

> Commit 7f7bfba919a76bb03a7f762eab0ac00d4f5c3184 (2015-02-01) introduced the
> allow_canned_success=1 configuration option, so I am assuming not removing
> data->phase2_success was an oversight of that commit.

Canned success is a case where the EAP authentication method is skipped
completely. This is quite different from the EAP-TTLS case and
allow_canned_success=1 is only for wired IEEE 802.1X cases without data
encryption. This does not give any justification for removal of the
data->phase2_success check from getKey().


> Debug log excerpt from before this commit:
> 
> […]
> wlp4s0: WPA: RX message 1 of 4-Way Handshake from 66:70:02:77:e2:70 (ver=2)

I'd need to see what happened before the start of this 4-way handshake
to be able to understand why the TTLS exchange did not complete
properly.

> diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
> @@ -1727,7 +1727,7 @@ static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
>  static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv)
>  {
>  	struct eap_ttls_data *data = priv;
> -	return data->key_data != NULL && data->phase2_success;
> +	return data->key_data != NULL;

I think it would be better to set data->phase2_success = 1 at a suitable
location in the exchange for this special case rather than getting rid
of this check.

As a quick workaround, you might be able to configure EAP-TTLS with a
Phase 2 mechanism that does not expect a response from the server, e.g.,
EAP-TTLS/PAP or EAP-TTLS/CHAP. Those would allow the server to complete
exchange with EAP-Success. Assuming this works here, the cleaner
approach would be to add a network profile parameter for specifying that
no password is to be used and Phase 2 is to be fully skipped.
Michael Stapelberg Dec. 13, 2016, 7:56 a.m. UTC | #2
[re-sending my reply as plain text only for the mailing list]

On Tue, Dec 13, 2016 at 8:53 AM, Michael Stapelberg
<stapelberg@google.com> wrote:
>
> Thanks for your patience with my slow reply, and thanks for looking into this. Replies inline:
>
> On Sun, Oct 2, 2016 at 8:47 PM, Jouni Malinen <j@w1.fi> wrote:
>>
>> On Sat, Sep 10, 2016 at 07:28:59AM -0400, Michael Stapelberg wrote:
>> > This fixes the 4-way handshake when joining a WPA-EAP wireless network which is
>> > configured to return EAP-Success as soon as the inner EAP method is started
>> > within an EAP-TTLS tunnel.
>>
>> How is the supplicant side network profile configured for this and how
>> does this terminate EAP-TTLS sequence? It should be noted that EAP-TTLS
>
>
> Here are the steps to reproduce the FreeRADIUS part of the network:
>
> (debian:stable is Debian 8.6, which contains FreeRADIUS 2.2.5.)
>
> $ docker run -t -i --net=host debian:stable /bin/bash
> root# apt update && apt install freeradius
> root# cat >/etc/freeradius/clients.conf <<'EOT'
> client openwrt {
> ipaddr = 10.0.0.254
> secret = secretkey
> nastype = other
> }
> EOT
> root# cat >/etc/freeradius/users <<'EOT'
> DEFAULT Auth-Type := Accept
> Tunnel-Type    = VLAN,
> Tunnel-Medium-Type  = IEEE-802,
> Tunnel-Private-Group-Id = 100
> EOT
> root# sed -i '0,/^\s*files/s//#files/' /etc/freeradius/sites-enabled/default
> root# freeradius -X -f
> […]
>
>>
>> has expectations on how the exchange is terminated and if wpa_supplicant
>> is configured with credentials that expect a specific response from the
>> server in Phase 2, the key should not really be used before that
>> response has been received.
>
>
> Interesting. Can you provide me a pointer to more details, or elaborate on why the key should not be used just yet please?
>
>>
>>
>> EAP-TTLS does allow operations with Phase 2 skipped completely and
>> wpa_supplicant supports some of the possible cases for this. For this
>> specific use case, it might make sense to add new Phase 2 configuration
>> option that makes wpa_supplicant expect it to be skipped completely.
>
>
> Could you outline why a new configuration option is necessary? I.e., couldn’t we just unconditionally allow phase2 to be skipped if the server so wishes? Is there a security risk here which I don’t see yet?
>
>>
>> That would allow a clean setting of data->phase2_success = 1 in the
>> exchange to avoid having to make keys available in unexpected cases in a
>> manner than proposed in the patch here.
>>
>> > Such a wireless configuration is common at Chaos Computer Club events (e.g. the
>> > yearly Chaos Communication Congress, or various smaller events which re-use the
>> > same wireless network configuration), where 802.1x is merely used to select
>> > VLANs and ensure individual users have different encryption keys, but NOT for
>> > authentication.
>>
>> Would you be able to share a more detailed wpa_supplicant debug log with
>> -ddK on the command line (obviously without any private keys being used
>> here since -K will reveal them) showing how the protocol works? Or
>> alternatively describe what the supplicant side is expected to do after
>> the termination of Phase 1, i.e., when receiving TLS Finished message
>> from the server.
>
>
> Sure!
>
> Attached you can find the following wpa_supplicant logs, each created by running
> wpa_supplicant -c /tmp/wpa.conf -ddK -i wlp4s0 -Dnl80211, starting with the following configuration file:
>
> network={
> ssid="8021x"
> key_mgmt=WPA-EAP
> identity="bob"
> password="hello"
> eap=TTLS
> phase2="auth=PAP"
> }
>
> /tmp/2.6-vanilla-authpap.log.gz:
>   wpa-supplicant 2.6 (vanilla), with phase2="auth=PAP" (working)
>
> /tmp/2.6-vanilla-autheapmd5.log.gz:
>   wpa-supplicant 2.6 (vanilla), with phase2="autheap=MD5" (not working)
>
> /tmp/2.6-vanilla-autheapmd5cannedsuccess.log.gz
>   wpa-supplicant 2.6 (vanilla), with phase2="autheap=MD5" and
>     phase1="allow_canned_success=1" (not working)
>
> /tmp/2.6-patched-autheapmd5cannedsuccess.log.gz:
>   wpa-supplicant 2.6 (with my patch from the previous mail),
>     with phase2="autheap=MD5" and phase1="allow_canned_success=1" (working)
>
>>
>>
>> > data->phase2_success was checked in eap_ttls_isKeyAvailable and eap_ttls_getKey
>> > since 2008 at least (commit 6fc6879bd55a394f807cbbe927df736c190cb8ab is the
>> > earliest commit that is included in the git repository).
>>
>> This was added with the commit message explicitly noting the addition in
>> 2004:
>> http://w1.fi/cgit/hostap-history/commit/?id=0620112483fd93e8bc8a6f6248ff039f35cf501f
>
>
> I must have missed the hostap-history repository. Thanks for the pointer!
>
>>
>>
>>
>> > Commit 7f7bfba919a76bb03a7f762eab0ac00d4f5c3184 (2015-02-01) introduced the
>> > allow_canned_success=1 configuration option, so I am assuming not removing
>> > data->phase2_success was an oversight of that commit.
>>
>> Canned success is a case where the EAP authentication method is skipped
>> completely. This is quite different from the EAP-TTLS case and
>> allow_canned_success=1 is only for wired IEEE 802.1X cases without data
>> encryption. This does not give any justification for removal of the
>> data->phase2_success check from getKey().
>>
>>
>> > Debug log excerpt from before this commit:
>> >
>> > […]
>> > wlp4s0: WPA: RX message 1 of 4-Way Handshake from 66:70:02:77:e2:70 (ver=2)
>>
>> I'd need to see what happened before the start of this 4-way handshake
>> to be able to understand why the TTLS exchange did not complete
>> properly.
>>
>> > diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
>> > @@ -1727,7 +1727,7 @@ static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
>> >  static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv)
>> >  {
>> >       struct eap_ttls_data *data = priv;
>> > -     return data->key_data != NULL && data->phase2_success;
>> > +     return data->key_data != NULL;
>>
>> I think it would be better to set data->phase2_success = 1 at a suitable
>> location in the exchange for this special case rather than getting rid
>> of this check.
>>
>> As a quick workaround, you might be able to configure EAP-TTLS with a
>> Phase 2 mechanism that does not expect a response from the server, e.g.,
>> EAP-TTLS/PAP or EAP-TTLS/CHAP. Those would allow the server to complete
>> exchange with EAP-Success. Assuming this works here, the cleaner
>
>
> For the record, using phase2="auth=PAP", phase2="auth=CHAP" or phase2="auth=MSCHAP" indeed work.
>
>>
>> approach would be to add a network profile parameter for specifying that
>> no password is to be used and Phase 2 is to be fully skipped.
>>
>> --
>> Jouni Malinen                                            PGP id EFC895FA
>
>
>
>
> --
> Best regards,
> Michael
diff mbox

Patch

diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index 92f94dc..0c7509c 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -1727,7 +1727,7 @@  static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
 static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv)
 {
 	struct eap_ttls_data *data = priv;
-	return data->key_data != NULL && data->phase2_success;
+	return data->key_data != NULL;
 }
 
 
@@ -1736,7 +1736,7 @@  static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
 	struct eap_ttls_data *data = priv;
 	u8 *key;
 
-	if (data->key_data == NULL || !data->phase2_success)
+	if (data->key_data == NULL)
 		return NULL;
 
 	key = os_malloc(EAP_TLS_KEY_LEN);