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