wpa_supplicant: Update to 29-Aug-2012 TOT

commit 6ffdc2f7bd496ace7a46e055f9714e7db4b1f722
Author: Jouni Malinen <jouni@qca.qualcomm.com>
Date:   Fri Mar 2 22:31:04 2012 +0200

    WFD: Add preliminary WSD request processing and response

    This commit does not yet address support for different device roles,
    i.e., the same set of subelements are returned regardless of which
    role was indicated in the request.

    Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>

Change-Id: I9d63acce719b982c02e589bb59602382e82988c8
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c
index 3d5aac6..5e4872e 100644
--- a/src/rsn_supp/peerkey.c
+++ b/src/rsn_supp/peerkey.c
@@ -221,6 +221,9 @@
 	if (cipher & WPA_CIPHER_CCMP) {
 		wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey");
 		cipher = WPA_CIPHER_CCMP;
+	} else if (cipher & WPA_CIPHER_GCMP) {
+		wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey");
+		cipher = WPA_CIPHER_GCMP;
 	} else if (cipher & WPA_CIPHER_TKIP) {
 		wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey");
 		cipher = WPA_CIPHER_TKIP;
@@ -269,6 +272,8 @@
 	pos += 2;
 	if (cipher == WPA_CIPHER_CCMP)
 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+	else if (cipher == WPA_CIPHER_GCMP)
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
 	else if (cipher == WPA_CIPHER_TKIP)
 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
 	pos += RSN_SELECTOR_LEN;
@@ -344,7 +349,7 @@
 
 	msg->type = EAPOL_KEY_TYPE_RSN;
 
-	if (peerkey->cipher == WPA_CIPHER_CCMP)
+	if (peerkey->cipher != WPA_CIPHER_TKIP)
 		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
 	else
 		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
@@ -352,7 +357,7 @@
 	key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK;
 	WPA_PUT_BE16(msg->key_info, key_info);
 
-	if (peerkey->cipher == WPA_CIPHER_CCMP)
+	if (peerkey->cipher != WPA_CIPHER_TKIP)
 		WPA_PUT_BE16(msg->key_length, 16);
 	else
 		WPA_PUT_BE16(msg->key_length, 32);
@@ -403,7 +408,7 @@
 
 	msg->type = EAPOL_KEY_TYPE_RSN;
 
-	if (peerkey->cipher == WPA_CIPHER_CCMP)
+	if (peerkey->cipher != WPA_CIPHER_TKIP)
 		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
 	else
 		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
@@ -412,7 +417,7 @@
 		WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
 	WPA_PUT_BE16(msg->key_info, key_info);
 
-	if (peerkey->cipher == WPA_CIPHER_CCMP)
+	if (peerkey->cipher != WPA_CIPHER_TKIP)
 		WPA_PUT_BE16(msg->key_length, 16);
 	else
 		WPA_PUT_BE16(msg->key_length, 32);
@@ -500,6 +505,9 @@
 	if (cipher & WPA_CIPHER_CCMP) {
 		wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey");
 		peerkey->cipher = WPA_CIPHER_CCMP;
+	} else if (cipher & WPA_CIPHER_GCMP) {
+		wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey");
+		peerkey->cipher = WPA_CIPHER_GCMP;
 	} else if (cipher & WPA_CIPHER_TKIP) {
 		wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey");
 		peerkey->cipher = WPA_CIPHER_TKIP;
@@ -1016,7 +1024,7 @@
 		return -1;
 	}
 
-	if (sm->pairwise_cipher == WPA_CIPHER_CCMP)
+	if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
 		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
 	else
 		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
@@ -1061,6 +1069,11 @@
 		pos += RSN_SELECTOR_LEN;
 		count++;
 	}
+	if (sm->allowed_pairwise_cipher & WPA_CIPHER_GCMP) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
+		pos += RSN_SELECTOR_LEN;
+		count++;
+	}
 	if (sm->allowed_pairwise_cipher & WPA_CIPHER_TKIP) {
 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
 		pos += RSN_SELECTOR_LEN;
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index 2d265d0..9783e7c 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -197,11 +197,25 @@
 	if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
 		/* Remove the oldest entry to make room for the new entry */
 		pos = pmksa->pmksa;
-		pmksa->pmksa = pos->next;
-		wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
-			   "entry (for " MACSTR ") to make room for new one",
-			   MAC2STR(pos->aa));
-		pmksa_cache_free_entry(pmksa, pos, 0);
+
+		if (pos == pmksa->sm->cur_pmksa) {
+			/*
+			 * Never remove the current PMKSA cache entry, since
+			 * it's in use, and removing it triggers a needless
+			 * deauthentication.
+			 */
+			pos = pos->next;
+			pmksa->pmksa->next = pos ? pos->next : NULL;
+		} else
+			pmksa->pmksa = pos->next;
+
+		if (pos) {
+			wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle "
+				   "PMKSA cache entry (for " MACSTR ") to "
+				   "make room for new one",
+				   MAC2STR(pos->aa));
+			pmksa_cache_free_entry(pmksa, pos, 0);
+		}
 	}
 
 	/* Add the new entry; order by expiration time */
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 7159c3a..bcd5951 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -91,7 +91,7 @@
 
 	if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt))
 		ver = WPA_KEY_INFO_TYPE_AES_128_CMAC;
-	else if (sm->pairwise_cipher == WPA_CIPHER_CCMP)
+	else if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
 		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
 	else
 		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
@@ -190,14 +190,17 @@
 #endif /* CONFIG_IEEE80211R */
 		}
 		if (res == 0) {
+			struct rsn_pmksa_cache_entry *sa = NULL;
 			wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
 					"machines", sm->pmk, pmk_len);
 			sm->pmk_len = pmk_len;
 			if (sm->proto == WPA_PROTO_RSN &&
 			    !wpa_key_mgmt_ft(sm->key_mgmt)) {
-				pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len,
-						src_addr, sm->own_addr,
-						sm->network_ctx, sm->key_mgmt);
+				sa = pmksa_cache_add(sm->pmksa,
+						     sm->pmk, pmk_len,
+						     src_addr, sm->own_addr,
+						     sm->network_ctx,
+						     sm->key_mgmt);
 			}
 			if (!sm->cur_pmksa && pmkid &&
 			    pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL))
@@ -207,6 +210,9 @@
 					"PMKID");
 				abort_cached = 0;
 			}
+
+			if (!sm->cur_pmksa)
+				sm->cur_pmksa = sa;
 		} else {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Failed to get master session key from "
@@ -350,7 +356,7 @@
 			  const struct wpa_eapol_key *key,
 			  struct wpa_ptk *ptk)
 {
-	size_t ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64;
+	size_t ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64;
 #ifdef CONFIG_IEEE80211R
 	if (wpa_key_mgmt_ft(sm->key_mgmt))
 		return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len);
@@ -520,6 +526,11 @@
 		keylen = 16;
 		rsclen = 6;
 		break;
+	case WPA_CIPHER_GCMP:
+		alg = WPA_ALG_GCMP;
+		keylen = 16;
+		rsclen = 6;
+		break;
 	case WPA_CIPHER_TKIP:
 		alg = WPA_ALG_TKIP;
 		keylen = 32;
@@ -579,6 +590,14 @@
 		*key_rsc_len = 6;
 		*alg = WPA_ALG_CCMP;
 		break;
+	case WPA_CIPHER_GCMP:
+		if (keylen != 16 || maxkeylen < 16) {
+			ret = -1;
+			break;
+		}
+		*key_rsc_len = 6;
+		*alg = WPA_ALG_GCMP;
+		break;
 	case WPA_CIPHER_TKIP:
 		if (keylen != 32 || maxkeylen < 32) {
 			ret = -1;
@@ -1125,6 +1144,14 @@
 			goto failed;
 		}
 		break;
+	case WPA_CIPHER_GCMP:
+		if (keylen != 16) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: Invalid GCMP key length %d (src=" MACSTR
+				")", keylen, MAC2STR(sm->bssid));
+			goto failed;
+		}
+		break;
 	case WPA_CIPHER_TKIP:
 		if (keylen != 32) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -1712,6 +1739,13 @@
 		} else
 			goto out;
 	}
+	if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
+	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: GCMP is used, but EAPOL-Key "
+			"descriptor version (%d) is not 2", ver);
+		goto out;
+	}
 
 #ifdef CONFIG_PEERKEY
 	for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
@@ -1851,6 +1885,8 @@
 	switch (cipher) {
 	case WPA_CIPHER_CCMP:
 		return 128;
+	case WPA_CIPHER_GCMP:
+		return 128;
 	case WPA_CIPHER_TKIP:
 		return 256;
 	case WPA_CIPHER_WEP104:
@@ -1900,6 +1936,8 @@
 	case WPA_CIPHER_CCMP:
 		return (sm->proto == WPA_PROTO_RSN ?
 			RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
+	case WPA_CIPHER_GCMP:
+		return RSN_CIPHER_SUITE_GCMP;
 	case WPA_CIPHER_TKIP:
 		return (sm->proto == WPA_PROTO_RSN ?
 			RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
@@ -2659,3 +2697,125 @@
 	pmksa_cache_flush(sm->pmksa, network_ctx);
 #endif /* CONFIG_NO_WPA2 */
 }
+
+
+#ifdef CONFIG_IEEE80211V
+int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
+{
+	struct wpa_gtk_data gd;
+#ifdef CONFIG_IEEE80211W
+	struct wpa_igtk_kde igd;
+	u16 keyidx;
+#endif /* CONFIG_IEEE80211W */
+	u16 keyinfo;
+	u8 keylen;  /* plaintext key len */
+	u8 keydatalen;
+	u8 *key_rsc;
+
+	os_memset(&gd, 0, sizeof(gd));
+#ifdef CONFIG_IEEE80211W
+	os_memset(&igd, 0, sizeof(igd));
+#endif /* CONFIG_IEEE80211W */
+
+	switch (sm->group_cipher) {
+	case WPA_CIPHER_CCMP:
+		keylen = 16;
+		gd.key_rsc_len = 6;
+		gd.alg = WPA_ALG_CCMP;
+		break;
+	case WPA_CIPHER_GCMP:
+		keylen = 16;
+		gd.key_rsc_len = 6;
+		gd.alg = WPA_ALG_GCMP;
+		break;
+	case WPA_CIPHER_TKIP:
+		keylen = 32;
+		gd.key_rsc_len = 6;
+		gd.alg = WPA_ALG_TKIP;
+		break;
+	case WPA_CIPHER_WEP104:
+		keylen = 13;
+		gd.key_rsc_len = 0;
+		gd.alg = WPA_ALG_WEP;
+		break;
+	case WPA_CIPHER_WEP40:
+		keylen = 5;
+		gd.key_rsc_len = 0;
+		gd.alg = WPA_ALG_WEP;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "Unsupported group cipher suite");
+		return -1;
+	}
+
+	if (subelem_id == WNM_SLEEP_SUBELEM_GTK) {
+		key_rsc = buf + 5;
+		keyinfo = WPA_GET_LE16(buf+2);
+		keydatalen = buf[1] - 11 - 8;
+		gd.gtk_len = keylen;
+		if (gd.gtk_len != buf[4]) {
+			wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d",
+				   gd.gtk_len, buf[4]);
+			return -1;
+		}
+		gd.keyidx = keyinfo & 0x03; /* B0 - B1 */
+		gd.tx = wpa_supplicant_gtk_tx_bit_workaround(
+		         sm, !!(keyinfo & WPA_KEY_INFO_TXRX));
+
+		if (keydatalen % 8) {
+			wpa_printf(MSG_DEBUG, "WPA: Unsupported AES-WRAP len "
+				   "%d", keydatalen);
+			return -1;
+		}
+
+		if (aes_unwrap(sm->ptk.kek, keydatalen / 8, buf + 13, gd.gtk))
+		{
+			wpa_printf(MSG_WARNING, "WNM: AES unwrap failed - "
+				   "could not decrypt GTK");
+			return -1;
+		}
+
+		wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)",
+				gd.gtk, gd.gtk_len);
+		if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) {
+			wpa_printf(MSG_DEBUG, "Failed to install the GTK in "
+				   "WNM mode");
+			return -1;
+		}
+#ifdef CONFIG_IEEE80211W
+	} else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) {
+		if (buf[1] != 2 + 6 + WPA_IGTK_LEN + 8) {
+			wpa_printf(MSG_DEBUG, "WPA: Unsupported AES-WRAP len "
+				   "%d", buf[1] - 2 - 6 - 8);
+			return -1;
+		}
+		os_memcpy(igd.keyid, buf + 2, 2);
+		os_memcpy(igd.pn, buf + 4, 6);
+
+		keyidx = WPA_GET_LE16(igd.keyid);
+
+		if (aes_unwrap(sm->ptk.kek, WPA_IGTK_LEN / 8, buf + 10,
+			       igd.igtk)) {
+			wpa_printf(MSG_WARNING, "WNM: AES unwrap failed - "
+				   "could not decrypr IGTK");
+			return -1;
+		}
+
+		wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)",
+				igd.igtk, WPA_IGTK_LEN);
+		if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr,
+				   keyidx, 0, igd.pn, sizeof(igd.pn),
+				   igd.igtk, WPA_IGTK_LEN) < 0) {
+			wpa_printf(MSG_DEBUG, "Failed to install the IGTK in "
+				   "WNM mode");
+			return -1;
+		}
+#endif /* CONFIG_IEEE80211W */
+	} else {
+		wpa_printf(MSG_DEBUG, "Unknown element id");
+		return -1;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_IEEE80211V */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index a70b57b..1077b5a 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -366,4 +366,8 @@
 void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr);
 int wpa_tdls_is_external_setup(struct wpa_sm *sm);
 
+#ifdef CONFIG_IEEE80211V
+int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
+#endif /* CONFIG_IEEE80211V */
+
 #endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index fec9544..bdf389b 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -173,6 +173,8 @@
 	/* Group Suite Selector */
 	if (sm->group_cipher == WPA_CIPHER_CCMP)
 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+	else if (sm->group_cipher == WPA_CIPHER_GCMP)
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
 	else if (sm->group_cipher == WPA_CIPHER_TKIP)
 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
 	else {
@@ -190,6 +192,8 @@
 	/* Pairwise Suite List */
 	if (sm->pairwise_cipher == WPA_CIPHER_CCMP)
 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+	else if (sm->pairwise_cipher == WPA_CIPHER_GCMP)
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
 	else if (sm->pairwise_cipher == WPA_CIPHER_TKIP)
 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
 	else {
@@ -328,6 +332,10 @@
 		alg = WPA_ALG_CCMP;
 		keylen = 16;
 		break;
+	case WPA_CIPHER_GCMP:
+		alg = WPA_ALG_GCMP;
+		keylen = 16;
+		break;
 	case WPA_CIPHER_TKIP:
 		alg = WPA_ALG_TKIP;
 		keylen = 32;
@@ -483,7 +491,7 @@
 		    sm->pmk_r1_name, WPA_PMK_NAME_LEN);
 
 	bssid = target_ap;
-	ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64;
+	ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64;
 	wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr,
 			  bssid, sm->pmk_r1_name,
 			  (u8 *) &sm->ptk, ptk_len, ptk_name);
@@ -577,6 +585,11 @@
 		rsc_len = 6;
 		alg = WPA_ALG_CCMP;
 		break;
+	case WPA_CIPHER_GCMP:
+		keylen = 16;
+		rsc_len = 6;
+		alg = WPA_ALG_GCMP;
+		break;
 	case WPA_CIPHER_TKIP:
 		keylen = 32;
 		rsc_len = 6;
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 56d564c..16268d5 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -132,6 +132,8 @@
 
 	if (group_cipher == WPA_CIPHER_CCMP) {
 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+	} else if (group_cipher == WPA_CIPHER_GCMP) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
 	} else if (group_cipher == WPA_CIPHER_TKIP) {
 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
 	} else if (group_cipher == WPA_CIPHER_WEP104) {
@@ -149,6 +151,8 @@
 	*pos++ = 0;
 	if (pairwise_cipher == WPA_CIPHER_CCMP) {
 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+	} else if (pairwise_cipher == WPA_CIPHER_GCMP) {
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
 	} else if (pairwise_cipher == WPA_CIPHER_TKIP) {
 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
 	} else if (pairwise_cipher == WPA_CIPHER_NONE) {