diff options
author | Martin Willi <martin@revosec.ch> | 2013-03-07 14:10:50 +0100 |
---|---|---|
committer | Martin Willi <martin@revosec.ch> | 2013-03-07 14:10:50 +0100 |
commit | 7d70a14779b2dc897bc46559e6475a9bd01b2be6 (patch) | |
tree | 746bdedefd60fae4e6b010e5415919cff4140e32 /src/libpttls | |
parent | 9d9042d6d95b0ecb292d77e7d8350fcd28e1aa27 (diff) | |
parent | 1db6bf2f3f8fe0240a63dbd7c79323140daa622e (diff) | |
download | strongswan-7d70a14779b2dc897bc46559e6475a9bd01b2be6.tar.bz2 strongswan-7d70a14779b2dc897bc46559e6475a9bd01b2be6.tar.xz |
Merge branch 'pt-tls'
Diffstat (limited to 'src/libpttls')
-rw-r--r-- | src/libpttls/Makefile.am | 4 | ||||
-rw-r--r-- | src/libpttls/pt_tls.h | 28 | ||||
-rw-r--r-- | src/libpttls/pt_tls_client.c | 225 | ||||
-rw-r--r-- | src/libpttls/pt_tls_client.h | 10 | ||||
-rw-r--r-- | src/libpttls/pt_tls_dispatcher.c | 10 | ||||
-rw-r--r-- | src/libpttls/pt_tls_dispatcher.h | 5 | ||||
-rw-r--r-- | src/libpttls/pt_tls_server.c | 281 | ||||
-rw-r--r-- | src/libpttls/pt_tls_server.h | 5 | ||||
-rw-r--r-- | src/libpttls/sasl/sasl_mechanism.c | 92 | ||||
-rw-r--r-- | src/libpttls/sasl/sasl_mechanism.h | 103 | ||||
-rw-r--r-- | src/libpttls/sasl/sasl_plain/sasl_plain.c | 171 | ||||
-rw-r--r-- | src/libpttls/sasl/sasl_plain/sasl_plain.h | 48 |
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_ @}*/ |