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