e929eb39d6
This makes it easier to do live parsing of captured pcap files from wlantest without having to rename and restart the capture file. Packet writes are flushed to disk after each packet if -N is included in the command line. Signed-off-by: Jouni Malinen <j@w1.fi>
472 lines
9.3 KiB
C
472 lines
9.3 KiB
C
/*
|
|
* wlantest - IEEE 802.11 protocol monitoring and testing tool
|
|
* Copyright (c) 2010-2015, Jouni Malinen <j@w1.fi>
|
|
*
|
|
* This software may be distributed under the terms of the BSD license.
|
|
* See README for more details.
|
|
*/
|
|
|
|
#include "utils/includes.h"
|
|
|
|
#include "utils/common.h"
|
|
#include "utils/eloop.h"
|
|
#include "wlantest.h"
|
|
|
|
|
|
static void wlantest_terminate(int sig, void *signal_ctx)
|
|
{
|
|
eloop_terminate();
|
|
}
|
|
|
|
|
|
static void usage(void)
|
|
{
|
|
printf("wlantest [-cddhqqFNt] [-i<ifname>] [-r<pcap file>] "
|
|
"[-p<passphrase>]\n"
|
|
" [-I<wired ifname>] [-R<wired pcap file>] "
|
|
"[-P<RADIUS shared secret>]\n"
|
|
" [-n<write pcapng file>]\n"
|
|
" [-w<write pcap file>] [-f<MSK/PMK file>]\n"
|
|
" [-L<log file>] [-T<PTK file>]\n");
|
|
}
|
|
|
|
|
|
static void passphrase_deinit(struct wlantest_passphrase *p)
|
|
{
|
|
dl_list_del(&p->list);
|
|
os_free(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)
|
|
{
|
|
int i;
|
|
os_memset(wt, 0, sizeof(*wt));
|
|
wt->monitor_sock = -1;
|
|
wt->ctrl_sock = -1;
|
|
for (i = 0; i < MAX_CTRL_CONNECTIONS; i++)
|
|
wt->ctrl_socks[i] = -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);
|
|
dl_list_init(&wt->ptk);
|
|
dl_list_init(&wt->wep);
|
|
}
|
|
|
|
|
|
void radius_deinit(struct wlantest_radius *r)
|
|
{
|
|
dl_list_del(&r->list);
|
|
os_free(r);
|
|
}
|
|
|
|
|
|
static void ptk_deinit(struct wlantest_ptk *ptk)
|
|
{
|
|
dl_list_del(&ptk->list);
|
|
os_free(ptk);
|
|
}
|
|
|
|
|
|
static void wlantest_deinit(struct wlantest *wt)
|
|
{
|
|
struct wlantest_passphrase *p, *pn;
|
|
struct wlantest_radius_secret *s, *sn;
|
|
struct wlantest_radius *r, *rn;
|
|
struct wlantest_pmk *pmk, *np;
|
|
struct wlantest_ptk *ptk, *npt;
|
|
struct wlantest_wep *wep, *nw;
|
|
|
|
if (wt->ctrl_sock >= 0)
|
|
ctrl_deinit(wt);
|
|
if (wt->monitor_sock >= 0)
|
|
monitor_deinit(wt);
|
|
bss_flush(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);
|
|
dl_list_for_each_safe(ptk, npt, &wt->ptk, struct wlantest_ptk, list)
|
|
ptk_deinit(ptk);
|
|
dl_list_for_each_safe(wep, nw, &wt->wep, struct wlantest_wep, list)
|
|
os_free(wep);
|
|
write_pcap_deinit(wt);
|
|
write_pcapng_deinit(wt);
|
|
clear_notes(wt);
|
|
os_free(wt->decrypted);
|
|
wt->decrypted = NULL;
|
|
}
|
|
|
|
|
|
static void add_passphrase(struct wlantest *wt, const char *passphrase)
|
|
{
|
|
struct wlantest_passphrase *p;
|
|
size_t len = os_strlen(passphrase);
|
|
|
|
if (len < 8 || len > 63)
|
|
return;
|
|
p = os_zalloc(sizeof(*p));
|
|
if (p == NULL)
|
|
return;
|
|
os_memcpy(p->passphrase, passphrase, len);
|
|
dl_list_add(&wt->passphrase, &p->list);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
static int add_pmk_file(struct wlantest *wt, const char *pmk_file)
|
|
{
|
|
FILE *f;
|
|
u8 pmk[32];
|
|
char buf[300], *pos;
|
|
struct wlantest_pmk *p;
|
|
|
|
f = fopen(pmk_file, "r");
|
|
if (f == NULL) {
|
|
wpa_printf(MSG_ERROR, "Could not open '%s'", pmk_file);
|
|
return -1;
|
|
}
|
|
|
|
while (fgets(buf, sizeof(buf), f)) {
|
|
pos = buf;
|
|
while (*pos && *pos != '\r' && *pos != '\n')
|
|
pos++;
|
|
*pos = '\0';
|
|
if (pos - buf < 2 * 32)
|
|
continue;
|
|
if (hexstr2bin(buf, pmk, 32) < 0)
|
|
continue;
|
|
p = os_zalloc(sizeof(*p));
|
|
if (p == NULL)
|
|
break;
|
|
os_memcpy(p->pmk, pmk, 32);
|
|
dl_list_add(&wt->pmk, &p->list);
|
|
wpa_hexdump(MSG_DEBUG, "Added PMK from file", pmk, 32);
|
|
}
|
|
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int add_ptk_file(struct wlantest *wt, const char *ptk_file)
|
|
{
|
|
FILE *f;
|
|
u8 ptk[64];
|
|
size_t ptk_len;
|
|
char buf[300], *pos;
|
|
struct wlantest_ptk *p;
|
|
|
|
f = fopen(ptk_file, "r");
|
|
if (f == NULL) {
|
|
wpa_printf(MSG_ERROR, "Could not open '%s'", ptk_file);
|
|
return -1;
|
|
}
|
|
|
|
while (fgets(buf, sizeof(buf), f)) {
|
|
pos = buf;
|
|
while (*pos && *pos != '\r' && *pos != '\n')
|
|
pos++;
|
|
*pos = '\0';
|
|
ptk_len = pos - buf;
|
|
if (ptk_len & 1)
|
|
continue;
|
|
ptk_len /= 2;
|
|
if (ptk_len != 16 && ptk_len != 32 &&
|
|
ptk_len != 48 && ptk_len != 64)
|
|
continue;
|
|
if (hexstr2bin(buf, ptk, ptk_len) < 0)
|
|
continue;
|
|
p = os_zalloc(sizeof(*p));
|
|
if (p == NULL)
|
|
break;
|
|
if (ptk_len < 48) {
|
|
os_memcpy(p->ptk.tk, ptk, ptk_len);
|
|
p->ptk.tk_len = ptk_len;
|
|
p->ptk_len = 32 + ptk_len;
|
|
} else {
|
|
os_memcpy(p->ptk.kck, ptk, 16);
|
|
p->ptk.kck_len = 16;
|
|
os_memcpy(p->ptk.kek, ptk + 16, 16);
|
|
p->ptk.kek_len = 16;
|
|
os_memcpy(p->ptk.tk, ptk + 32, ptk_len - 32);
|
|
p->ptk.tk_len = ptk_len - 32;
|
|
p->ptk_len = ptk_len;
|
|
}
|
|
dl_list_add(&wt->ptk, &p->list);
|
|
wpa_hexdump(MSG_DEBUG, "Added PTK from file", ptk, ptk_len);
|
|
}
|
|
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int add_wep(struct wlantest *wt, const char *key)
|
|
{
|
|
struct wlantest_wep *w;
|
|
size_t len = os_strlen(key);
|
|
|
|
if (len != 2 * 5 && len != 2 * 13) {
|
|
wpa_printf(MSG_INFO, "Invalid WEP key '%s'", key);
|
|
return -1;
|
|
}
|
|
w = os_zalloc(sizeof(*w));
|
|
if (w == NULL)
|
|
return -1;
|
|
if (hexstr2bin(key, w->key, len / 2) < 0) {
|
|
os_free(w);
|
|
wpa_printf(MSG_INFO, "Invalid WEP key '%s'", key);
|
|
return -1;
|
|
}
|
|
w->key_len = len / 2;
|
|
dl_list_add(&wt->wep, &w->list);
|
|
return 0;
|
|
}
|
|
|
|
|
|
void add_note(struct wlantest *wt, int level, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
size_t len = 1000;
|
|
int wlen;
|
|
|
|
if (wt->num_notes == MAX_NOTES)
|
|
return;
|
|
|
|
wt->notes[wt->num_notes] = os_malloc(len);
|
|
if (wt->notes[wt->num_notes] == NULL)
|
|
return;
|
|
va_start(ap, fmt);
|
|
wlen = vsnprintf(wt->notes[wt->num_notes], len, fmt, ap);
|
|
va_end(ap);
|
|
if (wlen < 0) {
|
|
os_free(wt->notes[wt->num_notes]);
|
|
wt->notes[wt->num_notes] = NULL;
|
|
return;
|
|
}
|
|
if (wlen >= len)
|
|
wt->notes[wt->num_notes][len - 1] = '\0';
|
|
wpa_printf(level, "%s", wt->notes[wt->num_notes]);
|
|
wt->num_notes++;
|
|
}
|
|
|
|
|
|
void clear_notes(struct wlantest *wt)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < wt->num_notes; i++) {
|
|
os_free(wt->notes[i]);
|
|
wt->notes[i] = NULL;
|
|
}
|
|
|
|
wt->num_notes = 0;
|
|
}
|
|
|
|
|
|
size_t notes_len(struct wlantest *wt, size_t hdrlen)
|
|
{
|
|
size_t i;
|
|
size_t len = wt->num_notes * hdrlen;
|
|
|
|
for (i = 0; i < wt->num_notes; i++)
|
|
len += os_strlen(wt->notes[i]);
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
int wlantest_relog(struct wlantest *wt)
|
|
{
|
|
int ret = 0;
|
|
|
|
wpa_printf(MSG_INFO, "Re-open log/capture files");
|
|
if (wpa_debug_reopen_file())
|
|
ret = -1;
|
|
|
|
if (wt->write_file) {
|
|
write_pcap_deinit(wt);
|
|
if (write_pcap_init(wt, wt->write_file) < 0)
|
|
ret = -1;
|
|
}
|
|
|
|
if (wt->pcapng_file) {
|
|
write_pcapng_deinit(wt);
|
|
if (write_pcapng_init(wt, wt->pcapng_file) < 0)
|
|
ret = -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int c;
|
|
const char *read_file = NULL;
|
|
const char *read_wired_file = NULL;
|
|
const char *ifname = NULL;
|
|
const char *ifname_wired = NULL;
|
|
const char *logfile = NULL;
|
|
struct wlantest wt;
|
|
int ctrl_iface = 0;
|
|
|
|
wpa_debug_level = MSG_INFO;
|
|
wpa_debug_show_keys = 1;
|
|
|
|
if (os_program_init())
|
|
return -1;
|
|
|
|
wlantest_init(&wt);
|
|
|
|
for (;;) {
|
|
c = getopt(argc, argv, "cdf:Fhi:I:L:n:Np:P:qr:R:tT:w:W:");
|
|
if (c < 0)
|
|
break;
|
|
switch (c) {
|
|
case 'c':
|
|
ctrl_iface = 1;
|
|
break;
|
|
case 'd':
|
|
if (wpa_debug_level > 0)
|
|
wpa_debug_level--;
|
|
break;
|
|
case 'f':
|
|
if (add_pmk_file(&wt, optarg) < 0)
|
|
return -1;
|
|
break;
|
|
case 'F':
|
|
wt.assume_fcs = 1;
|
|
break;
|
|
case 'h':
|
|
usage();
|
|
return 0;
|
|
case 'i':
|
|
ifname = optarg;
|
|
break;
|
|
case 'I':
|
|
ifname_wired = optarg;
|
|
break;
|
|
case 'L':
|
|
logfile = optarg;
|
|
break;
|
|
case 'n':
|
|
wt.pcapng_file = optarg;
|
|
break;
|
|
case 'N':
|
|
wt.pcap_no_buffer = 1;
|
|
break;
|
|
case 'p':
|
|
add_passphrase(&wt, optarg);
|
|
break;
|
|
case 'P':
|
|
add_secret(&wt, optarg);
|
|
break;
|
|
case 'q':
|
|
wpa_debug_level++;
|
|
break;
|
|
case 'r':
|
|
read_file = optarg;
|
|
break;
|
|
case 'R':
|
|
read_wired_file = optarg;
|
|
break;
|
|
case 't':
|
|
wpa_debug_timestamp = 1;
|
|
break;
|
|
case 'T':
|
|
if (add_ptk_file(&wt, optarg) < 0)
|
|
return -1;
|
|
break;
|
|
case 'w':
|
|
wt.write_file = optarg;
|
|
break;
|
|
case 'W':
|
|
if (add_wep(&wt, optarg) < 0)
|
|
return -1;
|
|
break;
|
|
default:
|
|
usage();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (ifname == NULL && ifname_wired == NULL &&
|
|
read_file == NULL && read_wired_file == NULL) {
|
|
usage();
|
|
return 0;
|
|
}
|
|
|
|
if (eloop_init())
|
|
return -1;
|
|
|
|
if (logfile)
|
|
wpa_debug_open_file(logfile);
|
|
|
|
if (wt.write_file && write_pcap_init(&wt, wt.write_file) < 0)
|
|
return -1;
|
|
|
|
if (wt.pcapng_file && write_pcapng_init(&wt, wt.pcapng_file) < 0)
|
|
return -1;
|
|
|
|
if (read_wired_file && read_wired_cap_file(&wt, read_wired_file) < 0)
|
|
return -1;
|
|
|
|
if (read_file && read_cap_file(&wt, read_file) < 0)
|
|
return -1;
|
|
|
|
if (ifname && monitor_init(&wt, ifname) < 0)
|
|
return -1;
|
|
|
|
if (ifname_wired && monitor_init_wired(&wt, ifname_wired) < 0)
|
|
return -1;
|
|
|
|
if (ctrl_iface && ctrl_init(&wt) < 0)
|
|
return -1;
|
|
|
|
eloop_register_signal_terminate(wlantest_terminate, &wt);
|
|
|
|
eloop_run();
|
|
|
|
wpa_printf(MSG_INFO, "Processed: rx_mgmt=%u rx_ctrl=%u rx_data=%u "
|
|
"fcs_error=%u",
|
|
wt.rx_mgmt, wt.rx_ctrl, wt.rx_data, wt.fcs_error);
|
|
|
|
wlantest_deinit(&wt);
|
|
|
|
wpa_debug_close_file();
|
|
eloop_destroy();
|
|
os_program_deinit();
|
|
|
|
return 0;
|
|
}
|