Branch data Line data Source code
1 : : /*
2 : : * RADIUS Dynamic Authorization Server (DAS) (RFC 5176)
3 : : * Copyright (c) 2012-2013, 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 "includes.h"
10 : : #include <net/if.h>
11 : :
12 : : #include "utils/common.h"
13 : : #include "utils/eloop.h"
14 : : #include "utils/ip_addr.h"
15 : : #include "radius.h"
16 : : #include "radius_das.h"
17 : :
18 : :
19 : : struct radius_das_data {
20 : : int sock;
21 : : u8 *shared_secret;
22 : : size_t shared_secret_len;
23 : : struct hostapd_ip_addr client_addr;
24 : : unsigned int time_window;
25 : : int require_event_timestamp;
26 : : void *ctx;
27 : : enum radius_das_res (*disconnect)(void *ctx,
28 : : struct radius_das_attrs *attr);
29 : : };
30 : :
31 : :
32 : 11 : static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
33 : : struct radius_msg *msg,
34 : : const char *abuf,
35 : : int from_port)
36 : : {
37 : : struct radius_hdr *hdr;
38 : : struct radius_msg *reply;
39 : 11 : u8 allowed[] = {
40 : : RADIUS_ATTR_USER_NAME,
41 : : RADIUS_ATTR_NAS_IP_ADDRESS,
42 : : RADIUS_ATTR_CALLING_STATION_ID,
43 : : RADIUS_ATTR_NAS_IDENTIFIER,
44 : : RADIUS_ATTR_ACCT_SESSION_ID,
45 : : RADIUS_ATTR_EVENT_TIMESTAMP,
46 : : RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
47 : : RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
48 : : #ifdef CONFIG_IPV6
49 : : RADIUS_ATTR_NAS_IPV6_ADDRESS,
50 : : #endif /* CONFIG_IPV6 */
51 : : 0
52 : : };
53 : 11 : int error = 405;
54 : : u8 attr;
55 : : enum radius_das_res res;
56 : : struct radius_das_attrs attrs;
57 : : u8 *buf;
58 : : size_t len;
59 : : char tmp[100];
60 : : u8 sta_addr[ETH_ALEN];
61 : :
62 : 11 : hdr = radius_msg_get_hdr(msg);
63 : :
64 : 11 : attr = radius_msg_find_unlisted_attr(msg, allowed);
65 [ + + ]: 11 : if (attr) {
66 : 1 : wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in "
67 : : "Disconnect-Request from %s:%d", attr,
68 : : abuf, from_port);
69 : 1 : error = 401;
70 : 1 : goto fail;
71 : : }
72 : :
73 : 10 : os_memset(&attrs, 0, sizeof(attrs));
74 : :
75 [ + + ]: 10 : if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
76 : : &buf, &len, NULL) == 0) {
77 [ - + ]: 3 : if (len != 4) {
78 : 0 : wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d",
79 : : abuf, from_port);
80 : 0 : error = 407;
81 : 0 : goto fail;
82 : : }
83 : 3 : attrs.nas_ip_addr = buf;
84 : : }
85 : :
86 : : #ifdef CONFIG_IPV6
87 [ - + ]: 10 : if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
88 : : &buf, &len, NULL) == 0) {
89 [ # # ]: 0 : if (len != 16) {
90 : 0 : wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d",
91 : : abuf, from_port);
92 : 0 : error = 407;
93 : 0 : goto fail;
94 : : }
95 : 0 : attrs.nas_ipv6_addr = buf;
96 : : }
97 : : #endif /* CONFIG_IPV6 */
98 : :
99 [ + + ]: 10 : if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
100 : : &buf, &len, NULL) == 0) {
101 : 3 : attrs.nas_identifier = buf;
102 : 3 : attrs.nas_identifier_len = len;
103 : : }
104 : :
105 [ + + ]: 10 : if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
106 : : &buf, &len, NULL) == 0) {
107 [ - + ]: 4 : if (len >= sizeof(tmp))
108 : 0 : len = sizeof(tmp) - 1;
109 : 4 : os_memcpy(tmp, buf, len);
110 : 4 : tmp[len] = '\0';
111 [ + + ]: 4 : if (hwaddr_aton2(tmp, sta_addr) < 0) {
112 : 1 : wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id "
113 : : "'%s' from %s:%d", tmp, abuf, from_port);
114 : 1 : error = 407;
115 : 1 : goto fail;
116 : : }
117 : 3 : attrs.sta_addr = sta_addr;
118 : : }
119 : :
120 [ + + ]: 9 : if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
121 : : &buf, &len, NULL) == 0) {
122 : 2 : attrs.user_name = buf;
123 : 2 : attrs.user_name_len = len;
124 : : }
125 : :
126 [ + + ]: 9 : if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
127 : : &buf, &len, NULL) == 0) {
128 : 4 : attrs.acct_session_id = buf;
129 : 4 : attrs.acct_session_id_len = len;
130 : : }
131 : :
132 [ + + ]: 9 : if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
133 : : &buf, &len, NULL) == 0) {
134 : 1 : attrs.cui = buf;
135 : 1 : attrs.cui_len = len;
136 : : }
137 : :
138 : 9 : res = das->disconnect(das->ctx, &attrs);
139 [ + + + - ]: 9 : switch (res) {
140 : : case RADIUS_DAS_NAS_MISMATCH:
141 : 2 : wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d",
142 : : abuf, from_port);
143 : 2 : error = 403;
144 : 2 : break;
145 : : case RADIUS_DAS_SESSION_NOT_FOUND:
146 : 3 : wpa_printf(MSG_INFO, "DAS: Session not found for request from "
147 : : "%s:%d", abuf, from_port);
148 : 3 : error = 503;
149 : 3 : break;
150 : : case RADIUS_DAS_SUCCESS:
151 : 4 : error = 0;
152 : 4 : break;
153 : : }
154 : :
155 : : fail:
156 [ + + ]: 11 : reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK :
157 : 11 : RADIUS_CODE_DISCONNECT_ACK, hdr->identifier);
158 [ - + ]: 11 : if (reply == NULL)
159 : 0 : return NULL;
160 : :
161 [ + + ]: 11 : if (error) {
162 [ - + ]: 7 : if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
163 : : error)) {
164 : 0 : radius_msg_free(reply);
165 : 0 : return NULL;
166 : : }
167 : : }
168 : :
169 : 11 : return reply;
170 : : }
171 : :
172 : :
173 : 15 : static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
174 : : {
175 : 15 : struct radius_das_data *das = eloop_ctx;
176 : : u8 buf[1500];
177 : : union {
178 : : struct sockaddr_storage ss;
179 : : struct sockaddr_in sin;
180 : : #ifdef CONFIG_IPV6
181 : : struct sockaddr_in6 sin6;
182 : : #endif /* CONFIG_IPV6 */
183 : : } from;
184 : : char abuf[50];
185 : 15 : int from_port = 0;
186 : : socklen_t fromlen;
187 : : int len;
188 : 15 : struct radius_msg *msg, *reply = NULL;
189 : : struct radius_hdr *hdr;
190 : : struct wpabuf *rbuf;
191 : : u32 val;
192 : : int res;
193 : : struct os_time now;
194 : :
195 : 15 : fromlen = sizeof(from);
196 : 15 : len = recvfrom(sock, buf, sizeof(buf), 0,
197 : : (struct sockaddr *) &from.ss, &fromlen);
198 [ - + ]: 15 : if (len < 0) {
199 : 0 : wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
200 : 0 : return;
201 : : }
202 : :
203 : 15 : os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
204 : 15 : from_port = ntohs(from.sin.sin_port);
205 : :
206 : 15 : wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
207 : : len, abuf, from_port);
208 [ - + ]: 15 : if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
209 : 0 : wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
210 : 0 : return;
211 : : }
212 : :
213 : 15 : msg = radius_msg_parse(buf, len);
214 [ - + ]: 15 : if (msg == NULL) {
215 : 0 : wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
216 : : "from %s:%d failed", abuf, from_port);
217 : 0 : return;
218 : : }
219 : :
220 [ + - ]: 15 : if (wpa_debug_level <= MSG_MSGDUMP)
221 : 15 : radius_msg_dump(msg);
222 : :
223 [ + + ]: 15 : if (radius_msg_verify_das_req(msg, das->shared_secret,
224 : : das->shared_secret_len)) {
225 : 1 : wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet "
226 : : "from %s:%d - drop", abuf, from_port);
227 : 1 : goto fail;
228 : : }
229 : :
230 : 14 : os_get_time(&now);
231 : 14 : res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
232 : : (u8 *) &val, 4);
233 [ + + ]: 14 : if (res == 4) {
234 : 13 : u32 timestamp = ntohl(val);
235 [ + + ]: 13 : if ((unsigned int) abs(now.sec - timestamp) >
236 : 13 : das->time_window) {
237 : 1 : wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
238 : : "Event-Timestamp (%u; local time %u) in "
239 : : "packet from %s:%d - drop",
240 : 1 : timestamp, (unsigned int) now.sec,
241 : : abuf, from_port);
242 : 1 : goto fail;
243 : : }
244 [ + - ]: 1 : } else if (das->require_event_timestamp) {
245 : 1 : wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet "
246 : : "from %s:%d - drop", abuf, from_port);
247 : 1 : goto fail;
248 : : }
249 : :
250 : 12 : hdr = radius_msg_get_hdr(msg);
251 : :
252 [ + + - ]: 12 : switch (hdr->code) {
253 : : case RADIUS_CODE_DISCONNECT_REQUEST:
254 : 11 : reply = radius_das_disconnect(das, msg, abuf, from_port);
255 : 11 : break;
256 : : case RADIUS_CODE_COA_REQUEST:
257 : : /* TODO */
258 : 1 : reply = radius_msg_new(RADIUS_CODE_COA_NAK,
259 : 1 : hdr->identifier);
260 [ - + ]: 1 : if (reply == NULL)
261 : 0 : break;
262 : :
263 : : /* Unsupported Service */
264 [ - + ]: 1 : if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
265 : : 405)) {
266 : 0 : radius_msg_free(reply);
267 : 0 : reply = NULL;
268 : 0 : break;
269 : : }
270 : 1 : break;
271 : : default:
272 : 0 : wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
273 : : "packet from %s:%d",
274 : 0 : hdr->code, abuf, from_port);
275 : : }
276 : :
277 [ + - ]: 12 : if (reply) {
278 : 12 : wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port);
279 : :
280 [ - + ]: 12 : if (!radius_msg_add_attr_int32(reply,
281 : : RADIUS_ATTR_EVENT_TIMESTAMP,
282 : 12 : now.sec)) {
283 : 0 : wpa_printf(MSG_DEBUG, "DAS: Failed to add "
284 : : "Event-Timestamp attribute");
285 : : }
286 : :
287 [ - + ]: 12 : if (radius_msg_finish_das_resp(reply, das->shared_secret,
288 : : das->shared_secret_len, hdr) <
289 : : 0) {
290 : 0 : wpa_printf(MSG_DEBUG, "DAS: Failed to add "
291 : : "Message-Authenticator attribute");
292 : : }
293 : :
294 [ + - ]: 12 : if (wpa_debug_level <= MSG_MSGDUMP)
295 : 12 : radius_msg_dump(reply);
296 : :
297 : 12 : rbuf = radius_msg_get_buf(reply);
298 : 12 : res = sendto(das->sock, wpabuf_head(rbuf),
299 : : wpabuf_len(rbuf), 0,
300 : : (struct sockaddr *) &from.ss, fromlen);
301 [ - + ]: 12 : if (res < 0) {
302 : 0 : wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
303 : 0 : abuf, from_port, strerror(errno));
304 : : }
305 : : }
306 : :
307 : : fail:
308 : 15 : radius_msg_free(msg);
309 : 15 : radius_msg_free(reply);
310 : : }
311 : :
312 : :
313 : 2 : static int radius_das_open_socket(int port)
314 : : {
315 : : int s;
316 : : struct sockaddr_in addr;
317 : :
318 : 2 : s = socket(PF_INET, SOCK_DGRAM, 0);
319 [ - + ]: 2 : if (s < 0) {
320 : 0 : wpa_printf(MSG_INFO, "RADIUS DAS: socket: %s", strerror(errno));
321 : 0 : return -1;
322 : : }
323 : :
324 : 2 : os_memset(&addr, 0, sizeof(addr));
325 : 2 : addr.sin_family = AF_INET;
326 : 2 : addr.sin_port = htons(port);
327 [ - + ]: 2 : if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
328 : 0 : wpa_printf(MSG_INFO, "RADIUS DAS: bind: %s", strerror(errno));
329 : 0 : close(s);
330 : 0 : return -1;
331 : : }
332 : :
333 : 2 : return s;
334 : : }
335 : :
336 : :
337 : : struct radius_das_data *
338 : 2 : radius_das_init(struct radius_das_conf *conf)
339 : : {
340 : : struct radius_das_data *das;
341 : :
342 [ + - ][ + - ]: 2 : if (conf->port == 0 || conf->shared_secret == NULL ||
[ - + ]
343 : 2 : conf->client_addr == NULL)
344 : 0 : return NULL;
345 : :
346 : 2 : das = os_zalloc(sizeof(*das));
347 [ - + ]: 2 : if (das == NULL)
348 : 0 : return NULL;
349 : :
350 : 2 : das->time_window = conf->time_window;
351 : 2 : das->require_event_timestamp = conf->require_event_timestamp;
352 : 2 : das->ctx = conf->ctx;
353 : 2 : das->disconnect = conf->disconnect;
354 : :
355 : 2 : os_memcpy(&das->client_addr, conf->client_addr,
356 : : sizeof(das->client_addr));
357 : :
358 : 2 : das->shared_secret = os_malloc(conf->shared_secret_len);
359 [ - + ]: 2 : if (das->shared_secret == NULL) {
360 : 0 : radius_das_deinit(das);
361 : 0 : return NULL;
362 : : }
363 : 2 : os_memcpy(das->shared_secret, conf->shared_secret,
364 : : conf->shared_secret_len);
365 : 2 : das->shared_secret_len = conf->shared_secret_len;
366 : :
367 : 2 : das->sock = radius_das_open_socket(conf->port);
368 [ - + ]: 2 : if (das->sock < 0) {
369 : 0 : wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
370 : : "DAS");
371 : 0 : radius_das_deinit(das);
372 : 0 : return NULL;
373 : : }
374 : :
375 [ - + ]: 2 : if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
376 : : {
377 : 0 : radius_das_deinit(das);
378 : 0 : return NULL;
379 : : }
380 : :
381 : 2 : return das;
382 : : }
383 : :
384 : :
385 : 343 : void radius_das_deinit(struct radius_das_data *das)
386 : : {
387 [ + + ]: 343 : if (das == NULL)
388 : 343 : return;
389 : :
390 [ + - ]: 2 : if (das->sock >= 0) {
391 : 2 : eloop_unregister_read_sock(das->sock);
392 : 2 : close(das->sock);
393 : : }
394 : :
395 : 2 : os_free(das->shared_secret);
396 : 2 : os_free(das);
397 : : }
|