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