diff options
-rw-r--r-- | src/charon/plugins/eap_tls/eap_tls.c | 66 | ||||
-rw-r--r-- | src/charon/plugins/eap_tls/tls/tls.c | 58 | ||||
-rw-r--r-- | src/charon/plugins/eap_tls/tls/tls.h | 45 |
3 files changed, 158 insertions, 11 deletions
diff --git a/src/charon/plugins/eap_tls/eap_tls.c b/src/charon/plugins/eap_tls/eap_tls.c index b24805273..2e87d8f1c 100644 --- a/src/charon/plugins/eap_tls/eap_tls.c +++ b/src/charon/plugins/eap_tls/eap_tls.c @@ -15,6 +15,8 @@ #include "eap_tls.h" +#include "tls/tls.h" + #include <daemon.h> #include <library.h> @@ -46,6 +48,11 @@ struct private_eap_tls_t { bool is_server; /** + * TLS layers + */ + tls_t *tls; + + /** * Allocated input buffer */ chunk_t input; @@ -212,28 +219,62 @@ static eap_payload_t *read_buf(private_eap_tls_t *this, u_int8_t identifier) */ static status_t process_buf(private_eap_tls_t *this) { - tls_record_t *record; - chunk_t input; + tls_record_t *in, out; + chunk_t data; u_int16_t len; + status_t status; - input = this->input; - while (input.len > sizeof(tls_record_t)) + /* pass input buffer to upper layer, record for record */ + data = this->input; + while (data.len > sizeof(tls_record_t)) { - record = (tls_record_t*)input.ptr; - len = untoh16(&record->length); - if (len > input.len) + in = (tls_record_t*)data.ptr; + len = untoh16(&in->length); + if (len > data.len - sizeof(tls_record_t)) { DBG1(DBG_IKE, "TLS record length invalid"); break; } - /* TODO: pass record to next layer */ - input = chunk_skip(input, len); + status = this->tls->process(this->tls, in->type, + chunk_create(in->data, len)); + if (status != NEED_MORE) + { + return status; + } + data = chunk_skip(data, len + sizeof(tls_record_t)); } chunk_free(&this->input); this->inpos = 0; - /* TODO: read records from next layer */ - return NEED_MORE; + /* read in records from upper layer, append to output buffer */ + chunk_free(&this->output); + while (TRUE) + { + tls_content_type_t type; + chunk_t header = chunk_from_thing(out); + + status = this->tls->build(this->tls, &type, &data); + switch (status) + { + case INVALID_STATE: + /* invalid state means we need more input from peer first */ + return NEED_MORE; + case NEED_MORE: + case SUCCESS: + break; + case FAILED: + default: + return FAILED; + } + out.type = type; + htoun16(&out.version, TLS_1_2); + htoun16(&out.length, data.len); + this->output = chunk_cat("mcm", this->output, header, data); + if (status == SUCCESS) + { + return SUCCESS; + } + } } METHOD(eap_method_t, process, status_t, @@ -309,6 +350,8 @@ METHOD(eap_method_t, destroy, void, free(this->input.ptr); free(this->output.ptr); + this->tls->destroy(this->tls); + free(this); } @@ -332,6 +375,7 @@ static eap_tls_t *eap_tls_create(identification_t *server, .peer = peer->clone(peer), .server = server->clone(server), .is_server = is_server, + .tls = tls_create(is_server), ); return &this->public; diff --git a/src/charon/plugins/eap_tls/tls/tls.c b/src/charon/plugins/eap_tls/tls/tls.c index 930ae78b6..ddb0c81f0 100644 --- a/src/charon/plugins/eap_tls/tls/tls.c +++ b/src/charon/plugins/eap_tls/tls/tls.c @@ -15,6 +15,8 @@ #include "tls.h" +#include <daemon.h> + ENUM(tls_version_names, SSL_2_0, TLS_1_2, "SSLv2", "SSLv3", @@ -44,3 +46,59 @@ ENUM_NEXT(tls_handshake_type_names, TLS_CERTIFICATE, TLS_CLIENT_KEY_EXCHANGE, TL ENUM_NEXT(tls_handshake_type_names, TLS_FINISHED, TLS_FINISHED, TLS_CLIENT_KEY_EXCHANGE, "Finished"); ENUM_END(tls_handshake_type_names, TLS_FINISHED); + + +typedef struct private_tls_t private_tls_t; + +/** + * Private data of an tls_protection_t object. + */ +struct private_tls_t { + + /** + * Public tls_t interface. + */ + tls_t public; + + /** + * Role this TLS stack acts as. + */ + bool is_server; +}; + +METHOD(tls_t, process, status_t, + private_tls_t *this, tls_content_type_t type, chunk_t data) +{ + return NEED_MORE; +} + +METHOD(tls_t, build, status_t, + private_tls_t *this, tls_content_type_t *type, chunk_t *data) +{ + return INVALID_STATE; +} + +METHOD(tls_t, destroy, void, + private_tls_t *this) +{ + free(this); +} + +/** + * See header + */ +tls_t *tls_create(bool is_server) +{ + private_tls_t *this; + + INIT(this, + .public = { + .process = _process, + .build = _build, + .destroy = _destroy, + }, + .is_server = is_server, + ); + + return &this->public; +} diff --git a/src/charon/plugins/eap_tls/tls/tls.h b/src/charon/plugins/eap_tls/tls/tls.h index d7a331756..2e3e04691 100644 --- a/src/charon/plugins/eap_tls/tls/tls.h +++ b/src/charon/plugins/eap_tls/tls/tls.h @@ -28,6 +28,7 @@ typedef enum tls_version_t tls_version_t; typedef enum tls_content_type_t tls_content_type_t; typedef enum tls_handshake_type_t tls_handshake_type_t; typedef enum tls_cipher_suite_t tls_cipher_suite_t; +typedef struct tls_t tls_t; #include <library.h> @@ -123,4 +124,48 @@ enum tls_cipher_suite_t { TLS_DH_ANON_WITH_AES_256_CBC_SHA256 = 0x6D, }; +/** + * A bottom-up driven TLS stack, suitable for EAP implementations. + */ +struct tls_t { + + /** + * Process a TLS record, pass it to upper layers. + * + * @param type type of the TLS record to process + * @param data associated TLS record data + * @return + * - SUCCESS if TLS negotiation complete + * - FAILED if TLS handshake failed + * - NEED_MORE if more invocations to process/build needed + */ + status_t (*process)(tls_t *this, tls_content_type_t type, chunk_t data); + + /** + * Query upper layer for TLS record, build protected record. + * + * @param type type of the built TLS record + * @param data allocated data of the built TLS record + * @return + * - SUCCESS if TLS negotiation complete + * - FAILED if TLS handshake failed + * - NEED_MORE if upper layers have more records to send + * - INVALID_STATE if more input records required + */ + status_t (*build)(tls_t *this, tls_content_type_t *type, chunk_t *data); + + /** + * Destroy a tls_t. + */ + void (*destroy)(tls_t *this); +}; + +/** + * Create a tls instance. + * + * @param is_server TRUE to act as server, FALSE for client + * @return TLS stack + */ +tls_t *tls_create(bool is_server); + #endif /** TLS_H_ @}*/ |