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