2 * Copyright (C) 2012 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
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>.
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
17 #include "tnc_pdp_connections.h"
22 #include <radius_message.h>
26 #include <threading/thread.h>
27 #include <processing/jobs/callback_job.h>
28 #include <sa/authenticators/eap/eap_method.h>
30 typedef struct private_tnc_pdp_t private_tnc_pdp_t
;
33 * Maximum size of a RADIUS IP packet
35 #define MAX_PACKET 4096
38 * private data of tnc_pdp_t
40 struct private_tnc_pdp_t
{
43 * implements tnc_pdp_t interface
50 identification_t
*server
;
53 * EAP method type to be used
68 * Callback job dispatching commands
73 * RADIUS shared secret
83 * HMAC MD5 signer, with secret set
88 * List of registered TNC-PDP connections
90 tnc_pdp_connections_t
*connections
;
95 * Open IPv4 or IPv6 UDP RADIUS socket
97 static int open_socket(private_tnc_pdp_t
*this, int family
, u_int16_t port
)
100 struct sockaddr_storage addr
;
104 memset(&addr
, 0, sizeof(addr
));
105 addr
.ss_family
= family
;
107 /* precalculate constants depending on address family */
112 struct sockaddr_in
*sin
= (struct sockaddr_in
*)&addr
;
114 htoun32(&sin
->sin_addr
.s_addr
, INADDR_ANY
);
115 htoun16(&sin
->sin_port
, port
);
116 addrlen
= sizeof(struct sockaddr_in
);
121 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&addr
;
123 memcpy(&sin6
->sin6_addr
, &in6addr_any
, sizeof(in6addr_any
));
124 htoun16(&sin6
->sin6_port
, port
);
125 addrlen
= sizeof(struct sockaddr_in6
);
132 /* open the socket */
133 skt
= socket(family
, SOCK_DGRAM
, IPPROTO_UDP
);
136 DBG1(DBG_CFG
, "opening RADIUS socket failed: %s", strerror(errno
));
139 if (setsockopt(skt
, SOL_SOCKET
, SO_REUSEADDR
, (void*)&on
, sizeof(on
)) < 0)
141 DBG1(DBG_CFG
, "unable to set SO_REUSEADDR on socket: %s", strerror(errno
));
146 /* bind the socket */
147 if (bind(skt
, (struct sockaddr
*)&addr
, addrlen
) < 0)
149 DBG1(DBG_CFG
, "unable to bind RADIUS socket: %s", strerror(errno
));
158 * Send a RADIUS message to client
160 static void send_message(private_tnc_pdp_t
*this, radius_message_t
*message
,
166 fd
= (client
->get_family(client
) == AF_INET
) ?
this->ipv4
: this->ipv6
;
167 data
= message
->get_encoding(message
);
169 DBG2(DBG_CFG
, "sending RADIUS packet to %#H", client
);
170 DBG3(DBG_CFG
, "%B", &data
);
172 if (sendto(fd
, data
.ptr
, data
.len
, 0, client
->get_sockaddr(client
),
173 *client
->get_sockaddr_len(client
)) != data
.len
)
175 DBG1(DBG_CFG
, "sending RADIUS message failed: %s", strerror(errno
));
180 * Send a RADIUS response for a request
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
, host_t
*client
)
186 radius_message_t
*response
;
189 response
= radius_message_create(code
);
192 data
= eap
->get_data(eap
);
193 DBG3(DBG_CFG
, "%N payload %B", eap_type_names
, this->type
, &data
);
195 /* fragment data suitable for RADIUS */
196 while (data
.len
> MAX_RADIUS_ATTRIBUTE_SIZE
)
198 response
->add(response
, RAT_EAP_MESSAGE
,
199 chunk_create(data
.ptr
, MAX_RADIUS_ATTRIBUTE_SIZE
));
200 data
= chunk_skip(data
, MAX_RADIUS_ATTRIBUTE_SIZE
);
202 response
->add(response
, RAT_EAP_MESSAGE
, data
);
204 response
->set_identifier(response
, request
->get_identifier(request
));
205 response
->sign(response
, request
->get_authenticator(request
),
206 this->secret
, this->hasher
, this->signer
, NULL
, TRUE
);
208 DBG1(DBG_CFG
, "sending RADIUS %N to client '%H'", radius_message_code_names
,
210 send_message(this, response
, client
);
211 response
->destroy(response
);
215 * Process EAP message
217 static void process_eap(private_tnc_pdp_t
*this, radius_message_t
*request
,
220 enumerator_t
*enumerator
;
221 eap_payload_t
*in
, *out
= NULL
;
222 eap_method_t
*method
;
224 chunk_t data
, message
= chunk_empty
;
225 chunk_t user_name
= chunk_empty
, nas_id
= chunk_empty
;
226 radius_message_code_t code
= RMC_ACCESS_CHALLENGE
;
227 u_int32_t eap_vendor
;
230 enumerator
= request
->create_enumerator(request
);
231 while (enumerator
->enumerate(enumerator
, &type
, &data
))
238 case RAT_NAS_IDENTIFIER
:
241 case RAT_EAP_MESSAGE
:
244 message
= chunk_cat("mc", message
, data
);
251 enumerator
->destroy(enumerator
);
255 in
= eap_payload_create_data(message
);
257 /* apply EAP method selected by RADIUS server */
258 eap_type
= in
->get_type(in
, &eap_vendor
);
260 DBG3(DBG_CFG
, "%N payload %B", eap_type_names
, eap_type
, &message
);
263 if (eap_type
== EAP_IDENTITY
)
265 identification_t
*peer
;
266 chunk_t eap_identity
;
272 eap_identity
= chunk_create(message
.ptr
+ 5, message
.len
- 5);
273 peer
= identification_create_from_data(eap_identity
);
275 method
= charon
->eap
->create_instance(charon
->eap
, this->type
,
276 0, EAP_SERVER
, this->server
, peer
);
283 this->connections
->add(this->connections
, nas_id
, user_name
, method
);
284 method
->initiate(method
, &out
);
288 method
= this->connections
->get_method(this->connections
, nas_id
,
295 switch (method
->process(method
, in
, &out
))
298 code
= RMC_ACCESS_CHALLENGE
;
301 code
= RMC_ACCESS_ACCEPT
;
303 out
= eap_payload_create_code(EAP_SUCCESS
,
304 in
->get_identifier(in
));
308 code
= RMC_ACCESS_REJECT
;
310 out
= eap_payload_create_code(EAP_FAILURE
,
311 in
->get_identifier(in
));
315 if (code
== RMC_ACCESS_ACCEPT
|| code
== RMC_ACCESS_REJECT
)
317 this->connections
->remove(this->connections
, nas_id
, user_name
);
320 send_response(this, request
, code
, out
, source
);
327 * Process packets received on the RADIUS socket
329 static job_requeue_t
receive(private_tnc_pdp_t
*this)
333 radius_message_t
*request
;
334 char buffer
[MAX_PACKET
];
335 int max_fd
= 0, selected
= 0, bytes_read
= 0;
342 struct sockaddr_in in4
;
343 struct sockaddr_in6 in6
;
350 FD_SET(this->ipv4
, &rfds
);
354 FD_SET(this->ipv6
, &rfds
);
356 max_fd
= max(this->ipv4
, this->ipv6
);
358 DBG2(DBG_CFG
, "waiting for data on RADIUS sockets");
359 oldstate
= thread_cancelability(TRUE
);
360 if (select(max_fd
+ 1, &rfds
, NULL
, NULL
, NULL
) <= 0)
362 thread_cancelability(oldstate
);
365 thread_cancelability(oldstate
);
367 if (FD_ISSET(this->ipv4
, &rfds
))
369 selected
= this->ipv4
;
371 else if (FD_ISSET(this->ipv6
, &rfds
))
373 selected
= this->ipv6
;
377 /* oops, shouldn't happen */
381 /* read received packet */
383 msg
.msg_namelen
= sizeof(src
);
384 iov
.iov_base
= buffer
;
385 iov
.iov_len
= MAX_PACKET
;
390 bytes_read
= recvmsg(selected
, &msg
, 0);
393 DBG1(DBG_CFG
, "error reading RADIUS socket: %s", strerror(errno
));
396 if (msg
.msg_flags
& MSG_TRUNC
)
398 DBG1(DBG_CFG
, "receive buffer too small, RADIUS packet discarded");
401 source
= host_create_from_sockaddr((sockaddr_t
*)&src
);
402 DBG2(DBG_CFG
, "received RADIUS packet from %#H", source
);
403 DBG3(DBG_CFG
, "%b", buffer
, bytes_read
);
404 request
= radius_message_parse(chunk_create(buffer
, bytes_read
));
407 DBG1(DBG_CFG
, "received RADIUS %N from client '%H'",
408 radius_message_code_names
, request
->get_code(request
), source
);
410 if (request
->verify(request
, NULL
, this->secret
, this->hasher
,
413 process_eap(this, request
, source
);
415 request
->destroy(request
);
420 DBG1(DBG_CFG
, "received invalid RADIUS message, ignored");
422 source
->destroy(source
);
424 return JOB_REQUEUE_FAIR
;
427 METHOD(tnc_pdp_t
, destroy
, void,
428 private_tnc_pdp_t
*this)
432 this->job
->cancel(this->job
);
442 DESTROY_IF(this->server
);
443 DESTROY_IF(this->signer
);
444 DESTROY_IF(this->hasher
);
445 DESTROY_IF(this->connections
);
452 tnc_pdp_t
*tnc_pdp_create(u_int16_t port
)
454 private_tnc_pdp_t
*this;
455 char *secret
, *server
;
462 .ipv4
= open_socket(this, AF_INET
, port
),
463 .ipv6
= open_socket(this, AF_INET6
, port
),
464 .hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_MD5
),
465 .signer
= lib
->crypto
->create_signer(lib
->crypto
, AUTH_HMAC_MD5_128
),
466 .connections
= tnc_pdp_connections_create(),
469 if (!this->ipv4
&& !this->ipv6
)
471 DBG1(DBG_NET
, "couldd not create any RADIUS sockets");
477 DBG1(DBG_NET
, "could not open IPv4 RADIUS socket, IPv4 disabled");
481 DBG1(DBG_NET
, "could not open IPv6 RADIUS socket, IPv6 disabled");
483 if (!this->hasher
|| !this->signer
)
489 server
= lib
->settings
->get_str(lib
->settings
,
490 "charon.plugins.tnc-pdp.server", NULL
);
493 DBG1(DBG_CFG
, "missing PDP server name, PDP disabled");
497 this->server
= identification_create_from_string(server
);
499 secret
= lib
->settings
->get_str(lib
->settings
,
500 "charon.plugins.tnc-pdp.secret", NULL
);
503 DBG1(DBG_CFG
, "missing RADIUS secret, PDP disabled");
507 this->secret
= chunk_create(secret
, strlen(secret
));
508 this->signer
->set_key(this->signer
, this->secret
);
511 this->job
= callback_job_create_with_prio((callback_job_cb_t
)receive
,
512 this, NULL
, NULL
, JOB_PRIO_CRITICAL
);
513 lib
->processor
->queue_job(lib
->processor
, (job_t
*)this->job
);
515 return &this->public;