1  /*
2   * Received frame processing for wired interface
3   * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
4   *
5   * This software may be distributed under the terms of the BSD license.
6   * See README for more details.
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  
radius_get(struct wlantest * wt,u32 srv,u32 cli)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  
radius_code_string(u8 code)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  
process_radius_access_request(struct wlantest * wt,u32 dst,u32 src,const u8 * data,size_t len)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;
72  	struct wlantest_radius *r;
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  
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  	}
86  	radius_msg_free(msg);
87  }
88  
89  
wlantest_add_pmk(struct wlantest * wt,const u8 * pmk,size_t pmk_len)90  static void wlantest_add_pmk(struct wlantest *wt, const u8 *pmk, size_t pmk_len)
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, pmk_len);
98  	p->pmk_len = pmk_len;
99  	dl_list_add(&wt->pmk, &p->list);
100  	wpa_hexdump(MSG_INFO, "Add PMK", pmk, pmk_len);
101  }
102  
103  
process_radius_access_accept(struct wlantest * wt,u32 dst,u32 src,const u8 * data,size_t len)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;
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  	}
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  
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) {
131  			u8 pmk[PMK_LEN_MAX];
132  			size_t pmk_len, len2;
133  
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);
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;
148  			}
149  			wlantest_add_pmk(wt, pmk, pmk_len);
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  
163  	radius_msg_free(msg);
164  }
165  
166  
process_radius(struct wlantest * wt,u32 dst,u16 dport,u32 src,u16 sport,const u8 * data,size_t len)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  
process_udp(struct wlantest * wt,u32 dst,u32 src,const u8 * data,size_t len)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->uh_sport);
216  	dport = be_to_host16(udp->uh_dport);
217  	ulen = be_to_host16(udp->uh_ulen);
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  
process_ipv4(struct wlantest * wt,const u8 * data,size_t len)232  static void process_ipv4(struct wlantest *wt, const u8 *data, size_t len)
233  {
234  	const struct ip *ip;
235  	const u8 *payload;
236  	size_t plen;
237  	uint16_t frag_off, ip_len;
238  
239  	if (len < sizeof(*ip))
240  		return;
241  
242  	ip = (const struct ip *) data;
243  	if (ip->ip_v != 4)
244  		return;
245  	if (ip->ip_hl < 5)
246  		return;
247  
248  	/* TODO: check header checksum in ip->check */
249  
250  	frag_off = be_to_host16(ip->ip_off);
251  	if (frag_off & 0x1fff) {
252  		wpa_printf(MSG_EXCESSIVE, "IP fragment reassembly not yet "
253  			   "supported");
254  		return;
255  	}
256  
257  	ip_len = be_to_host16(ip->ip_len);
258  	if (ip_len > len)
259  		return;
260  	if (ip_len < len)
261  		len = ip_len;
262  
263  	payload = data + 4 * ip->ip_hl;
264  	plen = len - 4 * ip->ip_hl;
265  	if (payload + plen > data + len)
266  		return;
267  
268  	switch (ip->ip_p) {
269  	case IPPROTO_UDP:
270  		process_udp(wt, ip->ip_dst.s_addr, ip->ip_src.s_addr,
271  			    payload, plen);
272  		break;
273  	}
274  }
275  
276  
wlantest_process_wired(struct wlantest * wt,const u8 * data,size_t len)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  }
296