diff options
author | Martin Willi <martin@revosec.ch> | 2013-02-27 13:47:08 +0100 |
---|---|---|
committer | Martin Willi <martin@revosec.ch> | 2013-02-28 16:46:07 +0100 |
commit | 66d8fd690c7817659e93bdecd79160fbfb79f7d0 (patch) | |
tree | 587d72594ccefa36e9c8ef9da9748d3fcb96009f /src/libpttls | |
parent | 3542c4f18a13d81048e3a2e1c9cc4340810f14ba (diff) | |
download | strongswan-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.c | 202 |
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; + } + } } /** |