]>
Commit | Line | Data |
---|---|---|
7d23e971 JM |
1 | /* |
2 | * wlantest frame injection | |
98cd3d1c | 3 | * Copyright (c) 2010-2015, Jouni Malinen <j@w1.fi> |
7d23e971 | 4 | * |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
7d23e971 JM |
7 | */ |
8 | ||
9 | #include "utils/includes.h" | |
10 | ||
11 | #include "utils/common.h" | |
571ab37b JM |
12 | #include "common/defs.h" |
13 | #include "common/ieee802_11_defs.h" | |
2e4c3469 | 14 | #include "crypto/aes_wrap.h" |
7d23e971 JM |
15 | #include "wlantest.h" |
16 | ||
17 | ||
18 | static int inject_frame(int s, const void *data, size_t len) | |
19 | { | |
20 | #define IEEE80211_RADIOTAP_F_FRAG 0x08 | |
21 | unsigned char rtap_hdr[] = { | |
22 | 0x00, 0x00, /* radiotap version */ | |
23 | 0x0e, 0x00, /* radiotap length */ | |
24 | 0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */ | |
25 | IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */ | |
26 | 0x00, /* padding */ | |
27 | 0x00, 0x00, /* RX and TX flags to indicate that */ | |
28 | 0x00, 0x00, /* this is the injected frame directly */ | |
29 | }; | |
30 | struct iovec iov[2] = { | |
31 | { | |
32 | .iov_base = &rtap_hdr, | |
33 | .iov_len = sizeof(rtap_hdr), | |
34 | }, | |
35 | { | |
36 | .iov_base = (void *) data, | |
37 | .iov_len = len, | |
38 | } | |
39 | }; | |
40 | struct msghdr msg = { | |
41 | .msg_name = NULL, | |
42 | .msg_namelen = 0, | |
43 | .msg_iov = iov, | |
44 | .msg_iovlen = 2, | |
45 | .msg_control = NULL, | |
46 | .msg_controllen = 0, | |
47 | .msg_flags = 0, | |
48 | }; | |
49 | int ret; | |
50 | ||
51 | ret = sendmsg(s, &msg, 0); | |
52 | if (ret < 0) | |
a193231d | 53 | wpa_printf(MSG_ERROR, "sendmsg: %s", strerror(errno)); |
7d23e971 JM |
54 | return ret; |
55 | } | |
56 | ||
57 | ||
571ab37b JM |
58 | static int is_robust_mgmt(u8 *frame, size_t len) |
59 | { | |
60 | struct ieee80211_mgmt *mgmt; | |
61 | u16 fc, stype; | |
62 | if (len < 24) | |
63 | return 0; | |
64 | mgmt = (struct ieee80211_mgmt *) frame; | |
65 | fc = le_to_host16(mgmt->frame_control); | |
66 | if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) | |
67 | return 0; | |
68 | stype = WLAN_FC_GET_STYPE(fc); | |
69 | if (stype == WLAN_FC_STYPE_DEAUTH || stype == WLAN_FC_STYPE_DISASSOC) | |
70 | return 1; | |
71 | if (stype == WLAN_FC_STYPE_ACTION) { | |
72 | if (len < 25) | |
73 | return 0; | |
74 | if (mgmt->u.action.category != WLAN_ACTION_PUBLIC) | |
75 | return 1; | |
76 | } | |
77 | return 0; | |
78 | } | |
79 | ||
80 | ||
2e4c3469 JM |
81 | static int wlantest_inject_bip(struct wlantest *wt, struct wlantest_bss *bss, |
82 | u8 *frame, size_t len, int incorrect_key) | |
83 | { | |
a9eae7ef | 84 | u8 *prot; |
cb80fada | 85 | u8 dummy[32]; |
2e4c3469 | 86 | int ret; |
2e4c3469 JM |
87 | size_t plen; |
88 | ||
cb80fada | 89 | if (!bss->igtk_len[bss->igtk_idx]) |
2e4c3469 JM |
90 | return -1; |
91 | ||
a9eae7ef | 92 | os_memset(dummy, 0x11, sizeof(dummy)); |
2e4c3469 | 93 | inc_byte_array(bss->ipn[bss->igtk_idx], 6); |
2e4c3469 | 94 | |
a9eae7ef | 95 | prot = bip_protect(incorrect_key ? dummy : bss->igtk[bss->igtk_idx], |
cb80fada | 96 | bss->igtk_len[bss->igtk_idx], |
a9eae7ef JM |
97 | frame, len, bss->ipn[bss->igtk_idx], |
98 | bss->igtk_idx, &plen); | |
99 | if (prot == NULL) | |
2e4c3469 | 100 | return -1; |
2e4c3469 | 101 | |
2e4c3469 JM |
102 | |
103 | ret = inject_frame(wt->monitor_sock, prot, plen); | |
104 | os_free(prot); | |
105 | ||
106 | return (ret < 0) ? -1 : 0; | |
107 | } | |
108 | ||
109 | ||
110 | static int wlantest_inject_prot_bc(struct wlantest *wt, | |
111 | struct wlantest_bss *bss, | |
112 | u8 *frame, size_t len, int incorrect_key) | |
113 | { | |
114 | u8 *crypt; | |
115 | size_t crypt_len; | |
116 | int ret; | |
117 | u8 dummy[64]; | |
118 | u8 *pn; | |
119 | struct ieee80211_hdr *hdr; | |
120 | u16 fc; | |
121 | int hdrlen; | |
122 | ||
123 | hdr = (struct ieee80211_hdr *) frame; | |
124 | hdrlen = 24; | |
125 | fc = le_to_host16(hdr->frame_control); | |
126 | ||
127 | if (!bss->gtk_len[bss->gtk_idx]) | |
128 | return -1; | |
129 | ||
130 | if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == | |
131 | (WLAN_FC_TODS | WLAN_FC_FROMDS)) | |
132 | hdrlen += ETH_ALEN; | |
133 | pn = bss->rsc[bss->gtk_idx]; | |
134 | inc_byte_array(pn, 6); | |
135 | ||
136 | os_memset(dummy, 0x11, sizeof(dummy)); | |
137 | if (bss->group_cipher == WPA_CIPHER_TKIP) | |
138 | crypt = tkip_encrypt(incorrect_key ? dummy : | |
139 | bss->gtk[bss->gtk_idx], | |
140 | frame, len, hdrlen, NULL, pn, | |
141 | bss->gtk_idx, &crypt_len); | |
142 | else | |
143 | crypt = ccmp_encrypt(incorrect_key ? dummy : | |
144 | bss->gtk[bss->gtk_idx], | |
145 | frame, len, hdrlen, NULL, pn, | |
146 | bss->gtk_idx, &crypt_len); | |
147 | ||
148 | if (crypt == NULL) | |
149 | return -1; | |
150 | ||
151 | ret = inject_frame(wt->monitor_sock, crypt, crypt_len); | |
152 | os_free(crypt); | |
153 | ||
154 | return (ret < 0) ? -1 : 0; | |
155 | } | |
156 | ||
157 | ||
571ab37b JM |
158 | static int wlantest_inject_prot(struct wlantest *wt, struct wlantest_bss *bss, |
159 | struct wlantest_sta *sta, u8 *frame, | |
160 | size_t len, int incorrect_key) | |
161 | { | |
162 | u8 *crypt; | |
163 | size_t crypt_len; | |
164 | int ret; | |
165 | u8 dummy[64]; | |
166 | u8 *pn; | |
167 | struct ieee80211_hdr *hdr; | |
168 | u16 fc; | |
169 | int tid = 0; | |
170 | u8 *qos = NULL; | |
171 | int hdrlen; | |
b3a6d9d4 JM |
172 | struct wlantest_tdls *tdls = NULL; |
173 | const u8 *tk = NULL; | |
571ab37b | 174 | |
2e4c3469 JM |
175 | hdr = (struct ieee80211_hdr *) frame; |
176 | hdrlen = 24; | |
177 | fc = le_to_host16(hdr->frame_control); | |
178 | ||
880a97dc JM |
179 | if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA && |
180 | (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == 0) { | |
b3a6d9d4 JM |
181 | struct wlantest_sta *sta2; |
182 | bss = bss_get(wt, hdr->addr3); | |
a8401116 JM |
183 | if (bss == NULL) { |
184 | wpa_printf(MSG_DEBUG, "No BSS found for TDLS " | |
185 | "injection"); | |
b3a6d9d4 | 186 | return -1; |
a8401116 | 187 | } |
b3a6d9d4 JM |
188 | sta = sta_find(bss, hdr->addr2); |
189 | sta2 = sta_find(bss, hdr->addr1); | |
a8401116 JM |
190 | if (sta == NULL || sta2 == NULL) { |
191 | wpa_printf(MSG_DEBUG, "No stations found for TDLS " | |
192 | "injection"); | |
b3a6d9d4 | 193 | return -1; |
a8401116 | 194 | } |
b3a6d9d4 JM |
195 | dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) |
196 | { | |
197 | if ((tdls->init == sta && tdls->resp == sta2) || | |
198 | (tdls->init == sta2 && tdls->resp == sta)) { | |
199 | if (!tdls->link_up) | |
200 | wpa_printf(MSG_DEBUG, "TDLS: Link not " | |
201 | "up, but injecting Data " | |
202 | "frame on direct link"); | |
203 | tk = tdls->tpk.tk; | |
204 | break; | |
205 | } | |
206 | } | |
207 | } | |
208 | ||
209 | if (tk == NULL && sta == NULL) { | |
2e4c3469 JM |
210 | if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) |
211 | return wlantest_inject_bip(wt, bss, frame, len, | |
212 | incorrect_key); | |
213 | return wlantest_inject_prot_bc(wt, bss, frame, len, | |
214 | incorrect_key); | |
215 | } | |
571ab37b | 216 | |
a8401116 JM |
217 | if (tk == NULL && !sta->ptk_set) { |
218 | wpa_printf(MSG_DEBUG, "No key known for injection"); | |
571ab37b | 219 | return -1; |
a8401116 | 220 | } |
571ab37b | 221 | |
571ab37b JM |
222 | if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) |
223 | tid = 16; | |
224 | else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) { | |
225 | if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == | |
226 | (WLAN_FC_TODS | WLAN_FC_FROMDS)) | |
227 | hdrlen += ETH_ALEN; | |
228 | if (WLAN_FC_GET_STYPE(fc) & 0x08) { | |
229 | qos = frame + hdrlen; | |
230 | hdrlen += 2; | |
231 | tid = qos[0] & 0x0f; | |
232 | } | |
233 | } | |
b3a6d9d4 JM |
234 | if (tk) { |
235 | if (os_memcmp(hdr->addr2, tdls->init->addr, ETH_ALEN) == 0) | |
236 | pn = tdls->rsc_init[tid]; | |
237 | else | |
238 | pn = tdls->rsc_resp[tid]; | |
239 | } else if (os_memcmp(hdr->addr2, bss->bssid, ETH_ALEN) == 0) | |
571ab37b JM |
240 | pn = sta->rsc_fromds[tid]; |
241 | else | |
242 | pn = sta->rsc_tods[tid]; | |
243 | inc_byte_array(pn, 6); | |
244 | ||
245 | os_memset(dummy, 0x11, sizeof(dummy)); | |
95de34a1 | 246 | if (tk) |
b3a6d9d4 JM |
247 | crypt = ccmp_encrypt(incorrect_key ? dummy : tk, |
248 | frame, len, hdrlen, qos, pn, 0, | |
249 | &crypt_len); | |
250 | else if (sta->pairwise_cipher == WPA_CIPHER_TKIP) | |
98cd3d1c | 251 | crypt = tkip_encrypt(incorrect_key ? dummy : sta->ptk.tk, |
571ab37b JM |
252 | frame, len, hdrlen, qos, pn, 0, |
253 | &crypt_len); | |
254 | else | |
98cd3d1c | 255 | crypt = ccmp_encrypt(incorrect_key ? dummy : sta->ptk.tk, |
571ab37b JM |
256 | frame, len, hdrlen, qos, pn, 0, |
257 | &crypt_len); | |
258 | ||
a8401116 JM |
259 | if (crypt == NULL) { |
260 | wpa_printf(MSG_DEBUG, "Frame encryption failed"); | |
571ab37b | 261 | return -1; |
a8401116 | 262 | } |
571ab37b | 263 | |
902621e2 | 264 | wpa_hexdump(MSG_DEBUG, "Inject frame (encrypted)", crypt, crypt_len); |
571ab37b JM |
265 | ret = inject_frame(wt->monitor_sock, crypt, crypt_len); |
266 | os_free(crypt); | |
a8401116 | 267 | wpa_printf(MSG_DEBUG, "inject_frame for protected frame: %d", ret); |
571ab37b JM |
268 | |
269 | return (ret < 0) ? -1 : 0; | |
270 | } | |
271 | ||
272 | ||
7d23e971 JM |
273 | int wlantest_inject(struct wlantest *wt, struct wlantest_bss *bss, |
274 | struct wlantest_sta *sta, u8 *frame, size_t len, | |
275 | enum wlantest_inject_protection prot) | |
276 | { | |
277 | int ret; | |
571ab37b JM |
278 | struct ieee80211_hdr *hdr; |
279 | u16 fc; | |
280 | int protectable, protect = 0; | |
7d23e971 JM |
281 | |
282 | wpa_hexdump(MSG_DEBUG, "Inject frame", frame, len); | |
283 | if (wt->monitor_sock < 0) { | |
284 | wpa_printf(MSG_INFO, "Cannot inject frames when monitor " | |
285 | "interface is not in use"); | |
286 | return -1; | |
287 | } | |
288 | ||
409cd147 C |
289 | if (prot != WLANTEST_INJECT_UNPROTECTED && bss == NULL) { |
290 | wpa_printf(MSG_INFO, "No BSS information to inject " | |
02b915f6 JM |
291 | "protected frames"); |
292 | return -1; | |
293 | } | |
294 | ||
571ab37b JM |
295 | hdr = (struct ieee80211_hdr *) frame; |
296 | fc = le_to_host16(hdr->frame_control); | |
297 | protectable = WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA || | |
298 | is_robust_mgmt(frame, len); | |
299 | ||
02b915f6 JM |
300 | if ((prot == WLANTEST_INJECT_PROTECTED || |
301 | prot == WLANTEST_INJECT_INCORRECT_KEY) && bss) { | |
2e4c3469 JM |
302 | if (!sta && |
303 | ((WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && | |
cb80fada | 304 | !bss->igtk_len[bss->igtk_idx]) || |
2e4c3469 JM |
305 | (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA && |
306 | !bss->gtk_len[bss->gtk_idx]))) { | |
307 | wpa_printf(MSG_INFO, "No GTK/IGTK known for " | |
308 | MACSTR " to protect the injected " | |
309 | "frame", MAC2STR(bss->bssid)); | |
571ab37b JM |
310 | return -1; |
311 | } | |
312 | if (sta && !sta->ptk_set) { | |
313 | wpa_printf(MSG_INFO, "No PTK known for the STA " MACSTR | |
314 | " to encrypt the injected frame", | |
315 | MAC2STR(sta->addr)); | |
316 | return -1; | |
317 | } | |
318 | protect = 1; | |
02b915f6 | 319 | } else if (protectable && prot != WLANTEST_INJECT_UNPROTECTED && bss) { |
571ab37b JM |
320 | if (sta && sta->ptk_set) |
321 | protect = 1; | |
322 | else if (!sta) { | |
323 | if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA && | |
2e4c3469 | 324 | bss->gtk_len[bss->gtk_idx]) |
571ab37b JM |
325 | protect = 1; |
326 | if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && | |
cb80fada | 327 | bss->igtk_len[bss->igtk_idx]) |
571ab37b JM |
328 | protect = 1; |
329 | } | |
330 | } | |
331 | ||
02b915f6 | 332 | if (protect && bss) |
571ab37b JM |
333 | return wlantest_inject_prot( |
334 | wt, bss, sta, frame, len, | |
335 | prot == WLANTEST_INJECT_INCORRECT_KEY); | |
7d23e971 JM |
336 | |
337 | ret = inject_frame(wt->monitor_sock, frame, len); | |
a8401116 | 338 | wpa_printf(MSG_DEBUG, "inject_frame for unprotected frame: %d", ret); |
7d23e971 JM |
339 | return (ret < 0) ? -1 : 0; |
340 | } |