]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/wpa/src/eap_server/tncs.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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 "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 = os_malloc(len + 1);
855         if (buf == NULL)
856                 return TNCCS_PROCESS_ERROR;
857
858         os_memcpy(buf, msg, len);
859         buf[len] = '\0';
860         start = os_strstr(buf, "<TNCCS-Batch ");
861         end = os_strstr(buf, "</TNCCS-Batch>");
862         if (start == NULL || end == NULL || start > end) {
863                 os_free(buf);
864                 return TNCCS_PROCESS_ERROR;
865         }
866
867         start += 13;
868         while (*start == ' ')
869                 start++;
870         *end = '\0';
871
872         pos = os_strstr(start, "BatchId=");
873         if (pos == NULL) {
874                 os_free(buf);
875                 return TNCCS_PROCESS_ERROR;
876         }
877
878         pos += 8;
879         if (*pos == '"')
880                 pos++;
881         batch_id = atoi(pos);
882         wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
883                    batch_id);
884         if (batch_id != tncs->last_batchid + 1) {
885                 wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
886                            "%u (expected %u)",
887                            batch_id, tncs->last_batchid + 1);
888                 os_free(buf);
889                 return TNCCS_PROCESS_ERROR;
890         }
891         tncs->last_batchid = batch_id;
892
893         while (*pos != '\0' && *pos != '>')
894                 pos++;
895         if (*pos == '\0') {
896                 os_free(buf);
897                 return TNCCS_PROCESS_ERROR;
898         }
899         pos++;
900         payload = start;
901
902         /*
903          * <IMC-IMV-Message>
904          * <Type>01234567</Type>
905          * <Base64>foo==</Base64>
906          * </IMC-IMV-Message>
907          */
908
909         while (*start) {
910                 char *endpos;
911                 unsigned int type;
912
913                 pos = os_strstr(start, "<IMC-IMV-Message>");
914                 if (pos == NULL)
915                         break;
916                 start = pos + 17;
917                 end = os_strstr(start, "</IMC-IMV-Message>");
918                 if (end == NULL)
919                         break;
920                 *end = '\0';
921                 endpos = end;
922                 end += 18;
923
924                 if (tncs_get_type(start, &type) < 0) {
925                         *endpos = '<';
926                         start = end;
927                         continue;
928                 }
929                 wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
930
931                 decoded = tncs_get_base64(start, &decoded_len);
932                 if (decoded == NULL) {
933                         *endpos = '<';
934                         start = end;
935                         continue;
936                 }
937
938                 tncs_send_to_imvs(tncs, type, decoded, decoded_len);
939
940                 os_free(decoded);
941
942                 start = end;
943         }
944
945         /*
946          * <TNCC-TNCS-Message>
947          * <Type>01234567</Type>
948          * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
949          * <Base64>foo==</Base64>
950          * </TNCC-TNCS-Message>
951          */
952
953         start = payload;
954         while (*start) {
955                 unsigned int type;
956                 char *xml, *xmlend, *endpos;
957
958                 pos = os_strstr(start, "<TNCC-TNCS-Message>");
959                 if (pos == NULL)
960                         break;
961                 start = pos + 19;
962                 end = os_strstr(start, "</TNCC-TNCS-Message>");
963                 if (end == NULL)
964                         break;
965                 *end = '\0';
966                 endpos = end;
967                 end += 20;
968
969                 if (tncs_get_type(start, &type) < 0) {
970                         *endpos = '<';
971                         start = end;
972                         continue;
973                 }
974                 wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
975                            type);
976
977                 /* Base64 OR XML */
978                 decoded = NULL;
979                 xml = NULL;
980                 xmlend = NULL;
981                 pos = os_strstr(start, "<XML>");
982                 if (pos) {
983                         pos += 5;
984                         pos2 = os_strstr(pos, "</XML>");
985                         if (pos2 == NULL) {
986                                 *endpos = '<';
987                                 start = end;
988                                 continue;
989                         }
990                         xmlend = pos2;
991                         xml = pos;
992                 } else {
993                         decoded = tncs_get_base64(start, &decoded_len);
994                         if (decoded == NULL) {
995                                 *endpos = '<';
996                                 start = end;
997                                 continue;
998                         }
999                 }
1000
1001                 if (decoded) {
1002                         wpa_hexdump_ascii(MSG_MSGDUMP,
1003                                           "TNC: TNCC-TNCS-Message Base64",
1004                                           decoded, decoded_len);
1005                         os_free(decoded);
1006                 }
1007
1008                 if (xml) {
1009                         wpa_hexdump_ascii(MSG_MSGDUMP,
1010                                           "TNC: TNCC-TNCS-Message XML",
1011                                           (unsigned char *) xml,
1012                                           xmlend - xml);
1013                 }
1014
1015                 start = end;
1016         }
1017
1018         os_free(buf);
1019
1020         tncs_batch_ending(tncs);
1021
1022         if (tncs_total_send_len(tncs) == 0)
1023                 return tncs_derive_recommendation(tncs);
1024
1025         return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
1026 }
1027
1028
1029 static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end,
1030                                           int *error)
1031 {
1032         struct tnc_if_imv *imv;
1033         char *pos, *pos2;
1034
1035         if (id >= TNC_MAX_IMV_ID) {
1036                 wpa_printf(MSG_DEBUG, "TNC: Too many IMVs");
1037                 return NULL;
1038         }
1039
1040         imv = os_zalloc(sizeof(*imv));
1041         if (imv == NULL) {
1042                 *error = 1;
1043                 return NULL;
1044         }
1045
1046         imv->imvID = id;
1047
1048         pos = start;
1049         wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos);
1050         if (pos + 1 >= end || *pos != '"') {
1051                 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
1052                            "(no starting quotation mark)", start);
1053                 os_free(imv);
1054                 return NULL;
1055         }
1056
1057         pos++;
1058         pos2 = pos;
1059         while (pos2 < end && *pos2 != '"')
1060                 pos2++;
1061         if (pos2 >= end) {
1062                 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
1063                            "(no ending quotation mark)", start);
1064                 os_free(imv);
1065                 return NULL;
1066         }
1067         *pos2 = '\0';
1068         wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
1069         imv->name = os_strdup(pos);
1070
1071         pos = pos2 + 1;
1072         if (pos >= end || *pos != ' ') {
1073                 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
1074                            "(no space after name)", start);
1075                 os_free(imv);
1076                 return NULL;
1077         }
1078
1079         pos++;
1080         wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos);
1081         imv->path = os_strdup(pos);
1082
1083         return imv;
1084 }
1085
1086
1087 static int tncs_read_config(struct tncs_global *global)
1088 {
1089         char *config, *end, *pos, *line_end;
1090         size_t config_len;
1091         struct tnc_if_imv *imv, *last;
1092         int id = 0;
1093
1094         last = NULL;
1095
1096         config = os_readfile(TNC_CONFIG_FILE, &config_len);
1097         if (config == NULL) {
1098                 wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
1099                            "file '%s'", TNC_CONFIG_FILE);
1100                 return -1;
1101         }
1102
1103         end = config + config_len;
1104         for (pos = config; pos < end; pos = line_end + 1) {
1105                 line_end = pos;
1106                 while (*line_end != '\n' && *line_end != '\r' &&
1107                        line_end < end)
1108                         line_end++;
1109                 *line_end = '\0';
1110
1111                 if (os_strncmp(pos, "IMV ", 4) == 0) {
1112                         int error = 0;
1113
1114                         imv = tncs_parse_imv(id++, pos + 4, line_end, &error);
1115                         if (error)
1116                                 return -1;
1117                         if (imv) {
1118                                 if (last == NULL)
1119                                         global->imv = imv;
1120                                 else
1121                                         last->next = imv;
1122                                 last = imv;
1123                         }
1124                 }
1125         }
1126
1127         os_free(config);
1128
1129         return 0;
1130 }
1131
1132
1133 struct tncs_data * tncs_init(void)
1134 {
1135         struct tncs_data *tncs;
1136
1137         if (tncs_global_data == NULL)
1138                 return NULL;
1139
1140         tncs = os_zalloc(sizeof(*tncs));
1141         if (tncs == NULL)
1142                 return NULL;
1143         tncs->imv = tncs_global_data->imv;
1144         tncs->connectionID = tncs_global_data->next_conn_id++;
1145         tncs->next = tncs_global_data->connections;
1146         tncs_global_data->connections = tncs;
1147
1148         return tncs;
1149 }
1150
1151
1152 void tncs_deinit(struct tncs_data *tncs)
1153 {
1154         int i;
1155         struct tncs_data *prev, *conn;
1156
1157         if (tncs == NULL)
1158                 return;
1159
1160         for (i = 0; i < TNC_MAX_IMV_ID; i++)
1161                 os_free(tncs->imv_data[i].imv_send);
1162
1163         prev = NULL;
1164         conn = tncs_global_data->connections;
1165         while (conn) {
1166                 if (conn == tncs) {
1167                         if (prev)
1168                                 prev->next = tncs->next;
1169                         else
1170                                 tncs_global_data->connections = tncs->next;
1171                         break;
1172                 }
1173                 prev = conn;
1174                 conn = conn->next;
1175         }
1176
1177         os_free(tncs->tncs_message);
1178         os_free(tncs);
1179 }
1180
1181
1182 int tncs_global_init(void)
1183 {
1184         struct tnc_if_imv *imv;
1185
1186         tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
1187         if (tncs_global_data == NULL)
1188                 return -1;
1189
1190         if (tncs_read_config(tncs_global_data) < 0) {
1191                 wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
1192                 goto failed;
1193         }
1194
1195         for (imv = tncs_global_data->imv; imv; imv = imv->next) {
1196                 if (tncs_load_imv(imv)) {
1197                         wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'",
1198                                    imv->name);
1199                         goto failed;
1200                 }
1201         }
1202
1203         return 0;
1204
1205 failed:
1206         tncs_global_deinit();
1207         return -1;
1208 }
1209
1210
1211 void tncs_global_deinit(void)
1212 {
1213         struct tnc_if_imv *imv, *prev;
1214
1215         if (tncs_global_data == NULL)
1216                 return;
1217
1218         imv = tncs_global_data->imv;
1219         while (imv) {
1220                 tncs_unload_imv(imv);
1221
1222                 prev = imv;
1223                 imv = imv->next;
1224                 os_free(prev);
1225         }
1226
1227         os_free(tncs_global_data);
1228         tncs_global_data = NULL;
1229 }
1230
1231
1232 struct wpabuf * tncs_build_soh_request(void)
1233 {
1234         struct wpabuf *buf;
1235
1236         /*
1237          * Build a SoH Request TLV (to be used inside SoH EAP Extensions
1238          * Method)
1239          */
1240
1241         buf = wpabuf_alloc(8 + 4);
1242         if (buf == NULL)
1243                 return NULL;
1244
1245         /* Vendor-Specific TLV (Microsoft) - SoH Request */
1246         wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
1247         wpabuf_put_be16(buf, 8); /* Length */
1248
1249         wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
1250
1251         wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */
1252         wpabuf_put_be16(buf, 0); /* Length */
1253
1254         return buf;
1255 }
1256
1257
1258 struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
1259                                  int *failure)
1260 {
1261         wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len);
1262         *failure = 0;
1263
1264         /* TODO: return MS-SoH Response TLV */
1265
1266         return NULL;
1267 }