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