Browse Source

wlantest: Derive PMK from RADIUS exchange

Decrypt MPPE keys and derive PMK from RADIUS exchange if RADIUS
shared secret is known. Use the derived PMK when trying to derive
PTK during 4-Way Handshake.
Jouni Malinen 14 years ago
parent
commit
d06df64d14
5 changed files with 188 additions and 24 deletions
  1. 1 1
      wlantest/bss.c
  2. 35 21
      wlantest/rx_data.c
  3. 85 0
      wlantest/wired.c
  4. 49 2
      wlantest/wlantest.c
  5. 18 0
      wlantest/wlantest.h

+ 1 - 1
wlantest/bss.c

@@ -45,7 +45,7 @@ struct wlantest_bss * bss_get(struct wlantest *wt, const u8 *bssid)
 }
 
 
-static void pmk_deinit(struct wlantest_pmk *pmk)
+void pmk_deinit(struct wlantest_pmk *pmk)
 {
 	dl_list_del(&pmk->list);
 	os_free(pmk);

+ 35 - 21
wlantest/rx_data.c

@@ -110,29 +110,43 @@ static void rx_data_eapol_key_1_of_4(struct wlantest *wt, const u8 *dst,
 }
 
 
-static void derive_ptk(struct wlantest_bss *bss, struct wlantest_sta *sta,
-		       u16 ver, const u8 *data, size_t len)
+static int try_pmk(struct wlantest_bss *bss, struct wlantest_sta *sta,
+		   u16 ver, const u8 *data, size_t len,
+		   struct wlantest_pmk *pmk)
+{
+	struct wpa_ptk ptk;
+	size_t ptk_len = 48; /* FIX: 64 for TKIP */
+	wpa_pmk_to_ptk(pmk->pmk, sizeof(pmk->pmk),
+		       "Pairwise key expansion",
+		       bss->bssid, sta->addr, sta->anonce, sta->snonce,
+		       (u8 *) &ptk, ptk_len,
+		       0 /* FIX: SHA256 based on AKM */);
+	if (check_mic(ptk.kck, ver,
+		      data, len) < 0)
+		return -1;
+
+	wpa_printf(MSG_INFO, "Derived PTK for STA " MACSTR " BSSID " MACSTR
+		   ")", MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	os_memcpy(&sta->ptk, &ptk, sizeof(ptk));
+	sta->ptk_set = 1;
+	return 0;
+}
+
+
+static void derive_ptk(struct wlantest *wt, struct wlantest_bss *bss,
+		       struct wlantest_sta *sta, u16 ver,
+		       const u8 *data, size_t len)
 {
 	struct wlantest_pmk *pmk;
 
 	dl_list_for_each(pmk, &bss->pmk, struct wlantest_pmk, list) {
-		struct wpa_ptk ptk;
-		size_t ptk_len = 48; /* FIX: 64 for TKIP */
-		wpa_pmk_to_ptk(pmk->pmk, sizeof(pmk->pmk),
-			       "Pairwise key expansion",
-			       bss->bssid, sta->addr, sta->anonce, sta->snonce,
-			       (u8 *) &ptk, ptk_len,
-			       0 /* FIX: SHA256 based on AKM */);
-		if (check_mic(ptk.kck, ver,
-			      data, len) < 0)
-			continue;
-
-		wpa_printf(MSG_INFO, "Derived PTK for STA " MACSTR " BSSID "
-			   MACSTR ")",
-			   MAC2STR(sta->addr), MAC2STR(bss->bssid));
-		os_memcpy(&sta->ptk, &ptk, sizeof(ptk));
-		sta->ptk_set = 1;
-		break;
+		if (try_pmk(bss, sta, ver, data, len, pmk) == 0)
+			return;
+	}
+
+	dl_list_for_each(pmk, &wt->pmk, struct wlantest_pmk, list) {
+		if (try_pmk(bss, sta, ver, data, len, pmk) == 0)
+			return;
 	}
 }
 
@@ -159,7 +173,7 @@ static void rx_data_eapol_key_2_of_4(struct wlantest *wt, const u8 *dst,
 	hdr = (const struct wpa_eapol_key *) (eapol + 1);
 	os_memcpy(sta->snonce, hdr->key_nonce, WPA_NONCE_LEN);
 	key_info = WPA_GET_BE16(hdr->key_info);
-	derive_ptk(bss, sta, key_info & WPA_KEY_INFO_TYPE_MASK, data, len);
+	derive_ptk(wt, bss, sta, key_info & WPA_KEY_INFO_TYPE_MASK, data, len);
 }
 
 
@@ -192,7 +206,7 @@ static void rx_data_eapol_key_3_of_4(struct wlantest *wt, const u8 *dst,
 	}
 	os_memcpy(sta->anonce, hdr->key_nonce, WPA_NONCE_LEN);
 	if (recalc) {
-		derive_ptk(bss, sta, key_info & WPA_KEY_INFO_TYPE_MASK,
+		derive_ptk(wt, bss, sta, key_info & WPA_KEY_INFO_TYPE_MASK,
 			   data, len);
 	}
 

+ 85 - 0
wlantest/wired.c

@@ -22,6 +22,28 @@
 #include "wlantest.h"
 
 
+static struct wlantest_radius * radius_get(struct wlantest *wt, u32 srv,
+					   u32 cli)
+{
+	struct wlantest_radius *r;
+
+	dl_list_for_each(r, &wt->radius, struct wlantest_radius, list) {
+		if (r->srv == srv && r->cli == cli)
+			return r;
+	}
+
+	r = os_zalloc(sizeof(*r));
+	if (r == NULL)
+		return NULL;
+
+	r->srv = srv;
+	r->cli = cli;
+	dl_list_add(&wt->radius, &r->list);
+
+	return r;
+}
+
+
 static const char * radius_code_string(u8 code)
 {
 	switch (code) {
@@ -53,6 +75,7 @@ static void process_radius_access_request(struct wlantest *wt, u32 dst,
 					  u32 src, const u8 *data, size_t len)
 {
 	struct radius_msg *msg;
+	struct wlantest_radius *r;
 
 	msg = radius_msg_parse(data, len);
 	if (msg == NULL) {
@@ -60,14 +83,43 @@ static void process_radius_access_request(struct wlantest *wt, u32 dst,
 		return;
 	}
 
+	r = radius_get(wt, dst, src);
+	if (r) {
+		radius_msg_free(r->last_req);
+		r->last_req = msg;
+		return;
+	}
 	radius_msg_free(msg);
 }
 
 
+static void wlantest_add_pmk(struct wlantest *wt, const u8 *pmk)
+{
+	struct wlantest_pmk *p;
+
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL)
+		return;
+	os_memcpy(p->pmk, pmk, 32);
+	dl_list_add(&wt->pmk, &p->list);
+	wpa_hexdump(MSG_INFO, "Add PMK", pmk, 32);
+}
+
+
 static void process_radius_access_accept(struct wlantest *wt, u32 dst, u32 src,
 					 const u8 *data, size_t len)
 {
 	struct radius_msg *msg;
+	struct wlantest_radius *r;
+	struct radius_ms_mppe_keys *keys;
+	struct wlantest_radius_secret *s;
+
+	r = radius_get(wt, src, dst);
+	if (r == NULL || r->last_req == NULL) {
+		wpa_printf(MSG_DEBUG, "No RADIUS Access-Challenge found for "
+			   "decrypting Access-Accept keys");
+		return;
+	}
 
 	msg = radius_msg_parse(data, len);
 	if (msg == NULL) {
@@ -75,6 +127,39 @@ static void process_radius_access_accept(struct wlantest *wt, u32 dst, u32 src,
 		return;
 	}
 
+	dl_list_for_each(s, &wt->secret, struct wlantest_radius_secret, list) {
+		int found = 0;
+		keys = radius_msg_get_ms_keys(msg, r->last_req,
+					      (u8 *) s->secret,
+					      os_strlen(s->secret));
+		if (keys && keys->send && keys->recv) {
+			u8 pmk[32];
+			wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key",
+					keys->send, keys->send_len);
+			wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key",
+					keys->recv, keys->recv_len);
+			os_memcpy(pmk, keys->recv,
+				  keys->recv_len > 32 ? 32 : keys->recv_len);
+			if (keys->recv_len < 32) {
+				os_memcpy(pmk + keys->recv_len,
+					  keys->send,
+					  keys->recv_len + keys->send_len > 32
+					  ? 32 : 32 - keys->recv_len);
+			}
+			wlantest_add_pmk(wt, pmk);
+			found = 1;
+		}
+
+		if (keys) {
+			os_free(keys->send);
+			os_free(keys->recv);
+			os_free(keys);
+		}
+
+		if (found)
+			break;
+	}
+
 	radius_msg_free(msg);
 }
 

+ 49 - 2
wlantest/wlantest.c

@@ -33,7 +33,8 @@ static void usage(void)
 {
 	printf("wlantest [-ddhqq] [-i<ifname>] [-r<pcap file>] "
 	       "[-p<passphrase>]\n"
-		"         [-I<wired ifname>] [-R<wired pcap file>]\n");
+		"         [-I<wired ifname>] [-R<wired pcap file>] "
+	       "[-P<RADIUS shared secret>]\n");
 }
 
 
@@ -44,12 +45,29 @@ static void passphrase_deinit(struct wlantest_passphrase *p)
 }
 
 
+static void secret_deinit(struct wlantest_radius_secret *r)
+{
+	dl_list_del(&r->list);
+	os_free(r);
+}
+
+
 static void wlantest_init(struct wlantest *wt)
 {
 	os_memset(wt, 0, sizeof(*wt));
 	wt->monitor_sock = -1;
 	dl_list_init(&wt->passphrase);
 	dl_list_init(&wt->bss);
+	dl_list_init(&wt->secret);
+	dl_list_init(&wt->radius);
+	dl_list_init(&wt->pmk);
+}
+
+
+void radius_deinit(struct wlantest_radius *r)
+{
+	dl_list_del(&r->list);
+	os_free(r);
 }
 
 
@@ -57,6 +75,10 @@ static void wlantest_deinit(struct wlantest *wt)
 {
 	struct wlantest_bss *bss, *n;
 	struct wlantest_passphrase *p, *pn;
+	struct wlantest_radius_secret *s, *sn;
+	struct wlantest_radius *r, *rn;
+	struct wlantest_pmk *pmk, *np;
+
 	if (wt->monitor_sock >= 0)
 		monitor_deinit(wt);
 	dl_list_for_each_safe(bss, n, &wt->bss, struct wlantest_bss, list)
@@ -64,6 +86,13 @@ static void wlantest_deinit(struct wlantest *wt)
 	dl_list_for_each_safe(p, pn, &wt->passphrase,
 			      struct wlantest_passphrase, list)
 		passphrase_deinit(p);
+	dl_list_for_each_safe(s, sn, &wt->secret,
+			      struct wlantest_radius_secret, list)
+		secret_deinit(s);
+	dl_list_for_each_safe(r, rn, &wt->radius, struct wlantest_radius, list)
+		radius_deinit(r);
+	dl_list_for_each_safe(pmk, np, &wt->pmk, struct wlantest_pmk, list)
+		pmk_deinit(pmk);
 }
 
 
@@ -82,6 +111,21 @@ static void add_passphrase(struct wlantest *wt, const char *passphrase)
 }
 
 
+static void add_secret(struct wlantest *wt, const char *secret)
+{
+	struct wlantest_radius_secret *s;
+	size_t len = os_strlen(secret);
+
+	if (len >= MAX_RADIUS_SECRET_LEN)
+		return;
+	s = os_zalloc(sizeof(*s));
+	if (s == NULL)
+		return;
+	os_memcpy(s->secret, secret, len);
+	dl_list_add(&wt->secret, &s->list);
+}
+
+
 int main(int argc, char *argv[])
 {
 	int c;
@@ -100,7 +144,7 @@ int main(int argc, char *argv[])
 	wlantest_init(&wt);
 
 	for (;;) {
-		c = getopt(argc, argv, "dhi:I:p:qr:R:");
+		c = getopt(argc, argv, "dhi:I:p:P:qr:R:");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -120,6 +164,9 @@ int main(int argc, char *argv[])
 		case 'p':
 			add_passphrase(&wt, optarg);
 			break;
+		case 'P':
+			add_secret(&wt, optarg);
+			break;
 		case 'q':
 			wpa_debug_level++;
 			break;

+ 18 - 0
wlantest/wlantest.h

@@ -19,7 +19,14 @@
 #include "common/wpa_common.h"
 
 struct ieee802_11_elems;
+struct radius_msg;
 
+#define MAX_RADIUS_SECRET_LEN 128
+
+struct wlantest_radius_secret {
+	struct dl_list list;
+	char secret[MAX_RADIUS_SECRET_LEN];
+};
 
 struct wlantest_passphrase {
 	struct dl_list list;
@@ -64,12 +71,22 @@ struct wlantest_bss {
 	struct dl_list pmk; /* struct wlantest_pmk */
 };
 
+struct wlantest_radius {
+	struct dl_list list;
+	u32 srv;
+	u32 cli;
+	struct radius_msg *last_req;
+};
+
 struct wlantest {
 	int monitor_sock;
 	int monitor_wired;
 
 	struct dl_list passphrase; /* struct wlantest_passphrase */
 	struct dl_list bss; /* struct wlantest_bss */
+	struct dl_list secret; /* struct wlantest_radius_secret */
+	struct dl_list radius; /* struct wlantest_radius */
+	struct dl_list pmk; /* struct wlantest_pmk */
 
 	unsigned int rx_mgmt;
 	unsigned int rx_ctrl;
@@ -92,6 +109,7 @@ struct wlantest_bss * bss_get(struct wlantest *wt, const u8 *bssid);
 void bss_deinit(struct wlantest_bss *bss);
 void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
 		struct ieee802_11_elems *elems);
+void pmk_deinit(struct wlantest_pmk *pmk);
 
 struct wlantest_sta * sta_get(struct wlantest_bss *bss, const u8 *addr);
 void sta_deinit(struct wlantest_sta *sta);