From 08704cd8859825e70b5a961d9791c1ab25435237 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 24 Nov 2010 13:08:03 +0200 Subject: [PATCH] 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. --- src/ap/wpa_auth.c | 30 ++++++++++++++++ src/ap/wpa_auth_i.h | 1 + src/crypto/random.c | 84 ++++++++++++++++++++++++++++++++++++++++++++- src/crypto/random.h | 4 +++ 4 files changed, 118 insertions(+), 1 deletion(-) diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 851612ef8..397fa9834 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -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); + 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 * 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; } 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, &kde) < 0) { 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 " "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_gtk_update(wpa_auth, group); wpa_group_config_group_keys(wpa_auth, group); diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index bc6962fd3..3173144ab 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -146,6 +146,7 @@ struct wpa_group { u8 GNonce[WPA_NONCE_LEN]; Boolean changed; Boolean first_sta_seen; + Boolean reject_4way_hs_for_entropy; #ifdef CONFIG_IEEE80211W u8 IGTK[2][WPA_IGTK_LEN]; int GN_igtk, GM_igtk; diff --git a/src/crypto/random.c b/src/crypto/random.c index 33eae4d65..9ea7ee66d 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -29,6 +29,9 @@ */ #include "utils/includes.h" +#ifdef __linux__ +#include +#endif /* __linux__ */ #include "utils/common.h" #include "sha1.h" @@ -42,14 +45,18 @@ #define POOL_TAP4 7 #define POOL_TAP5 1 #define EXTRACT_LEN 16 +#define MIN_READY_MARK 2 static u32 pool[POOL_WORDS]; static unsigned int input_rotate = 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 static unsigned int entropy = 0; +static unsigned int total_collected = 0; 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", (const u8 *) pool, sizeof(pool)); entropy++; + total_collected++; } @@ -174,3 +182,77 @@ int random_get_bytes(void *buf, size_t len) 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); +} diff --git a/src/crypto/random.h b/src/crypto/random.h index 65a19003b..7b6257737 100644 --- a/src/crypto/random.h +++ b/src/crypto/random.h @@ -18,9 +18,13 @@ #ifdef CONFIG_NO_RANDOM_POOL #define random_add_randomness(b, l) do { } while (0) #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 */ void random_add_randomness(const 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 /* RANDOM_H */