]> git.ipfire.org Git - thirdparty/hostap.git/blob - src/radius/radius_das.c
RADIUS DAS: Validate Event-Timestamp
[thirdparty/hostap.git] / 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 };
30
31
32 static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
33 {
34 struct radius_das_data *das = eloop_ctx;
35 u8 buf[1500];
36 union {
37 struct sockaddr_storage ss;
38 struct sockaddr_in sin;
39 #ifdef CONFIG_IPV6
40 struct sockaddr_in6 sin6;
41 #endif /* CONFIG_IPV6 */
42 } from;
43 char abuf[50];
44 int from_port = 0;
45 socklen_t fromlen;
46 int len;
47 struct radius_msg *msg, *reply = NULL;
48 struct radius_hdr *hdr;
49 struct wpabuf *rbuf;
50 u32 val;
51 int res;
52
53 fromlen = sizeof(from);
54 len = recvfrom(sock, buf, sizeof(buf), 0,
55 (struct sockaddr *) &from.ss, &fromlen);
56 if (len < 0) {
57 wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
58 return;
59 }
60
61 os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
62 from_port = ntohs(from.sin.sin_port);
63
64 wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
65 len, abuf, from_port);
66 if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
67 wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
68 return;
69 }
70
71 msg = radius_msg_parse(buf, len);
72 if (msg == NULL) {
73 wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
74 "from %s:%d failed", abuf, from_port);
75 return;
76 }
77
78 if (wpa_debug_level <= MSG_MSGDUMP)
79 radius_msg_dump(msg);
80
81 if (radius_msg_verify_das_req(msg, das->shared_secret,
82 das->shared_secret_len)) {
83 wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet "
84 "from %s:%d - drop", abuf, from_port);
85 goto fail;
86 }
87
88 res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
89 (u8 *) &val, 4);
90 if (res == 4) {
91 u32 timestamp = ntohl(val);
92 struct os_time now;
93
94 os_get_time(&now);
95 if (abs(now.sec - timestamp) > das->time_window) {
96 wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
97 "Event-Timestamp (%u; local time %u) in "
98 "packet from %s:%d - drop",
99 timestamp, (unsigned int) now.sec,
100 abuf, from_port);
101 goto fail;
102 }
103 } else if (das->require_event_timestamp) {
104 wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet "
105 "from %s:%d - drop", abuf, from_port);
106 goto fail;
107 }
108
109 hdr = radius_msg_get_hdr(msg);
110
111 switch (hdr->code) {
112 case RADIUS_CODE_DISCONNECT_REQUEST:
113 /* TODO */
114 reply = radius_msg_new(RADIUS_CODE_DISCONNECT_NAK,
115 hdr->identifier);
116 if (reply == NULL)
117 break;
118
119 radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, 405);
120 break;
121 case RADIUS_CODE_COA_REQUEST:
122 /* TODO */
123 reply = radius_msg_new(RADIUS_CODE_COA_NAK,
124 hdr->identifier);
125 if (reply == NULL)
126 break;
127
128 /* Unsupported Service */
129 radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, 405);
130 break;
131 default:
132 wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
133 "packet from %s:%d",
134 hdr->code, abuf, from_port);
135 }
136
137 if (reply) {
138 wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port);
139
140 if (radius_msg_finish_das_resp(reply, das->shared_secret,
141 das->shared_secret_len, hdr) <
142 0) {
143 wpa_printf(MSG_DEBUG, "DAS: Failed to add "
144 "Message-Authenticator attribute");
145 }
146
147 if (wpa_debug_level <= MSG_MSGDUMP)
148 radius_msg_dump(reply);
149
150 rbuf = radius_msg_get_buf(reply);
151 res = sendto(das->sock, wpabuf_head(rbuf),
152 wpabuf_len(rbuf), 0,
153 (struct sockaddr *) &from.ss, fromlen);
154 if (res < 0) {
155 wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
156 abuf, from_port, strerror(errno));
157 }
158 }
159
160 fail:
161 radius_msg_free(msg);
162 radius_msg_free(reply);
163 }
164
165
166 static int radius_das_open_socket(int port)
167 {
168 int s;
169 struct sockaddr_in addr;
170
171 s = socket(PF_INET, SOCK_DGRAM, 0);
172 if (s < 0) {
173 perror("socket");
174 return -1;
175 }
176
177 os_memset(&addr, 0, sizeof(addr));
178 addr.sin_family = AF_INET;
179 addr.sin_port = htons(port);
180 if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
181 perror("bind");
182 close(s);
183 return -1;
184 }
185
186 return s;
187 }
188
189
190 struct radius_das_data *
191 radius_das_init(struct radius_das_conf *conf)
192 {
193 struct radius_das_data *das;
194
195 if (conf->port == 0 || conf->shared_secret == NULL ||
196 conf->client_addr == NULL)
197 return NULL;
198
199 das = os_zalloc(sizeof(*das));
200 if (das == NULL)
201 return NULL;
202
203 das->time_window = conf->time_window;
204 das->require_event_timestamp = conf->require_event_timestamp;
205
206 os_memcpy(&das->client_addr, conf->client_addr,
207 sizeof(das->client_addr));
208
209 das->shared_secret = os_malloc(conf->shared_secret_len);
210 if (das->shared_secret == NULL) {
211 radius_das_deinit(das);
212 return NULL;
213 }
214 os_memcpy(das->shared_secret, conf->shared_secret,
215 conf->shared_secret_len);
216 das->shared_secret_len = conf->shared_secret_len;
217
218 das->sock = radius_das_open_socket(conf->port);
219 if (das->sock < 0) {
220 wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
221 "DAS");
222 radius_das_deinit(das);
223 return NULL;
224 }
225
226 if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
227 {
228 radius_das_deinit(das);
229 return NULL;
230 }
231
232 return das;
233 }
234
235
236 void radius_das_deinit(struct radius_das_data *das)
237 {
238 if (das == NULL)
239 return;
240
241 if (das->sock >= 0) {
242 eloop_unregister_read_sock(das->sock);
243 close(das->sock);
244 }
245
246 os_free(das->shared_secret);
247 os_free(das);
248 }