summaryrefslogtreecommitdiff
path: root/src/libcharon/plugins/tnc_pdp/tnc_pdp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/plugins/tnc_pdp/tnc_pdp.c')
-rw-r--r--src/libcharon/plugins/tnc_pdp/tnc_pdp.c446
1 files changed, 362 insertions, 84 deletions
diff --git a/src/libcharon/plugins/tnc_pdp/tnc_pdp.c b/src/libcharon/plugins/tnc_pdp/tnc_pdp.c
index a30d89535..31cee9e2b 100644
--- a/src/libcharon/plugins/tnc_pdp/tnc_pdp.c
+++ b/src/libcharon/plugins/tnc_pdp/tnc_pdp.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Andreas Steffen
+ * Copyright (C) 2012-2013 Andreas Steffen
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -22,6 +22,13 @@
#include <radius_message.h>
#include <radius_mppe.h>
+#include <pt_tls_server.h>
+
+#include <tnc/tnc.h>
+
+#include <tncifimv.h>
+#include <tncif_names.h>
+
#include <daemon.h>
#include <utils/debug.h>
#include <pen/pen.h>
@@ -32,6 +39,11 @@
typedef struct private_tnc_pdp_t private_tnc_pdp_t;
/**
+ * Default RADIUS port, when not configured
+ */
+#define RADIUS_PORT 1812
+
+/**
* Maximum size of a RADIUS IP packet
*/
#define MAX_PACKET 4096
@@ -57,14 +69,29 @@ struct private_tnc_pdp_t {
eap_type_t type;
/**
- * IPv4 RADIUS socket
+ * PT-TLS port of the server
+ */
+ u_int16_t pt_tls_port;
+
+ /**
+ * PT-TLS IPv4 socket
*/
- int ipv4;
+ int pt_tls_ipv4;
/**
- * IPv6 RADIUS socket
+ * PT-TLS IPv6 socket
*/
- int ipv6;
+ int pt_tls_ipv6;
+
+ /**
+ * RADIUS IPv4 socket
+ */
+ int radius_ipv4;
+
+ /**
+ * RADIUS IPv6 socket
+ */
+ int radius_ipv6;
/**
* RADIUS shared secret
@@ -90,13 +117,13 @@ struct private_tnc_pdp_t {
* List of registered TNC-PDP connections
*/
tnc_pdp_connections_t *connections;
-};
+};
/**
- * Open IPv4 or IPv6 UDP RADIUS socket
+ * Open IPv4 or IPv6 UDP socket
*/
-static int open_socket(int family, u_int16_t port)
+static int open_udp_socket(int family, u_int16_t port)
{
int on = TRUE;
struct sockaddr_storage addr;
@@ -135,20 +162,115 @@ static int open_socket(int family, u_int16_t port)
skt = socket(family, SOCK_DGRAM, IPPROTO_UDP);
if (skt < 0)
{
- DBG1(DBG_CFG, "opening RADIUS socket failed: %s", strerror(errno));
+ DBG1(DBG_CFG, "opening UDP socket failed: %s", strerror(errno));
return 0;
}
if (setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0)
{
- DBG1(DBG_CFG, "unable to set SO_REUSEADDR on socket: %s", strerror(errno));
+ DBG1(DBG_CFG, "unable to set SO_REUSEADDR on socket: %s",
+ strerror(errno));
close(skt);
return 0;
}
+ if (family == AF_INET6)
+ {
+ if (setsockopt(skt, IPPROTO_IPV6, IPV6_V6ONLY,
+ (void *)&on, sizeof(on)) < 0)
+ {
+ DBG1(DBG_CFG, "unable to set IPV6_V6ONLY on socket: %s",
+ strerror(errno));
+ close(skt);
+ return 0;
+ }
+ }
/* bind the socket */
if (bind(skt, (struct sockaddr *)&addr, addrlen) < 0)
{
- DBG1(DBG_CFG, "unable to bind RADIUS socket: %s", strerror(errno));
+ DBG1(DBG_CFG, "unable to bind UDP socket: %s", strerror(errno));
+ close(skt);
+ return 0;
+ }
+
+ return skt;
+}
+
+/**
+ * Open IPv4 or IPv6 TCP socket
+ */
+static int open_tcp_socket(int family, u_int16_t port)
+{
+ int on = TRUE;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ int skt;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.ss_family = family;
+
+ /* precalculate constants depending on address family */
+ switch (family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
+
+ htoun32(&sin->sin_addr.s_addr, INADDR_ANY);
+ htoun16(&sin->sin_port, port);
+ addrlen = sizeof(struct sockaddr_in);
+ break;
+ }
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
+
+ memcpy(&sin6->sin6_addr, &in6addr_any, sizeof(in6addr_any));
+ htoun16(&sin6->sin6_port, port);
+ addrlen = sizeof(struct sockaddr_in6);
+ break;
+ }
+ default:
+ return 0;
+ }
+
+ /* open the socket */
+ skt = socket(family, SOCK_STREAM, IPPROTO_TCP);
+ if (skt < 0)
+ {
+ DBG1(DBG_CFG, "opening TCP socket failed: %s", strerror(errno));
+ return 0;
+ }
+ if (setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0)
+ {
+ DBG1(DBG_CFG, "unable to set SO_REUSEADDR on socket: %s",
+ strerror(errno));
+ close(skt);
+ return 0;
+ }
+ if (family == AF_INET6)
+ {
+ if (setsockopt(skt, IPPROTO_IPV6, IPV6_V6ONLY,
+ (void *)&on, sizeof(on)) < 0)
+ {
+ DBG1(DBG_CFG, "unable to set IPV6_V6ONLY on socket: %s",
+ strerror(errno));
+ close(skt);
+ return 0;
+ }
+ }
+
+ /* bind the socket */
+ if (bind(skt, (struct sockaddr *)&addr, addrlen) < 0)
+ {
+ DBG1(DBG_CFG, "unable to bind TCP socket: %s", strerror(errno));
+ close(skt);
+ return 0;
+ }
+
+ /* start listening on socket */
+ if (listen(skt, 5) == -1)
+ {
+ DBG1(DBG_TNC, "listen on TCP socket failed: %s", strerror(errno));
close(skt);
return 0;
}
@@ -165,7 +287,8 @@ static void send_message(private_tnc_pdp_t *this, radius_message_t *message,
int fd;
chunk_t data;
- fd = (client->get_family(client) == AF_INET) ? this->ipv4 : this->ipv6;
+ fd = (client->get_family(client) == AF_INET) ?
+ this->radius_ipv4 : this->radius_ipv6;
data = message->get_encoding(message);
DBG2(DBG_CFG, "sending RADIUS packet to %#H", client);
@@ -448,9 +571,95 @@ end:
}
/**
+ * Callback function to get recommendation from TNCCS connection
+ */
+static bool get_recommendation(TNC_IMV_Action_Recommendation rec,
+ TNC_IMV_Evaluation_Result eval)
+{
+ DBG1(DBG_TNC, "final recommendation is '%N' and evaluation is '%N'",
+ TNC_IMV_Action_Recommendation_names, rec,
+ TNC_IMV_Evaluation_Result_names, eval);
+
+ return TRUE;
+}
+
+/**
+ * Get more data on a PT-TLS connection
+ */
+static bool pt_tls_receive_more(pt_tls_server_t *this, int fd,
+ watcher_event_t event)
+{
+ switch (this->handle(this))
+ {
+ case NEED_MORE:
+ return TRUE;
+ case FAILED:
+ case SUCCESS:
+ default:
+ DBG1(DBG_TNC, "PT-TLS connection terminates");
+ this->destroy(this);
+ close(fd);
+ return FALSE;
+ }
+}
+
+/**
+ * Accept TCP connection received on the PT-TLS listening socket
+ */
+static bool pt_tls_receive(private_tnc_pdp_t *this, int fd, watcher_event_t event)
+{
+ int pt_tls_fd;
+ struct sockaddr_storage addr;
+ socklen_t addrlen = sizeof(addr);
+ identification_t *peer;
+ host_t *host;
+ pt_tls_server_t *pt_tls;
+ tnccs_t *tnccs;
+ pt_tls_auth_t auth = PT_TLS_AUTH_TLS_OR_SASL;
+
+ pt_tls_fd = accept(fd, (sockaddr_t*)&addr, &addrlen);
+ if (pt_tls_fd == -1)
+ {
+ DBG1(DBG_TNC, "accepting PT-TLS stream failed: %s", strerror(errno));
+ return FALSE;
+ }
+ host = host_create_from_sockaddr((sockaddr_t*)&addr);
+ DBG1(DBG_TNC, "accepting PT-TLS stream from %H", host);
+ host->destroy(host);
+
+ /* At this moment the peer identity is not known yet */
+ peer = identification_create_from_encoding(ID_ANY, chunk_empty),
+
+ tnccs = tnc->tnccs->create_instance(tnc->tnccs, TNCCS_2_0, TRUE,
+ this->server, peer, TNC_IFT_TLS_2_0,
+ (tnccs_cb_t)get_recommendation);
+ peer->destroy(peer);
+
+ if (!tnccs)
+ {
+ DBG1(DBG_TNC, "could not create TNCCS 2.0 connection instance");
+ close(pt_tls_fd);
+ return FALSE;
+ }
+
+ pt_tls = pt_tls_server_create(this->server, pt_tls_fd, auth, tnccs);
+ if (!pt_tls)
+ {
+ DBG1(DBG_TNC, "could not create PT-TLS connection instance");
+ close(pt_tls_fd);
+ return FALSE;
+ }
+
+ lib->watcher->add(lib->watcher, pt_tls_fd, WATCHER_READ,
+ (watcher_cb_t)pt_tls_receive_more, pt_tls);
+
+ return TRUE;
+}
+
+/**
* Process packets received on the RADIUS socket
*/
-static bool receive(private_tnc_pdp_t *this, int fd, watcher_event_t event)
+static bool radius_receive(private_tnc_pdp_t *this, int fd, watcher_event_t event)
{
radius_message_t *request;
char buffer[MAX_PACKET];
@@ -510,15 +719,25 @@ static bool receive(private_tnc_pdp_t *this, int fd, watcher_event_t event)
METHOD(tnc_pdp_t, destroy, void,
private_tnc_pdp_t *this)
{
- if (this->ipv4)
+ if (this->pt_tls_ipv4)
+ {
+ lib->watcher->remove(lib->watcher, this->pt_tls_ipv4);
+ close(this->pt_tls_ipv4);
+ }
+ if (this->pt_tls_ipv6)
{
- lib->watcher->remove(lib->watcher, this->ipv4);
- close(this->ipv4);
+ lib->watcher->remove(lib->watcher, this->pt_tls_ipv6);
+ close(this->pt_tls_ipv6);
}
- if (this->ipv6)
+ if (this->radius_ipv4)
{
- lib->watcher->remove(lib->watcher, this->ipv6);
- close(this->ipv6);
+ lib->watcher->remove(lib->watcher, this->radius_ipv4);
+ close(this->radius_ipv4);
+ }
+ if (this->radius_ipv6)
+ {
+ lib->watcher->remove(lib->watcher, this->radius_ipv6);
+ close(this->radius_ipv6);
}
DESTROY_IF(this->server);
DESTROY_IF(this->signer);
@@ -531,90 +750,149 @@ METHOD(tnc_pdp_t, destroy, void,
/*
* see header file
*/
-tnc_pdp_t *tnc_pdp_create(u_int16_t port)
+tnc_pdp_t *tnc_pdp_create(void)
{
private_tnc_pdp_t *this;
char *secret, *server, *eap_type_str;
+ int radius_port, pt_tls_port;
+ bool radius_enable, pt_tls_enable;
- INIT(this,
- .public = {
- .destroy = _destroy,
- },
- .ipv4 = open_socket(AF_INET, port),
- .ipv6 = open_socket(AF_INET6, port),
- .hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5),
- .signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128),
- .ng = lib->crypto->create_nonce_gen(lib->crypto),
- .connections = tnc_pdp_connections_create(),
- );
+ server = lib->settings->get_str(lib->settings,
+ "%s.plugins.tnc-pdp.server", NULL, charon->name);
+ pt_tls_enable = lib->settings->get_bool(lib->settings,
+ "%s.plugins.tnc-pdp.pt_tls.enable", TRUE, charon->name);
+ pt_tls_port = lib->settings->get_int(lib->settings,
+ "%s.plugins.tnc-pdp.pt_tls.port", PT_TLS_PORT, charon->name);
+ radius_enable = lib->settings->get_bool(lib->settings,
+ "%s.plugins.tnc-pdp.radius.enable", TRUE, charon->name);
+ radius_port = lib->settings->get_int(lib->settings,
+ "%s.plugins.tnc-pdp.radius.port", RADIUS_PORT, charon->name);
+ secret = lib->settings->get_str(lib->settings,
+ "%s.plugins.tnc-pdp.radius.secret", NULL, charon->name);
+ eap_type_str = lib->settings->get_str(lib->settings,
+ "%s.plugins.tnc-pdp.radius.method", "ttls", charon->name);
- if (!this->hasher || !this->signer || !this->ng)
- {
- DBG1(DBG_CFG, "RADIUS initialization failed, HMAC/MD5/NG required");
- destroy(this);
- return NULL;
- }
- if (!this->ipv4 && !this->ipv6)
+ if (!pt_tls_enable && !radius_enable)
{
- DBG1(DBG_NET, "could not create any RADIUS sockets");
- destroy(this);
+ DBG1(DBG_CFG, " neither PT-TLS and RADIUS protocols enabled, PDP disabled");
return NULL;
}
- if (this->ipv4)
- {
- lib->watcher->add(lib->watcher, this->ipv4, WATCHER_READ,
- (watcher_cb_t)receive, this);
- }
- else
- {
- DBG1(DBG_NET, "could not open IPv4 RADIUS socket, IPv4 disabled");
- }
- if (this->ipv6)
- {
- lib->watcher->add(lib->watcher, this->ipv6, WATCHER_READ,
- (watcher_cb_t)receive, this);
- }
- else
- {
- DBG1(DBG_NET, "could not open IPv6 RADIUS socket, IPv6 disabled");
- }
-
- server = lib->settings->get_str(lib->settings,
- "%s.plugins.tnc-pdp.server", NULL, charon->name);
if (!server)
{
DBG1(DBG_CFG, "missing PDP server name, PDP disabled");
- destroy(this);
return NULL;
}
- this->server = identification_create_from_string(server);
- secret = lib->settings->get_str(lib->settings,
- "%s.plugins.tnc-pdp.secret", NULL, charon->name);
- if (!secret)
- {
- DBG1(DBG_CFG, "missing RADIUS secret, PDP disabled");
- destroy(this);
- return NULL;
- }
- this->secret = chunk_create(secret, strlen(secret));
- if (!this->signer->set_key(this->signer, this->secret))
+ INIT(this,
+ .public = {
+ .destroy = _destroy,
+ },
+ .server = identification_create_from_string(server),
+ .connections = tnc_pdp_connections_create(),
+ );
+
+ /* Create IPv4 and IPv6 PT-TLS listening sockets */
+ if (pt_tls_enable)
{
- DBG1(DBG_CFG, "could not set signer key");
- destroy(this);
- return NULL;
+ this->pt_tls_ipv4 = open_tcp_socket(AF_INET, pt_tls_port);
+ this->pt_tls_ipv6 = open_tcp_socket(AF_INET6, pt_tls_port);
+
+ if (!this->pt_tls_ipv4 && !this->pt_tls_ipv6)
+ {
+ DBG1(DBG_NET, "could not create any PT-TLS sockets");
+ destroy(this);
+ return NULL;
+ }
+ this->pt_tls_port = pt_tls_port;
+
+ if (this->pt_tls_ipv4)
+ {
+ lib->watcher->add(lib->watcher, this->pt_tls_ipv4, WATCHER_READ,
+ (watcher_cb_t)pt_tls_receive, this);
+ }
+ else
+ {
+ DBG1(DBG_NET, "could not open IPv4 PT-TLS socket, IPv4 disabled");
+ }
+
+ if (this->pt_tls_ipv6)
+ {
+ lib->watcher->add(lib->watcher, this->pt_tls_ipv6, WATCHER_READ,
+ (watcher_cb_t)pt_tls_receive, this);
+ }
+ else
+ {
+ DBG1(DBG_NET, "could not open IPv6 PT-TLS socket, IPv6 disabled");
+ }
+
+ /* register PT-TLS service */
+ lib->set(lib, "pt-tls-server", this->server);
+ lib->set(lib, "pt-tls-port", &this->pt_tls_port);
}
- eap_type_str = lib->settings->get_str(lib->settings,
- "%s.plugins.tnc-pdp.method", "ttls", charon->name);
- this->type = eap_type_from_string(eap_type_str);
- if (this->type == 0)
+ /* Create IPv4 and IPv6 RADIUS listening sockets */
+ if (radius_enable)
{
- DBG1(DBG_CFG, "unrecognized eap method \"%s\"", eap_type_str);
- destroy(this);
- return NULL;
+ if (!secret)
+ {
+ DBG1(DBG_CFG, "missing RADIUS secret, PDP disabled");
+ destroy(this);
+ return NULL;
+ }
+
+ this->radius_ipv4 = open_udp_socket(AF_INET, radius_port);
+ this->radius_ipv6 = open_udp_socket(AF_INET6, radius_port);
+ this->secret = chunk_from_str(secret);
+ this->type = eap_type_from_string(eap_type_str);
+ this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
+ this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128);
+ this->ng = lib->crypto->create_nonce_gen(lib->crypto);
+
+ if (!this->hasher || !this->signer || !this->ng)
+ {
+ DBG1(DBG_CFG, "RADIUS initialization failed, HMAC/MD5/NG required");
+ destroy(this);
+ return NULL;
+ }
+ if (!this->radius_ipv4 && !this->radius_ipv6)
+ {
+ DBG1(DBG_NET, "could not create any RADIUS sockets");
+ destroy(this);
+ return NULL;
+ }
+ if (this->radius_ipv4)
+ {
+ lib->watcher->add(lib->watcher, this->radius_ipv4, WATCHER_READ,
+ (watcher_cb_t)radius_receive, this);
+ }
+ else
+ {
+ DBG1(DBG_NET, "could not open IPv4 RADIUS socket, IPv4 disabled");
+ }
+ if (this->radius_ipv6)
+ {
+ lib->watcher->add(lib->watcher, this->radius_ipv6, WATCHER_READ,
+ (watcher_cb_t)radius_receive, this);
+ }
+ else
+ {
+ DBG1(DBG_NET, "could not open IPv6 RADIUS socket, IPv6 disabled");
+ }
+
+ if (!this->signer->set_key(this->signer, this->secret))
+ {
+ DBG1(DBG_CFG, "could not set signer key");
+ destroy(this);
+ return NULL;
+ }
+ if (this->type == 0)
+ {
+ DBG1(DBG_CFG, "unrecognized eap method \"%s\"", eap_type_str);
+ destroy(this);
+ return NULL;
+ }
+ DBG1(DBG_IKE, "eap method %N selected", eap_type_names, this->type);
}
- DBG1(DBG_IKE, "eap method %N selected", eap_type_names, this->type);
return &this->public;
}