tncs.c

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

Generated on Sat Nov 21 23:16:52 2009 for hostapd by  doxygen 1.6.1