2 * Radius code which used to be in nas.c
3 * Radius support for Network Access Server
4 * Copyright (C) 2010, Broadcom Corporation
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 $
17 #include <proto/eap.h>
20 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
28 #include <nas_radius.h>
29 #include <nas_wksp_radius.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 */
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
;
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
];
51 char eabuf
[ETHER_ADDR_STR_LEN
];
54 /* The STA could have been toss during the wait. */
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
,
65 /* Parse attributes */
66 left
= ntohs(response
->length
) - RADIUS_HEADER_LEN
;
67 cur
= response
->attributes
;
69 int attribute_error
= 0;
75 /* Bad attribute length */
77 dbg(nas
, "bad attribute length %d", length
);
83 case RD_TP_MESSAGE_AUTHENTICATOR
:
85 dbg(nas
, "bad signature length %d", length
);
90 /* Validate HMAC-MD5 checksum */
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");
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
))) {
114 memcpy(sta
->pae
.radius
.state
.data
, cur
,
115 sta
->pae
.radius
.state
.length
);
118 case RD_TP_EAP_MESSAGE
:
119 /* Initialize EAPOL header */
121 memcpy(&eapol
.eth
.ether_dhost
, &sta
->ea
, ETHER_ADDR_LEN
);
122 memcpy(&eapol
.eth
.ether_shost
, &nas
->ea
, ETHER_ADDR_LEN
);
124 if (sta
->flags
& STA_FLAG_PRE_AUTH
)
125 eapol
.eth
.ether_type
= htons(ETHER_TYPE_802_1X_PREAUTH
);
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
;
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
;
149 case RD_TP_VENDOR_SPECIFIC
:
151 dbg(nas
, "bad vendor attribute length %d", length
);
155 memcpy(&vendor
, cur
, 4);
156 vendor
= ntohl(vendor
);
162 /* Bad attribute length */
164 dbg(nas
, "bad vendor attribute length %d", length
);
169 /* Parse vendor-specific attributes */
172 case RD_VENDOR_MICROSOFT
:
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");
183 /* Key length (minus salt) must be a multiple of 16 and
186 if ((length
- 2) % 16 || (length
- 2) <= 32) {
187 dbg(nas
, "bad MS-MPPE-Key length %d", length
);
192 if (!(mppe_key
= malloc(length
- 2))) {
198 memcpy(mppe_key
, &cur
[2], length
- 2);
199 mppe_crypt(cur
, mppe_key
, length
- 2,
200 nas
->secret
.data
, nas
->secret
.length
,
202 /* Set key pointers */
203 if (type
== RD_MS_MPPE_SEND
)
204 mppe_send
= mppe_key
;
206 mppe_recv
= mppe_key
;
213 dbg(nas
, "unknown vendor attribute = %d", vendor
);
214 dbg(nas
, " vendor type = %d", type
);
215 dbg(nas
, " attribute string = %s", cur
);
220 case RD_TP_SESSION_TIMEOUT
:
221 if (response
->code
!= RADIUS_ACCESS_ACCEPT
)
224 dbg(nas
, "bad session timeout attribute length %d", length
);
228 memcpy(&ssnto
, cur
, 4);
229 sta
->pae
.ssnto
= ntohl(ssnto
);
230 dbg(nas
, "session timeout in %d seconds", sta
->pae
.ssnto
);
234 /* Ignore all other attributes */
237 /* Don't go on looking if something already went wrong. */
245 if (!authenticated
&& response
->code
!= RADIUS_ACCESS_REJECT
) {
246 dbg(nas
, "missing signature");
251 sta
->pae
.id
= eap
->id
;
254 (eap
->code
!= EAP_SUCCESS
|| response
->code
!= RADIUS_ACCESS_ACCEPT
) &&
255 (eap
->code
!= EAP_FAILURE
|| response
->code
!= RADIUS_ACCESS_REJECT
) &&
258 if (sta
->flags
& STA_FLAG_PRE_AUTH
)
259 nas_preauth_send_packet(nas
, frags
, nfrags
);
262 nas_eapol_send_packet(nas
, frags
, nfrags
);
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
);
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]);
291 /* Plump the keys to driver and send them to peer as well */
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
,
302 err(nas
, "invalid multicast key");
303 nas_handle_error(nas
, 1);
305 nas
->flags
|= NAS_FLAG_GTK_PLUMBED
;
308 sta
->rc4keyusec
= -1;
309 /* Send multicast key */
310 index
= nas
->wpa
->gtk_index
;
311 length
= nas
->wpa
->gtk_len
;
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);
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"
327 cleanup_sta(nas
, sta
, DOT11_RC_BUSY
, 0);
329 /* Set unicast key index */
331 eapol_key(nas
, sta
, &mppe_send
[1], mppe_send
[0],
333 NULL
, length
, index
, 1);
335 eapol_key(nas
, sta
, 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
);
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
);
351 case RADIUS_ACCESS_CHALLENGE
:
352 dbg(nas
, "Access Challenge");
356 dbg(nas
, "unknown RADIUS code %d", response
->code
);
366 sta
->pae
.radius
.request
= NULL
;
369 /* Add an attribute value pair */
371 radius_add(radius_header_t
*radius
, unsigned char type
, unsigned char *buf
, unsigned char length
)
375 if ((ntohs(radius
->length
) + length
) <= RADIUS_MAX_LEN
) {
376 cur
= (unsigned char *) radius
+ ntohs(radius
->length
);
379 memcpy(cur
, buf
, length
);
380 radius
->length
= htons(ntohs(radius
->length
) + 2 + length
);
384 /* Proxy EAP packet from PAE to RADIUS server */
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
;
394 /* Allocate packet */
395 if (!(request
= malloc(RADIUS_MAX_LEN
))) {
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 */
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
);
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
));
434 if (strlen(nas
->nas_id
))
435 radius_add(request
, RD_TP_NAS_IDENTIFIER
, (unsigned char *)nas
->nas_id
,
436 strlen(nas
->nas_id
));
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
));
446 val
= htonl((long) pae_hash(&sta
->ea
));
447 radius_add(request
, RD_TP_NAS_PORT
, (unsigned char *) &val
, sizeof(val
));
449 radius_add(request
, RD_TP_FRAMED_MTU
, (unsigned char *) &val
,
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;
460 val
= htonl((long) nas
->type
);
461 radius_add(request
, RD_TP_NAS_PORT_TYPE
, (unsigned char *) &val
, 4);
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 */
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
);
479 if (NAS_RADIUS_SEND_PACKET(nas
, request
, ntohs(request
->length
)) < 0) {
480 perror(inet_ntoa(nas
->server
.sin_addr
));
485 /* Save original request packet */
486 if (sta
->pae
.radius
.request
)
487 free(sta
->pae
.radius
.request
);
488 sta
->pae
.radius
.request
= request
;