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