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.
This commit is contained in:
Jouni Malinen 2010-11-07 19:43:10 +02:00
parent 3215df77ee
commit d06df64d14
5 changed files with 187 additions and 23 deletions

View 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);

View 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;
if (try_pmk(bss, sta, ver, data, len, pmk) == 0)
return;
}
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;
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);
}

View file

@ -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);
}

View 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;

View file

@ -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);