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