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