aboutsummaryrefslogtreecommitdiffstats
path: root/src/libpttls
diff options
context:
space:
mode:
authorMartin Willi <martin@revosec.ch>2013-03-07 14:10:50 +0100
committerMartin Willi <martin@revosec.ch>2013-03-07 14:10:50 +0100
commit7d70a14779b2dc897bc46559e6475a9bd01b2be6 (patch)
tree746bdedefd60fae4e6b010e5415919cff4140e32 /src/libpttls
parent9d9042d6d95b0ecb292d77e7d8350fcd28e1aa27 (diff)
parent1db6bf2f3f8fe0240a63dbd7c79323140daa622e (diff)
downloadstrongswan-7d70a14779b2dc897bc46559e6475a9bd01b2be6.tar.bz2
strongswan-7d70a14779b2dc897bc46559e6475a9bd01b2be6.tar.xz
Merge branch 'pt-tls'
Diffstat (limited to 'src/libpttls')
-rw-r--r--src/libpttls/Makefile.am4
-rw-r--r--src/libpttls/pt_tls.h28
-rw-r--r--src/libpttls/pt_tls_client.c225
-rw-r--r--src/libpttls/pt_tls_client.h10
-rw-r--r--src/libpttls/pt_tls_dispatcher.c10
-rw-r--r--src/libpttls/pt_tls_dispatcher.h5
-rw-r--r--src/libpttls/pt_tls_server.c281
-rw-r--r--src/libpttls/pt_tls_server.h5
-rw-r--r--src/libpttls/sasl/sasl_mechanism.c92
-rw-r--r--src/libpttls/sasl/sasl_mechanism.h103
-rw-r--r--src/libpttls/sasl/sasl_plain/sasl_plain.c171
-rw-r--r--src/libpttls/sasl/sasl_plain/sasl_plain.h48
12 files changed, 951 insertions, 31 deletions
diff --git a/src/libpttls/Makefile.am b/src/libpttls/Makefile.am
index d19af0365..48123181b 100644
--- a/src/libpttls/Makefile.am
+++ b/src/libpttls/Makefile.am
@@ -7,4 +7,6 @@ libpttls_la_LIBADD = $(top_builddir)/src/libtls/libtls.la
libpttls_la_SOURCES = pt_tls.c pt_tls.h \
pt_tls_client.c pt_tls_client.h \
pt_tls_server.c pt_tls_server.h \
- pt_tls_dispatcher.c pt_tls_dispatcher.h
+ pt_tls_dispatcher.c pt_tls_dispatcher.h \
+ sasl/sasl_plain/sasl_plain.c sasl/sasl_plain/sasl_plain.h \
+ sasl/sasl_mechanism.c sasl/sasl_mechanism.h
diff --git a/src/libpttls/pt_tls.h b/src/libpttls/pt_tls.h
index e9e8cfbeb..cb8bde05c 100644
--- a/src/libpttls/pt_tls.h
+++ b/src/libpttls/pt_tls.h
@@ -38,6 +38,8 @@
#define PT_TLS_HEADER_LEN 16
typedef enum pt_tls_message_type_t pt_tls_message_type_t;
+typedef enum pt_tls_sasl_result_t pt_tls_sasl_result_t;
+typedef enum pt_tls_auth_t pt_tls_auth_t;
/**
* Message types, as defined by NEA PT-TLS
@@ -55,6 +57,32 @@ enum pt_tls_message_type_t {
};
/**
+ * Result code for a single SASL mechansim, as sent in PT_TLS_SASL_RESULT
+ */
+enum pt_tls_sasl_result_t {
+ PT_TLS_SASL_RESULT_SUCCESS = 0,
+ PT_TLS_SASL_RESULT_FAILURE = 1,
+ PT_TLS_SASL_RESULT_ABORT = 2,
+ PT_TLS_SASL_RESULT_MECH_FAILURE = 3,
+};
+
+/**
+ * Client authentication to require as PT-TLS server.
+ */
+enum pt_tls_auth_t {
+ /** don't require TLS client certificate or request SASL authentication */
+ PT_TLS_AUTH_NONE,
+ /** require TLS certificate authentication, no SASL */
+ PT_TLS_AUTH_TLS,
+ /** do SASL regardless of TLS certificate authentication */
+ PT_TLS_AUTH_SASL,
+ /* if client does not authenticate with a TLS certificate, request SASL */
+ PT_TLS_AUTH_TLS_OR_SASL,
+ /* require both, TLS certificate authentication and SASL */
+ PT_TLS_AUTH_TLS_AND_SASL,
+};
+
+/**
* Read a PT-TLS message, create reader over Message Value.
*
* @param tls TLS socket to read from
diff --git a/src/libpttls/pt_tls_client.c b/src/libpttls/pt_tls_client.c
index 948d92982..a01da3939 100644
--- a/src/libpttls/pt_tls_client.c
+++ b/src/libpttls/pt_tls_client.c
@@ -16,6 +16,8 @@
#include "pt_tls_client.h"
#include "pt_tls.h"
+#include <sasl/sasl_mechanism.h>
+
#include <tls_socket.h>
#include <utils/debug.h>
@@ -48,7 +50,12 @@ struct private_pt_tls_client_t {
/**
* Server identity
*/
- identification_t *id;
+ identification_t *server;
+
+ /**
+ * Client authentication identity
+ */
+ identification_t *client;
/**
* Current PT-TLS message identifier
@@ -77,7 +84,7 @@ static bool make_connection(private_pt_tls_client_t *this)
return FALSE;
}
- this->tls = tls_socket_create(FALSE, this->id, NULL, fd, NULL);
+ this->tls = tls_socket_create(FALSE, this->server, this->client, fd, NULL);
if (!this->tls)
{
close(fd);
@@ -128,33 +135,211 @@ static bool negotiate_version(private_pt_tls_client_t *this)
}
/**
- * Authenticate session using SASL
+ * Run a SASL mechanism
*/
-static bool authenticate(private_pt_tls_client_t *this)
+static status_t do_sasl(private_pt_tls_client_t *this, sasl_mechanism_t *sasl)
+{
+ u_int32_t type, vendor, identifier;
+ u_int8_t result;
+ bio_reader_t *reader;
+ bio_writer_t *writer;
+ chunk_t data;
+
+ writer = bio_writer_create(32);
+ writer->write_data8(writer, chunk_from_str(sasl->get_name(sasl)));
+ switch (sasl->build(sasl, &data))
+ {
+ case INVALID_STATE:
+ break;
+ case NEED_MORE:
+ writer->write_data(writer, data);
+ free(data.ptr);
+ break;
+ case SUCCESS:
+ /* shouldn't happen */
+ free(data.ptr);
+ /* FALL */
+ case FAILED:
+ default:
+ writer->destroy(writer);
+ return FAILED;
+ }
+ if (!pt_tls_write(this->tls, writer, PT_TLS_SASL_MECH_SELECTION,
+ this->identifier++))
+ {
+ return FAILED;
+ }
+ while (TRUE)
+ {
+ reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
+ if (!reader)
+ {
+ return FAILED;
+ }
+ if (vendor != 0)
+ {
+ reader->destroy(reader);
+ return FAILED;
+ }
+ switch (type)
+ {
+ case PT_TLS_SASL_AUTH_DATA:
+ switch (sasl->process(sasl, reader->peek(reader)))
+ {
+ case NEED_MORE:
+ reader->destroy(reader);
+ break;
+ case SUCCESS:
+ /* should not happen, as it would come in a RESULT */
+ case FAILED:
+ default:
+ reader->destroy(reader);
+ return FAILED;
+ }
+ break;
+ case PT_TLS_SASL_RESULT:
+ if (!reader->read_uint8(reader, &result))
+ {
+ reader->destroy(reader);
+ return FAILED;
+ }
+ switch (result)
+ {
+ case PT_TLS_SASL_RESULT_ABORT:
+ DBG1(DBG_TNC, "received SASL abort result");
+ reader->destroy(reader);
+ return FAILED;
+ case PT_TLS_SASL_RESULT_SUCCESS:
+ DBG1(DBG_TNC, "received SASL success result");
+ switch (sasl->process(sasl, reader->peek(reader)))
+ {
+ case SUCCESS:
+ reader->destroy(reader);
+ return SUCCESS;
+ case NEED_MORE:
+ /* inacceptable, it won't get more. FALL */
+ case FAILED:
+ default:
+ reader->destroy(reader);
+ return FAILED;
+ }
+ break;
+ case PT_TLS_SASL_RESULT_MECH_FAILURE:
+ case PT_TLS_SASL_RESULT_FAILURE:
+ DBG1(DBG_TNC, "received SASL failure result");
+ /* non-fatal failure, try again */
+ reader->destroy(reader);
+ return NEED_MORE;
+ }
+ default:
+ return FAILED;
+ }
+
+ writer = bio_writer_create(32);
+ switch (sasl->build(sasl, &data))
+ {
+ case INVALID_STATE:
+ break;
+ case SUCCESS:
+ /* shoudln't happen, continue until we get a result */
+ case NEED_MORE:
+ writer->write_data(writer, data);
+ free(data.ptr);
+ break;
+ case FAILED:
+ default:
+ writer->destroy(writer);
+ return FAILED;
+ }
+ if (!pt_tls_write(this->tls, writer, PT_TLS_SASL_AUTH_DATA,
+ this->identifier++))
+ {
+ return FAILED;
+ }
+ }
+}
+
+/**
+ * Read SASL mechanism list, select and run mechanism
+ */
+static status_t select_and_do_sasl(private_pt_tls_client_t *this)
{
bio_reader_t *reader;
+ sasl_mechanism_t *sasl = NULL;
u_int32_t type, vendor, identifier;
+ u_int8_t len;
+ chunk_t chunk;
+ char buf[21];
+ status_t status = NEED_MORE;
reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
if (!reader)
{
- return FALSE;
+ return FAILED;
}
if (vendor != 0 || type != PT_TLS_SASL_MECHS)
{
- DBG1(DBG_TNC, "PT-TLS authentication failed");
reader->destroy(reader);
- return FALSE;
+ return FAILED;
}
-
- if (reader->remaining(reader))
- { /* mechanism list not empty, FAIL until we support it */
+ if (!reader->remaining(reader))
+ { /* mechanism list empty, SASL completed */
+ DBG1(DBG_TNC, "PT-TLS authentication complete");
reader->destroy(reader);
- return FALSE;
+ return SUCCESS;
+ }
+ while (reader->remaining(reader))
+ {
+ if (!reader->read_uint8(reader, &len) ||
+ !reader->read_data(reader, len & 0x1F, &chunk))
+ {
+ reader->destroy(reader);
+ return FAILED;
+ }
+ snprintf(buf, sizeof(buf), "%.*s", (int)chunk.len, chunk.ptr);
+ sasl = sasl_mechanism_create(buf, this->client);
+ if (sasl)
+ {
+ break;
+ }
}
- DBG1(DBG_TNC, "PT-TLS authentication complete");
reader->destroy(reader);
- return TRUE;
+
+ if (!sasl)
+ {
+ /* TODO: send PT-TLS error (5) */
+ return FAILED;
+ }
+ while (status == NEED_MORE)
+ {
+ status = do_sasl(this, sasl);
+ }
+ sasl->destroy(sasl);
+ if (status == SUCCESS)
+ { /* continue until we receive empty SASL mechanism list */
+ return NEED_MORE;
+ }
+ return FAILED;
+}
+
+/**
+ * Authenticate session using SASL
+ */
+static bool authenticate(private_pt_tls_client_t *this)
+{
+ while (TRUE)
+ {
+ switch (select_and_do_sasl(this))
+ {
+ case NEED_MORE:
+ continue;
+ case SUCCESS:
+ return TRUE;
+ case FAILED:
+ default:
+ return FALSE;
+ }
+ }
}
/**
@@ -276,18 +461,23 @@ METHOD(pt_tls_client_t, destroy, void,
{
if (this->tls)
{
- close(this->tls->get_fd(this->tls));
+ int fd;
+
+ fd = this->tls->get_fd(this->tls);
this->tls->destroy(this->tls);
+ close(fd);
}
this->address->destroy(this->address);
- this->id->destroy(this->id);
+ this->server->destroy(this->server);
+ this->client->destroy(this->client);
free(this);
}
/**
* See header
*/
-pt_tls_client_t *pt_tls_client_create(host_t *address, identification_t *id)
+pt_tls_client_t *pt_tls_client_create(host_t *address, identification_t *server,
+ identification_t *client)
{
private_pt_tls_client_t *this;
@@ -297,7 +487,8 @@ pt_tls_client_t *pt_tls_client_create(host_t *address, identification_t *id)
.destroy = _destroy,
},
.address = address,
- .id = id,
+ .server = server,
+ .client = client,
);
return &this->public;
diff --git a/src/libpttls/pt_tls_client.h b/src/libpttls/pt_tls_client.h
index 2e0553641..1d418d181 100644
--- a/src/libpttls/pt_tls_client.h
+++ b/src/libpttls/pt_tls_client.h
@@ -50,10 +50,16 @@ struct pt_tls_client_t {
/**
* Create a pt_tls_client instance.
*
+ * The client identity is used for:
+ * - TLS authentication if an appropirate certificate is found
+ * - SASL authentication if requested from the server
+ *
* @param address address/port to run assessments against, gets owned
- * @param id server identity to use for authentication, gets owned
+ * @param server server identity to use for authentication, gets owned
+ * @param client client identity to use for authentication, gets owned
* @return PT-TLS context
*/
-pt_tls_client_t *pt_tls_client_create(host_t *address, identification_t *id);
+pt_tls_client_t *pt_tls_client_create(host_t *address, identification_t *server,
+ identification_t *client);
#endif /** PT_TLS_CLIENT_H_ @}*/
diff --git a/src/libpttls/pt_tls_dispatcher.c b/src/libpttls/pt_tls_dispatcher.c
index 813580cd0..469951616 100644
--- a/src/libpttls/pt_tls_dispatcher.c
+++ b/src/libpttls/pt_tls_dispatcher.c
@@ -42,6 +42,11 @@ struct private_pt_tls_dispatcher_t {
int fd;
/**
+ * Client authentication requirements
+ */
+ pt_tls_auth_t auth;
+
+ /**
* Server identity
*/
identification_t *server;
@@ -141,7 +146,7 @@ METHOD(pt_tls_dispatcher_t, dispatch, void,
close(fd);
continue;
}
- connection = pt_tls_server_create(this->server, fd, tnccs);
+ connection = pt_tls_server_create(this->server, fd, this->auth, tnccs);
if (!connection)
{
close(fd);
@@ -171,7 +176,7 @@ METHOD(pt_tls_dispatcher_t, destroy, void,
* See header
*/
pt_tls_dispatcher_t *pt_tls_dispatcher_create(host_t *address,
- identification_t *id)
+ identification_t *id, pt_tls_auth_t auth)
{
private_pt_tls_dispatcher_t *this;
@@ -184,6 +189,7 @@ pt_tls_dispatcher_t *pt_tls_dispatcher_create(host_t *address,
/* we currently don't authenticate the peer, use %any identity */
.peer = identification_create_from_encoding(ID_ANY, chunk_empty),
.fd = -1,
+ .auth = auth,
);
if (!open_socket(this, address))
diff --git a/src/libpttls/pt_tls_dispatcher.h b/src/libpttls/pt_tls_dispatcher.h
index 3c6560baa..080197263 100644
--- a/src/libpttls/pt_tls_dispatcher.h
+++ b/src/libpttls/pt_tls_dispatcher.h
@@ -26,6 +26,8 @@
#include <tnc/tnccs/tnccs.h>
+#include "pt_tls.h"
+
typedef struct pt_tls_dispatcher_t pt_tls_dispatcher_t;
/**
@@ -64,9 +66,10 @@ struct pt_tls_dispatcher_t {
*
* @param address server address with port to listen on, gets owned
* @param id TLS server identity, gets owned
+ * @param auth client authentication to perform
* @return dispatcher service
*/
pt_tls_dispatcher_t *pt_tls_dispatcher_create(host_t *address,
- identification_t *id);
+ identification_t *id, pt_tls_auth_t auth);
#endif /** PT_TLS_DISPATCHER_H_ @}*/
diff --git a/src/libpttls/pt_tls_server.c b/src/libpttls/pt_tls_server.c
index b525acb6f..3e134f0dd 100644
--- a/src/libpttls/pt_tls_server.c
+++ b/src/libpttls/pt_tls_server.c
@@ -14,7 +14,8 @@
*/
#include "pt_tls_server.h"
-#include "pt_tls.h"
+
+#include <sasl/sasl_mechanism.h>
#include <utils/debug.h>
@@ -35,6 +36,11 @@ struct private_pt_tls_server_t {
*/
tls_socket_t *tls;
+ /**
+ * Client authentication requirements
+ */
+ pt_tls_auth_t auth;
+
enum {
/* expecting version negotiation */
PT_TLS_SERVER_VERSION,
@@ -99,19 +105,280 @@ static bool negotiate_version(private_pt_tls_server_t *this)
}
/**
- * Authenticated PT-TLS session with SASL
+ * Process SASL data, send result
*/
-static bool authenticate(private_pt_tls_server_t *this)
+static status_t process_sasl(private_pt_tls_server_t *this,
+ sasl_mechanism_t *sasl, chunk_t data)
+{
+ bio_writer_t *writer;
+
+ switch (sasl->process(sasl, data))
+ {
+ case NEED_MORE:
+ return NEED_MORE;
+ case SUCCESS:
+ DBG1(DBG_TNC, "SASL %s authentication successful",
+ sasl->get_name(sasl));
+ writer = bio_writer_create(1);
+ writer->write_uint8(writer, PT_TLS_SASL_RESULT_SUCCESS);
+ if (pt_tls_write(this->tls, writer, PT_TLS_SASL_RESULT,
+ this->identifier++))
+ {
+ return SUCCESS;
+ }
+ return FAILED;
+ case FAILED:
+ default:
+ DBG1(DBG_TNC, "SASL %s authentication failed",
+ sasl->get_name(sasl));
+ writer = bio_writer_create(1);
+ /* sending abort does not allow the client to retry */
+ writer->write_uint8(writer, PT_TLS_SASL_RESULT_ABORT);
+ pt_tls_write(this->tls, writer, PT_TLS_SASL_RESULT,
+ this->identifier++);
+ return FAILED;
+ }
+}
+
+/**
+ * Read a SASL message and process it
+ */
+static status_t read_sasl(private_pt_tls_server_t *this,
+ sasl_mechanism_t *sasl)
+{
+ u_int32_t vendor, type, identifier;
+ bio_reader_t *reader;
+ status_t status;
+ chunk_t data;
+
+ reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
+ if (!reader)
+ {
+ return FAILED;
+ }
+ if (vendor != 0 || type != PT_TLS_SASL_AUTH_DATA ||
+ !reader->read_data(reader, reader->remaining(reader), &data))
+ {
+ reader->destroy(reader);
+ return FAILED;
+ }
+ status = process_sasl(this, sasl, data);
+ reader->destroy(reader);
+ return status;
+}
+
+/**
+ * Build and write SASL message, or result message
+ */
+static status_t write_sasl(private_pt_tls_server_t *this,
+ sasl_mechanism_t *sasl)
{
bio_writer_t *writer;
+ chunk_t chunk;
+
+ switch (sasl->build(sasl, &chunk))
+ {
+ case NEED_MORE:
+ writer = bio_writer_create(chunk.len);
+ writer->write_data(writer, chunk);
+ free(chunk.ptr);
+ if (pt_tls_write(this->tls, writer, PT_TLS_SASL_AUTH_DATA,
+ this->identifier++))
+ {
+ return NEED_MORE;
+ }
+ return FAILED;
+ case SUCCESS:
+ DBG1(DBG_TNC, "SASL %s authentication successful",
+ sasl->get_name(sasl));
+ writer = bio_writer_create(1 + chunk.len);
+ writer->write_uint8(writer, PT_TLS_SASL_RESULT_SUCCESS);
+ writer->write_data(writer, chunk);
+ free(chunk.ptr);
+ if (pt_tls_write(this->tls, writer, PT_TLS_SASL_RESULT,
+ this->identifier++))
+ {
+ return SUCCESS;
+ }
+ return FAILED;
+ case FAILED:
+ default:
+ DBG1(DBG_TNC, "SASL %s authentication failed",
+ sasl->get_name(sasl));
+ writer = bio_writer_create(1);
+ /* sending abort does not allow the client to retry */
+ writer->write_uint8(writer, PT_TLS_SASL_RESULT_ABORT);
+ pt_tls_write(this->tls, writer, PT_TLS_SASL_RESULT,
+ this->identifier++);
+ return FAILED;
+ }
+}
+
+/**
+ * Send the list of supported SASL mechanisms
+ */
+static bool send_sasl_mechs(private_pt_tls_server_t *this)
+{
+ enumerator_t *enumerator;
+ bio_writer_t *writer = NULL;
+ char *name;
+
+ enumerator = sasl_mechanism_create_enumerator(TRUE);
+ while (enumerator->enumerate(enumerator, &name))
+ {
+ if (!writer)
+ {
+ writer = bio_writer_create(32);
+ }
+ DBG1(DBG_TNC, "offering SASL %s", name);
+ writer->write_data8(writer, chunk_from_str(name));
+ }
+ enumerator->destroy(enumerator);
- /* send empty SASL mechanims list to skip authentication */
- writer = bio_writer_create(0);
+ if (!writer)
+ { /* no mechanisms available? */
+ return FALSE;
+ }
return pt_tls_write(this->tls, writer, PT_TLS_SASL_MECHS,
this->identifier++);
}
/**
+ * Read the selected SASL mechanism, and process piggybacked data
+ */
+static status_t read_sasl_mech_selection(private_pt_tls_server_t *this,
+ sasl_mechanism_t **out)
+{
+ u_int32_t vendor, type, identifier;
+ sasl_mechanism_t *sasl;
+ bio_reader_t *reader;
+ chunk_t chunk;
+ u_int8_t len;
+ char buf[21];
+
+ reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
+ if (!reader)
+ {
+ return FAILED;
+ }
+ if (vendor != 0 || type != PT_TLS_SASL_MECH_SELECTION ||
+ !reader->read_uint8(reader, &len) ||
+ !reader->read_data(reader, len & 0x1F, &chunk))
+ {
+ reader->destroy(reader);
+ return FAILED;
+ }
+ snprintf(buf, sizeof(buf), "%.*s", (int)chunk.len, chunk.ptr);
+
+ DBG1(DBG_TNC, "client starts SASL %s authentication", buf);
+
+ sasl = sasl_mechanism_create(buf, NULL);
+ if (!sasl)
+ {
+ reader->destroy(reader);
+ return FAILED;
+ }
+ /* initial SASL data piggybacked? */
+ if (reader->remaining(reader))
+ {
+ switch (process_sasl(this, sasl, reader->peek(reader)))
+ {
+ case NEED_MORE:
+ break;
+ case SUCCESS:
+ reader->destroy(reader);
+ *out = sasl;
+ return SUCCESS;
+ case FAILED:
+ default:
+ reader->destroy(reader);
+ sasl->destroy(sasl);
+ return FAILED;
+ }
+ }
+ reader->destroy(reader);
+ *out = sasl;
+ return NEED_MORE;
+}
+
+/**
+ * Do a single SASL exchange
+ */
+static bool do_sasl(private_pt_tls_server_t *this)
+{
+ sasl_mechanism_t *sasl;
+ status_t status;
+
+ switch (this->auth)
+ {
+ case PT_TLS_AUTH_NONE:
+ return TRUE;
+ case PT_TLS_AUTH_TLS:
+ if (this->tls->get_peer_id(this->tls))
+ {
+ return TRUE;
+ }
+ DBG1(DBG_TNC, "requiring TLS certificate client authentication");
+ return FALSE;
+ case PT_TLS_AUTH_SASL:
+ break;
+ case PT_TLS_AUTH_TLS_OR_SASL:
+ if (this->tls->get_peer_id(this->tls))
+ {
+ DBG1(DBG_TNC, "skipping SASL, client authenticated with TLS "
+ "certificate");
+ return TRUE;
+ }
+ break;
+ case PT_TLS_AUTH_TLS_AND_SASL:
+ default:
+ if (!this->tls->get_peer_id(this->tls))
+ {
+ DBG1(DBG_TNC, "requiring TLS certificate client authentication");
+ return FALSE;
+ }
+ break;
+ }
+
+ if (!send_sasl_mechs(this))
+ {
+ return FALSE;
+ }
+ status = read_sasl_mech_selection(this, &sasl);
+ if (status == FAILED)
+ {
+ return FALSE;
+ }
+ while (status == NEED_MORE)
+ {
+ status = write_sasl(this, sasl);
+ if (status == NEED_MORE)
+ {
+ status = read_sasl(this, sasl);
+ }
+ }
+ sasl->destroy(sasl);
+ return status == SUCCESS;
+}
+
+/**
+ * Authenticated PT-TLS session with a single SASL method
+ */
+static bool authenticate(private_pt_tls_server_t *this)
+{
+ if (do_sasl(this))
+ {
+ /* complete SASL with emtpy mechanism list */
+ bio_writer_t *writer;
+
+ writer = bio_writer_create(0);
+ return pt_tls_write(this->tls, writer, PT_TLS_SASL_MECHS,
+ this->identifier++);
+ }
+ return FALSE;
+}
+
+/**
* Perform assessment
*/
static bool assess(private_pt_tls_server_t *this, tls_t *tnccs)
@@ -213,7 +480,6 @@ METHOD(pt_tls_server_t, handle, status_t,
this->state = PT_TLS_SERVER_AUTH;
break;
case PT_TLS_SERVER_AUTH:
- DBG1(DBG_TNC, "sending empty mechanism list to skip SASL");
if (!authenticate(this))
{
return FAILED;
@@ -251,7 +517,7 @@ METHOD(pt_tls_server_t, destroy, void,
* See header
*/
pt_tls_server_t *pt_tls_server_create(identification_t *server, int fd,
- tnccs_t *tnccs)
+ pt_tls_auth_t auth, tnccs_t *tnccs)
{
private_pt_tls_server_t *this;
@@ -264,6 +530,7 @@ pt_tls_server_t *pt_tls_server_create(identification_t *server, int fd,
.state = PT_TLS_SERVER_VERSION,
.tls = tls_socket_create(TRUE, server, NULL, fd, NULL),
.tnccs = (tls_t*)tnccs,
+ .auth = auth,
);
if (!this->tls)
diff --git a/src/libpttls/pt_tls_server.h b/src/libpttls/pt_tls_server.h
index 244111b43..3e18aee8f 100644
--- a/src/libpttls/pt_tls_server.h
+++ b/src/libpttls/pt_tls_server.h
@@ -25,6 +25,8 @@
#include <tnc/tnccs/tnccs.h>
+#include "pt_tls.h"
+
typedef struct pt_tls_server_t pt_tls_server_t;
/**
@@ -60,10 +62,11 @@ struct pt_tls_server_t {
*
* @param server TLS server identity
* @param fd client connection socket
+ * @param auth client authentication requirements
* @param tnccs inner TNCCS protocol handler to use for this connection
* @return PT-TLS server
*/
pt_tls_server_t *pt_tls_server_create(identification_t *server, int fd,
- tnccs_t *tnccs);
+ pt_tls_auth_t auth, tnccs_t *tnccs);
#endif /** PT_TLS_SERVER_H_ @}*/
diff --git a/src/libpttls/sasl/sasl_mechanism.c b/src/libpttls/sasl/sasl_mechanism.c
new file mode 100644
index 000000000..05a02e56d
--- /dev/null
+++ b/src/libpttls/sasl/sasl_mechanism.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "sasl_mechanism.h"
+
+#include "sasl_plain/sasl_plain.h"
+
+/**
+ * Available SASL mechanisms.
+ */
+static struct {
+ char *name;
+ bool server;
+ sasl_mechanism_constructor_t create;
+} mechs[] = {
+ { "PLAIN", TRUE, (sasl_mechanism_constructor_t)sasl_plain_create },
+ { "PLAIN", FALSE, (sasl_mechanism_constructor_t)sasl_plain_create },
+};
+
+/**
+ * See header.
+ */
+sasl_mechanism_t *sasl_mechanism_create(char *name, identification_t *client)
+{
+ int i;
+
+ for (i = 0; i < countof(mechs); i++)
+ {
+ if (streq(mechs[i].name, name) && mechs[i].server == (client == NULL))
+ {
+ return mechs[i].create(name, client);
+ }
+ }
+ return NULL;
+}
+
+/**
+ * SASL mechanism enumerator
+ */
+typedef struct {
+ /** implements enumerator_t */
+ enumerator_t public;
+ /** looking for client or server? */
+ bool server;
+ /** position in mechs[] */
+ int i;
+} mech_enumerator_t;
+
+METHOD(enumerator_t, mech_enumerate, bool,
+ mech_enumerator_t *this, char **name)
+{
+ while (this->i < countof(mechs))
+ {
+ if (mechs[this->i].server == this->server)
+ {
+ *name = mechs[this->i].name;
+ this->i++;
+ return TRUE;
+ }
+ this->i++;
+ }
+ return FALSE;
+}
+
+/**
+ * See header.
+ */
+enumerator_t* sasl_mechanism_create_enumerator(bool server)
+{
+ mech_enumerator_t *enumerator;
+
+ INIT(enumerator,
+ .public = {
+ .enumerate = (void*)_mech_enumerate,
+ .destroy = (void*)free,
+ },
+ .server = server,
+ );
+ return &enumerator->public;
+}
diff --git a/src/libpttls/sasl/sasl_mechanism.h b/src/libpttls/sasl/sasl_mechanism.h
new file mode 100644
index 000000000..1a23a119e
--- /dev/null
+++ b/src/libpttls/sasl/sasl_mechanism.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup sasl_mechanism sasl_mechanism
+ * @{ @ingroup sasl
+ */
+
+#ifndef SASL_MECHANISM_H_
+#define SASL_MECHANISM_H_
+
+typedef struct sasl_mechanism_t sasl_mechanism_t;
+
+#include <library.h>
+
+/**
+ * Constructor function for SASL mechansims.
+ *
+ * @param name name of the requested SASL mechanism
+ * @param client client identity, NULL to act as server
+ * @return SASL mechanism, NULL on failure
+ */
+typedef sasl_mechanism_t*(*sasl_mechanism_constructor_t)(char *name,
+ identification_t *client);
+
+/**
+ * Generic interface for SASL mechanisms.
+ */
+struct sasl_mechanism_t {
+
+ /**
+ * Get the name of this SASL mechanism.
+ *
+ * @return name of SASL mechanism
+ */
+ char* (*get_name)(sasl_mechanism_t *this);
+
+ /**
+ * Build a SASL message to send to remote host.
+ *
+ * A message is returned if the return value is NEED_MORE or SUCCESS. A
+ * client MUST NOT return SUCCESS in build(), as the final message
+ * is always from server to client (even if it is an empty result message).
+ *
+ * @param message receives allocated SASL message, to free
+ * @return
+ * - FAILED if mechanism failed
+ * - NEED_MORE if additional exchanges required
+ * - INVALID_STATE if currently nothing to build
+ * - SUCCESS if mechanism authenticated successfully
+ */
+ status_t (*build)(sasl_mechanism_t *this, chunk_t *message);
+
+ /**
+ * Process a SASL message received from remote host.
+ *
+ * If a server returns SUCCESS during process(), an empty result message
+ * is sent to complete the SASL exchange.
+ *
+ * @param message received SASL message to process
+ * @return
+ * - FAILED if mechanism failed
+ * - NEED_MORE if additional exchanges required
+ * - SUCCESS if mechanism authenticated successfully
+ */
+ status_t (*process)(sasl_mechanism_t *this, chunk_t message);
+
+ /**
+ * Destroy a sasl_mechanism_t.
+ */
+ void (*destroy)(sasl_mechanism_t *this);
+};
+
+/**
+ * Create a sasl_mechanism instance.
+ *
+ * @param name name of SASL mechanism to create
+ * @param client client identity, NULL to act as server
+ * @return SASL mechanism instance, NULL if not found
+ */
+sasl_mechanism_t *sasl_mechanism_create(char *name, identification_t *client);
+
+/**
+ * Create an enumerator over supported SASL mechanism names.
+ *
+ * @param server TRUE for server instance, FALSE for client
+ * @return enumerator over char*
+ */
+enumerator_t* sasl_mechanism_create_enumerator(bool server);
+
+#endif /** SASL_MECHANISM_H_ @}*/
diff --git a/src/libpttls/sasl/sasl_plain/sasl_plain.c b/src/libpttls/sasl/sasl_plain/sasl_plain.c
new file mode 100644
index 000000000..e8d6dc80b
--- /dev/null
+++ b/src/libpttls/sasl/sasl_plain/sasl_plain.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "sasl_plain.h"
+
+#include <utils/debug.h>
+
+typedef struct private_sasl_plain_t private_sasl_plain_t;
+
+/**
+ * Private data of an sasl_plain_t object.
+ */
+struct private_sasl_plain_t {
+
+ /**
+ * Public sasl_plain_t interface.
+ */
+ sasl_plain_t public;
+
+ /**
+ * Client identity
+ */
+ identification_t *client;
+};
+
+METHOD(sasl_mechanism_t, get_name, char*,
+ private_sasl_plain_t *this)
+{
+ return "PLAIN";
+}
+
+METHOD(sasl_mechanism_t, build_server, status_t,
+ private_sasl_plain_t *this, chunk_t *message)
+{
+ /* gets never called */
+ return FAILED;
+}
+
+METHOD(sasl_mechanism_t, process_server, status_t,
+ private_sasl_plain_t *this, chunk_t message)
+{
+ chunk_t authz, authi, password;
+ identification_t *id;
+ shared_key_t *shared;
+ u_char *pos;
+
+ pos = memchr(message.ptr, 0, message.len);
+ if (!pos)
+ {
+ DBG1(DBG_CFG, "invalid authz encoding");
+ return FAILED;
+ }
+ authz = chunk_create(message.ptr, pos - message.ptr);
+ message = chunk_skip(message, authz.len + 1);
+ pos = memchr(message.ptr, 0, message.len);
+ if (!pos)
+ {
+ DBG1(DBG_CFG, "invalid authi encoding");
+ return FAILED;
+ }
+ authi = chunk_create(message.ptr, pos - message.ptr);
+ password = chunk_skip(message, authi.len + 1);
+ id = identification_create_from_data(authi);
+ shared = lib->credmgr->get_shared(lib->credmgr, SHARED_EAP, id, NULL);
+ if (!shared)
+ {
+ DBG1(DBG_CFG, "no shared secret found for '%Y'", id);
+ id->destroy(id);
+ return FAILED;
+ }
+ if (!chunk_equals(shared->get_key(shared), password))
+ {
+ DBG1(DBG_CFG, "shared secret for '%Y' does not match", id);
+ id->destroy(id);
+ shared->destroy(shared);
+ return FAILED;
+ }
+ id->destroy(id);
+ shared->destroy(shared);
+ return SUCCESS;
+}
+
+METHOD(sasl_mechanism_t, build_client, status_t,
+ private_sasl_plain_t *this, chunk_t *message)
+{
+ shared_key_t *shared;
+ chunk_t password;
+ char buf[256];
+ ssize_t len;
+
+ /* we currently use the EAP type of shared secret */
+ shared = lib->credmgr->get_shared(lib->credmgr, SHARED_EAP,
+ this->client, NULL);
+ if (!shared)
+ {
+ DBG1(DBG_CFG, "no shared secret found for %Y", this->client);
+ return FAILED;
+ }
+
+ password = shared->get_key(shared);
+ len = snprintf(buf, sizeof(buf), "%s%c%Y%c%.*s",
+ "", 0, this->client, 0,
+ (int)password.len, password.ptr);
+ if (len < 0 || len >= sizeof(buf))
+ {
+ return FAILED;
+ }
+ *message = chunk_clone(chunk_create(buf, len));
+ return NEED_MORE;
+}
+
+METHOD(sasl_mechanism_t, process_client, status_t,
+ private_sasl_plain_t *this, chunk_t message)
+{
+ /* if the server sends a result, authentication successful */
+ return SUCCESS;
+}
+
+METHOD(sasl_mechanism_t, destroy, void,
+ private_sasl_plain_t *this)
+{
+ DESTROY_IF(this->client);
+ free(this);
+}
+
+/**
+ * See header
+ */
+sasl_plain_t *sasl_plain_create(char *name, identification_t *client)
+{
+ private_sasl_plain_t *this;
+
+ if (!streq(get_name(NULL), name))
+ {
+ return NULL;
+ }
+
+ INIT(this,
+ .public = {
+ .sasl = {
+ .get_name = _get_name,
+ .destroy = _destroy,
+ },
+ },
+ );
+
+ if (client)
+ {
+ this->public.sasl.build = _build_client;
+ this->public.sasl.process = _process_client;
+ this->client = client->clone(client);
+ }
+ else
+ {
+ this->public.sasl.build = _build_server;
+ this->public.sasl.process = _process_server;
+ }
+ return &this->public;
+}
diff --git a/src/libpttls/sasl/sasl_plain/sasl_plain.h b/src/libpttls/sasl/sasl_plain/sasl_plain.h
new file mode 100644
index 000000000..08b7fc76f
--- /dev/null
+++ b/src/libpttls/sasl/sasl_plain/sasl_plain.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup sasl_plain sasl_plain
+ * @{ @ingroup sasl
+ */
+
+#ifndef SASL_PLAIN_H_
+#define SASL_PLAIN_H_
+
+#include <sasl/sasl_mechanism.h>
+
+typedef struct sasl_plain_t sasl_plain_t;
+
+/**
+ * SASL Mechanism implementing PLAIN.
+ */
+struct sasl_plain_t {
+
+ /**
+ * Implements sasl_mechanism_t
+ */
+ sasl_mechanism_t sasl;
+};
+
+/**
+ * Create a sasl_plain instance.
+ *
+ * @param name name of mechanism, must be "PLAIN"
+ * @param client client identity, NULL to act as server
+ * @return mechanism implementing PLAIN, NULL on error
+ */
+sasl_plain_t *sasl_plain_create(char *name, identification_t *client);
+
+#endif /** SASL_PLAIN_H_ @}*/