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