read PDP server name from strongswan.conf
[strongswan.git] / src / libcharon / plugins / tnc_pdp / tnc_pdp.c
1 /*
2 * Copyright (C) 2012 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "tnc_pdp.h"
17
18 #include <errno.h>
19 #include <unistd.h>
20
21 #include <radius_message.h>
22
23 #include <daemon.h>
24 #include <debug.h>
25 #include <threading/thread.h>
26 #include <processing/jobs/callback_job.h>
27 #include <sa/authenticators/eap/eap_method.h>
28
29 typedef struct private_tnc_pdp_t private_tnc_pdp_t;
30
31 /**
32 * Maximum size of a RADIUS IP packet
33 */
34 #define MAX_PACKET 4096
35
36 /**
37 * private data of tnc_pdp_t
38 */
39 struct private_tnc_pdp_t {
40
41 /**
42 * implements tnc_pdp_t interface
43 */
44 tnc_pdp_t public;
45
46 /**
47 * ID of the server
48 */
49 identification_t *server;
50
51 /**
52 * EAP method type to be used
53 */
54 eap_type_t type;
55
56 /**
57 * IPv4 RADIUS socket
58 */
59 int ipv4;
60
61 /**
62 * IPv6 RADIUS socket
63 */
64 int ipv6;
65
66 /**
67 * Callback job dispatching commands
68 */
69 callback_job_t *job;
70
71 /**
72 * RADIUS shared secret
73 */
74 chunk_t secret;
75
76 /**
77 * MD5 hasher
78 */
79 hasher_t *hasher;
80
81 /**
82 * HMAC MD5 signer, with secret set
83 */
84 signer_t *signer;
85
86 /**
87 * EAP method
88 */
89 eap_method_t *method;
90 };
91
92
93 /**
94 * Open IPv4 or IPv6 UDP RADIUS socket
95 */
96 static int open_socket(private_tnc_pdp_t *this, int family, u_int16_t port)
97 {
98 int on = TRUE;
99 struct sockaddr_storage addr;
100 socklen_t addrlen;
101 int skt;
102
103 memset(&addr, 0, sizeof(addr));
104 addr.ss_family = family;
105
106 /* precalculate constants depending on address family */
107 switch (family)
108 {
109 case AF_INET:
110 {
111 struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
112
113 htoun32(&sin->sin_addr.s_addr, INADDR_ANY);
114 htoun16(&sin->sin_port, port);
115 addrlen = sizeof(struct sockaddr_in);
116 break;
117 }
118 case AF_INET6:
119 {
120 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
121
122 memcpy(&sin6->sin6_addr, &in6addr_any, sizeof(in6addr_any));
123 htoun16(&sin6->sin6_port, port);
124 addrlen = sizeof(struct sockaddr_in6);
125 break;
126 }
127 default:
128 return 0;
129 }
130
131 /* open the socket */
132 skt = socket(family, SOCK_DGRAM, IPPROTO_UDP);
133 if (skt < 0)
134 {
135 DBG1(DBG_CFG, "opening RADIUS socket failed: %s", strerror(errno));
136 return 0;
137 }
138 if (setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0)
139 {
140 DBG1(DBG_CFG, "unable to set SO_REUSEADDR on socket: %s", strerror(errno));
141 close(skt);
142 return 0;
143 }
144
145 /* bind the socket */
146 if (bind(skt, (struct sockaddr *)&addr, addrlen) < 0)
147 {
148 DBG1(DBG_CFG, "unable to bind RADIUS socket: %s", strerror(errno));
149 close(skt);
150 return 0;
151 }
152
153 return skt;
154 }
155
156 /**
157 * Send a RADIUS message to client
158 */
159 static void send_message(private_tnc_pdp_t *this, radius_message_t *message,
160 host_t *client)
161 {
162 int fd;
163 chunk_t data;
164
165 fd = (client->get_family(client) == AF_INET) ? this->ipv4 : this->ipv6;
166 data = message->get_encoding(message);
167
168 DBG2(DBG_CFG, "sending RADIUS packet to %#H", client);
169 DBG3(DBG_CFG, "%B", &data);
170
171 if (sendto(fd, data.ptr, data.len, 0, client->get_sockaddr(client),
172 *client->get_sockaddr_len(client)) != data.len)
173 {
174 DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno));
175 }
176 }
177
178 /**
179 * Send a RADIUS response for a request
180 */
181 static void send_response(private_tnc_pdp_t *this,
182 radius_message_t *request, radius_message_code_t code,
183 eap_payload_t *eap, host_t *client)
184 {
185 radius_message_t *response;
186 chunk_t data;
187
188 response = radius_message_create(code);
189 if (eap)
190 {
191 data = eap->get_data(eap);
192 response->add(response, RAT_EAP_MESSAGE, data);
193 }
194 response->set_identifier(response, request->get_identifier(request));
195 response->sign(response, request->get_authenticator(request),
196 this->secret, this->hasher, this->signer, NULL, TRUE);
197
198 DBG1(DBG_CFG, "sending RADIUS %N to client '%H'", radius_message_code_names,
199 code, client);
200 send_message(this, response, client);
201 }
202
203 /**
204 * Process EAP message
205 */
206 static void process_eap(private_tnc_pdp_t *this, radius_message_t *request,
207 host_t *source)
208 {
209 enumerator_t *enumerator;
210 eap_payload_t *in, *out = NULL;
211 eap_type_t eap_type;
212 chunk_t data, message = chunk_empty;
213 radius_message_code_t code = RMC_ACCESS_CHALLENGE;
214 u_int32_t eap_vendor;
215 int type;
216
217 enumerator = request->create_enumerator(request);
218 while (enumerator->enumerate(enumerator, &type, &data))
219 {
220 if (type == RAT_EAP_MESSAGE && data.len)
221 {
222 message = chunk_cat("mc", message, data);
223 }
224 }
225 enumerator->destroy(enumerator);
226
227 if (message.len)
228 {
229 in = eap_payload_create_data(message);
230
231 /* apply EAP method selected by RADIUS server */
232 eap_type = in->get_type(in, &eap_vendor);
233
234 DBG3(DBG_CFG, "%N payload %B", eap_type_names, eap_type, &message);
235 free(message.ptr);
236
237 if (eap_type == EAP_IDENTITY)
238 {
239 identification_t *peer;
240 chunk_t eap_identity;
241
242 if (message.len < 5)
243 {
244 return;
245 }
246 eap_identity = chunk_create(message.ptr + 5, message.len - 5);
247 peer = identification_create_from_data(eap_identity);
248
249 this->method = charon->eap->create_instance(charon->eap, this->type,
250 0, EAP_SERVER, this->server, peer);
251 peer->destroy(peer);
252 if (!this->method)
253 {
254 in->destroy(in);
255 return;
256 }
257 this->method->initiate(this->method, &out);
258 }
259 else
260 {
261 switch (this->method->process(this->method, in, &out))
262 {
263 case NEED_MORE:
264 code = RMC_ACCESS_CHALLENGE;
265 break;
266 case SUCCESS:
267 code = RMC_ACCESS_ACCEPT;
268 DESTROY_IF(out);
269 out = eap_payload_create_code(EAP_SUCCESS,
270 in->get_identifier(in));
271 break;
272 case FAILED:
273 default:
274 code = RMC_ACCESS_REJECT;
275 DESTROY_IF(out);
276 out = eap_payload_create_code(EAP_FAILURE,
277 in->get_identifier(in));
278 }
279 }
280
281 send_response(this, request, code, out, source);
282 in->destroy(in);
283 out->destroy(out);
284 }
285 }
286
287 /**
288 * Process packets received on the RADIUS socket
289 */
290 static job_requeue_t receive(private_tnc_pdp_t *this)
291 {
292 while (TRUE)
293 {
294 radius_message_t *request;
295 char buffer[MAX_PACKET];
296 int max_fd = 0, selected = 0, bytes_read = 0;
297 fd_set rfds;
298 bool oldstate;
299 host_t *source;
300 struct msghdr msg;
301 struct iovec iov;
302 union {
303 struct sockaddr_in in4;
304 struct sockaddr_in6 in6;
305 } src;
306
307 FD_ZERO(&rfds);
308
309 if (this->ipv4)
310 {
311 FD_SET(this->ipv4, &rfds);
312 }
313 if (this->ipv6)
314 {
315 FD_SET(this->ipv6, &rfds);
316 }
317 max_fd = max(this->ipv4, this->ipv6);
318
319 DBG2(DBG_CFG, "waiting for data on RADIUS sockets");
320 oldstate = thread_cancelability(TRUE);
321 if (select(max_fd + 1, &rfds, NULL, NULL, NULL) <= 0)
322 {
323 thread_cancelability(oldstate);
324 continue;
325 }
326 thread_cancelability(oldstate);
327
328 if (FD_ISSET(this->ipv4, &rfds))
329 {
330 selected = this->ipv4;
331 }
332 else if (FD_ISSET(this->ipv6, &rfds))
333 {
334 selected = this->ipv6;
335 }
336 else
337 {
338 /* oops, shouldn't happen */
339 continue;
340 }
341
342 /* read received packet */
343 msg.msg_name = &src;
344 msg.msg_namelen = sizeof(src);
345 iov.iov_base = buffer;
346 iov.iov_len = MAX_PACKET;
347 msg.msg_iov = &iov;
348 msg.msg_iovlen = 1;
349 msg.msg_flags = 0;
350
351 bytes_read = recvmsg(selected, &msg, 0);
352 if (bytes_read < 0)
353 {
354 DBG1(DBG_CFG, "error reading RADIUS socket: %s", strerror(errno));
355 continue;
356 }
357 if (msg.msg_flags & MSG_TRUNC)
358 {
359 DBG1(DBG_CFG, "receive buffer too small, RADIUS packet discarded");
360 continue;
361 }
362 source = host_create_from_sockaddr((sockaddr_t*)&src);
363 DBG2(DBG_CFG, "received RADIUS packet from %#H", source);
364 DBG3(DBG_CFG, "%b", buffer, bytes_read);
365 request = radius_message_parse(chunk_create(buffer, bytes_read));
366 if (request)
367 {
368 DBG1(DBG_CFG, "received RADIUS %N from client '%H'",
369 radius_message_code_names, request->get_code(request), source);
370
371 if (request->verify(request, NULL, this->secret, this->hasher,
372 this->signer))
373 {
374 process_eap(this, request, source);
375 }
376 request->destroy(request);
377
378 }
379 else
380 {
381 DBG1(DBG_CFG, "received invalid RADIUS message, ignored");
382 }
383 source->destroy(source);
384 }
385 return JOB_REQUEUE_FAIR;
386 }
387
388 METHOD(tnc_pdp_t, destroy, void,
389 private_tnc_pdp_t *this)
390 {
391 if (this->job)
392 {
393 this->job->cancel(this->job);
394 }
395 if (this->ipv4)
396 {
397 close(this->ipv4);
398 }
399 if (this->ipv6)
400 {
401 close(this->ipv6);
402 }
403 DESTROY_IF(this->server);
404 DESTROY_IF(this->signer);
405 DESTROY_IF(this->hasher);
406 DESTROY_IF(this->method);
407 free(this);
408 }
409
410 /*
411 * see header file
412 */
413 tnc_pdp_t *tnc_pdp_create(u_int16_t port)
414 {
415 private_tnc_pdp_t *this;
416 char *secret, *server;
417
418 INIT(this,
419 .public = {
420 .destroy = _destroy,
421 },
422 .type = EAP_TTLS,
423 .ipv4 = open_socket(this, AF_INET, port),
424 .ipv6 = open_socket(this, AF_INET6, port),
425 .hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5),
426 .signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128),
427 );
428
429 if (!this->ipv4 && !this->ipv6)
430 {
431 DBG1(DBG_NET, "couldd not create any RADIUS sockets");
432 destroy(this);
433 return NULL;
434 }
435 if (!this->ipv4)
436 {
437 DBG1(DBG_NET, "could not open IPv4 RADIUS socket, IPv4 disabled");
438 }
439 if (!this->ipv6)
440 {
441 DBG1(DBG_NET, "could not open IPv6 RADIUS socket, IPv6 disabled");
442 }
443 if (!this->hasher || !this->signer)
444 {
445 destroy(this);
446 return NULL;
447 }
448
449 server = lib->settings->get_str(lib->settings,
450 "charon.plugins.tnc-pdp.server", NULL);
451 if (!server)
452 {
453 DBG1(DBG_CFG, "missing PDP server name, PDP disabled");
454 destroy(this);
455 return NULL;
456 }
457 this->server = identification_create_from_string(server);
458
459 secret = lib->settings->get_str(lib->settings,
460 "charon.plugins.tnc-pdp.secret", NULL);
461 if (!secret)
462 {
463 DBG1(DBG_CFG, "missing RADIUS secret, PDP disabled");
464 destroy(this);
465 return NULL;
466 }
467 this->secret = chunk_create(secret, strlen(secret));
468 this->signer->set_key(this->signer, this->secret);
469
470
471 this->job = callback_job_create_with_prio((callback_job_cb_t)receive,
472 this, NULL, NULL, JOB_PRIO_CRITICAL);
473 lib->processor->queue_job(lib->processor, (job_t*)this->job);
474
475 return &this->public;
476 }
477