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 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 = dup_binstr(msg, len); 855 if (buf == NULL) 856 return TNCCS_PROCESS_ERROR; 857 858 start = os_strstr(buf, "<TNCCS-Batch "); 859 end = os_strstr(buf, "</TNCCS-Batch>"); 860 if (start == NULL || end == NULL || start > end) { 861 os_free(buf); 862 return TNCCS_PROCESS_ERROR; 863 } 864 865 start += 13; 866 while (*start == ' ') 867 start++; 868 *end = '\0'; 869 870 pos = os_strstr(start, "BatchId="); 871 if (pos == NULL) { 872 os_free(buf); 873 return TNCCS_PROCESS_ERROR; 874 } 875 876 pos += 8; 877 if (*pos == '"') 878 pos++; 879 batch_id = atoi(pos); 880 wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u", 881 batch_id); 882 if (batch_id != tncs->last_batchid + 1) { 883 wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId " 884 "%u (expected %u)", 885 batch_id, tncs->last_batchid + 1); 886 os_free(buf); 887 return TNCCS_PROCESS_ERROR; 888 } 889 tncs->last_batchid = batch_id; 890 891 while (*pos != '\0' && *pos != '>') 892 pos++; 893 if (*pos == '\0') { 894 os_free(buf); 895 return TNCCS_PROCESS_ERROR; 896 } 897 pos++; 898 payload = start; 899 900 /* 901 * <IMC-IMV-Message> 902 * <Type>01234567</Type> 903 * <Base64>foo==</Base64> 904 * </IMC-IMV-Message> 905 */ 906 907 while (*start) { 908 char *endpos; 909 unsigned int type; 910 911 pos = os_strstr(start, "<IMC-IMV-Message>"); 912 if (pos == NULL) 913 break; 914 start = pos + 17; 915 end = os_strstr(start, "</IMC-IMV-Message>"); 916 if (end == NULL) 917 break; 918 *end = '\0'; 919 endpos = end; 920 end += 18; 921 922 if (tncs_get_type(start, &type) < 0) { 923 *endpos = '<'; 924 start = end; 925 continue; 926 } 927 wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type); 928 929 decoded = tncs_get_base64(start, &decoded_len); 930 if (decoded == NULL) { 931 *endpos = '<'; 932 start = end; 933 continue; 934 } 935 936 tncs_send_to_imvs(tncs, type, decoded, decoded_len); 937 938 os_free(decoded); 939 940 start = end; 941 } 942 943 /* 944 * <TNCC-TNCS-Message> 945 * <Type>01234567</Type> 946 * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML> 947 * <Base64>foo==</Base64> 948 * </TNCC-TNCS-Message> 949 */ 950 951 start = payload; 952 while (*start) { 953 unsigned int type; 954 char *xml, *xmlend, *endpos; 955 956 pos = os_strstr(start, "<TNCC-TNCS-Message>"); 957 if (pos == NULL) 958 break; 959 start = pos + 19; 960 end = os_strstr(start, "</TNCC-TNCS-Message>"); 961 if (end == NULL) 962 break; 963 *end = '\0'; 964 endpos = end; 965 end += 20; 966 967 if (tncs_get_type(start, &type) < 0) { 968 *endpos = '<'; 969 start = end; 970 continue; 971 } 972 wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x", 973 type); 974 975 /* Base64 OR XML */ 976 decoded = NULL; 977 xml = NULL; 978 xmlend = NULL; 979 pos = os_strstr(start, "<XML>"); 980 if (pos) { 981 pos += 5; 982 pos2 = os_strstr(pos, "</XML>"); 983 if (pos2 == NULL) { 984 *endpos = '<'; 985 start = end; 986 continue; 987 } 988 xmlend = pos2; 989 xml = pos; 990 } else { 991 decoded = tncs_get_base64(start, &decoded_len); 992 if (decoded == NULL) { 993 *endpos = '<'; 994 start = end; 995 continue; 996 } 997 } 998 999 if (decoded) { 1000 wpa_hexdump_ascii(MSG_MSGDUMP, 1001 "TNC: TNCC-TNCS-Message Base64", 1002 decoded, decoded_len); 1003 os_free(decoded); 1004 } 1005 1006 if (xml) { 1007 wpa_hexdump_ascii(MSG_MSGDUMP, 1008 "TNC: TNCC-TNCS-Message XML", 1009 (unsigned char *) xml, 1010 xmlend - xml); 1011 } 1012 1013 start = end; 1014 } 1015 1016 os_free(buf); 1017 1018 tncs_batch_ending(tncs); 1019 1020 if (tncs_total_send_len(tncs) == 0) 1021 return tncs_derive_recommendation(tncs); 1022 1023 return TNCCS_PROCESS_OK_NO_RECOMMENDATION; 1024 } 1025 1026 1027 static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end, 1028 int *error) 1029 { 1030 struct tnc_if_imv *imv; 1031 char *pos, *pos2; 1032 1033 if (id >= TNC_MAX_IMV_ID) { 1034 wpa_printf(MSG_DEBUG, "TNC: Too many IMVs"); 1035 return NULL; 1036 } 1037 1038 imv = os_zalloc(sizeof(*imv)); 1039 if (imv == NULL) { 1040 *error = 1; 1041 return NULL; 1042 } 1043 1044 imv->imvID = id; 1045 1046 pos = start; 1047 wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos); 1048 if (pos + 1 >= end || *pos != '"') { 1049 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' " 1050 "(no starting quotation mark)", start); 1051 os_free(imv); 1052 return NULL; 1053 } 1054 1055 pos++; 1056 pos2 = pos; 1057 while (pos2 < end && *pos2 != '"') 1058 pos2++; 1059 if (pos2 >= end) { 1060 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' " 1061 "(no ending quotation mark)", start); 1062 os_free(imv); 1063 return NULL; 1064 } 1065 *pos2 = '\0'; 1066 wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos); 1067 imv->name = os_strdup(pos); 1068 1069 pos = pos2 + 1; 1070 if (pos >= end || *pos != ' ') { 1071 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' " 1072 "(no space after name)", start); 1073 os_free(imv); 1074 return NULL; 1075 } 1076 1077 pos++; 1078 wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos); 1079 imv->path = os_strdup(pos); 1080 1081 return imv; 1082 } 1083 1084 1085 static int tncs_read_config(struct tncs_global *global) 1086 { 1087 char *config, *end, *pos, *line_end; 1088 size_t config_len; 1089 struct tnc_if_imv *imv, *last; 1090 int id = 0; 1091 1092 last = NULL; 1093 1094 config = os_readfile(TNC_CONFIG_FILE, &config_len); 1095 if (config == NULL) { 1096 wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration " 1097 "file '%s'", TNC_CONFIG_FILE); 1098 return -1; 1099 } 1100 1101 end = config + config_len; 1102 for (pos = config; pos < end; pos = line_end + 1) { 1103 line_end = pos; 1104 while (*line_end != '\n' && *line_end != '\r' && 1105 line_end < end) 1106 line_end++; 1107 *line_end = '\0'; 1108 1109 if (os_strncmp(pos, "IMV ", 4) == 0) { 1110 int error = 0; 1111 1112 imv = tncs_parse_imv(id++, pos + 4, line_end, &error); 1113 if (error) 1114 return -1; 1115 if (imv) { 1116 if (last == NULL) 1117 global->imv = imv; 1118 else 1119 last->next = imv; 1120 last = imv; 1121 } 1122 } 1123 } 1124 1125 os_free(config); 1126 1127 return 0; 1128 } 1129 1130 1131 struct tncs_data * tncs_init(void) 1132 { 1133 struct tncs_data *tncs; 1134 1135 if (tncs_global_data == NULL) 1136 return NULL; 1137 1138 tncs = os_zalloc(sizeof(*tncs)); 1139 if (tncs == NULL) 1140 return NULL; 1141 tncs->imv = tncs_global_data->imv; 1142 tncs->connectionID = tncs_global_data->next_conn_id++; 1143 tncs->next = tncs_global_data->connections; 1144 tncs_global_data->connections = tncs; 1145 1146 return tncs; 1147 } 1148 1149 1150 void tncs_deinit(struct tncs_data *tncs) 1151 { 1152 int i; 1153 struct tncs_data *prev, *conn; 1154 1155 if (tncs == NULL) 1156 return; 1157 1158 for (i = 0; i < TNC_MAX_IMV_ID; i++) 1159 os_free(tncs->imv_data[i].imv_send); 1160 1161 prev = NULL; 1162 conn = tncs_global_data->connections; 1163 while (conn) { 1164 if (conn == tncs) { 1165 if (prev) 1166 prev->next = tncs->next; 1167 else 1168 tncs_global_data->connections = tncs->next; 1169 break; 1170 } 1171 prev = conn; 1172 conn = conn->next; 1173 } 1174 1175 os_free(tncs->tncs_message); 1176 os_free(tncs); 1177 } 1178 1179 1180 int tncs_global_init(void) 1181 { 1182 struct tnc_if_imv *imv; 1183 1184 tncs_global_data = os_zalloc(sizeof(*tncs_global_data)); 1185 if (tncs_global_data == NULL) 1186 return -1; 1187 1188 if (tncs_read_config(tncs_global_data) < 0) { 1189 wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration"); 1190 goto failed; 1191 } 1192 1193 for (imv = tncs_global_data->imv; imv; imv = imv->next) { 1194 if (tncs_load_imv(imv)) { 1195 wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'", 1196 imv->name); 1197 goto failed; 1198 } 1199 } 1200 1201 return 0; 1202 1203 failed: 1204 tncs_global_deinit(); 1205 return -1; 1206 } 1207 1208 1209 void tncs_global_deinit(void) 1210 { 1211 struct tnc_if_imv *imv, *prev; 1212 1213 if (tncs_global_data == NULL) 1214 return; 1215 1216 imv = tncs_global_data->imv; 1217 while (imv) { 1218 tncs_unload_imv(imv); 1219 1220 prev = imv; 1221 imv = imv->next; 1222 os_free(prev); 1223 } 1224 1225 os_free(tncs_global_data); 1226 tncs_global_data = NULL; 1227 } 1228 1229 1230 struct wpabuf * tncs_build_soh_request(void) 1231 { 1232 struct wpabuf *buf; 1233 1234 /* 1235 * Build a SoH Request TLV (to be used inside SoH EAP Extensions 1236 * Method) 1237 */ 1238 1239 buf = wpabuf_alloc(8 + 4); 1240 if (buf == NULL) 1241 return NULL; 1242 1243 /* Vendor-Specific TLV (Microsoft) - SoH Request */ 1244 wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */ 1245 wpabuf_put_be16(buf, 8); /* Length */ 1246 1247 wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */ 1248 1249 wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */ 1250 wpabuf_put_be16(buf, 0); /* Length */ 1251 1252 return buf; 1253 } 1254 1255 1256 struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len, 1257 int *failure) 1258 { 1259 wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len); 1260 *failure = 0; 1261 1262 /* TODO: return MS-SoH Response TLV */ 1263 1264 return NULL; 1265 } 1266