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