Merge branch 'vendor/ZLIB'
[dragonfly.git] / contrib / hostapd / src / eap_server / tncs.c
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
15 #include "includes.h"
16 #include <dlfcn.h>
17
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"
23
24
25 /* TODO: TNCS must be thread-safe; review the code and add locking etc. if
26  * needed.. */
27
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>"
37
38 /* TNC IF-IMV */
39
40 typedef unsigned long TNC_UInt32;
41 typedef unsigned char *TNC_BufferReference;
42
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;
56
57 typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)(
58         TNC_IMVID imvID,
59         char *functionName,
60         void **pOutfunctionPointer);
61
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
73
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
80
81 #define TNC_IFIMV_VERSION_1 1
82
83 #define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
84 #define TNC_SUBTYPE_ANY ((TNC_Subtype) 0xff)
85
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
91
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
98 };
99
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
107 };
108
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;
117
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);
144 };
145
146
147 #define TNC_MAX_IMV_ID 10
148
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;
156
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];
163
164         char *tncs_message;
165 };
166
167
168 struct tncs_global {
169         struct tnc_if_imv *imv;
170         TNC_ConnectionID next_conn_id;
171         struct tncs_data *connections;
172 };
173
174 static struct tncs_global *tncs_global_data = NULL;
175
176
177 static struct tnc_if_imv * tncs_get_imv(TNC_IMVID imvID)
178 {
179         struct tnc_if_imv *imv;
180
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;
188         }
189         return NULL;
190 }
191
192
193 static struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID)
194 {
195         struct tncs_data *tncs;
196
197         if (tncs_global_data == NULL)
198                 return NULL;
199
200         tncs = tncs_global_data->connections;
201         while (tncs) {
202                 if (tncs->connectionID == connectionID)
203                         return tncs;
204                 tncs = tncs->next;
205         }
206
207         wpa_printf(MSG_DEBUG, "TNC: Connection ID %lu not found",
208                    (unsigned long) connectionID);
209
210         return NULL;
211 }
212
213
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)
219 {
220         TNC_UInt32 i;
221         struct tnc_if_imv *imv;
222
223         wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ReportMessageTypes(imvID=%lu "
224                    "typeCount=%lu)",
225                    (unsigned long) imvID, (unsigned long) typeCount);
226
227         for (i = 0; i < typeCount; i++) {
228                 wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
229                            i, supportedTypes[i]);
230         }
231
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;
243
244         return TNC_RESULT_SUCCESS;
245 }
246
247
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)
254 {
255         struct tncs_data *tncs;
256         unsigned char *b64;
257         size_t b64len;
258
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);
264
265         if (tncs_get_imv(imvID) == NULL)
266                 return TNC_RESULT_INVALID_PARAMETER;
267
268         tncs = tncs_get_conn(connectionID);
269         if (tncs == NULL)
270                 return TNC_RESULT_INVALID_PARAMETER;
271
272         b64 = base64_encode(message, messageLength, &b64len);
273         if (b64 == NULL)
274                 return TNC_RESULT_FATAL;
275
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;
282         }
283
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);
290
291         os_free(b64);
292
293         return TNC_RESULT_SUCCESS;
294 }
295
296
297 TNC_Result TNC_TNCS_RequestHandshakeRetry(
298         TNC_IMVID imvID,
299         TNC_ConnectionID connectionID,
300         TNC_RetryReason reason)
301 {
302         wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_RequestHandshakeRetry");
303         /* TODO */
304         return TNC_RESULT_SUCCESS;
305 }
306
307
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)
313 {
314         struct tncs_data *tncs;
315
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);
320
321         if (tncs_get_imv(imvID) == NULL)
322                 return TNC_RESULT_INVALID_PARAMETER;
323
324         tncs = tncs_get_conn(connectionID);
325         if (tncs == NULL)
326                 return TNC_RESULT_INVALID_PARAMETER;
327
328         tncs->imv_data[imvID].recommendation = recommendation;
329         tncs->imv_data[imvID].recommendation_set = 1;
330
331         return TNC_RESULT_SUCCESS;
332 }
333
334
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)
342 {
343         wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_GetAttribute");
344         /* TODO */
345         return TNC_RESULT_SUCCESS;
346 }
347
348
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)
355 {
356         wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SetAttribute");
357         /* TODO */
358         return TNC_RESULT_SUCCESS;
359 }
360
361
362 TNC_Result TNC_TNCS_BindFunction(
363         TNC_IMVID imvID,
364         char *functionName,
365         void **pOutFunctionPointer)
366 {
367         wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_BindFunction(imcID=%lu, "
368                    "functionName='%s')", (unsigned long) imvID, functionName);
369
370         if (tncs_get_imv(imvID) == NULL)
371                 return TNC_RESULT_INVALID_PARAMETER;
372
373         if (pOutFunctionPointer == NULL)
374                 return TNC_RESULT_INVALID_PARAMETER;
375
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") ==
381                  0)
382                 *pOutFunctionPointer = TNC_TNCS_RequestHandshakeRetry;
383         else if (os_strcmp(functionName, "TNC_TNCS_ProvideRecommendation") ==
384                  0)
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;
392
393         return TNC_RESULT_SUCCESS;
394 }
395
396
397 static void * tncs_get_sym(void *handle, char *func)
398 {
399         void *fptr;
400
401         fptr = dlsym(handle, func);
402
403         return fptr;
404 }
405
406
407 static int tncs_imv_resolve_funcs(struct tnc_if_imv *imv)
408 {
409         void *handle = imv->dlhandle;
410
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;
417         }
418
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;
425         }
426
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;
433         }
434
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");
441
442         return 0;
443 }
444
445
446 static int tncs_imv_initialize(struct tnc_if_imv *imv)
447 {
448         TNC_Result res;
449         TNC_Version imv_ver;
450
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);
457
458         return res == TNC_RESULT_SUCCESS ? 0 : -1;
459 }
460
461
462 static int tncs_imv_terminate(struct tnc_if_imv *imv)
463 {
464         TNC_Result res;
465
466         if (imv->Terminate == NULL)
467                 return 0;
468
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);
474
475         return res == TNC_RESULT_SUCCESS ? 0 : -1;
476 }
477
478
479 static int tncs_imv_provide_bind_function(struct tnc_if_imv *imv)
480 {
481         TNC_Result res;
482
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);
488
489         return res == TNC_RESULT_SUCCESS ? 0 : -1;
490 }
491
492
493 static int tncs_imv_notify_connection_change(struct tnc_if_imv *imv,
494                                              TNC_ConnectionID conn,
495                                              TNC_ConnectionState state)
496 {
497         TNC_Result res;
498
499         if (imv->NotifyConnectionChange == NULL)
500                 return 0;
501
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);
507
508         return res == TNC_RESULT_SUCCESS ? 0 : -1;
509 }
510
511
512 static int tncs_load_imv(struct tnc_if_imv *imv)
513 {
514         if (imv->path == NULL) {
515                 wpa_printf(MSG_DEBUG, "TNC: No IMV configured");
516                 return -1;
517         }
518
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;
526         }
527
528         if (tncs_imv_resolve_funcs(imv) < 0) {
529                 wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMV functions");
530                 return -1;
531         }
532
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;
537         }
538
539         return 0;
540 }
541
542
543 static void tncs_free_imv(struct tnc_if_imv *imv)
544 {
545         os_free(imv->name);
546         os_free(imv->path);
547         os_free(imv->supported_types);
548 }
549
550 static void tncs_unload_imv(struct tnc_if_imv *imv)
551 {
552         tncs_imv_terminate(imv);
553
554         if (imv->dlhandle)
555                 dlclose(imv->dlhandle);
556
557         tncs_free_imv(imv);
558 }
559
560
561 static int tncs_supported_type(struct tnc_if_imv *imv, unsigned int type)
562 {
563         size_t i;
564         unsigned int vendor, subtype;
565
566         if (imv == NULL || imv->supported_types == NULL)
567                 return 0;
568
569         vendor = type >> 8;
570         subtype = type & 0xff;
571
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;
579         }
580
581         return 0;
582 }
583
584
585 static void tncs_send_to_imvs(struct tncs_data *tncs, unsigned int type,
586                               const u8 *msg, size_t len)
587 {
588         struct tnc_if_imv *imv;
589         TNC_Result res;
590
591         wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMV(s)", msg, len);
592
593         for (imv = tncs->imv; imv; imv = imv->next) {
594                 if (imv->ReceiveMessage == NULL ||
595                     !tncs_supported_type(imv, type))
596                         continue;
597
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);
605         }
606 }
607
608
609 static void tncs_batch_ending(struct tncs_data *tncs)
610 {
611         struct tnc_if_imv *imv;
612         TNC_Result res;
613
614         for (imv = tncs->imv; imv; imv = imv->next) {
615                 if (imv->BatchEnding == NULL)
616                         continue;
617
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);
623         }
624 }
625
626
627 static void tncs_solicit_recommendation(struct tncs_data *tncs)
628 {
629         struct tnc_if_imv *imv;
630         TNC_Result res;
631
632         for (imv = tncs->imv; imv; imv = imv->next) {
633                 if (tncs->imv_data[imv->imvID].recommendation_set)
634                         continue;
635
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);
642         }
643 }
644
645
646 void tncs_init_connection(struct tncs_data *tncs)
647 {
648         struct tnc_if_imv *imv;
649         int i;
650
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);
657         }
658
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;
663         }
664 }
665
666
667 size_t tncs_total_send_len(struct tncs_data *tncs)
668 {
669         int i;
670         size_t len = 0;
671
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;
677 }
678
679
680 u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos)
681 {
682         int i;
683
684         for (i = 0; i < TNC_MAX_IMV_ID; i++) {
685                 if (tncs->imv_data[i].imv_send == NULL)
686                         continue;
687
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;
694         }
695
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;
702         }
703
704         return pos;
705 }
706
707
708 char * tncs_if_tnccs_start(struct tncs_data *tncs)
709 {
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;
716 }
717
718
719 char * tncs_if_tnccs_end(void)
720 {
721         char *buf = os_malloc(100);
722         if (buf == NULL)
723                 return NULL;
724         os_snprintf(buf, 100, IF_TNCCS_END);
725         return buf;
726 }
727
728
729 static int tncs_get_type(char *start, unsigned int *type)
730 {
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;
737 }
738
739
740 static unsigned char * tncs_get_base64(char *start, size_t *decoded_len)
741 {
742         char *pos, *pos2;
743         unsigned char *decoded;
744
745         pos = os_strstr(start, "<Base64>");
746         if (pos == NULL)
747                 return NULL;
748
749         pos += 8;
750         pos2 = os_strstr(pos, "</Base64>");
751         if (pos2 == NULL)
752                 return NULL;
753         *pos2 = '\0';
754
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");
760         }
761
762         return decoded;
763 }
764
765
766 static enum tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs)
767 {
768         enum IMV_Action_Recommendation rec;
769         struct tnc_if_imv *imv;
770         TNC_ConnectionState state;
771         char *txt;
772
773         wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs");
774
775         if (tncs->done)
776                 return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
777
778         tncs_solicit_recommendation(tncs);
779
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;
793         }
794
795         wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec);
796         tncs->recommendation = rec;
797         tncs->done = 1;
798
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;
817         }
818
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);
829                 }
830         }
831
832         for (imv = tncs->imv; imv; imv = imv->next) {
833                 tncs_imv_notify_connection_change(imv, tncs->connectionID,
834                                                   state);
835         }
836
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;
848         }
849 }
850
851
852 enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
853                                             const u8 *msg, size_t len)
854 {
855         char *buf, *start, *end, *pos, *pos2, *payload;
856         unsigned int batch_id;
857         unsigned char *decoded;
858         size_t decoded_len;
859
860         buf = os_malloc(len + 1);
861         if (buf == NULL)
862                 return TNCCS_PROCESS_ERROR;
863
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;
871         }
872
873         start += 13;
874         while (*start == ' ')
875                 start++;
876         *end = '\0';
877
878         pos = os_strstr(start, "BatchId=");
879         if (pos == NULL) {
880                 os_free(buf);
881                 return TNCCS_PROCESS_ERROR;
882         }
883
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;
896         }
897         tncs->last_batchid = batch_id;
898
899         while (*pos != '\0' && *pos != '>')
900                 pos++;
901         if (*pos == '\0') {
902                 os_free(buf);
903                 return TNCCS_PROCESS_ERROR;
904         }
905         pos++;
906         payload = start;
907
908         /*
909          * <IMC-IMV-Message>
910          * <Type>01234567</Type>
911          * <Base64>foo==</Base64>
912          * </IMC-IMV-Message>
913          */
914
915         while (*start) {
916                 char *endpos;
917                 unsigned int type;
918
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;
929
930                 if (tncs_get_type(start, &type) < 0) {
931                         *endpos = '<';
932                         start = end;
933                         continue;
934                 }
935                 wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
936
937                 decoded = tncs_get_base64(start, &decoded_len);
938                 if (decoded == NULL) {
939                         *endpos = '<';
940                         start = end;
941                         continue;
942                 }
943
944                 tncs_send_to_imvs(tncs, type, decoded, decoded_len);
945
946                 os_free(decoded);
947
948                 start = end;
949         }
950
951         /*
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>
957          */
958
959         start = payload;
960         while (*start) {
961                 unsigned int type;
962                 char *xml, *xmlend, *endpos;
963
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;
974
975                 if (tncs_get_type(start, &type) < 0) {
976                         *endpos = '<';
977                         start = end;
978                         continue;
979                 }
980                 wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
981                            type);
982
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;
995                         }
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;
1004                         }
1005                 }
1006
1007                 if (decoded) {
1008                         wpa_hexdump_ascii(MSG_MSGDUMP,
1009                                           "TNC: TNCC-TNCS-Message Base64",
1010                                           decoded, decoded_len);
1011                         os_free(decoded);
1012                 }
1013
1014                 if (xml) {
1015                         wpa_hexdump_ascii(MSG_MSGDUMP,
1016                                           "TNC: TNCC-TNCS-Message XML",
1017                                           (unsigned char *) xml,
1018                                           xmlend - xml);
1019                 }
1020
1021                 start = end;
1022         }
1023
1024         os_free(buf);
1025
1026         tncs_batch_ending(tncs);
1027
1028         if (tncs_total_send_len(tncs) == 0)
1029                 return tncs_derive_recommendation(tncs);
1030
1031         return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
1032 }
1033
1034
1035 static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end,
1036                                           int *error)
1037 {
1038         struct tnc_if_imv *imv;
1039         char *pos, *pos2;
1040
1041         if (id >= TNC_MAX_IMV_ID) {
1042                 wpa_printf(MSG_DEBUG, "TNC: Too many IMVs");
1043                 return NULL;
1044         }
1045
1046         imv = os_zalloc(sizeof(*imv));
1047         if (imv == NULL) {
1048                 *error = 1;
1049                 return NULL;
1050         }
1051
1052         imv->imvID = id;
1053
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;
1061         }
1062
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;
1072         }
1073         *pos2 = '\0';
1074         wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
1075         imv->name = os_strdup(pos);
1076
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;
1083         }
1084
1085         pos++;
1086         wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos);
1087         imv->path = os_strdup(pos);
1088
1089         return imv;
1090 }
1091
1092
1093 static int tncs_read_config(struct tncs_global *global)
1094 {
1095         char *config, *end, *pos, *line_end;
1096         size_t config_len;
1097         struct tnc_if_imv *imv, *last;
1098         int id = 0;
1099
1100         last = NULL;
1101
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;
1107         }
1108
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';
1116
1117                 if (os_strncmp(pos, "IMV ", 4) == 0) {
1118                         int error = 0;
1119
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;
1129                         }
1130                 }
1131         }
1132
1133         os_free(config);
1134
1135         return 0;
1136 }
1137
1138
1139 struct tncs_data * tncs_init(void)
1140 {
1141         struct tncs_data *tncs;
1142
1143         if (tncs_global_data == NULL)
1144                 return NULL;
1145
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;
1153
1154         return tncs;
1155 }
1156
1157
1158 void tncs_deinit(struct tncs_data *tncs)
1159 {
1160         int i;
1161         struct tncs_data *prev, *conn;
1162
1163         if (tncs == NULL)
1164                 return;
1165
1166         for (i = 0; i < TNC_MAX_IMV_ID; i++)
1167                 os_free(tncs->imv_data[i].imv_send);
1168
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;
1178                 }
1179                 prev = conn;
1180                 conn = conn->next;
1181         }
1182
1183         os_free(tncs->tncs_message);
1184         os_free(tncs);
1185 }
1186
1187
1188 int tncs_global_init(void)
1189 {
1190         struct tnc_if_imv *imv;
1191
1192         tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
1193         if (tncs_global_data == NULL)
1194                 return -1;
1195
1196         if (tncs_read_config(tncs_global_data) < 0) {
1197                 wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
1198                 goto failed;
1199         }
1200
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;
1206                 }
1207         }
1208
1209         return 0;
1210
1211 failed:
1212         tncs_global_deinit();
1213         return -1;
1214 }
1215
1216
1217 void tncs_global_deinit(void)
1218 {
1219         struct tnc_if_imv *imv, *prev;
1220
1221         if (tncs_global_data == NULL)
1222                 return;
1223
1224         imv = tncs_global_data->imv;
1225         while (imv) {
1226                 tncs_unload_imv(imv);
1227
1228                 prev = imv;
1229                 imv = imv->next;
1230                 os_free(prev);
1231         }
1232
1233         os_free(tncs_global_data);
1234 }
1235
1236
1237 struct wpabuf * tncs_build_soh_request(void)
1238 {
1239         struct wpabuf *buf;
1240
1241         /*
1242          * Build a SoH Request TLV (to be used inside SoH EAP Extensions
1243          * Method)
1244          */
1245
1246         buf = wpabuf_alloc(8 + 4);
1247         if (buf == NULL)
1248                 return NULL;
1249
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 */
1253
1254         wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
1255
1256         wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */
1257         wpabuf_put_be16(buf, 0); /* Length */
1258
1259         return buf;
1260 }
1261
1262
1263 struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
1264                                  int *failure)
1265 {
1266         wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len);
1267         *failure = 0;
1268
1269         /* TODO: return MS-SoH Response TLV */
1270
1271         return NULL;
1272 }