From 4333c48a1bb7209515f97394ccbf164f7c95b249 Mon Sep 17 00:00:00 2001 From: Andreas Steffen Date: Thu, 9 Dec 2010 21:33:12 +0100 Subject: [PATCH] pb_tnc_batch_t class implements parsing and building of PB-TNC batches --- src/libcharon/plugins/tnccs_20/Makefile.am | 1 + .../plugins/tnccs_20/batch/pb_tnc_batch.c | 598 ++++++++++++++++++ .../plugins/tnccs_20/batch/pb_tnc_batch.h | 143 +++++ .../tnccs_20/messages/pb_error_message.c | 228 ++++--- .../tnccs_20/messages/pb_error_message.h | 66 +- .../messages/pb_language_preference_message.c | 37 +- .../tnccs_20/messages/pb_tnc_message.c | 11 + .../tnccs_20/messages/pb_tnc_message.h | 29 + src/libcharon/plugins/tnccs_20/tnccs_20.c | 440 ++++++------- .../plugins/tnccs_20/tnccs_20_types.c | 29 - .../plugins/tnccs_20/tnccs_20_types.h | 58 +- src/libcharon/tnc/tnccs/tnccs.h | 3 + 12 files changed, 1184 insertions(+), 459 deletions(-) create mode 100644 src/libcharon/plugins/tnccs_20/batch/pb_tnc_batch.c create mode 100644 src/libcharon/plugins/tnccs_20/batch/pb_tnc_batch.h diff --git a/src/libcharon/plugins/tnccs_20/Makefile.am b/src/libcharon/plugins/tnccs_20/Makefile.am index 73c18ce41..f5dd6ba2a 100644 --- a/src/libcharon/plugins/tnccs_20/Makefile.am +++ b/src/libcharon/plugins/tnccs_20/Makefile.am @@ -14,6 +14,7 @@ endif libstrongswan_tnccs_20_la_SOURCES = \ tnccs_20_plugin.h tnccs_20_plugin.c tnccs_20.h tnccs_20.c \ tnccs_20_types.h tnccs_20_types.c \ + batch/pb_tnc_batch.h batch/pb_tnc_batch.c \ messages/pb_tnc_message.h messages/pb_tnc_message.c \ messages/pb_pa_message.h messages/pb_pa_message.c \ messages/pb_assessment_result_message.h messages/pb_assessment_result_message.c \ diff --git a/src/libcharon/plugins/tnccs_20/batch/pb_tnc_batch.c b/src/libcharon/plugins/tnccs_20/batch/pb_tnc_batch.c new file mode 100644 index 000000000..fbe5cf661 --- /dev/null +++ b/src/libcharon/plugins/tnccs_20/batch/pb_tnc_batch.c @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2010 Sansar Choinyanbuu + * Copyright (C) 2010 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * 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 . + * + * 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 "tnccs_20_types.h" +#include "pb_tnc_batch.h" +#include "messages/pb_error_message.h" + +#include +#include +#include +#include +#include + +ENUM(pb_tnc_state_names, PB_STATE_INIT, PB_STATE_END, + "Init", + "Server Working", + "Client Working", + "Decided", + "End" +); + +ENUM(pb_tnc_batch_type_names, PB_BATCH_CDATA, PB_BATCH_CLOSE, + "CDATA", + "SDATA", + "RESULT", + "CRETRY", + "SRETRY", + "CLOSE" +); + +typedef struct private_pb_tnc_batch_t private_pb_tnc_batch_t; + +/** + * PB-Batch Header (see section 4.1 of RFC 5793) + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Version |D| Reserved | B-Type| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Batch Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +#define PB_TNC_BATCH_FLAG_NONE 0x00 +#define PB_TNC_BATCH_FLAG_D (1<<7) +#define PB_TNC_BATCH_HEADER_SIZE 8 + +/** + * PB-TNC Message (see section 4.2 of RFC 5793) + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Flags | PB-TNC Vendor ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | PB-TNC Message Type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | PB-TNC Message Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | PB-TNC Message Value (Variable Length) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +#define PB_TNC_FLAG_NONE 0x00 +#define PB_TNC_FLAG_NOSKIP (1<<7) +#define PB_TNC_HEADER_SIZE 12 + +#define PB_TNC_RESERVED_MSG_TYPE 0xffffffff + +/** + * Private data of a pb_tnc_batch_t object. + * + */ +struct private_pb_tnc_batch_t { + /** + * Public pb_pa_message_t interface. + */ + pb_tnc_batch_t public; + + /** + * TNCC if TRUE, TNCS if FALSE + */ + bool is_server; + + /** + * PB-TNC Batch type + */ + pb_tnc_batch_type_t type; + + /** + * linked list of PB-TNC messages + */ + linked_list_t *messages; + + /** + * linked list of PB-TNC error messages + */ + linked_list_t *errors; + + /** + * Encoded message + */ + chunk_t encoding; + + /** + * Offset into encoding (used for error reporting) + */ + size_t offset; +}; + +METHOD(pb_tnc_batch_t, get_type, pb_tnc_batch_type_t, + private_pb_tnc_batch_t *this) +{ + return this->type; +} + +METHOD(pb_tnc_batch_t, get_encoding, chunk_t, + private_pb_tnc_batch_t *this) +{ + return this->encoding; +} + +METHOD(pb_tnc_batch_t, add_message, void, + private_pb_tnc_batch_t *this, pb_tnc_message_t* msg) +{ + DBG2(DBG_TNC, " adding %N Message", pb_tnc_msg_type_names, + msg->get_type(msg)); + this->messages->insert_last(this->messages, msg); +} + +METHOD(pb_tnc_batch_t, build, void, + private_pb_tnc_batch_t *this) +{ + u_int32_t batch_len, msg_len; + u_int8_t flags = PB_TNC_FLAG_NONE; + chunk_t msg_value; + enumerator_t *enumerator; + pb_tnc_msg_type_t msg_type; + pb_tnc_message_t *msg; + tls_writer_t *writer; + + /* compute total PB-TNC batch size by summing over all messages */ + batch_len = PB_TNC_BATCH_HEADER_SIZE; + enumerator = this->messages->create_enumerator(this->messages); + while (enumerator->enumerate(enumerator, &msg)) + { + msg->build(msg); + msg_value = msg->get_encoding(msg); + batch_len += PB_TNC_HEADER_SIZE + msg_value.len; + } + enumerator->destroy(enumerator); + + /* build PB-TNC batch header */ + writer = tls_writer_create(batch_len); + writer->write_uint8 (writer, PB_TNC_VERSION); + writer->write_uint8 (writer, this->is_server ? + PB_TNC_BATCH_FLAG_D : PB_TNC_BATCH_FLAG_NONE); + writer->write_uint16(writer, this->type); + writer->write_uint32(writer, batch_len); + + /* build PB-TNC messages */ + enumerator = this->messages->create_enumerator(this->messages); + while (enumerator->enumerate(enumerator, &msg)) + { + /* build PB-TNC message */ + msg_value = msg->get_encoding(msg); + msg_len = PB_TNC_HEADER_SIZE + msg_value.len; + msg_type = msg->get_type(msg); + switch (msg_type) + { + case PB_MSG_PA: + case PB_MSG_ASSESSMENT_RESULT: + case PB_MSG_ERROR: + flags |= PB_TNC_FLAG_NOSKIP; + break; + case PB_MSG_EXPERIMENTAL: + case PB_MSG_ACCESS_RECOMMENDATION: + case PB_MSG_REMEDIATION_PARAMETERS: + case PB_MSG_LANGUAGE_PREFERENCE: + case PB_MSG_REASON_STRING: + break; + } + writer->write_uint8 (writer, flags); + writer->write_uint24(writer, IETF_VENDOR_ID); + writer->write_uint32(writer, msg_type); + writer->write_uint32(writer, msg_len); + writer->write_data (writer, msg_value); + } + enumerator->destroy(enumerator); + + this->encoding = chunk_clone(writer->get_buf(writer)); + writer->destroy(writer); +} + +static status_t process_batch_header(private_pb_tnc_batch_t *this, + pb_tnc_state_t *state) +{ + tls_reader_t *reader; + pb_tnc_message_t *msg; + pb_error_message_t *err_msg; + u_int8_t version, flags, reserved, type; + u_int32_t batch_len; + bool directionality, unexpected_batch_type = FALSE; + + if (this->encoding.len < PB_TNC_BATCH_HEADER_SIZE) + { + DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC batch header", + this->encoding.len); + msg = pb_error_message_create(TRUE, IETF_VENDOR_ID, + PB_ERROR_INVALID_PARAMETER); + err_msg = (pb_error_message_t*)msg; + err_msg->set_offset(err_msg, 0); + goto fatal; + } + + reader = tls_reader_create(this->encoding); + reader->read_uint8 (reader, &version); + reader->read_uint8 (reader, &flags); + reader->read_uint8 (reader, &reserved); + reader->read_uint8 (reader, &type); + reader->read_uint32(reader, &batch_len); + reader->destroy(reader); + + /* Version */ + if (version != PB_TNC_VERSION) + { + DBG1(DBG_TNC, "Unsupported TNCCS Batch Version 0x%01x", version); + msg = pb_error_message_create(TRUE, IETF_VENDOR_ID, + PB_ERROR_VERSION_NOT_SUPPORTED); + err_msg = (pb_error_message_t*)msg; + err_msg->set_bad_version(err_msg, version); + goto fatal; + } + + /* Directionality */ + directionality = (flags & PB_TNC_BATCH_FLAG_D) != PB_TNC_BATCH_FLAG_NONE; + if (directionality == this->is_server) + { + DBG1(DBG_TNC, "Wrong Directionality: Batch is from a PB %s", + directionality ? "Server" : "Client"); + msg = pb_error_message_create(TRUE, IETF_VENDOR_ID, + PB_ERROR_INVALID_PARAMETER); + err_msg = (pb_error_message_t*)msg; + err_msg->set_offset(err_msg, 1); + goto fatal; + } + + /* Batch Type */ + this->type = type & 0x0F; + if (this->type > PB_BATCH_ROOF) + { + DBG1(DBG_TNC, "Unknown PB-TNC Batch Type: %d", this->type); + msg = pb_error_message_create(TRUE, IETF_VENDOR_ID, + PB_ERROR_INVALID_PARAMETER); + err_msg = (pb_error_message_t*)msg; + err_msg->set_offset(err_msg, 3); + goto fatal; + } + switch (*state) + { + case PB_STATE_INIT: + if (this->is_server && this->type == PB_BATCH_CDATA) + { + *state = PB_STATE_SERVER_WORKING; + break; + } + if (!this->is_server && this->type == PB_BATCH_SDATA) + { + *state = PB_STATE_CLIENT_WORKING; + break; + } + if (this->type == PB_BATCH_CLOSE) + { + *state = PB_STATE_END; + break; + } + unexpected_batch_type = TRUE; + break; + case PB_STATE_SERVER_WORKING: + if (!this->is_server && this->type == PB_BATCH_SDATA) + { + *state = PB_STATE_CLIENT_WORKING; + break; + } + if (!this->is_server && this->type == PB_BATCH_RESULT) + { + *state = PB_STATE_DECIDED; + break; + } + if ((this->is_server && this->type == PB_BATCH_CRETRY) || + (!this->is_server && this->type == PB_BATCH_SRETRY)) + { + break; + } + if (this->type == PB_BATCH_CLOSE) + { + *state = PB_STATE_END; + break; + } + unexpected_batch_type = TRUE; + break; + case PB_STATE_CLIENT_WORKING: + if (this->is_server && this->type == PB_BATCH_CDATA) + { + *state = PB_STATE_SERVER_WORKING; + break; + } + if (this->is_server && this->type == PB_BATCH_CRETRY) + { + break; + } + if (this->type == PB_BATCH_CLOSE) + { + *state = PB_STATE_END; + break; + } + unexpected_batch_type = TRUE; + break; + case PB_STATE_DECIDED: + if ((this->is_server && this->type == PB_BATCH_CRETRY) || + (!this->is_server && this->type == PB_BATCH_SRETRY)) + { + *state = PB_STATE_SERVER_WORKING; + break; + } + if (this->type == PB_BATCH_CLOSE) + { + *state = PB_STATE_END; + break; + } + unexpected_batch_type = TRUE; + break; + case PB_STATE_END: + if (this->type == PB_BATCH_CLOSE) + { + break; + } + unexpected_batch_type = TRUE; + } + if (unexpected_batch_type) + { + DBG1(DBG_TNC, "Unexpected PB-TNC Batch Type: %N", + pb_tnc_batch_type_names, this->type); + msg = pb_error_message_create(TRUE, IETF_VENDOR_ID, + PB_ERROR_UNEXPECTED_BATCH_TYPE); + goto fatal; + } + + /* Batch Length */ + if (this->encoding.len != batch_len) + { + DBG1(DBG_TNC, "%u bytes of data is not equal to batch length of %u bytes", + this->encoding.len, batch_len); + msg = pb_error_message_create(TRUE, IETF_VENDOR_ID, + PB_ERROR_INVALID_PARAMETER); + err_msg = (pb_error_message_t*)msg; + err_msg->set_offset(err_msg, 4); + goto fatal; + } + + this->offset = PB_TNC_BATCH_HEADER_SIZE; + return SUCCESS; + +fatal: + this->errors->insert_last(this->errors, msg); + return FAILED; +} + +static status_t process_tnc_message(private_pb_tnc_batch_t *this) +{ + tls_reader_t *reader; + pb_tnc_message_t *pb_tnc_msg, *msg; + pb_error_message_t *err_msg; + u_int8_t flags; + u_int32_t vendor_id, msg_type, msg_len; + chunk_t data, msg_value; + status_t status; + + data = chunk_skip(this->encoding, this->offset); + + if (data.len < PB_TNC_HEADER_SIZE) + { + DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC message header", + data.len); + msg = pb_error_message_create(TRUE, IETF_VENDOR_ID, + PB_ERROR_INVALID_PARAMETER); + err_msg = (pb_error_message_t*)msg; + err_msg->set_offset(err_msg, this->offset); + goto fatal; + } + + reader = tls_reader_create(data); + reader->read_uint8 (reader, &flags); + reader->read_uint24(reader, &vendor_id); + reader->read_uint32(reader, &msg_type); + reader->read_uint32(reader, &msg_len); + reader->destroy(reader); + + if (msg_len < PB_TNC_HEADER_SIZE) + { + DBG1(DBG_TNC, "%u bytes too small for PB-TNC message length", msg_len); + msg = pb_error_message_create(TRUE, IETF_VENDOR_ID, + PB_ERROR_INVALID_PARAMETER); + err_msg = (pb_error_message_t*)msg; + err_msg->set_offset(err_msg, this->offset + 8); + goto fatal; + } + + if (msg_len > data.len) + { + DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC message", data.len); + msg = pb_error_message_create(TRUE, IETF_VENDOR_ID, + PB_ERROR_INVALID_PARAMETER); + err_msg = (pb_error_message_t*)msg; + err_msg->set_offset(err_msg, this->offset + 8); + goto fatal; + } + + if (vendor_id == RESERVED_VENDOR_ID) + { + DBG1(DBG_TNC, "Vendor ID 0x%06x is reserved", RESERVED_VENDOR_ID); + msg = pb_error_message_create(TRUE, IETF_VENDOR_ID, + PB_ERROR_INVALID_PARAMETER); + err_msg = (pb_error_message_t*)msg; + err_msg->set_offset(err_msg, this->offset + 1); + goto fatal; + + } + + if (msg_type == PB_TNC_RESERVED_MSG_TYPE) + { + DBG1(DBG_TNC, "PB-TNC Message Type 0x%08x is reserved", + PB_TNC_RESERVED_MSG_TYPE); + msg = pb_error_message_create(TRUE, IETF_VENDOR_ID, + PB_ERROR_INVALID_PARAMETER); + err_msg = (pb_error_message_t*)msg; + err_msg->set_offset(err_msg, this->offset + 4); + goto fatal; + } + + if (vendor_id != IETF_VENDOR_ID || msg_type > PB_MSG_ROOF) + { + if (flags & PB_TNC_FLAG_NOSKIP) + { + DBG1(DBG_TNC, "cannot process PB-TNC message with Vendor ID 0x%06x " + " and type 0x%08x", vendor_id, msg_type); + msg = pb_error_message_create(TRUE, IETF_VENDOR_ID, + PB_ERROR_UNSUPPORTED_MANDATORY_MESSAGE); + err_msg = (pb_error_message_t*)msg; + err_msg->set_offset(err_msg, this->offset); + goto fatal; + } + else + { + DBG1(DBG_TNC, "ignore PB-TNC message with Vendor ID 0x%06x " + " and type 0x%08x", vendor_id, msg_type); + this->offset += msg_len; + return INVALID_STATE; + } + } + + DBG2(DBG_TNC, "processing %N Message (%u bytes)", pb_tnc_msg_type_names, + msg_type, msg_len); + data.len = msg_len; + DBG3(DBG_TNC, "%B", &data); + msg_value = chunk_skip(data, PB_TNC_HEADER_SIZE); + pb_tnc_msg = pb_tnc_message_create(msg_type, msg_value); + + status = pb_tnc_msg->process(pb_tnc_msg); + if (status == FAILED) + { + pb_tnc_msg->destroy(pb_tnc_msg); + return FAILED; + } + this->messages->insert_last(this->messages, pb_tnc_msg); + this->offset += msg_len; + return SUCCESS; + +fatal: + this->errors->insert_last(this->errors, msg); + return FAILED; +} + +METHOD(pb_tnc_batch_t, process, status_t, + private_pb_tnc_batch_t *this, pb_tnc_state_t *state) +{ + status_t status; + + status = process_batch_header(this, state); + if (status == FAILED) + { + return FAILED; + } + + while (this->offset < this->encoding.len) + { + status = process_tnc_message(this); + if (status == FAILED) + { + return FAILED; + } + } + return SUCCESS; +} + +METHOD(pb_tnc_batch_t, create_msg_enumerator, enumerator_t*, + private_pb_tnc_batch_t *this) +{ + return this->messages->create_enumerator(this->messages); +} + +METHOD(pb_tnc_batch_t, create_error_enumerator, enumerator_t*, + private_pb_tnc_batch_t *this) +{ + return this->errors->create_enumerator(this->errors); +} + +METHOD(pb_tnc_batch_t, destroy, void, + private_pb_tnc_batch_t *this) +{ + this->messages->destroy_offset(this->messages, + offsetof(pb_tnc_message_t, destroy)); + this->errors->destroy_offset(this->errors, + offsetof(pb_tnc_message_t, destroy)); + free(this->encoding.ptr); + free(this); +} + +/** + * See header + */ +pb_tnc_batch_t* pb_tnc_batch_create(bool is_server, pb_tnc_batch_type_t type) +{ + private_pb_tnc_batch_t *this; + + INIT(this, + .public = { + .get_type = _get_type, + .get_encoding = _get_encoding, + .add_message = _add_message, + .build = _build, + .process = _process, + .create_msg_enumerator = _create_msg_enumerator, + .create_error_enumerator = _create_error_enumerator, + .destroy = _destroy, + }, + .is_server = is_server, + .type = type, + .messages = linked_list_create(), + .errors = linked_list_create(), + ); + + DBG2(DBG_TNC, "creating PB-TNC %N Batch", pb_tnc_batch_type_names, type); + + return &this->public; +} + +/** + * See header + */ +pb_tnc_batch_t* pb_tnc_batch_create_from_data(bool is_server, chunk_t data) +{ + private_pb_tnc_batch_t *this; + + INIT(this, + .public = { + .get_type = _get_type, + .get_encoding = _get_encoding, + .add_message = _add_message, + .build = _build, + .process = _process, + .create_msg_enumerator = _create_msg_enumerator, + .create_error_enumerator = _create_error_enumerator, + .destroy = _destroy, + }, + .is_server = is_server, + .messages = linked_list_create(), + .errors = linked_list_create(), + .encoding = chunk_clone(data), + ); + + return &this->public; +} + diff --git a/src/libcharon/plugins/tnccs_20/batch/pb_tnc_batch.h b/src/libcharon/plugins/tnccs_20/batch/pb_tnc_batch.h new file mode 100644 index 000000000..7bc4639d4 --- /dev/null +++ b/src/libcharon/plugins/tnccs_20/batch/pb_tnc_batch.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2010 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * 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 . + * + * 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 pb_tnc_batch pb_tnc_batch + * @{ @ingroup tnccs_20 + */ + +#ifndef PB_TNC_BATCH_H_ +#define PB_TNC_BATCH_H_ + +#include "messages/pb_tnc_message.h" + +#include + +typedef enum pb_tnc_state_t pb_tnc_state_t; + +/** + * PB-TNC States (State machine) as defined in section 3.2 of RFC 5793 + */ +enum pb_tnc_state_t { + PB_STATE_INIT, + PB_STATE_SERVER_WORKING, + PB_STATE_CLIENT_WORKING, + PB_STATE_DECIDED, + PB_STATE_END, +}; + +/** + * enum name for pb_tnc_state_t. + */ +extern enum_name_t *pb_tnc_state_names; + +typedef enum pb_tnc_batch_type_t pb_tnc_batch_type_t; + +/** + * PB-TNC Batch Types as defined in section 4.1 of RFC 5793 + */ +enum pb_tnc_batch_type_t { + PB_BATCH_CDATA = 1, + PB_BATCH_SDATA = 2, + PB_BATCH_RESULT = 3, + PB_BATCH_CRETRY = 4, + PB_BATCH_SRETRY = 5, + PB_BATCH_CLOSE = 6, + PB_BATCH_ROOF = 6 +}; + +typedef struct pb_tnc_batch_t pb_tnc_batch_t; + +/** + * enum name for pb_tnc_batch_type_t. + */ +extern enum_name_t *pb_tnc_batch_type_names; + +/** + * Interface for all PB-TNC Batch Types. + */ +struct pb_tnc_batch_t { + + /** + * Get the PB-TNC Message Type + * + * @return PB-TNC batch type + */ + pb_tnc_batch_type_t (*get_type)(pb_tnc_batch_t *this); + + /** + * Get the encoding of the PB-TNC Batch + * + * @return encoded PB-TNC batch + */ + chunk_t (*get_encoding)(pb_tnc_batch_t *this); + + /** + * Add a PB-TNC Message + * + * @param msg PB-TNC message to be addedd + */ + void (*add_message)(pb_tnc_batch_t *this, pb_tnc_message_t* msg); + + /** + * Build the PB-TNC Batch + */ + void (*build)(pb_tnc_batch_t *this); + + /** + * Process the PB-TNC Batch + * + * @param in: current state, out: new state + * @return return processing status + */ + status_t (*process)(pb_tnc_batch_t *this, pb_tnc_state_t *state); + + /** + * Enumerates over all PB-TNC Messages + * + * @return return message enumerator + */ + enumerator_t* (*create_msg_enumerator)(pb_tnc_batch_t *this); + + /** + * Enumerates over all parsing errors + * + * @return return error enumerator + */ + enumerator_t* (*create_error_enumerator)(pb_tnc_batch_t *this); + + /** + * Destroys a pb_tnc_batch_t object. + */ + void (*destroy)(pb_tnc_batch_t *this); +}; + +/** + * Create an empty PB-TNC Batch of a given type + * + * @parame is_server TRUE if server, FALSE if client + * @param type PB-TNC batch type + */ +pb_tnc_batch_t* pb_tnc_batch_create(bool is_server, pb_tnc_batch_type_t type); + +/** + * Create an unprocessed PB-TNC Batch from data + * + * @parame is_server TRUE if server, FALSE if client + * @param data encoded PB-TNC batch + */ +pb_tnc_batch_t* pb_tnc_batch_create_from_data(bool is_server, chunk_t data); + +#endif /** PB_TNC_BATCH_H_ @}*/ diff --git a/src/libcharon/plugins/tnccs_20/messages/pb_error_message.c b/src/libcharon/plugins/tnccs_20/messages/pb_error_message.c index 99d799213..43355d8b9 100644 --- a/src/libcharon/plugins/tnccs_20/messages/pb_error_message.c +++ b/src/libcharon/plugins/tnccs_20/messages/pb_error_message.c @@ -16,9 +16,19 @@ #include "pb_error_message.h" #include "../tnccs_20_types.h" +#include #include #include -#include +#include + +ENUM(pb_tnc_error_code_names, PB_ERROR_UNEXPECTED_BATCH_TYPE, + PB_ERROR_VERSION_NOT_SUPPORTED, + "Unexpected Batch Type", + "Invalid Parameter", + "Local Error", + "Unsupported Mandatory Message", + "Version Not Supported" +); typedef struct private_pb_error_message_t private_pb_error_message_t; @@ -72,14 +82,24 @@ struct private_pb_error_message_t { u_int16_t error_code; /** - * PB Error Parameters + * PB Error Offset */ - u_int32_t error_parameters; + u_int32_t error_offset; + + /** + * Bad PB-TNC version received + */ + u_int8_t bad_version; /** * Encoded message */ chunk_t encoding; + + /** + * reference count + */ + refcount_t ref; }; METHOD(pb_tnc_message_t, get_type, pb_tnc_msg_type_t, @@ -102,30 +122,27 @@ METHOD(pb_tnc_message_t, build, void, /* build message header */ writer = tls_writer_create(ERROR_HEADER_SIZE); writer->write_uint8 (writer, this->fatal ? - ERROR_FLAG_FATAL : ERROR_FLAG_NONE); + ERROR_FLAG_FATAL : ERROR_FLAG_NONE); writer->write_uint24(writer, this->vendor_id); writer->write_uint16(writer, this->error_code); writer->write_uint16(writer, ERROR_RESERVED); - /* create encoding by concatenating message header and message body */ - free(this->encoding.ptr); - - if (this->error_parameters) + /* build message body */ + if (this->error_code == PB_ERROR_VERSION_NOT_SUPPORTED) { - if (this->error_code == PB_ERROR_VERSION_NOT_SUPPORTED) - { - /* Bad version */ - writer->write_uint8(writer, this->error_parameters); - writer->write_uint8(writer, 2); /* Max version */ - writer->write_uint8(writer, 2); /* Min version */ - writer->write_uint8(writer, 0); /* Reserved */ - } - else - { - /* Error parameters */ - writer->write_uint32(writer, this->error_parameters); - } + /* Bad version */ + writer->write_uint8(writer, this->bad_version); + writer->write_uint8(writer, PB_TNC_VERSION); /* Max version */ + writer->write_uint8(writer, PB_TNC_VERSION); /* Min version */ + writer->write_uint8(writer, 0x00); /* Reserved */ } + else + { + /* Error Offset */ + writer->write_uint32(writer, this->error_offset); + } + + free(this->encoding.ptr); this->encoding = writer->get_buf(writer); this->encoding = chunk_clone(this->encoding); writer->destroy(writer); @@ -134,9 +151,8 @@ METHOD(pb_tnc_message_t, build, void, METHOD(pb_tnc_message_t, process, status_t, private_pb_error_message_t *this) { - u_int8_t flags; + u_int8_t flags, max_version, min_version; u_int16_t reserved; - size_t error_parameters_len; tls_reader_t *reader; if (this->encoding.len < ERROR_HEADER_SIZE) @@ -152,22 +168,47 @@ METHOD(pb_tnc_message_t, process, status_t, reader->read_uint24(reader, &this->vendor_id); reader->read_uint16(reader, &this->error_code); reader->read_uint16(reader, &reserved); + this->fatal = (flags & ERROR_FLAG_FATAL) != ERROR_FLAG_NONE; - /* process error parameters */ - error_parameters_len = reader->remaining(reader); - if (error_parameters_len) + if (this->vendor_id == IETF_VENDOR_ID && reader->remaining(reader) == 4) { - reader->read_uint32(reader, &this->error_parameters); + if (this->error_code == PB_ERROR_VERSION_NOT_SUPPORTED) + { + reader->read_uint8(reader, &this->bad_version); + reader->read_uint8(reader, &max_version); + reader->read_uint8(reader, &min_version); + } + else + { + reader->read_uint32(reader, &this->error_offset); + } } reader->destroy(reader); + return SUCCESS; } +METHOD(pb_tnc_message_t, get_ref, pb_tnc_message_t*, + private_pb_error_message_t *this) +{ + ref_get(&this->ref); + return &this->public.pb_interface; +} + METHOD(pb_tnc_message_t, destroy, void, private_pb_error_message_t *this) { - free(this->encoding.ptr); - free(this); + if (ref_put(&this->ref)) + { + free(this->encoding.ptr); + free(this); + } +} + +METHOD(pb_error_message_t, get_fatal_flag, bool, + private_pb_error_message_t *this) +{ + return this->fatal; } METHOD(pb_error_message_t, get_vendor_id, u_int32_t, @@ -182,22 +223,64 @@ METHOD(pb_error_message_t, get_error_code, u_int16_t, return this->error_code; } -METHOD(pb_error_message_t, get_parameters, u_int32_t, +METHOD(pb_error_message_t, get_offset, u_int32_t, private_pb_error_message_t *this) { - return this->error_parameters; + return this->error_offset; } -METHOD(pb_error_message_t, get_fatal_flag, bool, +METHOD(pb_error_message_t, set_offset, void, + private_pb_error_message_t *this, u_int32_t offset) +{ + this->error_offset = offset; +} + +METHOD(pb_error_message_t, get_bad_version, u_int8_t, private_pb_error_message_t *this) { - return this->fatal; + return this->bad_version; } -METHOD(pb_error_message_t, set_fatal_flag, void, - private_pb_error_message_t *this, bool fatal) +METHOD(pb_error_message_t, set_bad_version, void, + private_pb_error_message_t *this, u_int8_t version) { - this->fatal = fatal; + this->bad_version = version; +} + +/** + * See header + */ +pb_tnc_message_t* pb_error_message_create(bool fatal, u_int32_t vendor_id, + pb_tnc_error_code_t error_code) +{ + private_pb_error_message_t *this; + + INIT(this, + .public = { + .pb_interface = { + .get_type = _get_type, + .get_encoding = _get_encoding, + .build = _build, + .process = _process, + .get_ref = _get_ref, + .destroy = _destroy, + }, + .get_fatal_flag = _get_fatal_flag, + .get_vendor_id = _get_vendor_id, + .get_error_code = _get_error_code, + .get_offset = _get_offset, + .set_offset = _set_offset, + .get_bad_version = _get_bad_version, + .set_bad_version = _set_bad_version, + }, + .type = PB_MSG_ERROR, + .ref = 1, + .fatal = fatal, + .vendor_id = vendor_id, + .error_code = error_code, + ); + + return &this->public.pb_interface; } /** @@ -214,81 +297,22 @@ pb_tnc_message_t *pb_error_message_create_from_data(chunk_t data) .get_encoding = _get_encoding, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, + .get_fatal_flag = _get_fatal_flag, .get_vendor_id = _get_vendor_id, .get_error_code = _get_error_code, - .get_parameters = _get_parameters, - .get_fatal_flag = _get_fatal_flag, - .set_fatal_flag = _set_fatal_flag, + .get_offset = _get_offset, + .set_offset = _set_offset, + .get_bad_version = _get_bad_version, + .set_bad_version = _set_bad_version, }, .type = PB_MSG_ERROR, + .ref = 1, .encoding = chunk_clone(data), ); return &this->public.pb_interface; } -/** - * See header - */ -pb_tnc_message_t *pb_error_message_create(u_int32_t vendor_id, - pb_tnc_error_code_t error_code) -{ - private_pb_error_message_t *this; - - INIT(this, - .public = { - .pb_interface = { - .get_type = _get_type, - .get_encoding = _get_encoding, - .build = _build, - .process = _process, - .destroy = _destroy, - }, - .get_vendor_id = _get_vendor_id, - .get_error_code = _get_error_code, - .get_parameters = _get_parameters, - .get_fatal_flag = _get_fatal_flag, - .set_fatal_flag = _set_fatal_flag, - }, - .type = PB_MSG_ERROR, - .vendor_id = vendor_id, - .error_code = error_code, - ); - - return &this->public.pb_interface; -} - -/** - * See header - */ -pb_tnc_message_t *pb_error_message_create_with_parameter(u_int32_t vendor_id, - pb_tnc_error_code_t error_code, - u_int32_t error_parameters) -{ - private_pb_error_message_t *this; - - INIT(this, - .public = { - .pb_interface = { - .get_type = _get_type, - .get_encoding = _get_encoding, - .build = _build, - .process = _process, - .destroy = _destroy, - }, - .get_vendor_id = _get_vendor_id, - .get_error_code = _get_error_code, - .get_parameters = _get_parameters, - .get_fatal_flag = _get_fatal_flag, - .set_fatal_flag = _set_fatal_flag, - }, - .type = PB_MSG_ERROR, - .vendor_id = vendor_id, - .error_code = error_code, - .error_parameters = error_parameters, - ); - - return &this->public.pb_interface; -} diff --git a/src/libcharon/plugins/tnccs_20/messages/pb_error_message.h b/src/libcharon/plugins/tnccs_20/messages/pb_error_message.h index b6a7d911e..1286eab2e 100644 --- a/src/libcharon/plugins/tnccs_20/messages/pb_error_message.h +++ b/src/libcharon/plugins/tnccs_20/messages/pb_error_message.h @@ -23,8 +23,25 @@ #include "pb_tnc_message.h" +typedef enum pb_tnc_error_code_t pb_tnc_error_code_t; typedef struct pb_error_message_t pb_error_message_t; +/** + * PB-TNC Error Codes as defined in section 4.9.1 of RFC 5793 + */ +enum pb_tnc_error_code_t { + PB_ERROR_UNEXPECTED_BATCH_TYPE = 0, + PB_ERROR_INVALID_PARAMETER = 1, + PB_ERROR_LOCAL_ERROR = 2, + PB_ERROR_UNSUPPORTED_MANDATORY_MESSAGE = 3, + PB_ERROR_VERSION_NOT_SUPPORTED = 4 +}; + +/** + * enum name for pb_tnc_error_code_t. + */ +extern enum_name_t *pb_tnc_error_code_names; + /** * Classs representing the PB-Error message type. */ @@ -35,10 +52,17 @@ struct pb_error_message_t { */ pb_tnc_message_t pb_interface; + /** + * Get the fatal flag + * + * @return fatal flag + */ + bool (*get_fatal_flag)(pb_error_message_t *this); + /** * Get PB Error code Vendor ID * - * @return PB Error code Vendor ID + * @return PB Error Code Vendor ID */ u_int32_t (*get_vendor_id)(pb_error_message_t *this); @@ -50,45 +74,43 @@ struct pb_error_message_t { u_int16_t (*get_error_code)(pb_error_message_t *this); /** - * Get the PB Error Parameters + * Get the PB Error Offset * - * @return PB Error Parameter + * @return PB Error Offset */ - u_int32_t (*get_parameters)(pb_error_message_t *this); + u_int32_t (*get_offset)(pb_error_message_t *this); /** - * Get the fatal flag + * Set the PB Error Offset * - * @return fatal flag + * @param offset PB Error Offset */ - bool (*get_fatal_flag)(pb_error_message_t *this); + void (*set_offset)(pb_error_message_t *this, u_int32_t offset); /** - * Set the fatal flag + * Get the PB Bad Version * - * @param excl fatal flag + * @return PB Bad Version */ - void (*set_fatal_flag)(pb_error_message_t *this, bool is_fatal); + u_int8_t (*get_bad_version)(pb_error_message_t *this); + + /** + * Set the PB Bad Version + * + * @param version PB Bad Version + */ + void (*set_bad_version)(pb_error_message_t *this, u_int8_t version); }; /** * Create a PB-Error message from parameters * + * @param fatal fatal flag * @param vendor_id Error Code Vendor ID * @param error_code Error Code */ -pb_tnc_message_t* pb_error_message_create(u_int32_t vendor_id, - pb_tnc_error_code_t error_code); -/** - * Create a PB-Error message from parameters - * - * @param vendor_id Error Code Vendor ID - * @param error_code Error Code - * @param error_parameters Error parameters - */ -pb_tnc_message_t* pb_error_message_create_with_parameter(u_int32_t vendor_id, - pb_tnc_error_code_t error_code, - u_int32_t error_parameters); +pb_tnc_message_t* pb_error_message_create(bool fatal, u_int32_t vendor_id, + pb_tnc_error_code_t error_code); /** * Create an unprocessed PB-Error message from raw data * diff --git a/src/libcharon/plugins/tnccs_20/messages/pb_language_preference_message.c b/src/libcharon/plugins/tnccs_20/messages/pb_language_preference_message.c index 8dd4cd2e5..15ea05099 100644 --- a/src/libcharon/plugins/tnccs_20/messages/pb_language_preference_message.c +++ b/src/libcharon/plugins/tnccs_20/messages/pb_language_preference_message.c @@ -31,6 +31,9 @@ typedef struct private_pb_language_preference_message_t private_pb_language_pref * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ +#define PB_LANG_PREFIX "Accept-Language: " +#define PB_LANG_PREFIX_LEN strlen(PB_LANG_PREFIX) + /** * Private data of a private_pb_language_preference_message_t object. * @@ -72,34 +75,28 @@ METHOD(pb_tnc_message_t, get_encoding, chunk_t, METHOD(pb_tnc_message_t, build, void, private_pb_language_preference_message_t *this) { - tls_writer_t *writer; - - /* build message */ - writer = tls_writer_create(16); - writer->write_data(writer, this->language_preference); - - free(this->encoding.ptr); - this->encoding = writer->get_buf(writer); - this->encoding = chunk_clone(this->encoding); - writer->destroy(writer); + this->encoding = chunk_cat("cc", + chunk_create(PB_LANG_PREFIX, PB_LANG_PREFIX_LEN), + this->language_preference); } METHOD(pb_tnc_message_t, process, status_t, private_pb_language_preference_message_t *this) { - tls_reader_t *reader; + chunk_t lang; - if (this->encoding.len) + if (this->encoding.len >= PB_LANG_PREFIX_LEN && + memeq(this->encoding.ptr, PB_LANG_PREFIX, PB_LANG_PREFIX_LEN)) { - /* process message */ - reader = tls_reader_create(this->encoding); - reader->read_data(reader, this->encoding.len, - &this->language_preference); - this->language_preference = chunk_clone(this->language_preference); - reader->destroy(reader); + lang = chunk_skip(this->encoding, PB_LANG_PREFIX_LEN); + this->language_preference = lang.len ? chunk_clone(lang) : chunk_empty; + return SUCCESS; + } + else + { + /* TODO generate non-fatal PB-TNC error msg */ + return VERIFY_ERROR; } - - return SUCCESS; } METHOD(pb_tnc_message_t, destroy, void, diff --git a/src/libcharon/plugins/tnccs_20/messages/pb_tnc_message.c b/src/libcharon/plugins/tnccs_20/messages/pb_tnc_message.c index de0e118ad..989193342 100644 --- a/src/libcharon/plugins/tnccs_20/messages/pb_tnc_message.c +++ b/src/libcharon/plugins/tnccs_20/messages/pb_tnc_message.c @@ -23,6 +23,17 @@ #include +ENUM(pb_tnc_msg_type_names, PB_MSG_EXPERIMENTAL, PB_MSG_REASON_STRING, + "PB-Experimental", + "PB-PA", + "PB-Assessment-Result", + "PB-Access-Recommendation", + "PB-Remediation-Parameters", + "PB-Error", + "PB-Language-Preference", + "PB-Reason-String" +); + /** * See header */ diff --git a/src/libcharon/plugins/tnccs_20/messages/pb_tnc_message.h b/src/libcharon/plugins/tnccs_20/messages/pb_tnc_message.h index 246163dfc..729f22ef1 100644 --- a/src/libcharon/plugins/tnccs_20/messages/pb_tnc_message.h +++ b/src/libcharon/plugins/tnccs_20/messages/pb_tnc_message.h @@ -24,6 +24,28 @@ #include #include +typedef enum pb_tnc_msg_type_t pb_tnc_msg_type_t; + +/** + * PB-TNC Message Types as defined in section 4.3 of RFC 5793 + */ +enum pb_tnc_msg_type_t { + PB_MSG_EXPERIMENTAL = 0, + PB_MSG_PA = 1, + PB_MSG_ASSESSMENT_RESULT = 2, + PB_MSG_ACCESS_RECOMMENDATION = 3, + PB_MSG_REMEDIATION_PARAMETERS = 4, + PB_MSG_ERROR = 5, + PB_MSG_LANGUAGE_PREFERENCE = 6, + PB_MSG_REASON_STRING = 7, + PB_MSG_ROOF = 7 +}; + +/** + * enum name for pb_tnc_msg_type_t. + */ +extern enum_name_t *pb_tnc_msg_type_names; + typedef struct pb_tnc_message_t pb_tnc_message_t; /** @@ -60,6 +82,13 @@ struct pb_tnc_message_t { */ status_t (*process)(pb_tnc_message_t *this); + /** + * Get a new reference to the message. + * + * @return this, with an increased refcount + */ + pb_tnc_message_t* (*get_ref)(pb_tnc_message_t *this); + /** * Destroys a pb_tnc_message_t object. */ diff --git a/src/libcharon/plugins/tnccs_20/tnccs_20.c b/src/libcharon/plugins/tnccs_20/tnccs_20.c index cc10ab998..ebc57df6e 100644 --- a/src/libcharon/plugins/tnccs_20/tnccs_20.c +++ b/src/libcharon/plugins/tnccs_20/tnccs_20.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Sansar Choinyanbuu + * Copyright (C) 2010 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -15,8 +16,11 @@ #include "tnccs_20.h" #include "tnccs_20_types.h" +#include "batch/pb_tnc_batch.h" #include "messages/pb_tnc_message.h" #include "messages/pb_pa_message.h" +#include "messages/pb_error_message.h" +#include "messages/pb_language_preference_message.h" #include #include @@ -42,15 +46,20 @@ struct private_tnccs_20_t { */ bool is_server; + /** + * PB-TNC State Machine + */ + pb_tnc_state_t state; + /** * Connection ID assigned to this TNCCS connection */ TNC_ConnectionID connection_id; /** - * Batch being constructed + * PB-TNC batch being constructed */ - chunk_t batch; + pb_tnc_batch_t *batch; /** * Mutex locking the batch in construction @@ -68,141 +77,50 @@ struct private_tnccs_20_t { recommendations_t *recs; }; -/** - * PB-TNC Message (see section 4.2 of RFC 5793) - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Flags | PB-TNC Vendor ID | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | PB-TNC Message Type | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | PB-TNC Message Length | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | PB-TNC Message Value (Variable Length) | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - -#define PB_TNC_HEADER_SIZE 12 -#define PB_TNC_NOSKIP_FLAG (1<<7) -#define PB_TNC_IETF_VENDOR_ID 0x000000 - -static chunk_t build_pb_tnc_msg(pb_tnc_msg_type_t msg_type, chunk_t msg_value) -{ - chunk_t msg, msg_header; - tls_writer_t *writer; - size_t msg_len; - - msg_len = PB_TNC_HEADER_SIZE + msg_value.len; - writer = tls_writer_create(PB_TNC_HEADER_SIZE); - writer->write_uint8 (writer, PB_TNC_NOSKIP_FLAG); - writer->write_uint24(writer, PB_TNC_IETF_VENDOR_ID); - writer->write_uint32(writer, msg_type); - writer->write_uint32(writer, msg_len); - msg_header = writer->get_buf(writer); - msg = chunk_cat("cc", msg_header, msg_value); - writer->destroy(writer); - - DBG2(DBG_TNC, "building %N message (%u bytes)", pb_tnc_msg_type_names, - msg_type, msg_len); - DBG3(DBG_TNC,"%B", &msg); - - return msg; -} - -static status_t process_pb_tnc_msg(tls_reader_t *reader, - pb_tnc_message_t **pb_tnc_msg) -{ - u_int8_t flags; - u_int32_t vendor_id, msg_type; - size_t msg_len; - chunk_t msg, msg_value; - - msg = reader->peek(reader); - - if (msg.len < PB_TNC_HEADER_SIZE) - { - DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC message header", - msg.len); - return FAILED; - } - reader->read_uint8 (reader, &flags); - reader->read_uint24(reader, &vendor_id); - reader->read_uint32(reader, &msg_type); - reader->read_uint32(reader, &msg_len); - - DBG2(DBG_TNC, "processing PB-TNC message (%u bytes)", msg_len); - - if (msg_len < PB_TNC_HEADER_SIZE) - { - DBG1(DBG_TNC, "%u bytes too small for PB-TNC message length", msg_len); - return FAILED; - } - if (msg_len > msg.len) - { - DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC message", msg.len); - return FAILED; - } - msg.len = msg_len; - DBG3(DBG_TNC, "%B", &msg); - reader->read_data(reader, msg_len - PB_TNC_HEADER_SIZE, &msg_value); - - if (vendor_id != PB_TNC_IETF_VENDOR_ID || msg_type > PB_MSG_ROOF) - { - if (flags & PB_TNC_NOSKIP_FLAG) - { - DBG1(DBG_TNC, "cannot process PB-TNC message with Vendor ID 0x%06x " - " and type 0x%08x", vendor_id, msg); - return FAILED; - } - else - { - DBG1(DBG_TNC, "ignore PB-TNC message with Vendor ID 0x%06x " - " and type 0x%08x", vendor_id, msg); - return INVALID_STATE; - } - } - DBG2(DBG_TNC, "processing %N message (%u bytes)", pb_tnc_msg_type_names, - msg_type, msg_value.len); - *pb_tnc_msg = pb_tnc_message_create(msg_type, msg_value); - - return SUCCESS; -} - METHOD(tnccs_t, send_message, void, private_tnccs_20_t* this, TNC_IMCID imc_id, TNC_IMVID imv_id, TNC_BufferReference msg, TNC_UInt32 msg_len, TNC_MessageType msg_type) { - pb_tnc_message_t *pb_pa_msg; - chunk_t pb_tnc_msg; TNC_MessageSubtype msg_sub_type; - TNC_VendorID msg_vendor_id; + TNC_VendorID msg_vendor_id; + pb_tnc_message_t *pb_tnc_msg; + pb_tnc_batch_type_t batch_type; msg_sub_type = msg_type & TNC_SUBTYPE_ANY; msg_vendor_id = (msg_type >> 8) & TNC_VENDORID_ANY; - pb_pa_msg = pb_pa_message_create(msg_vendor_id, msg_sub_type, imc_id, imv_id, - chunk_create(msg, msg_len)); - pb_pa_msg->build(pb_pa_msg); - pb_tnc_msg = build_pb_tnc_msg(PB_MSG_PA, pb_pa_msg->get_encoding(pb_pa_msg)); - pb_pa_msg->destroy(pb_pa_msg); + pb_tnc_msg = pb_pa_message_create(msg_vendor_id, msg_sub_type, imc_id, imv_id, + chunk_create(msg, msg_len)); + /* adding PA message to SDATA or CDATA batch only */ + batch_type = this->is_server ? PB_BATCH_SDATA : PB_BATCH_CDATA; this->mutex->lock(this->mutex); - this->batch = chunk_cat("mm", this->batch, pb_tnc_msg); + if (!this->batch) + { + this->batch = pb_tnc_batch_create(this->is_server, batch_type); + } + if (this->batch->get_type(this->batch) == batch_type) + { + this->batch->add_message(this->batch, pb_tnc_msg); + } + else + { + pb_tnc_msg->destroy(pb_tnc_msg); + } this->mutex->unlock(this->mutex); } METHOD(tls_t, process, status_t, private_tnccs_20_t *this, void *buf, size_t buflen) { - tls_reader_t *reader; - pb_tnc_message_t *msg = NULL; + chunk_t data; + pb_tnc_batch_t *batch; + pb_tnc_message_t *msg; + pb_tnc_state_t old_state; + enumerator_t *enumerator; status_t status; - char *pos; - size_t len; if (this->is_server && !this->connection_id) { @@ -215,105 +133,43 @@ METHOD(tls_t, process, status_t, } charon->imvs->notify_connection_change(charon->imvs, this->connection_id, TNC_CONNECTION_STATE_CREATE); + } + data = chunk_create(buf, buflen); + DBG1(DBG_TNC, "received TNCCS Batch (%u bytes) for Connection ID %u", + data.len, this->connection_id); + DBG3(DBG_TNC, "%B", &data); + batch = pb_tnc_batch_create_from_data(this->is_server, data); - /* Test reason enumerator */ - { - chunk_t reason, reason_2 = { "virus alarm", 11 }; - chunk_t reason_lang, reason_lang_2 = { "en, ru", 6 }; - TNC_IMVID id; - enumerator_t *enumerator; - - this->recs->set_reason_string(this->recs, 2, reason_2); - this->recs->set_reason_language(this->recs, 2, reason_lang_2); - - enumerator = this->recs->create_reason_enumerator(this->recs); - while (enumerator->enumerate(enumerator, &id, &reason, &reason_lang)) + old_state = this->state; + status = batch->process(batch, &this->state); + if (this->state != old_state) + { + DBG2(DBG_TNC, "PB-TNC state transition from '%N' to '%N'", + pb_tnc_state_names, old_state, pb_tnc_state_names, this->state); + } + switch (status) + { + case SUCCESS: + default: + break; + case FAILED: + if (this->batch) { - DBG1(DBG_TNC, "IMV %u: reason = '%.*s', lang = '%.*s'", - id, reason.len, reason.ptr, reason_lang.len, reason_lang.ptr); + DBG1(DBG_TNC, "cancelling PB-TNC %N Batch", + pb_tnc_batch_type_names, this->batch->get_type(this->batch)); + this->batch->destroy(this->batch); + } + this->batch = pb_tnc_batch_create(this->is_server, PB_BATCH_CLOSE); + /* fall through */ + case VERIFY_ERROR: + enumerator = batch->create_error_enumerator(batch); + while (enumerator->enumerate(enumerator, &msg)) + { + this->batch->add_message(this->batch, msg->get_ref(msg)); } enumerator->destroy(enumerator); - } } - DBG1(DBG_TNC, "received TNCCS Batch (%u bytes) for Connection ID %u", - buflen, this->connection_id); - - pos = strchr(buf, '|'); - if (pos) - { - pos++; - len = buflen - (pos - (char*)buf); - } - else - { - pos = buf; - len = buflen; - } - reader = tls_reader_create(chunk_create(pos, len)); - while (reader->remaining(reader) > 0) - { - switch (process_pb_tnc_msg(reader, &msg)) - { - case SUCCESS: - break; - case INVALID_STATE: - continue; - default: - reader->destroy(reader); - return FAILED; - } - - status = msg->process(msg); - if (status != SUCCESS) - { - msg->destroy(msg); - reader->destroy(reader); - return status; - } - - switch (msg->get_type(msg)) - { - case PB_MSG_PA: - { - TNC_MessageType msg_type; - u_int32_t vendor_id, subtype; - chunk_t msg_body; - pb_pa_message_t *pb_pa_msg; - - pb_pa_msg = (pb_pa_message_t*)msg; - vendor_id = pb_pa_msg->get_vendor_id(pb_pa_msg, &subtype); - msg_type = (vendor_id << 8) | subtype; - msg_body = pb_pa_msg->get_body(pb_pa_msg); - DBG2(DBG_TNC, "message type: 0x%08x", msg_type); - if (this->is_server) - { - charon->imvs->receive_message(charon->imvs, - this->connection_id, msg_body.ptr, msg_body.len, - msg_type); - } - else - { - charon->imcs->receive_message(charon->imcs, - this->connection_id, msg_body.ptr, msg_body.len, - msg_type); - } - break; - } - case PB_MSG_ERROR: - break; - case PB_MSG_EXPERIMENTAL: - break; - case PB_MSG_LANGUAGE_PREFERENCE: - break; - case PB_MSG_ASSESSMENT_RESULT: - case PB_MSG_ACCESS_RECOMMENDATION: - case PB_MSG_REMEDIATION_PARAMETERS: - case PB_MSG_REASON_STRING: - break; - } - msg->destroy(msg); - } - reader->destroy(reader); + batch->destroy(batch); if (this->is_server) { @@ -323,22 +179,17 @@ METHOD(tls_t, process, status_t, { charon->imcs->batch_ending(charon->imcs, this->connection_id); } - return NEED_MORE; } METHOD(tls_t, build, status_t, private_tnccs_20_t *this, void *buf, size_t *buflen, size_t *msglen) { - char *msg = this->is_server ? "tncs->tncc 2.0|" : "tncc->tncs 2.0|"; - size_t len; - - this->mutex->lock(this->mutex); - this->batch = chunk_cat("cm", chunk_create(msg, strlen(msg)), this->batch); - this->mutex->unlock(this->mutex); - if (!this->is_server && !this->connection_id) { + pb_tnc_message_t *msg; + char *pref_lang; + this->connection_id = charon->tnccs->create_connection(charon->tnccs, (tnccs_t*)this, _send_message, &this->request_handshake_retry, NULL); @@ -346,6 +197,16 @@ METHOD(tls_t, build, status_t, { return FAILED; } + + /* Create PB-TNC Language Preference Message */ + pref_lang = charon->imcs->get_preferred_language(charon->imcs); + msg = pb_language_preference_message_create(chunk_create(pref_lang, + strlen(pref_lang))); + this->mutex->lock(this->mutex); + this->batch = pb_tnc_batch_create(this->is_server, PB_BATCH_CDATA); + this->batch->add_message(this->batch, msg); + this->mutex->unlock(this->mutex); + charon->imcs->notify_connection_change(charon->imcs, this->connection_id, TNC_CONNECTION_STATE_CREATE); charon->imcs->notify_connection_change(charon->imcs, @@ -353,18 +214,136 @@ METHOD(tls_t, build, status_t, charon->imcs->begin_handshake(charon->imcs, this->connection_id); } - this->mutex->lock(this->mutex); - len = this->batch.len; - *msglen = len; - *buflen = len; - memcpy(buf, this->batch.ptr, len); - chunk_free(&this->batch); - this->mutex->unlock(this->mutex); + if (this->batch) + { + pb_tnc_batch_type_t batch_type; + pb_tnc_state_t old_state; + status_t status; + chunk_t data; + bool unexpected_batch_type = FALSE; - DBG1(DBG_TNC, "sending TNCCS Batch (%d bytes) for Connection ID %u", - len, this->connection_id); + batch_type = this->batch->get_type(this->batch); + old_state = this->state; + + switch (this->state) + { + case PB_STATE_INIT: + if (batch_type == PB_BATCH_CDATA) + { + this->state = PB_STATE_SERVER_WORKING; + break; + } + if (batch_type == PB_BATCH_SDATA) + { + this->state = PB_STATE_CLIENT_WORKING; + break; + } + if (batch_type == PB_BATCH_CLOSE) + { + this->state = PB_STATE_END; + break; + } + unexpected_batch_type = TRUE; + break; + case PB_STATE_SERVER_WORKING: + if (batch_type == PB_BATCH_SDATA) + { + this->state = PB_STATE_CLIENT_WORKING; + break; + } + if (batch_type == PB_BATCH_RESULT) + { + this->state = PB_STATE_DECIDED; + break; + } + if (batch_type == PB_BATCH_CRETRY || + batch_type == PB_BATCH_SRETRY) + { + break; + } + if (batch_type == PB_BATCH_CLOSE) + { + this->state = PB_STATE_END; + break; + } + unexpected_batch_type = TRUE; + break; + case PB_STATE_CLIENT_WORKING: + if (batch_type == PB_BATCH_CDATA) + { + this->state = PB_STATE_SERVER_WORKING; + break; + } + if (batch_type == PB_BATCH_CRETRY) + { + break; + } + if (batch_type == PB_BATCH_CLOSE) + { + this->state = PB_STATE_END; + break; + } + unexpected_batch_type = TRUE; + break; + case PB_STATE_DECIDED: + if (batch_type == PB_BATCH_CRETRY || + batch_type == PB_BATCH_SRETRY) + { + this->state = PB_STATE_SERVER_WORKING; + break; + } + if (batch_type == PB_BATCH_CLOSE) + { + this->state = PB_STATE_END; + break; + } + unexpected_batch_type = TRUE; + break; + case PB_STATE_END: + if (batch_type == PB_BATCH_CLOSE) + { + break; + } + unexpected_batch_type = TRUE; + } - return ALREADY_DONE; + this->mutex->lock(this->mutex); + if (unexpected_batch_type) + { + DBG1(DBG_TNC, "cancelling unexpected PB-TNC Batch Type: %N", + pb_tnc_batch_type_names, batch_type); + status = INVALID_STATE; + } + else + { + this->batch->build(this->batch); + data = this->batch->get_encoding(this->batch); + DBG1(DBG_TNC, "sending PB-TNC %N Batch (%d bytes) for Connection ID %u", + pb_tnc_batch_type_names, batch_type, data.len, + this->connection_id); + DBG3(DBG_TNC, "%B", &data); + + *msglen = data.len; + *buflen = data.len; + memcpy(buf, data.ptr, data.len); + status = ALREADY_DONE; + } + this->batch->destroy(this->batch); + this->batch = NULL; + this->mutex->unlock(this->mutex); + + if (this->state != old_state) + { + DBG2(DBG_TNC, "PB-TNC state transition from '%N' to '%N'", + pb_tnc_state_names, old_state, pb_tnc_state_names, this->state); + } + return status; + } + else + { + DBG1(DBG_TNC, "no TNCCS Batch to send"); + return INVALID_STATE; + } } METHOD(tls_t, is_server, bool, @@ -409,7 +388,7 @@ METHOD(tls_t, destroy, void, { charon->tnccs->remove_connection(charon->tnccs, this->connection_id); this->mutex->destroy(this->mutex); - free(this->batch.ptr); + DESTROY_IF(this->batch); free(this); } @@ -431,6 +410,7 @@ tls_t *tnccs_20_create(bool is_server) .destroy = _destroy, }, .is_server = is_server, + .state = PB_STATE_INIT, .mutex = mutex_create(MUTEX_TYPE_DEFAULT), ); diff --git a/src/libcharon/plugins/tnccs_20/tnccs_20_types.c b/src/libcharon/plugins/tnccs_20/tnccs_20_types.c index 5f6f81f0b..d555a5052 100644 --- a/src/libcharon/plugins/tnccs_20/tnccs_20_types.c +++ b/src/libcharon/plugins/tnccs_20/tnccs_20_types.c @@ -15,40 +15,11 @@ #include "tnccs_20_types.h" -ENUM(pb_tnc_batch_type_names, PB_BATCH_CDATA, PB_BATCH_CLOSE, - "CDATA", - "SDATA", - "RESULT", - "CRETRY", - "SRETRY", - "CLOSE" -); - -ENUM(pb_tnc_msg_type_names, PB_MSG_EXPERIMENTAL, PB_MSG_REASON_STRING, - "PB-Experimental", - "PB-PA", - "PB-Assessment-Result", - "PB-Access-Recommendation", - "PB-Remediation-Parameters", - "PB-Error", - "PB-Language-Preference", - "PB-Reason-String" -); - ENUM(pb_tnc_remed_param_type_names, PB_REMEDIATION_URI, PB_REMEDIATION_STRING, "Remediation-URI", "Remediation-String" ); -ENUM(pb_tnc_error_code_names, PB_ERROR_UNEXPECTED_BATCH_TYPE, - PB_ERROR_VERSION_NOT_SUPPORTED, - "Unexpected Batch Type", - "Invalid Parameter", - "Local Error" - "Unsupported Mandatory Message", - "Version Not Supported" -); - ENUM(pa_tnc_subtype_names, PA_SUBTYPE_TESTING, PA_SUBTYPE_NEA_CLIENT, "Testing", "Operating System", diff --git a/src/libcharon/plugins/tnccs_20/tnccs_20_types.h b/src/libcharon/plugins/tnccs_20/tnccs_20_types.h index f45246541..78bd9a2f4 100644 --- a/src/libcharon/plugins/tnccs_20/tnccs_20_types.h +++ b/src/libcharon/plugins/tnccs_20/tnccs_20_types.h @@ -21,51 +21,13 @@ #ifndef TNCCS_20_TYPES_H_ #define TNCCS_20_TYPES_H_ +#define PB_TNC_VERSION 2 + #include -typedef enum pb_tnc_batch_type_t pb_tnc_batch_type_t; -typedef enum pb_tnc_msg_type_t pb_tnc_msg_type_t; typedef enum pb_tnc_remed_param_type_t pb_tnc_remed_param_type_t; -typedef enum pb_tnc_error_code_t pb_tnc_error_code_t; typedef enum pa_tnc_subtype_t pa_tnc_subtype_t; -/** - * PB-TNC Batch Types as defined in section 4.1 of RFC 5793 - */ -enum pb_tnc_batch_type_t { - PB_BATCH_CDATA = 1, - PB_BATCH_SDATA = 2, - PB_BATCH_RESULT = 3, - PB_BATCH_CRETRY = 4, - PB_BATCH_SRETRY = 5, - PB_BATCH_CLOSE = 6 -}; - -/** - * enum name for pb_tnc_batch_type_t. - */ -extern enum_name_t *pb_tnc_batch_type_names; - -/** - * PB-TNC Message Types as defined in section 4.3 of RFC 5793 - */ -enum pb_tnc_msg_type_t { - PB_MSG_EXPERIMENTAL = 0, - PB_MSG_PA = 1, - PB_MSG_ASSESSMENT_RESULT = 2, - PB_MSG_ACCESS_RECOMMENDATION = 3, - PB_MSG_REMEDIATION_PARAMETERS = 4, - PB_MSG_ERROR = 5, - PB_MSG_LANGUAGE_PREFERENCE = 6, - PB_MSG_REASON_STRING = 7, - PB_MSG_ROOF = 7 -}; - -/** - * enum name for pb_tnc_msg_type_t. - */ -extern enum_name_t *pb_tnc_msg_type_names; - /** * PB-TNC Remediation Parameter Types as defined in section 4.8.1 of RFC 5793 */ @@ -79,22 +41,6 @@ enum pb_tnc_remed_param_type_t { */ extern enum_name_t *pb_tnc_remed_param_type_names; -/** - * PB-TNC Error Codes as defined in section 4.9.1 of RFC 5793 - */ -enum pb_tnc_error_code_t { - PB_ERROR_UNEXPECTED_BATCH_TYPE = 0, - PB_ERROR_INVALID_PARAMETER = 1, - PB_ERROR_LOCAL_ERROR = 2, - PB_ERROR_UNSUPPORTED_MANDATORY_MESSAGE = 3, - PB_ERROR_VERSION_NOT_SUPPORTED = 4 -}; - -/** - * enum name for pb_tnc_error_code_t. - */ -extern enum_name_t *pb_tnc_error_code_names; - /** * PA-TNC Subtypes as defined in section 3.5 of RFC 5792 */ diff --git a/src/libcharon/tnc/tnccs/tnccs.h b/src/libcharon/tnc/tnccs/tnccs.h index 48374aadf..e4429d6c3 100644 --- a/src/libcharon/tnc/tnccs/tnccs.h +++ b/src/libcharon/tnc/tnccs/tnccs.h @@ -26,6 +26,9 @@ #include #include +#define IETF_VENDOR_ID 0x000000 +#define RESERVED_VENDOR_ID 0xffffff + typedef enum tnccs_type_t tnccs_type_t; /**