Home | History | Annotate | Download | only in eap_server
      1 /*
      2  * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
      3  * Copyright (c) 2007-2008, Jouni Malinen <j (at) w1.fi>
      4  *
      5  * This software may be distributed under the terms of the BSD license.
      6  * See README for more details.
      7  */
      8 
      9 #include "includes.h"
     10 #include <dlfcn.h>
     11 
     12 #include "common.h"
     13 #include "base64.h"
     14 #include "common/tnc.h"
     15 #include "tncs.h"
     16 #include "eap_common/eap_tlv_common.h"
     17 #include "eap_common/eap_defs.h"
     18 
     19 
     20 /* TODO: TNCS must be thread-safe; review the code and add locking etc. if
     21  * needed.. */
     22 
     23 #ifndef TNC_CONFIG_FILE
     24 #define TNC_CONFIG_FILE "/etc/tnc_config"
     25 #endif /* TNC_CONFIG_FILE */
     26 #define IF_TNCCS_START \
     27 "<?xml version=\"1.0\"?>\n" \
     28 "<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
     29 "xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
     30 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
     31 "xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
     32 "IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
     33 #define IF_TNCCS_END "\n</TNCCS-Batch>"
     34 
     35 /* TNC IF-IMV */
     36 
     37 struct tnc_if_imv {
     38 	struct tnc_if_imv *next;
     39 	char *name;
     40 	char *path;
     41 	void *dlhandle; /* from dlopen() */
     42 	TNC_IMVID imvID;
     43 	TNC_MessageTypeList supported_types;
     44 	size_t num_supported_types;
     45 
     46 	/* Functions implemented by IMVs (with TNC_IMV_ prefix) */
     47 	TNC_Result (*Initialize)(
     48 		TNC_IMVID imvID,
     49 		TNC_Version minVersion,
     50 		TNC_Version maxVersion,
     51 		TNC_Version *pOutActualVersion);
     52 	TNC_Result (*NotifyConnectionChange)(
     53 		TNC_IMVID imvID,
     54 		TNC_ConnectionID connectionID,
     55 		TNC_ConnectionState newState);
     56 	TNC_Result (*ReceiveMessage)(
     57 		TNC_IMVID imvID,
     58 		TNC_ConnectionID connectionID,
     59 		TNC_BufferReference message,
     60 		TNC_UInt32 messageLength,
     61 		TNC_MessageType messageType);
     62 	TNC_Result (*SolicitRecommendation)(
     63 		TNC_IMVID imvID,
     64 		TNC_ConnectionID connectionID);
     65 	TNC_Result (*BatchEnding)(
     66 		TNC_IMVID imvID,
     67 		TNC_ConnectionID connectionID);
     68 	TNC_Result (*Terminate)(TNC_IMVID imvID);
     69 	TNC_Result (*ProvideBindFunction)(
     70 		TNC_IMVID imvID,
     71 		TNC_TNCS_BindFunctionPointer bindFunction);
     72 };
     73 
     74 
     75 #define TNC_MAX_IMV_ID 10
     76 
     77 struct tncs_data {
     78 	struct tncs_data *next;
     79 	struct tnc_if_imv *imv; /* local copy of tncs_global_data->imv */
     80 	TNC_ConnectionID connectionID;
     81 	unsigned int last_batchid;
     82 	enum IMV_Action_Recommendation recommendation;
     83 	int done;
     84 
     85 	struct conn_imv {
     86 		u8 *imv_send;
     87 		size_t imv_send_len;
     88 		enum IMV_Action_Recommendation recommendation;
     89 		int recommendation_set;
     90 	} imv_data[TNC_MAX_IMV_ID];
     91 
     92 	char *tncs_message;
     93 };
     94 
     95 
     96 struct tncs_global {
     97 	struct tnc_if_imv *imv;
     98 	TNC_ConnectionID next_conn_id;
     99 	struct tncs_data *connections;
    100 };
    101 
    102 static struct tncs_global *tncs_global_data = NULL;
    103 
    104 
    105 static struct tnc_if_imv * tncs_get_imv(TNC_IMVID imvID)
    106 {
    107 	struct tnc_if_imv *imv;
    108 
    109 	if (imvID >= TNC_MAX_IMV_ID || tncs_global_data == NULL)
    110 		return NULL;
    111 	imv = tncs_global_data->imv;
    112 	while (imv) {
    113 		if (imv->imvID == imvID)
    114 			return imv;
    115 		imv = imv->next;
    116 	}
    117 	return NULL;
    118 }
    119 
    120 
    121 static struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID)
    122 {
    123 	struct tncs_data *tncs;
    124 
    125 	if (tncs_global_data == NULL)
    126 		return NULL;
    127 
    128 	tncs = tncs_global_data->connections;
    129 	while (tncs) {
    130 		if (tncs->connectionID == connectionID)
    131 			return tncs;
    132 		tncs = tncs->next;
    133 	}
    134 
    135 	wpa_printf(MSG_DEBUG, "TNC: Connection ID %lu not found",
    136 		   (unsigned long) connectionID);
    137 
    138 	return NULL;
    139 }
    140 
    141 
    142 /* TNCS functions that IMVs can call */
    143 static TNC_Result TNC_TNCS_ReportMessageTypes(
    144 	TNC_IMVID imvID,
    145 	TNC_MessageTypeList supportedTypes,
    146 	TNC_UInt32 typeCount)
    147 {
    148 	TNC_UInt32 i;
    149 	struct tnc_if_imv *imv;
    150 
    151 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ReportMessageTypes(imvID=%lu "
    152 		   "typeCount=%lu)",
    153 		   (unsigned long) imvID, (unsigned long) typeCount);
    154 
    155 	for (i = 0; i < typeCount; i++) {
    156 		wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
    157 			   i, supportedTypes[i]);
    158 	}
    159 
    160 	imv = tncs_get_imv(imvID);
    161 	if (imv == NULL)
    162 		return TNC_RESULT_INVALID_PARAMETER;
    163 	os_free(imv->supported_types);
    164 	imv->supported_types = os_memdup(supportedTypes,
    165 					 typeCount * sizeof(TNC_MessageType));
    166 	if (imv->supported_types == NULL)
    167 		return TNC_RESULT_FATAL;
    168 	imv->num_supported_types = typeCount;
    169 
    170 	return TNC_RESULT_SUCCESS;
    171 }
    172 
    173 
    174 static TNC_Result TNC_TNCS_SendMessage(
    175 	TNC_IMVID imvID,
    176 	TNC_ConnectionID connectionID,
    177 	TNC_BufferReference message,
    178 	TNC_UInt32 messageLength,
    179 	TNC_MessageType messageType)
    180 {
    181 	struct tncs_data *tncs;
    182 	unsigned char *b64;
    183 	size_t b64len;
    184 
    185 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage(imvID=%lu "
    186 		   "connectionID=%lu messageType=%lu)",
    187 		   imvID, connectionID, messageType);
    188 	wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage",
    189 			  message, messageLength);
    190 
    191 	if (tncs_get_imv(imvID) == NULL)
    192 		return TNC_RESULT_INVALID_PARAMETER;
    193 
    194 	tncs = tncs_get_conn(connectionID);
    195 	if (tncs == NULL)
    196 		return TNC_RESULT_INVALID_PARAMETER;
    197 
    198 	b64 = base64_encode(message, messageLength, &b64len);
    199 	if (b64 == NULL)
    200 		return TNC_RESULT_FATAL;
    201 
    202 	os_free(tncs->imv_data[imvID].imv_send);
    203 	tncs->imv_data[imvID].imv_send_len = 0;
    204 	tncs->imv_data[imvID].imv_send = os_zalloc(b64len + 100);
    205 	if (tncs->imv_data[imvID].imv_send == NULL) {
    206 		os_free(b64);
    207 		return TNC_RESULT_OTHER;
    208 	}
    209 
    210 	tncs->imv_data[imvID].imv_send_len =
    211 		os_snprintf((char *) tncs->imv_data[imvID].imv_send,
    212 			    b64len + 100,
    213 			    "<IMC-IMV-Message><Type>%08X</Type>"
    214 			    "<Base64>%s</Base64></IMC-IMV-Message>",
    215 			    (unsigned int) messageType, b64);
    216 
    217 	os_free(b64);
    218 
    219 	return TNC_RESULT_SUCCESS;
    220 }
    221 
    222 
    223 static TNC_Result TNC_TNCS_RequestHandshakeRetry(
    224 	TNC_IMVID imvID,
    225 	TNC_ConnectionID connectionID,
    226 	TNC_RetryReason reason)
    227 {
    228 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_RequestHandshakeRetry");
    229 	/* TODO */
    230 	return TNC_RESULT_SUCCESS;
    231 }
    232 
    233 
    234 static TNC_Result TNC_TNCS_ProvideRecommendation(
    235 	TNC_IMVID imvID,
    236 	TNC_ConnectionID connectionID,
    237 	TNC_IMV_Action_Recommendation recommendation,
    238 	TNC_IMV_Evaluation_Result evaluation)
    239 {
    240 	struct tncs_data *tncs;
    241 
    242 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ProvideRecommendation(imvID=%lu "
    243 		   "connectionID=%lu recommendation=%lu evaluation=%lu)",
    244 		   (unsigned long) imvID, (unsigned long) connectionID,
    245 		   (unsigned long) recommendation, (unsigned long) evaluation);
    246 
    247 	if (tncs_get_imv(imvID) == NULL)
    248 		return TNC_RESULT_INVALID_PARAMETER;
    249 
    250 	tncs = tncs_get_conn(connectionID);
    251 	if (tncs == NULL)
    252 		return TNC_RESULT_INVALID_PARAMETER;
    253 
    254 	tncs->imv_data[imvID].recommendation = recommendation;
    255 	tncs->imv_data[imvID].recommendation_set = 1;
    256 
    257 	return TNC_RESULT_SUCCESS;
    258 }
    259 
    260 
    261 static TNC_Result TNC_TNCS_GetAttribute(
    262 	TNC_IMVID imvID,
    263 	TNC_ConnectionID connectionID,
    264 	TNC_AttributeID attribureID,
    265 	TNC_UInt32 bufferLength,
    266 	TNC_BufferReference buffer,
    267 	TNC_UInt32 *pOutValueLength)
    268 {
    269 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_GetAttribute");
    270 	/* TODO */
    271 	return TNC_RESULT_SUCCESS;
    272 }
    273 
    274 
    275 static TNC_Result TNC_TNCS_SetAttribute(
    276 	TNC_IMVID imvID,
    277 	TNC_ConnectionID connectionID,
    278 	TNC_AttributeID attribureID,
    279 	TNC_UInt32 bufferLength,
    280 	TNC_BufferReference buffer)
    281 {
    282 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SetAttribute");
    283 	/* TODO */
    284 	return TNC_RESULT_SUCCESS;
    285 }
    286 
    287 
    288 static TNC_Result TNC_TNCS_BindFunction(
    289 	TNC_IMVID imvID,
    290 	char *functionName,
    291 	void **pOutFunctionPointer)
    292 {
    293 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_BindFunction(imcID=%lu, "
    294 		   "functionName='%s')", (unsigned long) imvID, functionName);
    295 
    296 	if (tncs_get_imv(imvID) == NULL)
    297 		return TNC_RESULT_INVALID_PARAMETER;
    298 
    299 	if (pOutFunctionPointer == NULL)
    300 		return TNC_RESULT_INVALID_PARAMETER;
    301 
    302 	if (os_strcmp(functionName, "TNC_TNCS_ReportMessageTypes") == 0)
    303 		*pOutFunctionPointer = TNC_TNCS_ReportMessageTypes;
    304 	else if (os_strcmp(functionName, "TNC_TNCS_SendMessage") == 0)
    305 		*pOutFunctionPointer = TNC_TNCS_SendMessage;
    306 	else if (os_strcmp(functionName, "TNC_TNCS_RequestHandshakeRetry") ==
    307 		 0)
    308 		*pOutFunctionPointer = TNC_TNCS_RequestHandshakeRetry;
    309 	else if (os_strcmp(functionName, "TNC_TNCS_ProvideRecommendation") ==
    310 		 0)
    311 		*pOutFunctionPointer = TNC_TNCS_ProvideRecommendation;
    312 	else if (os_strcmp(functionName, "TNC_TNCS_GetAttribute") == 0)
    313 		*pOutFunctionPointer = TNC_TNCS_GetAttribute;
    314 	else if (os_strcmp(functionName, "TNC_TNCS_SetAttribute") == 0)
    315 		*pOutFunctionPointer = TNC_TNCS_SetAttribute;
    316 	else
    317 		*pOutFunctionPointer = NULL;
    318 
    319 	return TNC_RESULT_SUCCESS;
    320 }
    321 
    322 
    323 static void * tncs_get_sym(void *handle, char *func)
    324 {
    325 	void *fptr;
    326 
    327 	fptr = dlsym(handle, func);
    328 
    329 	return fptr;
    330 }
    331 
    332 
    333 static int tncs_imv_resolve_funcs(struct tnc_if_imv *imv)
    334 {
    335 	void *handle = imv->dlhandle;
    336 
    337 	/* Mandatory IMV functions */
    338 	imv->Initialize = tncs_get_sym(handle, "TNC_IMV_Initialize");
    339 	if (imv->Initialize == NULL) {
    340 		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
    341 			   "TNC_IMV_Initialize");
    342 		return -1;
    343 	}
    344 
    345 	imv->SolicitRecommendation = tncs_get_sym(
    346 		handle, "TNC_IMV_SolicitRecommendation");
    347 	if (imv->SolicitRecommendation == NULL) {
    348 		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
    349 			   "TNC_IMV_SolicitRecommendation");
    350 		return -1;
    351 	}
    352 
    353 	imv->ProvideBindFunction =
    354 		tncs_get_sym(handle, "TNC_IMV_ProvideBindFunction");
    355 	if (imv->ProvideBindFunction == NULL) {
    356 		wpa_printf(MSG_ERROR, "TNC: IMV does not export "
    357 			   "TNC_IMV_ProvideBindFunction");
    358 		return -1;
    359 	}
    360 
    361 	/* Optional IMV functions */
    362 	imv->NotifyConnectionChange =
    363 		tncs_get_sym(handle, "TNC_IMV_NotifyConnectionChange");
    364 	imv->ReceiveMessage = tncs_get_sym(handle, "TNC_IMV_ReceiveMessage");
    365 	imv->BatchEnding = tncs_get_sym(handle, "TNC_IMV_BatchEnding");
    366 	imv->Terminate = tncs_get_sym(handle, "TNC_IMV_Terminate");
    367 
    368 	return 0;
    369 }
    370 
    371 
    372 static int tncs_imv_initialize(struct tnc_if_imv *imv)
    373 {
    374 	TNC_Result res;
    375 	TNC_Version imv_ver;
    376 
    377 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Initialize for IMV '%s'",
    378 		   imv->name);
    379 	res = imv->Initialize(imv->imvID, TNC_IFIMV_VERSION_1,
    380 			      TNC_IFIMV_VERSION_1, &imv_ver);
    381 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Initialize: res=%lu imv_ver=%lu",
    382 		   (unsigned long) res, (unsigned long) imv_ver);
    383 
    384 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
    385 }
    386 
    387 
    388 static int tncs_imv_terminate(struct tnc_if_imv *imv)
    389 {
    390 	TNC_Result res;
    391 
    392 	if (imv->Terminate == NULL)
    393 		return 0;
    394 
    395 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Terminate for IMV '%s'",
    396 		   imv->name);
    397 	res = imv->Terminate(imv->imvID);
    398 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Terminate: %lu",
    399 		   (unsigned long) res);
    400 
    401 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
    402 }
    403 
    404 
    405 static int tncs_imv_provide_bind_function(struct tnc_if_imv *imv)
    406 {
    407 	TNC_Result res;
    408 
    409 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_ProvideBindFunction for "
    410 		   "IMV '%s'", imv->name);
    411 	res = imv->ProvideBindFunction(imv->imvID, TNC_TNCS_BindFunction);
    412 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_ProvideBindFunction: res=%lu",
    413 		   (unsigned long) res);
    414 
    415 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
    416 }
    417 
    418 
    419 static int tncs_imv_notify_connection_change(struct tnc_if_imv *imv,
    420 					     TNC_ConnectionID conn,
    421 					     TNC_ConnectionState state)
    422 {
    423 	TNC_Result res;
    424 
    425 	if (imv->NotifyConnectionChange == NULL)
    426 		return 0;
    427 
    428 	wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_NotifyConnectionChange(%d)"
    429 		   " for IMV '%s'", (int) state, imv->name);
    430 	res = imv->NotifyConnectionChange(imv->imvID, conn, state);
    431 	wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
    432 		   (unsigned long) res);
    433 
    434 	return res == TNC_RESULT_SUCCESS ? 0 : -1;
    435 }
    436 
    437 
    438 static int tncs_load_imv(struct tnc_if_imv *imv)
    439 {
    440 	if (imv->path == NULL) {
    441 		wpa_printf(MSG_DEBUG, "TNC: No IMV configured");
    442 		return -1;
    443 	}
    444 
    445 	wpa_printf(MSG_DEBUG, "TNC: Opening IMV: %s (%s)",
    446 		   imv->name, imv->path);
    447 	imv->dlhandle = dlopen(imv->path, RTLD_LAZY);
    448 	if (imv->dlhandle == NULL) {
    449 		wpa_printf(MSG_ERROR, "TNC: Failed to open IMV '%s' (%s): %s",
    450 			   imv->name, imv->path, dlerror());
    451 		return -1;
    452 	}
    453 
    454 	if (tncs_imv_resolve_funcs(imv) < 0) {
    455 		wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMV functions");
    456 		return -1;
    457 	}
    458 
    459 	if (tncs_imv_initialize(imv) < 0 ||
    460 	    tncs_imv_provide_bind_function(imv) < 0) {
    461 		wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMV");
    462 		return -1;
    463 	}
    464 
    465 	return 0;
    466 }
    467 
    468 
    469 static void tncs_free_imv(struct tnc_if_imv *imv)
    470 {
    471 	os_free(imv->name);
    472 	os_free(imv->path);
    473 	os_free(imv->supported_types);
    474 }
    475 
    476 static void tncs_unload_imv(struct tnc_if_imv *imv)
    477 {
    478 	tncs_imv_terminate(imv);
    479 
    480 	if (imv->dlhandle)
    481 		dlclose(imv->dlhandle);
    482 
    483 	tncs_free_imv(imv);
    484 }
    485 
    486 
    487 static int tncs_supported_type(struct tnc_if_imv *imv, unsigned int type)
    488 {
    489 	size_t i;
    490 	unsigned int vendor, subtype;
    491 
    492 	if (imv == NULL || imv->supported_types == NULL)
    493 		return 0;
    494 
    495 	vendor = type >> 8;
    496 	subtype = type & 0xff;
    497 
    498 	for (i = 0; i < imv->num_supported_types; i++) {
    499 		unsigned int svendor, ssubtype;
    500 		svendor = imv->supported_types[i] >> 8;
    501 		ssubtype = imv->supported_types[i] & 0xff;
    502 		if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
    503 		    (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
    504 			return 1;
    505 	}
    506 
    507 	return 0;
    508 }
    509 
    510 
    511 static void tncs_send_to_imvs(struct tncs_data *tncs, unsigned int type,
    512 			      const u8 *msg, size_t len)
    513 {
    514 	struct tnc_if_imv *imv;
    515 	TNC_Result res;
    516 
    517 	wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMV(s)", msg, len);
    518 
    519 	for (imv = tncs->imv; imv; imv = imv->next) {
    520 		if (imv->ReceiveMessage == NULL ||
    521 		    !tncs_supported_type(imv, type))
    522 			continue;
    523 
    524 		wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMV '%s'",
    525 			   imv->name);
    526 		res = imv->ReceiveMessage(imv->imvID, tncs->connectionID,
    527 					  (TNC_BufferReference) msg, len,
    528 					  type);
    529 		wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
    530 			   (unsigned long) res);
    531 	}
    532 }
    533 
    534 
    535 static void tncs_batch_ending(struct tncs_data *tncs)
    536 {
    537 	struct tnc_if_imv *imv;
    538 	TNC_Result res;
    539 
    540 	for (imv = tncs->imv; imv; imv = imv->next) {
    541 		if (imv->BatchEnding == NULL)
    542 			continue;
    543 
    544 		wpa_printf(MSG_DEBUG, "TNC: Call BatchEnding for IMV '%s'",
    545 			   imv->name);
    546 		res = imv->BatchEnding(imv->imvID, tncs->connectionID);
    547 		wpa_printf(MSG_DEBUG, "TNC: BatchEnding: %lu",
    548 			   (unsigned long) res);
    549 	}
    550 }
    551 
    552 
    553 static void tncs_solicit_recommendation(struct tncs_data *tncs)
    554 {
    555 	struct tnc_if_imv *imv;
    556 	TNC_Result res;
    557 
    558 	for (imv = tncs->imv; imv; imv = imv->next) {
    559 		if (tncs->imv_data[imv->imvID].recommendation_set)
    560 			continue;
    561 
    562 		wpa_printf(MSG_DEBUG, "TNC: Call SolicitRecommendation for "
    563 			   "IMV '%s'", imv->name);
    564 		res = imv->SolicitRecommendation(imv->imvID,
    565 						 tncs->connectionID);
    566 		wpa_printf(MSG_DEBUG, "TNC: SolicitRecommendation: %lu",
    567 			   (unsigned long) res);
    568 	}
    569 }
    570 
    571 
    572 void tncs_init_connection(struct tncs_data *tncs)
    573 {
    574 	struct tnc_if_imv *imv;
    575 	int i;
    576 
    577 	for (imv = tncs->imv; imv; imv = imv->next) {
    578 		tncs_imv_notify_connection_change(
    579 			imv, tncs->connectionID, TNC_CONNECTION_STATE_CREATE);
    580 		tncs_imv_notify_connection_change(
    581 			imv, tncs->connectionID,
    582 			TNC_CONNECTION_STATE_HANDSHAKE);
    583 	}
    584 
    585 	for (i = 0; i < TNC_MAX_IMV_ID; i++) {
    586 		os_free(tncs->imv_data[i].imv_send);
    587 		tncs->imv_data[i].imv_send = NULL;
    588 		tncs->imv_data[i].imv_send_len = 0;
    589 	}
    590 }
    591 
    592 
    593 size_t tncs_total_send_len(struct tncs_data *tncs)
    594 {
    595 	int i;
    596 	size_t len = 0;
    597 
    598 	for (i = 0; i < TNC_MAX_IMV_ID; i++)
    599 		len += tncs->imv_data[i].imv_send_len;
    600 	if (tncs->tncs_message)
    601 		len += os_strlen(tncs->tncs_message);
    602 	return len;
    603 }
    604 
    605 
    606 u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos)
    607 {
    608 	int i;
    609 
    610 	for (i = 0; i < TNC_MAX_IMV_ID; i++) {
    611 		if (tncs->imv_data[i].imv_send == NULL)
    612 			continue;
    613 
    614 		os_memcpy(pos, tncs->imv_data[i].imv_send,
    615 			  tncs->imv_data[i].imv_send_len);
    616 		pos += tncs->imv_data[i].imv_send_len;
    617 		os_free(tncs->imv_data[i].imv_send);
    618 		tncs->imv_data[i].imv_send = NULL;
    619 		tncs->imv_data[i].imv_send_len = 0;
    620 	}
    621 
    622 	if (tncs->tncs_message) {
    623 		size_t len = os_strlen(tncs->tncs_message);
    624 		os_memcpy(pos, tncs->tncs_message, len);
    625 		pos += len;
    626 		os_free(tncs->tncs_message);
    627 		tncs->tncs_message = NULL;
    628 	}
    629 
    630 	return pos;
    631 }
    632 
    633 
    634 char * tncs_if_tnccs_start(struct tncs_data *tncs)
    635 {
    636 	char *buf = os_malloc(1000);
    637 	if (buf == NULL)
    638 		return NULL;
    639 	tncs->last_batchid++;
    640 	os_snprintf(buf, 1000, IF_TNCCS_START, tncs->last_batchid);
    641 	return buf;
    642 }
    643 
    644 
    645 char * tncs_if_tnccs_end(void)
    646 {
    647 	char *buf = os_malloc(100);
    648 	if (buf == NULL)
    649 		return NULL;
    650 	os_snprintf(buf, 100, IF_TNCCS_END);
    651 	return buf;
    652 }
    653 
    654 
    655 static int tncs_get_type(char *start, unsigned int *type)
    656 {
    657 	char *pos = os_strstr(start, "<Type>");
    658 	if (pos == NULL)
    659 		return -1;
    660 	pos += 6;
    661 	*type = strtoul(pos, NULL, 16);
    662 	return 0;
    663 }
    664 
    665 
    666 static unsigned char * tncs_get_base64(char *start, size_t *decoded_len)
    667 {
    668 	char *pos, *pos2;
    669 	unsigned char *decoded;
    670 
    671 	pos = os_strstr(start, "<Base64>");
    672 	if (pos == NULL)
    673 		return NULL;
    674 
    675 	pos += 8;
    676 	pos2 = os_strstr(pos, "</Base64>");
    677 	if (pos2 == NULL)
    678 		return NULL;
    679 	*pos2 = '\0';
    680 
    681 	decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
    682 				decoded_len);
    683 	*pos2 = '<';
    684 	if (decoded == NULL) {
    685 		wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
    686 	}
    687 
    688 	return decoded;
    689 }
    690 
    691 
    692 static enum tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs)
    693 {
    694 	enum IMV_Action_Recommendation rec;
    695 	struct tnc_if_imv *imv;
    696 	TNC_ConnectionState state;
    697 	char *txt;
    698 
    699 	wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs");
    700 
    701 	if (tncs->done)
    702 		return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
    703 
    704 	tncs_solicit_recommendation(tncs);
    705 
    706 	/* Select the most restrictive recommendation */
    707 	rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION;
    708 	for (imv = tncs->imv; imv; imv = imv->next) {
    709 		TNC_IMV_Action_Recommendation irec;
    710 		irec = tncs->imv_data[imv->imvID].recommendation;
    711 		if (irec == TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
    712 			rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS;
    713 		if (irec == TNC_IMV_ACTION_RECOMMENDATION_ISOLATE &&
    714 		    rec != TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
    715 			rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE;
    716 		if (irec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW &&
    717 		    rec == TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION)
    718 			rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW;
    719 	}
    720 
    721 	wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec);
    722 	tncs->recommendation = rec;
    723 	tncs->done = 1;
    724 
    725 	txt = NULL;
    726 	switch (rec) {
    727 	case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
    728 	case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
    729 		txt = "allow";
    730 		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
    731 		break;
    732 	case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
    733 		txt = "isolate";
    734 		state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
    735 		break;
    736 	case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
    737 		txt = "none";
    738 		state = TNC_CONNECTION_STATE_ACCESS_NONE;
    739 		break;
    740 	default:
    741 		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
    742 		break;
    743 	}
    744 
    745 	if (txt) {
    746 		os_free(tncs->tncs_message);
    747 		tncs->tncs_message = os_zalloc(200);
    748 		if (tncs->tncs_message) {
    749 			os_snprintf(tncs->tncs_message, 199,
    750 				    "<TNCC-TNCS-Message><Type>%08X</Type>"
    751 				    "<XML><TNCCS-Recommendation type=\"%s\">"
    752 				    "</TNCCS-Recommendation></XML>"
    753 				    "</TNCC-TNCS-Message>",
    754 				    TNC_TNCCS_RECOMMENDATION, txt);
    755 		}
    756 	}
    757 
    758 	for (imv = tncs->imv; imv; imv = imv->next) {
    759 		tncs_imv_notify_connection_change(imv, tncs->connectionID,
    760 						  state);
    761 	}
    762 
    763 	switch (rec) {
    764 	case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
    765 		return TNCCS_RECOMMENDATION_ALLOW;
    766 	case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
    767 		return TNCCS_RECOMMENDATION_NO_ACCESS;
    768 	case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
    769 		return TNCCS_RECOMMENDATION_ISOLATE;
    770 	case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
    771 		return TNCCS_RECOMMENDATION_NO_RECOMMENDATION;
    772 	default:
    773 		return TNCCS_PROCESS_ERROR;
    774 	}
    775 }
    776 
    777 
    778 enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
    779 					    const u8 *msg, size_t len)
    780 {
    781 	char *buf, *start, *end, *pos, *pos2, *payload;
    782 	unsigned int batch_id;
    783 	unsigned char *decoded;
    784 	size_t decoded_len;
    785 
    786 	buf = dup_binstr(msg, len);
    787 	if (buf == NULL)
    788 		return TNCCS_PROCESS_ERROR;
    789 
    790 	start = os_strstr(buf, "<TNCCS-Batch ");
    791 	end = os_strstr(buf, "</TNCCS-Batch>");
    792 	if (start == NULL || end == NULL || start > end) {
    793 		os_free(buf);
    794 		return TNCCS_PROCESS_ERROR;
    795 	}
    796 
    797 	start += 13;
    798 	while (*start == ' ')
    799 		start++;
    800 	*end = '\0';
    801 
    802 	pos = os_strstr(start, "BatchId=");
    803 	if (pos == NULL) {
    804 		os_free(buf);
    805 		return TNCCS_PROCESS_ERROR;
    806 	}
    807 
    808 	pos += 8;
    809 	if (*pos == '"')
    810 		pos++;
    811 	batch_id = atoi(pos);
    812 	wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
    813 		   batch_id);
    814 	if (batch_id != tncs->last_batchid + 1) {
    815 		wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
    816 			   "%u (expected %u)",
    817 			   batch_id, tncs->last_batchid + 1);
    818 		os_free(buf);
    819 		return TNCCS_PROCESS_ERROR;
    820 	}
    821 	tncs->last_batchid = batch_id;
    822 
    823 	while (*pos != '\0' && *pos != '>')
    824 		pos++;
    825 	if (*pos == '\0') {
    826 		os_free(buf);
    827 		return TNCCS_PROCESS_ERROR;
    828 	}
    829 	pos++;
    830 	payload = start;
    831 
    832 	/*
    833 	 * <IMC-IMV-Message>
    834 	 * <Type>01234567</Type>
    835 	 * <Base64>foo==</Base64>
    836 	 * </IMC-IMV-Message>
    837 	 */
    838 
    839 	while (*start) {
    840 		char *endpos;
    841 		unsigned int type;
    842 
    843 		pos = os_strstr(start, "<IMC-IMV-Message>");
    844 		if (pos == NULL)
    845 			break;
    846 		start = pos + 17;
    847 		end = os_strstr(start, "</IMC-IMV-Message>");
    848 		if (end == NULL)
    849 			break;
    850 		*end = '\0';
    851 		endpos = end;
    852 		end += 18;
    853 
    854 		if (tncs_get_type(start, &type) < 0) {
    855 			*endpos = '<';
    856 			start = end;
    857 			continue;
    858 		}
    859 		wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
    860 
    861 		decoded = tncs_get_base64(start, &decoded_len);
    862 		if (decoded == NULL) {
    863 			*endpos = '<';
    864 			start = end;
    865 			continue;
    866 		}
    867 
    868 		tncs_send_to_imvs(tncs, type, decoded, decoded_len);
    869 
    870 		os_free(decoded);
    871 
    872 		start = end;
    873 	}
    874 
    875 	/*
    876 	 * <TNCC-TNCS-Message>
    877 	 * <Type>01234567</Type>
    878 	 * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
    879 	 * <Base64>foo==</Base64>
    880 	 * </TNCC-TNCS-Message>
    881 	 */
    882 
    883 	start = payload;
    884 	while (*start) {
    885 		unsigned int type;
    886 		char *xml, *xmlend, *endpos;
    887 
    888 		pos = os_strstr(start, "<TNCC-TNCS-Message>");
    889 		if (pos == NULL)
    890 			break;
    891 		start = pos + 19;
    892 		end = os_strstr(start, "</TNCC-TNCS-Message>");
    893 		if (end == NULL)
    894 			break;
    895 		*end = '\0';
    896 		endpos = end;
    897 		end += 20;
    898 
    899 		if (tncs_get_type(start, &type) < 0) {
    900 			*endpos = '<';
    901 			start = end;
    902 			continue;
    903 		}
    904 		wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
    905 			   type);
    906 
    907 		/* Base64 OR XML */
    908 		decoded = NULL;
    909 		xml = NULL;
    910 		xmlend = NULL;
    911 		pos = os_strstr(start, "<XML>");
    912 		if (pos) {
    913 			pos += 5;
    914 			pos2 = os_strstr(pos, "</XML>");
    915 			if (pos2 == NULL) {
    916 				*endpos = '<';
    917 				start = end;
    918 				continue;
    919 			}
    920 			xmlend = pos2;
    921 			xml = pos;
    922 		} else {
    923 			decoded = tncs_get_base64(start, &decoded_len);
    924 			if (decoded == NULL) {
    925 				*endpos = '<';
    926 				start = end;
    927 				continue;
    928 			}
    929 		}
    930 
    931 		if (decoded) {
    932 			wpa_hexdump_ascii(MSG_MSGDUMP,
    933 					  "TNC: TNCC-TNCS-Message Base64",
    934 					  decoded, decoded_len);
    935 			os_free(decoded);
    936 		}
    937 
    938 		if (xml) {
    939 			wpa_hexdump_ascii(MSG_MSGDUMP,
    940 					  "TNC: TNCC-TNCS-Message XML",
    941 					  (unsigned char *) xml,
    942 					  xmlend - xml);
    943 		}
    944 
    945 		start = end;
    946 	}
    947 
    948 	os_free(buf);
    949 
    950 	tncs_batch_ending(tncs);
    951 
    952 	if (tncs_total_send_len(tncs) == 0)
    953 		return tncs_derive_recommendation(tncs);
    954 
    955 	return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
    956 }
    957 
    958 
    959 static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end,
    960 					  int *error)
    961 {
    962 	struct tnc_if_imv *imv;
    963 	char *pos, *pos2;
    964 
    965 	if (id >= TNC_MAX_IMV_ID) {
    966 		wpa_printf(MSG_DEBUG, "TNC: Too many IMVs");
    967 		return NULL;
    968 	}
    969 
    970 	imv = os_zalloc(sizeof(*imv));
    971 	if (imv == NULL) {
    972 		*error = 1;
    973 		return NULL;
    974 	}
    975 
    976 	imv->imvID = id;
    977 
    978 	pos = start;
    979 	wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos);
    980 	if (pos + 1 >= end || *pos != '"') {
    981 		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
    982 			   "(no starting quotation mark)", start);
    983 		os_free(imv);
    984 		return NULL;
    985 	}
    986 
    987 	pos++;
    988 	pos2 = pos;
    989 	while (pos2 < end && *pos2 != '"')
    990 		pos2++;
    991 	if (pos2 >= end) {
    992 		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
    993 			   "(no ending quotation mark)", start);
    994 		os_free(imv);
    995 		return NULL;
    996 	}
    997 	*pos2 = '\0';
    998 	wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
    999 	imv->name = os_strdup(pos);
   1000 
   1001 	pos = pos2 + 1;
   1002 	if (pos >= end || *pos != ' ') {
   1003 		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
   1004 			   "(no space after name)", start);
   1005 		os_free(imv);
   1006 		return NULL;
   1007 	}
   1008 
   1009 	pos++;
   1010 	wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos);
   1011 	imv->path = os_strdup(pos);
   1012 
   1013 	return imv;
   1014 }
   1015 
   1016 
   1017 static int tncs_read_config(struct tncs_global *global)
   1018 {
   1019 	char *config, *end, *pos, *line_end;
   1020 	size_t config_len;
   1021 	struct tnc_if_imv *imv, *last;
   1022 	int id = 0;
   1023 
   1024 	last = NULL;
   1025 
   1026 	config = os_readfile(TNC_CONFIG_FILE, &config_len);
   1027 	if (config == NULL) {
   1028 		wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
   1029 			   "file '%s'", TNC_CONFIG_FILE);
   1030 		return -1;
   1031 	}
   1032 
   1033 	end = config + config_len;
   1034 	for (pos = config; pos < end; pos = line_end + 1) {
   1035 		line_end = pos;
   1036 		while (*line_end != '\n' && *line_end != '\r' &&
   1037 		       line_end < end)
   1038 			line_end++;
   1039 		*line_end = '\0';
   1040 
   1041 		if (os_strncmp(pos, "IMV ", 4) == 0) {
   1042 			int error = 0;
   1043 
   1044 			imv = tncs_parse_imv(id++, pos + 4, line_end, &error);
   1045 			if (error)
   1046 				return -1;
   1047 			if (imv) {
   1048 				if (last == NULL)
   1049 					global->imv = imv;
   1050 				else
   1051 					last->next = imv;
   1052 				last = imv;
   1053 			}
   1054 		}
   1055 	}
   1056 
   1057 	os_free(config);
   1058 
   1059 	return 0;
   1060 }
   1061 
   1062 
   1063 struct tncs_data * tncs_init(void)
   1064 {
   1065 	struct tncs_data *tncs;
   1066 
   1067 	if (tncs_global_data == NULL)
   1068 		return NULL;
   1069 
   1070 	tncs = os_zalloc(sizeof(*tncs));
   1071 	if (tncs == NULL)
   1072 		return NULL;
   1073 	tncs->imv = tncs_global_data->imv;
   1074 	tncs->connectionID = tncs_global_data->next_conn_id++;
   1075 	tncs->next = tncs_global_data->connections;
   1076 	tncs_global_data->connections = tncs;
   1077 
   1078 	return tncs;
   1079 }
   1080 
   1081 
   1082 void tncs_deinit(struct tncs_data *tncs)
   1083 {
   1084 	int i;
   1085 	struct tncs_data *prev, *conn;
   1086 
   1087 	if (tncs == NULL)
   1088 		return;
   1089 
   1090 	for (i = 0; i < TNC_MAX_IMV_ID; i++)
   1091 		os_free(tncs->imv_data[i].imv_send);
   1092 
   1093 	prev = NULL;
   1094 	conn = tncs_global_data->connections;
   1095 	while (conn) {
   1096 		if (conn == tncs) {
   1097 			if (prev)
   1098 				prev->next = tncs->next;
   1099 			else
   1100 				tncs_global_data->connections = tncs->next;
   1101 			break;
   1102 		}
   1103 		prev = conn;
   1104 		conn = conn->next;
   1105 	}
   1106 
   1107 	os_free(tncs->tncs_message);
   1108 	os_free(tncs);
   1109 }
   1110 
   1111 
   1112 int tncs_global_init(void)
   1113 {
   1114 	struct tnc_if_imv *imv;
   1115 
   1116 	if (tncs_global_data)
   1117 		return 0;
   1118 
   1119 	tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
   1120 	if (tncs_global_data == NULL)
   1121 		return -1;
   1122 
   1123 	if (tncs_read_config(tncs_global_data) < 0) {
   1124 		wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
   1125 		goto failed;
   1126 	}
   1127 
   1128 	for (imv = tncs_global_data->imv; imv; imv = imv->next) {
   1129 		if (tncs_load_imv(imv)) {
   1130 			wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'",
   1131 				   imv->name);
   1132 			goto failed;
   1133 		}
   1134 	}
   1135 
   1136 	return 0;
   1137 
   1138 failed:
   1139 	tncs_global_deinit();
   1140 	return -1;
   1141 }
   1142 
   1143 
   1144 void tncs_global_deinit(void)
   1145 {
   1146 	struct tnc_if_imv *imv, *prev;
   1147 
   1148 	if (tncs_global_data == NULL)
   1149 		return;
   1150 
   1151 	imv = tncs_global_data->imv;
   1152 	while (imv) {
   1153 		tncs_unload_imv(imv);
   1154 
   1155 		prev = imv;
   1156 		imv = imv->next;
   1157 		os_free(prev);
   1158 	}
   1159 
   1160 	os_free(tncs_global_data);
   1161 	tncs_global_data = NULL;
   1162 }
   1163 
   1164 
   1165 struct wpabuf * tncs_build_soh_request(void)
   1166 {
   1167 	struct wpabuf *buf;
   1168 
   1169 	/*
   1170 	 * Build a SoH Request TLV (to be used inside SoH EAP Extensions
   1171 	 * Method)
   1172 	 */
   1173 
   1174 	buf = wpabuf_alloc(8 + 4);
   1175 	if (buf == NULL)
   1176 		return NULL;
   1177 
   1178 	/* Vendor-Specific TLV (Microsoft) - SoH Request */
   1179 	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
   1180 	wpabuf_put_be16(buf, 8); /* Length */
   1181 
   1182 	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
   1183 
   1184 	wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */
   1185 	wpabuf_put_be16(buf, 0); /* Length */
   1186 
   1187 	return buf;
   1188 }
   1189 
   1190 
   1191 struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
   1192 				 int *failure)
   1193 {
   1194 	wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len);
   1195 	*failure = 0;
   1196 
   1197 	/* TODO: return MS-SoH Response TLV */
   1198 
   1199 	return NULL;
   1200 }
   1201