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