check it specified IF-TNCCS protocol is enabled
[strongswan.git] / src / libcharon / plugins / eap_tnc / eap_tnc.c
index 6208774..f9ab742 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2010 Andreas Steffen
- * Hochschule fuer Technik Rapperswil
+ * Copyright (C) 2010-2013 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
 
 #include "eap_tnc.h"
 
+#include <tnc/tnc.h>
+#include <tnc/tnccs/tnccs_manager.h>
+#include <tls_eap.h>
+#include <utils/debug.h>
 #include <daemon.h>
-#include <library.h>
+
+#include <tncifimv.h>
+#include <tncif_names.h>
+
+/**
+ * Maximum size of an EAP-TNC message
+ */
+#define EAP_TNC_MAX_MESSAGE_LEN 65535
+
+/**
+ * Maximum number of EAP-TNC messages allowed
+ */
+#define EAP_TNC_MAX_MESSAGE_COUNT 10
 
 typedef struct private_eap_tnc_t private_eap_tnc_t;
 
@@ -31,84 +47,131 @@ struct private_eap_tnc_t {
        eap_tnc_t public;
 
        /**
-        * ID of the server
+        * Outer EAP authentication type
         */
-       identification_t *server;
+       eap_type_t auth_type;
 
        /**
-        * ID of the peer
+        * TLS stack, wrapped by EAP helper
         */
-       identification_t *peer;
-};
+       tls_eap_t *tls_eap;
 
-/**
- * Flags of an EAP-TNC message
- */
-typedef enum {
-       EAP_TNC_LENGTH = (1<<7),
-       EAP_TNC_MORE_FRAGS = (1<<6),
-       EAP_TNC_START = (1<<5),
-       EAP_TNC_DH = (1<<4),
-       EAP_TNC_VERSION = 0x07
-} eap_tnc_flags_t;
+       /**
+        * TNCCS instance running over EAP-TNC
+        */
+       tnccs_t *tnccs;
+
+};
 
 /**
- * EAP-TNC packet format
+ * Callback function to get recommendation from TNCCS connection
  */
-typedef struct __attribute__((packed)) {
-       u_int8_t code;
-       u_int8_t identifier;
-       u_int16_t length;
-       u_int8_t type;
-       u_int8_t flags;
-} eap_tnc_packet_t;
-
-METHOD(eap_method_t, initiate_peer, status_t,
-       private_eap_tnc_t *this, eap_payload_t **out)
+static bool enforce_recommendation(TNC_IMV_Action_Recommendation rec,
+                                                                  TNC_IMV_Evaluation_Result eval)
 {
-       /* peer never initiates */
-       return FAILED;
-}
+       char *group;
+       identification_t *id;
+       ike_sa_t *ike_sa;
+       auth_cfg_t *auth;
+       bool no_access = FALSE;
 
-METHOD(eap_method_t, initiate_server, status_t,
-       private_eap_tnc_t *this, eap_payload_t **out)
-{
-       return NEED_MORE;
+       DBG1(DBG_TNC, "final recommendation is '%N' and evaluation is '%N'",
+                TNC_IMV_Action_Recommendation_names, rec,
+                TNC_IMV_Evaluation_Result_names, eval);
+
+       switch (rec)
+       {
+               case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
+                       group = "allow";
+                       break;
+               case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
+                       group = "isolate";
+                       break;
+               case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
+               case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
+               default:
+                       group = "no access";
+                       no_access = TRUE;
+                       break;
+       }
+
+       ike_sa = charon->bus->get_sa(charon->bus);
+       if (!ike_sa)
+       {
+               DBG1(DBG_TNC, "policy enforcement point did not find IKE_SA");
+               return FALSE;
+       }
+
+       id = ike_sa->get_other_id(ike_sa);
+       DBG0(DBG_TNC, "policy enforced on peer '%Y' is '%s'", id, group);
+
+       if (no_access)
+       {
+               return FALSE;
+       }
+       else
+       {
+               auth = ike_sa->get_auth_cfg(ike_sa, FALSE);
+               id = identification_create_from_string(group);
+               auth->add(auth, AUTH_RULE_GROUP, id);
+               DBG1(DBG_TNC, "policy enforcement point added group membership '%s'",
+                        group);
+       }
+       return TRUE;
 }
 
-METHOD(eap_method_t, process_peer, status_t,
-       private_eap_tnc_t *this, eap_payload_t *in, eap_payload_t **out)
+METHOD(eap_method_t, initiate, status_t,
+       private_eap_tnc_t *this, eap_payload_t **out)
 {
-       eap_tnc_packet_t *pkt;
        chunk_t data;
+       u_int32_t auth_type;
 
-       data = in->get_data(in);
-
-       pkt = (eap_tnc_packet_t*)data.ptr;
-       if (data.len < sizeof(eap_tnc_packet_t) ||
-               untoh16(&pkt->length) != data.len)
+       /* Determine TNC Client Authentication Type */
+       switch (this->auth_type)
        {
-               DBG1(DBG_IKE, "invalid EAP-TNC packet length");
-               return FAILED;
+               case EAP_TLS:
+               case EAP_TTLS:
+               case EAP_PEAP:
+                       auth_type = TNC_AUTH_X509_CERT;
+                       break;
+               case EAP_MD5:
+               case EAP_MSCHAPV2:
+               case EAP_GTC:
+               case EAP_OTP:
+                       auth_type = TNC_AUTH_PASSWORD;
+                       break;
+               case EAP_SIM:
+               case EAP_AKA:
+                       auth_type = TNC_AUTH_SIM;
+                       break;
+               default:
+                       auth_type = TNC_AUTH_UNKNOWN;
        }
-       if (pkt->flags & EAP_TNC_START)
+       this->tnccs->set_auth_type(this->tnccs, auth_type);
+
+       if (this->tls_eap->initiate(this->tls_eap, &data) == NEED_MORE)
        {
-               DBG1(DBG_IKE, "EAP-TNC version is v%u", pkt->flags & EAP_TNC_VERSION);
+               *out = eap_payload_create_data(data);
+               free(data.ptr);
+               return NEED_MORE;
        }
-       *out = eap_payload_create_nak(in->get_identifier(in));
-
-       return NEED_MORE;
+       return FAILED;
 }
 
-METHOD(eap_method_t, process_server, status_t,
+METHOD(eap_method_t, process, status_t,
        private_eap_tnc_t *this, eap_payload_t *in, eap_payload_t **out)
 {
+       status_t status;
        chunk_t data;
 
        data = in->get_data(in);
-       DBG2(DBG_IKE, "received EAP-TNC data: %B", &data);
-
-       return SUCCESS;
+       status = this->tls_eap->process(this->tls_eap, data, &data);
+       if (status == NEED_MORE)
+       {
+               *out = eap_payload_create_data(data);
+               free(data.ptr);
+       }
+       return status;
 }
 
 METHOD(eap_method_t, get_type, eap_type_t,
@@ -121,9 +184,26 @@ METHOD(eap_method_t, get_type, eap_type_t,
 METHOD(eap_method_t, get_msk, status_t,
        private_eap_tnc_t *this, chunk_t *msk)
 {
+       *msk = this->tls_eap->get_msk(this->tls_eap);
+       if (msk->len)
+       {
+               return SUCCESS;
+       }
        return FAILED;
 }
 
+METHOD(eap_method_t, get_identifier, u_int8_t,
+       private_eap_tnc_t *this)
+{
+       return this->tls_eap->get_identifier(this->tls_eap);
+}
+
+METHOD(eap_method_t, set_identifier, void,
+       private_eap_tnc_t *this, u_int8_t identifier)
+{
+       this->tls_eap->set_identifier(this->tls_eap, identifier);
+}
+
 METHOD(eap_method_t, is_mutual, bool,
        private_eap_tnc_t *this)
 {
@@ -133,58 +213,117 @@ METHOD(eap_method_t, is_mutual, bool,
 METHOD(eap_method_t, destroy, void,
        private_eap_tnc_t *this)
 {
-       this->peer->destroy(this->peer);
-       this->server->destroy(this->server);
+       chunk_t pdp_server;
+       u_int16_t pdp_port;
+       tls_t *tls;
+
+       pdp_server = this->tnccs->get_pdp_server(this->tnccs, &pdp_port);
+       if (pdp_server.len)
+       {
+               DBG2(DBG_TNC, "TODO: setup PT-TLS connection to %.*s:%u",
+                        pdp_server.len, pdp_server.ptr, pdp_port);
+       }
+       tls = &this->tnccs->tls;
+       tls->destroy(tls);
+       this->tls_eap->destroy(this->tls_eap);
        free(this);
 }
 
-/*
- * See header
- */
-eap_tnc_t *eap_tnc_create_server(identification_t *server, identification_t *peer)
+METHOD(eap_inner_method_t, get_auth_type, eap_type_t,
+       private_eap_tnc_t *this)
 {
-       private_eap_tnc_t *this;
-
-       INIT(this,
-               .public = {
-                       .eap_method = {
-                               .initiate = _initiate_server,
-                               .process = _process_server,
-                               .get_type = _get_type,
-                               .is_mutual = _is_mutual,
-                               .get_msk = _get_msk,
-                               .destroy = _destroy,
-                       },
-               },
-               .peer = peer->clone(peer),
-               .server = server->clone(server),
-       );
+       return this->auth_type;
+}
 
-       return &this->public;
+METHOD(eap_inner_method_t, set_auth_type, void,
+       private_eap_tnc_t *this, eap_type_t type)
+{
+       this->auth_type = type;
 }
 
-/*
- * See header
+/**
+ * Generic private constructor
  */
-eap_tnc_t *eap_tnc_create_peer(identification_t *server, identification_t *peer)
+static eap_tnc_t *eap_tnc_create(identification_t *server,
+                                                                identification_t *peer, bool is_server)
 {
        private_eap_tnc_t *this;
+       int max_msg_count;
+       char* protocol;
+       tnccs_t *tnccs;
+       tnccs_type_t type;
 
        INIT(this,
                .public = {
-                       .eap_method = {
-                               .initiate = _initiate_peer,
-                               .process = _process_peer,
-                               .get_type = _get_type,
-                               .is_mutual = _is_mutual,
-                               .get_msk = _get_msk,
-                               .destroy = _destroy,
+                       .eap_inner_method = {
+                               .eap_method = {
+                                       .initiate = _initiate,
+                                       .process = _process,
+                                       .get_type = _get_type,
+                                       .is_mutual = _is_mutual,
+                                       .get_msk = _get_msk,
+                                       .get_identifier = _get_identifier,
+                                       .set_identifier = _set_identifier,
+                                       .destroy = _destroy,
+                               },
+                               .get_auth_type = _get_auth_type,
+                               .set_auth_type = _set_auth_type,
                        },
                },
-               .peer = peer->clone(peer),
-               .server = server->clone(server),
        );
 
+       max_msg_count = lib->settings->get_int(lib->settings,
+                                       "%s.plugins.eap-tnc.max_message_count",
+                                       EAP_TNC_MAX_MESSAGE_COUNT, charon->name);
+       protocol = lib->settings->get_str(lib->settings,
+                                       "%s.plugins.eap-tnc.protocol", "tnccs-1.1", charon->name);
+       if (strcaseeq(protocol, "tnccs-2.0"))
+       {
+               type = TNCCS_2_0;
+       }
+       else if (strcaseeq(protocol, "tnccs-1.1"))
+       {
+               type = TNCCS_1_1;
+       }
+       else if (strcaseeq(protocol, "tnccs-dynamic") && is_server)
+       {
+               type = TNCCS_DYNAMIC;
+       }
+       else
+       {
+               DBG1(DBG_TNC, "TNCCS protocol '%s' not supported", protocol);
+               free(this);
+               return NULL;
+       }
+       tnccs = tnc->tnccs->create_instance(tnc->tnccs, type,
+                                               is_server, server, peer, TNC_IFT_EAP_1_1,
+                                               is_server ? enforce_recommendation : NULL);
+       if (!tnccs)
+       {
+               DBG1(DBG_TNC, "TNCCS protocol '%s' not enabled", protocol);
+               free(this);
+               return NULL;
+       }
+       this->tnccs = tnccs->get_ref(tnccs);
+       this->tls_eap = tls_eap_create(EAP_TNC, &tnccs->tls,
+                                                                  EAP_TNC_MAX_MESSAGE_LEN,
+                                                                  max_msg_count, FALSE);
+       if (!this->tls_eap)
+       {
+               free(this);
+               return NULL;
+       }
        return &this->public;
 }
 
+eap_tnc_t *eap_tnc_create_server(identification_t *server,
+                                                                identification_t *peer)
+{
+       return eap_tnc_create(server, peer, TRUE);
+}
+
+eap_tnc_t *eap_tnc_create_peer(identification_t *server,
+                                                          identification_t *peer)
+{
+       return eap_tnc_create(server, peer, FALSE);
+}