hostapd: Verify availability of random data when using WPA/WPA2
On Linux, verify that the kernel entropy pool is capable of providing strong random data before allowing WPA/WPA2 connection to be established. If 20 bytes of data cannot be read from /dev/random, force first two 4-way handshakes to fail while collecting entropy into the internal pool in hostapd. After that, give up on /dev/random and allow the AP to function based on the combination of /dev/urandom and whatever data has been collected into the internal entropy pool.
This commit is contained in:
parent
dbb6ed7e75
commit
08704cd885
4 changed files with 118 additions and 1 deletions
|
@ -345,6 +345,12 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
|
||||||
|
|
||||||
wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
|
wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
|
||||||
|
|
||||||
|
if (random_pool_ready() != 1) {
|
||||||
|
wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool "
|
||||||
|
"for secure operations - update keys later when "
|
||||||
|
"the first station connects");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set initial GMK/Counter value here. The actual values that will be
|
* Set initial GMK/Counter value here. The actual values that will be
|
||||||
* used in negotiations will be set once the first station tries to
|
* used in negotiations will be set once the first station tries to
|
||||||
|
@ -825,6 +831,25 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
random_add_randomness(key->key_nonce, WPA_NONCE_LEN);
|
random_add_randomness(key->key_nonce, WPA_NONCE_LEN);
|
||||||
|
if (sm->group->reject_4way_hs_for_entropy) {
|
||||||
|
/*
|
||||||
|
* The system did not have enough entropy to generate
|
||||||
|
* strong random numbers. Reject the first 4-way
|
||||||
|
* handshake(s) and collect some entropy based on the
|
||||||
|
* information from it. Once enough entropy is
|
||||||
|
* available, the next atempt will trigger GMK/Key
|
||||||
|
* Counter update and the station will be allowed to
|
||||||
|
* continue.
|
||||||
|
*/
|
||||||
|
wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to "
|
||||||
|
"collect more entropy for random number "
|
||||||
|
"generation");
|
||||||
|
sm->group->reject_4way_hs_for_entropy = FALSE;
|
||||||
|
random_mark_pool_ready();
|
||||||
|
sm->group->first_sta_seen = FALSE;
|
||||||
|
wpa_sta_disconnect(wpa_auth, sm->addr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length,
|
if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length,
|
||||||
&kde) < 0) {
|
&kde) < 0) {
|
||||||
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
|
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
|
||||||
|
@ -1465,6 +1490,11 @@ static void wpa_group_first_station(struct wpa_authenticator *wpa_auth,
|
||||||
*/
|
*/
|
||||||
wpa_printf(MSG_DEBUG, "WPA: Re-initialize GMK/Counter on first "
|
wpa_printf(MSG_DEBUG, "WPA: Re-initialize GMK/Counter on first "
|
||||||
"station");
|
"station");
|
||||||
|
if (random_pool_ready() != 1) {
|
||||||
|
wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool "
|
||||||
|
"to proceed - reject first 4-way handshake");
|
||||||
|
group->reject_4way_hs_for_entropy = TRUE;
|
||||||
|
}
|
||||||
wpa_group_init_gmk_and_counter(wpa_auth, group);
|
wpa_group_init_gmk_and_counter(wpa_auth, group);
|
||||||
wpa_gtk_update(wpa_auth, group);
|
wpa_gtk_update(wpa_auth, group);
|
||||||
wpa_group_config_group_keys(wpa_auth, group);
|
wpa_group_config_group_keys(wpa_auth, group);
|
||||||
|
|
|
@ -146,6 +146,7 @@ struct wpa_group {
|
||||||
u8 GNonce[WPA_NONCE_LEN];
|
u8 GNonce[WPA_NONCE_LEN];
|
||||||
Boolean changed;
|
Boolean changed;
|
||||||
Boolean first_sta_seen;
|
Boolean first_sta_seen;
|
||||||
|
Boolean reject_4way_hs_for_entropy;
|
||||||
#ifdef CONFIG_IEEE80211W
|
#ifdef CONFIG_IEEE80211W
|
||||||
u8 IGTK[2][WPA_IGTK_LEN];
|
u8 IGTK[2][WPA_IGTK_LEN];
|
||||||
int GN_igtk, GM_igtk;
|
int GN_igtk, GM_igtk;
|
||||||
|
|
|
@ -29,6 +29,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "utils/includes.h"
|
#include "utils/includes.h"
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif /* __linux__ */
|
||||||
|
|
||||||
#include "utils/common.h"
|
#include "utils/common.h"
|
||||||
#include "sha1.h"
|
#include "sha1.h"
|
||||||
|
@ -42,14 +45,18 @@
|
||||||
#define POOL_TAP4 7
|
#define POOL_TAP4 7
|
||||||
#define POOL_TAP5 1
|
#define POOL_TAP5 1
|
||||||
#define EXTRACT_LEN 16
|
#define EXTRACT_LEN 16
|
||||||
|
#define MIN_READY_MARK 2
|
||||||
|
|
||||||
static u32 pool[POOL_WORDS];
|
static u32 pool[POOL_WORDS];
|
||||||
static unsigned int input_rotate = 0;
|
static unsigned int input_rotate = 0;
|
||||||
static unsigned int pool_pos = 0;
|
static unsigned int pool_pos = 0;
|
||||||
static const u8 dummy_key[20];
|
static u8 dummy_key[20];
|
||||||
|
static size_t dummy_key_avail = 0;
|
||||||
|
static unsigned int own_pool_ready = 0;
|
||||||
|
|
||||||
#define MIN_COLLECT_ENTROPY 1000
|
#define MIN_COLLECT_ENTROPY 1000
|
||||||
static unsigned int entropy = 0;
|
static unsigned int entropy = 0;
|
||||||
|
static unsigned int total_collected = 0;
|
||||||
|
|
||||||
|
|
||||||
static u32 __ROL32(u32 x, u32 y)
|
static u32 __ROL32(u32 x, u32 y)
|
||||||
|
@ -135,6 +142,7 @@ void random_add_randomness(const void *buf, size_t len)
|
||||||
wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
|
wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
|
||||||
(const u8 *) pool, sizeof(pool));
|
(const u8 *) pool, sizeof(pool));
|
||||||
entropy++;
|
entropy++;
|
||||||
|
total_collected++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -174,3 +182,77 @@ int random_get_bytes(void *buf, size_t len)
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int random_pool_ready(void)
|
||||||
|
{
|
||||||
|
#ifdef __linux__
|
||||||
|
int fd;
|
||||||
|
ssize_t res;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure that there is reasonable entropy available before allowing
|
||||||
|
* some key derivation operations to proceed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (dummy_key_avail == sizeof(dummy_key))
|
||||||
|
return 1; /* Already initialized - good to continue */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to fetch some more data from the kernel high quality
|
||||||
|
* /dev/random. There may not be enough data available at this point,
|
||||||
|
* so use non-blocking read to avoid blocking the application
|
||||||
|
* completely.
|
||||||
|
*/
|
||||||
|
fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
|
||||||
|
if (fd < 0) {
|
||||||
|
int error = errno;
|
||||||
|
perror("open(/dev/random)");
|
||||||
|
wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
|
||||||
|
strerror(error));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = read(fd, dummy_key + dummy_key_avail,
|
||||||
|
sizeof(dummy_key) - dummy_key_avail);
|
||||||
|
if (res < 0) {
|
||||||
|
wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
|
||||||
|
"%s", strerror(errno));
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
|
wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from "
|
||||||
|
"/dev/random", (unsigned) res,
|
||||||
|
(unsigned) (sizeof(dummy_key) - dummy_key_avail));
|
||||||
|
dummy_key_avail += res;
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
if (dummy_key_avail == sizeof(dummy_key))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong "
|
||||||
|
"random data available from /dev/random",
|
||||||
|
(unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key));
|
||||||
|
|
||||||
|
if (own_pool_ready >= MIN_READY_MARK ||
|
||||||
|
total_collected + 10 * own_pool_ready > MIN_COLLECT_ENTROPY) {
|
||||||
|
wpa_printf(MSG_INFO, "random: Allow operation to proceed "
|
||||||
|
"based on internal entropy");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wpa_printf(MSG_INFO, "random: Not enough entropy pool available for "
|
||||||
|
"secure operations");
|
||||||
|
return 0;
|
||||||
|
#else /* __linux__ */
|
||||||
|
/* TODO: could do similar checks on non-Linux platforms */
|
||||||
|
return 1;
|
||||||
|
#endif /* __linux__ */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void random_mark_pool_ready(void)
|
||||||
|
{
|
||||||
|
own_pool_ready++;
|
||||||
|
wpa_printf(MSG_DEBUG, "random: Mark internal entropy pool to be "
|
||||||
|
"ready (count=%u/%u)", own_pool_ready, MIN_READY_MARK);
|
||||||
|
}
|
||||||
|
|
|
@ -18,9 +18,13 @@
|
||||||
#ifdef CONFIG_NO_RANDOM_POOL
|
#ifdef CONFIG_NO_RANDOM_POOL
|
||||||
#define random_add_randomness(b, l) do { } while (0)
|
#define random_add_randomness(b, l) do { } while (0)
|
||||||
#define random_get_bytes(b, l) os_get_random((b), (l))
|
#define random_get_bytes(b, l) os_get_random((b), (l))
|
||||||
|
#define random_pool_ready() 1
|
||||||
|
#define random_mark_pool_ready() do { } while (0)
|
||||||
#else /* CONFIG_NO_RANDOM_POOL */
|
#else /* CONFIG_NO_RANDOM_POOL */
|
||||||
void random_add_randomness(const void *buf, size_t len);
|
void random_add_randomness(const void *buf, size_t len);
|
||||||
int random_get_bytes(void *buf, size_t len);
|
int random_get_bytes(void *buf, size_t len);
|
||||||
|
int random_pool_ready(void);
|
||||||
|
void random_mark_pool_ready(void);
|
||||||
#endif /* CONFIG_NO_RANDOM_POOL */
|
#endif /* CONFIG_NO_RANDOM_POOL */
|
||||||
|
|
||||||
#endif /* RANDOM_H */
|
#endif /* RANDOM_H */
|
||||||
|
|
Loading…
Reference in a new issue