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 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 37struct 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 77struct 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 96struct tncs_global { 97 struct tnc_if_imv *imv; 98 TNC_ConnectionID next_conn_id; 99 struct tncs_data *connections; 100}; 101 102static struct tncs_global *tncs_global_data = NULL; 103 104 105static 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 121static 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 */ 143TNC_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 176TNC_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 225TNC_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 236TNC_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 263TNC_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 277TNC_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 290TNC_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 325static 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 335static 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 374static 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 390static 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 407static 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 421static 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 440static 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 471static 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 478static 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 489static 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 513static 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 537static 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 555static 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 574void 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 595size_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 608u8 * 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 636char * 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 647char * 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 657static 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 668static 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 694static 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 780enum 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 961static 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 1019static 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 1065struct 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 1084void 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 1114int 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 1140failed: 1141 tncs_global_deinit(); 1142 return -1; 1143} 1144 1145 1146void 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 1167struct 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 1193struct 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