limit the size of a PB-TNC batch to the maximum EAP-TNC packet size
authorAndreas Steffen <andreas.steffen@strongswan.org>
Tue, 10 Jul 2012 20:51:49 +0000 (22:51 +0200)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Wed, 11 Jul 2012 15:09:05 +0000 (17:09 +0200)
src/libcharon/plugins/tnccs_20/batch/pb_tnc_batch.c
src/libcharon/plugins/tnccs_20/batch/pb_tnc_batch.h
src/libcharon/plugins/tnccs_20/messages/pb_access_recommendation_msg.c
src/libcharon/plugins/tnccs_20/messages/pb_assessment_result_msg.c
src/libcharon/plugins/tnccs_20/messages/pb_error_msg.c
src/libcharon/plugins/tnccs_20/messages/pb_language_preference_msg.c
src/libcharon/plugins/tnccs_20/messages/pb_pa_msg.c
src/libcharon/plugins/tnccs_20/messages/pb_reason_string_msg.c
src/libcharon/plugins/tnccs_20/messages/pb_remediation_parameters_msg.c
src/libcharon/plugins/tnccs_20/tnccs_20.c

index c6a4bb5..523ca0d 100644 (file)
@@ -51,7 +51,6 @@ typedef struct private_pb_tnc_batch_t private_pb_tnc_batch_t;
 
 #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)
@@ -71,7 +70,6 @@ typedef struct private_pb_tnc_batch_t private_pb_tnc_batch_t;
 
 #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
 
@@ -151,7 +149,6 @@ METHOD(pb_tnc_batch_t, build, void,
        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;
        }
index 17e5fff..8d91c13 100644 (file)
@@ -29,10 +29,14 @@ typedef struct pb_tnc_batch_t pb_tnc_batch_t;
 
 #include <library.h>
 
+#define PB_TNC_BATCH_HEADER_SIZE       8
+#define PB_TNC_HEADER_SIZE                     12
+
 /**
   * PB-TNC Batch Types as defined in section 4.1 of RFC 5793
  */
 enum pb_tnc_batch_type_t {
+       PB_BATCH_NONE =         0,      /* for internal use only */
        PB_BATCH_CDATA =        1,
        PB_BATCH_SDATA =        2,
        PB_BATCH_RESULT =       3,
index fa3dedd..974db4d 100644 (file)
@@ -82,11 +82,13 @@ METHOD(pb_tnc_msg_t, build, void,
 {
        bio_writer_t *writer;
 
-       /* build message */
+       if (this->encoding.ptr)
+       {
+               return;
+       }
        writer = bio_writer_create(ACCESS_RECOMMENDATION_MSG_SIZE);
        writer->write_uint16(writer, ACCESS_RECOMMENDATION_RESERVED);
        writer->write_uint16(writer, this->recommendation);
-       free(this->encoding.ptr);
        this->encoding = writer->get_buf(writer);
        this->encoding = chunk_clone(this->encoding);
        writer->destroy(writer);
@@ -98,7 +100,6 @@ METHOD(pb_tnc_msg_t, process, status_t,
        bio_reader_t *reader;
        u_int16_t reserved;
 
-       /* process message */
        reader = bio_reader_create(this->encoding);
        reader->read_uint16(reader, &reserved);
        reader->read_uint16(reader, &this->recommendation);
index 0d558c0..ee06575 100644 (file)
@@ -78,10 +78,12 @@ METHOD(pb_tnc_msg_t, build, void,
 {
        bio_writer_t *writer;
 
-       /* build message */
+       if (this->encoding.ptr)
+       {
+               return;
+       }
        writer = bio_writer_create(ASSESSMENT_RESULT_MSG_SIZE);
        writer->write_uint32(writer, this->assessment_result);
-       free(this->encoding.ptr);
        this->encoding = writer->get_buf(writer);
        this->encoding = chunk_clone(this->encoding);
        writer->destroy(writer);
@@ -92,7 +94,6 @@ METHOD(pb_tnc_msg_t, process, status_t,
 {
        bio_reader_t *reader;
 
-       /* process message */
        reader = bio_reader_create(this->encoding);
        reader->read_uint32(reader, &this->assessment_result);
        reader->destroy(reader);
index 03e3cec..457d3da 100644 (file)
@@ -120,6 +120,11 @@ METHOD(pb_tnc_msg_t, build, void,
 {
        bio_writer_t *writer;
 
+       if (this->encoding.ptr)
+       {
+               return;
+       }
+
        /* build message header */
        writer = bio_writer_create(ERROR_HEADER_SIZE);
        writer->write_uint8 (writer, this->fatal ?
@@ -142,8 +147,6 @@ METHOD(pb_tnc_msg_t, build, void,
                /* 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);
index 297cc8d..46df544 100644 (file)
@@ -75,6 +75,10 @@ METHOD(pb_tnc_msg_t, get_encoding, chunk_t,
 METHOD(pb_tnc_msg_t, build, void,
        private_pb_language_preference_msg_t *this)
 {
+       if (this->encoding.ptr)
+       {
+               return;
+       }
        this->encoding = chunk_cat("cc",
                                                chunk_create(PB_LANG_PREFIX, PB_LANG_PREFIX_LEN),
                                                this->language_preference);
index 1c4913e..4d5f983 100644 (file)
@@ -116,6 +116,11 @@ METHOD(pb_tnc_msg_t, build, void,
        chunk_t msg_header;
        bio_writer_t *writer;
 
+       if (this->encoding.ptr)
+       {
+               return;
+       }
+
        /* build message header */
        writer = bio_writer_create(64);
        writer->write_uint8 (writer, this->excl ? PA_FLAG_EXCL : PA_FLAG_NONE);
@@ -126,7 +131,6 @@ METHOD(pb_tnc_msg_t, build, void,
        msg_header = writer->get_buf(writer);
 
        /* create encoding by concatenating message header and message body */
-       free(this->encoding.ptr);
        this->encoding = chunk_cat("cc", msg_header, this->msg_body);
        writer->destroy(writer);
 }
index 181ecf6..511b454 100644 (file)
@@ -83,12 +83,14 @@ METHOD(pb_tnc_msg_t, build, void,
 {
        bio_writer_t *writer;
 
-       /* build message */
+       if (this->encoding.ptr)
+       {
+               return;
+       }
        writer = bio_writer_create(64);
        writer->write_data32(writer, this->reason_string);
        writer->write_data8 (writer, this->language_code);
 
-       free(this->encoding.ptr);
        this->encoding = writer->get_buf(writer);
        this->encoding = chunk_clone(this->encoding);
        writer->destroy(writer);
@@ -99,7 +101,6 @@ METHOD(pb_tnc_msg_t, process, status_t,
 {
        bio_reader_t *reader;
 
-       /* process message */
        reader = bio_reader_create(this->encoding);
        if (!reader->read_data32(reader, &this->reason_string))
        {
index d213db3..c853f03 100644 (file)
@@ -108,14 +108,16 @@ METHOD(pb_tnc_msg_t, build, void,
 {
        bio_writer_t *writer;
 
-       /* build message */
+       if (this->encoding.ptr)
+       {
+               return;
+       }
        writer = bio_writer_create(64);
        writer->write_uint32(writer, this->vendor_id);
        writer->write_uint32(writer, this->parameters_type);
        writer->write_data32(writer, this->remediation_string);
        writer->write_data8 (writer, this->language_code);
 
-       free(this->encoding.ptr);
        this->encoding = writer->get_buf(writer);
        this->encoding = chunk_clone(this->encoding);
        writer->destroy(writer);
index 66f2f66..576cd82 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2010 Sansar Choinyanbuu
- * Copyright (C) 2010-2011 Andreas Steffen
+ * Copyright (C) 2010-2012 Andreas Steffen
  * HSR Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -36,6 +36,7 @@
 
 #include <debug.h>
 #include <threading/mutex.h>
+#include <utils/linked_list.h>
 #include <pen/pen.h>
 
 typedef struct private_tnccs_20_t private_tnccs_20_t;
@@ -66,9 +67,14 @@ struct private_tnccs_20_t {
        TNC_ConnectionID connection_id;
 
        /**
-        * PB-TNC batch being constructed
+        * PB-TNC messages to be sent
         */
-       pb_tnc_batch_t *batch;
+       linked_list_t *messages;
+
+       /**
+        * Type of PB-TNC batch being constructed
+        */
+       pb_tnc_batch_type_t batch_type;
 
        /**
         * Mutex locking the batch in construction
@@ -97,6 +103,30 @@ struct private_tnccs_20_t {
 
 };
 
+/**
+ * If the batch type changes then delete all accumulated PB-TNC messages
+ */
+void change_batch_type(private_tnccs_20_t *this, pb_tnc_batch_type_t batch_type)
+{
+       pb_tnc_msg_t *msg;
+
+       if (batch_type != this->batch_type)
+       {
+               if (this->batch_type != PB_BATCH_NONE)
+               {
+                       DBG1(DBG_TNC, "cancelling PB-TNC %N batch",
+                                pb_tnc_batch_type_names, this->batch_type);
+
+                       while (this->messages->remove_last(this->messages,
+                                                                                         (void**)&msg) == SUCCESS)
+                       {
+                               msg->destroy(msg);
+                       }
+               }
+               this->batch_type = batch_type;
+       }
+}
+
 METHOD(tnccs_t, send_msg, TNC_Result,
        private_tnccs_20_t* this, TNC_IMCID imc_id, TNC_IMVID imv_id,
                                                      TNC_UInt32 msg_flags,
@@ -138,13 +168,13 @@ METHOD(tnccs_t, send_msg, TNC_Result,
        /* 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);
-       if (!this->batch)
+       if (this->batch_type == PB_BATCH_NONE)
        {
-               this->batch = pb_tnc_batch_create(this->is_server, batch_type);
+               this->batch_type = batch_type;
        }
-       if (this->batch->get_type(this->batch) == batch_type)
+       if (this->batch_type == batch_type)
        {
-               this->batch->add_msg(this->batch, pb_tnc_msg);
+               this->messages->insert_last(this->messages, pb_tnc_msg);
        }
        else
        {
@@ -344,23 +374,19 @@ static void build_retry_batch(private_tnccs_20_t *this)
        pb_tnc_batch_type_t batch_retry_type;
 
        batch_retry_type = this->is_server ? PB_BATCH_SRETRY : PB_BATCH_CRETRY;
-       if (this->batch)
+       if (this->batch_type == batch_retry_type)
        {
-               if (this->batch->get_type(this->batch) == batch_retry_type)
-               {
-                       /* retry batch has already been created */
-                       return;
-               }
-               DBG1(DBG_TNC, "cancelling PB-TNC %N batch",
-                       pb_tnc_batch_type_names, this->batch->get_type(this->batch));
-               this->batch->destroy(this->batch);
-        }
+               /* retry batch has already been selected */
+               return;
+       }
+
+       change_batch_type(this, batch_retry_type);
+
        if (this->is_server)
        {
                tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
                                                                                        TNC_CONNECTION_STATE_HANDSHAKE);
        }
-       this->batch = pb_tnc_batch_create(this->is_server, batch_retry_type);
 }
 
 METHOD(tls_t, process, status_t,
@@ -461,13 +487,7 @@ METHOD(tls_t, process, status_t,
                case FAILED:
                        this->fatal_error = TRUE;
                        this->mutex->lock(this->mutex);
-                       if (this->batch)
-                       {
-                               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);
+                       change_batch_type(this, PB_BATCH_CLOSE);
                        this->mutex->unlock(this->mutex);
                        /* fall through to add error messages to outbound batch */
                case VERIFY_ERROR:
@@ -475,7 +495,7 @@ METHOD(tls_t, process, status_t,
                        while (enumerator->enumerate(enumerator, &msg))
                        {
                                this->mutex->lock(this->mutex);
-                               this->batch->add_msg(this->batch, msg->get_ref(msg));
+                               this->messages->insert_last(this->messages, msg->get_ref(msg));
                                this->mutex->unlock(this->mutex);
                        }
                        enumerator->destroy(enumerator);
@@ -508,10 +528,10 @@ static void check_and_build_recommendation(private_tnccs_20_t *this)
        }
        if (this->recs->have_recommendation(this->recs, &rec, &eval))
        {
-               this->batch = pb_tnc_batch_create(this->is_server, PB_BATCH_RESULT);
+               this->batch_type = PB_BATCH_RESULT;
 
                msg = pb_assessment_result_msg_create(eval);
-               this->batch->add_msg(this->batch, msg);
+               this->messages->insert_last(this->messages, msg);
 
                /**
                 * Map IMV Action Recommendation codes to PB Access Recommendation codes
@@ -530,13 +550,13 @@ static void check_and_build_recommendation(private_tnccs_20_t *this)
                                pb_rec = PB_REC_ACCESS_DENIED;
                }
                msg = pb_access_recommendation_msg_create(pb_rec);
-               this->batch->add_msg(this->batch, msg);
+               this->messages->insert_last(this->messages, msg);
 
                enumerator = this->recs->create_reason_enumerator(this->recs);
                while (enumerator->enumerate(enumerator, &id, &reason, &language))
                {
                        msg = pb_reason_string_msg_create(reason, language);
-                       this->batch->add_msg(this->batch, msg);
+                       this->messages->insert_last(this->messages, msg);
                }
                enumerator->destroy(enumerator);
                this->recs->clear_reasons(this->recs);
@@ -568,8 +588,8 @@ METHOD(tls_t, build, status_t,
                msg = pb_language_preference_msg_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_msg(this->batch, msg);
+               this->batch_type = PB_BATCH_CDATA;
+               this->messages->insert_last(this->messages, msg);
                this->mutex->unlock(this->mutex);
 
                tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
@@ -603,66 +623,73 @@ METHOD(tls_t, build, status_t,
                this->request_handshake_retry = FALSE;
        }
 
-       if (!this->batch)
+       if (this->batch_type == PB_BATCH_NONE && this->is_server &&
+               state == PB_STATE_SERVER_WORKING)
        {
-               if (this->is_server)
-               {
-                       if (state == PB_STATE_SERVER_WORKING)
-                       {
-                               check_and_build_recommendation(this);
-                       }
-               }
-               else
-               {
-                       /**
-                        * if the DECIDED state has been reached and no CRETRY is under way
-                        * or if a CLOSE batch with error messages has been received,
-                        * a PB-TNC client replies with an empty CLOSE batch.
-                        */
-                       if (state == PB_STATE_DECIDED || state == PB_STATE_END)
-                       {
-                               this->batch = pb_tnc_batch_create(this->is_server, PB_BATCH_CLOSE);
-                       }
-               }
+               check_and_build_recommendation(this);
        }
 
-       if (this->batch)
+       if (this->batch_type != PB_BATCH_NONE)
        {
-               pb_tnc_batch_type_t batch_type;
-               chunk_t data;
-
-               batch_type = this->batch->get_type(this->batch);
+               pb_tnc_batch_t *batch;
+               pb_tnc_msg_t *msg;
+               chunk_t msg_value, data;
+               int msg_count;
+               size_t batch_len;
+               enumerator_t *enumerator;
 
-               if (this->state_machine->send_batch(this->state_machine, batch_type))
+               if (this->state_machine->send_batch(this->state_machine, this->batch_type))
                {
-                       this->batch->build(this->batch);
-                       data = this->batch->get_encoding(this->batch);
+                       batch = pb_tnc_batch_create(this->is_server, this->batch_type);
+                       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;
+                               if (batch_len > *buflen)
+                               {
+                                       /* message does not fit into batch of maximum size */
+                                       break;
+                               }
+                               batch->add_msg(batch, msg);
+                               this->messages->remove_at(this->messages, enumerator);
+                       }
+                       enumerator->destroy(enumerator);
+
+                       batch->build(batch);
+                       data = batch->get_encoding(batch);
                        DBG1(DBG_TNC, "sending PB-TNC %N batch (%d bytes) for Connection ID %u",
-                                                  pb_tnc_batch_type_names, batch_type, data.len,
+                                                  pb_tnc_batch_type_names, this->batch_type, data.len,
                                                   this->connection_id);
                        DBG3(DBG_TNC, "%B", &data);
+
+                       *buflen = data.len;
                        *msglen = 0;
+                       memcpy(buf, data.ptr, *buflen);
+                       batch->destroy(batch);
 
-                       if (data.len > *buflen)
+                       msg_count = this->messages->get_count(this->messages);
+                       if (msg_count)
                        {
-                               DBG1(DBG_TNC, "fragmentation of PB-TNC batch not supported yet");
+                               DBG2(DBG_TNC, "%d PB-TNC message%s for %N batch queued",
+                                        msg_count, (msg_count == 1) ? "" : "s",
+                                        pb_tnc_batch_type_names, this->batch_type);
                        }
                        else
                        {
-                               *buflen = data.len;
+                               this->batch_type = PB_BATCH_NONE;
                        }
-                       memcpy(buf, data.ptr, *buflen);
+                       
                        status = ALREADY_DONE;
                }
                else
                {
-                       DBG1(DBG_TNC, "cancelling unexpected PB-TNC batch type: %N",
-                                pb_tnc_batch_type_names, batch_type);
+                       change_batch_type(this, PB_BATCH_NONE);
                        status = INVALID_STATE;
                }
-
-               this->batch->destroy(this->batch);
-               this->batch = NULL;
        }
        else
        {
@@ -715,7 +742,8 @@ METHOD(tls_t, destroy, void,
                                                                                          this->is_server);
        this->state_machine->destroy(this->state_machine);
        this->mutex->destroy(this->mutex);
-       DESTROY_IF(this->batch);
+       this->messages->destroy_offset(this->messages,
+                                                                  offsetof(pb_tnc_msg_t, destroy));
        free(this);
 }
 
@@ -739,6 +767,7 @@ tls_t *tnccs_20_create(bool is_server)
                .is_server = is_server,
                .state_machine = pb_tnc_state_machine_create(is_server),
                .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+               .messages = linked_list_create(),
        );
 
        return &this->public;