Split IF-TNCCS 2.0 protocol processing into separate TNC client and server handlers
authorAndreas Steffen <andreas.steffen@strongswan.org>
Fri, 20 Mar 2015 21:01:46 +0000 (22:01 +0100)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Mon, 23 Mar 2015 21:25:42 +0000 (22:25 +0100)
src/libtnccs/plugins/tnccs_20/Makefile.am
src/libtnccs/plugins/tnccs_20/batch/pb_tnc_batch.c
src/libtnccs/plugins/tnccs_20/batch/pb_tnc_batch.h
src/libtnccs/plugins/tnccs_20/tnccs_20.c
src/libtnccs/plugins/tnccs_20/tnccs_20_client.c [new file with mode: 0644]
src/libtnccs/plugins/tnccs_20/tnccs_20_client.h [new file with mode: 0644]
src/libtnccs/plugins/tnccs_20/tnccs_20_handler.h [new file with mode: 0644]
src/libtnccs/plugins/tnccs_20/tnccs_20_server.c [new file with mode: 0644]
src/libtnccs/plugins/tnccs_20/tnccs_20_server.h [new file with mode: 0644]

index 2aefecd..3cb1d80 100644 (file)
@@ -18,6 +18,8 @@ endif
 
 libstrongswan_tnccs_20_la_SOURCES = \
        tnccs_20_plugin.h tnccs_20_plugin.c tnccs_20.h tnccs_20.c \
+       tnccs_20_handler.h \
+       tnccs_20_server.h tnccs_20_server.c tnccs_20_client.h tnccs_20_client.c \
        batch/pb_tnc_batch.h batch/pb_tnc_batch.c \
        messages/pb_tnc_msg.h messages/pb_tnc_msg.c \
        messages/ietf/pb_experimental_msg.h messages/ietf/pb_experimental_msg.c \
index 228c082..57e4ec0 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2010 Sansar Choinyanbuu
- * Copyright (C) 2010-2012 Andreas Steffen
+ * Copyright (C) 2010-2015 Andreas Steffen
  * HSR Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -85,7 +85,7 @@ struct private_pb_tnc_batch_t {
        pb_tnc_batch_t public;
 
        /**
-        * TNCC if TRUE, TNCS if FALSE
+        * from TNC server if TRUE, from TNC client if FALSE
         */
        bool is_server;
 
@@ -228,15 +228,15 @@ METHOD(pb_tnc_batch_t, build, void,
        writer->destroy(writer);
 }
 
-static status_t process_batch_header(private_pb_tnc_batch_t *this,
-                                                                        pb_tnc_state_machine_t *state_machine)
+METHOD(pb_tnc_batch_t, process_header, status_t,
+       private_pb_tnc_batch_t *this, bool directionality, bool is_server,
+       bool *from_server)
 {
        bio_reader_t *reader;
        pb_tnc_msg_t *msg;
        pb_error_msg_t *err_msg;
        u_int8_t version, flags, reserved, type;
        u_int32_t batch_len;
-       bool directionality;
 
        if (this->encoding.len < PB_TNC_BATCH_HEADER_SIZE)
        {
@@ -267,13 +267,14 @@ static status_t process_batch_header(private_pb_tnc_batch_t *this,
        }
 
        /* Directionality */
-       directionality = (flags & PB_TNC_BATCH_FLAG_D) != PB_TNC_BATCH_FLAG_NONE;
-       if (directionality == this->is_server)
+       *from_server = (flags & PB_TNC_BATCH_FLAG_D) != PB_TNC_BATCH_FLAG_NONE;
+
+       if (directionality & (*from_server == is_server))
        {
                DBG1(DBG_TNC, "wrong Directionality: batch is from a PB %s",
-                        directionality ? "server" : "client");
+                                          is_server ? "server" : "client");
                msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
-                                                       PB_ERROR_INVALID_PARAMETER, 1);
+                                          PB_ERROR_INVALID_PARAMETER, 1);
                goto fatal;
        }
 
@@ -287,17 +288,6 @@ static status_t process_batch_header(private_pb_tnc_batch_t *this,
                goto fatal;
        }
 
-       if (!state_machine->receive_batch(state_machine, this->type))
-       {
-               DBG1(DBG_TNC, "unexpected PB-TNC batch type: %N",
-                                          pb_tnc_batch_type_names, this->type);
-               msg = pb_error_msg_create(TRUE, PEN_IETF,
-                                                                 PB_ERROR_UNEXPECTED_BATCH_TYPE);
-               goto fatal;
-       }
-       DBG1(DBG_TNC, "processing PB-TNC %N batch", pb_tnc_batch_type_names,
-                                                                                               this->type);
-
        /* Batch Length */
        if (this->encoding.len != batch_len)
        {
@@ -310,17 +300,11 @@ static status_t process_batch_header(private_pb_tnc_batch_t *this,
 
        this->offset = PB_TNC_BATCH_HEADER_SIZE;
 
-       /* Register an empty CDATA batch with the state machine */
-       if (this->type == PB_BATCH_CDATA)
-       {
-               state_machine->set_empty_cdata(state_machine,
-                                                                          this->offset == this->encoding.len);
-       }
        return SUCCESS;
 
 fatal:
        this->errors->insert_last(this->errors, msg);
-       return FAILED;
+       return VERIFY_ERROR;
 }
 
 static status_t process_tnc_msg(private_pb_tnc_batch_t *this)
@@ -502,14 +486,26 @@ fatal:
 METHOD(pb_tnc_batch_t, process, status_t,
        private_pb_tnc_batch_t *this, pb_tnc_state_machine_t *state_machine)
 {
-       status_t status;
+       pb_tnc_msg_t *msg;
+       status_t status = SUCCESS;
 
-       status = process_batch_header(this, state_machine);
-       if (status != SUCCESS)
+       if (!state_machine->receive_batch(state_machine, this->type))
        {
+               DBG1(DBG_TNC, "unexpected PB-TNC batch type: %N",
+                                          pb_tnc_batch_type_names, this->type);
+               msg = pb_error_msg_create(TRUE, PEN_IETF,
+                                                                 PB_ERROR_UNEXPECTED_BATCH_TYPE);
+               this->errors->insert_last(this->errors, msg);
                return FAILED;
        }
 
+       /* Register an empty CDATA batch with the state machine */
+       if (this->type == PB_BATCH_CDATA)
+       {
+               state_machine->set_empty_cdata(state_machine,
+                                                                          this->offset == this->encoding.len);
+       }
+
        while (this->offset < this->encoding.len)
        {
                switch (process_tnc_msg(this))
@@ -585,7 +581,7 @@ pb_tnc_batch_t* pb_tnc_batch_create(bool is_server, pb_tnc_batch_type_t type,
 /**
  * See header
  */
-pb_tnc_batch_t* pb_tnc_batch_create_from_data(bool is_server, chunk_t data)
+pb_tnc_batch_t* pb_tnc_batch_create_from_data(chunk_t data)
 {
        private_pb_tnc_batch_t *this;
 
@@ -595,12 +591,12 @@ pb_tnc_batch_t* pb_tnc_batch_create_from_data(bool is_server, chunk_t data)
                        .get_encoding = _get_encoding,
                        .add_msg = _add_msg,
                        .build = _build,
+                       .process_header = _process_header,
                        .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),
index 106c557..6089c7d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2012 Andreas Steffen
+ * Copyright (C) 2010-2015 Andreas Steffen
  * HSR Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -84,10 +84,21 @@ struct pb_tnc_batch_t {
        void (*build)(pb_tnc_batch_t *this);
 
        /**
+        * Process the PB-TNC Batch header
+        *
+        * @param directionality        TRUE if no mutual TNC measurements
+        * @param is_server                     TRUE if called by TNC server
+        * @param from_server           TRUE if sent by TNC server
+        * @return                                      return processing status
+        */
+       status_t (*process_header)(pb_tnc_batch_t *this, bool directionality,
+                                                          bool is_server, bool *from_server);
+
+       /**
         * Process the PB-TNC Batch
         *
-        * @param                               PB-TNC state machine
-        * @return                              return processing status
+        * @param state_machine         PB-TNC state machine
+        * @return                                      return processing status
         */
        status_t (*process)(pb_tnc_batch_t *this,
                                                pb_tnc_state_machine_t *state_machine);
@@ -95,14 +106,14 @@ struct pb_tnc_batch_t {
        /**
         * Enumerates over all PB-TNC Messages
         *
-        * @return                              return message enumerator
+        * @return                                      return message enumerator
         */
        enumerator_t* (*create_msg_enumerator)(pb_tnc_batch_t *this);
 
        /**
         * Enumerates over all parsing errors
         *
-        * @return                              return error enumerator
+        * @return                                      return error enumerator
         */
        enumerator_t* (*create_error_enumerator)(pb_tnc_batch_t *this);
 
@@ -115,9 +126,9 @@ struct pb_tnc_batch_t {
 /**
  * Create an empty PB-TNC Batch of a given type
  *
- * @param is_server                    TRUE if server, FALSE if client
- * @param type                         PB-TNC batch type
- * @param max_batch_len                maximum size the PB-TNC batch
+ * @param is_server                            TRUE if server, FALSE if client
+ * @param type                                 PB-TNC batch type
+ * @param max_batch_len                        maximum size the PB-TNC batch
  */
 pb_tnc_batch_t* pb_tnc_batch_create(bool is_server, pb_tnc_batch_type_t type,
                                                                        size_t max_batch_len);
@@ -125,9 +136,8 @@ 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
  *
- * @param 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);
+pb_tnc_batch_t* pb_tnc_batch_create_from_data(chunk_t data);
 
 #endif /** PB_TNC_BATCH_H_ @}*/
index 9977714..33b0128 100644 (file)
  */
 
 #include "tnccs_20.h"
+#include "tnccs_20_handler.h"
+#include "tnccs_20_server.h"
+#include "tnccs_20_client.h"
 #include "batch/pb_tnc_batch.h"
 #include "messages/pb_tnc_msg.h"
 #include "messages/ietf/pb_pa_msg.h"
-#include "messages/ietf/pb_error_msg.h"
-#include "messages/ietf/pb_assessment_result_msg.h"
-#include "messages/ietf/pb_access_recommendation_msg.h"
-#include "messages/ietf/pb_remediation_parameters_msg.h"
-#include "messages/ietf/pb_reason_string_msg.h"
-#include "messages/ietf/pb_language_preference_msg.h"
-#include "messages/tcg/pb_pdp_referral_msg.h"
-#include "state_machine/pb_tnc_state_machine.h"
 
 #include <tncif_names.h>
 #include <tncif_pa_subtypes.h>
 
-#include <tnc/tnc.h>
-#include <tnc/tnccs/tnccs_manager.h>
-#include <tnc/imc/imc_manager.h>
-#include <tnc/imv/imv_manager.h>
-
-#include <threading/mutex.h>
 #include <utils/debug.h>
-#include <collections/linked_list.h>
-#include <pen/pen.h>
 
 typedef struct private_tnccs_20_t private_tnccs_20_t;
 
@@ -83,64 +70,49 @@ struct private_tnccs_20_t {
        tnc_ift_type_t transport;
 
        /**
-        * Type of TNC client authentication
-        */
-       u_int32_t auth_type;
-
-       /**
-        * PB-TNC State Machine
-        */
-       pb_tnc_state_machine_t *state_machine;
-
-       /**
-        * Connection ID assigned to this TNCCS connection
+        *  TNC IF-T transport protocol for EAP methods
         */
-       TNC_ConnectionID connection_id;
+       bool eap_transport;
 
        /**
-        * PB-TNC messages to be sent
+        * Type of TNC client authentication
         */
-       linked_list_t *messages;
+       u_int32_t auth_type;
 
        /**
-        * Type of PB-TNC batch being constructed
+        * Mutual TNC measurements
         */
-       pb_tnc_batch_type_t batch_type;
+       bool mutual;
 
        /**
-        * Maximum PB-TNC batch size
+        * Direction the next batch will go to
         */
-       size_t max_batch_len;
+       bool to_server;
 
        /**
-        * Maximum PA-TNC message size
+        * TNC Server
         */
-       size_t max_msg_len;
+       tnccs_20_handler_t *tnc_server;
 
        /**
-        * Mutex locking the batch in construction
+        * TNC Client
         */
-       mutex_t *mutex;
+       tnccs_20_handler_t *tnc_client;
 
        /**
-        * Flag set while processing
+        * Active TNCSS handler
         */
-       bool fatal_error;
+       tnccs_20_handler_t *tnccs_handler;
 
        /**
-        * Flag set by IMC/IMV RequestHandshakeRetry() function
+        * Maximum PB-TNC batch size
         */
-       bool request_handshake_retry;
-
-       /**
-         * SendMessage() by IMC/IMV only allowed if flag is set
-         */
-       bool send_msg;
+       size_t max_batch_len;
 
        /**
-        * Set of IMV recommendations  (TNC Server only)
+        * Maximum PA-TNC message size
         */
-       recommendations_t *recs;
+       size_t max_msg_len;
 
        /**
         * Callback function to communicate recommendation (TNC Server only)
@@ -148,69 +120,30 @@ struct private_tnccs_20_t {
        tnccs_cb_t callback;
 
        /**
-        * Data to pass to callback function (TNC Server only)
-        */
-       void *cb_data;
-
-       /**
-        * PDP server FQDN
-        */
-       chunk_t pdp_server;
-
-       /**
-        * PDP server port
-        */
-       u_int16_t pdp_port;
-
-       /**
         * reference count
         */
        refcount_t ref;
 
 };
 
-/**
- * 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,
+                                                         TNC_UInt32 msg_flags,
                                                          TNC_BufferReference msg,
                                                          TNC_UInt32 msg_len,
-                                                     TNC_VendorID msg_vid,
-                                                     TNC_MessageSubtype msg_subtype)
+                                                         TNC_VendorID msg_vid,
+                                                         TNC_MessageSubtype msg_subtype)
 {
        pb_tnc_msg_t *pb_tnc_msg;
-       pb_tnc_batch_type_t batch_type;
        enum_name_t *pa_subtype_names;
        bool excl;
 
-       if (!this->send_msg)
+       if (!this->tnccs_handler->get_send_flag(this->tnccs_handler))
        {
                DBG1(DBG_TNC, "%s %u not allowed to call SendMessage()",
-                       this->is_server ? "IMV" : "IMC",
-                       this->is_server ? imv_id : imc_id);
+                                          this->to_server ? "IMC" : "IMV",
+                                          this->to_server ? imc_id : imv_id);
+
                return TNC_RESULT_ILLEGAL_OPERATION;
        }
        excl = (msg_flags & TNC_MESSAGE_FLAGS_EXCLUSIVE) != 0;
@@ -230,697 +163,106 @@ METHOD(tnccs_t, send_msg, TNC_Result,
                DBG2(DBG_TNC, "creating PB-PA message type '%N' 0x%06x/0x%08x",
                                           pen_names, msg_vid, msg_vid, msg_subtype);
        }
+       this->tnccs_handler->add_msg(this->tnccs_handler, pb_tnc_msg);
 
-       /* 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_type == PB_BATCH_NONE)
-       {
-               this->batch_type = batch_type;
-       }
-       if (this->batch_type == batch_type)
-       {
-               this->messages->insert_last(this->messages, pb_tnc_msg);
-       }
-       else
-       {
-               pb_tnc_msg->destroy(pb_tnc_msg);
-       }
-       this->mutex->unlock(this->mutex);
        return TNC_RESULT_SUCCESS;
 }
 
-/**
- * Handle a single PB-TNC IETF standard message according to its type
- */
-static void handle_ietf_message(private_tnccs_20_t *this, pb_tnc_msg_t *msg)
-{
-       pen_type_t msg_type = msg->get_type(msg);
-
-       switch (msg_type.type)
-       {
-               case PB_MSG_EXPERIMENTAL:
-                       /* nothing to do */
-                       break;
-               case PB_MSG_PA:
-               {
-                       pb_pa_msg_t *pa_msg;
-                       pen_type_t msg_subtype;
-                       u_int16_t imc_id, imv_id;
-                       chunk_t msg_body;
-                       bool excl;
-                       enum_name_t *pa_subtype_names;
-
-                       pa_msg = (pb_pa_msg_t*)msg;
-                       msg_subtype = pa_msg->get_subtype(pa_msg);
-                       msg_body = pa_msg->get_body(pa_msg);
-                       imc_id = pa_msg->get_collector_id(pa_msg);
-                       imv_id = pa_msg->get_validator_id(pa_msg);
-                       excl = pa_msg->get_exclusive_flag(pa_msg);
-
-                       pa_subtype_names = get_pa_subtype_names(msg_subtype.vendor_id);
-                       if (pa_subtype_names)
-                       {
-                               DBG2(DBG_TNC, "handling PB-PA message type '%N/%N' 0x%06x/0x%08x",
-                                        pen_names, msg_subtype.vendor_id, pa_subtype_names,
-                                        msg_subtype.type, msg_subtype.vendor_id, msg_subtype.type);
-                       }
-                       else
-                       {
-                               DBG2(DBG_TNC, "handling PB-PA message type '%N' 0x%06x/0x%08x",
-                                        pen_names, msg_subtype.vendor_id, msg_subtype.vendor_id,
-                                        msg_subtype.type);
-                       }
-
-                       this->send_msg = TRUE;
-                       if (this->is_server)
-                       {
-                               tnc->imvs->receive_message(tnc->imvs, this->connection_id,
-                                                                                  excl, msg_body.ptr, msg_body.len,
-                                                                                  msg_subtype.vendor_id,
-                                                                                  msg_subtype.type, imc_id, imv_id);
-                       }
-                       else
-                       {
-                               tnc->imcs->receive_message(tnc->imcs, this->connection_id,
-                                                                                  excl, msg_body.ptr, msg_body.len,
-                                                                                  msg_subtype.vendor_id,
-                                                                                  msg_subtype.type, imv_id, imc_id);
-                       }
-                       this->send_msg = FALSE;
-                       break;
-               }
-               case PB_MSG_ASSESSMENT_RESULT:
-               {
-                       pb_assessment_result_msg_t *assess_msg;
-                       u_int32_t result;
-
-                       assess_msg = (pb_assessment_result_msg_t*)msg;
-                       result = assess_msg->get_assessment_result(assess_msg);
-                       DBG1(DBG_TNC, "PB-TNC assessment result is '%N'",
-                                TNC_IMV_Evaluation_Result_names, result);
-                       break;
-               }
-               case PB_MSG_ACCESS_RECOMMENDATION:
-               {
-                       pb_access_recommendation_msg_t *rec_msg;
-                       pb_access_recommendation_code_t rec;
-                       TNC_ConnectionState state = TNC_CONNECTION_STATE_ACCESS_NONE;
-
-                       rec_msg = (pb_access_recommendation_msg_t*)msg;
-                       rec = rec_msg->get_access_recommendation(rec_msg);
-                       DBG1(DBG_TNC, "PB-TNC access recommendation is '%N'",
-                                                  pb_access_recommendation_code_names, rec);
-                       switch (rec)
-                       {
-                               case PB_REC_ACCESS_ALLOWED:
-                                       state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
-                                       break;
-                               case PB_REC_ACCESS_DENIED:
-                                       state = TNC_CONNECTION_STATE_ACCESS_NONE;
-                                       break;
-                               case PB_REC_QUARANTINED:
-                                       state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
-                       }
-                       tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
-                                                                                               state);
-                       break;
-               }
-               case PB_MSG_REMEDIATION_PARAMETERS:
-               {
-                       pb_remediation_parameters_msg_t *rem_msg;
-                       pen_type_t parameters_type;
-                       chunk_t parameters, string, lang_code;
-
-                       rem_msg = (pb_remediation_parameters_msg_t*)msg;
-                       parameters_type = rem_msg->get_parameters_type(rem_msg);
-                       parameters = rem_msg->get_parameters(rem_msg);
-
-                       if (parameters_type.vendor_id == PEN_IETF)
-                       {
-                               switch (parameters_type.type)
-                               {
-                                       case PB_REMEDIATION_URI:
-                                               DBG1(DBG_TNC, "remediation uri: %.*s",
-                                                                          parameters.len, parameters.ptr);
-                                               break;
-                                       case PB_REMEDIATION_STRING:
-                                               string = rem_msg->get_string(rem_msg, &lang_code);
-                                               DBG1(DBG_TNC, "remediation string: [%.*s]\n%.*s",
-                                                                          lang_code.len, lang_code.ptr,
-                                                                          string.len, string.ptr);
-                                               break;
-                                       default:
-                                               DBG1(DBG_TNC, "remediation parameters: %B", &parameters);
-                               }
-                       }
-                       else
-                       {
-                               DBG1(DBG_TNC, "remediation parameters: %B", &parameters);
-                       }
-                       break;
-               }
-               case PB_MSG_ERROR:
-               {
-                       pb_error_msg_t *err_msg;
-                       bool fatal;
-                       u_int32_t vendor_id;
-                       u_int16_t error_code;
-
-                       err_msg = (pb_error_msg_t*)msg;
-                       fatal = err_msg->get_fatal_flag(err_msg);
-                       vendor_id = err_msg->get_vendor_id(err_msg);
-                       error_code = err_msg->get_error_code(err_msg);
-
-                       if (fatal)
-                       {
-                               this->fatal_error = TRUE;
-                       }
-
-                       if (vendor_id == PEN_IETF)
-                       {
-                               switch (error_code)
-                               {
-                                       case PB_ERROR_INVALID_PARAMETER:
-                                       case PB_ERROR_UNSUPPORTED_MANDATORY_MSG:
-                                               DBG1(DBG_TNC, "received %s PB-TNC error '%N' "
-                                                                         "(offset %u bytes)",
-                                                                         fatal ? "fatal" : "non-fatal",
-                                                                         pb_tnc_error_code_names, error_code,
-                                                                         err_msg->get_offset(err_msg));
-                                               break;
-                                       case PB_ERROR_VERSION_NOT_SUPPORTED:
-                                               DBG1(DBG_TNC, "received %s PB-TNC error '%N' "
-                                                                         "caused by bad version 0x%02x",
-                                                                         fatal ? "fatal" : "non-fatal",
-                                                                         pb_tnc_error_code_names, error_code,
-                                                                         err_msg->get_bad_version(err_msg));
-                                               break;
-                                       case PB_ERROR_UNEXPECTED_BATCH_TYPE:
-                                       case PB_ERROR_LOCAL_ERROR:
-                                       default:
-                                               DBG1(DBG_TNC, "received %s PB-TNC error '%N'",
-                                                                         fatal ? "fatal" : "non-fatal",
-                                                                         pb_tnc_error_code_names, error_code);
-                                               break;
-                               }
-                       }
-                       else
-                       {
-                               DBG1(DBG_TNC, "received %s PB-TNC error (%u) "
-                                                         "with Vendor ID 0x%06x",
-                                                         fatal ? "fatal" : "non-fatal",
-                                                         error_code, vendor_id);
-                       }
-                       break;
-               }
-               case PB_MSG_LANGUAGE_PREFERENCE:
-               {
-                       pb_language_preference_msg_t *lang_msg;
-                       chunk_t lang;
-
-                       lang_msg = (pb_language_preference_msg_t*)msg;
-                       lang = lang_msg->get_language_preference(lang_msg);
-
-                       if (this->recs)
-                       {
-                               DBG2(DBG_TNC, "setting language preference to '%.*s'",
-                                        (int)lang.len, lang.ptr);
-                               this->recs->set_preferred_language(this->recs, lang);
-                       }
-                       break;
-               }
-               case PB_MSG_REASON_STRING:
-               {
-                       pb_reason_string_msg_t *reason_msg;
-                       chunk_t reason_string, language_code;
-
-                       reason_msg = (pb_reason_string_msg_t*)msg;
-                       reason_string = reason_msg->get_reason_string(reason_msg);
-                       language_code = reason_msg->get_language_code(reason_msg);
-                       DBG1(DBG_TNC, "reason string is '%.*s' [%.*s]",
-                                (int)reason_string.len, reason_string.ptr,
-                                (int)language_code.len, language_code.ptr);
-                       break;
-               }
-               default:
-                       break;
-       }
-}
-
-/**
- * Handle a single PB-TNC TCG standard message according to its type
- */
-static void handle_tcg_message(private_tnccs_20_t *this, pb_tnc_msg_t *msg)
-{
-       pen_type_t msg_type = msg->get_type(msg);
-
-       switch (msg_type.type)
-       {
-               case PB_TCG_MSG_PDP_REFERRAL:
-               {
-                       pb_pdp_referral_msg_t *pdp_msg;
-                       pen_type_t pdp_id_type;
-                       u_int8_t pdp_protocol;
-
-                       pdp_msg = (pb_pdp_referral_msg_t*)msg;
-                       pdp_id_type = pdp_msg->get_identifier_type(pdp_msg);
-
-                       if (pdp_id_type.vendor_id == PEN_TCG &&
-                               pdp_id_type.type == PB_PDP_ID_FQDN)
-                       {
-                               this->pdp_server = chunk_clone(pdp_msg->get_fqdn(pdp_msg,
-                                                                                &pdp_protocol, &this->pdp_port));
-                               if (pdp_protocol != 0)
-                               {
-                                       DBG1(DBG_TNC, "unsupported PDP transport protocol");
-                                       break;
-                               }
-                               DBG1(DBG_TNC, "PDP server '%.*s' is listening on port %u",
-                                                          this->pdp_server.len, this->pdp_server.ptr,
-                                                          this->pdp_port);
-                       }
-                       break;
-               }
-               default:
-                       break;
-       }
-}
-
-/**
- * Handle a single PB-TNC message according to its type
- */
-static void handle_message(private_tnccs_20_t *this, pb_tnc_msg_t *msg)
-{
-       pen_type_t msg_type = msg->get_type(msg);
-
-       switch (msg_type.vendor_id)
-       {
-               case PEN_IETF:
-                       handle_ietf_message(this, msg);
-                       break;
-               case PEN_TCG:
-                       handle_tcg_message(this, msg);
-                       break;
-               default:
-                       break;
-       }
-}
-
-/**
- *  Build a CRETRY or SRETRY batch
- */
-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_type == batch_retry_type)
-       {
-               /* retry batch has already been selected */
-               return;
-       }
-
-       change_batch_type(this, batch_retry_type);
-
-       if (this->is_server)
-       {
-               this->recs->clear_recommendation(this->recs);
-               tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
-                                                                                       TNC_CONNECTION_STATE_HANDSHAKE);
-       }
-}
-
 METHOD(tls_t, process, status_t,
        private_tnccs_20_t *this, void *buf, size_t buflen)
 {
        chunk_t data;
        pb_tnc_batch_t *batch;
-       pb_tnc_msg_t *msg;
-       enumerator_t *enumerator;
-       identification_t *pdp_server;
-       u_int16_t *pdp_port;
+       bool from_server, mutual;
        status_t status;
 
-       if (this->is_server && !this->connection_id)
+       /* On arrival of first PB-TNC batch create TNC server */
+       if (this->is_server && !this->tnc_server)
        {
-               this->connection_id = tnc->tnccs->create_connection(tnc->tnccs,
-                                                                       TNCCS_2_0, (tnccs_t*)this, _send_msg,
-                                                                       &this->request_handshake_retry,
-                                                                       this->max_msg_len, &this->recs);
-               if (!this->connection_id)
+               this->tnc_server = tnccs_20_server_create(&this->public, _send_msg,
+                                                                       this->max_batch_len, this->max_msg_len,
+                                                                       this->eap_transport);
+               if (!this->tnc_server)
                {
                        return FAILED;
                }
-               tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
-                                                                                       TNC_CONNECTION_STATE_CREATE);
-               tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
-                                                                                       TNC_CONNECTION_STATE_HANDSHAKE);
-
-               /* Send a PB-TNC TCG PDP Referral message if PDP is known */
-               pdp_server = (identification_t*)lib->get(lib, "pt-tls-server");
-               pdp_port = (u_int16_t*)lib->get(lib, "pt-tls-port");
-
-               if ((this->transport == TNC_IFT_EAP_1_1 ||
-                        this->transport == TNC_IFT_EAP_2_0) && pdp_server && pdp_port)
-               {
-                       msg = pb_pdp_referral_msg_create_from_fqdn(
-                                               pdp_server->get_encoding(pdp_server), *pdp_port);
-                       this->messages->insert_last(this->messages, msg);
-               }
-
+               this->tnccs_handler = this->tnc_server;
+               this->tnccs_handler->begin_handshake(this->tnccs_handler);
        }
 
        data = chunk_create(buf, buflen);
-       DBG1(DBG_TNC, "received TNCCS batch (%u bytes) for Connection ID %u",
-                                  data.len, this->connection_id);
+       DBG1(DBG_TNC, "received TNCCS batch (%u bytes)", data.len);
        DBG3(DBG_TNC, "%B", &data);
-       batch = pb_tnc_batch_create_from_data(this->is_server, data);
-       status = batch->process(batch, this->state_machine);
 
-       if (status != FAILED)
-       {
-               enumerator_t *enumerator;
-               pb_tnc_msg_t *msg;
-               pb_tnc_batch_type_t batch_type;
-               bool empty = TRUE;
+       /* Has a mutual connection been established? */
+       mutual = this->tnc_client && this->tnc_server;
 
-               batch_type = batch->get_type(batch);
+       /* Parse the header of the received PB-TNC batch */
+       batch = pb_tnc_batch_create_from_data(data);
+       status = batch->process_header(batch, !mutual, this->is_server,
+                                                                  &from_server);
+       this->to_server = mutual ? from_server : !this->is_server;
 
-               if (batch_type == PB_BATCH_CRETRY)
-               {
-                       /* Send an SRETRY batch in response */
-                       this->mutex->lock(this->mutex);
-                       build_retry_batch(this);
-                       this->mutex->unlock(this->mutex);
-               }
-               else if (batch_type == PB_BATCH_SRETRY)
-               {
-                       /* Restart the measurements */
-                       tnc->imcs->notify_connection_change(tnc->imcs,
-                       this->connection_id, TNC_CONNECTION_STATE_HANDSHAKE);
-                       this->send_msg = TRUE;
-                       tnc->imcs->begin_handshake(tnc->imcs, this->connection_id);
-                       this->send_msg = FALSE;
-               }
-
-               enumerator = batch->create_msg_enumerator(batch);
-               while (enumerator->enumerate(enumerator, &msg))
-               {
-                       handle_message(this, msg);
-                       empty = FALSE;
-               }
-               enumerator->destroy(enumerator);
-
-               /* received an empty CLOSE batch from PB-TNC client */
-               if (this->is_server && batch_type == PB_BATCH_CLOSE && empty)
-               {
-                       batch->destroy(batch);
-                       if (this->fatal_error)
-                       {
-                               DBG1(DBG_TNC, "a fatal PB-TNC error occurred, "
-                                                         "terminating connection");
-                               return FAILED;
-                       }
-                       else
-                       {
-                               return SUCCESS;
-                       }
-               }
-
-               this->send_msg = TRUE;
-               if (this->is_server)
-               {
-                       tnc->imvs->batch_ending(tnc->imvs, this->connection_id);
-               }
-               else
-               {
-                       tnc->imcs->batch_ending(tnc->imcs, this->connection_id);
-               }
-               this->send_msg = FALSE;
-       }
+       /* Set active TNCCS handler */
+       this->tnccs_handler = this->to_server ? this->tnc_client : this->tnc_server;    
+       DBG2(DBG_TNC, "TNC %s is handling the connection",
+                                  this->to_server ? "client" : "server");
 
-       switch (status)
+       if (status == SUCCESS)
        {
-               case FAILED:
-                       this->fatal_error = TRUE;
-                       this->mutex->lock(this->mutex);
-                       change_batch_type(this, PB_BATCH_CLOSE);
-                       this->mutex->unlock(this->mutex);
-                       /* fall through to add error messages to outbound batch */
-               case VERIFY_ERROR:
-                       enumerator = batch->create_error_enumerator(batch);
-                       while (enumerator->enumerate(enumerator, &msg))
-                       {
-                               this->mutex->lock(this->mutex);
-                               this->messages->insert_last(this->messages, msg->get_ref(msg));
-                               this->mutex->unlock(this->mutex);
-                       }
-                       enumerator->destroy(enumerator);
-                       break;
-               case SUCCESS:
-               default:
-                       break;
+               status = this->tnccs_handler->process(this->tnccs_handler, batch);
        }
-       batch->destroy(batch);
-
-       return NEED_MORE;
-}
-
-/**
- *  Build a RESULT batch if a final recommendation is available
- */
-static void check_and_build_recommendation(private_tnccs_20_t *this)
-{
-       TNC_IMV_Action_Recommendation rec;
-       TNC_IMV_Evaluation_Result eval;
-       TNC_ConnectionState state;
-       TNC_IMVID id;
-       chunk_t reason, language;
-       enumerator_t *enumerator;
-       pb_tnc_msg_t *msg;
-       pb_access_recommendation_code_t pb_rec;
-
-       if (!this->recs->have_recommendation(this->recs, &rec, &eval))
+       if (status == VERIFY_ERROR)
        {
-               tnc->imvs->solicit_recommendation(tnc->imvs, this->connection_id);
+               this->tnccs_handler->handle_errors(this->tnccs_handler, batch);
+               status = NEED_MORE;
        }
-       if (this->recs->have_recommendation(this->recs, &rec, &eval))
-       {
-               this->batch_type = PB_BATCH_RESULT;
-
-               msg = pb_assessment_result_msg_create(eval);
-               this->messages->insert_last(this->messages, msg);
-
-               /**
-                * Map IMV Action Recommendation codes to PB Access Recommendation codes
-                * and communicate Access Recommendation to IMVs
-                */
-               switch (rec)
-               {
-                       case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
-                               state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
-                               pb_rec = PB_REC_ACCESS_ALLOWED;
-                               break;
-                       case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
-                               state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
-                               pb_rec = PB_REC_QUARANTINED;
-                               break;
-                       case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
-                       case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
-                       default:
-                               state = TNC_CONNECTION_STATE_ACCESS_NONE;
-                               pb_rec = PB_REC_ACCESS_DENIED;
-               }
-               tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
-                                                                                       state);
-
-               msg = pb_access_recommendation_msg_create(pb_rec);
-               this->messages->insert_last(this->messages, msg);
+       batch->destroy(batch);
 
-               enumerator = this->recs->create_reason_enumerator(this->recs);
-               while (enumerator->enumerate(enumerator, &id, &reason, &language))
-               {
-                       msg = pb_reason_string_msg_create(reason, language);
-                       this->messages->insert_last(this->messages, msg);
-               }
-               enumerator->destroy(enumerator);
-       }
+       return status;
 }
 
 METHOD(tls_t, build, status_t,
        private_tnccs_20_t *this, void *buf, size_t *buflen, size_t *msglen)
 {
        status_t status;
-       pb_tnc_state_t state;
-
-       /* Initialize the connection */
-       if (!this->is_server && !this->connection_id)
-       {
-               pb_tnc_msg_t *msg;
-               char *pref_lang;
-
-               this->connection_id = tnc->tnccs->create_connection(tnc->tnccs,
-                                                                               TNCCS_2_0, (tnccs_t*)this, _send_msg,
-                                                                               &this->request_handshake_retry,
-                                                                               this->max_msg_len, NULL);
-               if (!this->connection_id)
-               {
-                       return FAILED;
-               }
-
-               /* Create PB-TNC Language Preference message */
-               pref_lang = tnc->imcs->get_preferred_language(tnc->imcs);
-               msg = pb_language_preference_msg_create(chunk_create(pref_lang,
-                                                                                                       strlen(pref_lang)));
-               this->mutex->lock(this->mutex);
-               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,
-                                                                                       TNC_CONNECTION_STATE_CREATE);
-               tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
-                                                                                       TNC_CONNECTION_STATE_HANDSHAKE);
-               this->send_msg = TRUE;
-               tnc->imcs->begin_handshake(tnc->imcs, this->connection_id);
-               this->send_msg = FALSE;
-       }
-
-       state = this->state_machine->get_state(this->state_machine);
-
-       if (this->fatal_error && state == PB_STATE_END)
-       {
-               DBG1(DBG_TNC, "a fatal PB-TNC error occurred, terminating connection");
-               return FAILED;
-       }
-
-       /* Do not allow any asynchronous IMCs or IMVs to add additional messages */
-       this->mutex->lock(this->mutex);
-
-       if (this->request_handshake_retry)
-       {
-               if (state != PB_STATE_INIT)
-               {
-                       build_retry_batch(this);
-               }
-
-               /* Reset the flag for the next handshake retry request */
-               this->request_handshake_retry = FALSE;
-       }
-
-       if (this->is_server &&  state == PB_STATE_SERVER_WORKING &&
-               this->recs->have_recommendation(this->recs, NULL, NULL))
-       {
-               check_and_build_recommendation(this);
-       }
 
-       if (this->batch_type == PB_BATCH_NONE)
+       if (this->to_server)
        {
-               if (this->is_server)
-               {
-                       if (state == PB_STATE_SERVER_WORKING)
-                       {
-                               if (this->state_machine->get_empty_cdata(this->state_machine))
-                               {
-                                       check_and_build_recommendation(this);
-                               }
-                               else
-                               {
-                                       DBG2(DBG_TNC, "no recommendation available yet, "
-                                                                 "sending empty PB-TNC SDATA batch");
-                                       this->batch_type = PB_BATCH_SDATA;
-                               }
-                       }
-               }
-               else
+               /* Before sending the first PB-TNC batch create TNC client */
+               if (!this->tnc_client)
                {
-                       switch (state)
+                       DBG2(DBG_TNC, "TNC client is handling the connection");
+                       this->tnc_client = tnccs_20_client_create(&this->public, _send_msg,
+                                                                                                         this->max_batch_len,
+                                                                                                         this->max_msg_len);
+                       if (!this->tnc_client)
                        {
-                               case PB_STATE_CLIENT_WORKING:
-                                       DBG2(DBG_TNC, "no client data to send, "
-                                                                 "sending empty PB-TNC CDATA batch");
-                                       this->batch_type = PB_BATCH_CDATA;
-                                       break;
-                               case PB_STATE_DECIDED:
-                                       /**
-                                        * In the DECIDED state and if no CRETRY is under way,
-                                        * a PB-TNC client replies with an empty CLOSE batch.
-                                        */
-                                       this->batch_type = PB_BATCH_CLOSE;
-                                       break;
-                               default:
-                                       break;
+                               status = FAILED;
                        }
+                       this->tnccs_handler = this->tnc_client;
+                       this->tnccs_handler->begin_handshake(this->tnccs_handler);              
                }
        }
-
-       if (this->batch_type != PB_BATCH_NONE)
+       else
        {
-               pb_tnc_batch_t *batch;
-               pb_tnc_msg_t *msg;
-               chunk_t data;
-               int msg_count;
-               enumerator_t *enumerator;
-
-               if (this->state_machine->send_batch(this->state_machine, this->batch_type))
+               /* Before sending the first PB-TNC batch create TNC server */
+               if (!this->tnc_server)
                {
-                       batch = pb_tnc_batch_create(this->is_server, this->batch_type,
-                                                                               min(this->max_batch_len, *buflen));
-
-                       enumerator = this->messages->create_enumerator(this->messages);
-                       while (enumerator->enumerate(enumerator, &msg))
-                       {
-                               if (batch->add_msg(batch, msg))
-                               {
-                                       this->messages->remove_at(this->messages, enumerator);
-                               }
-                               else
-                               {
-                                       break;
-                               }
-                       }
-                       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, 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);
-
-                       msg_count = this->messages->get_count(this->messages);
-                       if (msg_count)
-                       {
-                               DBG2(DBG_TNC, "queued %d PB-TNC message%s for next %N batch",
-                                        msg_count, (msg_count == 1) ? "" : "s",
-                                        pb_tnc_batch_type_names, this->batch_type);
-                       }
-                       else
+                       DBG2(DBG_TNC, "TNC server is handling the connection");
+                       this->tnc_server = tnccs_20_server_create(&this->public, _send_msg,
+                                                                               this->max_batch_len, this->max_msg_len,
+                                                                               this->eap_transport);
+                       if (!this->tnc_server)
                        {
-                               this->batch_type = PB_BATCH_NONE;
+                               status = FAILED;
                        }
-
-                       status = ALREADY_DONE;
-               }
-               else
-               {
-                       change_batch_type(this, PB_BATCH_NONE);
-                       status = INVALID_STATE;
+                       this->tnccs_handler = this->tnc_server;
+                       this->tnccs_handler->begin_handshake(this->tnccs_handler);              
                }
        }
-       else
-       {
-               DBG1(DBG_TNC, "no PB-TNC batch to send");
-               status = INVALID_STATE;
-       }
-       this->mutex->unlock(this->mutex);
-
+       status = this->tnccs_handler->build(this->tnccs_handler, buf, buflen, msglen);
+       
        return status;
 }
 
@@ -961,14 +303,17 @@ METHOD(tls_t, is_complete, bool,
        TNC_IMV_Action_Recommendation rec;
        TNC_IMV_Evaluation_Result eval;
 
-       if (this->recs && this->recs->have_recommendation(this->recs, &rec, &eval))
+       if (this->tnc_server)
        {
-               return this->callback ? this->callback(rec, eval) : TRUE;
-       }
-       else
-       {
-               return FALSE;
+               tnccs_20_server_t *tnc_server;
+
+               tnc_server = (tnccs_20_server_t*)this->tnc_server;
+               if (tnc_server->have_recommendation(tnc_server, &rec, &eval))
+               {
+                       return this->callback ? this->callback(rec, eval) : TRUE;
+               }
        }
+       return FALSE;
 }
 
 METHOD(tls_t, get_eap_msk, chunk_t,
@@ -982,17 +327,12 @@ METHOD(tls_t, destroy, void,
 {
        if (ref_put(&this->ref))
        {
-               tnc->tnccs->remove_connection(tnc->tnccs, this->connection_id,
-                                                                                                 this->is_server);
+               DESTROY_IF(this->tnc_server);
+               DESTROY_IF(this->tnc_client);
                this->server_id->destroy(this->server_id);
                this->peer_id->destroy(this->peer_id);
                this->server_ip->destroy(this->server_ip);
                this->peer_ip->destroy(this->peer_ip);
-               this->state_machine->destroy(this->state_machine);
-               this->mutex->destroy(this->mutex);
-               this->messages->destroy_offset(this->messages,
-                                                                          offsetof(pb_tnc_msg_t, destroy));
-               free(this->pdp_server.ptr);
                free(this);
        }
 }
@@ -1036,9 +376,19 @@ METHOD(tnccs_t, set_auth_type, void,
 METHOD(tnccs_t, get_pdp_server, chunk_t,
        private_tnccs_20_t *this, u_int16_t *port)
 {
-       *port = this->pdp_port;
+       if (this->tnc_client)
+       {
+               tnccs_20_client_t *tnc_client;
+
+               tnc_client = (tnccs_20_client_t*)this->tnc_client;
 
-       return this->pdp_server;
+               return tnc_client->get_pdp_server(tnc_client, port);
+       }
+       else
+       {
+               *port = 0;
+               return chunk_empty;
+       }
 }
 
 METHOD(tnccs_t, get_ref, tnccs_t*,
@@ -1114,15 +464,15 @@ tnccs_t* tnccs_20_create(bool is_server, identification_t *server_id,
                        .get_ref = _get_ref,
                },
                .is_server = is_server,
+               .to_server = !is_server,
                .server_id = server_id->clone(server_id),
                .peer_id = peer_id->clone(peer_id),
                .server_ip = server_ip->clone(server_ip),
                .peer_ip = peer_ip->clone(peer_ip),
                .transport = transport,
+               .eap_transport = transport == TNC_IFT_EAP_1_1 ||
+                                                transport == TNC_IFT_EAP_2_0,
                .callback = cb,
-               .state_machine = pb_tnc_state_machine_create(is_server),
-               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
-               .messages = linked_list_create(),
                .max_batch_len = max_batch_size,
                .max_msg_len = max_message_size,
                .ref = 1,
diff --git a/src/libtnccs/plugins/tnccs_20/tnccs_20_client.c b/src/libtnccs/plugins/tnccs_20/tnccs_20_client.c
new file mode 100644 (file)
index 0000000..4401142
--- /dev/null
@@ -0,0 +1,726 @@
+/*
+ * Copyright (C) 2015 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 <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 "tnccs_20_client.h"
+#include "messages/pb_tnc_msg.h"
+#include "messages/ietf/pb_pa_msg.h"
+#include "messages/ietf/pb_error_msg.h"
+#include "messages/ietf/pb_assessment_result_msg.h"
+#include "messages/ietf/pb_access_recommendation_msg.h"
+#include "messages/ietf/pb_remediation_parameters_msg.h"
+#include "messages/ietf/pb_reason_string_msg.h"
+#include "messages/ietf/pb_language_preference_msg.h"
+#include "messages/tcg/pb_pdp_referral_msg.h"
+#include "state_machine/pb_tnc_state_machine.h"
+
+#include <tncif_names.h>
+#include <tncif_pa_subtypes.h>
+
+#include <tnc/tnc.h>
+#include <tnc/tnccs/tnccs_manager.h>
+#include <tnc/imc/imc_manager.h>
+
+#include <threading/mutex.h>
+#include <utils/debug.h>
+#include <collections/linked_list.h>
+#include <pen/pen.h>
+
+typedef struct private_tnccs_20_client_t private_tnccs_20_client_t;
+
+/**
+ * Private data of a tnccs_20_client_t object.
+ */
+struct private_tnccs_20_client_t {
+
+       /**
+        * Public tnccs_20_client_t interface.
+        */
+       tnccs_20_client_t public;
+
+       /**
+        * PB-TNC State Machine
+        */
+       pb_tnc_state_machine_t *state_machine;
+
+       /**
+        * Connection ID assigned to this TNCCS connection
+        */
+       TNC_ConnectionID connection_id;
+
+       /**
+        * PB-TNC messages to be sent
+        */
+       linked_list_t *messages;
+
+       /**
+        * Type of PB-TNC batch being constructed
+        */
+       pb_tnc_batch_type_t batch_type;
+
+       /**
+        * Maximum PB-TNC batch size
+        */
+       size_t max_batch_len;
+
+       /**
+        * Mutex locking the batch in construction
+        */
+       mutex_t *mutex;
+
+       /**
+        * Flag set while processing
+        */
+       bool fatal_error;
+
+       /**
+        * Flag set by IMC RequestHandshakeRetry() function
+        */
+       bool request_handshake_retry;
+
+       /**
+         * SendMessage() by IMC only allowed if flag is set
+         */
+       bool send_msg;
+
+       /**
+        * PDP server FQDN
+        */
+       chunk_t pdp_server;
+
+       /**
+        * PDP server port
+        */
+       u_int16_t pdp_port;
+
+};
+
+/**
+ * The following function is shared with the tnccs_20_server class
+ */
+void tnccs_20_handle_ietf_error_msg(pb_tnc_msg_t *msg, bool *fatal_error)
+{
+       pb_error_msg_t *err_msg;
+       u_int32_t vendor_id;
+       u_int16_t error_code;
+       bool fatal;
+
+       err_msg = (pb_error_msg_t*)msg;
+       fatal = err_msg->get_fatal_flag(err_msg);
+       vendor_id = err_msg->get_vendor_id(err_msg);
+       error_code = err_msg->get_error_code(err_msg);
+
+       if (fatal)
+       {
+               *fatal_error = TRUE;
+       }
+
+       if (vendor_id == PEN_IETF)
+       {
+               switch (error_code)
+               {
+                       case PB_ERROR_INVALID_PARAMETER:
+                       case PB_ERROR_UNSUPPORTED_MANDATORY_MSG:
+                               DBG1(DBG_TNC, "received %s PB-TNC error '%N' (offset %u bytes)",
+                                                         fatal ? "fatal" : "non-fatal",
+                                                         pb_tnc_error_code_names, error_code,
+                                                         err_msg->get_offset(err_msg));
+                               break;
+                       case PB_ERROR_VERSION_NOT_SUPPORTED:
+                               DBG1(DBG_TNC, "received %s PB-TNC error '%N' "
+                                                         "caused by bad version 0x%02x",
+                                                         fatal ? "fatal" : "non-fatal",
+                                                         pb_tnc_error_code_names, error_code,
+                                                         err_msg->get_bad_version(err_msg));
+                               break;
+                       case PB_ERROR_UNEXPECTED_BATCH_TYPE:
+                       case PB_ERROR_LOCAL_ERROR:
+                       default:
+                               DBG1(DBG_TNC, "received %s PB-TNC error '%N'",
+                                                         fatal ? "fatal" : "non-fatal",
+                                                         pb_tnc_error_code_names, error_code);
+                               break;
+               }
+       }
+       else
+       {
+               DBG1(DBG_TNC, "received %s PB-TNC error (%u) with Vendor ID 0x%06x",
+                                         fatal ? "fatal" : "non-fatal", error_code, vendor_id);
+       }
+}
+
+/**
+ * If the batch type changes then delete all accumulated PB-TNC messages
+ */
+static void change_batch_type(private_tnccs_20_client_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;
+       }
+}
+
+/**
+ * Handle a single PB-TNC IETF standard message according to its type
+ */
+static void handle_ietf_message(private_tnccs_20_client_t *this, pb_tnc_msg_t *msg)
+{
+       pen_type_t msg_type = msg->get_type(msg);
+
+       switch (msg_type.type)
+       {
+               case PB_MSG_EXPERIMENTAL:
+                       /* nothing to do */
+                       break;
+               case PB_MSG_PA:
+               {
+                       pb_pa_msg_t *pa_msg;
+                       pen_type_t msg_subtype;
+                       u_int16_t imc_id, imv_id;
+                       chunk_t msg_body;
+                       bool excl;
+                       enum_name_t *pa_subtype_names;
+
+                       pa_msg = (pb_pa_msg_t*)msg;
+                       msg_subtype = pa_msg->get_subtype(pa_msg);
+                       msg_body = pa_msg->get_body(pa_msg);
+                       imc_id = pa_msg->get_collector_id(pa_msg);
+                       imv_id = pa_msg->get_validator_id(pa_msg);
+                       excl = pa_msg->get_exclusive_flag(pa_msg);
+
+                       pa_subtype_names = get_pa_subtype_names(msg_subtype.vendor_id);
+                       if (pa_subtype_names)
+                       {
+                               DBG2(DBG_TNC, "handling PB-PA message type '%N/%N' 0x%06x/0x%08x",
+                                        pen_names, msg_subtype.vendor_id, pa_subtype_names,
+                                        msg_subtype.type, msg_subtype.vendor_id, msg_subtype.type);
+                       }
+                       else
+                       {
+                               DBG2(DBG_TNC, "handling PB-PA message type '%N' 0x%06x/0x%08x",
+                                        pen_names, msg_subtype.vendor_id, msg_subtype.vendor_id,
+                                        msg_subtype.type);
+                       }
+                       this->send_msg = TRUE;
+                       tnc->imcs->receive_message(tnc->imcs, this->connection_id,
+                                                                          excl, msg_body.ptr, msg_body.len,
+                                                                          msg_subtype.vendor_id,
+                                                                          msg_subtype.type, imv_id, imc_id);
+                       this->send_msg = FALSE;
+                       break;
+               }
+               case PB_MSG_ASSESSMENT_RESULT:
+               {
+                       pb_assessment_result_msg_t *assess_msg;
+                       u_int32_t result;
+
+                       assess_msg = (pb_assessment_result_msg_t*)msg;
+                       result = assess_msg->get_assessment_result(assess_msg);
+                       DBG1(DBG_TNC, "PB-TNC assessment result is '%N'",
+                                TNC_IMV_Evaluation_Result_names, result);
+                       break;
+               }
+               case PB_MSG_ACCESS_RECOMMENDATION:
+               {
+                       pb_access_recommendation_msg_t *rec_msg;
+                       pb_access_recommendation_code_t rec;
+                       TNC_ConnectionState state = TNC_CONNECTION_STATE_ACCESS_NONE;
+
+                       rec_msg = (pb_access_recommendation_msg_t*)msg;
+                       rec = rec_msg->get_access_recommendation(rec_msg);
+                       DBG1(DBG_TNC, "PB-TNC access recommendation is '%N'",
+                                                  pb_access_recommendation_code_names, rec);
+                       switch (rec)
+                       {
+                               case PB_REC_ACCESS_ALLOWED:
+                                       state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
+                                       break;
+                               case PB_REC_ACCESS_DENIED:
+                                       state = TNC_CONNECTION_STATE_ACCESS_NONE;
+                                       break;
+                               case PB_REC_QUARANTINED:
+                                       state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
+                       }
+                       tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
+                                                                                               state);
+                       break;
+               }
+               case PB_MSG_REMEDIATION_PARAMETERS:
+               {
+                       pb_remediation_parameters_msg_t *rem_msg;
+                       pen_type_t parameters_type;
+                       chunk_t parameters, string, lang_code;
+
+                       rem_msg = (pb_remediation_parameters_msg_t*)msg;
+                       parameters_type = rem_msg->get_parameters_type(rem_msg);
+                       parameters = rem_msg->get_parameters(rem_msg);
+
+                       if (parameters_type.vendor_id == PEN_IETF)
+                       {
+                               switch (parameters_type.type)
+                               {
+                                       case PB_REMEDIATION_URI:
+                                               DBG1(DBG_TNC, "remediation uri: %.*s",
+                                                                          parameters.len, parameters.ptr);
+                                               break;
+                                       case PB_REMEDIATION_STRING:
+                                               string = rem_msg->get_string(rem_msg, &lang_code);
+                                               DBG1(DBG_TNC, "remediation string: [%.*s]\n%.*s",
+                                                                          lang_code.len, lang_code.ptr,
+                                                                          string.len, string.ptr);
+                                               break;
+                                       default:
+                                               DBG1(DBG_TNC, "remediation parameters: %B", &parameters);
+                               }
+                       }
+                       else
+                       {
+                               DBG1(DBG_TNC, "remediation parameters: %B", &parameters);
+                       }
+                       break;
+               }
+               case PB_MSG_ERROR:
+                       tnccs_20_handle_ietf_error_msg(msg, &this->fatal_error);
+                       break;
+               case PB_MSG_REASON_STRING:
+               {
+                       pb_reason_string_msg_t *reason_msg;
+                       chunk_t reason_string, language_code;
+
+                       reason_msg = (pb_reason_string_msg_t*)msg;
+                       reason_string = reason_msg->get_reason_string(reason_msg);
+                       language_code = reason_msg->get_language_code(reason_msg);
+                       DBG1(DBG_TNC, "reason string is '%.*s' [%.*s]",
+                                (int)reason_string.len, reason_string.ptr,
+                                (int)language_code.len, language_code.ptr);
+                       break;
+               }
+               default:
+                       break;
+       }
+}
+
+/**
+ * Handle a single PB-TNC TCG standard message according to its type
+ */
+static void handle_tcg_message(private_tnccs_20_client_t *this, pb_tnc_msg_t *msg)
+{
+       pen_type_t msg_type = msg->get_type(msg);
+
+       switch (msg_type.type)
+       {
+               case PB_TCG_MSG_PDP_REFERRAL:
+               {
+                       pb_pdp_referral_msg_t *pdp_msg;
+                       pen_type_t pdp_id_type;
+                       u_int8_t pdp_protocol;
+
+                       pdp_msg = (pb_pdp_referral_msg_t*)msg;
+                       pdp_id_type = pdp_msg->get_identifier_type(pdp_msg);
+
+                       if (pdp_id_type.vendor_id == PEN_TCG &&
+                               pdp_id_type.type == PB_PDP_ID_FQDN)
+                       {
+                               this->pdp_server = chunk_clone(pdp_msg->get_fqdn(pdp_msg,
+                                                                                &pdp_protocol, &this->pdp_port));
+                               if (pdp_protocol != 0)
+                               {
+                                       DBG1(DBG_TNC, "unsupported PDP transport protocol");
+                                       break;
+                               }
+                               DBG1(DBG_TNC, "PDP server '%.*s' is listening on port %u",
+                                                          this->pdp_server.len, this->pdp_server.ptr,
+                                                          this->pdp_port);
+                       }
+                       break;
+               }
+               default:
+                       break;
+       }
+}
+
+/**
+ * Handle a single PB-TNC message according to its type
+ */
+static void handle_message(private_tnccs_20_client_t *this, pb_tnc_msg_t *msg)
+{
+       pen_type_t msg_type = msg->get_type(msg);
+
+       switch (msg_type.vendor_id)
+       {
+               case PEN_IETF:
+                       handle_ietf_message(this, msg);
+                       break;
+               case PEN_TCG:
+                       handle_tcg_message(this, msg);
+                       break;
+               default:
+                       break;
+       }
+}
+
+/**
+ *  Build a CRETRY batch
+ */
+static void build_retry_batch(private_tnccs_20_client_t *this)
+{
+       if (this->batch_type == PB_BATCH_CRETRY)
+       {
+               /* retry batch has already been selected */
+               return;
+       }
+       change_batch_type(this, PB_BATCH_CRETRY);
+}
+
+METHOD(tnccs_20_handler_t, process, status_t,
+       private_tnccs_20_client_t *this, pb_tnc_batch_t *batch)
+{
+       pb_tnc_msg_t *msg;
+       pb_tnc_batch_type_t batch_type;
+       enumerator_t *enumerator;
+       status_t status;
+
+       batch_type = batch->get_type(batch);
+
+       DBG1(DBG_TNC, "processing PB-TNC %N batch for Connection ID %d",
+                pb_tnc_batch_type_names, batch_type, this->connection_id);
+
+       status = batch->process(batch, this->state_machine);
+
+       if (status != FAILED)
+       {
+               enumerator_t *enumerator;
+               pb_tnc_msg_t *msg;
+               bool empty = TRUE;
+
+               if (batch_type == PB_BATCH_SRETRY)
+               {
+                       /* Restart the measurements */
+                       tnc->imcs->notify_connection_change(tnc->imcs,
+                                               this->connection_id, TNC_CONNECTION_STATE_HANDSHAKE);
+                       this->send_msg = TRUE;
+                       tnc->imcs->begin_handshake(tnc->imcs, this->connection_id);
+                       this->send_msg = FALSE;
+               }
+
+               enumerator = batch->create_msg_enumerator(batch);
+               while (enumerator->enumerate(enumerator, &msg))
+               {
+                       handle_message(this, msg);
+                       empty = FALSE;
+               }
+               enumerator->destroy(enumerator);
+
+               /* received a CLOSE batch from PB-TNC server */
+               if (batch_type == PB_BATCH_CLOSE)
+               {
+                       return empty ? SUCCESS : FAILED;
+               }
+
+               this->send_msg = TRUE;
+               tnc->imcs->batch_ending(tnc->imcs, this->connection_id);
+               this->send_msg = FALSE;
+       }
+
+       switch (status)
+       {
+               case FAILED:
+                       this->fatal_error = TRUE;
+                       this->mutex->lock(this->mutex);
+                       change_batch_type(this, PB_BATCH_CLOSE);
+                       this->mutex->unlock(this->mutex);
+                       status = VERIFY_ERROR;
+                       /* fall through to add error messages to outbound batch */
+               case VERIFY_ERROR:
+                       enumerator = batch->create_error_enumerator(batch);
+                       while (enumerator->enumerate(enumerator, &msg))
+                       {
+                               this->mutex->lock(this->mutex);
+                               this->messages->insert_last(this->messages, msg->get_ref(msg));
+                               this->mutex->unlock(this->mutex);
+                       }
+                       enumerator->destroy(enumerator);
+                       break;
+               case SUCCESS:
+               default:
+                       status = NEED_MORE;
+                       break;
+       }
+
+       return status;
+}
+
+METHOD(tnccs_20_handler_t, build, status_t,
+       private_tnccs_20_client_t *this, void *buf, size_t *buflen, size_t *msglen)
+{
+       status_t status;
+       pb_tnc_state_t state;
+
+       state = this->state_machine->get_state(this->state_machine);
+
+       if (this->fatal_error && state == PB_STATE_END)
+       {
+               DBG1(DBG_TNC, "a fatal PB-TNC error occurred, terminating connection");
+               return FAILED;
+       }
+
+       /* Do not allow any asynchronous IMCs to add additional messages */
+       this->mutex->lock(this->mutex);
+
+       if (this->request_handshake_retry)
+       {
+               if (state != PB_STATE_INIT)
+               {
+                       build_retry_batch(this);
+               }
+
+               /* Reset the flag for the next handshake retry request */
+               this->request_handshake_retry = FALSE;
+       }
+
+       if (this->batch_type == PB_BATCH_NONE)
+       {
+               switch (state)
+               {
+                       case PB_STATE_CLIENT_WORKING:
+                               DBG2(DBG_TNC, "no client data to send, "
+                                                         "sending empty PB-TNC CDATA batch");
+                               this->batch_type = PB_BATCH_CDATA;
+                               break;
+                       case PB_STATE_DECIDED:
+                               /**
+                                * In the DECIDED state and if no CRETRY is under way,
+                                * a PB-TNC client replies with an empty CLOSE batch.
+                                */
+                               this->batch_type = PB_BATCH_CLOSE;
+                               break;
+                       default:
+                               break;
+               }
+       }
+
+       if (this->batch_type != PB_BATCH_NONE)
+       {
+               pb_tnc_batch_t *batch;
+               pb_tnc_msg_t *msg;
+               chunk_t data;
+               int msg_count;
+               enumerator_t *enumerator;
+
+               if (this->state_machine->send_batch(this->state_machine, this->batch_type))
+               {
+                       batch = pb_tnc_batch_create(FALSE, this->batch_type,
+                                                                               min(this->max_batch_len, *buflen));
+
+                       enumerator = this->messages->create_enumerator(this->messages);
+                       while (enumerator->enumerate(enumerator, &msg))
+                       {
+                               if (batch->add_msg(batch, msg))
+                               {
+                                       this->messages->remove_at(this->messages, enumerator);
+                               }
+                               else
+                               {
+                                       break;
+                               }
+                       }
+                       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, 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);
+
+                       msg_count = this->messages->get_count(this->messages);
+                       if (msg_count)
+                       {
+                               DBG2(DBG_TNC, "queued %d PB-TNC message%s for next %N batch",
+                                        msg_count, (msg_count == 1) ? "" : "s",
+                                        pb_tnc_batch_type_names, this->batch_type);
+                       }
+                       else
+                       {
+                               this->batch_type = PB_BATCH_NONE;
+                       }
+
+                       status = ALREADY_DONE;
+               }
+               else
+               {
+                       change_batch_type(this, PB_BATCH_NONE);
+                       status = INVALID_STATE;
+               }
+       }
+       else
+       {
+               DBG1(DBG_TNC, "no PB-TNC batch to send");
+               status = INVALID_STATE;
+       }
+       this->mutex->unlock(this->mutex);
+
+       return status;
+}
+
+METHOD(tnccs_20_handler_t, begin_handshake, void,
+       private_tnccs_20_client_t *this)
+{
+       pb_tnc_msg_t *msg;
+       char *pref_lang;
+
+       tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
+                                                                               TNC_CONNECTION_STATE_HANDSHAKE);
+
+       /* Create PB-TNC Language Preference message */
+       pref_lang = tnc->imcs->get_preferred_language(tnc->imcs);
+       msg = pb_language_preference_msg_create(chunk_create(pref_lang,
+                                                                                               strlen(pref_lang)));
+       this->mutex->lock(this->mutex);
+       this->messages->insert_last(this->messages, msg);
+       this->mutex->unlock(this->mutex);
+
+       this->send_msg = TRUE;
+       tnc->imcs->begin_handshake(tnc->imcs, this->connection_id);
+       this->send_msg = FALSE;
+}
+
+METHOD(tnccs_20_handler_t, get_send_flag, bool,
+       private_tnccs_20_client_t *this)
+{
+       return this->send_msg;
+}
+
+METHOD(tnccs_20_handler_t, add_msg, void,
+       private_tnccs_20_client_t *this, pb_tnc_msg_t *msg)
+{
+       /* adding PA message to CDATA batch only */
+       this->mutex->lock(this->mutex);
+       if (this->batch_type == PB_BATCH_NONE)
+       {
+               this->batch_type = PB_BATCH_CDATA;
+       }
+       if (this->batch_type == PB_BATCH_CDATA)
+       {
+               this->messages->insert_last(this->messages, msg);
+       }
+       else
+       {
+               msg->destroy(msg);
+       }
+       this->mutex->unlock(this->mutex);
+}
+
+METHOD(tnccs_20_handler_t, handle_errors, void,
+       private_tnccs_20_client_t *this, pb_tnc_batch_t *batch)
+{
+       pb_tnc_msg_t *msg;
+       enumerator_t *enumerator;
+
+       enumerator = batch->create_error_enumerator(batch);
+       while (enumerator->enumerate(enumerator, &msg))
+       {
+               this->mutex->lock(this->mutex);
+               this->messages->insert_last(this->messages, msg->get_ref(msg));
+               this->mutex->unlock(this->mutex);
+       }
+       enumerator->destroy(enumerator);
+}
+
+METHOD(tnccs_20_handler_t, destroy, void,
+       private_tnccs_20_client_t *this)
+{
+       if (this->connection_id)
+       {
+               tnc->tnccs->remove_connection(tnc->tnccs, this->connection_id, FALSE);
+       }
+       this->state_machine->destroy(this->state_machine);
+       this->mutex->destroy(this->mutex);
+       this->messages->destroy_offset(this->messages,
+                                                                  offsetof(pb_tnc_msg_t, destroy));
+       free(this->pdp_server.ptr);
+       free(this);
+}
+
+METHOD(tnccs_20_client_t, get_pdp_server, chunk_t,
+       private_tnccs_20_client_t *this, u_int16_t *port)
+{
+       *port = this->pdp_port;
+
+       return this->pdp_server;
+}
+
+/**
+ * See header
+ */
+tnccs_20_handler_t* tnccs_20_client_create(tnccs_t *tnccs,
+                                                                                  tnccs_send_message_t send_msg,
+                                                                                  size_t max_batch_len,
+                                                                                  size_t max_msg_len)
+{
+       private_tnccs_20_client_t *this;
+
+       INIT(this,
+               .public = {
+                       .handler = {
+                               .process = _process,
+                               .build = _build,
+                               .begin_handshake = _begin_handshake,
+                               .get_send_flag = _get_send_flag,
+                               .add_msg = _add_msg,
+                               .handle_errors = _handle_errors,
+                               .destroy = _destroy,
+                       },
+                       .get_pdp_server = _get_pdp_server,
+               },
+               .state_machine = pb_tnc_state_machine_create(FALSE),
+               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+               .messages = linked_list_create(),
+               .batch_type = PB_BATCH_CDATA,
+               .max_batch_len = max_batch_len,
+       );
+
+       this->connection_id = tnc->tnccs->create_connection(tnc->tnccs,
+                                                                                       TNCCS_2_0, tnccs, send_msg,
+                                                                                       &this->request_handshake_retry,
+                                                                                       max_msg_len, NULL);
+       if (!this->connection_id)
+       {
+               destroy(this);
+               return NULL;
+       }
+       tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
+                                                                               TNC_CONNECTION_STATE_CREATE);
+
+       return &this->public.handler;
+}
diff --git a/src/libtnccs/plugins/tnccs_20/tnccs_20_client.h b/src/libtnccs/plugins/tnccs_20/tnccs_20_client.h
new file mode 100644 (file)
index 0000000..daabd4e
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 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 <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 tnccs_20_client_h tnccs_20_client
+ * @{ @ingroup tnccs_20_client
+ */
+
+#ifndef TNCCS_20_CLIENT_H_
+#define TNCCS_20_CLIENT_H_
+
+#include <library.h>
+
+#include <tnc/tnccs/tnccs.h>
+
+#include "tnccs_20_handler.h"
+
+typedef struct tnccs_20_client_t tnccs_20_client_t;
+
+/**
+ * Interface for a TNC client
+ */
+struct tnccs_20_client_t {
+
+       /**
+        * IF-TNCCS 2.0 protocol handler interface
+        */
+       tnccs_20_handler_t handler;
+
+       /**
+        * Get PDP server information if available
+        *
+        * @param port                  PT-TLS port of the PDP server
+        * @return                              FQDN of PDP server
+        */
+       chunk_t (*get_pdp_server)(tnccs_20_client_t *this, u_int16_t *port);
+
+};
+
+/**
+ * Create an instance of the TNC IF-TNCCS 2.0 client-side protocol handler.
+ *
+ * @param tnccs                                TNC IF-TNCCS 2.0 stack
+ * @param send_msg                     TNF IF-TNCCS 2.0 send message callback function
+ * @param max_batch_len                Maximum PB-TNC batch size
+ * @param max_msg_len          Maximum PA-TNC message size
+ */
+tnccs_20_handler_t* tnccs_20_client_create(tnccs_t *tnccs,
+                                                                                  tnccs_send_message_t send_msg,
+                                                                                  size_t max_batch_len,
+                                                                                  size_t max_msg_len);
+
+#endif /** TNCCS_20_CLIENT_H_ @}*/
diff --git a/src/libtnccs/plugins/tnccs_20/tnccs_20_handler.h b/src/libtnccs/plugins/tnccs_20/tnccs_20_handler.h
new file mode 100644 (file)
index 0000000..9eb6421
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 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 <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 tnccs_20_handler_h tnccs_20_handler
+ * @{ @ingroup tnccs_20_handler
+ */
+
+#ifndef TNCCS_20_HANDLER_H_
+#define TNCCS_20_HANDLER_H_
+
+#include <library.h>
+
+#include "batch/pb_tnc_batch.h"
+#include "messages/pb_tnc_msg.h"
+
+typedef struct tnccs_20_handler_t tnccs_20_handler_t;
+
+/**
+ * Interface for an IF-TNCCS 2.0 protocol handler
+ */
+struct tnccs_20_handler_t {
+
+       /**
+        * Process content of received PB-TNC batch
+        *
+        * @param batch                 PB-TNC batch to be processed
+        * @return                              status
+        */
+       status_t (*process)(tnccs_20_handler_t *this, pb_tnc_batch_t *batch);
+
+       /**
+        * Build PB-TNC batch to be sent
+        *
+        * @param buf                   buffer to write PB-TNC batch to
+        * @param buflen                size of buffer, receives bytes written
+        * @param msglen                receives size of all PB-TNCH batch
+        * @return                              status
+        */
+       status_t (*build)(tnccs_20_handler_t *this, void *buf, size_t *buflen,
+                                                                                                                 size_t *msglen);
+
+       /**
+        * Put the IMCs or IMVs into the handshake state
+        */
+       void (*begin_handshake)(tnccs_20_handler_t *this);
+
+       /**
+        * Indicates if IMCs or IMVs are allowed to send PA-TNC messages
+        *
+        * @return                              TRUE if allowed to send
+        */
+       bool (*get_send_flag)(tnccs_20_handler_t *this);
+
+       /**
+        * Add a PB-PA message to the handler's message queue
+        *
+        * @param msg                   PB-PA message to be added
+        */
+       void (*add_msg)(tnccs_20_handler_t *this, pb_tnc_msg_t *msg);
+
+       /**
+        * Handle errors that occurred during PB-TNC batch header processing
+        *
+        * @param batch                 batch where a fatal error occured
+        */
+       void (*handle_errors)(tnccs_20_handler_t *this, pb_tnc_batch_t *batch);
+
+       /**
+        * Destroys a tnccs_20_handler_t object.
+        */
+       void (*destroy)(tnccs_20_handler_t *this);
+};
+
+#endif /** TNCCS_20_HANDLER_H_ @}*/
diff --git a/src/libtnccs/plugins/tnccs_20/tnccs_20_server.c b/src/libtnccs/plugins/tnccs_20/tnccs_20_server.c
new file mode 100644 (file)
index 0000000..90131b3
--- /dev/null
@@ -0,0 +1,640 @@
+/*
+ * Copyright (C) 2015 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 <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 "tnccs_20_server.h"
+#include "messages/pb_tnc_msg.h"
+#include "messages/ietf/pb_pa_msg.h"
+#include "messages/ietf/pb_error_msg.h"
+#include "messages/ietf/pb_assessment_result_msg.h"
+#include "messages/ietf/pb_access_recommendation_msg.h"
+#include "messages/ietf/pb_remediation_parameters_msg.h"
+#include "messages/ietf/pb_reason_string_msg.h"
+#include "messages/ietf/pb_language_preference_msg.h"
+#include "messages/tcg/pb_pdp_referral_msg.h"
+#include "state_machine/pb_tnc_state_machine.h"
+
+#include <tncif_names.h>
+#include <tncif_pa_subtypes.h>
+
+#include <tnc/tnc.h>
+#include <tnc/tnccs/tnccs_manager.h>
+#include <tnc/imv/imv_manager.h>
+
+#include <threading/mutex.h>
+#include <utils/debug.h>
+#include <collections/linked_list.h>
+#include <pen/pen.h>
+
+typedef struct private_tnccs_20_server_t private_tnccs_20_server_t;
+
+/**
+ * Private data of a tnccs_20_server_t object.
+ */
+struct private_tnccs_20_server_t {
+
+       /**
+        * Public tnccs_20_server_t interface.
+        */
+       tnccs_20_server_t public;
+
+       /**
+        * PB-TNC State Machine
+        */
+       pb_tnc_state_machine_t *state_machine;
+
+       /**
+        * Connection ID assigned to this TNCCS connection
+        */
+       TNC_ConnectionID connection_id;
+
+       /**
+        * PB-TNC messages to be sent
+        */
+       linked_list_t *messages;
+
+       /**
+        * Type of PB-TNC batch being constructed
+        */
+       pb_tnc_batch_type_t batch_type;
+
+       /**
+        * Maximum PB-TNC batch size
+        */
+       size_t max_batch_len;
+
+       /**
+        * Mutex locking the batch in construction
+        */
+       mutex_t *mutex;
+
+       /**
+        * Flag set while processing
+        */
+       bool fatal_error;
+
+       /**
+        * Flag set by IMC/IMV RequestHandshakeRetry() function
+        */
+       bool request_handshake_retry;
+
+       /**
+         * SendMessage() by IMV only allowed if flag is set
+         */
+       bool send_msg;
+
+       /**
+        * Set of IMV recommendations
+        */
+       recommendations_t *recs;
+
+       /**
+        *  TNC IF-T transport protocol for EAP methods
+        */
+       bool eap_transport;
+
+       /**
+        * PDP server FQDN
+        */
+       chunk_t pdp_server;
+
+       /**
+        * PDP server port
+        */
+       u_int16_t pdp_port;
+
+};
+
+
+extern void tnccs_20_handle_ietf_error_msg(pb_tnc_msg_t *msg, bool *fatal_error);
+
+/**
+ * If the batch type changes then delete all accumulated PB-TNC messages
+ */
+static void change_batch_type(private_tnccs_20_server_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;
+       }
+}
+
+/**
+ * Handle a single PB-TNC IETF standard message according to its type
+ */
+static void handle_ietf_message(private_tnccs_20_server_t *this, pb_tnc_msg_t *msg)
+{
+       pen_type_t msg_type = msg->get_type(msg);
+
+       switch (msg_type.type)
+       {
+               case PB_MSG_EXPERIMENTAL:
+                       /* nothing to do */
+                       break;
+               case PB_MSG_PA:
+               {
+                       pb_pa_msg_t *pa_msg;
+                       pen_type_t msg_subtype;
+                       u_int16_t imc_id, imv_id;
+                       chunk_t msg_body;
+                       bool excl;
+                       enum_name_t *pa_subtype_names;
+
+                       pa_msg = (pb_pa_msg_t*)msg;
+                       msg_subtype = pa_msg->get_subtype(pa_msg);
+                       msg_body = pa_msg->get_body(pa_msg);
+                       imc_id = pa_msg->get_collector_id(pa_msg);
+                       imv_id = pa_msg->get_validator_id(pa_msg);
+                       excl = pa_msg->get_exclusive_flag(pa_msg);
+
+                       pa_subtype_names = get_pa_subtype_names(msg_subtype.vendor_id);
+                       if (pa_subtype_names)
+                       {
+                               DBG2(DBG_TNC, "handling PB-PA message type '%N/%N' 0x%06x/0x%08x",
+                                        pen_names, msg_subtype.vendor_id, pa_subtype_names,
+                                        msg_subtype.type, msg_subtype.vendor_id, msg_subtype.type);
+                       }
+                       else
+                       {
+                               DBG2(DBG_TNC, "handling PB-PA message type '%N' 0x%06x/0x%08x",
+                                        pen_names, msg_subtype.vendor_id, msg_subtype.vendor_id,
+                                        msg_subtype.type);
+                       }
+                       this->send_msg = TRUE;
+                       tnc->imvs->receive_message(tnc->imvs, this->connection_id,
+                                                                          excl, msg_body.ptr, msg_body.len,
+                                                                          msg_subtype.vendor_id,
+                                                                          msg_subtype.type, imc_id, imv_id);
+                       this->send_msg = FALSE;
+                       break;
+               }
+               case PB_MSG_ERROR:
+                       tnccs_20_handle_ietf_error_msg(msg, &this->fatal_error);
+                       break;
+               case PB_MSG_LANGUAGE_PREFERENCE:
+               {
+                       pb_language_preference_msg_t *lang_msg;
+                       chunk_t lang;
+
+                       lang_msg = (pb_language_preference_msg_t*)msg;
+                       lang = lang_msg->get_language_preference(lang_msg);
+                       DBG2(DBG_TNC, "setting language preference to '%.*s'",
+                                (int)lang.len, lang.ptr);
+                       this->recs->set_preferred_language(this->recs, lang);
+                       break;
+               }
+               default:
+                       break;
+       }
+}
+
+/**
+ * Handle a single PB-TNC message according to its type
+ */
+static void handle_message(private_tnccs_20_server_t *this, pb_tnc_msg_t *msg)
+{
+       pen_type_t msg_type = msg->get_type(msg);
+
+       switch (msg_type.vendor_id)
+       {
+               case PEN_IETF:
+                       handle_ietf_message(this, msg);
+                       break;
+               default:
+                       break;
+       }
+}
+
+/**
+ *  Build an SRETRY batch
+ */
+static void build_retry_batch(private_tnccs_20_server_t *this)
+{
+       if (this->batch_type == PB_BATCH_SRETRY)
+       {
+               /* retry batch has already been selected */
+               return;
+       }
+       change_batch_type(this, PB_BATCH_SRETRY);
+
+       this->recs->clear_recommendation(this->recs);
+       tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
+                                                                               TNC_CONNECTION_STATE_HANDSHAKE);
+}
+
+METHOD(tnccs_20_handler_t, process, status_t,
+       private_tnccs_20_server_t *this, pb_tnc_batch_t *batch)
+{
+       pb_tnc_msg_t *msg;
+       pb_tnc_batch_type_t batch_type;
+       enumerator_t *enumerator;
+       status_t status;
+
+       batch_type = batch->get_type(batch);
+
+       DBG1(DBG_TNC, "processing PB-TNC %N batch for Connection ID %d",
+                pb_tnc_batch_type_names, batch_type, this->connection_id);
+
+       status = batch->process(batch, this->state_machine);
+       DBG2(DBG_TNC, "status after batch process: %N", status_names, status);
+
+       if (status != FAILED)
+       {
+               enumerator_t *enumerator;
+               pb_tnc_msg_t *msg;
+               bool empty = TRUE;
+
+               if (batch_type == PB_BATCH_CRETRY)
+               {
+                       /* Send an SRETRY batch in response */
+                       this->mutex->lock(this->mutex);
+                       build_retry_batch(this);
+                       this->mutex->unlock(this->mutex);
+               }
+
+               enumerator = batch->create_msg_enumerator(batch);
+               while (enumerator->enumerate(enumerator, &msg))
+               {
+                       handle_message(this, msg);
+                       empty = FALSE;
+               }
+               enumerator->destroy(enumerator);
+
+               /* received a CLOSE batch from PB-TNC client */
+               if (batch_type == PB_BATCH_CLOSE)
+               {
+                       return empty ? SUCCESS : FAILED;
+               }
+
+               this->send_msg = TRUE;
+               tnc->imvs->batch_ending(tnc->imvs, this->connection_id);
+               this->send_msg = FALSE;
+       }
+
+       switch (status)
+       {
+               case FAILED:
+                       this->fatal_error = TRUE;
+                       this->mutex->lock(this->mutex);
+                       change_batch_type(this, PB_BATCH_CLOSE);
+                       this->mutex->unlock(this->mutex);
+                       status = VERIFY_ERROR;
+                       /* fall through to add error messages to outbound batch */
+               case VERIFY_ERROR:
+                       enumerator = batch->create_error_enumerator(batch);
+                       while (enumerator->enumerate(enumerator, &msg))
+                       {
+                               this->mutex->lock(this->mutex);
+                               this->messages->insert_last(this->messages, msg->get_ref(msg));
+                               this->mutex->unlock(this->mutex);
+                       }
+                       enumerator->destroy(enumerator);
+                       break;
+               case SUCCESS:
+               default:
+                       status = NEED_MORE;
+                       break;
+       }
+
+       return status;
+}
+
+/**
+ *  Build a RESULT batch if a final recommendation is available
+ */
+static void check_and_build_recommendation(private_tnccs_20_server_t *this)
+{
+       TNC_IMV_Action_Recommendation rec;
+       TNC_IMV_Evaluation_Result eval;
+       TNC_ConnectionState state;
+       TNC_IMVID id;
+       chunk_t reason, language;
+       enumerator_t *enumerator;
+       pb_tnc_msg_t *msg;
+       pb_access_recommendation_code_t pb_rec;
+
+       if (!this->recs->have_recommendation(this->recs, &rec, &eval))
+       {
+               tnc->imvs->solicit_recommendation(tnc->imvs, this->connection_id);
+       }
+       if (this->recs->have_recommendation(this->recs, &rec, &eval))
+       {
+               this->batch_type = PB_BATCH_RESULT;
+
+               msg = pb_assessment_result_msg_create(eval);
+               this->messages->insert_last(this->messages, msg);
+
+               /**
+                * Map IMV Action Recommendation codes to PB Access Recommendation codes
+                * and communicate Access Recommendation to IMVs
+                */
+               switch (rec)
+               {
+                       case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
+                               state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
+                               pb_rec = PB_REC_ACCESS_ALLOWED;
+                               break;
+                       case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
+                               state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
+                               pb_rec = PB_REC_QUARANTINED;
+                               break;
+                       case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
+                       case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
+                       default:
+                               state = TNC_CONNECTION_STATE_ACCESS_NONE;
+                               pb_rec = PB_REC_ACCESS_DENIED;
+               }
+               tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
+                                                                                       state);
+
+               msg = pb_access_recommendation_msg_create(pb_rec);
+               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->messages->insert_last(this->messages, msg);
+               }
+               enumerator->destroy(enumerator);
+       }
+}
+
+METHOD(tnccs_20_handler_t, build, status_t,
+       private_tnccs_20_server_t *this, void *buf, size_t *buflen, size_t *msglen)
+{
+       status_t status;
+       pb_tnc_state_t state;
+
+       state = this->state_machine->get_state(this->state_machine);
+
+       if (this->fatal_error && state == PB_STATE_END)
+       {
+               DBG1(DBG_TNC, "a fatal PB-TNC error occurred, terminating connection");
+               return FAILED;
+       }
+
+       /* Do not allow any asynchronous IMVs to add additional messages */
+       this->mutex->lock(this->mutex);
+
+       if (this->request_handshake_retry)
+       {
+               if (state != PB_STATE_INIT)
+               {
+                       build_retry_batch(this);
+               }
+
+               /* Reset the flag for the next handshake retry request */
+               this->request_handshake_retry = FALSE;
+       }
+
+       if (state == PB_STATE_SERVER_WORKING &&
+               this->recs->have_recommendation(this->recs, NULL, NULL))
+       {
+               check_and_build_recommendation(this);
+       }
+
+       if (this->batch_type == PB_BATCH_NONE)
+       {
+               if (state == PB_STATE_SERVER_WORKING)
+               {
+                       if (this->state_machine->get_empty_cdata(this->state_machine))
+                       {
+                               check_and_build_recommendation(this);
+                       }
+                       else
+                       {
+                               DBG2(DBG_TNC, "no recommendation available yet, "
+                                                         "sending empty PB-TNC SDATA batch");
+                               this->batch_type = PB_BATCH_SDATA;
+                       }
+               }
+       }
+
+       if (this->batch_type != PB_BATCH_NONE)
+       {
+               pb_tnc_batch_t *batch;
+               pb_tnc_msg_t *msg;
+               chunk_t data;
+               int msg_count;
+               enumerator_t *enumerator;
+
+               if (this->state_machine->send_batch(this->state_machine, this->batch_type))
+               {
+                       batch = pb_tnc_batch_create(TRUE, this->batch_type,
+                                                                               min(this->max_batch_len, *buflen));
+
+                       enumerator = this->messages->create_enumerator(this->messages);
+                       while (enumerator->enumerate(enumerator, &msg))
+                       {
+                               if (batch->add_msg(batch, msg))
+                               {
+                                       this->messages->remove_at(this->messages, enumerator);
+                               }
+                               else
+                               {
+                                       break;
+                               }
+                       }
+                       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, 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);
+
+                       msg_count = this->messages->get_count(this->messages);
+                       if (msg_count)
+                       {
+                               DBG2(DBG_TNC, "queued %d PB-TNC message%s for next %N batch",
+                                        msg_count, (msg_count == 1) ? "" : "s",
+                                        pb_tnc_batch_type_names, this->batch_type);
+                       }
+                       else
+                       {
+                               this->batch_type = PB_BATCH_NONE;
+                       }
+
+                       status = ALREADY_DONE;
+               }
+               else
+               {
+                       change_batch_type(this, PB_BATCH_NONE);
+                       status = INVALID_STATE;
+               }
+       }
+       else
+       {
+               DBG1(DBG_TNC, "no PB-TNC batch to send");
+               status = INVALID_STATE;
+       }
+       this->mutex->unlock(this->mutex);
+
+       return status;
+}
+
+METHOD(tnccs_20_handler_t, begin_handshake, void,
+       private_tnccs_20_server_t *this)
+{
+       pb_tnc_msg_t *msg;
+       identification_t *pdp_server;
+       u_int16_t *pdp_port;
+
+       tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
+                                                                               TNC_CONNECTION_STATE_HANDSHAKE);
+
+       /* Send a PB-TNC TCG PDP Referral message if PDP is known */
+       pdp_server = (identification_t*)lib->get(lib, "pt-tls-server");
+       pdp_port = (u_int16_t*)lib->get(lib, "pt-tls-port");
+
+       if (this->eap_transport && pdp_server && pdp_port)
+       {
+               msg = pb_pdp_referral_msg_create_from_fqdn(
+                                               pdp_server->get_encoding(pdp_server), *pdp_port);
+               this->mutex->lock(this->mutex);
+               this->messages->insert_last(this->messages, msg);
+               this->mutex->unlock(this->mutex);
+       }
+}
+
+METHOD(tnccs_20_handler_t, get_send_flag, bool,
+       private_tnccs_20_server_t *this)
+{
+       return this->send_msg;
+}
+
+METHOD(tnccs_20_handler_t, add_msg, void,
+       private_tnccs_20_server_t *this, pb_tnc_msg_t *msg)
+{
+       /* adding PA message to SDATA batch only */
+       this->mutex->lock(this->mutex);
+       if (this->batch_type == PB_BATCH_NONE)
+       {
+               this->batch_type = PB_BATCH_SDATA;
+       }
+       if (this->batch_type == PB_BATCH_SDATA)
+       {
+               this->messages->insert_last(this->messages, msg);
+       }
+       else
+       {
+               msg->destroy(msg);
+       }
+       this->mutex->unlock(this->mutex);
+}
+
+METHOD(tnccs_20_handler_t, handle_errors, void,
+       private_tnccs_20_server_t *this,  pb_tnc_batch_t *batch)
+{
+       pb_tnc_msg_t *msg;
+       enumerator_t *enumerator;
+
+       enumerator = batch->create_error_enumerator(batch);
+       while (enumerator->enumerate(enumerator, &msg))
+       {
+               this->mutex->lock(this->mutex);
+               this->messages->insert_last(this->messages, msg->get_ref(msg));
+               this->mutex->unlock(this->mutex);
+       }
+       enumerator->destroy(enumerator);
+}
+
+METHOD(tnccs_20_handler_t, destroy, void,
+       private_tnccs_20_server_t *this)
+{
+       if (this->connection_id)
+       {
+               tnc->tnccs->remove_connection(tnc->tnccs, this->connection_id, TRUE);
+       }
+       this->state_machine->destroy(this->state_machine);
+       this->mutex->destroy(this->mutex);
+       this->messages->destroy_offset(this->messages,
+                                                                  offsetof(pb_tnc_msg_t, destroy));
+       free(this);
+}
+
+METHOD(tnccs_20_server_t, have_recommendation, bool,
+       private_tnccs_20_server_t *this, TNC_IMV_Action_Recommendation *rec,
+       TNC_IMV_Evaluation_Result *eval)
+{
+       return this->recs->have_recommendation(this->recs, rec, eval);
+}
+
+/**
+ * See header
+ */
+tnccs_20_handler_t* tnccs_20_server_create(tnccs_t *tnccs,
+                                                                                  tnccs_send_message_t send_msg,
+                                                                                  size_t max_batch_len,
+                                                                                  size_t max_msg_len,
+                                                                                  bool eap_transport)
+{
+       private_tnccs_20_server_t *this;
+
+       INIT(this,
+               .public = {
+                       .handler = {
+                               .process = _process,
+                               .build = _build,
+                               .begin_handshake = _begin_handshake,
+                               .get_send_flag = _get_send_flag,
+                               .add_msg = _add_msg,
+                               .handle_errors = _handle_errors,
+                               .destroy = _destroy,
+                       },
+                       .have_recommendation = _have_recommendation,
+               },
+               .state_machine = pb_tnc_state_machine_create(TRUE),
+               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+               .messages = linked_list_create(),
+               .batch_type = PB_BATCH_SDATA,
+               .max_batch_len = max_batch_len,
+               .eap_transport = eap_transport,
+       );
+
+       this->connection_id = tnc->tnccs->create_connection(tnc->tnccs,
+                                                                                       TNCCS_2_0, tnccs, send_msg,
+                                                                                       &this->request_handshake_retry,
+                                                                                       max_msg_len, &this->recs);
+       if (!this->connection_id)
+       {
+               destroy(this);
+               return NULL;
+       }
+       tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
+                                                                               TNC_CONNECTION_STATE_CREATE);
+
+       return &this->public.handler;
+}
diff --git a/src/libtnccs/plugins/tnccs_20/tnccs_20_server.h b/src/libtnccs/plugins/tnccs_20/tnccs_20_server.h
new file mode 100644 (file)
index 0000000..5d8d561
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 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 <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 tnccs_20_server_h tnccs_20_server
+ * @{ @ingroup tnccs_20_server
+ */
+
+#ifndef TNCCS_20_SERVER_H_
+#define TNCCS_20_SERVER_H_
+
+#include <library.h>
+
+#include <tnc/tnccs/tnccs.h>
+
+#include "tnccs_20_handler.h"
+
+typedef struct tnccs_20_server_t tnccs_20_server_t;
+
+/**
+ * Interface for a TNC server
+ */
+struct tnccs_20_server_t {
+
+       /**
+        * IF-TNCCS 2.0 protocol handler interface
+        */
+       tnccs_20_handler_t handler;
+
+       /**
+        * Check if an Action Recommendation is already available
+        *
+        * @param rec                   TNC Action Recommendation
+        * @param eval                  TNC Evaluation Result
+        * @return                              TRUE if Action Recommendation is 
+        */
+       bool (*have_recommendation)(tnccs_20_server_t *this,
+                                                               TNC_IMV_Action_Recommendation *rec,
+                                                               TNC_IMV_Evaluation_Result *eval);
+
+};
+
+/**
+ * Create an instance of the TNC IF-TNCCS 2.0 server-side protocol handler.
+ *
+ * @param tnccs                                TNC IF-TNCCS 2.0 stack
+ * @param send_msg                     TNF IF-TNCCS 2.0 send message callback function
+ * @param max_batch_len                Maximum PB-TNC batch size
+ * @param max_msg_len          Maximum PA-TNC message size
+ * @param eap_transport                TRUE if IF-T for EAP methods
+ */
+tnccs_20_handler_t* tnccs_20_server_create(tnccs_t *tnccs, 
+                                                                                  tnccs_send_message_t send_msg,
+                                                                                  size_t max_batch_len,
+                                                                                  size_t max_msg_len,
+                                                                                  bool eap_transport);
+
+
+#endif /** TNCCS_20_SERVER_H_ @}*/