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