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