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