1/* 2 * EAP-TNC - TNCC (IF-IMC and IF-TNCCS) 3 * Copyright (c) 2007, 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#ifndef CONFIG_NATIVE_WINDOWS 11#include <dlfcn.h> 12#endif /* CONFIG_NATIVE_WINDOWS */ 13 14#include "common.h" 15#include "base64.h" 16#include "common/tnc.h" 17#include "tncc.h" 18#include "eap_common/eap_tlv_common.h" 19#include "eap_common/eap_defs.h" 20 21 22#ifdef UNICODE 23#define TSTR "%S" 24#else /* UNICODE */ 25#define TSTR "%s" 26#endif /* UNICODE */ 27 28 29#ifndef TNC_CONFIG_FILE 30#define TNC_CONFIG_FILE "/etc/tnc_config" 31#endif /* TNC_CONFIG_FILE */ 32#define TNC_WINREG_PATH TEXT("SOFTWARE\\Trusted Computing Group\\TNC\\IMCs") 33#define IF_TNCCS_START \ 34"<?xml version=\"1.0\"?>\n" \ 35"<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \ 36"xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \ 37"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \ 38"xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \ 39"IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n" 40#define IF_TNCCS_END "\n</TNCCS-Batch>" 41 42/* TNC IF-IMC */ 43 44/* IF-TNCCS-SOH - SSoH and SSoHR Attributes */ 45enum { 46 SSOH_MS_MACHINE_INVENTORY = 1, 47 SSOH_MS_QUARANTINE_STATE = 2, 48 SSOH_MS_PACKET_INFO = 3, 49 SSOH_MS_SYSTEMGENERATED_IDS = 4, 50 SSOH_MS_MACHINENAME = 5, 51 SSOH_MS_CORRELATIONID = 6, 52 SSOH_MS_INSTALLED_SHVS = 7, 53 SSOH_MS_MACHINE_INVENTORY_EX = 8 54}; 55 56struct tnc_if_imc { 57 struct tnc_if_imc *next; 58 char *name; 59 char *path; 60 void *dlhandle; /* from dlopen() */ 61 TNC_IMCID imcID; 62 TNC_ConnectionID connectionID; 63 TNC_MessageTypeList supported_types; 64 size_t num_supported_types; 65 u8 *imc_send; 66 size_t imc_send_len; 67 68 /* Functions implemented by IMCs (with TNC_IMC_ prefix) */ 69 TNC_Result (*Initialize)( 70 TNC_IMCID imcID, 71 TNC_Version minVersion, 72 TNC_Version maxVersion, 73 TNC_Version *pOutActualVersion); 74 TNC_Result (*NotifyConnectionChange)( 75 TNC_IMCID imcID, 76 TNC_ConnectionID connectionID, 77 TNC_ConnectionState newState); 78 TNC_Result (*BeginHandshake)( 79 TNC_IMCID imcID, 80 TNC_ConnectionID connectionID); 81 TNC_Result (*ReceiveMessage)( 82 TNC_IMCID imcID, 83 TNC_ConnectionID connectionID, 84 TNC_BufferReference messageBuffer, 85 TNC_UInt32 messageLength, 86 TNC_MessageType messageType); 87 TNC_Result (*BatchEnding)( 88 TNC_IMCID imcID, 89 TNC_ConnectionID connectionID); 90 TNC_Result (*Terminate)(TNC_IMCID imcID); 91 TNC_Result (*ProvideBindFunction)( 92 TNC_IMCID imcID, 93 TNC_TNCC_BindFunctionPointer bindFunction); 94}; 95 96struct tncc_data { 97 struct tnc_if_imc *imc; 98 unsigned int last_batchid; 99}; 100 101#define TNC_MAX_IMC_ID 10 102static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL }; 103 104 105/* TNCC functions that IMCs can call */ 106 107TNC_Result TNC_TNCC_ReportMessageTypes( 108 TNC_IMCID imcID, 109 TNC_MessageTypeList supportedTypes, 110 TNC_UInt32 typeCount) 111{ 112 TNC_UInt32 i; 113 struct tnc_if_imc *imc; 114 115 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_ReportMessageTypes(imcID=%lu " 116 "typeCount=%lu)", 117 (unsigned long) imcID, (unsigned long) typeCount); 118 119 for (i = 0; i < typeCount; i++) { 120 wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu", 121 i, supportedTypes[i]); 122 } 123 124 if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) 125 return TNC_RESULT_INVALID_PARAMETER; 126 127 imc = tnc_imc[imcID]; 128 os_free(imc->supported_types); 129 imc->supported_types = 130 os_malloc(typeCount * sizeof(TNC_MessageType)); 131 if (imc->supported_types == NULL) 132 return TNC_RESULT_FATAL; 133 os_memcpy(imc->supported_types, supportedTypes, 134 typeCount * sizeof(TNC_MessageType)); 135 imc->num_supported_types = typeCount; 136 137 return TNC_RESULT_SUCCESS; 138} 139 140 141TNC_Result TNC_TNCC_SendMessage( 142 TNC_IMCID imcID, 143 TNC_ConnectionID connectionID, 144 TNC_BufferReference message, 145 TNC_UInt32 messageLength, 146 TNC_MessageType messageType) 147{ 148 struct tnc_if_imc *imc; 149 unsigned char *b64; 150 size_t b64len; 151 152 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage(imcID=%lu " 153 "connectionID=%lu messageType=%lu)", 154 imcID, connectionID, messageType); 155 wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage", 156 message, messageLength); 157 158 if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) 159 return TNC_RESULT_INVALID_PARAMETER; 160 161 b64 = base64_encode(message, messageLength, &b64len); 162 if (b64 == NULL) 163 return TNC_RESULT_FATAL; 164 165 imc = tnc_imc[imcID]; 166 os_free(imc->imc_send); 167 imc->imc_send_len = 0; 168 imc->imc_send = os_zalloc(b64len + 100); 169 if (imc->imc_send == NULL) { 170 os_free(b64); 171 return TNC_RESULT_OTHER; 172 } 173 174 imc->imc_send_len = 175 os_snprintf((char *) imc->imc_send, b64len + 100, 176 "<IMC-IMV-Message><Type>%08X</Type>" 177 "<Base64>%s</Base64></IMC-IMV-Message>", 178 (unsigned int) messageType, b64); 179 180 os_free(b64); 181 182 return TNC_RESULT_SUCCESS; 183} 184 185 186TNC_Result TNC_TNCC_RequestHandshakeRetry( 187 TNC_IMCID imcID, 188 TNC_ConnectionID connectionID, 189 TNC_RetryReason reason) 190{ 191 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_RequestHandshakeRetry"); 192 193 if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) 194 return TNC_RESULT_INVALID_PARAMETER; 195 196 /* 197 * TODO: trigger a call to eapol_sm_request_reauth(). This would 198 * require that the IMC continues to be loaded in memory afer 199 * authentication.. 200 */ 201 202 return TNC_RESULT_SUCCESS; 203} 204 205 206TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity, 207 const char *message) 208{ 209 wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu " 210 "severity==%lu message='%s')", 211 imcID, severity, message); 212 return TNC_RESULT_SUCCESS; 213} 214 215 216TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID, 217 const char *message) 218{ 219 wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu " 220 "connectionID==%lu message='%s')", 221 imcID, connectionID, message); 222 return TNC_RESULT_SUCCESS; 223} 224 225 226TNC_Result TNC_TNCC_BindFunction( 227 TNC_IMCID imcID, 228 char *functionName, 229 void **pOutfunctionPointer) 230{ 231 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_BindFunction(imcID=%lu, " 232 "functionName='%s')", (unsigned long) imcID, functionName); 233 234 if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) 235 return TNC_RESULT_INVALID_PARAMETER; 236 237 if (pOutfunctionPointer == NULL) 238 return TNC_RESULT_INVALID_PARAMETER; 239 240 if (os_strcmp(functionName, "TNC_TNCC_ReportMessageTypes") == 0) 241 *pOutfunctionPointer = TNC_TNCC_ReportMessageTypes; 242 else if (os_strcmp(functionName, "TNC_TNCC_SendMessage") == 0) 243 *pOutfunctionPointer = TNC_TNCC_SendMessage; 244 else if (os_strcmp(functionName, "TNC_TNCC_RequestHandshakeRetry") == 245 0) 246 *pOutfunctionPointer = TNC_TNCC_RequestHandshakeRetry; 247 else if (os_strcmp(functionName, "TNC_9048_LogMessage") == 0) 248 *pOutfunctionPointer = TNC_9048_LogMessage; 249 else if (os_strcmp(functionName, "TNC_9048_UserMessage") == 0) 250 *pOutfunctionPointer = TNC_9048_UserMessage; 251 else 252 *pOutfunctionPointer = NULL; 253 254 return TNC_RESULT_SUCCESS; 255} 256 257 258static void * tncc_get_sym(void *handle, char *func) 259{ 260 void *fptr; 261 262#ifdef CONFIG_NATIVE_WINDOWS 263#ifdef _WIN32_WCE 264 fptr = GetProcAddressA(handle, func); 265#else /* _WIN32_WCE */ 266 fptr = GetProcAddress(handle, func); 267#endif /* _WIN32_WCE */ 268#else /* CONFIG_NATIVE_WINDOWS */ 269 fptr = dlsym(handle, func); 270#endif /* CONFIG_NATIVE_WINDOWS */ 271 272 return fptr; 273} 274 275 276static int tncc_imc_resolve_funcs(struct tnc_if_imc *imc) 277{ 278 void *handle = imc->dlhandle; 279 280 /* Mandatory IMC functions */ 281 imc->Initialize = tncc_get_sym(handle, "TNC_IMC_Initialize"); 282 if (imc->Initialize == NULL) { 283 wpa_printf(MSG_ERROR, "TNC: IMC does not export " 284 "TNC_IMC_Initialize"); 285 return -1; 286 } 287 288 imc->BeginHandshake = tncc_get_sym(handle, "TNC_IMC_BeginHandshake"); 289 if (imc->BeginHandshake == NULL) { 290 wpa_printf(MSG_ERROR, "TNC: IMC does not export " 291 "TNC_IMC_BeginHandshake"); 292 return -1; 293 } 294 295 imc->ProvideBindFunction = 296 tncc_get_sym(handle, "TNC_IMC_ProvideBindFunction"); 297 if (imc->ProvideBindFunction == NULL) { 298 wpa_printf(MSG_ERROR, "TNC: IMC does not export " 299 "TNC_IMC_ProvideBindFunction"); 300 return -1; 301 } 302 303 /* Optional IMC functions */ 304 imc->NotifyConnectionChange = 305 tncc_get_sym(handle, "TNC_IMC_NotifyConnectionChange"); 306 imc->ReceiveMessage = tncc_get_sym(handle, "TNC_IMC_ReceiveMessage"); 307 imc->BatchEnding = tncc_get_sym(handle, "TNC_IMC_BatchEnding"); 308 imc->Terminate = tncc_get_sym(handle, "TNC_IMC_Terminate"); 309 310 return 0; 311} 312 313 314static int tncc_imc_initialize(struct tnc_if_imc *imc) 315{ 316 TNC_Result res; 317 TNC_Version imc_ver; 318 319 wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Initialize for IMC '%s'", 320 imc->name); 321 res = imc->Initialize(imc->imcID, TNC_IFIMC_VERSION_1, 322 TNC_IFIMC_VERSION_1, &imc_ver); 323 wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Initialize: res=%lu imc_ver=%lu", 324 (unsigned long) res, (unsigned long) imc_ver); 325 326 return res == TNC_RESULT_SUCCESS ? 0 : -1; 327} 328 329 330static int tncc_imc_terminate(struct tnc_if_imc *imc) 331{ 332 TNC_Result res; 333 334 if (imc->Terminate == NULL) 335 return 0; 336 337 wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Terminate for IMC '%s'", 338 imc->name); 339 res = imc->Terminate(imc->imcID); 340 wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Terminate: %lu", 341 (unsigned long) res); 342 343 return res == TNC_RESULT_SUCCESS ? 0 : -1; 344} 345 346 347static int tncc_imc_provide_bind_function(struct tnc_if_imc *imc) 348{ 349 TNC_Result res; 350 351 wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_ProvideBindFunction for " 352 "IMC '%s'", imc->name); 353 res = imc->ProvideBindFunction(imc->imcID, TNC_TNCC_BindFunction); 354 wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_ProvideBindFunction: res=%lu", 355 (unsigned long) res); 356 357 return res == TNC_RESULT_SUCCESS ? 0 : -1; 358} 359 360 361static int tncc_imc_notify_connection_change(struct tnc_if_imc *imc, 362 TNC_ConnectionState state) 363{ 364 TNC_Result res; 365 366 if (imc->NotifyConnectionChange == NULL) 367 return 0; 368 369 wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_NotifyConnectionChange(%d)" 370 " for IMC '%s'", (int) state, imc->name); 371 res = imc->NotifyConnectionChange(imc->imcID, imc->connectionID, 372 state); 373 wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu", 374 (unsigned long) res); 375 376 return res == TNC_RESULT_SUCCESS ? 0 : -1; 377} 378 379 380static int tncc_imc_begin_handshake(struct tnc_if_imc *imc) 381{ 382 TNC_Result res; 383 384 wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_BeginHandshake for IMC " 385 "'%s'", imc->name); 386 res = imc->BeginHandshake(imc->imcID, imc->connectionID); 387 wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_BeginHandshake: %lu", 388 (unsigned long) res); 389 390 return res == TNC_RESULT_SUCCESS ? 0 : -1; 391} 392 393 394static int tncc_load_imc(struct tnc_if_imc *imc) 395{ 396 if (imc->path == NULL) { 397 wpa_printf(MSG_DEBUG, "TNC: No IMC configured"); 398 return -1; 399 } 400 401 wpa_printf(MSG_DEBUG, "TNC: Opening IMC: %s (%s)", 402 imc->name, imc->path); 403#ifdef CONFIG_NATIVE_WINDOWS 404#ifdef UNICODE 405 { 406 TCHAR *lib = wpa_strdup_tchar(imc->path); 407 if (lib == NULL) 408 return -1; 409 imc->dlhandle = LoadLibrary(lib); 410 os_free(lib); 411 } 412#else /* UNICODE */ 413 imc->dlhandle = LoadLibrary(imc->path); 414#endif /* UNICODE */ 415 if (imc->dlhandle == NULL) { 416 wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %d", 417 imc->name, imc->path, (int) GetLastError()); 418 return -1; 419 } 420#else /* CONFIG_NATIVE_WINDOWS */ 421 imc->dlhandle = dlopen(imc->path, RTLD_LAZY); 422 if (imc->dlhandle == NULL) { 423 wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %s", 424 imc->name, imc->path, dlerror()); 425 return -1; 426 } 427#endif /* CONFIG_NATIVE_WINDOWS */ 428 429 if (tncc_imc_resolve_funcs(imc) < 0) { 430 wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMC functions"); 431 return -1; 432 } 433 434 if (tncc_imc_initialize(imc) < 0 || 435 tncc_imc_provide_bind_function(imc) < 0) { 436 wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMC"); 437 return -1; 438 } 439 440 return 0; 441} 442 443 444static void tncc_unload_imc(struct tnc_if_imc *imc) 445{ 446 tncc_imc_terminate(imc); 447 tnc_imc[imc->imcID] = NULL; 448 449 if (imc->dlhandle) { 450#ifdef CONFIG_NATIVE_WINDOWS 451 FreeLibrary(imc->dlhandle); 452#else /* CONFIG_NATIVE_WINDOWS */ 453 dlclose(imc->dlhandle); 454#endif /* CONFIG_NATIVE_WINDOWS */ 455 } 456 os_free(imc->name); 457 os_free(imc->path); 458 os_free(imc->supported_types); 459 os_free(imc->imc_send); 460} 461 462 463static int tncc_supported_type(struct tnc_if_imc *imc, unsigned int type) 464{ 465 size_t i; 466 unsigned int vendor, subtype; 467 468 if (imc == NULL || imc->supported_types == NULL) 469 return 0; 470 471 vendor = type >> 8; 472 subtype = type & 0xff; 473 474 for (i = 0; i < imc->num_supported_types; i++) { 475 unsigned int svendor, ssubtype; 476 svendor = imc->supported_types[i] >> 8; 477 ssubtype = imc->supported_types[i] & 0xff; 478 if ((vendor == svendor || svendor == TNC_VENDORID_ANY) && 479 (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY)) 480 return 1; 481 } 482 483 return 0; 484} 485 486 487static void tncc_send_to_imcs(struct tncc_data *tncc, unsigned int type, 488 const u8 *msg, size_t len) 489{ 490 struct tnc_if_imc *imc; 491 TNC_Result res; 492 493 wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMC(s)", msg, len); 494 495 for (imc = tncc->imc; imc; imc = imc->next) { 496 if (imc->ReceiveMessage == NULL || 497 !tncc_supported_type(imc, type)) 498 continue; 499 500 wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMC '%s'", 501 imc->name); 502 res = imc->ReceiveMessage(imc->imcID, imc->connectionID, 503 (TNC_BufferReference) msg, len, 504 type); 505 wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu", 506 (unsigned long) res); 507 } 508} 509 510 511void tncc_init_connection(struct tncc_data *tncc) 512{ 513 struct tnc_if_imc *imc; 514 515 for (imc = tncc->imc; imc; imc = imc->next) { 516 tncc_imc_notify_connection_change( 517 imc, TNC_CONNECTION_STATE_CREATE); 518 tncc_imc_notify_connection_change( 519 imc, TNC_CONNECTION_STATE_HANDSHAKE); 520 521 os_free(imc->imc_send); 522 imc->imc_send = NULL; 523 imc->imc_send_len = 0; 524 525 tncc_imc_begin_handshake(imc); 526 } 527} 528 529 530size_t tncc_total_send_len(struct tncc_data *tncc) 531{ 532 struct tnc_if_imc *imc; 533 534 size_t len = 0; 535 for (imc = tncc->imc; imc; imc = imc->next) 536 len += imc->imc_send_len; 537 return len; 538} 539 540 541u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos) 542{ 543 struct tnc_if_imc *imc; 544 545 for (imc = tncc->imc; imc; imc = imc->next) { 546 if (imc->imc_send == NULL) 547 continue; 548 549 os_memcpy(pos, imc->imc_send, imc->imc_send_len); 550 pos += imc->imc_send_len; 551 os_free(imc->imc_send); 552 imc->imc_send = NULL; 553 imc->imc_send_len = 0; 554 } 555 556 return pos; 557} 558 559 560char * tncc_if_tnccs_start(struct tncc_data *tncc) 561{ 562 char *buf = os_malloc(1000); 563 if (buf == NULL) 564 return NULL; 565 tncc->last_batchid++; 566 os_snprintf(buf, 1000, IF_TNCCS_START, tncc->last_batchid); 567 return buf; 568} 569 570 571char * tncc_if_tnccs_end(void) 572{ 573 char *buf = os_malloc(100); 574 if (buf == NULL) 575 return NULL; 576 os_snprintf(buf, 100, IF_TNCCS_END); 577 return buf; 578} 579 580 581static void tncc_notify_recommendation(struct tncc_data *tncc, 582 enum tncc_process_res res) 583{ 584 TNC_ConnectionState state; 585 struct tnc_if_imc *imc; 586 587 switch (res) { 588 case TNCCS_RECOMMENDATION_ALLOW: 589 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED; 590 break; 591 case TNCCS_RECOMMENDATION_NONE: 592 state = TNC_CONNECTION_STATE_ACCESS_NONE; 593 break; 594 case TNCCS_RECOMMENDATION_ISOLATE: 595 state = TNC_CONNECTION_STATE_ACCESS_ISOLATED; 596 break; 597 default: 598 state = TNC_CONNECTION_STATE_ACCESS_NONE; 599 break; 600 } 601 602 for (imc = tncc->imc; imc; imc = imc->next) 603 tncc_imc_notify_connection_change(imc, state); 604} 605 606 607static int tncc_get_type(char *start, unsigned int *type) 608{ 609 char *pos = os_strstr(start, "<Type>"); 610 if (pos == NULL) 611 return -1; 612 pos += 6; 613 *type = strtoul(pos, NULL, 16); 614 return 0; 615} 616 617 618static unsigned char * tncc_get_base64(char *start, size_t *decoded_len) 619{ 620 char *pos, *pos2; 621 unsigned char *decoded; 622 623 pos = os_strstr(start, "<Base64>"); 624 if (pos == NULL) 625 return NULL; 626 627 pos += 8; 628 pos2 = os_strstr(pos, "</Base64>"); 629 if (pos2 == NULL) 630 return NULL; 631 *pos2 = '\0'; 632 633 decoded = base64_decode((unsigned char *) pos, os_strlen(pos), 634 decoded_len); 635 *pos2 = '<'; 636 if (decoded == NULL) { 637 wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data"); 638 } 639 640 return decoded; 641} 642 643 644static enum tncc_process_res tncc_get_recommendation(char *start) 645{ 646 char *pos, *pos2, saved; 647 int recom; 648 649 pos = os_strstr(start, "<TNCCS-Recommendation "); 650 if (pos == NULL) 651 return TNCCS_RECOMMENDATION_ERROR; 652 653 pos += 21; 654 pos = os_strstr(pos, " type="); 655 if (pos == NULL) 656 return TNCCS_RECOMMENDATION_ERROR; 657 pos += 6; 658 659 if (*pos == '"') 660 pos++; 661 662 pos2 = pos; 663 while (*pos2 != '\0' && *pos2 != '"' && *pos2 != '>') 664 pos2++; 665 666 if (*pos2 == '\0') 667 return TNCCS_RECOMMENDATION_ERROR; 668 669 saved = *pos2; 670 *pos2 = '\0'; 671 wpa_printf(MSG_DEBUG, "TNC: TNCCS-Recommendation: '%s'", pos); 672 673 recom = TNCCS_RECOMMENDATION_ERROR; 674 if (os_strcmp(pos, "allow") == 0) 675 recom = TNCCS_RECOMMENDATION_ALLOW; 676 else if (os_strcmp(pos, "none") == 0) 677 recom = TNCCS_RECOMMENDATION_NONE; 678 else if (os_strcmp(pos, "isolate") == 0) 679 recom = TNCCS_RECOMMENDATION_ISOLATE; 680 681 *pos2 = saved; 682 683 return recom; 684} 685 686 687enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc, 688 const u8 *msg, size_t len) 689{ 690 char *buf, *start, *end, *pos, *pos2, *payload; 691 unsigned int batch_id; 692 unsigned char *decoded; 693 size_t decoded_len; 694 enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION; 695 int recommendation_msg = 0; 696 697 wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Received IF-TNCCS message", 698 msg, len); 699 buf = dup_binstr(msg, len); 700 if (buf == NULL) 701 return TNCCS_PROCESS_ERROR; 702 703 start = os_strstr(buf, "<TNCCS-Batch "); 704 end = os_strstr(buf, "</TNCCS-Batch>"); 705 if (start == NULL || end == NULL || start > end) { 706 os_free(buf); 707 return TNCCS_PROCESS_ERROR; 708 } 709 710 start += 13; 711 while (*start == ' ') 712 start++; 713 *end = '\0'; 714 715 pos = os_strstr(start, "BatchId="); 716 if (pos == NULL) { 717 os_free(buf); 718 return TNCCS_PROCESS_ERROR; 719 } 720 721 pos += 8; 722 if (*pos == '"') 723 pos++; 724 batch_id = atoi(pos); 725 wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u", 726 batch_id); 727 if (batch_id != tncc->last_batchid + 1) { 728 wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId " 729 "%u (expected %u)", 730 batch_id, tncc->last_batchid + 1); 731 os_free(buf); 732 return TNCCS_PROCESS_ERROR; 733 } 734 tncc->last_batchid = batch_id; 735 736 while (*pos != '\0' && *pos != '>') 737 pos++; 738 if (*pos == '\0') { 739 os_free(buf); 740 return TNCCS_PROCESS_ERROR; 741 } 742 pos++; 743 payload = start; 744 745 /* 746 * <IMC-IMV-Message> 747 * <Type>01234567</Type> 748 * <Base64>foo==</Base64> 749 * </IMC-IMV-Message> 750 */ 751 752 while (*start) { 753 char *endpos; 754 unsigned int type; 755 756 pos = os_strstr(start, "<IMC-IMV-Message>"); 757 if (pos == NULL) 758 break; 759 start = pos + 17; 760 end = os_strstr(start, "</IMC-IMV-Message>"); 761 if (end == NULL) 762 break; 763 *end = '\0'; 764 endpos = end; 765 end += 18; 766 767 if (tncc_get_type(start, &type) < 0) { 768 *endpos = '<'; 769 start = end; 770 continue; 771 } 772 wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type); 773 774 decoded = tncc_get_base64(start, &decoded_len); 775 if (decoded == NULL) { 776 *endpos = '<'; 777 start = end; 778 continue; 779 } 780 781 tncc_send_to_imcs(tncc, type, decoded, decoded_len); 782 783 os_free(decoded); 784 785 start = end; 786 } 787 788 /* 789 * <TNCC-TNCS-Message> 790 * <Type>01234567</Type> 791 * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML> 792 * <Base64>foo==</Base64> 793 * </TNCC-TNCS-Message> 794 */ 795 796 start = payload; 797 while (*start) { 798 unsigned int type; 799 char *xml, *xmlend, *endpos; 800 801 pos = os_strstr(start, "<TNCC-TNCS-Message>"); 802 if (pos == NULL) 803 break; 804 start = pos + 19; 805 end = os_strstr(start, "</TNCC-TNCS-Message>"); 806 if (end == NULL) 807 break; 808 *end = '\0'; 809 endpos = end; 810 end += 20; 811 812 if (tncc_get_type(start, &type) < 0) { 813 *endpos = '<'; 814 start = end; 815 continue; 816 } 817 wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x", 818 type); 819 820 /* Base64 OR XML */ 821 decoded = NULL; 822 xml = NULL; 823 xmlend = NULL; 824 pos = os_strstr(start, "<XML>"); 825 if (pos) { 826 pos += 5; 827 pos2 = os_strstr(pos, "</XML>"); 828 if (pos2 == NULL) { 829 *endpos = '<'; 830 start = end; 831 continue; 832 } 833 xmlend = pos2; 834 xml = pos; 835 } else { 836 decoded = tncc_get_base64(start, &decoded_len); 837 if (decoded == NULL) { 838 *endpos = '<'; 839 start = end; 840 continue; 841 } 842 } 843 844 if (decoded) { 845 wpa_hexdump_ascii(MSG_MSGDUMP, 846 "TNC: TNCC-TNCS-Message Base64", 847 decoded, decoded_len); 848 os_free(decoded); 849 } 850 851 if (xml) { 852 wpa_hexdump_ascii(MSG_MSGDUMP, 853 "TNC: TNCC-TNCS-Message XML", 854 (unsigned char *) xml, 855 xmlend - xml); 856 } 857 858 if (type == TNC_TNCCS_RECOMMENDATION && xml) { 859 /* 860 * <TNCCS-Recommendation type="allow"> 861 * </TNCCS-Recommendation> 862 */ 863 *xmlend = '\0'; 864 res = tncc_get_recommendation(xml); 865 *xmlend = '<'; 866 recommendation_msg = 1; 867 } 868 869 start = end; 870 } 871 872 os_free(buf); 873 874 if (recommendation_msg) 875 tncc_notify_recommendation(tncc, res); 876 877 return res; 878} 879 880 881#ifdef CONFIG_NATIVE_WINDOWS 882static int tncc_read_config_reg(struct tncc_data *tncc, HKEY hive) 883{ 884 HKEY hk, hk2; 885 LONG ret; 886 DWORD i; 887 struct tnc_if_imc *imc, *last; 888 int j; 889 890 last = tncc->imc; 891 while (last && last->next) 892 last = last->next; 893 894 ret = RegOpenKeyEx(hive, TNC_WINREG_PATH, 0, KEY_ENUMERATE_SUB_KEYS, 895 &hk); 896 if (ret != ERROR_SUCCESS) 897 return 0; 898 899 for (i = 0; ; i++) { 900 TCHAR name[255], *val; 901 DWORD namelen, buflen; 902 903 namelen = 255; 904 ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL, 905 NULL); 906 907 if (ret == ERROR_NO_MORE_ITEMS) 908 break; 909 910 if (ret != ERROR_SUCCESS) { 911 wpa_printf(MSG_DEBUG, "TNC: RegEnumKeyEx failed: 0x%x", 912 (unsigned int) ret); 913 break; 914 } 915 916 if (namelen >= 255) 917 namelen = 255 - 1; 918 name[namelen] = '\0'; 919 920 wpa_printf(MSG_DEBUG, "TNC: IMC '" TSTR "'", name); 921 922 ret = RegOpenKeyEx(hk, name, 0, KEY_QUERY_VALUE, &hk2); 923 if (ret != ERROR_SUCCESS) { 924 wpa_printf(MSG_DEBUG, "Could not open IMC key '" TSTR 925 "'", name); 926 continue; 927 } 928 929 ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, NULL, 930 &buflen); 931 if (ret != ERROR_SUCCESS) { 932 wpa_printf(MSG_DEBUG, "TNC: Could not read Path from " 933 "IMC key '" TSTR "'", name); 934 RegCloseKey(hk2); 935 continue; 936 } 937 938 val = os_malloc(buflen); 939 if (val == NULL) { 940 RegCloseKey(hk2); 941 continue; 942 } 943 944 ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, 945 (LPBYTE) val, &buflen); 946 if (ret != ERROR_SUCCESS) { 947 os_free(val); 948 RegCloseKey(hk2); 949 continue; 950 } 951 952 RegCloseKey(hk2); 953 954 wpa_unicode2ascii_inplace(val); 955 wpa_printf(MSG_DEBUG, "TNC: IMC Path '%s'", (char *) val); 956 957 for (j = 0; j < TNC_MAX_IMC_ID; j++) { 958 if (tnc_imc[j] == NULL) 959 break; 960 } 961 if (j >= TNC_MAX_IMC_ID) { 962 wpa_printf(MSG_DEBUG, "TNC: Too many IMCs"); 963 os_free(val); 964 continue; 965 } 966 967 imc = os_zalloc(sizeof(*imc)); 968 if (imc == NULL) { 969 os_free(val); 970 break; 971 } 972 973 imc->imcID = j; 974 975 wpa_unicode2ascii_inplace(name); 976 imc->name = os_strdup((char *) name); 977 imc->path = os_strdup((char *) val); 978 979 os_free(val); 980 981 if (last == NULL) 982 tncc->imc = imc; 983 else 984 last->next = imc; 985 last = imc; 986 987 tnc_imc[imc->imcID] = imc; 988 } 989 990 RegCloseKey(hk); 991 992 return 0; 993} 994 995 996static int tncc_read_config(struct tncc_data *tncc) 997{ 998 if (tncc_read_config_reg(tncc, HKEY_LOCAL_MACHINE) < 0 || 999 tncc_read_config_reg(tncc, HKEY_CURRENT_USER) < 0) 1000 return -1; 1001 return 0; 1002} 1003 1004#else /* CONFIG_NATIVE_WINDOWS */ 1005 1006static struct tnc_if_imc * tncc_parse_imc(char *start, char *end, int *error) 1007{ 1008 struct tnc_if_imc *imc; 1009 char *pos, *pos2; 1010 int i; 1011 1012 for (i = 0; i < TNC_MAX_IMC_ID; i++) { 1013 if (tnc_imc[i] == NULL) 1014 break; 1015 } 1016 if (i >= TNC_MAX_IMC_ID) { 1017 wpa_printf(MSG_DEBUG, "TNC: Too many IMCs"); 1018 return NULL; 1019 } 1020 1021 imc = os_zalloc(sizeof(*imc)); 1022 if (imc == NULL) { 1023 *error = 1; 1024 return NULL; 1025 } 1026 1027 imc->imcID = i; 1028 1029 pos = start; 1030 wpa_printf(MSG_DEBUG, "TNC: Configured IMC: %s", pos); 1031 if (pos + 1 >= end || *pos != '"') { 1032 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' " 1033 "(no starting quotation mark)", start); 1034 os_free(imc); 1035 return NULL; 1036 } 1037 1038 pos++; 1039 pos2 = pos; 1040 while (pos2 < end && *pos2 != '"') 1041 pos2++; 1042 if (pos2 >= end) { 1043 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' " 1044 "(no ending quotation mark)", start); 1045 os_free(imc); 1046 return NULL; 1047 } 1048 *pos2 = '\0'; 1049 wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos); 1050 imc->name = os_strdup(pos); 1051 1052 pos = pos2 + 1; 1053 if (pos >= end || *pos != ' ') { 1054 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' " 1055 "(no space after name)", start); 1056 os_free(imc->name); 1057 os_free(imc); 1058 return NULL; 1059 } 1060 1061 pos++; 1062 wpa_printf(MSG_DEBUG, "TNC: IMC file: '%s'", pos); 1063 imc->path = os_strdup(pos); 1064 tnc_imc[imc->imcID] = imc; 1065 1066 return imc; 1067} 1068 1069 1070static int tncc_read_config(struct tncc_data *tncc) 1071{ 1072 char *config, *end, *pos, *line_end; 1073 size_t config_len; 1074 struct tnc_if_imc *imc, *last; 1075 1076 last = NULL; 1077 1078 config = os_readfile(TNC_CONFIG_FILE, &config_len); 1079 if (config == NULL) { 1080 wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration " 1081 "file '%s'", TNC_CONFIG_FILE); 1082 return -1; 1083 } 1084 1085 end = config + config_len; 1086 for (pos = config; pos < end; pos = line_end + 1) { 1087 line_end = pos; 1088 while (*line_end != '\n' && *line_end != '\r' && 1089 line_end < end) 1090 line_end++; 1091 *line_end = '\0'; 1092 1093 if (os_strncmp(pos, "IMC ", 4) == 0) { 1094 int error = 0; 1095 1096 imc = tncc_parse_imc(pos + 4, line_end, &error); 1097 if (error) { 1098 os_free(config); 1099 return -1; 1100 } 1101 if (imc) { 1102 if (last == NULL) 1103 tncc->imc = imc; 1104 else 1105 last->next = imc; 1106 last = imc; 1107 } 1108 } 1109 } 1110 1111 os_free(config); 1112 1113 return 0; 1114} 1115 1116#endif /* CONFIG_NATIVE_WINDOWS */ 1117 1118 1119struct tncc_data * tncc_init(void) 1120{ 1121 struct tncc_data *tncc; 1122 struct tnc_if_imc *imc; 1123 1124 tncc = os_zalloc(sizeof(*tncc)); 1125 if (tncc == NULL) 1126 return NULL; 1127 1128 /* TODO: 1129 * move loading and Initialize() to a location that is not 1130 * re-initialized for every EAP-TNC session (?) 1131 */ 1132 1133 if (tncc_read_config(tncc) < 0) { 1134 wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration"); 1135 goto failed; 1136 } 1137 1138 for (imc = tncc->imc; imc; imc = imc->next) { 1139 if (tncc_load_imc(imc)) { 1140 wpa_printf(MSG_ERROR, "TNC: Failed to load IMC '%s'", 1141 imc->name); 1142 goto failed; 1143 } 1144 } 1145 1146 return tncc; 1147 1148failed: 1149 tncc_deinit(tncc); 1150 return NULL; 1151} 1152 1153 1154void tncc_deinit(struct tncc_data *tncc) 1155{ 1156 struct tnc_if_imc *imc, *prev; 1157 1158 imc = tncc->imc; 1159 while (imc) { 1160 tncc_unload_imc(imc); 1161 1162 prev = imc; 1163 imc = imc->next; 1164 os_free(prev); 1165 } 1166 1167 os_free(tncc); 1168} 1169 1170 1171static struct wpabuf * tncc_build_soh(int ver) 1172{ 1173 struct wpabuf *buf; 1174 u8 *tlv_len, *tlv_len2, *outer_len, *inner_len, *ssoh_len, *end; 1175 u8 correlation_id[24]; 1176 /* TODO: get correct name */ 1177 char *machinename = "wpa_supplicant@w1.fi"; 1178 1179 if (os_get_random(correlation_id, sizeof(correlation_id))) 1180 return NULL; 1181 wpa_hexdump(MSG_DEBUG, "TNC: SoH Correlation ID", 1182 correlation_id, sizeof(correlation_id)); 1183 1184 buf = wpabuf_alloc(200); 1185 if (buf == NULL) 1186 return NULL; 1187 1188 /* Vendor-Specific TLV (Microsoft) - SoH */ 1189 wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */ 1190 tlv_len = wpabuf_put(buf, 2); /* Length */ 1191 wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */ 1192 wpabuf_put_be16(buf, 0x01); /* TLV Type - SoH TLV */ 1193 tlv_len2 = wpabuf_put(buf, 2); /* Length */ 1194 1195 /* SoH Header */ 1196 wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* Outer Type */ 1197 outer_len = wpabuf_put(buf, 2); 1198 wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */ 1199 wpabuf_put_be16(buf, ver); /* Inner Type */ 1200 inner_len = wpabuf_put(buf, 2); 1201 1202 if (ver == 2) { 1203 /* SoH Mode Sub-Header */ 1204 /* Outer Type */ 1205 wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); 1206 wpabuf_put_be16(buf, 4 + 24 + 1 + 1); /* Length */ 1207 wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */ 1208 /* Value: */ 1209 wpabuf_put_data(buf, correlation_id, sizeof(correlation_id)); 1210 wpabuf_put_u8(buf, 0x01); /* Intent Flag - Request */ 1211 wpabuf_put_u8(buf, 0x00); /* Content-Type Flag */ 1212 } 1213 1214 /* SSoH TLV */ 1215 /* System-Health-Id */ 1216 wpabuf_put_be16(buf, 0x0002); /* Type */ 1217 wpabuf_put_be16(buf, 4); /* Length */ 1218 wpabuf_put_be32(buf, 79616); 1219 /* Vendor-Specific Attribute */ 1220 wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); 1221 ssoh_len = wpabuf_put(buf, 2); 1222 wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */ 1223 1224 /* MS-Packet-Info */ 1225 wpabuf_put_u8(buf, SSOH_MS_PACKET_INFO); 1226 /* Note: IF-TNCCS-SOH v1.0 r8 claims this field to be: 1227 * Reserved(4 bits) r(1 bit) Vers(3 bits), but Windows XP 1228 * SP3 seems to be sending 0x11 for SSoH, i.e., r(request/response) bit 1229 * would not be in the specified location. 1230 * [MS-SOH] 4.0.2: Reserved(3 bits) r(1 bit) Vers(4 bits) 1231 */ 1232 wpabuf_put_u8(buf, 0x11); /* r=request, vers=1 */ 1233 1234 /* MS-Machine-Inventory */ 1235 /* TODO: get correct values; 0 = not applicable for OS */ 1236 wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY); 1237 wpabuf_put_be32(buf, 0); /* osVersionMajor */ 1238 wpabuf_put_be32(buf, 0); /* osVersionMinor */ 1239 wpabuf_put_be32(buf, 0); /* osVersionBuild */ 1240 wpabuf_put_be16(buf, 0); /* spVersionMajor */ 1241 wpabuf_put_be16(buf, 0); /* spVersionMinor */ 1242 wpabuf_put_be16(buf, 0); /* procArch */ 1243 1244 /* MS-MachineName */ 1245 wpabuf_put_u8(buf, SSOH_MS_MACHINENAME); 1246 wpabuf_put_be16(buf, os_strlen(machinename) + 1); 1247 wpabuf_put_data(buf, machinename, os_strlen(machinename) + 1); 1248 1249 /* MS-CorrelationId */ 1250 wpabuf_put_u8(buf, SSOH_MS_CORRELATIONID); 1251 wpabuf_put_data(buf, correlation_id, sizeof(correlation_id)); 1252 1253 /* MS-Quarantine-State */ 1254 wpabuf_put_u8(buf, SSOH_MS_QUARANTINE_STATE); 1255 wpabuf_put_be16(buf, 1); /* Flags: ExtState=0, f=0, qState=1 */ 1256 wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (hi) */ 1257 wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (lo) */ 1258 wpabuf_put_be16(buf, 1); /* urlLenInBytes */ 1259 wpabuf_put_u8(buf, 0); /* null termination for the url */ 1260 1261 /* MS-Machine-Inventory-Ex */ 1262 wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY_EX); 1263 wpabuf_put_be32(buf, 0); /* Reserved 1264 * (note: Windows XP SP3 uses 0xdecafbad) */ 1265 wpabuf_put_u8(buf, 1); /* ProductType: Client */ 1266 1267 /* Update SSoH Length */ 1268 end = wpabuf_put(buf, 0); 1269 WPA_PUT_BE16(ssoh_len, end - ssoh_len - 2); 1270 1271 /* TODO: SoHReportEntry TLV (zero or more) */ 1272 1273 /* Update length fields */ 1274 end = wpabuf_put(buf, 0); 1275 WPA_PUT_BE16(tlv_len, end - tlv_len - 2); 1276 WPA_PUT_BE16(tlv_len2, end - tlv_len2 - 2); 1277 WPA_PUT_BE16(outer_len, end - outer_len - 2); 1278 WPA_PUT_BE16(inner_len, end - inner_len - 2); 1279 1280 return buf; 1281} 1282 1283 1284struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len) 1285{ 1286 const u8 *pos; 1287 1288 wpa_hexdump(MSG_DEBUG, "TNC: SoH Request", data, len); 1289 1290 if (len < 12) 1291 return NULL; 1292 1293 /* SoH Request */ 1294 pos = data; 1295 1296 /* TLV Type */ 1297 if (WPA_GET_BE16(pos) != EAP_TLV_VENDOR_SPECIFIC_TLV) 1298 return NULL; 1299 pos += 2; 1300 1301 /* Length */ 1302 if (WPA_GET_BE16(pos) < 8) 1303 return NULL; 1304 pos += 2; 1305 1306 /* Vendor_Id */ 1307 if (WPA_GET_BE32(pos) != EAP_VENDOR_MICROSOFT) 1308 return NULL; 1309 pos += 4; 1310 1311 /* TLV Type */ 1312 if (WPA_GET_BE16(pos) != 0x02 /* SoH request TLV */) 1313 return NULL; 1314 1315 wpa_printf(MSG_DEBUG, "TNC: SoH Request TLV received"); 1316 1317 return tncc_build_soh(2); 1318} 1319