]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
wlantest: Derive PMK from RADIUS exchange
authorJouni Malinen <j@w1.fi>
Sun, 7 Nov 2010 17:43:10 +0000 (19:43 +0200)
committerJouni Malinen <j@w1.fi>
Sun, 7 Nov 2010 21:29:02 +0000 (23:29 +0200)
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.

wlantest/bss.c
wlantest/rx_data.c
wlantest/wired.c
wlantest/wlantest.c
wlantest/wlantest.h

index 156d8b37faa8b433fe82b02e0adf509d4b0f2321..2435dce58ad33d7662a5c3f67401f5c5fff9dd68 100644 (file)
@@ -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);
index 0320144a34f5473078f9ad7fabb967803fa23b81..b9a12c92da0d936985b634181559f571d77bb540 100644 (file)
@@ -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);
        }
 
index fe64d45224dcfa169d1f2bf8955da24d3e2dca76..bf9f16368f6fd661f87de54f15bb38041fc001e8 100644 (file)
 #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);
 }
 
index b28f43d32b9c03a39bfba1a989cb1f754c7ccb3b..fc1d40811e349ff9b2da462dd85fc9643f906ca0 100644 (file)
@@ -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;
index 203044af52f4f2cb26c591e6628024468aa97d76..71eea524594d68cc44e7ea8f72d68e94fa82b6fe 100644 (file)
 #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);