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:
parent
3215df77ee
commit
d06df64d14
5 changed files with 187 additions and 23 deletions
|
@ -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);
|
dl_list_del(&pmk->list);
|
||||||
os_free(pmk);
|
os_free(pmk);
|
||||||
|
|
|
@ -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,
|
static int try_pmk(struct wlantest_bss *bss, struct wlantest_sta *sta,
|
||||||
u16 ver, const u8 *data, size_t len)
|
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;
|
struct wlantest_pmk *pmk;
|
||||||
|
|
||||||
dl_list_for_each(pmk, &bss->pmk, struct wlantest_pmk, list) {
|
dl_list_for_each(pmk, &bss->pmk, struct wlantest_pmk, list) {
|
||||||
struct wpa_ptk ptk;
|
if (try_pmk(bss, sta, ver, data, len, pmk) == 0)
|
||||||
size_t ptk_len = 48; /* FIX: 64 for TKIP */
|
return;
|
||||||
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 "
|
dl_list_for_each(pmk, &wt->pmk, struct wlantest_pmk, list) {
|
||||||
MACSTR ")",
|
if (try_pmk(bss, sta, ver, data, len, pmk) == 0)
|
||||||
MAC2STR(sta->addr), MAC2STR(bss->bssid));
|
return;
|
||||||
os_memcpy(&sta->ptk, &ptk, sizeof(ptk));
|
|
||||||
sta->ptk_set = 1;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
hdr = (const struct wpa_eapol_key *) (eapol + 1);
|
||||||
os_memcpy(sta->snonce, hdr->key_nonce, WPA_NONCE_LEN);
|
os_memcpy(sta->snonce, hdr->key_nonce, WPA_NONCE_LEN);
|
||||||
key_info = WPA_GET_BE16(hdr->key_info);
|
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);
|
os_memcpy(sta->anonce, hdr->key_nonce, WPA_NONCE_LEN);
|
||||||
if (recalc) {
|
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);
|
data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,28 @@
|
||||||
#include "wlantest.h"
|
#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)
|
static const char * radius_code_string(u8 code)
|
||||||
{
|
{
|
||||||
switch (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)
|
u32 src, const u8 *data, size_t len)
|
||||||
{
|
{
|
||||||
struct radius_msg *msg;
|
struct radius_msg *msg;
|
||||||
|
struct wlantest_radius *r;
|
||||||
|
|
||||||
msg = radius_msg_parse(data, len);
|
msg = radius_msg_parse(data, len);
|
||||||
if (msg == NULL) {
|
if (msg == NULL) {
|
||||||
|
@ -60,14 +83,43 @@ static void process_radius_access_request(struct wlantest *wt, u32 dst,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r = radius_get(wt, dst, src);
|
||||||
|
if (r) {
|
||||||
|
radius_msg_free(r->last_req);
|
||||||
|
r->last_req = msg;
|
||||||
|
return;
|
||||||
|
}
|
||||||
radius_msg_free(msg);
|
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,
|
static void process_radius_access_accept(struct wlantest *wt, u32 dst, u32 src,
|
||||||
const u8 *data, size_t len)
|
const u8 *data, size_t len)
|
||||||
{
|
{
|
||||||
struct radius_msg *msg;
|
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);
|
msg = radius_msg_parse(data, len);
|
||||||
if (msg == NULL) {
|
if (msg == NULL) {
|
||||||
|
@ -75,6 +127,39 @@ static void process_radius_access_accept(struct wlantest *wt, u32 dst, u32 src,
|
||||||
return;
|
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);
|
radius_msg_free(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,8 @@ static void usage(void)
|
||||||
{
|
{
|
||||||
printf("wlantest [-ddhqq] [-i<ifname>] [-r<pcap file>] "
|
printf("wlantest [-ddhqq] [-i<ifname>] [-r<pcap file>] "
|
||||||
"[-p<passphrase>]\n"
|
"[-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)
|
static void wlantest_init(struct wlantest *wt)
|
||||||
{
|
{
|
||||||
os_memset(wt, 0, sizeof(*wt));
|
os_memset(wt, 0, sizeof(*wt));
|
||||||
wt->monitor_sock = -1;
|
wt->monitor_sock = -1;
|
||||||
dl_list_init(&wt->passphrase);
|
dl_list_init(&wt->passphrase);
|
||||||
dl_list_init(&wt->bss);
|
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_bss *bss, *n;
|
||||||
struct wlantest_passphrase *p, *pn;
|
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)
|
if (wt->monitor_sock >= 0)
|
||||||
monitor_deinit(wt);
|
monitor_deinit(wt);
|
||||||
dl_list_for_each_safe(bss, n, &wt->bss, struct wlantest_bss, list)
|
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,
|
dl_list_for_each_safe(p, pn, &wt->passphrase,
|
||||||
struct wlantest_passphrase, list)
|
struct wlantest_passphrase, list)
|
||||||
passphrase_deinit(p);
|
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 main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
|
@ -100,7 +144,7 @@ int main(int argc, char *argv[])
|
||||||
wlantest_init(&wt);
|
wlantest_init(&wt);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
c = getopt(argc, argv, "dhi:I:p:qr:R:");
|
c = getopt(argc, argv, "dhi:I:p:P:qr:R:");
|
||||||
if (c < 0)
|
if (c < 0)
|
||||||
break;
|
break;
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
@ -120,6 +164,9 @@ int main(int argc, char *argv[])
|
||||||
case 'p':
|
case 'p':
|
||||||
add_passphrase(&wt, optarg);
|
add_passphrase(&wt, optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'P':
|
||||||
|
add_secret(&wt, optarg);
|
||||||
|
break;
|
||||||
case 'q':
|
case 'q':
|
||||||
wpa_debug_level++;
|
wpa_debug_level++;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -19,7 +19,14 @@
|
||||||
#include "common/wpa_common.h"
|
#include "common/wpa_common.h"
|
||||||
|
|
||||||
struct ieee802_11_elems;
|
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 wlantest_passphrase {
|
||||||
struct dl_list list;
|
struct dl_list list;
|
||||||
|
@ -64,12 +71,22 @@ struct wlantest_bss {
|
||||||
struct dl_list pmk; /* struct wlantest_pmk */
|
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 {
|
struct wlantest {
|
||||||
int monitor_sock;
|
int monitor_sock;
|
||||||
int monitor_wired;
|
int monitor_wired;
|
||||||
|
|
||||||
struct dl_list passphrase; /* struct wlantest_passphrase */
|
struct dl_list passphrase; /* struct wlantest_passphrase */
|
||||||
struct dl_list bss; /* struct wlantest_bss */
|
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_mgmt;
|
||||||
unsigned int rx_ctrl;
|
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_deinit(struct wlantest_bss *bss);
|
||||||
void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
|
void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
|
||||||
struct ieee802_11_elems *elems);
|
struct ieee802_11_elems *elems);
|
||||||
|
void pmk_deinit(struct wlantest_pmk *pmk);
|
||||||
|
|
||||||
struct wlantest_sta * sta_get(struct wlantest_bss *bss, const u8 *addr);
|
struct wlantest_sta * sta_get(struct wlantest_bss *bss, const u8 *addr);
|
||||||
void sta_deinit(struct wlantest_sta *sta);
|
void sta_deinit(struct wlantest_sta *sta);
|
||||||
|
|
Loading…
Reference in a new issue