tncs.c
Go to the documentation of this file.
00001 /*
00002  * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
00003  * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License version 2 as
00007  * published by the Free Software Foundation.
00008  *
00009  * Alternatively, this software may be distributed under the terms of BSD
00010  * license.
00011  *
00012  * See README and COPYING for more details.
00013  */
00014 
00015 #include "includes.h"
00016 #include <dlfcn.h>
00017 
00018 #include "common.h"
00019 #include "base64.h"
00020 #include "tncs.h"
00021 #include "eap_common/eap_tlv_common.h"
00022 #include "eap_common/eap_defs.h"
00023 
00024 
00025 /* TODO: TNCS must be thread-safe; review the code and add locking etc. if
00026  * needed.. */
00027 
00028 #define TNC_CONFIG_FILE "/etc/tnc_config"
00029 #define IF_TNCCS_START \
00030 "<?xml version=\"1.0\"?>\n" \
00031 "<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
00032 "xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
00033 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
00034 "xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
00035 "IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
00036 #define IF_TNCCS_END "\n</TNCCS-Batch>"
00037 
00038 /* TNC IF-IMV */
00039 
00040 typedef unsigned long TNC_UInt32;
00041 typedef unsigned char *TNC_BufferReference;
00042 
00043 typedef TNC_UInt32 TNC_IMVID;
00044 typedef TNC_UInt32 TNC_ConnectionID;
00045 typedef TNC_UInt32 TNC_ConnectionState;
00046 typedef TNC_UInt32 TNC_RetryReason;
00047 typedef TNC_UInt32 TNC_IMV_Action_Recommendation;
00048 typedef TNC_UInt32 TNC_IMV_Evaluation_Result;
00049 typedef TNC_UInt32 TNC_MessageType;
00050 typedef TNC_MessageType *TNC_MessageTypeList;
00051 typedef TNC_UInt32 TNC_VendorID;
00052 typedef TNC_UInt32 TNC_Subtype;
00053 typedef TNC_UInt32 TNC_Version;
00054 typedef TNC_UInt32 TNC_Result;
00055 typedef TNC_UInt32 TNC_AttributeID;
00056 
00057 typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)(
00058         TNC_IMVID imvID,
00059         char *functionName,
00060         void **pOutfunctionPointer);
00061 
00062 #define TNC_RESULT_SUCCESS 0
00063 #define TNC_RESULT_NOT_INITIALIZED 1
00064 #define TNC_RESULT_ALREADY_INITIALIZED 2
00065 #define TNC_RESULT_NO_COMMON_VERSION 3
00066 #define TNC_RESULT_CANT_RETRY 4
00067 #define TNC_RESULT_WONT_RETRY 5
00068 #define TNC_RESULT_INVALID_PARAMETER 6
00069 #define TNC_RESULT_CANT_RESPOND 7
00070 #define TNC_RESULT_ILLEGAL_OPERATION 8
00071 #define TNC_RESULT_OTHER 9
00072 #define TNC_RESULT_FATAL 10
00073 
00074 #define TNC_CONNECTION_STATE_CREATE 0
00075 #define TNC_CONNECTION_STATE_HANDSHAKE 1
00076 #define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
00077 #define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
00078 #define TNC_CONNECTION_STATE_ACCESS_NONE 4
00079 #define TNC_CONNECTION_STATE_DELETE 5
00080 
00081 #define TNC_IFIMV_VERSION_1 1
00082 
00083 #define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
00084 #define TNC_SUBTYPE_ANY ((TNC_Subtype) 0xff)
00085 
00086 /* TNCC-TNCS Message Types */
00087 #define TNC_TNCCS_RECOMMENDATION                0x00000001
00088 #define TNC_TNCCS_ERROR                         0x00000002
00089 #define TNC_TNCCS_PREFERREDLANGUAGE             0x00000003
00090 #define TNC_TNCCS_REASONSTRINGS                 0x00000004
00091 
00092 /* Possible TNC_IMV_Action_Recommendation values: */
00093 enum IMV_Action_Recommendation {
00094         TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
00095         TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS,
00096         TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
00097         TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
00098 };
00099 
00100 /* Possible TNC_IMV_Evaluation_Result values: */
00101 enum IMV_Evaluation_Result {
00102         TNC_IMV_EVALUATION_RESULT_COMPLIANT,
00103         TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR,
00104         TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR,
00105         TNC_IMV_EVALUATION_RESULT_ERROR,
00106         TNC_IMV_EVALUATION_RESULT_DONT_KNOW
00107 };
00108 
00109 struct tnc_if_imv {
00110         struct tnc_if_imv *next;
00111         char *name;
00112         char *path;
00113         void *dlhandle; /* from dlopen() */
00114         TNC_IMVID imvID;
00115         TNC_MessageTypeList supported_types;
00116         size_t num_supported_types;
00117 
00118         /* Functions implemented by IMVs (with TNC_IMV_ prefix) */
00119         TNC_Result (*Initialize)(
00120                 TNC_IMVID imvID,
00121                 TNC_Version minVersion,
00122                 TNC_Version maxVersion,
00123                 TNC_Version *pOutActualVersion);
00124         TNC_Result (*NotifyConnectionChange)(
00125                 TNC_IMVID imvID,
00126                 TNC_ConnectionID connectionID,
00127                 TNC_ConnectionState newState);
00128         TNC_Result (*ReceiveMessage)(
00129                 TNC_IMVID imvID,
00130                 TNC_ConnectionID connectionID,
00131                 TNC_BufferReference message,
00132                 TNC_UInt32 messageLength,
00133                 TNC_MessageType messageType);
00134         TNC_Result (*SolicitRecommendation)(
00135                 TNC_IMVID imvID,
00136                 TNC_ConnectionID connectionID);
00137         TNC_Result (*BatchEnding)(
00138                 TNC_IMVID imvID,
00139                 TNC_ConnectionID connectionID);
00140         TNC_Result (*Terminate)(TNC_IMVID imvID);
00141         TNC_Result (*ProvideBindFunction)(
00142                 TNC_IMVID imvID,
00143                 TNC_TNCS_BindFunctionPointer bindFunction);
00144 };
00145 
00146 
00147 #define TNC_MAX_IMV_ID 10
00148 
00149 struct tncs_data {
00150         struct tncs_data *next;
00151         struct tnc_if_imv *imv; /* local copy of tncs_global_data->imv */
00152         TNC_ConnectionID connectionID;
00153         unsigned int last_batchid;
00154         enum IMV_Action_Recommendation recommendation;
00155         int done;
00156 
00157         struct conn_imv {
00158                 u8 *imv_send;
00159                 size_t imv_send_len;
00160                 enum IMV_Action_Recommendation recommendation;
00161                 int recommendation_set;
00162         } imv_data[TNC_MAX_IMV_ID];
00163 
00164         char *tncs_message;
00165 };
00166 
00167 
00168 struct tncs_global {
00169         struct tnc_if_imv *imv;
00170         TNC_ConnectionID next_conn_id;
00171         struct tncs_data *connections;
00172 };
00173 
00174 static struct tncs_global *tncs_global_data = NULL;
00175 
00176 
00177 static struct tnc_if_imv * tncs_get_imv(TNC_IMVID imvID)
00178 {
00179         struct tnc_if_imv *imv;
00180 
00181         if (imvID >= TNC_MAX_IMV_ID || tncs_global_data == NULL)
00182                 return NULL;
00183         imv = tncs_global_data->imv;
00184         while (imv) {
00185                 if (imv->imvID == imvID)
00186                         return imv;
00187                 imv = imv->next;
00188         }
00189         return NULL;
00190 }
00191 
00192 
00193 static struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID)
00194 {
00195         struct tncs_data *tncs;
00196 
00197         if (tncs_global_data == NULL)
00198                 return NULL;
00199 
00200         tncs = tncs_global_data->connections;
00201         while (tncs) {
00202                 if (tncs->connectionID == connectionID)
00203                         return tncs;
00204                 tncs = tncs->next;
00205         }
00206 
00207         wpa_printf(MSG_DEBUG, "TNC: Connection ID %lu not found",
00208                    (unsigned long) connectionID);
00209 
00210         return NULL;
00211 }
00212 
00213 
00214 /* TNCS functions that IMVs can call */
00215 TNC_Result TNC_TNCS_ReportMessageTypes(
00216         TNC_IMVID imvID,
00217         TNC_MessageTypeList supportedTypes,
00218         TNC_UInt32 typeCount)
00219 {
00220         TNC_UInt32 i;
00221         struct tnc_if_imv *imv;
00222 
00223         wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ReportMessageTypes(imvID=%lu "
00224                    "typeCount=%lu)",
00225                    (unsigned long) imvID, (unsigned long) typeCount);
00226 
00227         for (i = 0; i < typeCount; i++) {
00228                 wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
00229                            i, supportedTypes[i]);
00230         }
00231 
00232         imv = tncs_get_imv(imvID);
00233         if (imv == NULL)
00234                 return TNC_RESULT_INVALID_PARAMETER;
00235         os_free(imv->supported_types);
00236         imv->supported_types =
00237                 os_malloc(typeCount * sizeof(TNC_MessageTypeList));
00238         if (imv->supported_types == NULL)
00239                 return TNC_RESULT_FATAL;
00240         os_memcpy(imv->supported_types, supportedTypes,
00241                   typeCount * sizeof(TNC_MessageTypeList));
00242         imv->num_supported_types = typeCount;
00243 
00244         return TNC_RESULT_SUCCESS;
00245 }
00246 
00247 
00248 TNC_Result TNC_TNCS_SendMessage(
00249         TNC_IMVID imvID,
00250         TNC_ConnectionID connectionID,
00251         TNC_BufferReference message,
00252         TNC_UInt32 messageLength,
00253         TNC_MessageType messageType)
00254 {
00255         struct tncs_data *tncs;
00256         unsigned char *b64;
00257         size_t b64len;
00258 
00259         wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage(imvID=%lu "
00260                    "connectionID=%lu messageType=%lu)",
00261                    imvID, connectionID, messageType);
00262         wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage",
00263                           message, messageLength);
00264 
00265         if (tncs_get_imv(imvID) == NULL)
00266                 return TNC_RESULT_INVALID_PARAMETER;
00267 
00268         tncs = tncs_get_conn(connectionID);
00269         if (tncs == NULL)
00270                 return TNC_RESULT_INVALID_PARAMETER;
00271 
00272         b64 = base64_encode(message, messageLength, &b64len);
00273         if (b64 == NULL)
00274                 return TNC_RESULT_FATAL;
00275 
00276         os_free(tncs->imv_data[imvID].imv_send);
00277         tncs->imv_data[imvID].imv_send_len = 0;
00278         tncs->imv_data[imvID].imv_send = os_zalloc(b64len + 100);
00279         if (tncs->imv_data[imvID].imv_send == NULL) {
00280                 os_free(b64);
00281                 return TNC_RESULT_OTHER;
00282         }
00283 
00284         tncs->imv_data[imvID].imv_send_len =
00285                 os_snprintf((char *) tncs->imv_data[imvID].imv_send,
00286                             b64len + 100,
00287                             "<IMC-IMV-Message><Type>%08X</Type>"
00288                             "<Base64>%s</Base64></IMC-IMV-Message>",
00289                             (unsigned int) messageType, b64);
00290 
00291         os_free(b64);
00292 
00293         return TNC_RESULT_SUCCESS;
00294 }
00295 
00296 
00297 TNC_Result TNC_TNCS_RequestHandshakeRetry(
00298         TNC_IMVID imvID,
00299         TNC_ConnectionID connectionID,
00300         TNC_RetryReason reason)
00301 {
00302         wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_RequestHandshakeRetry");
00303         /* TODO */
00304         return TNC_RESULT_SUCCESS;
00305 }
00306 
00307 
00308 TNC_Result TNC_TNCS_ProvideRecommendation(
00309         TNC_IMVID imvID,
00310         TNC_ConnectionID connectionID,
00311         TNC_IMV_Action_Recommendation recommendation,
00312         TNC_IMV_Evaluation_Result evaluation)
00313 {
00314         struct tncs_data *tncs;
00315 
00316         wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ProvideRecommendation(imvID=%lu "
00317                    "connectionID=%lu recommendation=%lu evaluation=%lu)",
00318                    (unsigned long) imvID, (unsigned long) connectionID,
00319                    (unsigned long) recommendation, (unsigned long) evaluation);
00320 
00321         if (tncs_get_imv(imvID) == NULL)
00322                 return TNC_RESULT_INVALID_PARAMETER;
00323 
00324         tncs = tncs_get_conn(connectionID);
00325         if (tncs == NULL)
00326                 return TNC_RESULT_INVALID_PARAMETER;
00327 
00328         tncs->imv_data[imvID].recommendation = recommendation;
00329         tncs->imv_data[imvID].recommendation_set = 1;
00330 
00331         return TNC_RESULT_SUCCESS;
00332 }
00333 
00334 
00335 TNC_Result TNC_TNCS_GetAttribute(
00336         TNC_IMVID imvID,
00337         TNC_ConnectionID connectionID,
00338         TNC_AttributeID attribureID,
00339         TNC_UInt32 bufferLength,
00340         TNC_BufferReference buffer,
00341         TNC_UInt32 *pOutValueLength)
00342 {
00343         wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_GetAttribute");
00344         /* TODO */
00345         return TNC_RESULT_SUCCESS;
00346 }
00347 
00348 
00349 TNC_Result TNC_TNCS_SetAttribute(
00350         TNC_IMVID imvID,
00351         TNC_ConnectionID connectionID,
00352         TNC_AttributeID attribureID,
00353         TNC_UInt32 bufferLength,
00354         TNC_BufferReference buffer)
00355 {
00356         wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SetAttribute");
00357         /* TODO */
00358         return TNC_RESULT_SUCCESS;
00359 }
00360 
00361 
00362 TNC_Result TNC_TNCS_BindFunction(
00363         TNC_IMVID imvID,
00364         char *functionName,
00365         void **pOutFunctionPointer)
00366 {
00367         wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_BindFunction(imcID=%lu, "
00368                    "functionName='%s')", (unsigned long) imvID, functionName);
00369 
00370         if (tncs_get_imv(imvID) == NULL)
00371                 return TNC_RESULT_INVALID_PARAMETER;
00372 
00373         if (pOutFunctionPointer == NULL)
00374                 return TNC_RESULT_INVALID_PARAMETER;
00375 
00376         if (os_strcmp(functionName, "TNC_TNCS_ReportMessageTypes") == 0)
00377                 *pOutFunctionPointer = TNC_TNCS_ReportMessageTypes;
00378         else if (os_strcmp(functionName, "TNC_TNCS_SendMessage") == 0)
00379                 *pOutFunctionPointer = TNC_TNCS_SendMessage;
00380         else if (os_strcmp(functionName, "TNC_TNCS_RequestHandshakeRetry") ==
00381                  0)
00382                 *pOutFunctionPointer = TNC_TNCS_RequestHandshakeRetry;
00383         else if (os_strcmp(functionName, "TNC_TNCS_ProvideRecommendation") ==
00384                  0)
00385                 *pOutFunctionPointer = TNC_TNCS_ProvideRecommendation;
00386         else if (os_strcmp(functionName, "TNC_TNCS_GetAttribute") == 0)
00387                 *pOutFunctionPointer = TNC_TNCS_GetAttribute;
00388         else if (os_strcmp(functionName, "TNC_TNCS_SetAttribute") == 0)
00389                 *pOutFunctionPointer = TNC_TNCS_SetAttribute;
00390         else
00391                 *pOutFunctionPointer = NULL;
00392 
00393         return TNC_RESULT_SUCCESS;
00394 }
00395 
00396 
00397 static void * tncs_get_sym(void *handle, char *func)
00398 {
00399         void *fptr;
00400 
00401         fptr = dlsym(handle, func);
00402 
00403         return fptr;
00404 }
00405 
00406 
00407 static int tncs_imv_resolve_funcs(struct tnc_if_imv *imv)
00408 {
00409         void *handle = imv->dlhandle;
00410 
00411         /* Mandatory IMV functions */
00412         imv->Initialize = tncs_get_sym(handle, "TNC_IMV_Initialize");
00413         if (imv->Initialize == NULL) {
00414                 wpa_printf(MSG_ERROR, "TNC: IMV does not export "
00415                            "TNC_IMV_Initialize");
00416                 return -1;
00417         }
00418 
00419         imv->SolicitRecommendation = tncs_get_sym(
00420                 handle, "TNC_IMV_SolicitRecommendation");
00421         if (imv->SolicitRecommendation == NULL) {
00422                 wpa_printf(MSG_ERROR, "TNC: IMV does not export "
00423                            "TNC_IMV_SolicitRecommendation");
00424                 return -1;
00425         }
00426 
00427         imv->ProvideBindFunction =
00428                 tncs_get_sym(handle, "TNC_IMV_ProvideBindFunction");
00429         if (imv->ProvideBindFunction == NULL) {
00430                 wpa_printf(MSG_ERROR, "TNC: IMV does not export "
00431                            "TNC_IMV_ProvideBindFunction");
00432                 return -1;
00433         }
00434 
00435         /* Optional IMV functions */
00436         imv->NotifyConnectionChange =
00437                 tncs_get_sym(handle, "TNC_IMV_NotifyConnectionChange");
00438         imv->ReceiveMessage = tncs_get_sym(handle, "TNC_IMV_ReceiveMessage");
00439         imv->BatchEnding = tncs_get_sym(handle, "TNC_IMV_BatchEnding");
00440         imv->Terminate = tncs_get_sym(handle, "TNC_IMV_Terminate");
00441 
00442         return 0;
00443 }
00444 
00445 
00446 static int tncs_imv_initialize(struct tnc_if_imv *imv)
00447 {
00448         TNC_Result res;
00449         TNC_Version imv_ver;
00450 
00451         wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Initialize for IMV '%s'",
00452                    imv->name);
00453         res = imv->Initialize(imv->imvID, TNC_IFIMV_VERSION_1,
00454                               TNC_IFIMV_VERSION_1, &imv_ver);
00455         wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Initialize: res=%lu imv_ver=%lu",
00456                    (unsigned long) res, (unsigned long) imv_ver);
00457 
00458         return res == TNC_RESULT_SUCCESS ? 0 : -1;
00459 }
00460 
00461 
00462 static int tncs_imv_terminate(struct tnc_if_imv *imv)
00463 {
00464         TNC_Result res;
00465 
00466         if (imv->Terminate == NULL)
00467                 return 0;
00468 
00469         wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Terminate for IMV '%s'",
00470                    imv->name);
00471         res = imv->Terminate(imv->imvID);
00472         wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Terminate: %lu",
00473                    (unsigned long) res);
00474 
00475         return res == TNC_RESULT_SUCCESS ? 0 : -1;
00476 }
00477 
00478 
00479 static int tncs_imv_provide_bind_function(struct tnc_if_imv *imv)
00480 {
00481         TNC_Result res;
00482 
00483         wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_ProvideBindFunction for "
00484                    "IMV '%s'", imv->name);
00485         res = imv->ProvideBindFunction(imv->imvID, TNC_TNCS_BindFunction);
00486         wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_ProvideBindFunction: res=%lu",
00487                    (unsigned long) res);
00488 
00489         return res == TNC_RESULT_SUCCESS ? 0 : -1;
00490 }
00491 
00492 
00493 static int tncs_imv_notify_connection_change(struct tnc_if_imv *imv,
00494                                              TNC_ConnectionID conn,
00495                                              TNC_ConnectionState state)
00496 {
00497         TNC_Result res;
00498 
00499         if (imv->NotifyConnectionChange == NULL)
00500                 return 0;
00501 
00502         wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_NotifyConnectionChange(%d)"
00503                    " for IMV '%s'", (int) state, imv->name);
00504         res = imv->NotifyConnectionChange(imv->imvID, conn, state);
00505         wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
00506                    (unsigned long) res);
00507 
00508         return res == TNC_RESULT_SUCCESS ? 0 : -1;
00509 }
00510 
00511 
00512 static int tncs_load_imv(struct tnc_if_imv *imv)
00513 {
00514         if (imv->path == NULL) {
00515                 wpa_printf(MSG_DEBUG, "TNC: No IMV configured");
00516                 return -1;
00517         }
00518 
00519         wpa_printf(MSG_DEBUG, "TNC: Opening IMV: %s (%s)",
00520                    imv->name, imv->path);
00521         imv->dlhandle = dlopen(imv->path, RTLD_LAZY);
00522         if (imv->dlhandle == NULL) {
00523                 wpa_printf(MSG_ERROR, "TNC: Failed to open IMV '%s' (%s): %s",
00524                            imv->name, imv->path, dlerror());
00525                 return -1;
00526         }
00527 
00528         if (tncs_imv_resolve_funcs(imv) < 0) {
00529                 wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMV functions");
00530                 return -1;
00531         }
00532 
00533         if (tncs_imv_initialize(imv) < 0 ||
00534             tncs_imv_provide_bind_function(imv) < 0) {
00535                 wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMV");
00536                 return -1;
00537         }
00538 
00539         return 0;
00540 }
00541 
00542 
00543 static void tncs_free_imv(struct tnc_if_imv *imv)
00544 {
00545         os_free(imv->name);
00546         os_free(imv->path);
00547         os_free(imv->supported_types);
00548 }
00549 
00550 static void tncs_unload_imv(struct tnc_if_imv *imv)
00551 {
00552         tncs_imv_terminate(imv);
00553 
00554         if (imv->dlhandle)
00555                 dlclose(imv->dlhandle);
00556 
00557         tncs_free_imv(imv);
00558 }
00559 
00560 
00561 static int tncs_supported_type(struct tnc_if_imv *imv, unsigned int type)
00562 {
00563         size_t i;
00564         unsigned int vendor, subtype;
00565 
00566         if (imv == NULL || imv->supported_types == NULL)
00567                 return 0;
00568 
00569         vendor = type >> 8;
00570         subtype = type & 0xff;
00571 
00572         for (i = 0; i < imv->num_supported_types; i++) {
00573                 unsigned int svendor, ssubtype;
00574                 svendor = imv->supported_types[i] >> 8;
00575                 ssubtype = imv->supported_types[i] & 0xff;
00576                 if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
00577                     (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
00578                         return 1;
00579         }
00580 
00581         return 0;
00582 }
00583 
00584 
00585 static void tncs_send_to_imvs(struct tncs_data *tncs, unsigned int type,
00586                               const u8 *msg, size_t len)
00587 {
00588         struct tnc_if_imv *imv;
00589         TNC_Result res;
00590 
00591         wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMV(s)", msg, len);
00592 
00593         for (imv = tncs->imv; imv; imv = imv->next) {
00594                 if (imv->ReceiveMessage == NULL ||
00595                     !tncs_supported_type(imv, type))
00596                         continue;
00597 
00598                 wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMV '%s'",
00599                            imv->name);
00600                 res = imv->ReceiveMessage(imv->imvID, tncs->connectionID,
00601                                           (TNC_BufferReference) msg, len,
00602                                           type);
00603                 wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
00604                            (unsigned long) res);
00605         }
00606 }
00607 
00608 
00609 static void tncs_batch_ending(struct tncs_data *tncs)
00610 {
00611         struct tnc_if_imv *imv;
00612         TNC_Result res;
00613 
00614         for (imv = tncs->imv; imv; imv = imv->next) {
00615                 if (imv->BatchEnding == NULL)
00616                         continue;
00617 
00618                 wpa_printf(MSG_DEBUG, "TNC: Call BatchEnding for IMV '%s'",
00619                            imv->name);
00620                 res = imv->BatchEnding(imv->imvID, tncs->connectionID);
00621                 wpa_printf(MSG_DEBUG, "TNC: BatchEnding: %lu",
00622                            (unsigned long) res);
00623         }
00624 }
00625 
00626 
00627 static void tncs_solicit_recommendation(struct tncs_data *tncs)
00628 {
00629         struct tnc_if_imv *imv;
00630         TNC_Result res;
00631 
00632         for (imv = tncs->imv; imv; imv = imv->next) {
00633                 if (tncs->imv_data[imv->imvID].recommendation_set)
00634                         continue;
00635 
00636                 wpa_printf(MSG_DEBUG, "TNC: Call SolicitRecommendation for "
00637                            "IMV '%s'", imv->name);
00638                 res = imv->SolicitRecommendation(imv->imvID,
00639                                                  tncs->connectionID);
00640                 wpa_printf(MSG_DEBUG, "TNC: SolicitRecommendation: %lu",
00641                            (unsigned long) res);
00642         }
00643 }
00644 
00645 
00646 void tncs_init_connection(struct tncs_data *tncs)
00647 {
00648         struct tnc_if_imv *imv;
00649         int i;
00650 
00651         for (imv = tncs->imv; imv; imv = imv->next) {
00652                 tncs_imv_notify_connection_change(
00653                         imv, tncs->connectionID, TNC_CONNECTION_STATE_CREATE);
00654                 tncs_imv_notify_connection_change(
00655                         imv, tncs->connectionID,
00656                         TNC_CONNECTION_STATE_HANDSHAKE);
00657         }
00658 
00659         for (i = 0; i < TNC_MAX_IMV_ID; i++) {
00660                 os_free(tncs->imv_data[i].imv_send);
00661                 tncs->imv_data[i].imv_send = NULL;
00662                 tncs->imv_data[i].imv_send_len = 0;
00663         }
00664 }
00665 
00666 
00667 size_t tncs_total_send_len(struct tncs_data *tncs)
00668 {
00669         int i;
00670         size_t len = 0;
00671 
00672         for (i = 0; i < TNC_MAX_IMV_ID; i++)
00673                 len += tncs->imv_data[i].imv_send_len;
00674         if (tncs->tncs_message)
00675                 len += os_strlen(tncs->tncs_message);
00676         return len;
00677 }
00678 
00679 
00680 u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos)
00681 {
00682         int i;
00683 
00684         for (i = 0; i < TNC_MAX_IMV_ID; i++) {
00685                 if (tncs->imv_data[i].imv_send == NULL)
00686                         continue;
00687 
00688                 os_memcpy(pos, tncs->imv_data[i].imv_send,
00689                           tncs->imv_data[i].imv_send_len);
00690                 pos += tncs->imv_data[i].imv_send_len;
00691                 os_free(tncs->imv_data[i].imv_send);
00692                 tncs->imv_data[i].imv_send = NULL;
00693                 tncs->imv_data[i].imv_send_len = 0;
00694         }
00695 
00696         if (tncs->tncs_message) {
00697                 size_t len = os_strlen(tncs->tncs_message);
00698                 os_memcpy(pos, tncs->tncs_message, len);
00699                 pos += len;
00700                 os_free(tncs->tncs_message);
00701                 tncs->tncs_message = NULL;
00702         }
00703 
00704         return pos;
00705 }
00706 
00707 
00708 char * tncs_if_tnccs_start(struct tncs_data *tncs)
00709 {
00710         char *buf = os_malloc(1000);
00711         if (buf == NULL)
00712                 return NULL;
00713         tncs->last_batchid++;
00714         os_snprintf(buf, 1000, IF_TNCCS_START, tncs->last_batchid);
00715         return buf;
00716 }
00717 
00718 
00719 char * tncs_if_tnccs_end(void)
00720 {
00721         char *buf = os_malloc(100);
00722         if (buf == NULL)
00723                 return NULL;
00724         os_snprintf(buf, 100, IF_TNCCS_END);
00725         return buf;
00726 }
00727 
00728 
00729 static int tncs_get_type(char *start, unsigned int *type)
00730 {
00731         char *pos = os_strstr(start, "<Type>");
00732         if (pos == NULL)
00733                 return -1;
00734         pos += 6;
00735         *type = strtoul(pos, NULL, 16);
00736         return 0;
00737 }
00738 
00739 
00740 static unsigned char * tncs_get_base64(char *start, size_t *decoded_len)
00741 {
00742         char *pos, *pos2;
00743         unsigned char *decoded;
00744 
00745         pos = os_strstr(start, "<Base64>");
00746         if (pos == NULL)
00747                 return NULL;
00748 
00749         pos += 8;
00750         pos2 = os_strstr(pos, "</Base64>");
00751         if (pos2 == NULL)
00752                 return NULL;
00753         *pos2 = '\0';
00754 
00755         decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
00756                                 decoded_len);
00757         *pos2 = '<';
00758         if (decoded == NULL) {
00759                 wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
00760         }
00761 
00762         return decoded;
00763 }
00764 
00765 
00766 static enum tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs)
00767 {
00768         enum IMV_Action_Recommendation rec;
00769         struct tnc_if_imv *imv;
00770         TNC_ConnectionState state;
00771         char *txt;
00772 
00773         wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs");
00774 
00775         if (tncs->done)
00776                 return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
00777 
00778         tncs_solicit_recommendation(tncs);
00779 
00780         /* Select the most restrictive recommendation */
00781         rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION;
00782         for (imv = tncs->imv; imv; imv = imv->next) {
00783                 TNC_IMV_Action_Recommendation irec;
00784                 irec = tncs->imv_data[imv->imvID].recommendation;
00785                 if (irec == TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
00786                         rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS;
00787                 if (irec == TNC_IMV_ACTION_RECOMMENDATION_ISOLATE &&
00788                     rec != TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
00789                         rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE;
00790                 if (irec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW &&
00791                     rec == TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION)
00792                         rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW;
00793         }
00794 
00795         wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec);
00796         tncs->recommendation = rec;
00797         tncs->done = 1;
00798 
00799         txt = NULL;
00800         switch (rec) {
00801         case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
00802         case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
00803                 txt = "allow";
00804                 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
00805                 break;
00806         case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
00807                 txt = "isolate";
00808                 state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
00809                 break;
00810         case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
00811                 txt = "none";
00812                 state = TNC_CONNECTION_STATE_ACCESS_NONE;
00813                 break;
00814         default:
00815                 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
00816                 break;
00817         }
00818 
00819         if (txt) {
00820                 os_free(tncs->tncs_message);
00821                 tncs->tncs_message = os_zalloc(200);
00822                 if (tncs->tncs_message) {
00823                         os_snprintf(tncs->tncs_message, 199,
00824                                     "<TNCC-TNCS-Message><Type>%08X</Type>"
00825                                     "<XML><TNCCS-Recommendation type=\"%s\">"
00826                                     "</TNCCS-Recommendation></XML>"
00827                                     "</TNCC-TNCS-Message>",
00828                                     TNC_TNCCS_RECOMMENDATION, txt);
00829                 }
00830         }
00831 
00832         for (imv = tncs->imv; imv; imv = imv->next) {
00833                 tncs_imv_notify_connection_change(imv, tncs->connectionID,
00834                                                   state);
00835         }
00836 
00837         switch (rec) {
00838         case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
00839                 return TNCCS_RECOMMENDATION_ALLOW;
00840         case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
00841                 return TNCCS_RECOMMENDATION_NO_ACCESS;
00842         case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
00843                 return TNCCS_RECOMMENDATION_ISOLATE;
00844         case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
00845                 return TNCCS_RECOMMENDATION_NO_RECOMMENDATION;
00846         default:
00847                 return TNCCS_PROCESS_ERROR;
00848         }
00849 }
00850 
00851 
00852 enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
00853                                             const u8 *msg, size_t len)
00854 {
00855         char *buf, *start, *end, *pos, *pos2, *payload;
00856         unsigned int batch_id;
00857         unsigned char *decoded;
00858         size_t decoded_len;
00859 
00860         buf = os_malloc(len + 1);
00861         if (buf == NULL)
00862                 return TNCCS_PROCESS_ERROR;
00863 
00864         os_memcpy(buf, msg, len);
00865         buf[len] = '\0';
00866         start = os_strstr(buf, "<TNCCS-Batch ");
00867         end = os_strstr(buf, "</TNCCS-Batch>");
00868         if (start == NULL || end == NULL || start > end) {
00869                 os_free(buf);
00870                 return TNCCS_PROCESS_ERROR;
00871         }
00872 
00873         start += 13;
00874         while (*start == ' ')
00875                 start++;
00876         *end = '\0';
00877 
00878         pos = os_strstr(start, "BatchId=");
00879         if (pos == NULL) {
00880                 os_free(buf);
00881                 return TNCCS_PROCESS_ERROR;
00882         }
00883 
00884         pos += 8;
00885         if (*pos == '"')
00886                 pos++;
00887         batch_id = atoi(pos);
00888         wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
00889                    batch_id);
00890         if (batch_id != tncs->last_batchid + 1) {
00891                 wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
00892                            "%u (expected %u)",
00893                            batch_id, tncs->last_batchid + 1);
00894                 os_free(buf);
00895                 return TNCCS_PROCESS_ERROR;
00896         }
00897         tncs->last_batchid = batch_id;
00898 
00899         while (*pos != '\0' && *pos != '>')
00900                 pos++;
00901         if (*pos == '\0') {
00902                 os_free(buf);
00903                 return TNCCS_PROCESS_ERROR;
00904         }
00905         pos++;
00906         payload = start;
00907 
00908         /*
00909          * <IMC-IMV-Message>
00910          * <Type>01234567</Type>
00911          * <Base64>foo==</Base64>
00912          * </IMC-IMV-Message>
00913          */
00914 
00915         while (*start) {
00916                 char *endpos;
00917                 unsigned int type;
00918 
00919                 pos = os_strstr(start, "<IMC-IMV-Message>");
00920                 if (pos == NULL)
00921                         break;
00922                 start = pos + 17;
00923                 end = os_strstr(start, "</IMC-IMV-Message>");
00924                 if (end == NULL)
00925                         break;
00926                 *end = '\0';
00927                 endpos = end;
00928                 end += 18;
00929 
00930                 if (tncs_get_type(start, &type) < 0) {
00931                         *endpos = '<';
00932                         start = end;
00933                         continue;
00934                 }
00935                 wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
00936 
00937                 decoded = tncs_get_base64(start, &decoded_len);
00938                 if (decoded == NULL) {
00939                         *endpos = '<';
00940                         start = end;
00941                         continue;
00942                 }
00943 
00944                 tncs_send_to_imvs(tncs, type, decoded, decoded_len);
00945 
00946                 os_free(decoded);
00947 
00948                 start = end;
00949         }
00950 
00951         /*
00952          * <TNCC-TNCS-Message>
00953          * <Type>01234567</Type>
00954          * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
00955          * <Base64>foo==</Base64>
00956          * </TNCC-TNCS-Message>
00957          */
00958 
00959         start = payload;
00960         while (*start) {
00961                 unsigned int type;
00962                 char *xml, *xmlend, *endpos;
00963 
00964                 pos = os_strstr(start, "<TNCC-TNCS-Message>");
00965                 if (pos == NULL)
00966                         break;
00967                 start = pos + 19;
00968                 end = os_strstr(start, "</TNCC-TNCS-Message>");
00969                 if (end == NULL)
00970                         break;
00971                 *end = '\0';
00972                 endpos = end;
00973                 end += 20;
00974 
00975                 if (tncs_get_type(start, &type) < 0) {
00976                         *endpos = '<';
00977                         start = end;
00978                         continue;
00979                 }
00980                 wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
00981                            type);
00982 
00983                 /* Base64 OR XML */
00984                 decoded = NULL;
00985                 xml = NULL;
00986                 xmlend = NULL;
00987                 pos = os_strstr(start, "<XML>");
00988                 if (pos) {
00989                         pos += 5;
00990                         pos2 = os_strstr(pos, "</XML>");
00991                         if (pos2 == NULL) {
00992                                 *endpos = '<';
00993                                 start = end;
00994                                 continue;
00995                         }
00996                         xmlend = pos2;
00997                         xml = pos;
00998                 } else {
00999                         decoded = tncs_get_base64(start, &decoded_len);
01000                         if (decoded == NULL) {
01001                                 *endpos = '<';
01002                                 start = end;
01003                                 continue;
01004                         }
01005                 }
01006 
01007                 if (decoded) {
01008                         wpa_hexdump_ascii(MSG_MSGDUMP,
01009                                           "TNC: TNCC-TNCS-Message Base64",
01010                                           decoded, decoded_len);
01011                         os_free(decoded);
01012                 }
01013 
01014                 if (xml) {
01015                         wpa_hexdump_ascii(MSG_MSGDUMP,
01016                                           "TNC: TNCC-TNCS-Message XML",
01017                                           (unsigned char *) xml,
01018                                           xmlend - xml);
01019                 }
01020 
01021                 start = end;
01022         }
01023 
01024         os_free(buf);
01025 
01026         tncs_batch_ending(tncs);
01027 
01028         if (tncs_total_send_len(tncs) == 0)
01029                 return tncs_derive_recommendation(tncs);
01030 
01031         return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
01032 }
01033 
01034 
01035 static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end,
01036                                           int *error)
01037 {
01038         struct tnc_if_imv *imv;
01039         char *pos, *pos2;
01040 
01041         if (id >= TNC_MAX_IMV_ID) {
01042                 wpa_printf(MSG_DEBUG, "TNC: Too many IMVs");
01043                 return NULL;
01044         }
01045 
01046         imv = os_zalloc(sizeof(*imv));
01047         if (imv == NULL) {
01048                 *error = 1;
01049                 return NULL;
01050         }
01051 
01052         imv->imvID = id;
01053 
01054         pos = start;
01055         wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos);
01056         if (pos + 1 >= end || *pos != '"') {
01057                 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
01058                            "(no starting quotation mark)", start);
01059                 os_free(imv);
01060                 return NULL;
01061         }
01062 
01063         pos++;
01064         pos2 = pos;
01065         while (pos2 < end && *pos2 != '"')
01066                 pos2++;
01067         if (pos2 >= end) {
01068                 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
01069                            "(no ending quotation mark)", start);
01070                 os_free(imv);
01071                 return NULL;
01072         }
01073         *pos2 = '\0';
01074         wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
01075         imv->name = os_strdup(pos);
01076 
01077         pos = pos2 + 1;
01078         if (pos >= end || *pos != ' ') {
01079                 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
01080                            "(no space after name)", start);
01081                 os_free(imv);
01082                 return NULL;
01083         }
01084 
01085         pos++;
01086         wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos);
01087         imv->path = os_strdup(pos);
01088 
01089         return imv;
01090 }
01091 
01092 
01093 static int tncs_read_config(struct tncs_global *global)
01094 {
01095         char *config, *end, *pos, *line_end;
01096         size_t config_len;
01097         struct tnc_if_imv *imv, *last;
01098         int id = 0;
01099 
01100         last = NULL;
01101 
01102         config = os_readfile(TNC_CONFIG_FILE, &config_len);
01103         if (config == NULL) {
01104                 wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
01105                            "file '%s'", TNC_CONFIG_FILE);
01106                 return -1;
01107         }
01108 
01109         end = config + config_len;
01110         for (pos = config; pos < end; pos = line_end + 1) {
01111                 line_end = pos;
01112                 while (*line_end != '\n' && *line_end != '\r' &&
01113                        line_end < end)
01114                         line_end++;
01115                 *line_end = '\0';
01116 
01117                 if (os_strncmp(pos, "IMV ", 4) == 0) {
01118                         int error = 0;
01119 
01120                         imv = tncs_parse_imv(id++, pos + 4, line_end, &error);
01121                         if (error)
01122                                 return -1;
01123                         if (imv) {
01124                                 if (last == NULL)
01125                                         global->imv = imv;
01126                                 else
01127                                         last->next = imv;
01128                                 last = imv;
01129                         }
01130                 }
01131         }
01132 
01133         os_free(config);
01134 
01135         return 0;
01136 }
01137 
01138 
01139 struct tncs_data * tncs_init(void)
01140 {
01141         struct tncs_data *tncs;
01142 
01143         if (tncs_global_data == NULL)
01144                 return NULL;
01145 
01146         tncs = os_zalloc(sizeof(*tncs));
01147         if (tncs == NULL)
01148                 return NULL;
01149         tncs->imv = tncs_global_data->imv;
01150         tncs->connectionID = tncs_global_data->next_conn_id++;
01151         tncs->next = tncs_global_data->connections;
01152         tncs_global_data->connections = tncs;
01153 
01154         return tncs;
01155 }
01156 
01157 
01158 void tncs_deinit(struct tncs_data *tncs)
01159 {
01160         int i;
01161         struct tncs_data *prev, *conn;
01162 
01163         if (tncs == NULL)
01164                 return;
01165 
01166         for (i = 0; i < TNC_MAX_IMV_ID; i++)
01167                 os_free(tncs->imv_data[i].imv_send);
01168 
01169         prev = NULL;
01170         conn = tncs_global_data->connections;
01171         while (conn) {
01172                 if (conn == tncs) {
01173                         if (prev)
01174                                 prev->next = tncs->next;
01175                         else
01176                                 tncs_global_data->connections = tncs->next;
01177                         break;
01178                 }
01179                 prev = conn;
01180                 conn = conn->next;
01181         }
01182 
01183         os_free(tncs->tncs_message);
01184         os_free(tncs);
01185 }
01186 
01187 
01188 int tncs_global_init(void)
01189 {
01190         struct tnc_if_imv *imv;
01191 
01192         tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
01193         if (tncs_global_data == NULL)
01194                 return -1;
01195 
01196         if (tncs_read_config(tncs_global_data) < 0) {
01197                 wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
01198                 goto failed;
01199         }
01200 
01201         for (imv = tncs_global_data->imv; imv; imv = imv->next) {
01202                 if (tncs_load_imv(imv)) {
01203                         wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'",
01204                                    imv->name);
01205                         goto failed;
01206                 }
01207         }
01208 
01209         return 0;
01210 
01211 failed:
01212         tncs_global_deinit();
01213         return -1;
01214 }
01215 
01216 
01217 void tncs_global_deinit(void)
01218 {
01219         struct tnc_if_imv *imv, *prev;
01220 
01221         if (tncs_global_data == NULL)
01222                 return;
01223 
01224         imv = tncs_global_data->imv;
01225         while (imv) {
01226                 tncs_unload_imv(imv);
01227 
01228                 prev = imv;
01229                 imv = imv->next;
01230                 os_free(prev);
01231         }
01232 
01233         os_free(tncs_global_data);
01234         tncs_global_data = NULL;
01235 }
01236 
01237 
01238 struct wpabuf * tncs_build_soh_request(void)
01239 {
01240         struct wpabuf *buf;
01241 
01242         /*
01243          * Build a SoH Request TLV (to be used inside SoH EAP Extensions
01244          * Method)
01245          */
01246 
01247         buf = wpabuf_alloc(8 + 4);
01248         if (buf == NULL)
01249                 return NULL;
01250 
01251         /* Vendor-Specific TLV (Microsoft) - SoH Request */
01252         wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
01253         wpabuf_put_be16(buf, 8); /* Length */
01254 
01255         wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
01256 
01257         wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */
01258         wpabuf_put_be16(buf, 0); /* Length */
01259 
01260         return buf;
01261 }
01262 
01263 
01264 struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
01265                                  int *failure)
01266 {
01267         wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len);
01268         *failure = 0;
01269 
01270         /* TODO: return MS-SoH Response TLV */
01271 
01272         return NULL;
01273 }


wpa_supplicant
Author(s): Package maintained by Blaise Gassend
autogenerated on Thu Apr 24 2014 15:34:36