Parcourir la source

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.
Jouni Malinen il y a 14 ans
Parent
commit
08704cd885
4 fichiers modifiés avec 118 ajouts et 1 suppressions
  1. 30 0
      src/ap/wpa_auth.c
  2. 1 0
      src/ap/wpa_auth_i.h
  3. 83 1
      src/crypto/random.c
  4. 4 0
      src/crypto/random.h

+ 30 - 0
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);

+ 1 - 0
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;

+ 83 - 1
src/crypto/random.c

@@ -29,6 +29,9 @@
  */
 
 #include "utils/includes.h"
+#ifdef __linux__
+#include <fcntl.h>
+#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);
+}

+ 4 - 0
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 */