1beeb8d1e8fcb8251ed0aa35804e4654f11621bd
[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 #include "tnc_pdp_connections.h"
18
19 #include <errno.h>
20 #include <unistd.h>
21
22 #include <radius_message.h>
23
24 #include <daemon.h>
25 #include <debug.h>
26 #include <threading/thread.h>
27 #include <processing/jobs/callback_job.h>
28 #include <sa/authenticators/eap/eap_method.h>
29
30 typedef struct private_tnc_pdp_t private_tnc_pdp_t;
31
32 /**
33 * Maximum size of a RADIUS IP packet
34 */
35 #define MAX_PACKET 4096
36
37 /**
38 * private data of tnc_pdp_t
39 */
40 struct private_tnc_pdp_t {
41
42 /**
43 * implements tnc_pdp_t interface
44 */
45 tnc_pdp_t public;
46
47 /**
48 * ID of the server
49 */
50 identification_t *server;
51
52 /**
53 * EAP method type to be used
54 */
55 eap_type_t type;
56
57 /**
58 * IPv4 RADIUS socket
59 */
60 int ipv4;
61
62 /**
63 * IPv6 RADIUS socket
64 */
65 int ipv6;
66
67 /**
68 * Callback job dispatching commands
69 */
70 callback_job_t *job;
71
72 /**
73 * RADIUS shared secret
74 */
75 chunk_t secret;
76
77 /**
78 * MD5 hasher
79 */
80 hasher_t *hasher;
81
82 /**
83 * HMAC MD5 signer, with secret set
84 */
85 signer_t *signer;
86
87 /**
88 * List of registered TNC-PDP connections
89 */
90 tnc_pdp_connections_t *connections;
91 };
92
93
94 /**
95 * Open IPv4 or IPv6 UDP RADIUS socket
96 */
97 static int open_socket(private_tnc_pdp_t *this, int family, u_int16_t port)
98 {
99 int on = TRUE;
100 struct sockaddr_storage addr;
101 socklen_t addrlen;
102 int skt;
103
104 memset(&addr, 0, sizeof(addr));
105 addr.ss_family = family;
106
107 /* precalculate constants depending on address family */
108 switch (family)
109 {
110 case AF_INET:
111 {
112 struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
113
114 htoun32(&sin->sin_addr.s_addr, INADDR_ANY);
115 htoun16(&sin->sin_port, port);
116 addrlen = sizeof(struct sockaddr_in);
117 break;
118 }
119 case AF_INET6:
120 {
121 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
122
123 memcpy(&sin6->sin6_addr, &in6addr_any, sizeof(in6addr_any));
124 htoun16(&sin6->sin6_port, port);
125 addrlen = sizeof(struct sockaddr_in6);
126 break;
127 }
128 default:
129 return 0;
130 }
131
132 /* open the socket */
133 skt = socket(family, SOCK_DGRAM, IPPROTO_UDP);
134 if (skt < 0)
135 {
136 DBG1(DBG_CFG, "opening RADIUS socket failed: %s", strerror(errno));
137 return 0;
138 }
139 if (setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0)
140 {
141 DBG1(DBG_CFG, "unable to set SO_REUSEADDR on socket: %s", strerror(errno));
142 close(skt);
143 return 0;
144 }
145
146 /* bind the socket */
147 if (bind(skt, (struct sockaddr *)&addr, addrlen) < 0)
148 {
149 DBG1(DBG_CFG, "unable to bind RADIUS socket: %s", strerror(errno));
150 close(skt);
151 return 0;
152 }
153
154 return skt;
155 }
156
157 /**
158 * Send a RADIUS message to client
159 */
160 static void send_message(private_tnc_pdp_t *this, radius_message_t *message,
161 host_t *client)
162 {
163 int fd;
164 chunk_t data;
165
166 fd = (client->get_family(client) == AF_INET) ? this->ipv4 : this->ipv6;
167 data = message->get_encoding(message);
168
169 DBG2(DBG_CFG, "sending RADIUS packet to %#H", client);
170 DBG3(DBG_CFG, "%B", &data);
171
172 if (sendto(fd, data.ptr, data.len, 0, client->get_sockaddr(client),
173 *client->get_sockaddr_len(client)) != data.len)
174 {
175 DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno));
176 }
177 }
178
179 /**
180 * Send a RADIUS response for a request
181 */
182 static void send_response(private_tnc_pdp_t *this,
183 radius_message_t *request, radius_message_code_t code,
184 eap_payload_t *eap, identification_t *group,
185 host_t *client)
186 {
187 radius_message_t *response;
188 chunk_t data;
189 u_int32_t tunnel_type;
190
191 response = radius_message_create(code);
192 if (eap)
193 {
194 data = eap->get_data(eap);
195 DBG3(DBG_CFG, "%N payload %B", eap_type_names, this->type, &data);
196
197 /* fragment data suitable for RADIUS */
198 while (data.len > MAX_RADIUS_ATTRIBUTE_SIZE)
199 {
200 response->add(response, RAT_EAP_MESSAGE,
201 chunk_create(data.ptr, MAX_RADIUS_ATTRIBUTE_SIZE));
202 data = chunk_skip(data, MAX_RADIUS_ATTRIBUTE_SIZE);
203 }
204 response->add(response, RAT_EAP_MESSAGE, data);
205 }
206 if (group)
207 {
208 tunnel_type = RADIUS_TUNNEL_TYPE_ESP;
209 htoun32(data.ptr, tunnel_type);
210 data.len = sizeof(tunnel_type);
211 response->add(response, RAT_TUNNEL_TYPE, data);
212 response->add(response, RAT_FILTER_ID, group->get_encoding(group));
213 }
214 response->set_identifier(response, request->get_identifier(request));
215 response->sign(response, request->get_authenticator(request),
216 this->secret, this->hasher, this->signer, NULL, TRUE);
217
218 DBG1(DBG_CFG, "sending RADIUS %N to client '%H'", radius_message_code_names,
219 code, client);
220 send_message(this, response, client);
221 response->destroy(response);
222 }
223
224 /**
225 * Process EAP message
226 */
227 static void process_eap(private_tnc_pdp_t *this, radius_message_t *request,
228 host_t *source)
229 {
230 enumerator_t *enumerator;
231 eap_payload_t *in, *out = NULL;
232 eap_method_t *method;
233 eap_type_t eap_type;
234 u_int32_t eap_vendor;
235 chunk_t data, message = chunk_empty;
236 chunk_t user_name = chunk_empty, nas_id = chunk_empty;
237 identification_t *group = NULL;
238 radius_message_code_t code = RMC_ACCESS_CHALLENGE;
239 int type;
240
241 enumerator = request->create_enumerator(request);
242 while (enumerator->enumerate(enumerator, &type, &data))
243 {
244 switch (type)
245 {
246 case RAT_USER_NAME:
247 user_name = data;
248 break;
249 case RAT_NAS_IDENTIFIER:
250 nas_id = data;
251 break;
252 case RAT_EAP_MESSAGE:
253 if (data.len)
254 {
255 message = chunk_cat("mc", message, data);
256 }
257 break;
258 default:
259 break;
260 }
261 }
262 enumerator->destroy(enumerator);
263
264 if (message.len)
265 {
266 in = eap_payload_create_data(message);
267
268 /* apply EAP method selected by RADIUS server */
269 eap_type = in->get_type(in, &eap_vendor);
270
271 DBG3(DBG_CFG, "%N payload %B", eap_type_names, eap_type, &message);
272
273 if (eap_type == EAP_IDENTITY)
274 {
275 identification_t *peer;
276 chunk_t eap_identity;
277
278 if (message.len < 5)
279 {
280 goto end;
281 }
282 eap_identity = chunk_create(message.ptr + 5, message.len - 5);
283 peer = identification_create_from_data(eap_identity);
284 method = charon->eap->create_instance(charon->eap, this->type,
285 0, EAP_SERVER, this->server, peer);
286 if (!method)
287 {
288 peer->destroy(peer);
289 goto end;
290 }
291 this->connections->add(this->connections, nas_id, user_name, peer,
292 method);
293 method->initiate(method, &out);
294 }
295 else
296 {
297 ike_sa_t *ike_sa;
298 auth_cfg_t *auth;
299 auth_rule_t type;
300 identification_t *data;
301 enumerator_t *e;
302
303 method = this->connections->get_state(this->connections, nas_id,
304 user_name, &ike_sa);
305 if (!method)
306 {
307 goto end;
308 }
309 charon->bus->set_sa(charon->bus, ike_sa);
310
311 switch (method->process(method, in, &out))
312 {
313 case NEED_MORE:
314 code = RMC_ACCESS_CHALLENGE;
315 break;
316 case SUCCESS:
317 code = RMC_ACCESS_ACCEPT;
318
319 auth = ike_sa->get_auth_cfg(ike_sa, FALSE);
320 e = auth->create_enumerator(auth);
321 while (e->enumerate(e, &type, &data))
322 {
323 /* look for group memberships */
324 if (type == AUTH_RULE_GROUP)
325 {
326 group = data;
327 }
328 }
329 e->destroy(e);
330
331 DESTROY_IF(out);
332 out = eap_payload_create_code(EAP_SUCCESS,
333 in->get_identifier(in));
334 break;
335 case FAILED:
336 default:
337 code = RMC_ACCESS_REJECT;
338 DESTROY_IF(out);
339 out = eap_payload_create_code(EAP_FAILURE,
340 in->get_identifier(in));
341 }
342 charon->bus->set_sa(charon->bus, NULL);
343 }
344
345 send_response(this, request, code, out, group, source);
346 out->destroy(out);
347
348 if (code == RMC_ACCESS_ACCEPT || code == RMC_ACCESS_REJECT)
349 {
350 this->connections->remove(this->connections, nas_id, user_name);
351 }
352
353 end:
354 free(message.ptr);
355 in->destroy(in);
356 }
357 }
358
359 /**
360 * Process packets received on the RADIUS socket
361 */
362 static job_requeue_t receive(private_tnc_pdp_t *this)
363 {
364 while (TRUE)
365 {
366 radius_message_t *request;
367 char buffer[MAX_PACKET];
368 int max_fd = 0, selected = 0, bytes_read = 0;
369 fd_set rfds;
370 bool oldstate;
371 host_t *source;
372 struct msghdr msg;
373 struct iovec iov;
374 union {
375 struct sockaddr_in in4;
376 struct sockaddr_in6 in6;
377 } src;
378
379 FD_ZERO(&rfds);
380
381 if (this->ipv4)
382 {
383 FD_SET(this->ipv4, &rfds);
384 }
385 if (this->ipv6)
386 {
387 FD_SET(this->ipv6, &rfds);
388 }
389 max_fd = max(this->ipv4, this->ipv6);
390
391 DBG2(DBG_CFG, "waiting for data on RADIUS sockets");
392 oldstate = thread_cancelability(TRUE);
393 if (select(max_fd + 1, &rfds, NULL, NULL, NULL) <= 0)
394 {
395 thread_cancelability(oldstate);
396 continue;
397 }
398 thread_cancelability(oldstate);
399
400 if (FD_ISSET(this->ipv4, &rfds))
401 {
402 selected = this->ipv4;
403 }
404 else if (FD_ISSET(this->ipv6, &rfds))
405 {
406 selected = this->ipv6;
407 }
408 else
409 {
410 /* oops, shouldn't happen */
411 continue;
412 }
413
414 /* read received packet */
415 msg.msg_name = &src;
416 msg.msg_namelen = sizeof(src);
417 iov.iov_base = buffer;
418 iov.iov_len = MAX_PACKET;
419 msg.msg_iov = &iov;
420 msg.msg_iovlen = 1;
421 msg.msg_flags = 0;
422
423 bytes_read = recvmsg(selected, &msg, 0);
424 if (bytes_read < 0)
425 {
426 DBG1(DBG_CFG, "error reading RADIUS socket: %s", strerror(errno));
427 continue;
428 }
429 if (msg.msg_flags & MSG_TRUNC)
430 {
431 DBG1(DBG_CFG, "receive buffer too small, RADIUS packet discarded");
432 continue;
433 }
434 source = host_create_from_sockaddr((sockaddr_t*)&src);
435 DBG2(DBG_CFG, "received RADIUS packet from %#H", source);
436 DBG3(DBG_CFG, "%b", buffer, bytes_read);
437 request = radius_message_parse(chunk_create(buffer, bytes_read));
438 if (request)
439 {
440 DBG1(DBG_CFG, "received RADIUS %N from client '%H'",
441 radius_message_code_names, request->get_code(request), source);
442
443 if (request->verify(request, NULL, this->secret, this->hasher,
444 this->signer))
445 {
446 process_eap(this, request, source);
447 }
448 request->destroy(request);
449
450 }
451 else
452 {
453 DBG1(DBG_CFG, "received invalid RADIUS message, ignored");
454 }
455 source->destroy(source);
456 }
457 return JOB_REQUEUE_FAIR;
458 }
459
460 METHOD(tnc_pdp_t, destroy, void,
461 private_tnc_pdp_t *this)
462 {
463 if (this->job)
464 {
465 this->job->cancel(this->job);
466 }
467 if (this->ipv4)
468 {
469 close(this->ipv4);
470 }
471 if (this->ipv6)
472 {
473 close(this->ipv6);
474 }
475 DESTROY_IF(this->server);
476 DESTROY_IF(this->signer);
477 DESTROY_IF(this->hasher);
478 DESTROY_IF(this->connections);
479 free(this);
480 }
481
482 /*
483 * see header file
484 */
485 tnc_pdp_t *tnc_pdp_create(u_int16_t port)
486 {
487 private_tnc_pdp_t *this;
488 char *secret, *server, *eap_type_str;
489
490 INIT(this,
491 .public = {
492 .destroy = _destroy,
493 },
494 .ipv4 = open_socket(this, AF_INET, port),
495 .ipv6 = open_socket(this, AF_INET6, port),
496 .hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5),
497 .signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128),
498 .connections = tnc_pdp_connections_create(),
499 );
500
501 if (!this->ipv4 && !this->ipv6)
502 {
503 DBG1(DBG_NET, "couldd not create any RADIUS sockets");
504 destroy(this);
505 return NULL;
506 }
507 if (!this->ipv4)
508 {
509 DBG1(DBG_NET, "could not open IPv4 RADIUS socket, IPv4 disabled");
510 }
511 if (!this->ipv6)
512 {
513 DBG1(DBG_NET, "could not open IPv6 RADIUS socket, IPv6 disabled");
514 }
515 if (!this->hasher || !this->signer)
516 {
517 destroy(this);
518 return NULL;
519 }
520
521 server = lib->settings->get_str(lib->settings,
522 "charon.plugins.tnc-pdp.server", NULL);
523 if (!server)
524 {
525 DBG1(DBG_CFG, "missing PDP server name, PDP disabled");
526 destroy(this);
527 return NULL;
528 }
529 this->server = identification_create_from_string(server);
530
531 secret = lib->settings->get_str(lib->settings,
532 "charon.plugins.tnc-pdp.secret", NULL);
533 if (!secret)
534 {
535 DBG1(DBG_CFG, "missing RADIUS secret, PDP disabled");
536 destroy(this);
537 return NULL;
538 }
539 this->secret = chunk_create(secret, strlen(secret));
540 this->signer->set_key(this->signer, this->secret);
541
542 eap_type_str = lib->settings->get_str(lib->settings,
543 "charon.plugins.tnc-pdp.method", "ttls");
544 this->type = eap_type_from_string(eap_type_str);
545 if (this->type == 0)
546 {
547 DBG1(DBG_CFG, "unrecognized eap method \"%s\"", eap_type_str);
548 destroy(this);
549 return NULL;
550 }
551 DBG1(DBG_IKE, "eap method %N selected", eap_type_names, this->type);
552
553 this->job = callback_job_create_with_prio((callback_job_cb_t)receive,
554 this, NULL, NULL, JOB_PRIO_CRITICAL);
555 lib->processor->queue_job(lib->processor, (job_t*)this->job);
556
557 return &this->public;
558 }
559