hostapd: Update vendor branch to 0.6.10
[dragonfly.git] / contrib / hostapd / src / eap_server / tncs.c
blob21d83b3bd9614e7be1fe03452338b25010abf925
1 /*
2 * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
3 * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
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.
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
12 * See README and COPYING for more details.
15 #include "includes.h"
16 #include <dlfcn.h>
18 #include "common.h"
19 #include "base64.h"
20 #include "tncs.h"
21 #include "eap_common/eap_tlv_common.h"
22 #include "eap_common/eap_defs.h"
25 /* TODO: TNCS must be thread-safe; review the code and add locking etc. if
26 * needed.. */
28 #define TNC_CONFIG_FILE "/etc/tnc_config"
29 #define IF_TNCCS_START \
30 "<?xml version=\"1.0\"?>\n" \
31 "<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
32 "xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
33 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
34 "xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
35 "IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
36 #define IF_TNCCS_END "\n</TNCCS-Batch>"
38 /* TNC IF-IMV */
40 typedef unsigned long TNC_UInt32;
41 typedef unsigned char *TNC_BufferReference;
43 typedef TNC_UInt32 TNC_IMVID;
44 typedef TNC_UInt32 TNC_ConnectionID;
45 typedef TNC_UInt32 TNC_ConnectionState;
46 typedef TNC_UInt32 TNC_RetryReason;
47 typedef TNC_UInt32 TNC_IMV_Action_Recommendation;
48 typedef TNC_UInt32 TNC_IMV_Evaluation_Result;
49 typedef TNC_UInt32 TNC_MessageType;
50 typedef TNC_MessageType *TNC_MessageTypeList;
51 typedef TNC_UInt32 TNC_VendorID;
52 typedef TNC_UInt32 TNC_Subtype;
53 typedef TNC_UInt32 TNC_Version;
54 typedef TNC_UInt32 TNC_Result;
55 typedef TNC_UInt32 TNC_AttributeID;
57 typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)(
58 TNC_IMVID imvID,
59 char *functionName,
60 void **pOutfunctionPointer);
62 #define TNC_RESULT_SUCCESS 0
63 #define TNC_RESULT_NOT_INITIALIZED 1
64 #define TNC_RESULT_ALREADY_INITIALIZED 2
65 #define TNC_RESULT_NO_COMMON_VERSION 3
66 #define TNC_RESULT_CANT_RETRY 4
67 #define TNC_RESULT_WONT_RETRY 5
68 #define TNC_RESULT_INVALID_PARAMETER 6
69 #define TNC_RESULT_CANT_RESPOND 7
70 #define TNC_RESULT_ILLEGAL_OPERATION 8
71 #define TNC_RESULT_OTHER 9
72 #define TNC_RESULT_FATAL 10
74 #define TNC_CONNECTION_STATE_CREATE 0
75 #define TNC_CONNECTION_STATE_HANDSHAKE 1
76 #define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
77 #define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
78 #define TNC_CONNECTION_STATE_ACCESS_NONE 4
79 #define TNC_CONNECTION_STATE_DELETE 5
81 #define TNC_IFIMV_VERSION_1 1
83 #define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
84 #define TNC_SUBTYPE_ANY ((TNC_Subtype) 0xff)
86 /* TNCC-TNCS Message Types */
87 #define TNC_TNCCS_RECOMMENDATION 0x00000001
88 #define TNC_TNCCS_ERROR 0x00000002
89 #define TNC_TNCCS_PREFERREDLANGUAGE 0x00000003
90 #define TNC_TNCCS_REASONSTRINGS 0x00000004
92 /* Possible TNC_IMV_Action_Recommendation values: */
93 enum IMV_Action_Recommendation {
94 TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
95 TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS,
96 TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
97 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
100 /* Possible TNC_IMV_Evaluation_Result values: */
101 enum IMV_Evaluation_Result {
102 TNC_IMV_EVALUATION_RESULT_COMPLIANT,
103 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR,
104 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR,
105 TNC_IMV_EVALUATION_RESULT_ERROR,
106 TNC_IMV_EVALUATION_RESULT_DONT_KNOW
109 struct tnc_if_imv {
110 struct tnc_if_imv *next;
111 char *name;
112 char *path;
113 void *dlhandle; /* from dlopen() */
114 TNC_IMVID imvID;
115 TNC_MessageTypeList supported_types;
116 size_t num_supported_types;
118 /* Functions implemented by IMVs (with TNC_IMV_ prefix) */
119 TNC_Result (*Initialize)(
120 TNC_IMVID imvID,
121 TNC_Version minVersion,
122 TNC_Version maxVersion,
123 TNC_Version *pOutActualVersion);
124 TNC_Result (*NotifyConnectionChange)(
125 TNC_IMVID imvID,
126 TNC_ConnectionID connectionID,
127 TNC_ConnectionState newState);
128 TNC_Result (*ReceiveMessage)(
129 TNC_IMVID imvID,
130 TNC_ConnectionID connectionID,
131 TNC_BufferReference message,
132 TNC_UInt32 messageLength,
133 TNC_MessageType messageType);
134 TNC_Result (*SolicitRecommendation)(
135 TNC_IMVID imvID,
136 TNC_ConnectionID connectionID);
137 TNC_Result (*BatchEnding)(
138 TNC_IMVID imvID,
139 TNC_ConnectionID connectionID);
140 TNC_Result (*Terminate)(TNC_IMVID imvID);
141 TNC_Result (*ProvideBindFunction)(
142 TNC_IMVID imvID,
143 TNC_TNCS_BindFunctionPointer bindFunction);
147 #define TNC_MAX_IMV_ID 10
149 struct tncs_data {
150 struct tncs_data *next;
151 struct tnc_if_imv *imv; /* local copy of tncs_global_data->imv */
152 TNC_ConnectionID connectionID;
153 unsigned int last_batchid;
154 enum IMV_Action_Recommendation recommendation;
155 int done;
157 struct conn_imv {
158 u8 *imv_send;
159 size_t imv_send_len;
160 enum IMV_Action_Recommendation recommendation;
161 int recommendation_set;
162 } imv_data[TNC_MAX_IMV_ID];
164 char *tncs_message;
168 struct tncs_global {
169 struct tnc_if_imv *imv;
170 TNC_ConnectionID next_conn_id;
171 struct tncs_data *connections;
174 static struct tncs_global *tncs_global_data = NULL;
177 static struct tnc_if_imv * tncs_get_imv(TNC_IMVID imvID)
179 struct tnc_if_imv *imv;
181 if (imvID >= TNC_MAX_IMV_ID || tncs_global_data == NULL)
182 return NULL;
183 imv = tncs_global_data->imv;
184 while (imv) {
185 if (imv->imvID == imvID)
186 return imv;
187 imv = imv->next;
189 return NULL;
193 static struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID)
195 struct tncs_data *tncs;
197 if (tncs_global_data == NULL)
198 return NULL;
200 tncs = tncs_global_data->connections;
201 while (tncs) {
202 if (tncs->connectionID == connectionID)
203 return tncs;
204 tncs = tncs->next;
207 wpa_printf(MSG_DEBUG, "TNC: Connection ID %lu not found",
208 (unsigned long) connectionID);
210 return NULL;
214 /* TNCS functions that IMVs can call */
215 TNC_Result TNC_TNCS_ReportMessageTypes(
216 TNC_IMVID imvID,
217 TNC_MessageTypeList supportedTypes,
218 TNC_UInt32 typeCount)
220 TNC_UInt32 i;
221 struct tnc_if_imv *imv;
223 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ReportMessageTypes(imvID=%lu "
224 "typeCount=%lu)",
225 (unsigned long) imvID, (unsigned long) typeCount);
227 for (i = 0; i < typeCount; i++) {
228 wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
229 i, supportedTypes[i]);
232 imv = tncs_get_imv(imvID);
233 if (imv == NULL)
234 return TNC_RESULT_INVALID_PARAMETER;
235 os_free(imv->supported_types);
236 imv->supported_types =
237 os_malloc(typeCount * sizeof(TNC_MessageTypeList));
238 if (imv->supported_types == NULL)
239 return TNC_RESULT_FATAL;
240 os_memcpy(imv->supported_types, supportedTypes,
241 typeCount * sizeof(TNC_MessageTypeList));
242 imv->num_supported_types = typeCount;
244 return TNC_RESULT_SUCCESS;
248 TNC_Result TNC_TNCS_SendMessage(
249 TNC_IMVID imvID,
250 TNC_ConnectionID connectionID,
251 TNC_BufferReference message,
252 TNC_UInt32 messageLength,
253 TNC_MessageType messageType)
255 struct tncs_data *tncs;
256 unsigned char *b64;
257 size_t b64len;
259 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage(imvID=%lu "
260 "connectionID=%lu messageType=%lu)",
261 imvID, connectionID, messageType);
262 wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage",
263 message, messageLength);
265 if (tncs_get_imv(imvID) == NULL)
266 return TNC_RESULT_INVALID_PARAMETER;
268 tncs = tncs_get_conn(connectionID);
269 if (tncs == NULL)
270 return TNC_RESULT_INVALID_PARAMETER;
272 b64 = base64_encode(message, messageLength, &b64len);
273 if (b64 == NULL)
274 return TNC_RESULT_FATAL;
276 os_free(tncs->imv_data[imvID].imv_send);
277 tncs->imv_data[imvID].imv_send_len = 0;
278 tncs->imv_data[imvID].imv_send = os_zalloc(b64len + 100);
279 if (tncs->imv_data[imvID].imv_send == NULL) {
280 os_free(b64);
281 return TNC_RESULT_OTHER;
284 tncs->imv_data[imvID].imv_send_len =
285 os_snprintf((char *) tncs->imv_data[imvID].imv_send,
286 b64len + 100,
287 "<IMC-IMV-Message><Type>%08X</Type>"
288 "<Base64>%s</Base64></IMC-IMV-Message>",
289 (unsigned int) messageType, b64);
291 os_free(b64);
293 return TNC_RESULT_SUCCESS;
297 TNC_Result TNC_TNCS_RequestHandshakeRetry(
298 TNC_IMVID imvID,
299 TNC_ConnectionID connectionID,
300 TNC_RetryReason reason)
302 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_RequestHandshakeRetry");
303 /* TODO */
304 return TNC_RESULT_SUCCESS;
308 TNC_Result TNC_TNCS_ProvideRecommendation(
309 TNC_IMVID imvID,
310 TNC_ConnectionID connectionID,
311 TNC_IMV_Action_Recommendation recommendation,
312 TNC_IMV_Evaluation_Result evaluation)
314 struct tncs_data *tncs;
316 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ProvideRecommendation(imvID=%lu "
317 "connectionID=%lu recommendation=%lu evaluation=%lu)",
318 (unsigned long) imvID, (unsigned long) connectionID,
319 (unsigned long) recommendation, (unsigned long) evaluation);
321 if (tncs_get_imv(imvID) == NULL)
322 return TNC_RESULT_INVALID_PARAMETER;
324 tncs = tncs_get_conn(connectionID);
325 if (tncs == NULL)
326 return TNC_RESULT_INVALID_PARAMETER;
328 tncs->imv_data[imvID].recommendation = recommendation;
329 tncs->imv_data[imvID].recommendation_set = 1;
331 return TNC_RESULT_SUCCESS;
335 TNC_Result TNC_TNCS_GetAttribute(
336 TNC_IMVID imvID,
337 TNC_ConnectionID connectionID,
338 TNC_AttributeID attribureID,
339 TNC_UInt32 bufferLength,
340 TNC_BufferReference buffer,
341 TNC_UInt32 *pOutValueLength)
343 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_GetAttribute");
344 /* TODO */
345 return TNC_RESULT_SUCCESS;
349 TNC_Result TNC_TNCS_SetAttribute(
350 TNC_IMVID imvID,
351 TNC_ConnectionID connectionID,
352 TNC_AttributeID attribureID,
353 TNC_UInt32 bufferLength,
354 TNC_BufferReference buffer)
356 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SetAttribute");
357 /* TODO */
358 return TNC_RESULT_SUCCESS;
362 TNC_Result TNC_TNCS_BindFunction(
363 TNC_IMVID imvID,
364 char *functionName,
365 void **pOutFunctionPointer)
367 wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_BindFunction(imcID=%lu, "
368 "functionName='%s')", (unsigned long) imvID, functionName);
370 if (tncs_get_imv(imvID) == NULL)
371 return TNC_RESULT_INVALID_PARAMETER;
373 if (pOutFunctionPointer == NULL)
374 return TNC_RESULT_INVALID_PARAMETER;
376 if (os_strcmp(functionName, "TNC_TNCS_ReportMessageTypes") == 0)
377 *pOutFunctionPointer = TNC_TNCS_ReportMessageTypes;
378 else if (os_strcmp(functionName, "TNC_TNCS_SendMessage") == 0)
379 *pOutFunctionPointer = TNC_TNCS_SendMessage;
380 else if (os_strcmp(functionName, "TNC_TNCS_RequestHandshakeRetry") ==
382 *pOutFunctionPointer = TNC_TNCS_RequestHandshakeRetry;
383 else if (os_strcmp(functionName, "TNC_TNCS_ProvideRecommendation") ==
385 *pOutFunctionPointer = TNC_TNCS_ProvideRecommendation;
386 else if (os_strcmp(functionName, "TNC_TNCS_GetAttribute") == 0)
387 *pOutFunctionPointer = TNC_TNCS_GetAttribute;
388 else if (os_strcmp(functionName, "TNC_TNCS_SetAttribute") == 0)
389 *pOutFunctionPointer = TNC_TNCS_SetAttribute;
390 else
391 *pOutFunctionPointer = NULL;
393 return TNC_RESULT_SUCCESS;
397 static void * tncs_get_sym(void *handle, char *func)
399 void *fptr;
401 fptr = dlsym(handle, func);
403 return fptr;
407 static int tncs_imv_resolve_funcs(struct tnc_if_imv *imv)
409 void *handle = imv->dlhandle;
411 /* Mandatory IMV functions */
412 imv->Initialize = tncs_get_sym(handle, "TNC_IMV_Initialize");
413 if (imv->Initialize == NULL) {
414 wpa_printf(MSG_ERROR, "TNC: IMV does not export "
415 "TNC_IMV_Initialize");
416 return -1;
419 imv->SolicitRecommendation = tncs_get_sym(
420 handle, "TNC_IMV_SolicitRecommendation");
421 if (imv->SolicitRecommendation == NULL) {
422 wpa_printf(MSG_ERROR, "TNC: IMV does not export "
423 "TNC_IMV_SolicitRecommendation");
424 return -1;
427 imv->ProvideBindFunction =
428 tncs_get_sym(handle, "TNC_IMV_ProvideBindFunction");
429 if (imv->ProvideBindFunction == NULL) {
430 wpa_printf(MSG_ERROR, "TNC: IMV does not export "
431 "TNC_IMV_ProvideBindFunction");
432 return -1;
435 /* Optional IMV functions */
436 imv->NotifyConnectionChange =
437 tncs_get_sym(handle, "TNC_IMV_NotifyConnectionChange");
438 imv->ReceiveMessage = tncs_get_sym(handle, "TNC_IMV_ReceiveMessage");
439 imv->BatchEnding = tncs_get_sym(handle, "TNC_IMV_BatchEnding");
440 imv->Terminate = tncs_get_sym(handle, "TNC_IMV_Terminate");
442 return 0;
446 static int tncs_imv_initialize(struct tnc_if_imv *imv)
448 TNC_Result res;
449 TNC_Version imv_ver;
451 wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Initialize for IMV '%s'",
452 imv->name);
453 res = imv->Initialize(imv->imvID, TNC_IFIMV_VERSION_1,
454 TNC_IFIMV_VERSION_1, &imv_ver);
455 wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Initialize: res=%lu imv_ver=%lu",
456 (unsigned long) res, (unsigned long) imv_ver);
458 return res == TNC_RESULT_SUCCESS ? 0 : -1;
462 static int tncs_imv_terminate(struct tnc_if_imv *imv)
464 TNC_Result res;
466 if (imv->Terminate == NULL)
467 return 0;
469 wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Terminate for IMV '%s'",
470 imv->name);
471 res = imv->Terminate(imv->imvID);
472 wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Terminate: %lu",
473 (unsigned long) res);
475 return res == TNC_RESULT_SUCCESS ? 0 : -1;
479 static int tncs_imv_provide_bind_function(struct tnc_if_imv *imv)
481 TNC_Result res;
483 wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_ProvideBindFunction for "
484 "IMV '%s'", imv->name);
485 res = imv->ProvideBindFunction(imv->imvID, TNC_TNCS_BindFunction);
486 wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_ProvideBindFunction: res=%lu",
487 (unsigned long) res);
489 return res == TNC_RESULT_SUCCESS ? 0 : -1;
493 static int tncs_imv_notify_connection_change(struct tnc_if_imv *imv,
494 TNC_ConnectionID conn,
495 TNC_ConnectionState state)
497 TNC_Result res;
499 if (imv->NotifyConnectionChange == NULL)
500 return 0;
502 wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_NotifyConnectionChange(%d)"
503 " for IMV '%s'", (int) state, imv->name);
504 res = imv->NotifyConnectionChange(imv->imvID, conn, state);
505 wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
506 (unsigned long) res);
508 return res == TNC_RESULT_SUCCESS ? 0 : -1;
512 static int tncs_load_imv(struct tnc_if_imv *imv)
514 if (imv->path == NULL) {
515 wpa_printf(MSG_DEBUG, "TNC: No IMV configured");
516 return -1;
519 wpa_printf(MSG_DEBUG, "TNC: Opening IMV: %s (%s)",
520 imv->name, imv->path);
521 imv->dlhandle = dlopen(imv->path, RTLD_LAZY);
522 if (imv->dlhandle == NULL) {
523 wpa_printf(MSG_ERROR, "TNC: Failed to open IMV '%s' (%s): %s",
524 imv->name, imv->path, dlerror());
525 return -1;
528 if (tncs_imv_resolve_funcs(imv) < 0) {
529 wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMV functions");
530 return -1;
533 if (tncs_imv_initialize(imv) < 0 ||
534 tncs_imv_provide_bind_function(imv) < 0) {
535 wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMV");
536 return -1;
539 return 0;
543 static void tncs_free_imv(struct tnc_if_imv *imv)
545 os_free(imv->name);
546 os_free(imv->path);
547 os_free(imv->supported_types);
550 static void tncs_unload_imv(struct tnc_if_imv *imv)
552 tncs_imv_terminate(imv);
554 if (imv->dlhandle)
555 dlclose(imv->dlhandle);
557 tncs_free_imv(imv);
561 static int tncs_supported_type(struct tnc_if_imv *imv, unsigned int type)
563 size_t i;
564 unsigned int vendor, subtype;
566 if (imv == NULL || imv->supported_types == NULL)
567 return 0;
569 vendor = type >> 8;
570 subtype = type & 0xff;
572 for (i = 0; i < imv->num_supported_types; i++) {
573 unsigned int svendor, ssubtype;
574 svendor = imv->supported_types[i] >> 8;
575 ssubtype = imv->supported_types[i] & 0xff;
576 if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
577 (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
578 return 1;
581 return 0;
585 static void tncs_send_to_imvs(struct tncs_data *tncs, unsigned int type,
586 const u8 *msg, size_t len)
588 struct tnc_if_imv *imv;
589 TNC_Result res;
591 wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMV(s)", msg, len);
593 for (imv = tncs->imv; imv; imv = imv->next) {
594 if (imv->ReceiveMessage == NULL ||
595 !tncs_supported_type(imv, type))
596 continue;
598 wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMV '%s'",
599 imv->name);
600 res = imv->ReceiveMessage(imv->imvID, tncs->connectionID,
601 (TNC_BufferReference) msg, len,
602 type);
603 wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
604 (unsigned long) res);
609 static void tncs_batch_ending(struct tncs_data *tncs)
611 struct tnc_if_imv *imv;
612 TNC_Result res;
614 for (imv = tncs->imv; imv; imv = imv->next) {
615 if (imv->BatchEnding == NULL)
616 continue;
618 wpa_printf(MSG_DEBUG, "TNC: Call BatchEnding for IMV '%s'",
619 imv->name);
620 res = imv->BatchEnding(imv->imvID, tncs->connectionID);
621 wpa_printf(MSG_DEBUG, "TNC: BatchEnding: %lu",
622 (unsigned long) res);
627 static void tncs_solicit_recommendation(struct tncs_data *tncs)
629 struct tnc_if_imv *imv;
630 TNC_Result res;
632 for (imv = tncs->imv; imv; imv = imv->next) {
633 if (tncs->imv_data[imv->imvID].recommendation_set)
634 continue;
636 wpa_printf(MSG_DEBUG, "TNC: Call SolicitRecommendation for "
637 "IMV '%s'", imv->name);
638 res = imv->SolicitRecommendation(imv->imvID,
639 tncs->connectionID);
640 wpa_printf(MSG_DEBUG, "TNC: SolicitRecommendation: %lu",
641 (unsigned long) res);
646 void tncs_init_connection(struct tncs_data *tncs)
648 struct tnc_if_imv *imv;
649 int i;
651 for (imv = tncs->imv; imv; imv = imv->next) {
652 tncs_imv_notify_connection_change(
653 imv, tncs->connectionID, TNC_CONNECTION_STATE_CREATE);
654 tncs_imv_notify_connection_change(
655 imv, tncs->connectionID,
656 TNC_CONNECTION_STATE_HANDSHAKE);
659 for (i = 0; i < TNC_MAX_IMV_ID; i++) {
660 os_free(tncs->imv_data[i].imv_send);
661 tncs->imv_data[i].imv_send = NULL;
662 tncs->imv_data[i].imv_send_len = 0;
667 size_t tncs_total_send_len(struct tncs_data *tncs)
669 int i;
670 size_t len = 0;
672 for (i = 0; i < TNC_MAX_IMV_ID; i++)
673 len += tncs->imv_data[i].imv_send_len;
674 if (tncs->tncs_message)
675 len += os_strlen(tncs->tncs_message);
676 return len;
680 u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos)
682 int i;
684 for (i = 0; i < TNC_MAX_IMV_ID; i++) {
685 if (tncs->imv_data[i].imv_send == NULL)
686 continue;
688 os_memcpy(pos, tncs->imv_data[i].imv_send,
689 tncs->imv_data[i].imv_send_len);
690 pos += tncs->imv_data[i].imv_send_len;
691 os_free(tncs->imv_data[i].imv_send);
692 tncs->imv_data[i].imv_send = NULL;
693 tncs->imv_data[i].imv_send_len = 0;
696 if (tncs->tncs_message) {
697 size_t len = os_strlen(tncs->tncs_message);
698 os_memcpy(pos, tncs->tncs_message, len);
699 pos += len;
700 os_free(tncs->tncs_message);
701 tncs->tncs_message = NULL;
704 return pos;
708 char * tncs_if_tnccs_start(struct tncs_data *tncs)
710 char *buf = os_malloc(1000);
711 if (buf == NULL)
712 return NULL;
713 tncs->last_batchid++;
714 os_snprintf(buf, 1000, IF_TNCCS_START, tncs->last_batchid);
715 return buf;
719 char * tncs_if_tnccs_end(void)
721 char *buf = os_malloc(100);
722 if (buf == NULL)
723 return NULL;
724 os_snprintf(buf, 100, IF_TNCCS_END);
725 return buf;
729 static int tncs_get_type(char *start, unsigned int *type)
731 char *pos = os_strstr(start, "<Type>");
732 if (pos == NULL)
733 return -1;
734 pos += 6;
735 *type = strtoul(pos, NULL, 16);
736 return 0;
740 static unsigned char * tncs_get_base64(char *start, size_t *decoded_len)
742 char *pos, *pos2;
743 unsigned char *decoded;
745 pos = os_strstr(start, "<Base64>");
746 if (pos == NULL)
747 return NULL;
749 pos += 8;
750 pos2 = os_strstr(pos, "</Base64>");
751 if (pos2 == NULL)
752 return NULL;
753 *pos2 = '\0';
755 decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
756 decoded_len);
757 *pos2 = '<';
758 if (decoded == NULL) {
759 wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
762 return decoded;
766 static enum tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs)
768 enum IMV_Action_Recommendation rec;
769 struct tnc_if_imv *imv;
770 TNC_ConnectionState state;
771 char *txt;
773 wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs");
775 if (tncs->done)
776 return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
778 tncs_solicit_recommendation(tncs);
780 /* Select the most restrictive recommendation */
781 rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION;
782 for (imv = tncs->imv; imv; imv = imv->next) {
783 TNC_IMV_Action_Recommendation irec;
784 irec = tncs->imv_data[imv->imvID].recommendation;
785 if (irec == TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
786 rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS;
787 if (irec == TNC_IMV_ACTION_RECOMMENDATION_ISOLATE &&
788 rec != TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
789 rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE;
790 if (irec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW &&
791 rec == TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION)
792 rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW;
795 wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec);
796 tncs->recommendation = rec;
797 tncs->done = 1;
799 txt = NULL;
800 switch (rec) {
801 case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
802 case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
803 txt = "allow";
804 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
805 break;
806 case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
807 txt = "isolate";
808 state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
809 break;
810 case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
811 txt = "none";
812 state = TNC_CONNECTION_STATE_ACCESS_NONE;
813 break;
814 default:
815 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
816 break;
819 if (txt) {
820 os_free(tncs->tncs_message);
821 tncs->tncs_message = os_zalloc(200);
822 if (tncs->tncs_message) {
823 os_snprintf(tncs->tncs_message, 199,
824 "<TNCC-TNCS-Message><Type>%08X</Type>"
825 "<XML><TNCCS-Recommendation type=\"%s\">"
826 "</TNCCS-Recommendation></XML>"
827 "</TNCC-TNCS-Message>",
828 TNC_TNCCS_RECOMMENDATION, txt);
832 for (imv = tncs->imv; imv; imv = imv->next) {
833 tncs_imv_notify_connection_change(imv, tncs->connectionID,
834 state);
837 switch (rec) {
838 case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
839 return TNCCS_RECOMMENDATION_ALLOW;
840 case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
841 return TNCCS_RECOMMENDATION_NO_ACCESS;
842 case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
843 return TNCCS_RECOMMENDATION_ISOLATE;
844 case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
845 return TNCCS_RECOMMENDATION_NO_RECOMMENDATION;
846 default:
847 return TNCCS_PROCESS_ERROR;
852 enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
853 const u8 *msg, size_t len)
855 char *buf, *start, *end, *pos, *pos2, *payload;
856 unsigned int batch_id;
857 unsigned char *decoded;
858 size_t decoded_len;
860 buf = os_malloc(len + 1);
861 if (buf == NULL)
862 return TNCCS_PROCESS_ERROR;
864 os_memcpy(buf, msg, len);
865 buf[len] = '\0';
866 start = os_strstr(buf, "<TNCCS-Batch ");
867 end = os_strstr(buf, "</TNCCS-Batch>");
868 if (start == NULL || end == NULL || start > end) {
869 os_free(buf);
870 return TNCCS_PROCESS_ERROR;
873 start += 13;
874 while (*start == ' ')
875 start++;
876 *end = '\0';
878 pos = os_strstr(start, "BatchId=");
879 if (pos == NULL) {
880 os_free(buf);
881 return TNCCS_PROCESS_ERROR;
884 pos += 8;
885 if (*pos == '"')
886 pos++;
887 batch_id = atoi(pos);
888 wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
889 batch_id);
890 if (batch_id != tncs->last_batchid + 1) {
891 wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
892 "%u (expected %u)",
893 batch_id, tncs->last_batchid + 1);
894 os_free(buf);
895 return TNCCS_PROCESS_ERROR;
897 tncs->last_batchid = batch_id;
899 while (*pos != '\0' && *pos != '>')
900 pos++;
901 if (*pos == '\0') {
902 os_free(buf);
903 return TNCCS_PROCESS_ERROR;
905 pos++;
906 payload = start;
909 * <IMC-IMV-Message>
910 * <Type>01234567</Type>
911 * <Base64>foo==</Base64>
912 * </IMC-IMV-Message>
915 while (*start) {
916 char *endpos;
917 unsigned int type;
919 pos = os_strstr(start, "<IMC-IMV-Message>");
920 if (pos == NULL)
921 break;
922 start = pos + 17;
923 end = os_strstr(start, "</IMC-IMV-Message>");
924 if (end == NULL)
925 break;
926 *end = '\0';
927 endpos = end;
928 end += 18;
930 if (tncs_get_type(start, &type) < 0) {
931 *endpos = '<';
932 start = end;
933 continue;
935 wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
937 decoded = tncs_get_base64(start, &decoded_len);
938 if (decoded == NULL) {
939 *endpos = '<';
940 start = end;
941 continue;
944 tncs_send_to_imvs(tncs, type, decoded, decoded_len);
946 os_free(decoded);
948 start = end;
952 * <TNCC-TNCS-Message>
953 * <Type>01234567</Type>
954 * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
955 * <Base64>foo==</Base64>
956 * </TNCC-TNCS-Message>
959 start = payload;
960 while (*start) {
961 unsigned int type;
962 char *xml, *xmlend, *endpos;
964 pos = os_strstr(start, "<TNCC-TNCS-Message>");
965 if (pos == NULL)
966 break;
967 start = pos + 19;
968 end = os_strstr(start, "</TNCC-TNCS-Message>");
969 if (end == NULL)
970 break;
971 *end = '\0';
972 endpos = end;
973 end += 20;
975 if (tncs_get_type(start, &type) < 0) {
976 *endpos = '<';
977 start = end;
978 continue;
980 wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
981 type);
983 /* Base64 OR XML */
984 decoded = NULL;
985 xml = NULL;
986 xmlend = NULL;
987 pos = os_strstr(start, "<XML>");
988 if (pos) {
989 pos += 5;
990 pos2 = os_strstr(pos, "</XML>");
991 if (pos2 == NULL) {
992 *endpos = '<';
993 start = end;
994 continue;
996 xmlend = pos2;
997 xml = pos;
998 } else {
999 decoded = tncs_get_base64(start, &decoded_len);
1000 if (decoded == NULL) {
1001 *endpos = '<';
1002 start = end;
1003 continue;
1007 if (decoded) {
1008 wpa_hexdump_ascii(MSG_MSGDUMP,
1009 "TNC: TNCC-TNCS-Message Base64",
1010 decoded, decoded_len);
1011 os_free(decoded);
1014 if (xml) {
1015 wpa_hexdump_ascii(MSG_MSGDUMP,
1016 "TNC: TNCC-TNCS-Message XML",
1017 (unsigned char *) xml,
1018 xmlend - xml);
1021 start = end;
1024 os_free(buf);
1026 tncs_batch_ending(tncs);
1028 if (tncs_total_send_len(tncs) == 0)
1029 return tncs_derive_recommendation(tncs);
1031 return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
1035 static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end,
1036 int *error)
1038 struct tnc_if_imv *imv;
1039 char *pos, *pos2;
1041 if (id >= TNC_MAX_IMV_ID) {
1042 wpa_printf(MSG_DEBUG, "TNC: Too many IMVs");
1043 return NULL;
1046 imv = os_zalloc(sizeof(*imv));
1047 if (imv == NULL) {
1048 *error = 1;
1049 return NULL;
1052 imv->imvID = id;
1054 pos = start;
1055 wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos);
1056 if (pos + 1 >= end || *pos != '"') {
1057 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
1058 "(no starting quotation mark)", start);
1059 os_free(imv);
1060 return NULL;
1063 pos++;
1064 pos2 = pos;
1065 while (pos2 < end && *pos2 != '"')
1066 pos2++;
1067 if (pos2 >= end) {
1068 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
1069 "(no ending quotation mark)", start);
1070 os_free(imv);
1071 return NULL;
1073 *pos2 = '\0';
1074 wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
1075 imv->name = os_strdup(pos);
1077 pos = pos2 + 1;
1078 if (pos >= end || *pos != ' ') {
1079 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
1080 "(no space after name)", start);
1081 os_free(imv);
1082 return NULL;
1085 pos++;
1086 wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos);
1087 imv->path = os_strdup(pos);
1089 return imv;
1093 static int tncs_read_config(struct tncs_global *global)
1095 char *config, *end, *pos, *line_end;
1096 size_t config_len;
1097 struct tnc_if_imv *imv, *last;
1098 int id = 0;
1100 last = NULL;
1102 config = os_readfile(TNC_CONFIG_FILE, &config_len);
1103 if (config == NULL) {
1104 wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
1105 "file '%s'", TNC_CONFIG_FILE);
1106 return -1;
1109 end = config + config_len;
1110 for (pos = config; pos < end; pos = line_end + 1) {
1111 line_end = pos;
1112 while (*line_end != '\n' && *line_end != '\r' &&
1113 line_end < end)
1114 line_end++;
1115 *line_end = '\0';
1117 if (os_strncmp(pos, "IMV ", 4) == 0) {
1118 int error = 0;
1120 imv = tncs_parse_imv(id++, pos + 4, line_end, &error);
1121 if (error)
1122 return -1;
1123 if (imv) {
1124 if (last == NULL)
1125 global->imv = imv;
1126 else
1127 last->next = imv;
1128 last = imv;
1133 os_free(config);
1135 return 0;
1139 struct tncs_data * tncs_init(void)
1141 struct tncs_data *tncs;
1143 if (tncs_global_data == NULL)
1144 return NULL;
1146 tncs = os_zalloc(sizeof(*tncs));
1147 if (tncs == NULL)
1148 return NULL;
1149 tncs->imv = tncs_global_data->imv;
1150 tncs->connectionID = tncs_global_data->next_conn_id++;
1151 tncs->next = tncs_global_data->connections;
1152 tncs_global_data->connections = tncs;
1154 return tncs;
1158 void tncs_deinit(struct tncs_data *tncs)
1160 int i;
1161 struct tncs_data *prev, *conn;
1163 if (tncs == NULL)
1164 return;
1166 for (i = 0; i < TNC_MAX_IMV_ID; i++)
1167 os_free(tncs->imv_data[i].imv_send);
1169 prev = NULL;
1170 conn = tncs_global_data->connections;
1171 while (conn) {
1172 if (conn == tncs) {
1173 if (prev)
1174 prev->next = tncs->next;
1175 else
1176 tncs_global_data->connections = tncs->next;
1177 break;
1179 prev = conn;
1180 conn = conn->next;
1183 os_free(tncs->tncs_message);
1184 os_free(tncs);
1188 int tncs_global_init(void)
1190 struct tnc_if_imv *imv;
1192 tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
1193 if (tncs_global_data == NULL)
1194 return -1;
1196 if (tncs_read_config(tncs_global_data) < 0) {
1197 wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
1198 goto failed;
1201 for (imv = tncs_global_data->imv; imv; imv = imv->next) {
1202 if (tncs_load_imv(imv)) {
1203 wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'",
1204 imv->name);
1205 goto failed;
1209 return 0;
1211 failed:
1212 tncs_global_deinit();
1213 return -1;
1217 void tncs_global_deinit(void)
1219 struct tnc_if_imv *imv, *prev;
1221 if (tncs_global_data == NULL)
1222 return;
1224 imv = tncs_global_data->imv;
1225 while (imv) {
1226 tncs_unload_imv(imv);
1228 prev = imv;
1229 imv = imv->next;
1230 os_free(prev);
1233 os_free(tncs_global_data);
1237 struct wpabuf * tncs_build_soh_request(void)
1239 struct wpabuf *buf;
1242 * Build a SoH Request TLV (to be used inside SoH EAP Extensions
1243 * Method)
1246 buf = wpabuf_alloc(8 + 4);
1247 if (buf == NULL)
1248 return NULL;
1250 /* Vendor-Specific TLV (Microsoft) - SoH Request */
1251 wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
1252 wpabuf_put_be16(buf, 8); /* Length */
1254 wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
1256 wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */
1257 wpabuf_put_be16(buf, 0); /* Length */
1259 return buf;
1263 struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
1264 int *failure)
1266 wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len);
1267 *failure = 0;
1269 /* TODO: return MS-SoH Response TLV */
1271 return NULL;