From 73ed03f33323414ba02e50c15149bcb1c37d57e8 Mon Sep 17 00:00:00 2001 From: Max Stepanov Date: Wed, 14 Oct 2015 12:26:33 +0300 Subject: [PATCH] wpa_supplicant: Add GTK RSC relaxation workaround Some APs may send RSC octets in EAPOL-Key message 3 of 4-Way Handshake or in EAPOL-Key message 1 of Group Key Handshake in the opposite byte order (or by some other corrupted way). Thus, after a successful EAPOL-Key exchange the TSC values of received multicast packets, such as DHCP, don't match the RSC one and as a result these packets are dropped on replay attack TSC verification. An example of such AP is Sapido RB-1732. Work around this by setting RSC octets to 0 on GTK installation if the AP RSC value is identified as a potentially having the byte order issue. This may open a short window during which older (but valid) group-addressed frames could be replayed. However, the local receive counter will be updated on the first received group-addressed frame and the workaround is enabled only if the common invalid cases are detected, so this workaround is acceptable as not decreasing security significantly. The wpa_rsc_relaxation global configuration property allows the GTK RSC workaround to be disabled if it's not needed. Signed-off-by: Max Stepanov --- src/rsn_supp/wpa.c | 51 +++++++++++++++++++++++++++++++++--- src/rsn_supp/wpa.h | 1 + src/rsn_supp/wpa_i.h | 1 + wpa_supplicant/config.c | 2 ++ wpa_supplicant/config.h | 11 ++++++++ wpa_supplicant/config_file.c | 4 +++ wpa_supplicant/wpas_glue.c | 1 + 7 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 505c7a12d..0b5fe51ee 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -1,6 +1,7 @@ /* * WPA Supplicant - WPA state machine and EAPOL-Key processing * Copyright (c) 2003-2015, Jouni Malinen + * Copyright(c) 2015 Intel Deutschland GmbH * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -23,6 +24,9 @@ #include "peerkey.h" +static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + /** * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -611,7 +615,6 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, int keylen, rsclen; enum wpa_alg alg; const u8 *key_rsc; - u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; if (!sm->tk_to_set) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, @@ -768,12 +771,43 @@ static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm, } +static int wpa_supplicant_rsc_relaxation(const struct wpa_sm *sm, + const u8 *rsc) +{ + int rsclen; + + if (!sm->wpa_rsc_relaxation) + return 0; + + rsclen = wpa_cipher_rsc_len(sm->group_cipher); + + /* + * Try to detect RSC (endian) corruption issue where the AP sends + * the RSC bytes in EAPOL-Key message in the wrong order, both if + * it's actually a 6-byte field (as it should be) and if it treats + * it as an 8-byte field. + * An AP model known to have this bug is the Sapido RB-1632. + */ + if (rsclen == 6 && ((rsc[5] && !rsc[0]) || rsc[6] || rsc[7])) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "RSC %02x%02x%02x%02x%02x%02x%02x%02x is likely bogus, using 0", + rsc[0], rsc[1], rsc[2], rsc[3], + rsc[4], rsc[5], rsc[6], rsc[7]); + + return 1; + } + + return 0; +} + + static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, const struct wpa_eapol_key *key, const u8 *gtk, size_t gtk_len, int key_info) { struct wpa_gtk_data gd; + const u8 *key_rsc; /* * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x @@ -799,11 +833,15 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, os_memcpy(gd.gtk, gtk, gtk_len); gd.gtk_len = gtk_len; + key_rsc = key->key_rsc; + if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc)) + key_rsc = null_rsc; + if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED && (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len, gtk_len, &gd.key_rsc_len, &gd.alg) || - wpa_supplicant_install_gtk(sm, &gd, key->key_rsc))) { + wpa_supplicant_install_gtk(sm, &gd, key_rsc))) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Failed to install GTK"); os_memset(&gd, 0, sizeof(gd)); @@ -1464,6 +1502,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, u16 key_info; int rekey, ret; struct wpa_gtk_data gd; + const u8 *key_rsc; if (!sm->msg_3_of_4_ok) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, @@ -1494,7 +1533,11 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, if (ret) goto failed; - if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) || + key_rsc = key->key_rsc; + if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc)) + key_rsc = null_rsc; + + if (wpa_supplicant_install_gtk(sm, &gd, key_rsc) || wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0) goto failed; os_memset(&gd, 0, sizeof(gd)); @@ -2450,6 +2493,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->ssid_len = 0; sm->wpa_ptk_rekey = config->wpa_ptk_rekey; sm->p2p = config->p2p; + sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation; } else { sm->network_ctx = NULL; sm->peerkey_enabled = 0; @@ -2460,6 +2504,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->ssid_len = 0; sm->wpa_ptk_rekey = 0; sm->p2p = 0; + sm->wpa_rsc_relaxation = 0; } } diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index e163b7010..9bfe0e259 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -104,6 +104,7 @@ struct rsn_supp_config { size_t ssid_len; int wpa_ptk_rekey; int p2p; + int wpa_rsc_relaxation; }; #ifndef CONFIG_NO_WPA diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 4368caa0d..14b77994e 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -61,6 +61,7 @@ struct wpa_sm { size_t ssid_len; int wpa_ptk_rekey; int p2p; + int wpa_rsc_relaxation; u8 own_addr[ETH_ALEN]; const char *ifname; diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 03b91a22b..db5de5fb4 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -3540,6 +3540,7 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME; config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD; config->cert_in_cb = DEFAULT_CERT_IN_CB; + config->wpa_rsc_relaxation = DEFAULT_WPA_RSC_RELAXATION; if (ctrl_interface) config->ctrl_interface = os_strdup(ctrl_interface); @@ -4246,6 +4247,7 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(fst_priority, 1, FST_MAX_PRIO_VALUE), 0 }, { INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 }, #endif /* CONFIG_FST */ + { INT_RANGE(wpa_rsc_relaxation, 0, 1), 0 }, }; #undef FUNC diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 3ee0797a6..2dd147574 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -39,6 +39,7 @@ #define DEFAULT_KEY_MGMT_OFFLOAD 1 #define DEFAULT_CERT_IN_CB 1 #define DEFAULT_P2P_GO_CTWINDOW 0 +#define DEFAULT_WPA_RSC_RELAXATION 1 #include "config_ssid.h" #include "wps/wps.h" @@ -1252,6 +1253,16 @@ struct wpa_config { * interface. */ int fst_llt; + + /** + * wpa_rsc_relaxation - RSC relaxation on GTK installation + * + * Values: + * 0 - use the EAPOL-Key RSC value on GTK installation + * 1 - use the null RSC if a bogus RSC value is detected in message 3 + * of 4-Way Handshake or message 1 of Group Key Handshake. + */ + int wpa_rsc_relaxation; }; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index fb438ea43..215388c8e 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -1299,6 +1299,10 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->wps_priority) fprintf(f, "wps_priority=%d\n", config->wps_priority); + + if (config->wpa_rsc_relaxation != DEFAULT_WPA_RSC_RELAXATION) + fprintf(f, "wpa_rsc_relaxation=%d\n", + config->wpa_rsc_relaxation); } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index 29c22ba2c..aaadb959b 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -1124,6 +1124,7 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, } } #endif /* CONFIG_P2P */ + conf.wpa_rsc_relaxation = wpa_s->conf->wpa_rsc_relaxation; } wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL); }