K2.6 patches and update.
[tomato.git] / release / src-rt / wl / nas / nas_radius.c
blobad91724dfc37ca4fe28d5a138f4676653e03cfa7
1 /*
2 * Radius code which used to be in nas.c
3 * Radius support for Network Access Server
4 * Copyright (C) 2010, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
8 * the contents of this file may not be disclosed to third parties, copied
9 * or duplicated in any form, in whole or in part, without the prior
10 * written permission of Broadcom Corporation.
11 * $Id: nas_radius.c 241388 2011-02-18 03:33:22Z stakita $
14 #include <typedefs.h>
15 #include <stdlib.h>
17 #include <proto/eap.h>
18 #include <bcmutils.h>
19 #ifdef __ECOS
20 #include <sys/socket.h>
21 #include <net/if.h>
22 #endif
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
26 #include <nas.h>
27 #include <nas_wksp.h>
28 #include <nas_radius.h>
29 #include <nas_wksp_radius.h>
30 #include <mppe.h>
32 static void radius_add(radius_header_t *radius, unsigned char type,
33 unsigned char *buf, unsigned char length);
35 /* Proxy EAP packet from RADIUS server to PAE */
36 void
37 radius_dispatch(nas_t *nas, radius_header_t *response)
39 nas_sta_t *sta = &nas->sta[response->id % MAX_SUPPLICANTS];
40 radius_header_t *request;
42 int left, type, length = 0, index, authenticated = 0;
43 unsigned char buf[16], *cur;
44 eapol_header_t eapol;
45 eap_header_t *eap = NULL;
46 unsigned int vendor, ssnto;
47 unsigned char *mppe_send = NULL, *mppe_recv = NULL, *mppe_key;
48 struct iovec frags[RADIUS_MAX_ATTRIBUTES];
49 int nfrags = 0;
50 #ifdef BCMDBG
51 char eabuf[ETHER_ADDR_STR_LEN];
52 #endif
54 /* The STA could have been toss during the wait. */
55 if (!sta->used)
56 return;
58 request = sta->pae.radius.request;
59 if (!request || request->id != response->id) {
60 dbg(nas, "bogus RADIUS packet response->id=%d request->id=%d", response->id,
61 request->id);
62 return;
65 /* Parse attributes */
66 left = ntohs(response->length) - RADIUS_HEADER_LEN;
67 cur = response->attributes;
68 while (left >= 2) {
69 int attribute_error = 0;
71 type = *cur++;
72 length = *cur++ - 2;
73 left -= 2;
75 /* Bad attribute length */
76 if (length > left) {
77 dbg(nas, "bad attribute length %d", length);
78 break;
81 switch (type) {
83 case RD_TP_MESSAGE_AUTHENTICATOR:
84 if (length < 16) {
85 dbg(nas, "bad signature length %d", length);
86 attribute_error = 1;
87 break;
90 /* Validate HMAC-MD5 checksum */
91 memcpy(buf, cur, 16);
92 memset(cur, 0, 16);
93 memcpy(response->vector, request->vector, 16);
95 /* Calculate HMAC-MD5 checksum with request vector and null signature */
96 hmac_md5((unsigned char *) response, ntohs(response->length),
97 nas->secret.data, nas->secret.length, cur);
98 if ((authenticated = !memcmp(buf, cur, 16)) == 0) {
99 dbg(nas, "Invalid signature");
100 attribute_error = 1;
102 break;
104 case RD_TP_STATE:
105 /* Preserve server state unmodified */
106 sta->pae.radius.state.length = length;
107 if (sta->pae.radius.state.data)
108 free(sta->pae.radius.state.data);
109 sta->pae.radius.state.length = length;
110 if (!(sta->pae.radius.state.data = malloc(sta->pae.radius.state.length))) {
111 perror("malloc");
112 attribute_error = 1;
113 } else
114 memcpy(sta->pae.radius.state.data, cur,
115 sta->pae.radius.state.length);
116 break;
118 case RD_TP_EAP_MESSAGE:
119 /* Initialize EAPOL header */
120 if (!nfrags) {
121 memcpy(&eapol.eth.ether_dhost, &sta->ea, ETHER_ADDR_LEN);
122 memcpy(&eapol.eth.ether_shost, &nas->ea, ETHER_ADDR_LEN);
123 #ifdef BCMWPA2
124 if (sta->flags & STA_FLAG_PRE_AUTH)
125 eapol.eth.ether_type = htons(ETHER_TYPE_802_1X_PREAUTH);
126 else
127 #endif
128 eapol.eth.ether_type = htons(ETHER_TYPE_802_1X);
129 eapol.version = sta->eapol_version;
130 eapol.type = EAP_PACKET;
131 eapol.length = htons(0);
132 eap = (eap_header_t *) cur;
133 frags[nfrags].iov_base = (caddr_t) &eapol;
134 frags[nfrags].iov_len = EAPOL_HEADER_LEN;
135 nfrags++;
136 /* Set up internal flags */
137 if (eap->code == EAP_SUCCESS)
138 sta->pae.flags |= PAE_FLAG_EAP_SUCCESS;
140 /* Gather fragmented EAP messages */
141 if (nfrags < ARRAYSIZE(frags)) {
142 eapol.length = htons(ntohs(eapol.length) + length);
143 frags[nfrags].iov_base = (caddr_t) cur;
144 frags[nfrags].iov_len = length;
145 nfrags++;
147 break;
149 case RD_TP_VENDOR_SPECIFIC:
150 if (length < 6) {
151 dbg(nas, "bad vendor attribute length %d", length);
152 attribute_error = 1;
153 break;
155 memcpy(&vendor, cur, 4);
156 vendor = ntohl(vendor);
157 cur += 4;
158 type = *cur++;
159 length = *cur++ - 2;
160 left -= 6;
162 /* Bad attribute length */
163 if (length > left) {
164 dbg(nas, "bad vendor attribute length %d", length);
165 attribute_error = 1;
166 break;
169 /* Parse vendor-specific attributes */
170 switch (vendor) {
172 case RD_VENDOR_MICROSOFT:
173 switch (type) {
175 case RD_MS_MPPE_SEND:
176 case RD_MS_MPPE_RECV:
177 if (response->code != RADIUS_ACCESS_ACCEPT) {
178 dbg(nas, "ignore MS-MPPE-Key in non"
179 " RADIUS_ACCESS_ACCEPT packet");
180 break;
183 /* Key length (minus salt) must be a multiple of 16 and
184 * greater than 32
186 if ((length - 2) % 16 || (length - 2) <= 32) {
187 dbg(nas, "bad MS-MPPE-Key length %d", length);
188 attribute_error = 1;
189 break;
191 /* Allocate key */
192 if (!(mppe_key = malloc(length - 2))) {
193 perror("malloc");
194 attribute_error = 1;
195 break;
197 /* Decrypt key */
198 memcpy(mppe_key, &cur[2], length - 2);
199 mppe_crypt(cur, mppe_key, length - 2,
200 nas->secret.data, nas->secret.length,
201 request->vector, 0);
202 /* Set key pointers */
203 if (type == RD_MS_MPPE_SEND)
204 mppe_send = mppe_key;
205 else
206 mppe_recv = mppe_key;
207 break;
209 break;
212 default:
213 dbg(nas, "unknown vendor attribute = %d", vendor);
214 dbg(nas, " vendor type = %d", type);
215 dbg(nas, " attribute string = %s", cur);
216 break;
218 break;
220 case RD_TP_SESSION_TIMEOUT:
221 if (response->code != RADIUS_ACCESS_ACCEPT)
222 break;
223 if (length < 4) {
224 dbg(nas, "bad session timeout attribute length %d", length);
225 attribute_error = 1;
226 break;
228 memcpy(&ssnto, cur, 4);
229 sta->pae.ssnto = ntohl(ssnto);
230 dbg(nas, "session timeout in %d seconds", sta->pae.ssnto);
231 break;
233 default:
234 /* Ignore all other attributes */
235 break;
237 /* Don't go on looking if something already went wrong. */
238 if (attribute_error)
239 goto done;
241 left -= length;
242 cur += length;
245 if (!authenticated && response->code != RADIUS_ACCESS_REJECT) {
246 dbg(nas, "missing signature");
247 goto done;
250 if (eap)
251 sta->pae.id = eap->id;
253 if (eap &&
254 (eap->code != EAP_SUCCESS || response->code != RADIUS_ACCESS_ACCEPT) &&
255 (eap->code != EAP_FAILURE || response->code != RADIUS_ACCESS_REJECT) &&
256 nfrags) {
257 #ifdef BCMWPA2
258 if (sta->flags & STA_FLAG_PRE_AUTH)
259 nas_preauth_send_packet(nas, frags, nfrags);
260 else
261 #endif
262 nas_eapol_send_packet(nas, frags, nfrags);
265 /* RADIUS event */
266 switch (response->code) {
268 case RADIUS_ACCESS_ACCEPT:
269 /* Check for EAP-Success before allowing complete access */
270 if (!(sta->pae.flags & PAE_FLAG_EAP_SUCCESS)) {
271 dbg(nas, "Radius success without EAP success?!");
272 pae_state(nas, sta, HELD);
273 dbg(nas, "deauthenticating %s", ether_etoa((uint8 *)&sta->ea, eabuf));
274 nas_deauthorize(nas, &sta->ea);
275 goto done;
278 dbg(nas, "Access Accept");
279 pae_state(nas, sta, AUTHENTICATED);
281 /* overwrite session timeout with global setting */
282 if (!sta->pae.ssnto || sta->pae.ssnto > nas->ssn_to)
283 sta->pae.ssnto = nas->ssn_to;
285 /* WPA-mode needs to do the 4-way handshake here instead. */
286 if (CHECK_WPA(sta->mode) && mppe_recv) {
287 fix_wpa(nas, sta, (char *)&mppe_recv[1], (int)mppe_recv[0]);
288 break;
291 /* Plump the keys to driver and send them to peer as well */
292 if (mppe_recv) {
293 /* Cobble a multicast key if there isn't one yet. */
294 if (!(nas->flags & NAS_FLAG_GTK_PLUMBED)) {
295 nas->wpa->gtk_index = GTK_INDEX_1;
296 if (nas->wpa->gtk_len == 0)
297 nas->wpa->gtk_len = WEP128_KEY_SIZE;
298 nas_rand128(nas->wpa->gtk);
299 if (nas_set_key(nas, NULL, nas->wpa->gtk,
300 nas->wpa->gtk_len, nas->wpa->gtk_index,
301 1, 0, 0) < 0) {
302 err(nas, "invalid multicast key");
303 nas_handle_error(nas, 1);
305 nas->flags |= NAS_FLAG_GTK_PLUMBED;
307 sta->rc4keysec = -1;
308 sta->rc4keyusec = -1;
309 /* Send multicast key */
310 index = nas->wpa->gtk_index;
311 length = nas->wpa->gtk_len;
312 if (mppe_send)
313 eapol_key(nas, sta, &mppe_send[1], mppe_send[0],
314 &mppe_recv[1], mppe_recv[0],
315 nas->wpa->gtk, length, index, 0);
316 else
317 eapol_key(nas, sta, NULL, 0,
318 &mppe_recv[1], mppe_recv[0],
319 nas->wpa->gtk, length, index, 0);
321 /* MS-MPPE-Recv-Key is MS-MPPE-Send-Key on the Suppl */
322 index = DOT11_MAX_DEFAULT_KEYS - 1;
323 length = WEP128_KEY_SIZE;
324 if (nas_set_key(nas, &sta->ea, &mppe_recv[1], length, index, 1, 0, 0) < 0) {
325 dbg(nas, "unicast key rejected by driver, assuming too many"
326 " associated STAs");
327 cleanup_sta(nas, sta, DOT11_RC_BUSY, 0);
329 /* Set unicast key index */
330 if (mppe_send)
331 eapol_key(nas, sta, &mppe_send[1], mppe_send[0],
332 NULL, 0,
333 NULL, length, index, 1);
334 else
335 eapol_key(nas, sta, NULL, 0,
336 NULL, 0,
337 NULL, length, index, 1);
338 dbg(nas, "authorize %s (802.1x)", ether_etoa((uint8 *)&sta->ea, eabuf));
339 nas_authorize(nas, &sta->ea);
341 break;
343 case RADIUS_ACCESS_REJECT:
344 dbg(nas, "Access Reject");
345 pae_state(nas, sta, HELD);
346 dbg(nas, "deauthenticating %s", ether_etoa((uint8 *)&sta->ea, eabuf));
347 nas_deauthorize(nas, &sta->ea);
348 sta->pae.ssnto = 0;
349 break;
351 case RADIUS_ACCESS_CHALLENGE:
352 dbg(nas, "Access Challenge");
353 break;
355 default:
356 dbg(nas, "unknown RADIUS code %d", response->code);
357 break;
360 done:
361 if (mppe_send)
362 free(mppe_send);
363 if (mppe_recv)
364 free(mppe_recv);
365 free(request);
366 sta->pae.radius.request = NULL;
369 /* Add an attribute value pair */
370 static void
371 radius_add(radius_header_t *radius, unsigned char type, unsigned char *buf, unsigned char length)
373 unsigned char *cur;
375 if ((ntohs(radius->length) + length) <= RADIUS_MAX_LEN) {
376 cur = (unsigned char *) radius + ntohs(radius->length);
377 *cur++ = type;
378 *cur++ = 2 + length;
379 memcpy(cur, buf, length);
380 radius->length = htons(ntohs(radius->length) + 2 + length);
384 /* Proxy EAP packet from PAE to RADIUS server */
385 void
386 radius_forward(nas_t *nas, nas_sta_t *sta, eap_header_t *eap)
388 radius_header_t *request;
390 unsigned char buf[16], *ptr;
391 long val;
392 int left;
394 /* Allocate packet */
395 if (!(request = malloc(RADIUS_MAX_LEN))) {
396 perror("malloc");
397 return;
400 /* Fill header */
401 request->code = RADIUS_ACCESS_REQUEST;
402 request->id = sta - nas->sta;
403 request->length = htons(RADIUS_HEADER_LEN);
405 /* Fill Request Authenticator */
406 nas_rand128(request->vector);
408 /* Fill attributes */
410 /* User Name */
411 if (sta->pae.radius.username.data && sta->pae.radius.username.length)
412 radius_add(request, RD_TP_USER_NAME, sta->pae.radius.username.data,
413 sta->pae.radius.username.length);
414 /* NAS IP Address */
415 radius_add(request, RD_TP_NAS_IP_ADDRESS, (unsigned char *) &nas->client.sin_addr,
416 sizeof(nas->client.sin_addr));
417 /* Called Station Id */
419 snprintf((char *)buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x",
420 ((unsigned char *) &nas->ea)[0], ((unsigned char *) &nas->ea)[1],
421 ((unsigned char *) &nas->ea)[2],
422 ((unsigned char *) &nas->ea)[3], ((unsigned char *) &nas->ea)[4],
423 ((unsigned char *) &nas->ea)[5]);
425 radius_add(request, RD_TP_CALLED_STATION_ID, buf, strlen((char *)buf));
426 /* Calling Station Id */
427 snprintf((char *)buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x",
428 ((unsigned char *) &sta->ea)[0], ((unsigned char *) &sta->ea)[1],
429 ((unsigned char *) &sta->ea)[2],
430 ((unsigned char *) &sta->ea)[3], ((unsigned char *) &sta->ea)[4],
431 ((unsigned char *) &sta->ea)[5]);
432 radius_add(request, RD_TP_CALLING_STATION_ID, buf, strlen((char *)buf));
433 /* NAS identifier */
434 if (strlen(nas->nas_id))
435 radius_add(request, RD_TP_NAS_IDENTIFIER, (unsigned char *)nas->nas_id,
436 strlen(nas->nas_id));
437 else {
438 snprintf((char *)buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x",
439 ((unsigned char *) &nas->ea)[0], ((unsigned char *) &nas->ea)[1],
440 ((unsigned char *) &nas->ea)[2],
441 ((unsigned char *) &nas->ea)[3], ((unsigned char *) &nas->ea)[4],
442 ((unsigned char *) &nas->ea)[5]);
443 radius_add(request, RD_TP_NAS_IDENTIFIER, buf, strlen((char *)buf));
445 /* NAS Port */
446 val = htonl((long) pae_hash(&sta->ea));
447 radius_add(request, RD_TP_NAS_PORT, (unsigned char *) &val, sizeof(val));
448 val = htonl(1400);
449 radius_add(request, RD_TP_FRAMED_MTU, (unsigned char *) &val,
450 sizeof(val));
451 /* State */
452 if (sta->pae.radius.state.data && sta->pae.radius.state.length) {
453 radius_add(request, RD_TP_STATE, sta->pae.radius.state.data,
454 sta->pae.radius.state.length);
455 free(sta->pae.radius.state.data);
456 sta->pae.radius.state.data = NULL;
457 sta->pae.radius.state.length = 0;
459 /* NAS Port Type */
460 val = htonl((long) nas->type);
461 radius_add(request, RD_TP_NAS_PORT_TYPE, (unsigned char *) &val, 4);
462 /* EAP Message(s) */
463 if (eap) {
464 for (left = ntohs(eap->length); left > 0; left -= 253) {
465 radius_add(request, RD_TP_EAP_MESSAGE,
466 (unsigned char *) eap + ntohs(eap->length) - left,
467 left <= 253 ? left : 253);
470 /* Message Authenticator */
471 memset(buf, 0, 16);
472 radius_add(request, RD_TP_MESSAGE_AUTHENTICATOR, buf, 16);
473 ptr = (unsigned char *) request + ntohs(request->length) - 16;
474 /* Calculate HMAC-MD5 checksum with null signature */
475 hmac_md5((unsigned char *) request, ntohs(request->length),
476 nas->secret.data, nas->secret.length, ptr);
478 /* Send packet */
479 if (NAS_RADIUS_SEND_PACKET(nas, request, ntohs(request->length)) < 0) {
480 perror(inet_ntoa(nas->server.sin_addr));
481 free(request);
482 request = NULL;
485 /* Save original request packet */
486 if (sta->pae.radius.request)
487 free(sta->pae.radius.request);
488 sta->pae.radius.request = request;