aboutsummaryrefslogtreecommitdiffstats
path: root/src/libpttls
diff options
context:
space:
mode:
authorMartin Willi <martin@revosec.ch>2013-02-27 13:47:08 +0100
committerMartin Willi <martin@revosec.ch>2013-02-28 16:46:07 +0100
commit66d8fd690c7817659e93bdecd79160fbfb79f7d0 (patch)
tree587d72594ccefa36e9c8ef9da9748d3fcb96009f /src/libpttls
parent3542c4f18a13d81048e3a2e1c9cc4340810f14ba (diff)
downloadstrongswan-66d8fd690c7817659e93bdecd79160fbfb79f7d0.tar.bz2
strongswan-66d8fd690c7817659e93bdecd79160fbfb79f7d0.tar.xz
Implement SASL authentication in PT-TLS client
Diffstat (limited to 'src/libpttls')
-rw-r--r--src/libpttls/pt_tls_client.c202
1 files changed, 191 insertions, 11 deletions
diff --git a/src/libpttls/pt_tls_client.c b/src/libpttls/pt_tls_client.c
index 2f695ee99..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>
@@ -133,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;
+ }
+ }
}
/**