]>
Commit | Line | Data |
---|---|---|
a149fcc7 JM |
1 | /* |
2 | * wlantest - IEEE 802.11 protocol monitoring and testing tool | |
98cd3d1c | 3 | * Copyright (c) 2010-2015, Jouni Malinen <j@w1.fi> |
a149fcc7 | 4 | * |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
a149fcc7 JM |
7 | */ |
8 | ||
9 | #include "utils/includes.h" | |
10 | ||
11 | #include "utils/common.h" | |
12 | #include "utils/eloop.h" | |
13 | #include "wlantest.h" | |
14 | ||
15 | ||
a149fcc7 JM |
16 | static void wlantest_terminate(int sig, void *signal_ctx) |
17 | { | |
18 | eloop_terminate(); | |
19 | } | |
20 | ||
21 | ||
22 | static void usage(void) | |
23 | { | |
e929eb39 | 24 | printf("wlantest [-cddhqqFNt] [-i<ifname>] [-r<pcap file>] " |
3215df77 | 25 | "[-p<passphrase>]\n" |
ba2beacc | 26 | " [-I<wired ifname>] [-R<wired pcap file>] " |
64f45d07 | 27 | "[-P<RADIUS shared secret>]\n" |
ba2beacc | 28 | " [-n<write pcapng file>]\n" |
d33fef57 | 29 | " [-w<write pcap file>] [-f<MSK/PMK file>]\n" |
a0530dff | 30 | " [-L<log file>] [-T<PTK file>]\n"); |
53650bca JM |
31 | } |
32 | ||
33 | ||
34 | static void passphrase_deinit(struct wlantest_passphrase *p) | |
35 | { | |
36 | dl_list_del(&p->list); | |
37 | os_free(p); | |
a149fcc7 JM |
38 | } |
39 | ||
40 | ||
d06df64d JM |
41 | static void secret_deinit(struct wlantest_radius_secret *r) |
42 | { | |
43 | dl_list_del(&r->list); | |
44 | os_free(r); | |
45 | } | |
46 | ||
47 | ||
d84d3893 JM |
48 | static void wlantest_init(struct wlantest *wt) |
49 | { | |
644fb8c8 | 50 | int i; |
d84d3893 JM |
51 | os_memset(wt, 0, sizeof(*wt)); |
52 | wt->monitor_sock = -1; | |
644fb8c8 JM |
53 | wt->ctrl_sock = -1; |
54 | for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) | |
55 | wt->ctrl_socks[i] = -1; | |
53650bca | 56 | dl_list_init(&wt->passphrase); |
d84d3893 | 57 | dl_list_init(&wt->bss); |
d06df64d JM |
58 | dl_list_init(&wt->secret); |
59 | dl_list_init(&wt->radius); | |
60 | dl_list_init(&wt->pmk); | |
a0530dff | 61 | dl_list_init(&wt->ptk); |
2e479416 | 62 | dl_list_init(&wt->wep); |
d06df64d JM |
63 | } |
64 | ||
65 | ||
66 | void radius_deinit(struct wlantest_radius *r) | |
67 | { | |
68 | dl_list_del(&r->list); | |
69 | os_free(r); | |
d84d3893 JM |
70 | } |
71 | ||
72 | ||
a0530dff JM |
73 | static void ptk_deinit(struct wlantest_ptk *ptk) |
74 | { | |
75 | dl_list_del(&ptk->list); | |
76 | os_free(ptk); | |
77 | } | |
78 | ||
79 | ||
d84d3893 JM |
80 | static void wlantest_deinit(struct wlantest *wt) |
81 | { | |
53650bca | 82 | struct wlantest_passphrase *p, *pn; |
d06df64d JM |
83 | struct wlantest_radius_secret *s, *sn; |
84 | struct wlantest_radius *r, *rn; | |
85 | struct wlantest_pmk *pmk, *np; | |
a0530dff | 86 | struct wlantest_ptk *ptk, *npt; |
2e479416 | 87 | struct wlantest_wep *wep, *nw; |
d06df64d | 88 | |
644fb8c8 JM |
89 | if (wt->ctrl_sock >= 0) |
90 | ctrl_deinit(wt); | |
d84d3893 JM |
91 | if (wt->monitor_sock >= 0) |
92 | monitor_deinit(wt); | |
d356bd63 | 93 | bss_flush(wt); |
53650bca JM |
94 | dl_list_for_each_safe(p, pn, &wt->passphrase, |
95 | struct wlantest_passphrase, list) | |
96 | passphrase_deinit(p); | |
d06df64d JM |
97 | dl_list_for_each_safe(s, sn, &wt->secret, |
98 | struct wlantest_radius_secret, list) | |
99 | secret_deinit(s); | |
100 | dl_list_for_each_safe(r, rn, &wt->radius, struct wlantest_radius, list) | |
101 | radius_deinit(r); | |
102 | dl_list_for_each_safe(pmk, np, &wt->pmk, struct wlantest_pmk, list) | |
103 | pmk_deinit(pmk); | |
a0530dff JM |
104 | dl_list_for_each_safe(ptk, npt, &wt->ptk, struct wlantest_ptk, list) |
105 | ptk_deinit(ptk); | |
2e479416 JM |
106 | dl_list_for_each_safe(wep, nw, &wt->wep, struct wlantest_wep, list) |
107 | os_free(wep); | |
64f45d07 | 108 | write_pcap_deinit(wt); |
ba2beacc JM |
109 | write_pcapng_deinit(wt); |
110 | clear_notes(wt); | |
111 | os_free(wt->decrypted); | |
112 | wt->decrypted = NULL; | |
53650bca JM |
113 | } |
114 | ||
115 | ||
116 | static void add_passphrase(struct wlantest *wt, const char *passphrase) | |
117 | { | |
118 | struct wlantest_passphrase *p; | |
119 | size_t len = os_strlen(passphrase); | |
120 | ||
121 | if (len < 8 || len > 63) | |
122 | return; | |
123 | p = os_zalloc(sizeof(*p)); | |
124 | if (p == NULL) | |
125 | return; | |
126 | os_memcpy(p->passphrase, passphrase, len); | |
127 | dl_list_add(&wt->passphrase, &p->list); | |
d84d3893 JM |
128 | } |
129 | ||
130 | ||
d06df64d JM |
131 | static void add_secret(struct wlantest *wt, const char *secret) |
132 | { | |
133 | struct wlantest_radius_secret *s; | |
134 | size_t len = os_strlen(secret); | |
135 | ||
136 | if (len >= MAX_RADIUS_SECRET_LEN) | |
137 | return; | |
138 | s = os_zalloc(sizeof(*s)); | |
139 | if (s == NULL) | |
140 | return; | |
141 | os_memcpy(s->secret, secret, len); | |
142 | dl_list_add(&wt->secret, &s->list); | |
143 | } | |
144 | ||
145 | ||
219fd441 JM |
146 | static int add_pmk_file(struct wlantest *wt, const char *pmk_file) |
147 | { | |
148 | FILE *f; | |
6c29d95a JM |
149 | u8 pmk[PMK_LEN_MAX]; |
150 | size_t pmk_len; | |
219fd441 JM |
151 | char buf[300], *pos; |
152 | struct wlantest_pmk *p; | |
153 | ||
154 | f = fopen(pmk_file, "r"); | |
155 | if (f == NULL) { | |
156 | wpa_printf(MSG_ERROR, "Could not open '%s'", pmk_file); | |
157 | return -1; | |
158 | } | |
159 | ||
160 | while (fgets(buf, sizeof(buf), f)) { | |
161 | pos = buf; | |
162 | while (*pos && *pos != '\r' && *pos != '\n') | |
163 | pos++; | |
164 | *pos = '\0'; | |
165 | if (pos - buf < 2 * 32) | |
166 | continue; | |
6c29d95a JM |
167 | pmk_len = (pos - buf) / 2; |
168 | if (pmk_len > PMK_LEN_MAX) | |
169 | pmk_len = PMK_LEN_MAX; | |
170 | if (hexstr2bin(buf, pmk, pmk_len) < 0) | |
219fd441 JM |
171 | continue; |
172 | p = os_zalloc(sizeof(*p)); | |
173 | if (p == NULL) | |
174 | break; | |
6c29d95a JM |
175 | os_memcpy(p->pmk, pmk, pmk_len); |
176 | p->pmk_len = pmk_len; | |
219fd441 | 177 | dl_list_add(&wt->pmk, &p->list); |
6c29d95a | 178 | wpa_hexdump(MSG_DEBUG, "Added PMK from file", pmk, pmk_len); |
f58afccd JM |
179 | |
180 | /* For FT, the send half of MSK is used */ | |
6c29d95a | 181 | if (hexstr2bin(&buf[2 * PMK_LEN], pmk, PMK_LEN) < 0) |
f58afccd JM |
182 | continue; |
183 | p = os_zalloc(sizeof(*p)); | |
184 | if (p == NULL) | |
185 | break; | |
6c29d95a JM |
186 | os_memcpy(p->pmk, pmk, PMK_LEN); |
187 | p->pmk_len = PMK_LEN; | |
f58afccd JM |
188 | dl_list_add(&wt->pmk, &p->list); |
189 | wpa_hexdump(MSG_DEBUG, "Added PMK from file (2nd half of MSK)", | |
6c29d95a | 190 | pmk, PMK_LEN); |
219fd441 JM |
191 | } |
192 | ||
193 | fclose(f); | |
194 | return 0; | |
195 | } | |
196 | ||
197 | ||
a0530dff JM |
198 | static int add_ptk_file(struct wlantest *wt, const char *ptk_file) |
199 | { | |
200 | FILE *f; | |
201 | u8 ptk[64]; | |
202 | size_t ptk_len; | |
203 | char buf[300], *pos; | |
204 | struct wlantest_ptk *p; | |
205 | ||
206 | f = fopen(ptk_file, "r"); | |
207 | if (f == NULL) { | |
208 | wpa_printf(MSG_ERROR, "Could not open '%s'", ptk_file); | |
209 | return -1; | |
210 | } | |
211 | ||
212 | while (fgets(buf, sizeof(buf), f)) { | |
213 | pos = buf; | |
214 | while (*pos && *pos != '\r' && *pos != '\n') | |
215 | pos++; | |
216 | *pos = '\0'; | |
217 | ptk_len = pos - buf; | |
218 | if (ptk_len & 1) | |
219 | continue; | |
220 | ptk_len /= 2; | |
221 | if (ptk_len != 16 && ptk_len != 32 && | |
222 | ptk_len != 48 && ptk_len != 64) | |
223 | continue; | |
224 | if (hexstr2bin(buf, ptk, ptk_len) < 0) | |
225 | continue; | |
226 | p = os_zalloc(sizeof(*p)); | |
227 | if (p == NULL) | |
228 | break; | |
229 | if (ptk_len < 48) { | |
98cd3d1c JM |
230 | os_memcpy(p->ptk.tk, ptk, ptk_len); |
231 | p->ptk.tk_len = ptk_len; | |
a0530dff JM |
232 | p->ptk_len = 32 + ptk_len; |
233 | } else { | |
98cd3d1c JM |
234 | os_memcpy(p->ptk.kck, ptk, 16); |
235 | p->ptk.kck_len = 16; | |
236 | os_memcpy(p->ptk.kek, ptk + 16, 16); | |
237 | p->ptk.kek_len = 16; | |
238 | os_memcpy(p->ptk.tk, ptk + 32, ptk_len - 32); | |
239 | p->ptk.tk_len = ptk_len - 32; | |
a0530dff JM |
240 | p->ptk_len = ptk_len; |
241 | } | |
242 | dl_list_add(&wt->ptk, &p->list); | |
243 | wpa_hexdump(MSG_DEBUG, "Added PTK from file", ptk, ptk_len); | |
244 | } | |
245 | ||
246 | fclose(f); | |
247 | return 0; | |
248 | } | |
249 | ||
250 | ||
9a994178 | 251 | int add_wep(struct wlantest *wt, const char *key) |
2e479416 JM |
252 | { |
253 | struct wlantest_wep *w; | |
254 | size_t len = os_strlen(key); | |
255 | ||
256 | if (len != 2 * 5 && len != 2 * 13) { | |
257 | wpa_printf(MSG_INFO, "Invalid WEP key '%s'", key); | |
9a994178 | 258 | return -1; |
2e479416 JM |
259 | } |
260 | w = os_zalloc(sizeof(*w)); | |
261 | if (w == NULL) | |
9a994178 | 262 | return -1; |
2e479416 JM |
263 | if (hexstr2bin(key, w->key, len / 2) < 0) { |
264 | os_free(w); | |
265 | wpa_printf(MSG_INFO, "Invalid WEP key '%s'", key); | |
9a994178 | 266 | return -1; |
2e479416 JM |
267 | } |
268 | w->key_len = len / 2; | |
269 | dl_list_add(&wt->wep, &w->list); | |
9a994178 | 270 | return 0; |
2e479416 JM |
271 | } |
272 | ||
273 | ||
ba2beacc JM |
274 | void add_note(struct wlantest *wt, int level, const char *fmt, ...) |
275 | { | |
276 | va_list ap; | |
277 | size_t len = 1000; | |
278 | int wlen; | |
279 | ||
280 | if (wt->num_notes == MAX_NOTES) | |
281 | return; | |
282 | ||
283 | wt->notes[wt->num_notes] = os_malloc(len); | |
284 | if (wt->notes[wt->num_notes] == NULL) | |
285 | return; | |
286 | va_start(ap, fmt); | |
287 | wlen = vsnprintf(wt->notes[wt->num_notes], len, fmt, ap); | |
288 | va_end(ap); | |
289 | if (wlen < 0) { | |
290 | os_free(wt->notes[wt->num_notes]); | |
291 | wt->notes[wt->num_notes] = NULL; | |
292 | return; | |
293 | } | |
294 | if (wlen >= len) | |
295 | wt->notes[wt->num_notes][len - 1] = '\0'; | |
296 | wpa_printf(level, "%s", wt->notes[wt->num_notes]); | |
297 | wt->num_notes++; | |
298 | } | |
299 | ||
300 | ||
301 | void clear_notes(struct wlantest *wt) | |
302 | { | |
303 | size_t i; | |
304 | ||
305 | for (i = 0; i < wt->num_notes; i++) { | |
306 | os_free(wt->notes[i]); | |
307 | wt->notes[i] = NULL; | |
308 | } | |
309 | ||
310 | wt->num_notes = 0; | |
311 | } | |
312 | ||
313 | ||
314 | size_t notes_len(struct wlantest *wt, size_t hdrlen) | |
315 | { | |
316 | size_t i; | |
317 | size_t len = wt->num_notes * hdrlen; | |
318 | ||
319 | for (i = 0; i < wt->num_notes; i++) | |
320 | len += os_strlen(wt->notes[i]); | |
321 | ||
322 | return len; | |
323 | } | |
324 | ||
325 | ||
c99a721e JM |
326 | int wlantest_relog(struct wlantest *wt) |
327 | { | |
328 | int ret = 0; | |
329 | ||
330 | wpa_printf(MSG_INFO, "Re-open log/capture files"); | |
d33fef57 JM |
331 | if (wpa_debug_reopen_file()) |
332 | ret = -1; | |
c99a721e JM |
333 | |
334 | if (wt->write_file) { | |
335 | write_pcap_deinit(wt); | |
336 | if (write_pcap_init(wt, wt->write_file) < 0) | |
337 | ret = -1; | |
338 | } | |
339 | ||
340 | if (wt->pcapng_file) { | |
341 | write_pcapng_deinit(wt); | |
342 | if (write_pcapng_init(wt, wt->pcapng_file) < 0) | |
343 | ret = -1; | |
344 | } | |
345 | ||
346 | return ret; | |
347 | } | |
348 | ||
349 | ||
a149fcc7 JM |
350 | int main(int argc, char *argv[]) |
351 | { | |
352 | int c; | |
353 | const char *read_file = NULL; | |
3215df77 | 354 | const char *read_wired_file = NULL; |
a149fcc7 | 355 | const char *ifname = NULL; |
3215df77 | 356 | const char *ifname_wired = NULL; |
d33fef57 | 357 | const char *logfile = NULL; |
a149fcc7 | 358 | struct wlantest wt; |
644fb8c8 | 359 | int ctrl_iface = 0; |
a149fcc7 JM |
360 | |
361 | wpa_debug_level = MSG_INFO; | |
53650bca | 362 | wpa_debug_show_keys = 1; |
a149fcc7 JM |
363 | |
364 | if (os_program_init()) | |
365 | return -1; | |
366 | ||
d84d3893 | 367 | wlantest_init(&wt); |
a149fcc7 JM |
368 | |
369 | for (;;) { | |
e929eb39 | 370 | c = getopt(argc, argv, "cdf:Fhi:I:L:n:Np:P:qr:R:tT:w:W:"); |
a149fcc7 JM |
371 | if (c < 0) |
372 | break; | |
373 | switch (c) { | |
644fb8c8 JM |
374 | case 'c': |
375 | ctrl_iface = 1; | |
376 | break; | |
a149fcc7 JM |
377 | case 'd': |
378 | if (wpa_debug_level > 0) | |
379 | wpa_debug_level--; | |
380 | break; | |
219fd441 JM |
381 | case 'f': |
382 | if (add_pmk_file(&wt, optarg) < 0) | |
383 | return -1; | |
384 | break; | |
25315176 JM |
385 | case 'F': |
386 | wt.assume_fcs = 1; | |
387 | break; | |
a149fcc7 JM |
388 | case 'h': |
389 | usage(); | |
390 | return 0; | |
391 | case 'i': | |
392 | ifname = optarg; | |
393 | break; | |
3215df77 JM |
394 | case 'I': |
395 | ifname_wired = optarg; | |
396 | break; | |
d33fef57 JM |
397 | case 'L': |
398 | logfile = optarg; | |
399 | break; | |
ba2beacc | 400 | case 'n': |
c99a721e | 401 | wt.pcapng_file = optarg; |
ba2beacc | 402 | break; |
e929eb39 JM |
403 | case 'N': |
404 | wt.pcap_no_buffer = 1; | |
405 | break; | |
53650bca JM |
406 | case 'p': |
407 | add_passphrase(&wt, optarg); | |
408 | break; | |
d06df64d JM |
409 | case 'P': |
410 | add_secret(&wt, optarg); | |
411 | break; | |
a149fcc7 JM |
412 | case 'q': |
413 | wpa_debug_level++; | |
414 | break; | |
415 | case 'r': | |
416 | read_file = optarg; | |
417 | break; | |
3215df77 JM |
418 | case 'R': |
419 | read_wired_file = optarg; | |
420 | break; | |
de8bb171 JM |
421 | case 't': |
422 | wpa_debug_timestamp = 1; | |
423 | break; | |
a0530dff JM |
424 | case 'T': |
425 | if (add_ptk_file(&wt, optarg) < 0) | |
426 | return -1; | |
427 | break; | |
64f45d07 | 428 | case 'w': |
c99a721e | 429 | wt.write_file = optarg; |
64f45d07 | 430 | break; |
2e479416 | 431 | case 'W': |
9a994178 JM |
432 | if (add_wep(&wt, optarg) < 0) |
433 | return -1; | |
2e479416 | 434 | break; |
a149fcc7 JM |
435 | default: |
436 | usage(); | |
437 | return -1; | |
438 | } | |
439 | } | |
440 | ||
3215df77 JM |
441 | if (ifname == NULL && ifname_wired == NULL && |
442 | read_file == NULL && read_wired_file == NULL) { | |
a149fcc7 JM |
443 | usage(); |
444 | return 0; | |
445 | } | |
446 | ||
447 | if (eloop_init()) | |
448 | return -1; | |
449 | ||
d33fef57 JM |
450 | if (logfile) |
451 | wpa_debug_open_file(logfile); | |
452 | ||
c99a721e | 453 | if (wt.write_file && write_pcap_init(&wt, wt.write_file) < 0) |
64f45d07 JM |
454 | return -1; |
455 | ||
c99a721e | 456 | if (wt.pcapng_file && write_pcapng_init(&wt, wt.pcapng_file) < 0) |
ba2beacc JM |
457 | return -1; |
458 | ||
3215df77 JM |
459 | if (read_wired_file && read_wired_cap_file(&wt, read_wired_file) < 0) |
460 | return -1; | |
461 | ||
a149fcc7 JM |
462 | if (read_file && read_cap_file(&wt, read_file) < 0) |
463 | return -1; | |
464 | ||
465 | if (ifname && monitor_init(&wt, ifname) < 0) | |
466 | return -1; | |
467 | ||
3215df77 JM |
468 | if (ifname_wired && monitor_init_wired(&wt, ifname_wired) < 0) |
469 | return -1; | |
470 | ||
644fb8c8 JM |
471 | if (ctrl_iface && ctrl_init(&wt) < 0) |
472 | return -1; | |
473 | ||
a149fcc7 JM |
474 | eloop_register_signal_terminate(wlantest_terminate, &wt); |
475 | ||
476 | eloop_run(); | |
477 | ||
478 | wpa_printf(MSG_INFO, "Processed: rx_mgmt=%u rx_ctrl=%u rx_data=%u " | |
479 | "fcs_error=%u", | |
480 | wt.rx_mgmt, wt.rx_ctrl, wt.rx_data, wt.fcs_error); | |
481 | ||
d84d3893 | 482 | wlantest_deinit(&wt); |
a149fcc7 | 483 | |
d33fef57 | 484 | wpa_debug_close_file(); |
a149fcc7 JM |
485 | eloop_destroy(); |
486 | os_program_deinit(); | |
487 | ||
488 | return 0; | |
489 | } |