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