tncs.c 29 KB

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