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