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