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