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