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