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