]>
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 | ||
d06df64d JM |
90 | static void wlantest_add_pmk(struct wlantest *wt, const u8 *pmk) |
91 | { | |
92 | struct wlantest_pmk *p; | |
93 | ||
94 | p = os_zalloc(sizeof(*p)); | |
95 | if (p == NULL) | |
96 | return; | |
97 | os_memcpy(p->pmk, pmk, 32); | |
98 | dl_list_add(&wt->pmk, &p->list); | |
99 | wpa_hexdump(MSG_INFO, "Add PMK", pmk, 32); | |
100 | } | |
101 | ||
102 | ||
3215df77 JM |
103 | static void process_radius_access_accept(struct wlantest *wt, u32 dst, u32 src, |
104 | const u8 *data, size_t len) | |
105 | { | |
106 | struct radius_msg *msg; | |
d06df64d JM |
107 | struct wlantest_radius *r; |
108 | struct radius_ms_mppe_keys *keys; | |
109 | struct wlantest_radius_secret *s; | |
110 | ||
111 | r = radius_get(wt, src, dst); | |
112 | if (r == NULL || r->last_req == NULL) { | |
113 | wpa_printf(MSG_DEBUG, "No RADIUS Access-Challenge found for " | |
114 | "decrypting Access-Accept keys"); | |
115 | return; | |
116 | } | |
3215df77 JM |
117 | |
118 | msg = radius_msg_parse(data, len); | |
119 | if (msg == NULL) { | |
120 | wpa_printf(MSG_DEBUG, "Failed to parse RADIUS Access-Accept"); | |
121 | return; | |
122 | } | |
123 | ||
d06df64d JM |
124 | dl_list_for_each(s, &wt->secret, struct wlantest_radius_secret, list) { |
125 | int found = 0; | |
126 | keys = radius_msg_get_ms_keys(msg, r->last_req, | |
127 | (u8 *) s->secret, | |
128 | os_strlen(s->secret)); | |
129 | if (keys && keys->send && keys->recv) { | |
130 | u8 pmk[32]; | |
131 | wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key", | |
132 | keys->send, keys->send_len); | |
133 | wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key", | |
134 | keys->recv, keys->recv_len); | |
135 | os_memcpy(pmk, keys->recv, | |
136 | keys->recv_len > 32 ? 32 : keys->recv_len); | |
137 | if (keys->recv_len < 32) { | |
138 | os_memcpy(pmk + keys->recv_len, | |
139 | keys->send, | |
140 | keys->recv_len + keys->send_len > 32 | |
141 | ? 32 : 32 - keys->recv_len); | |
142 | } | |
143 | wlantest_add_pmk(wt, pmk); | |
144 | found = 1; | |
145 | } | |
146 | ||
147 | if (keys) { | |
148 | os_free(keys->send); | |
149 | os_free(keys->recv); | |
150 | os_free(keys); | |
151 | } | |
152 | ||
153 | if (found) | |
154 | break; | |
155 | } | |
156 | ||
3215df77 JM |
157 | radius_msg_free(msg); |
158 | } | |
159 | ||
160 | ||
161 | static void process_radius(struct wlantest *wt, u32 dst, u16 dport, u32 src, | |
162 | u16 sport, const u8 *data, size_t len) | |
163 | { | |
164 | struct in_addr addr; | |
165 | char buf[20]; | |
166 | const struct radius_hdr *hdr; | |
167 | u16 rlen; | |
168 | ||
169 | if (len < sizeof(*hdr)) | |
170 | return; | |
171 | hdr = (const struct radius_hdr *) data; | |
172 | rlen = be_to_host16(hdr->length); | |
173 | if (len < rlen) | |
174 | return; | |
175 | if (len > rlen) | |
176 | len = rlen; | |
177 | ||
178 | addr.s_addr = dst; | |
179 | snprintf(buf, sizeof(buf), "%s", inet_ntoa(addr)); | |
180 | ||
181 | addr.s_addr = src; | |
182 | wpa_printf(MSG_DEBUG, "RADIUS %s:%u -> %s:%u id=%u %s", | |
183 | inet_ntoa(addr), sport, buf, dport, hdr->identifier, | |
184 | radius_code_string(hdr->code)); | |
185 | ||
186 | switch (hdr->code) { | |
187 | case RADIUS_CODE_ACCESS_REQUEST: | |
188 | process_radius_access_request(wt, dst, src, data, len); | |
189 | break; | |
190 | case RADIUS_CODE_ACCESS_ACCEPT: | |
191 | process_radius_access_accept(wt, dst, src, data, len); | |
192 | break; | |
193 | } | |
194 | } | |
195 | ||
196 | ||
197 | static void process_udp(struct wlantest *wt, u32 dst, u32 src, | |
198 | const u8 *data, size_t len) | |
199 | { | |
200 | const struct udphdr *udp; | |
201 | u16 sport, dport, ulen; | |
202 | const u8 *payload; | |
203 | size_t plen; | |
204 | ||
205 | if (len < sizeof(*udp)) | |
206 | return; | |
207 | udp = (const struct udphdr *) data; | |
208 | /* TODO: check UDP checksum */ | |
209 | sport = be_to_host16(udp->source); | |
210 | dport = be_to_host16(udp->dest); | |
211 | ulen = be_to_host16(udp->len); | |
212 | ||
213 | if (ulen > len) | |
214 | return; | |
215 | if (len < ulen) | |
216 | len = ulen; | |
217 | ||
218 | payload = (const u8 *) (udp + 1); | |
219 | plen = len - sizeof(*udp); | |
220 | ||
221 | if (sport == 1812 || dport == 1812) | |
222 | process_radius(wt, dst, dport, src, sport, payload, plen); | |
223 | } | |
224 | ||
225 | ||
226 | static void process_ipv4(struct wlantest *wt, const u8 *data, size_t len) | |
227 | { | |
228 | const struct iphdr *ip; | |
229 | const u8 *payload; | |
230 | size_t plen; | |
231 | u16 frag_off, tot_len; | |
232 | ||
233 | if (len < sizeof(*ip)) | |
234 | return; | |
235 | ||
236 | ip = (const struct iphdr *) data; | |
237 | if (ip->version != 4) | |
238 | return; | |
239 | if (ip->ihl < 5) | |
240 | return; | |
241 | ||
242 | /* TODO: check header checksum in ip->check */ | |
243 | ||
244 | frag_off = be_to_host16(ip->frag_off); | |
245 | if (frag_off & 0x1fff) { | |
246 | wpa_printf(MSG_EXCESSIVE, "IP fragment reassembly not yet " | |
247 | "supported"); | |
248 | return; | |
249 | } | |
250 | ||
251 | tot_len = be_to_host16(ip->tot_len); | |
252 | if (tot_len > len) | |
253 | return; | |
254 | if (tot_len < len) | |
255 | len = tot_len; | |
256 | ||
257 | payload = data + 4 * ip->ihl; | |
258 | plen = len - 4 * ip->ihl; | |
259 | if (payload + plen > data + len) | |
260 | return; | |
261 | ||
262 | switch (ip->protocol) { | |
263 | case IPPROTO_UDP: | |
264 | process_udp(wt, ip->daddr, ip->saddr, payload, plen); | |
265 | break; | |
266 | } | |
267 | } | |
268 | ||
269 | ||
270 | void wlantest_process_wired(struct wlantest *wt, const u8 *data, size_t len) | |
271 | { | |
272 | const struct ether_header *eth; | |
273 | u16 ethertype; | |
274 | ||
275 | wpa_hexdump(MSG_EXCESSIVE, "Process wired frame", data, len); | |
276 | ||
277 | if (len < sizeof(*eth)) | |
278 | return; | |
279 | ||
280 | eth = (const struct ether_header *) data; | |
281 | ethertype = be_to_host16(eth->ether_type); | |
282 | ||
283 | switch (ethertype) { | |
284 | case ETHERTYPE_IP: | |
285 | process_ipv4(wt, data + sizeof(*eth), len - sizeof(*eth)); | |
286 | break; | |
287 | } | |
288 | } |