Update to version 2.6 from upstream

Remove obsolete patches for NL80211_ATTR_SMPS_MODE encoding and KRACK
Fix CVE-2019-9494 (cache attack against SAE)
Fix CVE-2019-9495 (cache attack against EAP-pwd)
Fix CVE-2019-9496 (SAE confirm missing state validation in hostapd/AP)
Fix CVE-2019-9497 (EAP-pwd server not checking for reflection attack)
Fix CVE-2019-9498 (EAP-pwd server missing commit validation for scalar/element)
Fix CVE-2019-9499 (EAP-pwd peer missing commit validation for scalar/element)
This commit is contained in:
John W. Linville 2019-04-12 13:18:49 -04:00
parent a7b700ec1b
commit aeb7fa69dd
25 changed files with 2028 additions and 1016 deletions

2
.gitignore vendored
View File

@ -1 +1 @@
/hostapd-2.6.tar.gz
/hostapd-2.7.tar.gz

View File

@ -0,0 +1,93 @@
From d42c477cc794163a3757956bbffca5cea000923c Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Tue, 26 Feb 2019 11:43:03 +0200
Subject: [PATCH 01/14] OpenSSL: Use constant time operations for private
bignums
This helps in reducing measurable timing differences in operations
involving private information. BoringSSL has removed BN_FLG_CONSTTIME
and expects specific constant time functions to be called instead, so a
bit different approach is needed depending on which library is used.
The main operation that needs protection against side channel attacks is
BN_mod_exp() that depends on private keys (the public key validation
step in crypto_dh_derive_secret() is an exception that can use the
faster version since it does not depend on private keys).
crypto_bignum_div() is currently used only in SAE FFC case with not
safe-prime groups and only with values that do not depend on private
keys, so it is not critical to protect it.
crypto_bignum_inverse() is currently used only in SAE FFC PWE
derivation. The additional protection here is targeting only OpenSSL.
BoringSSL may need conversion to using BN_mod_inverse_blinded().
This is related to CVE-2019-9494 and CVE-2019-9495.
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
---
src/crypto/crypto_openssl.c | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 9c2ba58..ac53cc8 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -607,7 +607,8 @@ int crypto_mod_exp(const u8 *base, size_t base_len,
bn_result == NULL)
goto error;
- if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1)
+ if (BN_mod_exp_mont_consttime(bn_result, bn_base, bn_exp, bn_modulus,
+ ctx, NULL) != 1)
goto error;
*result_len = BN_bn2bin(bn_result, result);
@@ -1360,8 +1361,9 @@ int crypto_bignum_exptmod(const struct crypto_bignum *a,
bnctx = BN_CTX_new();
if (bnctx == NULL)
return -1;
- res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
- (const BIGNUM *) c, bnctx);
+ res = BN_mod_exp_mont_consttime((BIGNUM *) d, (const BIGNUM *) a,
+ (const BIGNUM *) b, (const BIGNUM *) c,
+ bnctx, NULL);
BN_CTX_free(bnctx);
return res ? 0 : -1;
@@ -1380,6 +1382,11 @@ int crypto_bignum_inverse(const struct crypto_bignum *a,
bnctx = BN_CTX_new();
if (bnctx == NULL)
return -1;
+#ifdef OPENSSL_IS_BORINGSSL
+ /* TODO: use BN_mod_inverse_blinded() ? */
+#else /* OPENSSL_IS_BORINGSSL */
+ BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME);
+#endif /* OPENSSL_IS_BORINGSSL */
res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a,
(const BIGNUM *) b, bnctx);
BN_CTX_free(bnctx);
@@ -1413,6 +1420,9 @@ int crypto_bignum_div(const struct crypto_bignum *a,
bnctx = BN_CTX_new();
if (bnctx == NULL)
return -1;
+#ifndef OPENSSL_IS_BORINGSSL
+ BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME);
+#endif /* OPENSSL_IS_BORINGSSL */
res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a,
(const BIGNUM *) b, bnctx);
BN_CTX_free(bnctx);
@@ -1504,8 +1514,8 @@ int crypto_bignum_legendre(const struct crypto_bignum *a,
/* exp = (p-1) / 2 */
!BN_sub(exp, (const BIGNUM *) p, BN_value_one()) ||
!BN_rshift1(exp, exp) ||
- !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p,
- bnctx))
+ !BN_mod_exp_mont_consttime(tmp, (const BIGNUM *) a, exp,
+ (const BIGNUM *) p, bnctx, NULL))
goto fail;
if (BN_is_word(tmp, 1))
--
2.7.4

View File

@ -0,0 +1,218 @@
From 6e34f618d37ddbb5854c42e2ad4fca83492fa7b7 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Wed, 27 Feb 2019 18:38:30 +0200
Subject: [PATCH 02/14] Add helper functions for constant time operations
These functions can be used to help implement constant time operations
for various cryptographic operations that must minimize externally
observable differences in processing (both in timing and also in
internal cache use, etc.).
This is related to CVE-2019-9494 and CVE-2019-9495.
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
---
src/utils/const_time.h | 191 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 191 insertions(+)
create mode 100644 src/utils/const_time.h
diff --git a/src/utils/const_time.h b/src/utils/const_time.h
new file mode 100644
index 0000000..ab8f611
--- /dev/null
+++ b/src/utils/const_time.h
@@ -0,0 +1,191 @@
+/*
+ * Helper functions for constant time operations
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * These helper functions can be used to implement logic that needs to minimize
+ * externally visible differences in execution path by avoiding use of branches,
+ * avoiding early termination or other time differences, and forcing same memory
+ * access pattern regardless of values.
+ */
+
+#ifndef CONST_TIME_H
+#define CONST_TIME_H
+
+
+#if defined(__clang__)
+#define NO_UBSAN_UINT_OVERFLOW \
+ __attribute__((no_sanitize("unsigned-integer-overflow")))
+#else
+#define NO_UBSAN_UINT_OVERFLOW
+#endif
+
+
+/**
+ * const_time_fill_msb - Fill all bits with MSB value
+ * @val: Input value
+ * Returns: Value with all the bits set to the MSB of the input val
+ */
+static inline unsigned int const_time_fill_msb(unsigned int val)
+{
+ /* Move the MSB to LSB and multiple by -1 to fill in all bits. */
+ return (val >> (sizeof(val) * 8 - 1)) * ~0U;
+}
+
+
+/* Returns: -1 if val is zero; 0 if val is not zero */
+static inline unsigned int const_time_is_zero(unsigned int val)
+ NO_UBSAN_UINT_OVERFLOW
+{
+ /* Set MSB to 1 for 0 and fill rest of bits with the MSB value */
+ return const_time_fill_msb(~val & (val - 1));
+}
+
+
+/* Returns: -1 if a == b; 0 if a != b */
+static inline unsigned int const_time_eq(unsigned int a, unsigned int b)
+{
+ return const_time_is_zero(a ^ b);
+}
+
+
+/* Returns: -1 if a == b; 0 if a != b */
+static inline u8 const_time_eq_u8(unsigned int a, unsigned int b)
+{
+ return (u8) const_time_eq(a, b);
+}
+
+
+/**
+ * const_time_eq_bin - Constant time memory comparison
+ * @a: First buffer to compare
+ * @b: Second buffer to compare
+ * @len: Number of octets to compare
+ * Returns: -1 if buffers are equal, 0 if not
+ *
+ * This function is meant for comparing passwords or hash values where
+ * difference in execution time or memory access pattern could provide external
+ * observer information about the location of the difference in the memory
+ * buffers. The return value does not behave like memcmp(), i.e.,
+ * const_time_eq_bin() cannot be used to sort items into a defined order. Unlike
+ * memcmp(), the execution time of const_time_eq_bin() does not depend on the
+ * contents of the compared memory buffers, but only on the total compared
+ * length.
+ */
+static inline unsigned int const_time_eq_bin(const void *a, const void *b,
+ size_t len)
+{
+ const u8 *aa = a;
+ const u8 *bb = b;
+ size_t i;
+ u8 res = 0;
+
+ for (i = 0; i < len; i++)
+ res |= aa[i] ^ bb[i];
+
+ return const_time_is_zero(res);
+}
+
+
+/**
+ * const_time_select - Constant time unsigned int selection
+ * @mask: 0 (false) or -1 (true) to identify which value to select
+ * @true_val: Value to select for the true case
+ * @false_val: Value to select for the false case
+ * Returns: true_val if mask == -1, false_val if mask == 0
+ */
+static inline unsigned int const_time_select(unsigned int mask,
+ unsigned int true_val,
+ unsigned int false_val)
+{
+ return (mask & true_val) | (~mask & false_val);
+}
+
+
+/**
+ * const_time_select_int - Constant time int selection
+ * @mask: 0 (false) or -1 (true) to identify which value to select
+ * @true_val: Value to select for the true case
+ * @false_val: Value to select for the false case
+ * Returns: true_val if mask == -1, false_val if mask == 0
+ */
+static inline int const_time_select_int(unsigned int mask, int true_val,
+ int false_val)
+{
+ return (int) const_time_select(mask, (unsigned int) true_val,
+ (unsigned int) false_val);
+}
+
+
+/**
+ * const_time_select_u8 - Constant time u8 selection
+ * @mask: 0 (false) or -1 (true) to identify which value to select
+ * @true_val: Value to select for the true case
+ * @false_val: Value to select for the false case
+ * Returns: true_val if mask == -1, false_val if mask == 0
+ */
+static inline u8 const_time_select_u8(u8 mask, u8 true_val, u8 false_val)
+{
+ return (u8) const_time_select(mask, true_val, false_val);
+}
+
+
+/**
+ * const_time_select_s8 - Constant time s8 selection
+ * @mask: 0 (false) or -1 (true) to identify which value to select
+ * @true_val: Value to select for the true case
+ * @false_val: Value to select for the false case
+ * Returns: true_val if mask == -1, false_val if mask == 0
+ */
+static inline s8 const_time_select_s8(u8 mask, s8 true_val, s8 false_val)
+{
+ return (s8) const_time_select(mask, (unsigned int) true_val,
+ (unsigned int) false_val);
+}
+
+
+/**
+ * const_time_select_bin - Constant time binary buffer selection copy
+ * @mask: 0 (false) or -1 (true) to identify which value to copy
+ * @true_val: Buffer to copy for the true case
+ * @false_val: Buffer to copy for the false case
+ * @len: Number of octets to copy
+ * @dst: Destination buffer for the copy
+ *
+ * This function copies the specified buffer into the destination buffer using
+ * operations with identical memory access pattern regardless of which buffer
+ * is being copied.
+ */
+static inline void const_time_select_bin(u8 mask, const u8 *true_val,
+ const u8 *false_val, size_t len,
+ u8 *dst)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ dst[i] = const_time_select_u8(mask, true_val[i], false_val[i]);
+}
+
+
+static inline int const_time_memcmp(const void *a, const void *b, size_t len)
+{
+ const u8 *aa = a;
+ const u8 *bb = b;
+ int diff, res = 0;
+ unsigned int mask;
+
+ if (len == 0)
+ return 0;
+ do {
+ len--;
+ diff = (int) aa[len] - (int) bb[len];
+ mask = const_time_is_zero((unsigned int) diff);
+ res = const_time_select_int(mask, res, diff);
+ } while (len);
+
+ return res;
+}
+
+#endif /* CONST_TIME_H */
--
2.7.4

View File

@ -0,0 +1,60 @@
From c93461c1d98f52681717a088776ab32fd97872b0 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Fri, 8 Mar 2019 00:24:12 +0200
Subject: [PATCH 03/14] OpenSSL: Use constant time selection for
crypto_bignum_legendre()
Get rid of the branches that depend on the result of the Legendre
operation. This is needed to avoid leaking information about different
temporary results in blinding mechanisms.
This is related to CVE-2019-9494 and CVE-2019-9495.
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
---
src/crypto/crypto_openssl.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index ac53cc8..0f52101 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -24,6 +24,7 @@
#endif /* CONFIG_ECC */
#include "common.h"
+#include "utils/const_time.h"
#include "wpabuf.h"
#include "dh_group5.h"
#include "sha1.h"
@@ -1500,6 +1501,7 @@ int crypto_bignum_legendre(const struct crypto_bignum *a,
BN_CTX *bnctx;
BIGNUM *exp = NULL, *tmp = NULL;
int res = -2;
+ unsigned int mask;
if (TEST_FAIL())
return -2;
@@ -1518,12 +1520,13 @@ int crypto_bignum_legendre(const struct crypto_bignum *a,
(const BIGNUM *) p, bnctx, NULL))
goto fail;
- if (BN_is_word(tmp, 1))
- res = 1;
- else if (BN_is_zero(tmp))
- res = 0;
- else
- res = -1;
+ /* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need to use
+ * constant time selection to avoid branches here. */
+ res = -1;
+ mask = const_time_eq(BN_is_word(tmp, 1), 1);
+ res = const_time_select_int(mask, 1, res);
+ mask = const_time_eq(BN_is_zero(tmp), 1);
+ res = const_time_select_int(mask, 0, res);
fail:
BN_clear_free(tmp);
--
2.7.4

View File

@ -0,0 +1,324 @@
From aaf65feac67c3993935634eefe5bc76b9fce03aa Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Tue, 26 Feb 2019 11:59:45 +0200
Subject: [PATCH 04/14] EAP-pwd: Use constant time and memory access for
finding the PWE
This algorithm could leak information to external observers in form of
timing differences or memory access patterns (cache use). While the
previous implementation had protection against the most visible timing
differences (looping 40 rounds and masking the legendre operation), it
did not protect against memory access patterns between the two possible
code paths in the masking operations. That might be sufficient to allow
an unprivileged process running on the same device to be able to
determine which path is being executed through a cache attack and based
on that, determine information about the used password.
Convert the PWE finding loop to use constant time functions and
identical memory access path without different branches for the QR/QNR
cases to minimize possible side-channel information similarly to the
changes done for SAE authentication. (CVE-2019-9495)
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
---
src/eap_common/eap_pwd_common.c | 187 +++++++++++++++++++++-------------------
1 file changed, 99 insertions(+), 88 deletions(-)
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 02fe01e..e49aaf8 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -8,11 +8,15 @@
#include "includes.h"
#include "common.h"
+#include "utils/const_time.h"
#include "crypto/sha256.h"
#include "crypto/crypto.h"
#include "eap_defs.h"
#include "eap_pwd_common.h"
+#define MAX_ECC_PRIME_LEN 66
+
+
/* The random function H(x) = HMAC-SHA256(0^32, x) */
struct crypto_hash * eap_pwd_h_init(void)
{
@@ -102,6 +106,15 @@ EAP_PWD_group * get_eap_pwd_group(u16 num)
}
+static void buf_shift_right(u8 *buf, size_t len, size_t bits)
+{
+ size_t i;
+ for (i = len - 1; i > 0; i--)
+ buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits);
+ buf[0] >>= bits;
+}
+
+
/*
* compute a "random" secret point on an elliptic curve based
* on the password and identities.
@@ -113,17 +126,27 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
const u8 *token)
{
struct crypto_bignum *qr = NULL, *qnr = NULL, *one = NULL;
+ struct crypto_bignum *qr_or_qnr = NULL;
+ u8 qr_bin[MAX_ECC_PRIME_LEN];
+ u8 qnr_bin[MAX_ECC_PRIME_LEN];
+ u8 qr_or_qnr_bin[MAX_ECC_PRIME_LEN];
+ u8 x_bin[MAX_ECC_PRIME_LEN];
struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL;
struct crypto_hash *hash;
unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
- int is_odd, ret = 0, check, found = 0;
- size_t primebytelen, primebitlen;
- struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
+ int ret = 0, check, res;
+ u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
+ * mask */
+ size_t primebytelen = 0, primebitlen;
+ struct crypto_bignum *x_candidate = NULL, *cofactor = NULL;
const struct crypto_bignum *prime;
+ u8 mask, found_ctr = 0, is_odd = 0;
if (grp->pwe)
return -1;
+ os_memset(x_bin, 0, sizeof(x_bin));
+
prime = crypto_ec_get_prime(grp->group);
cofactor = crypto_bignum_init();
grp->pwe = crypto_ec_point_init(grp->group);
@@ -152,8 +175,6 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
/* get a random quadratic residue and nonresidue */
while (!qr || !qnr) {
- int res;
-
if (crypto_bignum_rand(tmp1, prime) < 0)
goto fail;
res = crypto_bignum_legendre(tmp1, prime);
@@ -167,6 +188,11 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
if (!tmp1)
goto fail;
}
+ if (crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin),
+ primebytelen) < 0 ||
+ crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin),
+ primebytelen) < 0)
+ goto fail;
os_memset(prfbuf, 0, primebytelen);
ctr = 0;
@@ -194,17 +220,16 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
eap_pwd_h_update(hash, &ctr, sizeof(ctr));
eap_pwd_h_final(hash, pwe_digest);
- crypto_bignum_deinit(rnd, 1);
- rnd = crypto_bignum_init_set(pwe_digest, SHA256_MAC_LEN);
- if (!rnd) {
- wpa_printf(MSG_INFO, "EAP-pwd: unable to create rnd");
- goto fail;
- }
+ is_odd = const_time_select_u8(
+ found, is_odd, pwe_digest[SHA256_MAC_LEN - 1] & 0x01);
if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN,
(u8 *) "EAP-pwd Hunting And Pecking",
os_strlen("EAP-pwd Hunting And Pecking"),
prfbuf, primebitlen) < 0)
goto fail;
+ if (primebitlen % 8)
+ buf_shift_right(prfbuf, primebytelen,
+ 8 - primebitlen % 8);
crypto_bignum_deinit(x_candidate, 1);
x_candidate = crypto_bignum_init_set(prfbuf, primebytelen);
@@ -214,24 +239,13 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
goto fail;
}
- /*
- * eap_pwd_kdf() returns a string of bits 0..primebitlen but
- * BN_bin2bn will treat that string of bits as a big endian
- * number. If the primebitlen is not an even multiple of 8
- * then excessive bits-- those _after_ primebitlen-- so now
- * we have to shift right the amount we masked off.
- */
- if ((primebitlen % 8) &&
- crypto_bignum_rshift(x_candidate,
- (8 - (primebitlen % 8)),
- x_candidate) < 0)
- goto fail;
-
if (crypto_bignum_cmp(x_candidate, prime) >= 0)
continue;
- wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
- prfbuf, primebytelen);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: x_candidate",
+ prfbuf, primebytelen);
+ const_time_select_bin(found, x_bin, prfbuf, primebytelen,
+ x_bin);
/*
* compute y^2 using the equation of the curve
@@ -261,13 +275,15 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
* Flip a coin, multiply by the random quadratic residue or the
* random quadratic nonresidue and record heads or tails.
*/
- if (crypto_bignum_is_odd(tmp1)) {
- crypto_bignum_mulmod(tmp2, qr, prime, tmp2);
- check = 1;
- } else {
- crypto_bignum_mulmod(tmp2, qnr, prime, tmp2);
- check = -1;
- }
+ mask = const_time_eq_u8(crypto_bignum_is_odd(tmp1), 1);
+ check = const_time_select_s8(mask, 1, -1);
+ const_time_select_bin(mask, qr_bin, qnr_bin, primebytelen,
+ qr_or_qnr_bin);
+ crypto_bignum_deinit(qr_or_qnr, 1);
+ qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, primebytelen);
+ if (!qr_or_qnr ||
+ crypto_bignum_mulmod(tmp2, qr_or_qnr, prime, tmp2) < 0)
+ goto fail;
/*
* Now it's safe to do legendre, if check is 1 then it's
@@ -275,59 +291,12 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
* change result), if check is -1 then it's the opposite test
* (multiplying a qr by qnr would make a qnr).
*/
- if (crypto_bignum_legendre(tmp2, prime) == check) {
- if (found == 1)
- continue;
-
- /* need to unambiguously identify the solution */
- is_odd = crypto_bignum_is_odd(rnd);
-
- /*
- * We know x_candidate is a quadratic residue so set
- * it here.
- */
- if (crypto_ec_point_solve_y_coord(grp->group, grp->pwe,
- x_candidate,
- is_odd) != 0) {
- wpa_printf(MSG_INFO,
- "EAP-pwd: Could not solve for y");
- continue;
- }
-
- /*
- * If there's a solution to the equation then the point
- * must be on the curve so why check again explicitly?
- * OpenSSL code says this is required by X9.62. We're
- * not X9.62 but it can't hurt just to be sure.
- */
- if (!crypto_ec_point_is_on_curve(grp->group,
- grp->pwe)) {
- wpa_printf(MSG_INFO,
- "EAP-pwd: point is not on curve");
- continue;
- }
-
- if (!crypto_bignum_is_one(cofactor)) {
- /* make sure the point is not in a small
- * sub-group */
- if (crypto_ec_point_mul(grp->group, grp->pwe,
- cofactor,
- grp->pwe) != 0) {
- wpa_printf(MSG_INFO,
- "EAP-pwd: cannot multiply generator by order");
- continue;
- }
- if (crypto_ec_point_is_at_infinity(grp->group,
- grp->pwe)) {
- wpa_printf(MSG_INFO,
- "EAP-pwd: point is at infinity");
- continue;
- }
- }
- wpa_printf(MSG_DEBUG,
- "EAP-pwd: found a PWE in %d tries", ctr);
- found = 1;
- }
+ res = crypto_bignum_legendre(tmp2, prime);
+ if (res == -2)
+ goto fail;
+ mask = const_time_eq(res, check);
+ found_ctr = const_time_select_u8(found, found_ctr, ctr);
+ found |= mask;
}
if (found == 0) {
wpa_printf(MSG_INFO,
@@ -335,6 +304,44 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
num);
goto fail;
}
+
+ /*
+ * We know x_candidate is a quadratic residue so set it here.
+ */
+ crypto_bignum_deinit(x_candidate, 1);
+ x_candidate = crypto_bignum_init_set(x_bin, primebytelen);
+ if (!x_candidate ||
+ crypto_ec_point_solve_y_coord(grp->group, grp->pwe, x_candidate,
+ is_odd) != 0) {
+ wpa_printf(MSG_INFO, "EAP-pwd: Could not solve for y");
+ goto fail;
+ }
+
+ /*
+ * If there's a solution to the equation then the point must be on the
+ * curve so why check again explicitly? OpenSSL code says this is
+ * required by X9.62. We're not X9.62 but it can't hurt just to be sure.
+ */
+ if (!crypto_ec_point_is_on_curve(grp->group, grp->pwe)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
+ goto fail;
+ }
+
+ if (!crypto_bignum_is_one(cofactor)) {
+ /* make sure the point is not in a small sub-group */
+ if (crypto_ec_point_mul(grp->group, grp->pwe, cofactor,
+ grp->pwe) != 0) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: cannot multiply generator by order");
+ goto fail;
+ }
+ if (crypto_ec_point_is_at_infinity(grp->group, grp->pwe)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: point is at infinity");
+ goto fail;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %02d tries", found_ctr);
+
if (0) {
fail:
crypto_ec_point_deinit(grp->pwe, 1);
@@ -344,14 +351,18 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
/* cleanliness and order.... */
crypto_bignum_deinit(cofactor, 1);
crypto_bignum_deinit(x_candidate, 1);
- crypto_bignum_deinit(rnd, 1);
crypto_bignum_deinit(pm1, 0);
crypto_bignum_deinit(tmp1, 1);
crypto_bignum_deinit(tmp2, 1);
crypto_bignum_deinit(qr, 1);
crypto_bignum_deinit(qnr, 1);
+ crypto_bignum_deinit(qr_or_qnr, 1);
crypto_bignum_deinit(one, 0);
- os_free(prfbuf);
+ bin_clear_free(prfbuf, primebytelen);
+ os_memset(qr_bin, 0, sizeof(qr_bin));
+ os_memset(qnr_bin, 0, sizeof(qnr_bin));
+ os_memset(qr_or_qnr_bin, 0, sizeof(qr_or_qnr_bin));
+ os_memset(pwe_digest, 0, sizeof(pwe_digest));
return ret;
}
--
2.7.4

View File

@ -0,0 +1,241 @@
From 6513db3e96c43c2e36805cf5ead349765d18eaf7 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Tue, 26 Feb 2019 13:05:09 +0200
Subject: [PATCH 05/14] SAE: Minimize timing differences in PWE derivation
The QR test result can provide information about the password to an
attacker, so try to minimize differences in how the
sae_test_pwd_seed_ecc() result is used. (CVE-2019-9494)
Use heap memory for the dummy password to allow the same password length
to be used even with long passwords.
Use constant time selection functions to track the real vs. dummy
variables so that the exact same operations can be performed for both QR
test results.
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
---
src/common/sae.c | 106 ++++++++++++++++++++++++++++++-------------------------
1 file changed, 57 insertions(+), 49 deletions(-)
diff --git a/src/common/sae.c b/src/common/sae.c
index 8129a7c..d55323b 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -9,6 +9,7 @@
#include "includes.h"
#include "common.h"
+#include "utils/const_time.h"
#include "crypto/crypto.h"
#include "crypto/sha256.h"
#include "crypto/random.h"
@@ -292,15 +293,12 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
const u8 *prime,
const struct crypto_bignum *qr,
const struct crypto_bignum *qnr,
- struct crypto_bignum **ret_x_cand)
+ u8 *pwd_value)
{
- u8 pwd_value[SAE_MAX_ECC_PRIME_LEN];
struct crypto_bignum *y_sqr, *x_cand;
int res;
size_t bits;
- *ret_x_cand = NULL;
-
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
/* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
@@ -309,7 +307,7 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
prime, sae->tmp->prime_len, pwd_value, bits) < 0)
return -1;
if (bits % 8)
- buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
+ buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8);
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
pwd_value, sae->tmp->prime_len);
@@ -320,20 +318,13 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
if (!x_cand)
return -1;
y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand);
- if (!y_sqr) {
- crypto_bignum_deinit(x_cand, 1);
+ crypto_bignum_deinit(x_cand, 1);
+ if (!y_sqr)
return -1;
- }
res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr);
crypto_bignum_deinit(y_sqr, 1);
- if (res <= 0) {
- crypto_bignum_deinit(x_cand, 1);
- return res;
- }
-
- *ret_x_cand = x_cand;
- return 1;
+ return res;
}
@@ -454,25 +445,30 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
const u8 *addr[3];
size_t len[3];
size_t num_elem;
- u8 dummy_password[32];
- size_t dummy_password_len;
+ u8 *dummy_password, *tmp_password;
int pwd_seed_odd = 0;
u8 prime[SAE_MAX_ECC_PRIME_LEN];
size_t prime_len;
- struct crypto_bignum *x = NULL, *qr, *qnr;
+ struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL;
+ u8 x_bin[SAE_MAX_ECC_PRIME_LEN];
+ u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
size_t bits;
- int res;
+ int res = -1;
+ u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
+ * mask */
- dummy_password_len = password_len;
- if (dummy_password_len > sizeof(dummy_password))
- dummy_password_len = sizeof(dummy_password);
- if (random_get_bytes(dummy_password, dummy_password_len) < 0)
- return -1;
+ os_memset(x_bin, 0, sizeof(x_bin));
+
+ dummy_password = os_malloc(password_len);
+ tmp_password = os_malloc(password_len);
+ if (!dummy_password || !tmp_password ||
+ random_get_bytes(dummy_password, password_len) < 0)
+ goto fail;
prime_len = sae->tmp->prime_len;
if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
prime_len) < 0)
- return -1;
+ goto fail;
bits = crypto_ec_prime_len_bits(sae->tmp->ec);
/*
@@ -481,7 +477,7 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
*/
if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
&qr, &qnr) < 0)
- return -1;
+ goto fail;
wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
password, password_len);
@@ -497,7 +493,7 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
*/
sae_pwd_seed_key(addr1, addr2, addrs);
- addr[0] = password;
+ addr[0] = tmp_password;
len[0] = password_len;
num_elem = 1;
if (identifier) {
@@ -514,9 +510,8 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
* attacks that attempt to determine the number of iterations required
* in the loop.
*/
- for (counter = 1; counter <= k || !x; counter++) {
+ for (counter = 1; counter <= k || !found; counter++) {
u8 pwd_seed[SHA256_MAC_LEN];
- struct crypto_bignum *x_cand;
if (counter > 200) {
/* This should not happen in practice */
@@ -524,36 +519,45 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
break;
}
- wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+ wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter);
+ const_time_select_bin(found, dummy_password, password,
+ password_len, tmp_password);
if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
addr, len, pwd_seed) < 0)
break;
res = sae_test_pwd_seed_ecc(sae, pwd_seed,
- prime, qr, qnr, &x_cand);
+ prime, qr, qnr, x_cand_bin);
+ const_time_select_bin(found, x_bin, x_cand_bin, prime_len,
+ x_bin);
+ pwd_seed_odd = const_time_select_u8(
+ found, pwd_seed_odd,
+ pwd_seed[SHA256_MAC_LEN - 1] & 0x01);
+ os_memset(pwd_seed, 0, sizeof(pwd_seed));
if (res < 0)
goto fail;
- if (res > 0 && !x) {
- wpa_printf(MSG_DEBUG,
- "SAE: Selected pwd-seed with counter %u",
- counter);
- x = x_cand;
- pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
- os_memset(pwd_seed, 0, sizeof(pwd_seed));
+ /* Need to minimize differences in handling res == 0 and 1 here
+ * to avoid differences in timing and instruction cache access,
+ * so use const_time_select_*() to make local copies of the
+ * values based on whether this loop iteration was the one that
+ * found the pwd-seed/x. */
+
+ /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them
+ * (with res converted to 0/0xff) handles this in constant time.
+ */
+ found |= res * 0xff;
+ wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x",
+ res, found);
+ }
- /*
- * Use a dummy password for the following rounds, if
- * any.
- */
- addr[0] = dummy_password;
- len[0] = dummy_password_len;
- } else if (res > 0) {
- crypto_bignum_deinit(x_cand, 1);
- }
+ if (!found) {
+ wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
+ res = -1;
+ goto fail;
}
+ x = crypto_bignum_init_set(x_bin, prime_len);
if (!x) {
- wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
res = -1;
goto fail;
}
@@ -566,7 +570,6 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
sae->tmp->pwe_ecc, x,
pwd_seed_odd);
- crypto_bignum_deinit(x, 1);
if (res < 0) {
/*
* This should not happen since we already checked that there
@@ -578,6 +581,11 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
fail:
crypto_bignum_deinit(qr, 0);
crypto_bignum_deinit(qnr, 0);
+ os_free(dummy_password);
+ bin_clear_free(tmp_password, password_len);
+ crypto_bignum_deinit(x, 1);
+ os_memset(x_bin, 0, sizeof(x_bin));
+ os_memset(x_cand_bin, 0, sizeof(x_cand_bin));
return res;
}
--
2.7.4

View File

@ -0,0 +1,144 @@
From 362704dda04507e7ebb8035122e83d9f0ae7c320 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Tue, 26 Feb 2019 19:34:38 +0200
Subject: [PATCH 06/14] SAE: Avoid branches in is_quadratic_residue_blind()
Make the non-failure path in the function proceed without branches based
on r_odd and in constant time to minimize risk of observable differences
in timing or cache use. (CVE-2019-9494)
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
---
src/common/sae.c | 64 ++++++++++++++++++++++++++++++++------------------------
1 file changed, 37 insertions(+), 27 deletions(-)
diff --git a/src/common/sae.c b/src/common/sae.c
index d55323b..5df9b95 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -232,12 +232,14 @@ get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits,
static int is_quadratic_residue_blind(struct sae_data *sae,
const u8 *prime, size_t bits,
- const struct crypto_bignum *qr,
- const struct crypto_bignum *qnr,
+ const u8 *qr, const u8 *qnr,
const struct crypto_bignum *y_sqr)
{
- struct crypto_bignum *r, *num;
+ struct crypto_bignum *r, *num, *qr_or_qnr = NULL;
int r_odd, check, res = -1;
+ u8 qr_or_qnr_bin[SAE_MAX_ECC_PRIME_LEN];
+ size_t prime_len = sae->tmp->prime_len;
+ unsigned int mask;
/*
* Use the blinding technique to mask y_sqr while determining
@@ -248,7 +250,7 @@ static int is_quadratic_residue_blind(struct sae_data *sae,
* r = a random number between 1 and p-1, inclusive
* num = (v * r * r) modulo p
*/
- r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd);
+ r = get_rand_1_to_p_1(prime, prime_len, bits, &r_odd);
if (!r)
return -1;
@@ -258,41 +260,45 @@ static int is_quadratic_residue_blind(struct sae_data *sae,
crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0)
goto fail;
- if (r_odd) {
- /*
- * num = (num * qr) module p
- * LGR(num, p) = 1 ==> quadratic residue
- */
- if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0)
- goto fail;
- check = 1;
- } else {
- /*
- * num = (num * qnr) module p
- * LGR(num, p) = -1 ==> quadratic residue
- */
- if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0)
- goto fail;
- check = -1;
- }
+ /*
+ * Need to minimize differences in handling different cases, so try to
+ * avoid branches and timing differences.
+ *
+ * If r_odd:
+ * num = (num * qr) module p
+ * LGR(num, p) = 1 ==> quadratic residue
+ * else:
+ * num = (num * qnr) module p
+ * LGR(num, p) = -1 ==> quadratic residue
+ */
+ mask = const_time_is_zero(r_odd);
+ const_time_select_bin(mask, qnr, qr, prime_len, qr_or_qnr_bin);
+ qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, prime_len);
+ if (!qr_or_qnr ||
+ crypto_bignum_mulmod(num, qr_or_qnr, sae->tmp->prime, num) < 0)
+ goto fail;
+ /* r_odd is 0 or 1; branchless version of check = r_odd ? 1 : -1, */
+ check = const_time_select_int(mask, -1, 1);
res = crypto_bignum_legendre(num, sae->tmp->prime);
if (res == -2) {
res = -1;
goto fail;
}
- res = res == check;
+ /* branchless version of res = res == check
+ * (res is -1, 0, or 1; check is -1 or 1) */
+ mask = const_time_eq(res, check);
+ res = const_time_select_int(mask, 1, 0);
fail:
crypto_bignum_deinit(num, 1);
crypto_bignum_deinit(r, 1);
+ crypto_bignum_deinit(qr_or_qnr, 1);
return res;
}
static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
- const u8 *prime,
- const struct crypto_bignum *qr,
- const struct crypto_bignum *qnr,
+ const u8 *prime, const u8 *qr, const u8 *qnr,
u8 *pwd_value)
{
struct crypto_bignum *y_sqr, *x_cand;
@@ -452,6 +458,8 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL;
u8 x_bin[SAE_MAX_ECC_PRIME_LEN];
u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
+ u8 qr_bin[SAE_MAX_ECC_PRIME_LEN];
+ u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN];
size_t bits;
int res = -1;
u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
@@ -476,7 +484,9 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
* (qnr) modulo p for blinding purposes during the loop.
*/
if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
- &qr, &qnr) < 0)
+ &qr, &qnr) < 0 ||
+ crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 ||
+ crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0)
goto fail;
wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
@@ -527,7 +537,7 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
break;
res = sae_test_pwd_seed_ecc(sae, pwd_seed,
- prime, qr, qnr, x_cand_bin);
+ prime, qr_bin, qnr_bin, x_cand_bin);
const_time_select_bin(found, x_bin, x_cand_bin, prime_len,
x_bin);
pwd_seed_odd = const_time_select_u8(
--
2.7.4

View File

@ -0,0 +1,118 @@
From 90839597cc4016b33f00055b12d59174c62770a3 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Sat, 2 Mar 2019 12:24:09 +0200
Subject: [PATCH 07/14] SAE: Mask timing of MODP groups 22, 23, 24
These groups have significant probability of coming up with pwd-value
that is equal or greater than the prime and as such, need for going
through the PWE derivation loop multiple times. This can result in
sufficient timing different to allow an external observer to determine
how many rounds are needed and that can leak information about the used
password.
Force at least 40 loop rounds for these MODP groups similarly to the ECC
group design to mask timing. This behavior is not described in IEEE Std
802.11-2016 for SAE, but it does not result in different values (i.e.,
only different timing), so such implementation specific countermeasures
can be done without breaking interoperability with other implementation.
Note: These MODP groups 22, 23, and 24 are not considered sufficiently
strong to be used with SAE (or more or less anything else). As such,
they should never be enabled in runtime configuration for any production
use cases. These changes to introduce additional protection to mask
timing is only for completeness of implementation and not an indication
that these groups should be used.
This is related to CVE-2019-9494.
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
---
src/common/sae.c | 38 ++++++++++++++++++++++++++++----------
1 file changed, 28 insertions(+), 10 deletions(-)
diff --git a/src/common/sae.c b/src/common/sae.c
index 5df9b95..75b1b4a 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -601,22 +601,27 @@ fail:
}
+static int sae_modp_group_require_masking(int group)
+{
+ /* Groups for which pwd-value is likely to be >= p frequently */
+ return group == 22 || group == 23 || group == 24;
+}
+
+
static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
const u8 *addr2, const u8 *password,
size_t password_len, const char *identifier)
{
- u8 counter;
+ u8 counter, k;
u8 addrs[2 * ETH_ALEN];
const u8 *addr[3];
size_t len[3];
size_t num_elem;
int found = 0;
+ struct crypto_bignum *pwe = NULL;
- if (sae->tmp->pwe_ffc == NULL) {
- sae->tmp->pwe_ffc = crypto_bignum_init();
- if (sae->tmp->pwe_ffc == NULL)
- return -1;
- }
+ crypto_bignum_deinit(sae->tmp->pwe_ffc, 1);
+ sae->tmp->pwe_ffc = NULL;
wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
password, password_len);
@@ -640,7 +645,9 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
len[num_elem] = sizeof(counter);
num_elem++;
- for (counter = 1; !found; counter++) {
+ k = sae_modp_group_require_masking(sae->group) ? 40 : 1;
+
+ for (counter = 1; counter <= k || !found; counter++) {
u8 pwd_seed[SHA256_MAC_LEN];
int res;
@@ -650,19 +657,30 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
break;
}
- wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+ wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter);
if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
addr, len, pwd_seed) < 0)
break;
- res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc);
+ if (!pwe) {
+ pwe = crypto_bignum_init();
+ if (!pwe)
+ break;
+ }
+ res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe);
if (res < 0)
break;
if (res > 0) {
- wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
found = 1;
+ if (!sae->tmp->pwe_ffc) {
+ wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
+ sae->tmp->pwe_ffc = pwe;
+ pwe = NULL;
+ }
}
}
+ crypto_bignum_deinit(pwe, 1);
+
return found ? 0 : -1;
}
--
2.7.4

View File

@ -0,0 +1,105 @@
From f8f20717f87eff1f025f48ed585c7684debacf72 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Sat, 2 Mar 2019 12:45:33 +0200
Subject: [PATCH 08/14] SAE: Use const_time selection for PWE in FFC
This is an initial step towards making the FFC case use strictly
constant time operations similarly to the ECC case.
sae_test_pwd_seed_ffc() does not yet have constant time behavior,
though.
This is related to CVE-2019-9494.
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
---
src/common/sae.c | 53 +++++++++++++++++++++++++++++++++++------------------
1 file changed, 35 insertions(+), 18 deletions(-)
diff --git a/src/common/sae.c b/src/common/sae.c
index 75b1b4a..fa9a145 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -612,17 +612,28 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
const u8 *addr2, const u8 *password,
size_t password_len, const char *identifier)
{
- u8 counter, k;
+ u8 counter, k, sel_counter = 0;
u8 addrs[2 * ETH_ALEN];
const u8 *addr[3];
size_t len[3];
size_t num_elem;
- int found = 0;
- struct crypto_bignum *pwe = NULL;
+ u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
+ * mask */
+ u8 mask;
+ struct crypto_bignum *pwe;
+ size_t prime_len = sae->tmp->prime_len * 8;
+ u8 *pwe_buf;
crypto_bignum_deinit(sae->tmp->pwe_ffc, 1);
sae->tmp->pwe_ffc = NULL;
+ /* Allocate a buffer to maintain selected and candidate PWE for constant
+ * time selection. */
+ pwe_buf = os_zalloc(prime_len * 2);
+ pwe = crypto_bignum_init();
+ if (!pwe_buf || !pwe)
+ goto fail;
+
wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
password, password_len);
@@ -661,27 +672,33 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
addr, len, pwd_seed) < 0)
break;
- if (!pwe) {
- pwe = crypto_bignum_init();
- if (!pwe)
- break;
- }
res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe);
+ /* res is -1 for fatal failure, 0 if a valid PWE was not found,
+ * or 1 if a valid PWE was found. */
if (res < 0)
break;
- if (res > 0) {
- found = 1;
- if (!sae->tmp->pwe_ffc) {
- wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
- sae->tmp->pwe_ffc = pwe;
- pwe = NULL;
- }
- }
+ /* Store the candidate PWE into the second half of pwe_buf and
+ * the selected PWE in the beginning of pwe_buf using constant
+ * time selection. */
+ if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len,
+ prime_len) < 0)
+ break;
+ const_time_select_bin(found, pwe_buf, pwe_buf + prime_len,
+ prime_len, pwe_buf);
+ sel_counter = const_time_select_u8(found, sel_counter, counter);
+ mask = const_time_eq_u8(res, 1);
+ found = const_time_select_u8(found, found, mask);
}
- crypto_bignum_deinit(pwe, 1);
+ if (!found)
+ goto fail;
- return found ? 0 : -1;
+ wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter);
+ sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len);
+fail:
+ crypto_bignum_deinit(pwe, 1);
+ bin_clear_free(pwe_buf, prime_len * 2);
+ return sae->tmp->pwe_ffc ? 0 : -1;
}
--
2.7.4

View File

@ -0,0 +1,136 @@
From cff138b0747fa39765cbc641b66cfa5d7f1735d1 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Sat, 2 Mar 2019 16:05:56 +0200
Subject: [PATCH 09/14] SAE: Use constant time operations in
sae_test_pwd_seed_ffc()
Try to avoid showing externally visible timing or memory access
differences regardless of whether the derived pwd-value is smaller than
the group prime.
This is related to CVE-2019-9494.
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
---
src/common/sae.c | 75 ++++++++++++++++++++++++++++++++++----------------------
1 file changed, 46 insertions(+), 29 deletions(-)
diff --git a/src/common/sae.c b/src/common/sae.c
index fa9a145..eaf825d 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -334,14 +334,17 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
}
+/* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided
+ * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */
static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
struct crypto_bignum *pwe)
{
u8 pwd_value[SAE_MAX_PRIME_LEN];
size_t bits = sae->tmp->prime_len * 8;
u8 exp[1];
- struct crypto_bignum *a, *b;
- int res;
+ struct crypto_bignum *a, *b = NULL;
+ int res, is_val;
+ u8 pwd_value_valid;
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
@@ -353,16 +356,29 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
sae->tmp->prime_len);
- if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0)
- {
- wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p");
- return 0;
- }
+ /* Check whether pwd-value < p */
+ res = const_time_memcmp(pwd_value, sae->tmp->dh->prime,
+ sae->tmp->prime_len);
+ /* pwd-value >= p is invalid, so res is < 0 for the valid cases and
+ * the negative sign can be used to fill the mask for constant time
+ * selection */
+ pwd_value_valid = const_time_fill_msb(res);
+
+ /* If pwd-value >= p, force pwd-value to be < p and perform the
+ * calculations anyway to hide timing difference. The derived PWE will
+ * be ignored in that case. */
+ pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0);
/* PWE = pwd-value^((p-1)/r) modulo p */
+ res = -1;
a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
+ if (!a)
+ goto fail;
+ /* This is an optimization based on the used group that does not depend
+ * on the password in any way, so it is fine to use separate branches
+ * for this step without constant time operations. */
if (sae->tmp->dh->safe_prime) {
/*
* r = (p-1)/2 for the group used here, so this becomes:
@@ -376,33 +392,34 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
b = crypto_bignum_init_set(exp, sizeof(exp));
if (b == NULL ||
crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
- crypto_bignum_div(b, sae->tmp->order, b) < 0) {
- crypto_bignum_deinit(b, 0);
- b = NULL;
- }
+ crypto_bignum_div(b, sae->tmp->order, b) < 0)
+ goto fail;
}
- if (a == NULL || b == NULL)
- res = -1;
- else
- res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
-
- crypto_bignum_deinit(a, 0);
- crypto_bignum_deinit(b, 0);
+ if (!b)
+ goto fail;
- if (res < 0) {
- wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
- return -1;
- }
+ res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
+ if (res < 0)
+ goto fail;
- /* if (PWE > 1) --> found */
- if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
- wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
- return 0;
- }
+ /* There were no fatal errors in calculations, so determine the return
+ * value using constant time operations. We get here for number of
+ * invalid cases which are cleared here after having performed all the
+ * computation. PWE is valid if pwd-value was less than prime and
+ * PWE > 1. Start with pwd-value check first and then use constant time
+ * operations to clear res to 0 if PWE is 0 or 1.
+ */
+ res = const_time_select_u8(pwd_value_valid, 1, 0);
+ is_val = crypto_bignum_is_zero(pwe);
+ res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
+ is_val = crypto_bignum_is_one(pwe);
+ res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
- wpa_printf(MSG_DEBUG, "SAE: PWE found");
- return 1;
+fail:
+ crypto_bignum_deinit(a, 1);
+ crypto_bignum_deinit(b, 1);
+ return res;
}
--
2.7.4

View File

@ -0,0 +1,57 @@
From ac8fa9ef198640086cf2ce7c94673be2b6a018a0 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Tue, 5 Mar 2019 23:43:25 +0200
Subject: [PATCH 10/14] SAE: Fix confirm message validation in error cases
Explicitly verify that own and peer commit scalar/element are available
when trying to check SAE confirm message. It could have been possible to
hit a NULL pointer dereference if the peer element could not have been
parsed. (CVE-2019-9496)
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
---
src/common/sae.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/src/common/sae.c b/src/common/sae.c
index eaf825d..5a50294 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -1487,23 +1487,31 @@ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
- if (sae->tmp == NULL) {
+ if (!sae->tmp || !sae->peer_commit_scalar ||
+ !sae->tmp->own_commit_scalar) {
wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available");
return -1;
}
- if (sae->tmp->ec)
+ if (sae->tmp->ec) {
+ if (!sae->tmp->peer_commit_element_ecc ||
+ !sae->tmp->own_commit_element_ecc)
+ return -1;
sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
sae->tmp->peer_commit_element_ecc,
sae->tmp->own_commit_scalar,
sae->tmp->own_commit_element_ecc,
verifier);
- else
+ } else {
+ if (!sae->tmp->peer_commit_element_ffc ||
+ !sae->tmp->own_commit_element_ffc)
+ return -1;
sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar,
sae->tmp->peer_commit_element_ffc,
sae->tmp->own_commit_scalar,
sae->tmp->own_commit_element_ffc,
verifier);
+ }
if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) {
wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
--
2.7.4

View File

@ -0,0 +1,58 @@
From 70ff850e89fbc8bc7da515321b4d15b5eef70581 Mon Sep 17 00:00:00 2001
From: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
Date: Sun, 31 Mar 2019 17:13:06 +0200
Subject: [PATCH 11/14] EAP-pwd server: Verify received scalar and element
When processing an EAP-pwd Commit frame, the peer's scalar and element
(elliptic curve point) were not validated. This allowed an adversary to
bypass authentication, and impersonate any user if the crypto
implementation did not verify the validity of the EC point.
Fix this vulnerability by assuring the received scalar lies within the
valid range, and by checking that the received element is not the point
at infinity and lies on the elliptic curve being used. (CVE-2019-9498)
The vulnerability is only exploitable if OpenSSL version 1.0.2 or lower
is used, or if LibreSSL or wolfssl is used. Newer versions of OpenSSL
(and also BoringSSL) implicitly validate the elliptic curve point in
EC_POINT_set_affine_coordinates_GFp(), preventing the attack.
Signed-off-by: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
---
src/eap_server/eap_server_pwd.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index d0fa54a..74979da 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -718,6 +718,26 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
goto fin;
}
+ /* verify received scalar */
+ if (crypto_bignum_is_zero(data->peer_scalar) ||
+ crypto_bignum_is_one(data->peer_scalar) ||
+ crypto_bignum_cmp(data->peer_scalar,
+ crypto_ec_get_order(data->grp->group)) >= 0) {
+ wpa_printf(MSG_INFO,
+ "EAP-PWD (server): received scalar is invalid");
+ goto fin;
+ }
+
+ /* verify received element */
+ if (!crypto_ec_point_is_on_curve(data->grp->group,
+ data->peer_element) ||
+ crypto_ec_point_is_at_infinity(data->grp->group,
+ data->peer_element)) {
+ wpa_printf(MSG_INFO,
+ "EAP-PWD (server): received element is invalid");
+ goto fin;
+ }
+
/* check to ensure peer's element is not in a small sub-group */
if (!crypto_bignum_is_one(cofactor)) {
if (crypto_ec_point_mul(data->grp->group, data->peer_element,
--
2.7.4

View File

@ -0,0 +1,45 @@
From d63edfa90243e9a7de6ae5c275032f2cc79fef95 Mon Sep 17 00:00:00 2001
From: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
Date: Sun, 31 Mar 2019 17:26:01 +0200
Subject: [PATCH 12/14] EAP-pwd server: Detect reflection attacks
When processing an EAP-pwd Commit frame, verify that the peer's scalar
and elliptic curve element differ from the one sent by the server. This
prevents reflection attacks where the adversary reflects the scalar and
element sent by the server. (CVE-2019-9497)
The vulnerability allows an adversary to complete the EAP-pwd handshake
as any user. However, the adversary does not learn the negotiated
session key, meaning the subsequent 4-way handshake would fail. As a
result, this cannot be abused to bypass authentication unless EAP-pwd is
used in non-WLAN cases without any following key exchange that would
require the attacker to learn the MSK.
Signed-off-by: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
---
src/eap_server/eap_server_pwd.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index 74979da..16057e9 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -753,6 +753,15 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
}
}
+ /* detect reflection attacks */
+ if (crypto_bignum_cmp(data->my_scalar, data->peer_scalar) == 0 ||
+ crypto_ec_point_cmp(data->grp->group, data->my_element,
+ data->peer_element) == 0) {
+ wpa_printf(MSG_INFO,
+ "EAP-PWD (server): detected reflection attack!");
+ goto fin;
+ }
+
/* compute the shared key, k */
if ((crypto_ec_point_mul(data->grp->group, data->grp->pwe,
data->peer_scalar, K) < 0) ||
--
2.7.4

View File

@ -0,0 +1,58 @@
From 8ad8585f91823ddcc3728155e288e0f9f872e31a Mon Sep 17 00:00:00 2001
From: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
Date: Sun, 31 Mar 2019 17:43:44 +0200
Subject: [PATCH 13/14] EAP-pwd client: Verify received scalar and element
When processing an EAP-pwd Commit frame, the server's scalar and element
(elliptic curve point) were not validated. This allowed an adversary to
bypass authentication, and act as a rogue Access Point (AP) if the
crypto implementation did not verify the validity of the EC point.
Fix this vulnerability by assuring the received scalar lies within the
valid range, and by checking that the received element is not the point
at infinity and lies on the elliptic curve being used. (CVE-2019-9499)
The vulnerability is only exploitable if OpenSSL version 1.0.2 or lower
is used, or if LibreSSL or wolfssl is used. Newer versions of OpenSSL
(and also BoringSSL) implicitly validate the elliptic curve point in
EC_POINT_set_affine_coordinates_GFp(), preventing the attack.
Signed-off-by: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
---
src/eap_peer/eap_pwd.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index 761c16a..5a05e54 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -594,6 +594,26 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
goto fin;
}
+ /* verify received scalar */
+ if (crypto_bignum_is_zero(data->server_scalar) ||
+ crypto_bignum_is_one(data->server_scalar) ||
+ crypto_bignum_cmp(data->server_scalar,
+ crypto_ec_get_order(data->grp->group)) >= 0) {
+ wpa_printf(MSG_INFO,
+ "EAP-PWD (peer): received scalar is invalid");
+ goto fin;
+ }
+
+ /* verify received element */
+ if (!crypto_ec_point_is_on_curve(data->grp->group,
+ data->server_element) ||
+ crypto_ec_point_is_at_infinity(data->grp->group,
+ data->server_element)) {
+ wpa_printf(MSG_INFO,
+ "EAP-PWD (peer): received element is invalid");
+ goto fin;
+ }
+
/* check to ensure server's element is not in a small sub-group */
if (!crypto_bignum_is_one(cofactor)) {
if (crypto_ec_point_mul(data->grp->group, data->server_element,
--
2.7.4

View File

@ -0,0 +1,331 @@
From 16d4f1069118aa19bfce013493e1ac5783f92f1d Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni@codeaurora.org>
Date: Fri, 5 Apr 2019 02:12:50 +0300
Subject: [PATCH 14/14] EAP-pwd: Check element x,y coordinates explicitly
This adds an explicit check for 0 < x,y < prime based on RFC 5931,
2.8.5.2.2 requirement. The earlier checks might have covered this
implicitly, but it is safer to avoid any dependency on implicit checks
and specific crypto library behavior. (CVE-2019-9498 and CVE-2019-9499)
Furthermore, this moves the EAP-pwd element and scalar parsing and
validation steps into shared helper functions so that there is no need
to maintain two separate copies of this common functionality between the
server and peer implementations.
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
---
src/eap_common/eap_pwd_common.c | 106 ++++++++++++++++++++++++++++++++++++++++
src/eap_common/eap_pwd_common.h | 3 ++
src/eap_peer/eap_pwd.c | 45 ++---------------
src/eap_server/eap_server_pwd.c | 45 ++---------------
4 files changed, 117 insertions(+), 82 deletions(-)
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index e49aaf8..c28b56d 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -428,3 +428,109 @@ int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k,
return 1;
}
+
+
+static int eap_pwd_element_coord_ok(const struct crypto_bignum *prime,
+ const u8 *buf, size_t len)
+{
+ struct crypto_bignum *val;
+ int ok = 1;
+
+ val = crypto_bignum_init_set(buf, len);
+ if (!val || crypto_bignum_is_zero(val) ||
+ crypto_bignum_cmp(val, prime) >= 0)
+ ok = 0;
+ crypto_bignum_deinit(val, 0);
+ return ok;
+}
+
+
+struct crypto_ec_point * eap_pwd_get_element(EAP_PWD_group *group,
+ const u8 *buf)
+{
+ struct crypto_ec_point *element;
+ const struct crypto_bignum *prime;
+ size_t prime_len;
+ struct crypto_bignum *cofactor = NULL;
+
+ prime = crypto_ec_get_prime(group->group);
+ prime_len = crypto_ec_prime_len(group->group);
+
+ /* RFC 5931, 2.8.5.2.2: 0 < x,y < p */
+ if (!eap_pwd_element_coord_ok(prime, buf, prime_len) ||
+ !eap_pwd_element_coord_ok(prime, buf + prime_len, prime_len)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: Invalid coordinate in element");
+ return NULL;
+ }
+
+ element = crypto_ec_point_from_bin(group->group, buf);
+ if (!element) {
+ wpa_printf(MSG_INFO, "EAP-pwd: EC point from element failed");
+ return NULL;
+ }
+
+ /* RFC 5931, 2.8.5.2.2: on curve and not the point at infinity */
+ if (!crypto_ec_point_is_on_curve(group->group, element) ||
+ crypto_ec_point_is_at_infinity(group->group, element)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: Invalid element");
+ goto fail;
+ }
+
+ cofactor = crypto_bignum_init();
+ if (!cofactor || crypto_ec_cofactor(group->group, cofactor) < 0) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: Unable to get cofactor for curve");
+ goto fail;
+ }
+
+ if (!crypto_bignum_is_one(cofactor)) {
+ struct crypto_ec_point *point;
+ int ok = 1;
+
+ /* check to ensure peer's element is not in a small sub-group */
+ point = crypto_ec_point_init(group->group);
+ if (!point ||
+ crypto_ec_point_mul(group->group, element,
+ cofactor, point) != 0 ||
+ crypto_ec_point_is_at_infinity(group->group, point))
+ ok = 0;
+ crypto_ec_point_deinit(point, 0);
+
+ if (!ok) {
+ wpa_printf(MSG_INFO,
+ "EAP-pwd: Small sub-group check on peer element failed");
+ goto fail;
+ }
+ }
+
+out:
+ crypto_bignum_deinit(cofactor, 0);
+ return element;
+fail:
+ crypto_ec_point_deinit(element, 0);
+ element = NULL;
+ goto out;
+}
+
+
+struct crypto_bignum * eap_pwd_get_scalar(EAP_PWD_group *group, const u8 *buf)
+{
+ struct crypto_bignum *scalar;
+ const struct crypto_bignum *order;
+ size_t order_len;
+
+ order = crypto_ec_get_order(group->group);
+ order_len = crypto_ec_order_len(group->group);
+
+ /* RFC 5931, 2.8.5.2: 1 < scalar < r */
+ scalar = crypto_bignum_init_set(buf, order_len);
+ if (!scalar || crypto_bignum_is_zero(scalar) ||
+ crypto_bignum_is_one(scalar) ||
+ crypto_bignum_cmp(scalar, order) >= 0) {
+ wpa_printf(MSG_INFO, "EAP-pwd: received scalar is invalid");
+ crypto_bignum_deinit(scalar, 0);
+ scalar = NULL;
+ }
+
+ return scalar;
+}
diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h
index 6b07cf8..2387e59 100644
--- a/src/eap_common/eap_pwd_common.h
+++ b/src/eap_common/eap_pwd_common.h
@@ -67,5 +67,8 @@ int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k,
struct crypto_hash * eap_pwd_h_init(void);
void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len);
void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest);
+struct crypto_ec_point * eap_pwd_get_element(EAP_PWD_group *group,
+ const u8 *buf);
+struct crypto_bignum * eap_pwd_get_scalar(EAP_PWD_group *group, const u8 *buf);
#endif /* EAP_PWD_COMMON_H */
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index 5a05e54..f37b974 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -308,7 +308,7 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
const struct wpabuf *reqData,
const u8 *payload, size_t payload_len)
{
- struct crypto_ec_point *K = NULL, *point = NULL;
+ struct crypto_ec_point *K = NULL;
struct crypto_bignum *mask = NULL, *cofactor = NULL;
const u8 *ptr = payload;
u8 *scalar = NULL, *element = NULL;
@@ -572,63 +572,27 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
/* process the request */
data->k = crypto_bignum_init();
K = crypto_ec_point_init(data->grp->group);
- point = crypto_ec_point_init(data->grp->group);
- if (!data->k || !K || !point) {
+ if (!data->k || !K) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation "
"fail");
goto fin;
}
/* element, x then y, followed by scalar */
- data->server_element = crypto_ec_point_from_bin(data->grp->group, ptr);
+ data->server_element = eap_pwd_get_element(data->grp, ptr);
if (!data->server_element) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element "
"fail");
goto fin;
}
ptr += prime_len * 2;
- data->server_scalar = crypto_bignum_init_set(ptr, order_len);
+ data->server_scalar = eap_pwd_get_scalar(data->grp, ptr);
if (!data->server_scalar) {
wpa_printf(MSG_INFO,
"EAP-PWD (peer): setting peer scalar fail");
goto fin;
}
- /* verify received scalar */
- if (crypto_bignum_is_zero(data->server_scalar) ||
- crypto_bignum_is_one(data->server_scalar) ||
- crypto_bignum_cmp(data->server_scalar,
- crypto_ec_get_order(data->grp->group)) >= 0) {
- wpa_printf(MSG_INFO,
- "EAP-PWD (peer): received scalar is invalid");
- goto fin;
- }
-
- /* verify received element */
- if (!crypto_ec_point_is_on_curve(data->grp->group,
- data->server_element) ||
- crypto_ec_point_is_at_infinity(data->grp->group,
- data->server_element)) {
- wpa_printf(MSG_INFO,
- "EAP-PWD (peer): received element is invalid");
- goto fin;
- }
-
- /* check to ensure server's element is not in a small sub-group */
- if (!crypto_bignum_is_one(cofactor)) {
- if (crypto_ec_point_mul(data->grp->group, data->server_element,
- cofactor, point) < 0) {
- wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
- "server element by order!\n");
- goto fin;
- }
- if (crypto_ec_point_is_at_infinity(data->grp->group, point)) {
- wpa_printf(MSG_INFO, "EAP-PWD (peer): server element "
- "is at infinity!\n");
- goto fin;
- }
- }
-
/* compute the shared key, k */
if (crypto_ec_point_mul(data->grp->group, data->grp->pwe,
data->server_scalar, K) < 0 ||
@@ -702,7 +666,6 @@ fin:
crypto_bignum_deinit(mask, 1);
crypto_bignum_deinit(cofactor, 1);
crypto_ec_point_deinit(K, 1);
- crypto_ec_point_deinit(point, 1);
if (data->outbuf == NULL)
eap_pwd_state(data, FAILURE);
else
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index 16057e9..f6c75cf 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -669,7 +669,7 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
{
const u8 *ptr;
struct crypto_bignum *cofactor = NULL;
- struct crypto_ec_point *K = NULL, *point = NULL;
+ struct crypto_ec_point *K = NULL;
int res = 0;
size_t prime_len, order_len;
@@ -688,9 +688,8 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
data->k = crypto_bignum_init();
cofactor = crypto_bignum_init();
- point = crypto_ec_point_init(data->grp->group);
K = crypto_ec_point_init(data->grp->group);
- if (!data->k || !cofactor || !point || !K) {
+ if (!data->k || !cofactor || !K) {
wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
"fail");
goto fin;
@@ -704,55 +703,20 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
/* element, x then y, followed by scalar */
ptr = payload;
- data->peer_element = crypto_ec_point_from_bin(data->grp->group, ptr);
+ data->peer_element = eap_pwd_get_element(data->grp, ptr);
if (!data->peer_element) {
wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element "
"fail");
goto fin;
}
ptr += prime_len * 2;
- data->peer_scalar = crypto_bignum_init_set(ptr, order_len);
+ data->peer_scalar = eap_pwd_get_scalar(data->grp, ptr);
if (!data->peer_scalar) {
wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
"fail");
goto fin;
}
- /* verify received scalar */
- if (crypto_bignum_is_zero(data->peer_scalar) ||
- crypto_bignum_is_one(data->peer_scalar) ||
- crypto_bignum_cmp(data->peer_scalar,
- crypto_ec_get_order(data->grp->group)) >= 0) {
- wpa_printf(MSG_INFO,
- "EAP-PWD (server): received scalar is invalid");
- goto fin;
- }
-
- /* verify received element */
- if (!crypto_ec_point_is_on_curve(data->grp->group,
- data->peer_element) ||
- crypto_ec_point_is_at_infinity(data->grp->group,
- data->peer_element)) {
- wpa_printf(MSG_INFO,
- "EAP-PWD (server): received element is invalid");
- goto fin;
- }
-
- /* check to ensure peer's element is not in a small sub-group */
- if (!crypto_bignum_is_one(cofactor)) {
- if (crypto_ec_point_mul(data->grp->group, data->peer_element,
- cofactor, point) != 0) {
- wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
- "multiply peer element by order");
- goto fin;
- }
- if (crypto_ec_point_is_at_infinity(data->grp->group, point)) {
- wpa_printf(MSG_INFO, "EAP-PWD (server): peer element "
- "is at infinity!\n");
- goto fin;
- }
- }
-
/* detect reflection attacks */
if (crypto_bignum_cmp(data->my_scalar, data->peer_scalar) == 0 ||
crypto_ec_point_cmp(data->grp->group, data->my_element,
@@ -804,7 +768,6 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
fin:
crypto_ec_point_deinit(K, 1);
- crypto_ec_point_deinit(point, 1);
crypto_bignum_deinit(cofactor, 1);
if (res)
--
2.7.4

View File

@ -1,8 +1,8 @@
%global _hardened_build 1
Name: hostapd
Version: 2.6
Release: 12%{?dist}
Version: 2.7
Release: 1%{?dist}
Summary: IEEE 802.11 AP, IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator
License: BSD
URL: http://w1.fi/hostapd
@ -13,16 +13,27 @@ Source2: %{name}.conf
Source3: %{name}.sysconfig
Source4: %{name}.init
# https://w1.fi/security/2017-1/wpa-packet-number-reuse-with-replayed-messages.txt
Patch1: https://w1.fi/security/2017-1/rebased-v2.6-0001-hostapd-Avoid-key-reinstallation-in-FT-handshake.patch
Patch2: https://w1.fi/security/2017-1/rebased-v2.6-0002-Prevent-reinstallation-of-an-already-in-use-group-ke.patch
Patch3: https://w1.fi/security/2017-1/rebased-v2.6-0003-Extend-protection-of-GTK-IGTK-reinstallation-of-WNM-.patch
Patch4: https://w1.fi/security/2017-1/rebased-v2.6-0004-Prevent-installation-of-an-all-zero-TK.patch
Patch5: https://w1.fi/security/2017-1/rebased-v2.6-0005-Fix-PTK-rekeying-to-generate-a-new-ANonce.patch
Patch6: https://w1.fi/security/2017-1/rebased-v2.6-0006-TDLS-Reject-TPK-TK-reconfiguration.patch
Patch8: https://w1.fi/security/2017-1/rebased-v2.6-0008-FT-Do-not-allow-multiple-Reassociation-Response-fram.patch
# https://w1.fi/security/2019-1/sae-side-channel-attacks.txt
Patch1: https://w1.fi/security/2019-1/0001-OpenSSL-Use-constant-time-operations-for-private-big.patch
Patch2: https://w1.fi/security/2019-1/0002-Add-helper-functions-for-constant-time-operations.patch
Patch3: https://w1.fi/security/2019-1/0003-OpenSSL-Use-constant-time-selection-for-crypto_bignu.patch
Patch4: https://w1.fi/security/2019-1/0005-SAE-Minimize-timing-differences-in-PWE-derivation.patch
Patch5: https://w1.fi/security/2019-1/0006-SAE-Avoid-branches-in-is_quadratic_residue_blind.patch
Patch6: https://w1.fi/security/2019-1/0007-SAE-Mask-timing-of-MODP-groups-22-23-24.patch
Patch7: https://w1.fi/security/2019-1/0008-SAE-Use-const_time-selection-for-PWE-in-FFC.patch
Patch8: https://w1.fi/security/2019-1/0009-SAE-Use-constant-time-operations-in-sae_test_pwd_see.patch
Patch9: nl80211-Fix-NL80211_ATTR_SMPS_MODE-encoding.patch
# https://w1.fi/security/2019-2/eap-pwd-side-channel-attack.txt
Patch9: https://w1.fi/security/2019-2/0004-EAP-pwd-Use-constant-time-and-memory-access-for-find.patch
# https://w1.fi/security/2019-3/sae-confirm-missing-state-validation.txt
Patch10: https://w1.fi/security/2019-3/0010-SAE-Fix-confirm-message-validation-in-error-cases.patch
# https://w1.fi/security/2019-4/eap-pwd-missing-commit-validation.txt
Patch11: https://w1.fi/security/2019-4/0011-EAP-pwd-server-Verify-received-scalar-and-element.patch
Patch12: https://w1.fi/security/2019-4/0012-EAP-pwd-server-Detect-reflection-attacks.patch
Patch13: https://w1.fi/security/2019-4/0013-EAP-pwd-client-Verify-received-scalar-and-element.patch
Patch14: https://w1.fi/security/2019-4/0014-EAP-pwd-Check-element-x-y-coordinates-explicitly.patch
BuildRequires: libnl3-devel
BuildRequires: openssl-devel
@ -74,8 +85,14 @@ Logwatch scripts for hostapd.
%patch4 -p1
%patch5 -p1
%patch6 -p1
%patch7 -p1
%patch8 -p1
%patch9 -p1
%patch10 -p1
%patch11 -p1
%patch12 -p1
%patch13 -p1
%patch14 -p1
%build
cd hostapd
@ -195,6 +212,16 @@ fi
%{_sysconfdir}/logwatch/scripts/services/%{name}
%changelog
* Fri Apr 12 2019 John W. Linville <linville@redhat.com> - 2.7-1
- Update to version 2.6 from upstream
- Remove obsolete patches for NL80211_ATTR_SMPS_MODE encoding and KRACK
- Fix CVE-2019-9494 (cache attack against SAE)
- Fix CVE-2019-9495 (cache attack against EAP-pwd)
- Fix CVE-2019-9496 (SAE confirm missing state validation in hostapd/AP)
- Fix CVE-2019-9497 (EAP-pwd server not checking for reflection attack)
- Fix CVE-2019-9498 (EAP-pwd server missing commit validation for scalar/element)
- Fix CVE-2019-9499 (EAP-pwd peer missing commit validation for scalar/element)
* Fri Feb 01 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2.6-12
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild

View File

@ -1,38 +0,0 @@
From a2426829ce426de82d2fa47071ca41ea81c43307 Mon Sep 17 00:00:00 2001
Message-Id: <a2426829ce426de82d2fa47071ca41ea81c43307.1527581614.git.davide.caratti@gmail.com>
From: Jouni Malinen <jouni@qca.qualcomm.com>
Date: Fri, 1 Dec 2017 20:22:44 +0200
Subject: [PATCH] nl80211: Fix NL80211_ATTR_SMPS_MODE encoding
This nl80211 attribute uses NLA_U8 policy in cfg80211 and
hostapd/wpa_supplicant needs to use same size when writing the
attribute.
This fixes AP mode regression triggered by kernel commit "net: netlink:
Update attr validation to require exact length for some types" in
v4.15-rc1 that resulted in the following debug log entry when trying to
enable beaconing:
nl80211: Beacon set failed: -34 (Numerical result out of range)
Fixes: da1080d7215f ("nl80211: Advertise and configure SMPS modes")
Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
---
src/drivers/driver_nl80211.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 2ce03ed76..7571e77f0 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -3957,7 +3957,7 @@ static int wpa_driver_nl80211_set_ap(void *priv,
smps_mode = NL80211_SMPS_OFF;
break;
}
- if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode))
+ if (nla_put_u8(msg, NL80211_ATTR_SMPS_MODE, smps_mode))
goto fail;
}
--
2.17.0

View File

@ -1,174 +0,0 @@
From cf4cab804c7afd5c45505528a8d16e46163243a2 Mon Sep 17 00:00:00 2001
From: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
Date: Fri, 14 Jul 2017 15:15:35 +0200
Subject: [PATCH 1/8] hostapd: Avoid key reinstallation in FT handshake
Do not reinstall TK to the driver during Reassociation Response frame
processing if the first attempt of setting the TK succeeded. This avoids
issues related to clearing the TX/RX PN that could result in reusing
same PN values for transmitted frames (e.g., due to CCM nonce reuse and
also hitting replay protection on the receiver) and accepting replayed
frames on RX side.
This issue was introduced by the commit
0e84c25434e6a1f283c7b4e62e483729085b78d2 ('FT: Fix PTK configuration in
authenticator') which allowed wpa_ft_install_ptk() to be called multiple
times with the same PTK. While the second configuration attempt is
needed with some drivers, it must be done only if the first attempt
failed.
Signed-off-by: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
---
src/ap/ieee802_11.c | 16 +++++++++++++---
src/ap/wpa_auth.c | 11 +++++++++++
src/ap/wpa_auth.h | 3 ++-
src/ap/wpa_auth_ft.c | 10 ++++++++++
src/ap/wpa_auth_i.h | 1 +
5 files changed, 37 insertions(+), 4 deletions(-)
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 4e04169..333035f 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1841,6 +1841,7 @@ static int add_associated_sta(struct hostapd_data *hapd,
{
struct ieee80211_ht_capabilities ht_cap;
struct ieee80211_vht_capabilities vht_cap;
+ int set = 1;
/*
* Remove the STA entry to ensure the STA PS state gets cleared and
@@ -1848,9 +1849,18 @@ static int add_associated_sta(struct hostapd_data *hapd,
* FT-over-the-DS, where a station re-associates back to the same AP but
* skips the authentication flow, or if working with a driver that
* does not support full AP client state.
+ *
+ * Skip this if the STA has already completed FT reassociation and the
+ * TK has been configured since the TX/RX PN must not be reset to 0 for
+ * the same key.
*/
- if (!sta->added_unassoc)
+ if (!sta->added_unassoc &&
+ (!(sta->flags & WLAN_STA_AUTHORIZED) ||
+ !wpa_auth_sta_ft_tk_already_set(sta->wpa_sm))) {
hostapd_drv_sta_remove(hapd, sta->addr);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
+ set = 0;
+ }
#ifdef CONFIG_IEEE80211N
if (sta->flags & WLAN_STA_HT)
@@ -1873,11 +1883,11 @@ static int add_associated_sta(struct hostapd_data *hapd,
sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
sta->vht_opmode, sta->p2p_ie ? 1 : 0,
- sta->added_unassoc)) {
+ set)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
"Could not %s STA to kernel driver",
- sta->added_unassoc ? "set" : "add");
+ set ? "set" : "add");
if (sta->added_unassoc) {
hostapd_drv_sta_remove(hapd, sta->addr);
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 3587086..707971d 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -1745,6 +1745,9 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
#else /* CONFIG_IEEE80211R */
break;
#endif /* CONFIG_IEEE80211R */
+ case WPA_DRV_STA_REMOVED:
+ sm->tk_already_set = FALSE;
+ return 0;
}
#ifdef CONFIG_IEEE80211R
@@ -3250,6 +3253,14 @@ int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm)
}
+int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm)
+{
+ if (!sm || !wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+ return 0;
+ return sm->tk_already_set;
+}
+
+
int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
struct rsn_pmksa_cache_entry *entry)
{
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 0de8d97..97461b0 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -267,7 +267,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
u8 *data, size_t data_len);
enum wpa_event {
WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
- WPA_REAUTH_EAPOL, WPA_ASSOC_FT
+ WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_DRV_STA_REMOVED
};
void wpa_remove_ptk(struct wpa_state_machine *sm);
int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event);
@@ -280,6 +280,7 @@ int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
+int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm);
int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
struct rsn_pmksa_cache_entry *entry);
struct rsn_pmksa_cache_entry *
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 42242a5..e63b99a 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -780,6 +780,14 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm)
return;
}
+ if (sm->tk_already_set) {
+ /* Must avoid TK reconfiguration to prevent clearing of TX/RX
+ * PN in the driver */
+ wpa_printf(MSG_DEBUG,
+ "FT: Do not re-install same PTK to the driver");
+ return;
+ }
+
/* FIX: add STA entry to kernel/driver here? The set_key will fail
* most likely without this.. At the moment, STA entry is added only
* after association has been completed. This function will be called
@@ -792,6 +800,7 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm)
/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
sm->pairwise_set = TRUE;
+ sm->tk_already_set = TRUE;
}
@@ -898,6 +907,7 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
sm->pairwise = pairwise;
sm->PTK_valid = TRUE;
+ sm->tk_already_set = FALSE;
wpa_ft_install_ptk(sm);
buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 72b7eb3..7fd8f05 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -65,6 +65,7 @@ struct wpa_state_machine {
struct wpa_ptk PTK;
Boolean PTK_valid;
Boolean pairwise_set;
+ Boolean tk_already_set;
int keycount;
Boolean Pair;
struct wpa_key_replay_counter {
--
2.7.4

View File

@ -1,250 +0,0 @@
From 927f891007c402fefd1ff384645b3f07597c3ede Mon Sep 17 00:00:00 2001
From: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
Date: Wed, 12 Jul 2017 16:03:24 +0200
Subject: [PATCH 2/8] Prevent reinstallation of an already in-use group key
Track the current GTK and IGTK that is in use and when receiving a
(possibly retransmitted) Group Message 1 or WNM-Sleep Mode Response, do
not install the given key if it is already in use. This prevents an
attacker from trying to trick the client into resetting or lowering the
sequence counter associated to the group key.
Signed-off-by: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
---
src/common/wpa_common.h | 11 +++++
src/rsn_supp/wpa.c | 116 ++++++++++++++++++++++++++++++------------------
src/rsn_supp/wpa_i.h | 4 ++
3 files changed, 87 insertions(+), 44 deletions(-)
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index af1d0f0..d200285 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -217,6 +217,17 @@ struct wpa_ptk {
size_t tk_len;
};
+struct wpa_gtk {
+ u8 gtk[WPA_GTK_MAX_LEN];
+ size_t gtk_len;
+};
+
+#ifdef CONFIG_IEEE80211W
+struct wpa_igtk {
+ u8 igtk[WPA_IGTK_MAX_LEN];
+ size_t igtk_len;
+};
+#endif /* CONFIG_IEEE80211W */
/* WPA IE version 1
* 00-50-f2:1 (OUI:OUI type)
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 3c47879..95bd7be 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -714,6 +714,15 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
const u8 *_gtk = gd->gtk;
u8 gtk_buf[32];
+ /* Detect possible key reinstallation */
+ if (sm->gtk.gtk_len == (size_t) gd->gtk_len &&
+ os_memcmp(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len) == 0) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Not reinstalling already in-use GTK to the driver (keyidx=%d tx=%d len=%d)",
+ gd->keyidx, gd->tx, gd->gtk_len);
+ return 0;
+ }
+
wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)",
@@ -748,6 +757,9 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
}
os_memset(gtk_buf, 0, sizeof(gtk_buf));
+ sm->gtk.gtk_len = gd->gtk_len;
+ os_memcpy(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len);
+
return 0;
}
@@ -854,6 +866,48 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
}
+#ifdef CONFIG_IEEE80211W
+static int wpa_supplicant_install_igtk(struct wpa_sm *sm,
+ const struct wpa_igtk_kde *igtk)
+{
+ size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+ u16 keyidx = WPA_GET_LE16(igtk->keyid);
+
+ /* Detect possible key reinstallation */
+ if (sm->igtk.igtk_len == len &&
+ os_memcmp(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len) == 0) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Not reinstalling already in-use IGTK to the driver (keyidx=%d)",
+ keyidx);
+ return 0;
+ }
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: IGTK keyid %d pn %02x%02x%02x%02x%02x%02x",
+ keyidx, MAC2STR(igtk->pn));
+ wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", igtk->igtk, len);
+ if (keyidx > 4095) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid IGTK KeyID %d", keyidx);
+ return -1;
+ }
+ if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+ broadcast_ether_addr,
+ keyidx, 0, igtk->pn, sizeof(igtk->pn),
+ igtk->igtk, len) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to configure IGTK to the driver");
+ return -1;
+ }
+
+ sm->igtk.igtk_len = len;
+ os_memcpy(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len);
+
+ return 0;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
static int ieee80211w_set_keys(struct wpa_sm *sm,
struct wpa_eapol_ie_parse *ie)
{
@@ -864,30 +918,14 @@ static int ieee80211w_set_keys(struct wpa_sm *sm,
if (ie->igtk) {
size_t len;
const struct wpa_igtk_kde *igtk;
- u16 keyidx;
+
len = wpa_cipher_key_len(sm->mgmt_group_cipher);
if (ie->igtk_len != WPA_IGTK_KDE_PREFIX_LEN + len)
return -1;
+
igtk = (const struct wpa_igtk_kde *) ie->igtk;
- keyidx = WPA_GET_LE16(igtk->keyid);
- wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: IGTK keyid %d "
- "pn %02x%02x%02x%02x%02x%02x",
- keyidx, MAC2STR(igtk->pn));
- wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK",
- igtk->igtk, len);
- if (keyidx > 4095) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Invalid IGTK KeyID %d", keyidx);
- return -1;
- }
- if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
- broadcast_ether_addr,
- keyidx, 0, igtk->pn, sizeof(igtk->pn),
- igtk->igtk, len) < 0) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Failed to configure IGTK to the driver");
+ if (wpa_supplicant_install_igtk(sm, igtk) < 0)
return -1;
- }
}
return 0;
@@ -2307,7 +2345,7 @@ void wpa_sm_deinit(struct wpa_sm *sm)
*/
void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
{
- int clear_ptk = 1;
+ int clear_keys = 1;
if (sm == NULL)
return;
@@ -2333,11 +2371,11 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
/* Prepare for the next transition */
wpa_ft_prepare_auth_request(sm, NULL);
- clear_ptk = 0;
+ clear_keys = 0;
}
#endif /* CONFIG_IEEE80211R */
- if (clear_ptk) {
+ if (clear_keys) {
/*
* IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if
* this is not part of a Fast BSS Transition.
@@ -2347,6 +2385,10 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
os_memset(&sm->ptk, 0, sizeof(sm->ptk));
sm->tptk_set = 0;
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+ os_memset(&sm->gtk, 0, sizeof(sm->gtk));
+#ifdef CONFIG_IEEE80211W
+ os_memset(&sm->igtk, 0, sizeof(sm->igtk));
+#endif /* CONFIG_IEEE80211W */
}
#ifdef CONFIG_TDLS
@@ -2877,6 +2919,10 @@ void wpa_sm_drop_sa(struct wpa_sm *sm)
os_memset(sm->pmk, 0, sizeof(sm->pmk));
os_memset(&sm->ptk, 0, sizeof(sm->ptk));
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+ os_memset(&sm->gtk, 0, sizeof(sm->gtk));
+#ifdef CONFIG_IEEE80211W
+ os_memset(&sm->igtk, 0, sizeof(sm->igtk));
+#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_IEEE80211R
os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0));
@@ -2949,29 +2995,11 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
os_memset(&gd, 0, sizeof(gd));
#ifdef CONFIG_IEEE80211W
} else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) {
- struct wpa_igtk_kde igd;
- u16 keyidx;
-
- os_memset(&igd, 0, sizeof(igd));
- keylen = wpa_cipher_key_len(sm->mgmt_group_cipher);
- os_memcpy(igd.keyid, buf + 2, 2);
- os_memcpy(igd.pn, buf + 4, 6);
-
- keyidx = WPA_GET_LE16(igd.keyid);
- os_memcpy(igd.igtk, buf + 10, keylen);
-
- wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)",
- igd.igtk, keylen);
- if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
- broadcast_ether_addr,
- keyidx, 0, igd.pn, sizeof(igd.pn),
- igd.igtk, keylen) < 0) {
- wpa_printf(MSG_DEBUG, "Failed to install the IGTK in "
- "WNM mode");
- os_memset(&igd, 0, sizeof(igd));
+ const struct wpa_igtk_kde *igtk;
+
+ igtk = (const struct wpa_igtk_kde *) (buf + 2);
+ if (wpa_supplicant_install_igtk(sm, igtk) < 0)
return -1;
- }
- os_memset(&igd, 0, sizeof(igd));
#endif /* CONFIG_IEEE80211W */
} else {
wpa_printf(MSG_DEBUG, "Unknown element id");
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index f653ba6..afc9e37 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -31,6 +31,10 @@ struct wpa_sm {
u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN];
int rx_replay_counter_set;
u8 request_counter[WPA_REPLAY_COUNTER_LEN];
+ struct wpa_gtk gtk;
+#ifdef CONFIG_IEEE80211W
+ struct wpa_igtk igtk;
+#endif /* CONFIG_IEEE80211W */
struct eapol_sm *eapol; /* EAPOL state machine from upper level code */
--
2.7.4

View File

@ -1,184 +0,0 @@
From 8280294e74846ea342389a0cd17215050fa5afe8 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Sun, 1 Oct 2017 12:12:24 +0300
Subject: [PATCH 3/8] Extend protection of GTK/IGTK reinstallation of WNM-Sleep
Mode cases
This extends the protection to track last configured GTK/IGTK value
separately from EAPOL-Key frames and WNM-Sleep Mode frames to cover a
corner case where these two different mechanisms may get used when the
GTK/IGTK has changed and tracking a single value is not sufficient to
detect a possible key reconfiguration.
Signed-off-by: Jouni Malinen <j@w1.fi>
---
src/rsn_supp/wpa.c | 53 +++++++++++++++++++++++++++++++++++++---------------
src/rsn_supp/wpa_i.h | 2 ++
2 files changed, 40 insertions(+), 15 deletions(-)
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 95bd7be..7a2c68d 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -709,14 +709,17 @@ struct wpa_gtk_data {
static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
const struct wpa_gtk_data *gd,
- const u8 *key_rsc)
+ const u8 *key_rsc, int wnm_sleep)
{
const u8 *_gtk = gd->gtk;
u8 gtk_buf[32];
/* Detect possible key reinstallation */
- if (sm->gtk.gtk_len == (size_t) gd->gtk_len &&
- os_memcmp(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len) == 0) {
+ if ((sm->gtk.gtk_len == (size_t) gd->gtk_len &&
+ os_memcmp(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len) == 0) ||
+ (sm->gtk_wnm_sleep.gtk_len == (size_t) gd->gtk_len &&
+ os_memcmp(sm->gtk_wnm_sleep.gtk, gd->gtk,
+ sm->gtk_wnm_sleep.gtk_len) == 0)) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Not reinstalling already in-use GTK to the driver (keyidx=%d tx=%d len=%d)",
gd->keyidx, gd->tx, gd->gtk_len);
@@ -757,8 +760,14 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
}
os_memset(gtk_buf, 0, sizeof(gtk_buf));
- sm->gtk.gtk_len = gd->gtk_len;
- os_memcpy(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len);
+ if (wnm_sleep) {
+ sm->gtk_wnm_sleep.gtk_len = gd->gtk_len;
+ os_memcpy(sm->gtk_wnm_sleep.gtk, gd->gtk,
+ sm->gtk_wnm_sleep.gtk_len);
+ } else {
+ sm->gtk.gtk_len = gd->gtk_len;
+ os_memcpy(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len);
+ }
return 0;
}
@@ -852,7 +861,7 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
(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_rsc))) {
+ wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0))) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: Failed to install GTK");
os_memset(&gd, 0, sizeof(gd));
@@ -868,14 +877,18 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
#ifdef CONFIG_IEEE80211W
static int wpa_supplicant_install_igtk(struct wpa_sm *sm,
- const struct wpa_igtk_kde *igtk)
+ const struct wpa_igtk_kde *igtk,
+ int wnm_sleep)
{
size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher);
u16 keyidx = WPA_GET_LE16(igtk->keyid);
/* Detect possible key reinstallation */
- if (sm->igtk.igtk_len == len &&
- os_memcmp(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len) == 0) {
+ if ((sm->igtk.igtk_len == len &&
+ os_memcmp(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len) == 0) ||
+ (sm->igtk_wnm_sleep.igtk_len == len &&
+ os_memcmp(sm->igtk_wnm_sleep.igtk, igtk->igtk,
+ sm->igtk_wnm_sleep.igtk_len) == 0)) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Not reinstalling already in-use IGTK to the driver (keyidx=%d)",
keyidx);
@@ -900,8 +913,14 @@ static int wpa_supplicant_install_igtk(struct wpa_sm *sm,
return -1;
}
- sm->igtk.igtk_len = len;
- os_memcpy(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len);
+ if (wnm_sleep) {
+ sm->igtk_wnm_sleep.igtk_len = len;
+ os_memcpy(sm->igtk_wnm_sleep.igtk, igtk->igtk,
+ sm->igtk_wnm_sleep.igtk_len);
+ } else {
+ sm->igtk.igtk_len = len;
+ os_memcpy(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len);
+ }
return 0;
}
@@ -924,7 +943,7 @@ static int ieee80211w_set_keys(struct wpa_sm *sm,
return -1;
igtk = (const struct wpa_igtk_kde *) ie->igtk;
- if (wpa_supplicant_install_igtk(sm, igtk) < 0)
+ if (wpa_supplicant_install_igtk(sm, igtk, 0) < 0)
return -1;
}
@@ -1574,7 +1593,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
key_rsc = null_rsc;
- if (wpa_supplicant_install_gtk(sm, &gd, key_rsc) ||
+ if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0) ||
wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0)
goto failed;
os_memset(&gd, 0, sizeof(gd));
@@ -2386,8 +2405,10 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
sm->tptk_set = 0;
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
os_memset(&sm->gtk, 0, sizeof(sm->gtk));
+ os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
#ifdef CONFIG_IEEE80211W
os_memset(&sm->igtk, 0, sizeof(sm->igtk));
+ os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
#endif /* CONFIG_IEEE80211W */
}
@@ -2920,8 +2941,10 @@ void wpa_sm_drop_sa(struct wpa_sm *sm)
os_memset(&sm->ptk, 0, sizeof(sm->ptk));
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
os_memset(&sm->gtk, 0, sizeof(sm->gtk));
+ os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
#ifdef CONFIG_IEEE80211W
os_memset(&sm->igtk, 0, sizeof(sm->igtk));
+ os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_IEEE80211R
os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
@@ -2986,7 +3009,7 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)",
gd.gtk, gd.gtk_len);
- if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) {
+ if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 1)) {
os_memset(&gd, 0, sizeof(gd));
wpa_printf(MSG_DEBUG, "Failed to install the GTK in "
"WNM mode");
@@ -2998,7 +3021,7 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
const struct wpa_igtk_kde *igtk;
igtk = (const struct wpa_igtk_kde *) (buf + 2);
- if (wpa_supplicant_install_igtk(sm, igtk) < 0)
+ if (wpa_supplicant_install_igtk(sm, igtk, 1) < 0)
return -1;
#endif /* CONFIG_IEEE80211W */
} else {
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index afc9e37..9a54631 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -32,8 +32,10 @@ struct wpa_sm {
int rx_replay_counter_set;
u8 request_counter[WPA_REPLAY_COUNTER_LEN];
struct wpa_gtk gtk;
+ struct wpa_gtk gtk_wnm_sleep;
#ifdef CONFIG_IEEE80211W
struct wpa_igtk igtk;
+ struct wpa_igtk igtk_wnm_sleep;
#endif /* CONFIG_IEEE80211W */
struct eapol_sm *eapol; /* EAPOL state machine from upper level code */
--
2.7.4

View File

@ -1,79 +0,0 @@
From 8f82bc94e8697a9d47fa8774dfdaaede1084912c Mon Sep 17 00:00:00 2001
From: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
Date: Fri, 29 Sep 2017 04:22:51 +0200
Subject: [PATCH 4/8] Prevent installation of an all-zero TK
Properly track whether a PTK has already been installed to the driver
and the TK part cleared from memory. This prevents an attacker from
trying to trick the client into installing an all-zero TK.
This fixes the earlier fix in commit
ad00d64e7d8827b3cebd665a0ceb08adabf15e1e ('Fix TK configuration to the
driver in EAPOL-Key 3/4 retry case') which did not take into account
possibility of an extra message 1/4 showing up between retries of
message 3/4.
Signed-off-by: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
---
src/common/wpa_common.h | 1 +
src/rsn_supp/wpa.c | 5 ++---
src/rsn_supp/wpa_i.h | 1 -
3 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index d200285..1021ccb 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -215,6 +215,7 @@ struct wpa_ptk {
size_t kck_len;
size_t kek_len;
size_t tk_len;
+ int installed; /* 1 if key has already been installed to driver */
};
struct wpa_gtk {
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 7a2c68d..0550a41 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -510,7 +510,6 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
os_memset(buf, 0, sizeof(buf));
}
sm->tptk_set = 1;
- sm->tk_to_set = 1;
kde = sm->assoc_wpa_ie;
kde_len = sm->assoc_wpa_ie_len;
@@ -615,7 +614,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
enum wpa_alg alg;
const u8 *key_rsc;
- if (!sm->tk_to_set) {
+ if (sm->ptk.installed) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Do not re-install same PTK to the driver");
return 0;
@@ -659,7 +658,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
/* TK is not needed anymore in supplicant */
os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
- sm->tk_to_set = 0;
+ sm->ptk.installed = 1;
if (sm->wpa_ptk_rekey) {
eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 9a54631..41f371f 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -24,7 +24,6 @@ struct wpa_sm {
struct wpa_ptk ptk, tptk;
int ptk_set, tptk_set;
unsigned int msg_3_of_4_ok:1;
- unsigned int tk_to_set:1;
u8 snonce[WPA_NONCE_LEN];
u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */
int renew_snonce;
--
2.7.4

View File

@ -1,64 +0,0 @@
From 12fac09b437a1dc8a0f253e265934a8aaf4d2f8b Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Sun, 1 Oct 2017 12:32:57 +0300
Subject: [PATCH 5/8] Fix PTK rekeying to generate a new ANonce
The Authenticator state machine path for PTK rekeying ended up bypassing
the AUTHENTICATION2 state where a new ANonce is generated when going
directly to the PTKSTART state since there is no need to try to
determine the PMK again in such a case. This is far from ideal since the
new PTK would depend on a new nonce only from the supplicant.
Fix this by generating a new ANonce when moving to the PTKSTART state
for the purpose of starting new 4-way handshake to rekey PTK.
Signed-off-by: Jouni Malinen <j@w1.fi>
---
src/ap/wpa_auth.c | 24 +++++++++++++++++++++---
1 file changed, 21 insertions(+), 3 deletions(-)
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 707971d..bf10cc1 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -1901,6 +1901,21 @@ SM_STATE(WPA_PTK, AUTHENTICATION2)
}
+static int wpa_auth_sm_ptk_update(struct wpa_state_machine *sm)
+{
+ if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
+ wpa_printf(MSG_ERROR,
+ "WPA: Failed to get random data for ANonce");
+ sm->Disconnect = TRUE;
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPA: Assign new ANonce", sm->ANonce,
+ WPA_NONCE_LEN);
+ sm->TimeoutCtr = 0;
+ return 0;
+}
+
+
SM_STATE(WPA_PTK, INITPMK)
{
u8 msk[2 * PMK_LEN];
@@ -2458,9 +2473,12 @@ SM_STEP(WPA_PTK)
SM_ENTER(WPA_PTK, AUTHENTICATION);
else if (sm->ReAuthenticationRequest)
SM_ENTER(WPA_PTK, AUTHENTICATION2);
- else if (sm->PTKRequest)
- SM_ENTER(WPA_PTK, PTKSTART);
- else switch (sm->wpa_ptk_state) {
+ else if (sm->PTKRequest) {
+ if (wpa_auth_sm_ptk_update(sm) < 0)
+ SM_ENTER(WPA_PTK, DISCONNECTED);
+ else
+ SM_ENTER(WPA_PTK, PTKSTART);
+ } else switch (sm->wpa_ptk_state) {
case WPA_PTK_INITIALIZE:
break;
case WPA_PTK_DISCONNECT:
--
2.7.4

View File

@ -1,132 +0,0 @@
From 6c4bed4f47d1960ec04981a9d50e5076aea5223d Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Fri, 22 Sep 2017 11:03:15 +0300
Subject: [PATCH 6/8] TDLS: Reject TPK-TK reconfiguration
Do not try to reconfigure the same TPK-TK to the driver after it has
been successfully configured. This is an explicit check to avoid issues
related to resetting the TX/RX packet number. There was already a check
for this for TPK M2 (retries of that message are ignored completely), so
that behavior does not get modified.
For TPK M3, the TPK-TK could have been reconfigured, but that was
followed by immediate teardown of the link due to an issue in updating
the STA entry. Furthermore, for TDLS with any real security (i.e.,
ignoring open/WEP), the TPK message exchange is protected on the AP path
and simple replay attacks are not feasible.
As an additional corner case, make sure the local nonce gets updated if
the peer uses a very unlikely "random nonce" of all zeros.
Signed-off-by: Jouni Malinen <j@w1.fi>
---
src/rsn_supp/tdls.c | 38 ++++++++++++++++++++++++++++++++++++--
1 file changed, 36 insertions(+), 2 deletions(-)
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index e424168..9eb9738 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -112,6 +112,7 @@ struct wpa_tdls_peer {
u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */
} tpk;
int tpk_set;
+ int tk_set; /* TPK-TK configured to the driver */
int tpk_success;
int tpk_in_progress;
@@ -192,6 +193,20 @@ static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
u8 rsc[6];
enum wpa_alg alg;
+ if (peer->tk_set) {
+ /*
+ * This same TPK-TK has already been configured to the driver
+ * and this new configuration attempt (likely due to an
+ * unexpected retransmitted frame) would result in clearing
+ * the TX/RX sequence number which can break security, so must
+ * not allow that to happen.
+ */
+ wpa_printf(MSG_INFO, "TDLS: TPK-TK for the peer " MACSTR
+ " has already been configured to the driver - do not reconfigure",
+ MAC2STR(peer->addr));
+ return -1;
+ }
+
os_memset(rsc, 0, 6);
switch (peer->cipher) {
@@ -209,12 +224,15 @@ static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
return -1;
}
+ wpa_printf(MSG_DEBUG, "TDLS: Configure pairwise key for peer " MACSTR,
+ MAC2STR(peer->addr));
if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1,
rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) {
wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the "
"driver");
return -1;
}
+ peer->tk_set = 1;
return 0;
}
@@ -696,7 +714,7 @@ static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
peer->cipher = 0;
peer->qos_info = 0;
peer->wmm_capable = 0;
- peer->tpk_set = peer->tpk_success = 0;
+ peer->tk_set = peer->tpk_set = peer->tpk_success = 0;
peer->chan_switch_enabled = 0;
os_memset(&peer->tpk, 0, sizeof(peer->tpk));
os_memset(peer->inonce, 0, WPA_NONCE_LEN);
@@ -1159,6 +1177,7 @@ skip_rsnie:
wpa_tdls_peer_free(sm, peer);
return -1;
}
+ peer->tk_set = 0; /* A new nonce results in a new TK */
wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake",
peer->inonce, WPA_NONCE_LEN);
os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
@@ -1751,6 +1770,19 @@ static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
}
+static int tdls_nonce_set(const u8 *nonce)
+{
+ int i;
+
+ for (i = 0; i < WPA_NONCE_LEN; i++) {
+ if (nonce[i])
+ return 1;
+ }
+
+ return 0;
+}
+
+
static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
const u8 *buf, size_t len)
{
@@ -2004,7 +2036,8 @@ skip_rsn:
peer->rsnie_i_len = kde.rsn_ie_len;
peer->cipher = cipher;
- if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0 ||
+ !tdls_nonce_set(peer->inonce)) {
/*
* There is no point in updating the RNonce for every obtained
* TPK M1 frame (e.g., retransmission due to timeout) with the
@@ -2020,6 +2053,7 @@ skip_rsn:
"TDLS: Failed to get random data for responder nonce");
goto error;
}
+ peer->tk_set = 0; /* A new nonce results in a new TK */
}
#if 0
--
2.7.4

View File

@ -1,82 +0,0 @@
From b372ab0b7daea719749194dc554b26e6367603f2 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Fri, 22 Sep 2017 12:06:37 +0300
Subject: [PATCH 8/8] FT: Do not allow multiple Reassociation Response frames
The driver is expected to not report a second association event without
the station having explicitly request a new association. As such, this
case should not be reachable. However, since reconfiguring the same
pairwise or group keys to the driver could result in nonce reuse issues,
be extra careful here and do an additional state check to avoid this
even if the local driver ends up somehow accepting an unexpected
Reassociation Response frame.
Signed-off-by: Jouni Malinen <j@w1.fi>
---
src/rsn_supp/wpa.c | 3 +++
src/rsn_supp/wpa_ft.c | 8 ++++++++
src/rsn_supp/wpa_i.h | 1 +
3 files changed, 12 insertions(+)
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 0550a41..2a53c6f 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -2440,6 +2440,9 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm)
#ifdef CONFIG_TDLS
wpa_tdls_disassoc(sm);
#endif /* CONFIG_TDLS */
+#ifdef CONFIG_IEEE80211R
+ sm->ft_reassoc_completed = 0;
+#endif /* CONFIG_IEEE80211R */
/* Keys are not needed in the WPA state machine anymore */
wpa_sm_drop_sa(sm);
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 205793e..d45bb45 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -153,6 +153,7 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
u16 capab;
sm->ft_completed = 0;
+ sm->ft_reassoc_completed = 0;
buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
2 + sm->r0kh_id_len + ric_ies_len + 100;
@@ -681,6 +682,11 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
return -1;
}
+ if (sm->ft_reassoc_completed) {
+ wpa_printf(MSG_DEBUG, "FT: Reassociation has already been completed for this FT protocol instance - ignore unexpected retransmission");
+ return 0;
+ }
+
if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
return -1;
@@ -781,6 +787,8 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
return -1;
}
+ sm->ft_reassoc_completed = 1;
+
if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0)
return -1;
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 41f371f..56f88dc 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -128,6 +128,7 @@ struct wpa_sm {
size_t r0kh_id_len;
u8 r1kh_id[FT_R1KH_ID_LEN];
int ft_completed;
+ int ft_reassoc_completed;
int over_the_ds_in_progress;
u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */
int set_ptk_after_assoc;
--
2.7.4

View File

@ -1 +1 @@
eaa56dce9bd8f1d195eb62596eab34c7 hostapd-2.6.tar.gz
SHA512 (hostapd-2.7.tar.gz) = 1c9a210dfffb951fb667be19aa44ad8c66dccd2aed26cdab939185923550e3c1998a678ebe6975e560e1b3385bff2098f1b2cb773452ba66fb35246fdd3eb2c1