@@ -115,6 +115,7 @@ struct eap_tls_state {
bool expecting_frag_ack:1;
bool tunnel_ready:1;
+ bool tls_session_resumed:1;
struct l_queue *ca_cert;
struct l_certchain *client_cert;
@@ -129,8 +130,11 @@ static struct l_settings *eap_tls_session_cache;
static eap_tls_session_cache_load_func_t eap_tls_session_cache_load;
static eap_tls_session_cache_sync_func_t eap_tls_session_cache_sync;
-static void __eap_tls_common_state_reset(struct eap_tls_state *eap_tls)
+static void __eap_tls_common_state_reset(struct eap_state *eap)
{
+ struct eap_tls_state *eap_tls = eap_get_data(eap);
+ const char *peer_id;
+
eap_tls->version_negotiated = EAP_TLS_VERSION_NOT_NEGOTIATED;
eap_tls->method_completed = false;
eap_tls->phase2_failed = false;
@@ -145,6 +149,41 @@ static void __eap_tls_common_state_reset(struct eap_tls_state *eap_tls)
if (eap_tls->tunnel)
l_tls_reset(eap_tls->tunnel);
+ /*
+ * Drop the TLS session cache for this peer if the overall EAP
+ * method didn't succeed.
+ *
+ * Additionally if the session was cached previously, meaning
+ * that we've had a successful authentication at least once before,
+ * and we now used session resumption successfully and the method
+ * failed, become suspicious of this server's TLS session
+ * resumption support. Some authenticators strangely allow
+ * resumption but can't handle it all the way to EAP method
+ * success. This improves the chances that authentication
+ * succeeds on the next attempt.
+ *
+ * Drop the cache even if we have no indication that the
+ * method failed but it just didn't succeed, to handle cases like
+ * the server getting stuck and a timout occuring at a higher
+ * layer. The risk is that we may occasionally flush the session
+ * data when there was only a momentary radio issue, invalid
+ * phase2 credentials or decision to abort. Those are not hot
+ * paths.
+ *
+ * Note: TLS errors before the ready callback are handled in l_tls.
+ */
+ peer_id = eap_get_peer_id(eap);
+ if (peer_id && eap_tls_session_cache && !eap_method_is_success(eap) &&
+ l_settings_has_group(eap_tls_session_cache, peer_id)) {
+ eap_tls_forget_peer(peer_id);
+
+ if (eap_tls->tls_session_resumed)
+ l_warn("EAP: method did not finish after successful TLS"
+ " session resumption.");
+ }
+
+ eap_tls->tls_session_resumed = false;
+
eap_tls->tx_frag_offset = 0;
eap_tls->tx_frag_last_len = 0;
@@ -187,7 +226,7 @@ bool eap_tls_common_state_reset(struct eap_state *eap)
{
struct eap_tls_state *eap_tls = eap_get_data(eap);
- __eap_tls_common_state_reset(eap_tls);
+ __eap_tls_common_state_reset(eap);
if (eap_tls->variant_ops->reset)
eap_tls->variant_ops->reset(eap_tls->variant_data);
@@ -199,7 +238,7 @@ void eap_tls_common_state_free(struct eap_state *eap)
{
struct eap_tls_state *eap_tls = eap_get_data(eap);
- __eap_tls_common_state_reset(eap_tls);
+ __eap_tls_common_state_reset(eap);
eap_set_data(eap, NULL);
@@ -244,7 +283,9 @@ static void eap_tls_tunnel_ready(const char *peer_identity, void *user_data)
{
struct eap_state *eap = user_data;
struct eap_tls_state *eap_tls = eap_get_data(eap);
- bool resumed = l_tls_get_session_resumed(eap_tls->tunnel);
+
+ eap_tls->tls_session_resumed =
+ l_tls_get_session_resumed(eap_tls->tunnel);
if (eap_tls->ca_cert && !peer_identity) {
l_error("%s: TLS did not verify AP identity",
@@ -265,7 +306,8 @@ static void eap_tls_tunnel_ready(const char *peer_identity, void *user_data)
if (!eap_tls->variant_ops->tunnel_ready)
return;
- if (!eap_tls->variant_ops->tunnel_ready(eap, peer_identity, resumed))
+ if (!eap_tls->variant_ops->tunnel_ready(eap, peer_identity,
+ eap_tls->tls_session_resumed))
l_tls_close(eap_tls->tunnel);
}