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  	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(pos, os_strlen(pos), decoded_len);
682  	*pos2 = '<';
683  	if (decoded == NULL) {
684  		wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
685  	}
686  
687  	return decoded;
688  }
689  
690  
tncs_derive_recommendation(struct tncs_data * tncs)691  static enum tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs)
692  {
693  	enum IMV_Action_Recommendation rec;
694  	struct tnc_if_imv *imv;
695  	TNC_ConnectionState state;
696  	char *txt;
697  
698  	wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs");
699  
700  	if (tncs->done)
701  		return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
702  
703  	tncs_solicit_recommendation(tncs);
704  
705  	/* Select the most restrictive recommendation */
706  	rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION;
707  	for (imv = tncs->imv; imv; imv = imv->next) {
708  		TNC_IMV_Action_Recommendation irec;
709  		irec = tncs->imv_data[imv->imvID].recommendation;
710  		if (irec == TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
711  			rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS;
712  		if (irec == TNC_IMV_ACTION_RECOMMENDATION_ISOLATE &&
713  		    rec != TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
714  			rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE;
715  		if (irec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW &&
716  		    rec == TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION)
717  			rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW;
718  	}
719  
720  	wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec);
721  	tncs->recommendation = rec;
722  	tncs->done = 1;
723  
724  	txt = NULL;
725  	switch (rec) {
726  	case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
727  	case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
728  		txt = "allow";
729  		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
730  		break;
731  	case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
732  		txt = "isolate";
733  		state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
734  		break;
735  	case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
736  		txt = "none";
737  		state = TNC_CONNECTION_STATE_ACCESS_NONE;
738  		break;
739  	default:
740  		state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
741  		break;
742  	}
743  
744  	if (txt) {
745  		os_free(tncs->tncs_message);
746  		tncs->tncs_message = os_zalloc(200);
747  		if (tncs->tncs_message) {
748  			os_snprintf(tncs->tncs_message, 199,
749  				    "<TNCC-TNCS-Message><Type>%08X</Type>"
750  				    "<XML><TNCCS-Recommendation type=\"%s\">"
751  				    "</TNCCS-Recommendation></XML>"
752  				    "</TNCC-TNCS-Message>",
753  				    TNC_TNCCS_RECOMMENDATION, txt);
754  		}
755  	}
756  
757  	for (imv = tncs->imv; imv; imv = imv->next) {
758  		tncs_imv_notify_connection_change(imv, tncs->connectionID,
759  						  state);
760  	}
761  
762  	switch (rec) {
763  	case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
764  		return TNCCS_RECOMMENDATION_ALLOW;
765  	case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
766  		return TNCCS_RECOMMENDATION_NO_ACCESS;
767  	case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
768  		return TNCCS_RECOMMENDATION_ISOLATE;
769  	case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
770  		return TNCCS_RECOMMENDATION_NO_RECOMMENDATION;
771  	default:
772  		return TNCCS_PROCESS_ERROR;
773  	}
774  }
775  
776  
tncs_process_if_tnccs(struct tncs_data * tncs,const u8 * msg,size_t len)777  enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
778  					    const u8 *msg, size_t len)
779  {
780  	char *buf, *start, *end, *pos, *pos2, *payload;
781  	unsigned int batch_id;
782  	unsigned char *decoded;
783  	size_t decoded_len;
784  
785  	buf = dup_binstr(msg, len);
786  	if (buf == NULL)
787  		return TNCCS_PROCESS_ERROR;
788  
789  	start = os_strstr(buf, "<TNCCS-Batch ");
790  	end = os_strstr(buf, "</TNCCS-Batch>");
791  	if (start == NULL || end == NULL || start > end) {
792  		os_free(buf);
793  		return TNCCS_PROCESS_ERROR;
794  	}
795  
796  	start += 13;
797  	while (*start == ' ')
798  		start++;
799  	*end = '\0';
800  
801  	pos = os_strstr(start, "BatchId=");
802  	if (pos == NULL) {
803  		os_free(buf);
804  		return TNCCS_PROCESS_ERROR;
805  	}
806  
807  	pos += 8;
808  	if (*pos == '"')
809  		pos++;
810  	batch_id = atoi(pos);
811  	wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
812  		   batch_id);
813  	if (batch_id != tncs->last_batchid + 1) {
814  		wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
815  			   "%u (expected %u)",
816  			   batch_id, tncs->last_batchid + 1);
817  		os_free(buf);
818  		return TNCCS_PROCESS_ERROR;
819  	}
820  	tncs->last_batchid = batch_id;
821  
822  	while (*pos != '\0' && *pos != '>')
823  		pos++;
824  	if (*pos == '\0') {
825  		os_free(buf);
826  		return TNCCS_PROCESS_ERROR;
827  	}
828  	pos++;
829  	payload = start;
830  
831  	/*
832  	 * <IMC-IMV-Message>
833  	 * <Type>01234567</Type>
834  	 * <Base64>foo==</Base64>
835  	 * </IMC-IMV-Message>
836  	 */
837  
838  	while (*start) {
839  		char *endpos;
840  		unsigned int type;
841  
842  		pos = os_strstr(start, "<IMC-IMV-Message>");
843  		if (pos == NULL)
844  			break;
845  		start = pos + 17;
846  		end = os_strstr(start, "</IMC-IMV-Message>");
847  		if (end == NULL)
848  			break;
849  		*end = '\0';
850  		endpos = end;
851  		end += 18;
852  
853  		if (tncs_get_type(start, &type) < 0) {
854  			*endpos = '<';
855  			start = end;
856  			continue;
857  		}
858  		wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
859  
860  		decoded = tncs_get_base64(start, &decoded_len);
861  		if (decoded == NULL) {
862  			*endpos = '<';
863  			start = end;
864  			continue;
865  		}
866  
867  		tncs_send_to_imvs(tncs, type, decoded, decoded_len);
868  
869  		os_free(decoded);
870  
871  		start = end;
872  	}
873  
874  	/*
875  	 * <TNCC-TNCS-Message>
876  	 * <Type>01234567</Type>
877  	 * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
878  	 * <Base64>foo==</Base64>
879  	 * </TNCC-TNCS-Message>
880  	 */
881  
882  	start = payload;
883  	while (*start) {
884  		unsigned int type;
885  		char *xml, *xmlend, *endpos;
886  
887  		pos = os_strstr(start, "<TNCC-TNCS-Message>");
888  		if (pos == NULL)
889  			break;
890  		start = pos + 19;
891  		end = os_strstr(start, "</TNCC-TNCS-Message>");
892  		if (end == NULL)
893  			break;
894  		*end = '\0';
895  		endpos = end;
896  		end += 20;
897  
898  		if (tncs_get_type(start, &type) < 0) {
899  			*endpos = '<';
900  			start = end;
901  			continue;
902  		}
903  		wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
904  			   type);
905  
906  		/* Base64 OR XML */
907  		decoded = NULL;
908  		xml = NULL;
909  		xmlend = NULL;
910  		pos = os_strstr(start, "<XML>");
911  		if (pos) {
912  			pos += 5;
913  			pos2 = os_strstr(pos, "</XML>");
914  			if (pos2 == NULL) {
915  				*endpos = '<';
916  				start = end;
917  				continue;
918  			}
919  			xmlend = pos2;
920  			xml = pos;
921  		} else {
922  			decoded = tncs_get_base64(start, &decoded_len);
923  			if (decoded == NULL) {
924  				*endpos = '<';
925  				start = end;
926  				continue;
927  			}
928  		}
929  
930  		if (decoded) {
931  			wpa_hexdump_ascii(MSG_MSGDUMP,
932  					  "TNC: TNCC-TNCS-Message Base64",
933  					  decoded, decoded_len);
934  			os_free(decoded);
935  		}
936  
937  		if (xml) {
938  			wpa_hexdump_ascii(MSG_MSGDUMP,
939  					  "TNC: TNCC-TNCS-Message XML",
940  					  (unsigned char *) xml,
941  					  xmlend - xml);
942  		}
943  
944  		start = end;
945  	}
946  
947  	os_free(buf);
948  
949  	tncs_batch_ending(tncs);
950  
951  	if (tncs_total_send_len(tncs) == 0)
952  		return tncs_derive_recommendation(tncs);
953  
954  	return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
955  }
956  
957  
tncs_parse_imv(int id,char * start,char * end,int * error)958  static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end,
959  					  int *error)
960  {
961  	struct tnc_if_imv *imv;
962  	char *pos, *pos2;
963  
964  	if (id >= TNC_MAX_IMV_ID) {
965  		wpa_printf(MSG_DEBUG, "TNC: Too many IMVs");
966  		return NULL;
967  	}
968  
969  	imv = os_zalloc(sizeof(*imv));
970  	if (imv == NULL) {
971  		*error = 1;
972  		return NULL;
973  	}
974  
975  	imv->imvID = id;
976  
977  	pos = start;
978  	wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos);
979  	if (pos + 1 >= end || *pos != '"') {
980  		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
981  			   "(no starting quotation mark)", start);
982  		os_free(imv);
983  		return NULL;
984  	}
985  
986  	pos++;
987  	pos2 = pos;
988  	while (pos2 < end && *pos2 != '"')
989  		pos2++;
990  	if (pos2 >= end) {
991  		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
992  			   "(no ending quotation mark)", start);
993  		os_free(imv);
994  		return NULL;
995  	}
996  	*pos2 = '\0';
997  	wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
998  	imv->name = os_strdup(pos);
999  
1000  	pos = pos2 + 1;
1001  	if (pos >= end || *pos != ' ') {
1002  		wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
1003  			   "(no space after name)", start);
1004  		os_free(imv);
1005  		return NULL;
1006  	}
1007  
1008  	pos++;
1009  	wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos);
1010  	imv->path = os_strdup(pos);
1011  
1012  	return imv;
1013  }
1014  
1015  
tncs_read_config(struct tncs_global * global)1016  static int tncs_read_config(struct tncs_global *global)
1017  {
1018  	char *config, *end, *pos, *line_end;
1019  	size_t config_len;
1020  	struct tnc_if_imv *imv, *last;
1021  	int id = 0;
1022  
1023  	last = NULL;
1024  
1025  	config = os_readfile(TNC_CONFIG_FILE, &config_len);
1026  	if (config == NULL) {
1027  		wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
1028  			   "file '%s'", TNC_CONFIG_FILE);
1029  		return -1;
1030  	}
1031  
1032  	end = config + config_len;
1033  	for (pos = config; pos < end; pos = line_end + 1) {
1034  		line_end = pos;
1035  		while (*line_end != '\n' && *line_end != '\r' &&
1036  		       line_end < end)
1037  			line_end++;
1038  		*line_end = '\0';
1039  
1040  		if (os_strncmp(pos, "IMV ", 4) == 0) {
1041  			int error = 0;
1042  
1043  			imv = tncs_parse_imv(id++, pos + 4, line_end, &error);
1044  			if (error)
1045  				return -1;
1046  			if (imv) {
1047  				if (last == NULL)
1048  					global->imv = imv;
1049  				else
1050  					last->next = imv;
1051  				last = imv;
1052  			}
1053  		}
1054  	}
1055  
1056  	os_free(config);
1057  
1058  	return 0;
1059  }
1060  
1061  
tncs_init(void)1062  struct tncs_data * tncs_init(void)
1063  {
1064  	struct tncs_data *tncs;
1065  
1066  	if (tncs_global_data == NULL)
1067  		return NULL;
1068  
1069  	tncs = os_zalloc(sizeof(*tncs));
1070  	if (tncs == NULL)
1071  		return NULL;
1072  	tncs->imv = tncs_global_data->imv;
1073  	tncs->connectionID = tncs_global_data->next_conn_id++;
1074  	tncs->next = tncs_global_data->connections;
1075  	tncs_global_data->connections = tncs;
1076  
1077  	return tncs;
1078  }
1079  
1080  
tncs_deinit(struct tncs_data * tncs)1081  void tncs_deinit(struct tncs_data *tncs)
1082  {
1083  	int i;
1084  	struct tncs_data *prev, *conn;
1085  
1086  	if (tncs == NULL)
1087  		return;
1088  
1089  	for (i = 0; i < TNC_MAX_IMV_ID; i++)
1090  		os_free(tncs->imv_data[i].imv_send);
1091  
1092  	prev = NULL;
1093  	conn = tncs_global_data->connections;
1094  	while (conn) {
1095  		if (conn == tncs) {
1096  			if (prev)
1097  				prev->next = tncs->next;
1098  			else
1099  				tncs_global_data->connections = tncs->next;
1100  			break;
1101  		}
1102  		prev = conn;
1103  		conn = conn->next;
1104  	}
1105  
1106  	os_free(tncs->tncs_message);
1107  	os_free(tncs);
1108  }
1109  
1110  
tncs_global_init(void)1111  int tncs_global_init(void)
1112  {
1113  	struct tnc_if_imv *imv;
1114  
1115  	if (tncs_global_data)
1116  		return 0;
1117  
1118  	tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
1119  	if (tncs_global_data == NULL)
1120  		return -1;
1121  
1122  	if (tncs_read_config(tncs_global_data) < 0) {
1123  		wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
1124  		goto failed;
1125  	}
1126  
1127  	for (imv = tncs_global_data->imv; imv; imv = imv->next) {
1128  		if (tncs_load_imv(imv)) {
1129  			wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'",
1130  				   imv->name);
1131  			goto failed;
1132  		}
1133  	}
1134  
1135  	return 0;
1136  
1137  failed:
1138  	tncs_global_deinit();
1139  	return -1;
1140  }
1141  
1142  
tncs_global_deinit(void)1143  void tncs_global_deinit(void)
1144  {
1145  	struct tnc_if_imv *imv, *prev;
1146  
1147  	if (tncs_global_data == NULL)
1148  		return;
1149  
1150  	imv = tncs_global_data->imv;
1151  	while (imv) {
1152  		tncs_unload_imv(imv);
1153  
1154  		prev = imv;
1155  		imv = imv->next;
1156  		os_free(prev);
1157  	}
1158  
1159  	os_free(tncs_global_data);
1160  	tncs_global_data = NULL;
1161  }
1162  
1163  
tncs_build_soh_request(void)1164  struct wpabuf * tncs_build_soh_request(void)
1165  {
1166  	struct wpabuf *buf;
1167  
1168  	/*
1169  	 * Build a SoH Request TLV (to be used inside SoH EAP Extensions
1170  	 * Method)
1171  	 */
1172  
1173  	buf = wpabuf_alloc(8 + 4);
1174  	if (buf == NULL)
1175  		return NULL;
1176  
1177  	/* Vendor-Specific TLV (Microsoft) - SoH Request */
1178  	wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
1179  	wpabuf_put_be16(buf, 8); /* Length */
1180  
1181  	wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
1182  
1183  	wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */
1184  	wpabuf_put_be16(buf, 0); /* Length */
1185  
1186  	return buf;
1187  }
1188  
1189  
tncs_process_soh(const u8 * soh_tlv,size_t soh_tlv_len,int * failure)1190  struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
1191  				 int *failure)
1192  {
1193  	wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len);
1194  	*failure = 0;
1195  
1196  	/* TODO: return MS-SoH Response TLV */
1197  
1198  	return NULL;
1199  }
1200