Message ID | 20150708191100.GA24433@w1.fi |
---|---|
State | Deferred |
Headers | show |
On Wed, 2015-07-08 at 22:11 +0300, Jouni Malinen wrote: > Write a clear debug log entry and also send it to control interface > monitors when it looks likely that this case has been hit. I'll test these patches later; thanks. In the meantime, this touches on something else I was hoping to look at... is this failure mode actually supposed to be reported coherently to NetworkManager, and then to the user? It is extremely suboptimal at the moment that any form of failure is mostly just silent. I haven't yet worked out where the real fault lies.
On Wed, Jul 08, 2015 at 08:26:19PM +0100, David Woodhouse wrote: > On Wed, 2015-07-08 at 22:11 +0300, Jouni Malinen wrote: > > Write a clear debug log entry and also send it to control interface > > monitors when it looks likely that this case has been hit. > > I'll test these patches later; thanks. In the meantime, this touches on > something else I was hoping to look at... is this failure mode actually > supposed to be reported coherently to NetworkManager, and then to the > user? Unfortunately, it is not. Some of the EAP operation events are still available only through the (non-D-Bus) control interface and this new message is in that category. > It is extremely suboptimal at the moment that any form of failure is > mostly just silent. I haven't yet worked out where the real fault lies. Agreed. Someone would need to define a suitable extension to the D-Bus events to make this information available from wpa_supplicant and matching changes in NetworkManager to be able to display this. Some of the related events are available as D-Bus signals (e.g., as far as TLS certificate validation steps are concerned), but I don't think there is any generic event signal that would allow wpa_supplicant to make NM display something all the way to the user in a reasonable manner today.
On Wed, 2015-07-08 at 22:11 +0300, Jouni Malinen wrote: > > RSN: Stop connection attempt on apparent PMK mismatch > > If WPA2-Enterprise connection with full EAP authentication (i.e., no > PMKSA caching used) results in a PMKID that does not match the one the > AP/Authenticator indicates in EAPOL-Key msg 1/4, there is not much point > in trying to trigger full EAP authentication by sending EAPOL-Start > since this sequence was immediately after such full authentication > attempt. That works... wlo1: SME: Trying to authenticate with 18:33:9d:0c:da:de (SSID='TSNOfficeWLAN' freq=5300 MHz) wlo1: Trying to associate with 18:33:9d:0c:da:de (SSID='TSNOfficeWLAN' freq=5300 MHz) wlo1: Associated with 18:33:9d:0c:da:de wlo1: CTRL-EVENT-EAP-STARTED EAP authentication started wlo1: CTRL-EVENT-REGDOM-CHANGE init=COUNTRY_IE type=COUNTRY alpha2=FR wlo1: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=13 TLS - SSL error: error:0B07C065:x509 certificate routines:X509_STORE_add_cert:cert already in hash table wlo1: CTRL-EVENT-EAP-METHOD EAP vendor 0 method 13 (TLS) selected wlo1: CTRL-EVENT-EAP-PEER-CERT depth=3 subject='/C=US/O=Intel Corporation/CN=Intel Root CA' wlo1: CTRL-EVENT-EAP-PEER-CERT depth=2 subject='/C=US/O=Intel Corporation/CN=Intel Intranet Basic Policy CA' wlo1: CTRL-EVENT-EAP-PEER-CERT depth=1 subject='/C=US/O=Intel Corporation/CN=Intel Intranet Basic Issuing CA 1A' wlo1: CTRL-EVENT-EAP-PEER-CERT depth=0 subject='/CN=ir10d-pra1.ir.intel.com' wlo1: CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully wlo1: RSN: PMKID mismatch - authentication server may have derived different MSK?! wlo1: CTRL-EVENT-DISCONNECTED bssid=18:33:9d:0c:da:de reason=1 locally_generated=1 We end up *blacklisting* the offending BSSIDs and not trying them again for a while... would it be possible to start by disabling TLSv1.2 for the offending BSSIDs, rather than giving up entirely? That might be a simpler workaround than the other one (which I'm about to test).
On Wed, 2015-07-08 at 22:11 +0300, Jouni Malinen wrote: > > EAP-TLS/TTLS/PEAP workaround for incorrect TLS v1.2 MSK derivation > > Some authentication servers (e.g., FreeRADIUS 2.2.6 or 3.0.7 when built > with OpenSSL 1.0.2) are known to derive MSK incorrectly with TLS v1.2 is > used. If WPA2-Enterprise is used with an AP that includes PMKID in > EAPOL-Key msg 1/4, it is possible to detect this incorrect > authentication server behavior and work around it by using matching, > incorrect MSK derivation on the peer side. That appears to work here. Less trivial to backport to 2.4 though :) wlo1: SME: Trying to authenticate with 18:33:9d:0c:da:de (SSID='TSNOfficeWLAN' freq=5300 MHz) wlo1: Trying to associate with 18:33:9d:0c:da:de (SSID='TSNOfficeWLAN' freq=5300 MHz) wlo1: Associated with 18:33:9d:0c:da:de wlo1: CTRL-EVENT-EAP-STARTED EAP authentication started wlo1: CTRL-EVENT-REGDOM-CHANGE init=COUNTRY_IE type=COUNTRY alpha2=FR wlo1: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=13 TLS - SSL error: error:0B07C065:x509 certificate routines:X509_STORE_add_cert:cert already in hash table wlo1: CTRL-EVENT-EAP-METHOD EAP vendor 0 method 13 (TLS) selected wlo1: CTRL-EVENT-EAP-PEER-CERT depth=3 subject='/C=US/O=Intel Corporation/CN=Intel Root CA' wlo1: CTRL-EVENT-EAP-PEER-CERT depth=2 subject='/C=US/O=Intel Corporation/CN=Intel Intranet Basic Policy CA' wlo1: CTRL-EVENT-EAP-PEER-CERT depth=1 subject='/C=US/O=Intel Corporation/CN=Intel Intranet Basic Issuing CA 1A' wlo1: CTRL-EVENT-EAP-PEER-CERT depth=0 subject='/CN=ir10d-pra1.ir.intel.com' wlo1: CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully wlo1: RSN: PMKID mismatch - authentication server used incorrect MSK derivation with TLS v1.2 - accept that as an interoperability workaround wlo1: WPA: Key negotiation completed with 18:33:9d:0c:da:de [PTK=CCMP GTK=TKIP] wlo1: CTRL-EVENT-CONNECTED - Connection to 18:33:9d:0c:da:de completed [id=0 id_str=]
On Thu, Jul 09, 2015 at 12:38:54AM +0100, David Woodhouse wrote: > On Wed, 2015-07-08 at 22:11 +0300, Jouni Malinen wrote: > > > > EAP-TLS/TTLS/PEAP workaround for incorrect TLS v1.2 MSK derivation > That appears to work here. Less trivial to backport to 2.4 though :) Thanks for testing. Was it clear that this uses an authentication server other than FreeRADIUS? If so, there would be at least two different authentication servers with this issue and it would be good to make sure the vendor becomes aware (and well, a suitable person at the vendor so that this actually gets fixed rather sooner than later)..
On Wed, Jul 08, 2015 at 11:47:17PM +0100, David Woodhouse wrote: > On Wed, 2015-07-08 at 22:11 +0300, Jouni Malinen wrote: > > > > RSN: Stop connection attempt on apparent PMK mismatch > wlo1: CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully > wlo1: RSN: PMKID mismatch - authentication server may have derived different MSK?! > wlo1: CTRL-EVENT-DISCONNECTED bssid=18:33:9d:0c:da:de reason=1 locally_generated=1 > > We end up *blacklisting* the offending BSSIDs and not trying them again > for a while... would it be possible to start by disabling TLSv1.2 for > the offending BSSIDs, rather than giving up entirely? > > That might be a simpler workaround than the other one (which I'm about > to test). It would certainly be simpler, but I don't really like the idea of wpa_supplicant disabling TLSv1.2 completely at least as far as modifying the network configuration is concerned. I guess I could live with a temporary disabling of TLSv1.2 (i.e., just do that outside the persistent configuration parameters and for limited duration). However, I'd rather do that only in case this can really be shown to be because of the incorrect MSK derivation. Or well, I guess it could be considered secure enough to do this even without checking the "alternative MSK derivation" and just check that TLSv1.2 was used during the exchange. That would already be enough to show that TLSv1.2 was successfully completed which would make it quite a bit less likely for an attacker to be able to use this for a downgrade attack. An attack would still be possible with the simple implementation, though, since all it takes is a quick transmission of a bogus EAPOL-Key msg 1/4 immediately after the EAP-Success message which would be doable without that much effort.. In other words, I'm not really sure I would be accepting such a change into hostap.git or well, at least not enabling that behavior by default and with that in mind, it might be as simple to just have an out-of-tree patch available for anyone who wants to build a binary with such a capability while understanding the implications this would have on security (allowing TLS downgrade attack from v1.2 to v1.0/1.1).
On Fri, 2015-07-10 at 20:57 +0300, Jouni Malinen wrote: > On Thu, Jul 09, 2015 at 12:38:54AM +0100, David Woodhouse wrote: > > On Wed, 2015-07-08 at 22:11 +0300, Jouni Malinen wrote: > > > > > > EAP-TLS/TTLS/PEAP workaround for incorrect TLS v1.2 MSK derivation > > > That appears to work here. Less trivial to backport to 2.4 though :) > > Thanks for testing. Was it clear that this uses an authentication server > other than FreeRADIUS? If so, there would be at least two different > authentication servers with this issue and it would be good to make sure > the vendor becomes aware (and well, a suitable person at the vendor so > that this actually gets fixed rather sooner than later).. Last I knew it was Cisco ISE. Which would probably mean that there's not much prospect of actually getting it *fixed* before 2016. Cisco are not known for the competence of their support. The IT folks who own it are in Israel, so I should hopefully know more when they get back from their weekend, on Sunday.
On Fri, 2015-07-10 at 21:07 +0300, Jouni Malinen wrote: > However, I'd rather do that only in case this can really be shown to > be because of the incorrect MSK derivation. Yeah, that makes sense. In which case you'd still need the whole infrastructure to calculate the 'alternative' MSK. So we might as well stick with your existing patch which just *uses* the alternative MSK. > it might be as simple to just have an out-of-tree patch available for > anyone who wants to build a binary with such a capability I don't think there's much benefit in that. If they're going to have to fight the lack of coherent error reporting to work out what the problem is, and then take remedial action, then they might as well just *configure* it not to use TLSv1.2. A patch is probably harder than the config change (although Dan we *will* need NetworkManager to be able to set it on demand according to the config). The benefit in a code-based 'fix' is only really if it can be merged by default and enabled whenever eap_workaround is set.
On Fri, Jul 10, 2015 at 09:02:11PM +0100, David Woodhouse wrote: > On Fri, 2015-07-10 at 21:07 +0300, Jouni Malinen wrote: > > However, I'd rather do that only in case this can really be shown to > > be because of the incorrect MSK derivation. > > Yeah, that makes sense. In which case you'd still need the whole > infrastructure to calculate the 'alternative' MSK. So we might as well > stick with your existing patch which just *uses* the alternative MSK. Yes, that sounds likely in practice. > > it might be as simple to just have an out-of-tree patch available for > > anyone who wants to build a binary with such a capability > > I don't think there's much benefit in that. If they're going to have to > fight the lack of coherent error reporting to work out what the problem > is, and then take remedial action, then they might as well just > *configure* it not to use TLSv1.2. A patch is probably harder than the > config change (although Dan we *will* need NetworkManager to be able to > set it on demand according to the config). > > The benefit in a code-based 'fix' is only really if it can be merged by > default and enabled whenever eap_workaround is set. Agreed. eap_workaround is enabled by default, so for this to be acceptable, that complexity of calculating the incorrect MSK would be needed.. Once I get a bit more information on the scale of the issue (mainly, whether it is only the two previously identified server components that have clear fixes already available or whether there are some other servers impacted as well with no easy fix), I'll figure out whether I can convince myself to accept the workaround into hostap.git.. If you do get confirmation on the authentication server (ideally including its version number) being from Cisco, I can also check with the engineers directly to avoid going through normal support requests so as to see if this could be fixed soon for wpa_supplicant not having to care too much.
On Fri, 2015-07-10 at 23:10 +0300, Jouni Malinen wrote: > > Once I get a bit more information on the scale of the issue (mainly, > whether it is only the two previously identified server components that > have clear fixes already available or whether there are some other > servers impacted as well with no easy fix), I'll figure out whether I > can convince myself to accept the workaround into hostap.git.. > > If you do get confirmation on the authentication server (ideally > including its version number) being from Cisco, I can also check with > the engineers directly to avoid going through normal support requests so > as to see if this could be fixed soon for wpa_supplicant not having to > care too much. Ack. I'll chase them up and hopefully have that information on Sunday.
On Fri, 2015-07-10 at 23:10 +0300, Jouni Malinen wrote: > Once I get a bit more information on the scale of the issue (mainly, > whether it is only the two previously identified server components that > have clear fixes already available or whether there are some other > servers impacted as well with no easy fix), I'll figure out whether I > can convince myself to accept the workaround into hostap.git.. > > If you do get confirmation on the authentication server (ideally > including its version number) being from Cisco, I can also check with > the engineers directly to avoid going through normal support requests so > as to see if this could be fixed soon for wpa_supplicant not having to > care too much. The initial response was: "We are using Aruba ClearPass Policy Manager release 6.5.1 as our RADIUS server. This release does not support TLSv1.2." I have showed them a packet trace which clearly shows a client authenticating using EAP-TLSv1.2. And invited further comment :)
On 12/07/15 10:52, David Woodhouse wrote: > The initial response was: > > "We are using Aruba ClearPass Policy Manager release 6.5.1 as our > RADIUS server. This release does not support TLSv1.2." > > I have showed them a packet trace which clearly shows a client > authenticating using EAP-TLSv1.2. And invited further comment :) According to [1] TLSv1.2 support was added in release 6.5.2 [1]: https://support.arubanetworks.com/Documentation/tabid/77/DMXModule/512/Command/Core_Download/Default.aspx?EntryId=17940
It would be worth checking what the Windows supplicant does after configuring it to use TLS 1.2. See "More Information" from: https://support.microsoft.com/en-us/kb/2977292
On Sun, Jul 12, 2015 at 09:52:27AM +0100, David Woodhouse wrote: > The initial response was: > > "We are using Aruba ClearPass Policy Manager release 6.5.1 as our > RADIUS server. This release does not support TLSv1.2." > > I have showed them a packet trace which clearly shows a client > authenticating using EAP-TLSv1.2. And invited further comment :) Thanks. I asked Aruba and got a response that this was fixed in 6.5.2 which I interpreted as 6.5.1 unfortunately enabling TLSv1.2 even though it was not "supported" and then not using the correct PRF.. Anyway, this issue will hopefully go away with the server upgrade. As such, I'm not currently planning on adding the workaround into hostap.git.
On Tue, 2015-07-14 at 21:01 +0300, Jouni Malinen wrote: > On Sun, Jul 12, 2015 at 09:52:27AM +0100, David Woodhouse wrote: > > The initial response was: > > > > "We are using Aruba ClearPass Policy Manager release 6.5.1 as our > > RADIUS server. This release does not support TLSv1.2." > > > > I have showed them a packet trace which clearly shows a client > > authenticating using EAP-TLSv1.2. And invited further comment :) > > Thanks. I asked Aruba and got a response that this was fixed in 6.5.2 > which I interpreted as 6.5.1 unfortunately enabling TLSv1.2 even though > it was not "supported" and then not using the correct PRF.. Anyway, this > issue will hopefully go away with the server upgrade. At least for us, the server upgrade isn't planned imminently because of issues with it — I'm told of a vulnerability in 6.5.2, as well as the fact that there's no easy deployment rollback. If you have competent contacts in Aruba, please could you ask them if it's possible to *prevent* 6.5.1 from using TLSv1.2? Either in configuration, or a minor bugfix update without requiring users to do a full upgrade to 6.5.2? Thanks.
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 8adeef4..faffe36 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -249,6 +249,17 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, "RSN: the new PMK matches with the " "PMKID"); abort_cached = 0; + } else if (sa && !sm->cur_pmksa && pmkid) { + /* + * It looks like the authentication server + * derived mismatching MSK. This should not + * really happen, but bugs happen.. There is not + * much we can do here without knowing what + * exactly caused the server to misbehave. + */ + wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: PMKID mismatch - authentication server may have derived different MSK?!"); + return -1; } if (!sm->cur_pmksa) > > A variant on this workaround idea is that we don't just add the 'wrong' > > one to the PMKSA cache, but we add some kind of 'poison' instead, which > > if matched will trigger a fallback to TLSv1.1. But given that falling > > back to TLSv1.1 will cause us to use the old PRF *anyway*, it's not > > clear that there's any real benefit in doing it that way instead... > > Probably not.. This is likely more complex and I don't think the > possibility of using HMAC-SHA1+MD5-based PRF after a successfully > completed TLS v1.2 exchange would be that horrible price to pay as a > temporary (hopefully..) interop workaround. It is a bit more complex than I hoped for, but the following patch does this. However, I'm not sure I really would like to apply this.. Anyway, here it is if someone would like to have such a workaround and run a test with an authentication server to confirm whether this exact TLSv1.2 PRF issue is behind the interoperability issue. I verified that this works with FreeRADIUS. EAP-TLS/TTLS/PEAP workaround for incorrect TLS v1.2 MSK derivation Some authentication servers (e.g., FreeRADIUS 2.2.6 or 3.0.7 when built with OpenSSL 1.0.2) are known to derive MSK incorrectly with TLS v1.2 is used. If WPA2-Enterprise is used with an AP that includes PMKID in EAPOL-Key msg 1/4, it is possible to detect this incorrect authentication server behavior and work around it by using matching, incorrect MSK derivation on the peer side. Check for this interoperability issue and allow 4-way handshake to continue if the alternative (incorrect) MSK derivation design results in a PMKID that matches the one sent by the AP/Authenticator. Signed-off-by: Jouni Malinen <j@w1.fi> --- src/crypto/tls.h | 7 ++++++ src/crypto/tls_openssl.c | 18 ++++++++++++++ src/eap_peer/eap.c | 33 ++++++++++++++++++++++++++ src/eap_peer/eap.h | 1 + src/eap_peer/eap_i.h | 15 ++++++++++++ src/eap_peer/eap_peap.c | 25 ++++++++++++++++++++ src/eap_peer/eap_tls.c | 18 ++++++++++++++ src/eap_peer/eap_tls_common.c | 19 +++++++++++++++ src/eap_peer/eap_tls_common.h | 2 ++ src/eap_peer/eap_ttls.c | 18 ++++++++++++++ src/eapol_supp/eapol_supp_sm.c | 30 ++++++++++++++++++++++++ src/eapol_supp/eapol_supp_sm.h | 1 + src/rsn_supp/wpa.c | 53 ++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 240 insertions(+) diff --git a/src/crypto/tls.h b/src/crypto/tls.h index dbe9fd1..a69f86d 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -350,6 +350,13 @@ int __must_check tls_connection_prf(void *tls_ctx, int skip_keyblock, u8 *out, size_t out_len); +int __must_check tls_connection_prf_alt(void *tls_ctx, + struct tls_connection *conn, + const char *label, + int server_random_first, + int skip_keyblock, + u8 *out, size_t out_len); + /** * tls_connection_handshake - Process TLS handshake (client side) * @tls_ctx: TLS context data from tls_init() diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index eff942c..c2ba64b 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -2784,6 +2784,24 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, } +int tls_connection_prf_alt(void *tls_ctx, struct tls_connection *conn, + const char *label, int server_random_first, + int skip_keyblock, u8 *out, size_t out_len) +{ + const char *name; + + if (conn == NULL || conn->ssl == NULL) + return -1; + + name = SSL_get_version(conn->ssl); + if (name == NULL || os_strcmp(name, "TLSv1.2") != 0) + return -1; + + return openssl_tls_prf(tls_ctx, conn, label, server_random_first, + skip_keyblock, out, out_len); +} + + static struct wpabuf * openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, int server) diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index c8a1231..f6b393b 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -99,6 +99,10 @@ static void eap_sm_free_key(struct eap_sm *sm) bin_clear_free(sm->eapKeyData, sm->eapKeyDataLen); sm->eapKeyData = NULL; } + if (sm->eapKeyData_alt) { + bin_clear_free(sm->eapKeyData_alt, sm->eapKeyDataLen_alt); + sm->eapKeyData_alt = NULL; + } } @@ -683,6 +687,11 @@ SM_STATE(EAP, METHOD) eap_sm_free_key(sm); sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, &sm->eapKeyDataLen); + if (sm->m->getKey_alt) { + sm->eapKeyData_alt = sm->m->getKey_alt( + sm, sm->eap_method_priv, + &sm->eapKeyDataLen_alt); + } os_free(sm->eapSessionId); sm->eapSessionId = NULL; if (sm->m->getSessionId) { @@ -2775,6 +2784,30 @@ const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len) /** + * eap_get_eapKeyData_alt - Get alternative master session key (MSK) from EAP + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Pointer to variable that will be set to number of bytes in the key + * Returns: Pointer to the EAP keying data or %NULL on failure + * + * Fetch EAP keying material (MSK, eapKeyData) from the EAP state machine using + * an alternative derivation function (incorrect TLS v1.2 PRF). The key is + * available only after a successful authentication. EAP state machine continues + * to manage the key data and the caller must not change or free the returned + * data. + */ +const u8 * eap_get_eapKeyData_alt(struct eap_sm *sm, size_t *len) +{ + if (sm == NULL || sm->eapKeyData_alt == NULL) { + *len = 0; + return NULL; + } + + *len = sm->eapKeyDataLen_alt; + return sm->eapKeyData_alt; +} + + +/** * eap_get_eapKeyData - Get EAP response data * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * Returns: Pointer to the EAP response (eapRespData) or %NULL on failure diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h index 1a645af..9ae3e50 100644 --- a/src/eap_peer/eap.h +++ b/src/eap_peer/eap.h @@ -336,6 +336,7 @@ void eap_notify_success(struct eap_sm *sm); void eap_notify_lower_layer_success(struct eap_sm *sm); const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len); const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len); +const u8 * eap_get_eapKeyData_alt(struct eap_sm *sm, size_t *len); struct wpabuf * eap_get_eapRespData(struct eap_sm *sm); void eap_register_scard_ctx(struct eap_sm *sm, void *ctx); void eap_invalidate_cached_session(struct eap_sm *sm); diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h index 5f8b5fa..2bbae44 100644 --- a/src/eap_peer/eap_i.h +++ b/src/eap_peer/eap_i.h @@ -139,6 +139,19 @@ struct eap_method { u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len); /** + * getKey_alt - Get EAP method specific keying material (eapKeyData_alt) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to variable to store key length (eapKeyDataLen_alt) + * Returns: Keying material (eapKeyData_alt) or %NULL if not available + * + * This function can be used to get the alternative keying material from + * the EAP method as an interoperability workaround for incorrect TLS + * v1.2 implementation in some authentication servers. + */ + u8 * (*getKey_alt)(struct eap_sm *sm, void *priv, size_t *len); + + /** * get_status - Get EAP method status * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @priv: Pointer to private EAP method data from eap_method::init() @@ -322,6 +335,8 @@ struct eap_sm { Boolean eapKeyAvailable; /* peer to lower layer */ u8 *eapKeyData; /* peer to lower layer */ size_t eapKeyDataLen; /* peer to lower layer */ + u8 *eapKeyData_alt; /* peer to lower layer */ + size_t eapKeyDataLen_alt; /* peer to lower layer */ u8 *eapSessionId; /* peer to lower layer */ size_t eapSessionIdLen; /* peer to lower layer */ const struct eap_method *m; /* selected EAP method */ diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c index 4f68fce..e8f6282 100644 --- a/src/eap_peer/eap_peap.c +++ b/src/eap_peer/eap_peap.c @@ -1208,6 +1208,30 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_peap_getKey_alt(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *key; + char *label; + + if (data->crypto_binding_used) + return NULL; + + if (data->force_new_label) + label = "client PEAP encryption"; + else + label = "client EAP encryption"; + key = eap_peer_tls_derive_key_alt(sm, &data->ssl, label, + EAP_TLS_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + + return key; +} + + static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) { struct eap_peap_data *data = priv; @@ -1242,6 +1266,7 @@ int eap_peer_peap_register(void) eap->process = eap_peap_process; eap->isKeyAvailable = eap_peap_isKeyAvailable; eap->getKey = eap_peap_getKey; + eap->getKey_alt = eap_peap_getKey_alt; eap->get_status = eap_peap_get_status; eap->has_reauth_data = eap_peap_has_reauth_data; eap->deinit_for_reauth = eap_peap_deinit_for_reauth; diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c index 66a027a..b4c0eb4 100644 --- a/src/eap_peer/eap_tls.c +++ b/src/eap_peer/eap_tls.c @@ -309,6 +309,23 @@ static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_tls_getKey_alt(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *key; + + key = eap_peer_tls_derive_key_alt(sm, &data->ssl, + "client EAP encryption", + EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + + return key; +} + + static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_tls_data *data = priv; @@ -362,6 +379,7 @@ int eap_peer_tls_register(void) eap->process = eap_tls_process; eap->isKeyAvailable = eap_tls_isKeyAvailable; eap->getKey = eap_tls_getKey; + eap->getKey_alt = eap_tls_getKey_alt; eap->getSessionId = eap_tls_get_session_id; eap->get_status = eap_tls_get_status; eap->has_reauth_data = eap_tls_has_reauth_data; diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index 2a108da..5a20d31 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -330,6 +330,25 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, } +u8 * eap_peer_tls_derive_key_alt(struct eap_sm *sm, struct eap_ssl_data *data, + const char *label, size_t len) +{ + u8 *out; + + out = os_malloc(len); + if (out == NULL) + return NULL; + + if (tls_connection_prf_alt(data->ssl_ctx, data->conn, label, 0, 0, + out, len)) { + os_free(out); + return NULL; + } + + return out; +} + + /** * eap_peer_tls_derive_session_id - Derive a Session-Id based on TLS data * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h index acd2b78..e7e1776 100644 --- a/src/eap_peer/eap_tls_common.h +++ b/src/eap_peer/eap_tls_common.h @@ -95,6 +95,8 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, const char *label, size_t len); +u8 * eap_peer_tls_derive_key_alt(struct eap_sm *sm, struct eap_ssl_data *data, + const char *label, size_t len); u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, struct eap_ssl_data *data, u8 eap_type, size_t *len); diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index 189a6f1..0c322cf 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -1631,6 +1631,23 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_ttls_getKey_alt(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ttls_data *data = priv; + u8 *key; + + key = eap_peer_tls_derive_key_alt(sm, &data->ssl, + "ttls keying material", + EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + + return key; +} + + static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) { struct eap_ttls_data *data = priv; @@ -1684,6 +1701,7 @@ int eap_peer_ttls_register(void) eap->process = eap_ttls_process; eap->isKeyAvailable = eap_ttls_isKeyAvailable; eap->getKey = eap_ttls_getKey; + eap->getKey = eap_ttls_getKey_alt; eap->getSessionId = eap_ttls_get_session_id; eap->get_status = eap_ttls_get_status; eap->has_reauth_data = eap_ttls_has_reauth_data; diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index 39b4319..eba9291 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -1577,6 +1577,36 @@ key_fetched: /** + * eapol_sm_get_key_alt - Get alternative master session key (MSK) from EAP + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @key: Pointer for key buffer + * @len: Number of bytes to copy to key + * Returns: 0 on success (len of key available), or -1 on failure. + * + * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key + * is available only after a successful authentication. This function is similar + * to eapol_sm_get_key(), but requests an alternative MSK derivation algorithm + * to be used. This is a workaround for incorrect TLSv1.2 PRF use in some + * authentication servers. + */ +int eapol_sm_get_key_alt(struct eapol_sm *sm, u8 *key, size_t len) +{ + const u8 *eap_key; + size_t eap_len; + + if (sm == NULL || !eap_key_available(sm->eap)) + return -1; + eap_key = eap_get_eapKeyData_alt(sm->eap, &eap_len); + if (eap_key == NULL || len > eap_len) + return -1; + if (len > eap_len) + return eap_len; + os_memcpy(key, eap_key, len); + return 0; +} + + +/** * eapol_sm_get_session_id - Get EAP Session-Id * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @len: Pointer to variable that will be set to number of bytes in the session diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h index 1309ff7..79f173b 100644 --- a/src/eapol_supp/eapol_supp_sm.h +++ b/src/eapol_supp/eapol_supp_sm.h @@ -312,6 +312,7 @@ void eapol_sm_notify_config(struct eapol_sm *sm, struct eap_peer_config *config, const struct eapol_config *conf); int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len); +int eapol_sm_get_key_alt(struct eapol_sm *sm, u8 *key, size_t len); const u8 * eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len); void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff); void eapol_sm_notify_cached(struct eapol_sm *sm); diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index faffe36..a0fd2e2 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -171,6 +171,56 @@ static void wpa_supplicant_key_mgmt_set_pmk(struct wpa_sm *sm) } +static int rsn_msk_mismatch_workaround(struct wpa_sm *sm, const u8 *src_addr, + const u8 *pmkid) +{ + u8 pmk[PMK_LEN], new_pmkid[PMKID_LEN]; + int res; + + if (!sm->eap_workaround || wpa_key_mgmt_suite_b(sm->key_mgmt) || + wpa_key_mgmt_ft(sm->key_mgmt)) + return 0; /* EAP workarounds not allowed */ + + /* + * If TLS v1.2 was used, try to derive MSK in a known, incorrect way + * that some authentication servers used. If that results in a matching + * PMKID, continue authentication with the incorrectly derived MSK as + * an interoperability workaround. + */ + + res = eapol_sm_get_key_alt(sm->eapol, pmk, PMK_LEN); + if (res) + return 0; + wpa_hexdump_key(MSG_DEBUG, + "RSN: A possible workaround PMK from EAPOL state machines", + pmk, PMK_LEN); + + rsn_pmkid(pmk, PMK_LEN, src_addr, sm->own_addr, new_pmkid, + wpa_key_mgmt_sha256(sm->key_mgmt)); + wpa_hexdump(MSG_DEBUG, "RSN: PMKID for the possible workaround PMK", + new_pmkid, PMKID_LEN); + if (os_memcmp(pmkid, new_pmkid, PMKID_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "RSN: Workaround PMK did not match PMKID from AP"); + os_memset(pmk, 0, PMK_LEN); + return 0; + } + + sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, PMK_LEN, NULL, 0, + src_addr, sm->own_addr, sm->network_ctx, + sm->key_mgmt); + + os_memcpy(sm->pmk, pmk, PMK_LEN); + os_memset(pmk, 0, PMK_LEN); + sm->pmk_len = PMK_LEN; + wpa_supplicant_key_mgmt_set_pmk(sm); + + wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: PMKID mismatch - authentication server used incorrect MSK derivation with TLS v1.2 - accept that as an interoperability workaround"); + return 1; +} + + static int wpa_supplicant_get_pmk(struct wpa_sm *sm, const unsigned char *src_addr, const u8 *pmkid) @@ -257,6 +307,9 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, * much we can do here without knowing what * exactly caused the server to misbehave. */ + if (rsn_msk_mismatch_workaround(sm, src_addr, + pmkid) == 1) + return 0; wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, "RSN: PMKID mismatch - authentication server may have derived different MSK?!"); return -1;