/* * Portions of this code unrelated to FreeRADIUS are available * separately under a commercial license. If you require an * implementation of EAP-TNC that is not under the GPLv2, please * contact trust@f4-i.fh-hannover.de for details. * * 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. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * */ /** * $Id$ * @file rlm_eap_tnc.c * @brief Interfaces with the naeap library to provide EAP-TNC inner method. * * @copyright 2013 The FreeRADIUS project * @copyright 2007 Alan DeKok * @copyright 2006-2009 FH Hannover */ /* * EAP-TNC Packet with EAP Header, general structure * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Code | Identifier | Length | * | | | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type | Flags | Ver | Data Length | * | |L M S R R| =1 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Data Length | Data ... * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #include #include #include #include "eap.h" #include #include #define VERSION "0.7.0" #define SET_START(x) ((x) | (0x20)) typedef struct rlm_eap_tnc { char const *connection_string; } rlm_eap_tnc_t; static CONF_PARSER module_config[] = { { "connection_string", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_eap_tnc_t, connection_string), "NAS Port: %{NAS-Port} NAS IP: %{NAS-IP-Address} NAS_PORT_TYPE: %{NAS-Port-Type}" }, CONF_PARSER_TERMINATOR }; static int mod_instantiate(CONF_SECTION *cs, void **instance) { rlm_eap_tnc_t *inst; TNC_Result result; *instance = inst = talloc_zero(cs, rlm_eap_tnc_t); if (!inst) return -1; /* * Parse the configuration attributes. */ if (cf_section_parse(cs, inst, module_config) < 0) { return -1; } result = initializeDefault(); if (result != TNC_RESULT_SUCCESS) { ERROR("rlm_eap_tnc: NAA-EAP initializeDefault returned an " "error code"); return -1; } return 0; } static int mod_detach(void *instance) { TNC_Result result; talloc_free(instance); result = terminate(); if (result != TNC_RESULT_SUCCESS) { ERROR("rlm_eap_tnc: NAA-EAP terminate returned an " "error code whilst detaching"); return -1; } return 0; } /* * This function is called when the first EAP_IDENTITY_RESPONSE message * was received. * * Initiates the EPA_TNC session by sending the first EAP_TNC_RESPONSE * to the peer. The packet has the Start-Bit set and contains no data. * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Code | Identifier | Length | * | | | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type | Flags | Ver | * | |0 0 1 0 0|0 0 1| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * For this package, only 'Identifier' has to be set dynamically. Any * other information is static. */ static int mod_session_init(void *instance, eap_handler_t *handler) { rlm_eap_tnc_t *inst = instance; REQUEST *request = NULL; char buff[71]; ssize_t len = 0; TNC_Result result; TNC_ConnectionID conn_id; TNC_BufferReference eap_tnc_request; TNC_BufferReference eap_tnc_user; VALUE_PAIR *username; /* * Check if we run inside a secure EAP method. * FIXME check concrete outer EAP method. */ if (!handler->request || !handler->request->parent) { ERROR("rlm_eap_tnc: EAP_TNC must only be used as an " "inner method within a protected tunneled EAP created " "by an outer EAP method"); return 0; } request = handler->request->parent; /* * Build the connection string */ len = radius_xlat(buff, sizeof(buff), request, inst->connection_string, NULL, NULL); if (len < 0){ return 0; } RDEBUG("Getting connection from NAA-EAP"); /* * Get connection (uses a function from the NAA-EAP-library) */ result = getConnection(buff, &conn_id); if (result != TNC_RESULT_SUCCESS) { ERROR("rlm_eap_tnc: NAA-EAP getConnection returned an " "error code"); return 0; } /* * Previous code manually parsed the EAP identity response * this was wrong. rlm_eap will *always* create the Username * from the EAP Identity response. * * Something has gone very wrong if the User-Name doesn't exist. */ username = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY); RDEBUG("Username for TNC connection: %s", username->vp_strvalue); /* * Stores the username associated with the connection * * What becomes of username? Who knows... but we don't free it * so not safe to use talloc. */ MEM(eap_tnc_user = (TNC_BufferReference) strdup(username->vp_strvalue)); result = storeUsername(conn_id, eap_tnc_user, username->vp_length); if (result != TNC_RESULT_SUCCESS) { ERROR("rlm_eap_tnc: NAA-EAP storeUsername returned an " "error code"); return 0; } /* * Set connection ID */ handler->opaque = talloc(handler, TNC_ConnectionID); memcpy(handler->opaque, &conn_id, sizeof(TNC_ConnectionID)); /* * Bild first EAP TNC request */ MEM(eap_tnc_request = talloc_array(handler->eap_ds->request, uint8_t, 1)); *eap_tnc_request = SET_START(1); handler->eap_ds->request->code = PW_EAP_REQUEST; handler->eap_ds->request->type.num = PW_EAP_TNC; handler->eap_ds->request->type.length = 1; talloc_free(handler->eap_ds->request->type.data); handler->eap_ds->request->type.data = eap_tnc_request; /* * We don't need to authorize the user at this point. * * We also don't need to keep the challenge, as it's * stored in 'handler->eap_ds', which will be given back * to us... */ handler->stage = PROCESS; return 1; } /** * This function is called when a EAP_TNC_RESPONSE was received. * It basically forwards the EAP_TNC data to NAA-TNCS and forms * and appropriate EAP_RESPONSE. Furthermore, it sets the VlanID * based on the TNC_ConnectionState determined by NAA-TNCS. * * @param instance The configuration data. * @param handler The eap_handler_t. * @return True, if successfully, else false. */ static int mod_process(UNUSED void *instance, eap_handler_t *handler) { TNC_ConnectionID conn_id; TNC_Result result; TNC_BufferReference data = NULL; TNC_UInt32 datalen = 0; TNC_ConnectionState connection_state; uint8_t code = 0; REQUEST *request = handler->request; if (handler->eap_ds->response->type.num != PW_EAP_TNC) { ERROR("rlm_eap_tnc: Incorrect response type"); return 0; } /* * Retrieve connection ID */ conn_id = *((TNC_ConnectionID *) (handler->opaque)); RDEBUG2("Starting authentication for connection ID %lX", conn_id); /* * Pass EAP_TNC data to NAA-EAP and get answer data */ connection_state = TNC_CONNECTION_STATE_CREATE; /* * Forwards the eap_tnc data to NAA-EAP and gets the response */ result = processEAPTNCData(conn_id, handler->eap_ds->response->type.data, handler->eap_ds->response->type.length, &data, &datalen, &connection_state); if (result != TNC_RESULT_SUCCESS) { RDEBUG("NAA-EAP processEAPTNCData returned " "an error code"); return 0; } /* * Determine eap code for the response */ switch (connection_state) { case TNC_CONNECTION_STATE_HANDSHAKE: code = PW_EAP_REQUEST; break; case TNC_CONNECTION_STATE_ACCESS_NONE: code = PW_EAP_FAILURE; pair_make_config("TNC-Status", "None", T_OP_SET); break; case TNC_CONNECTION_STATE_ACCESS_ALLOWED: code = PW_EAP_SUCCESS; pair_make_config("TNC-Status", "Access", T_OP_SET); break; case TNC_CONNECTION_STATE_ACCESS_ISOLATED: code = PW_EAP_SUCCESS; pair_make_config("TNC-Status", "Isolate", T_OP_SET); break; default: ERROR("rlm_eap_tnc: Invalid connection state"); return 0; } /* * Build the TNC EAP request */ handler->eap_ds->request->code = code; handler->eap_ds->request->type.num = PW_EAP_TNC; handler->eap_ds->request->type.length = datalen; talloc_free(handler->eap_ds->request->type.data); /* * "data" is not talloc'd memory. */ handler->eap_ds->request->type.data = talloc_array(handler->eap_ds->request, uint8_t, datalen); memcpy(handler->eap_ds->request->type.data, data, datalen); free(data); return 1; } /* * The module name should be the only globally exported symbol. * That is, everything else should be 'static'. */ extern rlm_eap_module_t rlm_eap_tnc; rlm_eap_module_t rlm_eap_tnc = { .name = "eap_tnc", .instantiate = mod_instantiate, /* Create new submodule instance */ .session_init = mod_session_init, /* Initialise a new EAP session */ .process = mod_process, /* Process next round of EAP method */ .detach = mod_detach /* detach */ };