]>
Commit | Line | Data |
---|---|---|
3215df77 JM |
1 | /* |
2 | * Received frame processing for wired interface | |
3 | * Copyright (c) 2010, Jouni Malinen <j@w1.fi> | |
4 | * | |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
3215df77 JM |
7 | */ |
8 | ||
9 | #include "utils/includes.h" | |
10 | #include <net/ethernet.h> | |
11 | #include <netinet/ip.h> | |
12 | #include <netinet/udp.h> | |
13 | ||
14 | #include "utils/common.h" | |
15 | #include "radius/radius.h" | |
16 | #include "wlantest.h" | |
17 | ||
18 | ||
d06df64d JM |
19 | static struct wlantest_radius * radius_get(struct wlantest *wt, u32 srv, |
20 | u32 cli) | |
21 | { | |
22 | struct wlantest_radius *r; | |
23 | ||
24 | dl_list_for_each(r, &wt->radius, struct wlantest_radius, list) { | |
25 | if (r->srv == srv && r->cli == cli) | |
26 | return r; | |
27 | } | |
28 | ||
29 | r = os_zalloc(sizeof(*r)); | |
30 | if (r == NULL) | |
31 | return NULL; | |
32 | ||
33 | r->srv = srv; | |
34 | r->cli = cli; | |
35 | dl_list_add(&wt->radius, &r->list); | |
36 | ||
37 | return r; | |
38 | } | |
39 | ||
40 | ||
3215df77 JM |
41 | static const char * radius_code_string(u8 code) |
42 | { | |
43 | switch (code) { | |
44 | case RADIUS_CODE_ACCESS_REQUEST: | |
45 | return "Access-Request"; | |
46 | case RADIUS_CODE_ACCESS_ACCEPT: | |
47 | return "Access-Accept"; | |
48 | case RADIUS_CODE_ACCESS_REJECT: | |
49 | return "Access-Reject"; | |
50 | case RADIUS_CODE_ACCOUNTING_REQUEST: | |
51 | return "Accounting-Request"; | |
52 | case RADIUS_CODE_ACCOUNTING_RESPONSE: | |
53 | return "Accounting-Response"; | |
54 | case RADIUS_CODE_ACCESS_CHALLENGE: | |
55 | return "Access-Challenge"; | |
56 | case RADIUS_CODE_STATUS_SERVER: | |
57 | return "Status-Server"; | |
58 | case RADIUS_CODE_STATUS_CLIENT: | |
59 | return "Status-Client"; | |
60 | case RADIUS_CODE_RESERVED: | |
61 | return "Reserved"; | |
62 | default: | |
63 | return "?Unknown?"; | |
64 | } | |
65 | } | |
66 | ||
67 | ||
68 | static void process_radius_access_request(struct wlantest *wt, u32 dst, | |
69 | u32 src, const u8 *data, size_t len) | |
70 | { | |
71 | struct radius_msg *msg; | |
d06df64d | 72 | struct wlantest_radius *r; |
3215df77 JM |
73 | |
74 | msg = radius_msg_parse(data, len); | |
75 | if (msg == NULL) { | |
76 | wpa_printf(MSG_DEBUG, "Failed to parse RADIUS Access-Request"); | |
77 | return; | |
78 | } | |
79 | ||
d06df64d JM |
80 | r = radius_get(wt, dst, src); |
81 | if (r) { | |
82 | radius_msg_free(r->last_req); | |
83 | r->last_req = msg; | |
84 | return; | |
85 | } | |
3215df77 JM |
86 | radius_msg_free(msg); |
87 | } | |
88 | ||
89 | ||
6c29d95a | 90 | static void wlantest_add_pmk(struct wlantest *wt, const u8 *pmk, size_t pmk_len) |
d06df64d JM |
91 | { |
92 | struct wlantest_pmk *p; | |
93 | ||
94 | p = os_zalloc(sizeof(*p)); | |
95 | if (p == NULL) | |
96 | return; | |
6c29d95a JM |
97 | os_memcpy(p->pmk, pmk, pmk_len); |
98 | p->pmk_len = pmk_len; | |
d06df64d | 99 | dl_list_add(&wt->pmk, &p->list); |
6c29d95a | 100 | wpa_hexdump(MSG_INFO, "Add PMK", pmk, pmk_len); |
d06df64d JM |
101 | } |
102 | ||
103 | ||
3215df77 JM |
104 | static void process_radius_access_accept(struct wlantest *wt, u32 dst, u32 src, |
105 | const u8 *data, size_t len) | |
106 | { | |
107 | struct radius_msg *msg; | |
d06df64d JM |
108 | struct wlantest_radius *r; |
109 | struct radius_ms_mppe_keys *keys; | |
110 | struct wlantest_radius_secret *s; | |
111 | ||
112 | r = radius_get(wt, src, dst); | |
113 | if (r == NULL || r->last_req == NULL) { | |
114 | wpa_printf(MSG_DEBUG, "No RADIUS Access-Challenge found for " | |
115 | "decrypting Access-Accept keys"); | |
116 | return; | |
117 | } | |
3215df77 JM |
118 | |
119 | msg = radius_msg_parse(data, len); | |
120 | if (msg == NULL) { | |
121 | wpa_printf(MSG_DEBUG, "Failed to parse RADIUS Access-Accept"); | |
122 | return; | |
123 | } | |
124 | ||
d06df64d JM |
125 | dl_list_for_each(s, &wt->secret, struct wlantest_radius_secret, list) { |
126 | int found = 0; | |
127 | keys = radius_msg_get_ms_keys(msg, r->last_req, | |
128 | (u8 *) s->secret, | |
129 | os_strlen(s->secret)); | |
130 | if (keys && keys->send && keys->recv) { | |
6c29d95a JM |
131 | u8 pmk[PMK_LEN_MAX]; |
132 | size_t pmk_len, len2; | |
133 | ||
d06df64d JM |
134 | wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key", |
135 | keys->send, keys->send_len); | |
136 | wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key", | |
137 | keys->recv, keys->recv_len); | |
6c29d95a JM |
138 | pmk_len = keys->recv_len; |
139 | if (pmk_len > PMK_LEN_MAX) | |
140 | pmk_len = PMK_LEN_MAX; | |
141 | os_memcpy(pmk, keys->recv, pmk_len); | |
142 | if (pmk_len < PMK_LEN_MAX) { | |
143 | len2 = keys->send_len; | |
144 | if (pmk_len + len2 > PMK_LEN_MAX) | |
145 | len2 = PMK_LEN_MAX - pmk_len; | |
146 | os_memcpy(pmk + pmk_len, keys->send, len2); | |
147 | pmk_len += len2; | |
d06df64d | 148 | } |
6c29d95a | 149 | wlantest_add_pmk(wt, pmk, pmk_len); |
d06df64d JM |
150 | found = 1; |
151 | } | |
152 | ||
153 | if (keys) { | |
154 | os_free(keys->send); | |
155 | os_free(keys->recv); | |
156 | os_free(keys); | |
157 | } | |
158 | ||
159 | if (found) | |
160 | break; | |
161 | } | |
162 | ||
3215df77 JM |
163 | radius_msg_free(msg); |
164 | } | |
165 | ||
166 | ||
167 | static void process_radius(struct wlantest *wt, u32 dst, u16 dport, u32 src, | |
168 | u16 sport, const u8 *data, size_t len) | |
169 | { | |
170 | struct in_addr addr; | |
171 | char buf[20]; | |
172 | const struct radius_hdr *hdr; | |
173 | u16 rlen; | |
174 | ||
175 | if (len < sizeof(*hdr)) | |
176 | return; | |
177 | hdr = (const struct radius_hdr *) data; | |
178 | rlen = be_to_host16(hdr->length); | |
179 | if (len < rlen) | |
180 | return; | |
181 | if (len > rlen) | |
182 | len = rlen; | |
183 | ||
184 | addr.s_addr = dst; | |
185 | snprintf(buf, sizeof(buf), "%s", inet_ntoa(addr)); | |
186 | ||
187 | addr.s_addr = src; | |
188 | wpa_printf(MSG_DEBUG, "RADIUS %s:%u -> %s:%u id=%u %s", | |
189 | inet_ntoa(addr), sport, buf, dport, hdr->identifier, | |
190 | radius_code_string(hdr->code)); | |
191 | ||
192 | switch (hdr->code) { | |
193 | case RADIUS_CODE_ACCESS_REQUEST: | |
194 | process_radius_access_request(wt, dst, src, data, len); | |
195 | break; | |
196 | case RADIUS_CODE_ACCESS_ACCEPT: | |
197 | process_radius_access_accept(wt, dst, src, data, len); | |
198 | break; | |
199 | } | |
200 | } | |
201 | ||
202 | ||
203 | static void process_udp(struct wlantest *wt, u32 dst, u32 src, | |
204 | const u8 *data, size_t len) | |
205 | { | |
206 | const struct udphdr *udp; | |
207 | u16 sport, dport, ulen; | |
208 | const u8 *payload; | |
209 | size_t plen; | |
210 | ||
211 | if (len < sizeof(*udp)) | |
212 | return; | |
213 | udp = (const struct udphdr *) data; | |
214 | /* TODO: check UDP checksum */ | |
215 | sport = be_to_host16(udp->source); | |
216 | dport = be_to_host16(udp->dest); | |
217 | ulen = be_to_host16(udp->len); | |
218 | ||
219 | if (ulen > len) | |
220 | return; | |
221 | if (len < ulen) | |
222 | len = ulen; | |
223 | ||
224 | payload = (const u8 *) (udp + 1); | |
225 | plen = len - sizeof(*udp); | |
226 | ||
227 | if (sport == 1812 || dport == 1812) | |
228 | process_radius(wt, dst, dport, src, sport, payload, plen); | |
229 | } | |
230 | ||
231 | ||
232 | static void process_ipv4(struct wlantest *wt, const u8 *data, size_t len) | |
233 | { | |
a8b00423 | 234 | const struct ip *ip; |
3215df77 JM |
235 | const u8 *payload; |
236 | size_t plen; | |
a8b00423 | 237 | uint16_t frag_off, ip_len; |
3215df77 JM |
238 | |
239 | if (len < sizeof(*ip)) | |
240 | return; | |
241 | ||
a8b00423 RM |
242 | ip = (const struct ip *) data; |
243 | if (ip->ip_v != 4) | |
3215df77 | 244 | return; |
a8b00423 | 245 | if (ip->ip_hl < 5) |
3215df77 JM |
246 | return; |
247 | ||
248 | /* TODO: check header checksum in ip->check */ | |
249 | ||
a8b00423 | 250 | frag_off = be_to_host16(ip->ip_off); |
3215df77 JM |
251 | if (frag_off & 0x1fff) { |
252 | wpa_printf(MSG_EXCESSIVE, "IP fragment reassembly not yet " | |
253 | "supported"); | |
254 | return; | |
255 | } | |
256 | ||
a8b00423 RM |
257 | ip_len = be_to_host16(ip->ip_len); |
258 | if (ip_len > len) | |
3215df77 | 259 | return; |
a8b00423 RM |
260 | if (ip_len < len) |
261 | len = ip_len; | |
3215df77 | 262 | |
a8b00423 RM |
263 | payload = data + 4 * ip->ip_hl; |
264 | plen = len - 4 * ip->ip_hl; | |
3215df77 JM |
265 | if (payload + plen > data + len) |
266 | return; | |
267 | ||
a8b00423 | 268 | switch (ip->ip_p) { |
3215df77 | 269 | case IPPROTO_UDP: |
a8b00423 RM |
270 | process_udp(wt, ip->ip_dst.s_addr, ip->ip_src.s_addr, |
271 | payload, plen); | |
3215df77 JM |
272 | break; |
273 | } | |
274 | } | |
275 | ||
276 | ||
277 | void wlantest_process_wired(struct wlantest *wt, const u8 *data, size_t len) | |
278 | { | |
279 | const struct ether_header *eth; | |
280 | u16 ethertype; | |
281 | ||
282 | wpa_hexdump(MSG_EXCESSIVE, "Process wired frame", data, len); | |
283 | ||
284 | if (len < sizeof(*eth)) | |
285 | return; | |
286 | ||
287 | eth = (const struct ether_header *) data; | |
288 | ethertype = be_to_host16(eth->ether_type); | |
289 | ||
290 | switch (ethertype) { | |
291 | case ETHERTYPE_IP: | |
292 | process_ipv4(wt, data + sizeof(*eth), len - sizeof(*eth)); | |
293 | break; | |
294 | } | |
295 | } |