Re-initialize hostapd/wpa_supplicant git repository based on 0.6.3 release
[wpasupplicant] / src / eap_peer / tncc.c
1 /*
2  * EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
3  * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14
15 #include "includes.h"
16 #ifndef CONFIG_NATIVE_WINDOWS
17 #include <dlfcn.h>
18 #endif /* CONFIG_NATIVE_WINDOWS */
19
20 #include "common.h"
21 #include "base64.h"
22 #include "tncc.h"
23
24
25 #ifdef UNICODE
26 #define TSTR "%S"
27 #else /* UNICODE */
28 #define TSTR "%s"
29 #endif /* UNICODE */
30
31
32 #define TNC_CONFIG_FILE "/etc/tnc_config"
33 #define TNC_WINREG_PATH TEXT("SOFTWARE\\Trusted Computing Group\\TNC\\IMCs")
34 #define IF_TNCCS_START \
35 "<?xml version=\"1.0\"?>\n" \
36 "<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
37 "xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
38 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
39 "xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
40 "IF_TNCCS#https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
41 #define IF_TNCCS_END "\n</TNCCS-Batch>"
42
43 /* TNC IF-IMC */
44
45 typedef unsigned long TNC_UInt32;
46 typedef unsigned char *TNC_BufferReference;
47
48 typedef TNC_UInt32 TNC_IMCID;
49 typedef TNC_UInt32 TNC_ConnectionID;
50 typedef TNC_UInt32 TNC_ConnectionState;
51 typedef TNC_UInt32 TNC_RetryReason;
52 typedef TNC_UInt32 TNC_MessageType;
53 typedef TNC_MessageType *TNC_MessageTypeList;
54 typedef TNC_UInt32 TNC_VendorID;
55 typedef TNC_UInt32 TNC_MessageSubtype;
56 typedef TNC_UInt32 TNC_Version;
57 typedef TNC_UInt32 TNC_Result;
58
59 typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)(
60         TNC_IMCID imcID,
61         char *functionName,
62         void **pOutfunctionPointer);
63
64 #define TNC_RESULT_SUCCESS 0
65 #define TNC_RESULT_NOT_INITIALIZED 1
66 #define TNC_RESULT_ALREADY_INITIALIZED 2
67 #define TNC_RESULT_NO_COMMON_VERSION 3
68 #define TNC_RESULT_CANT_RETRY 4
69 #define TNC_RESULT_WONT_RETRY 5
70 #define TNC_RESULT_INVALID_PARAMETER 6
71 #define TNC_RESULT_CANT_RESPOND 7
72 #define TNC_RESULT_ILLEGAL_OPERATION 8
73 #define TNC_RESULT_OTHER 9
74 #define TNC_RESULT_FATAL 10
75
76 #define TNC_CONNECTION_STATE_CREATE 0
77 #define TNC_CONNECTION_STATE_HANDSHAKE 1
78 #define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
79 #define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
80 #define TNC_CONNECTION_STATE_ACCESS_NONE 4
81 #define TNC_CONNECTION_STATE_DELETE 5
82
83 #define TNC_IFIMC_VERSION_1 1
84
85 #define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
86 #define TNC_SUBTYPE_ANY ((TNC_MessageSubtype) 0xff)
87
88 /* TNCC-TNCS Message Types */
89 #define TNC_TNCCS_RECOMMENDATION                0x00000001
90 #define TNC_TNCCS_ERROR                         0x00000002
91 #define TNC_TNCCS_PREFERREDLANGUAGE             0x00000003
92 #define TNC_TNCCS_REASONSTRINGS                 0x00000004
93
94
95 struct tnc_if_imc {
96         struct tnc_if_imc *next;
97         char *name;
98         char *path;
99         void *dlhandle; /* from dlopen() */
100         TNC_IMCID imcID;
101         TNC_ConnectionID connectionID;
102         TNC_MessageTypeList supported_types;
103         size_t num_supported_types;
104         u8 *imc_send;
105         size_t imc_send_len;
106
107         /* Functions implemented by IMCs (with TNC_IMC_ prefix) */
108         TNC_Result (*Initialize)(
109                 TNC_IMCID imcID,
110                 TNC_Version minVersion,
111                 TNC_Version maxVersion,
112                 TNC_Version *pOutActualVersion);
113         TNC_Result (*NotifyConnectionChange)(
114                 TNC_IMCID imcID,
115                 TNC_ConnectionID connectionID,
116                 TNC_ConnectionState newState);
117         TNC_Result (*BeginHandshake)(
118                 TNC_IMCID imcID,
119                 TNC_ConnectionID connectionID);
120         TNC_Result (*ReceiveMessage)(
121                 TNC_IMCID imcID,
122                 TNC_ConnectionID connectionID,
123                 TNC_BufferReference messageBuffer,
124                 TNC_UInt32 messageLength,
125                 TNC_MessageType messageType);
126         TNC_Result (*BatchEnding)(
127                 TNC_IMCID imcID,
128                 TNC_ConnectionID connectionID);
129         TNC_Result (*Terminate)(TNC_IMCID imcID);
130         TNC_Result (*ProvideBindFunction)(
131                 TNC_IMCID imcID,
132                 TNC_TNCC_BindFunctionPointer bindFunction);
133 };
134
135 struct tncc_data {
136         struct tnc_if_imc *imc;
137         unsigned int last_batchid;
138 };
139
140 #define TNC_MAX_IMC_ID 10
141 static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL };
142
143
144 /* TNCC functions that IMCs can call */
145
146 TNC_Result TNC_TNCC_ReportMessageTypes(
147         TNC_IMCID imcID,
148         TNC_MessageTypeList supportedTypes,
149         TNC_UInt32 typeCount)
150 {
151         TNC_UInt32 i;
152         struct tnc_if_imc *imc;
153
154         wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_ReportMessageTypes(imcID=%lu "
155                    "typeCount=%lu)",
156                    (unsigned long) imcID, (unsigned long) typeCount);
157
158         for (i = 0; i < typeCount; i++) {
159                 wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
160                            i, supportedTypes[i]);
161         }
162
163         if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
164                 return TNC_RESULT_INVALID_PARAMETER;
165
166         imc = tnc_imc[imcID];
167         os_free(imc->supported_types);
168         imc->supported_types =
169                 os_malloc(typeCount * sizeof(TNC_MessageTypeList));
170         if (imc->supported_types == NULL)
171                 return TNC_RESULT_FATAL;
172         os_memcpy(imc->supported_types, supportedTypes,
173                   typeCount * sizeof(TNC_MessageTypeList));
174         imc->num_supported_types = typeCount;
175
176         return TNC_RESULT_SUCCESS;
177 }
178
179
180 TNC_Result TNC_TNCC_SendMessage(
181         TNC_IMCID imcID,
182         TNC_ConnectionID connectionID,
183         TNC_BufferReference message,
184         TNC_UInt32 messageLength,
185         TNC_MessageType messageType)
186 {
187         struct tnc_if_imc *imc;
188         unsigned char *b64;
189         size_t b64len;
190
191         wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage(imcID=%lu "
192                    "connectionID=%lu messageType=%lu)",
193                    imcID, connectionID, messageType);
194         wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage",
195                           message, messageLength);
196
197         if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == 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         imc = tnc_imc[imcID];
205         os_free(imc->imc_send);
206         imc->imc_send_len = 0;
207         imc->imc_send = os_zalloc(b64len + 100);
208         if (imc->imc_send == NULL) {
209                 os_free(b64);
210                 return TNC_RESULT_OTHER;
211         }
212
213         imc->imc_send_len =
214                 os_snprintf((char *) imc->imc_send, 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
225 TNC_Result TNC_TNCC_RequestHandshakeRetry(
226         TNC_IMCID imcID,
227         TNC_ConnectionID connectionID,
228         TNC_RetryReason reason)
229 {
230         wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_RequestHandshakeRetry");
231
232         if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
233                 return TNC_RESULT_INVALID_PARAMETER;
234
235         /*
236          * TODO: trigger a call to eapol_sm_request_reauth(). This would
237          * require that the IMC continues to be loaded in memory afer
238          * authentication..
239          */
240
241         return TNC_RESULT_SUCCESS;
242 }
243
244
245 TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity,
246                                const char *message)
247 {
248         wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu "
249                    "severity==%lu message='%s')",
250                    imcID, severity, message);
251         return TNC_RESULT_SUCCESS;
252 }
253
254
255 TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID,
256                                 const char *message)
257 {
258         wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu "
259                    "connectionID==%lu message='%s')",
260                    imcID, connectionID, message);
261         return TNC_RESULT_SUCCESS;
262 }
263
264
265 TNC_Result TNC_TNCC_BindFunction(
266         TNC_IMCID imcID,
267         char *functionName,
268         void **pOutfunctionPointer)
269 {
270         wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_BindFunction(imcID=%lu, "
271                    "functionName='%s')", (unsigned long) imcID, functionName);
272
273         if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
274                 return TNC_RESULT_INVALID_PARAMETER;
275
276         if (pOutfunctionPointer == NULL)
277                 return TNC_RESULT_INVALID_PARAMETER;
278
279         if (os_strcmp(functionName, "TNC_TNCC_ReportMessageTypes") == 0)
280                 *pOutfunctionPointer = TNC_TNCC_ReportMessageTypes;
281         else if (os_strcmp(functionName, "TNC_TNCC_SendMessage") == 0)
282                 *pOutfunctionPointer = TNC_TNCC_SendMessage;
283         else if (os_strcmp(functionName, "TNC_TNCC_RequestHandshakeRetry") ==
284                  0)
285                 *pOutfunctionPointer = TNC_TNCC_RequestHandshakeRetry;
286         else if (os_strcmp(functionName, "TNC_9048_LogMessage") == 0)
287                 *pOutfunctionPointer = TNC_9048_LogMessage;
288         else if (os_strcmp(functionName, "TNC_9048_UserMessage") == 0)
289                 *pOutfunctionPointer = TNC_9048_UserMessage;
290         else
291                 *pOutfunctionPointer = NULL;
292
293         return TNC_RESULT_SUCCESS;
294 }
295
296
297 static void * tncc_get_sym(void *handle, char *func)
298 {
299         void *fptr;
300
301 #ifdef CONFIG_NATIVE_WINDOWS
302 #ifdef _WIN32_WCE
303         fptr = GetProcAddressA(handle, func);
304 #else /* _WIN32_WCE */
305         fptr = GetProcAddress(handle, func);
306 #endif /* _WIN32_WCE */
307 #else /* CONFIG_NATIVE_WINDOWS */
308         fptr = dlsym(handle, func);
309 #endif /* CONFIG_NATIVE_WINDOWS */
310
311         return fptr;
312 }
313
314
315 static int tncc_imc_resolve_funcs(struct tnc_if_imc *imc)
316 {
317         void *handle = imc->dlhandle;
318
319         /* Mandatory IMC functions */
320         imc->Initialize = tncc_get_sym(handle, "TNC_IMC_Initialize");
321         if (imc->Initialize == NULL) {
322                 wpa_printf(MSG_ERROR, "TNC: IMC does not export "
323                            "TNC_IMC_Initialize");
324                 return -1;
325         }
326
327         imc->BeginHandshake = tncc_get_sym(handle, "TNC_IMC_BeginHandshake");
328         if (imc->BeginHandshake == NULL) {
329                 wpa_printf(MSG_ERROR, "TNC: IMC does not export "
330                            "TNC_IMC_BeginHandshake");
331                 return -1;
332         }
333
334         imc->ProvideBindFunction =
335                 tncc_get_sym(handle, "TNC_IMC_ProvideBindFunction");
336         if (imc->ProvideBindFunction == NULL) {
337                 wpa_printf(MSG_ERROR, "TNC: IMC does not export "
338                            "TNC_IMC_ProvideBindFunction");
339                 return -1;
340         }
341
342         /* Optional IMC functions */
343         imc->NotifyConnectionChange =
344                 tncc_get_sym(handle, "TNC_IMC_NotifyConnectionChange");
345         imc->ReceiveMessage = tncc_get_sym(handle, "TNC_IMC_ReceiveMessage");
346         imc->BatchEnding = tncc_get_sym(handle, "TNC_IMC_BatchEnding");
347         imc->Terminate = tncc_get_sym(handle, "TNC_IMC_Terminate");
348
349         return 0;
350 }
351
352
353 static int tncc_imc_initialize(struct tnc_if_imc *imc)
354 {
355         TNC_Result res;
356         TNC_Version imc_ver;
357
358         wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Initialize for IMC '%s'",
359                    imc->name);
360         res = imc->Initialize(imc->imcID, TNC_IFIMC_VERSION_1,
361                               TNC_IFIMC_VERSION_1, &imc_ver);
362         wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Initialize: res=%lu imc_ver=%lu",
363                    (unsigned long) res, (unsigned long) imc_ver);
364
365         return res == TNC_RESULT_SUCCESS ? 0 : -1;
366 }
367
368
369 static int tncc_imc_terminate(struct tnc_if_imc *imc)
370 {
371         TNC_Result res;
372
373         if (imc->Terminate == NULL)
374                 return 0;
375
376         wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Terminate for IMC '%s'",
377                    imc->name);
378         res = imc->Terminate(imc->imcID);
379         wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Terminate: %lu",
380                    (unsigned long) res);
381
382         return res == TNC_RESULT_SUCCESS ? 0 : -1;
383 }
384
385
386 static int tncc_imc_provide_bind_function(struct tnc_if_imc *imc)
387 {
388         TNC_Result res;
389
390         wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_ProvideBindFunction for "
391                    "IMC '%s'", imc->name);
392         res = imc->ProvideBindFunction(imc->imcID, TNC_TNCC_BindFunction);
393         wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_ProvideBindFunction: res=%lu",
394                    (unsigned long) res);
395
396         return res == TNC_RESULT_SUCCESS ? 0 : -1;
397 }
398
399
400 static int tncc_imc_notify_connection_change(struct tnc_if_imc *imc,
401                                              TNC_ConnectionState state)
402 {
403         TNC_Result res;
404
405         if (imc->NotifyConnectionChange == NULL)
406                 return 0;
407
408         wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_NotifyConnectionChange(%d)"
409                    " for IMC '%s'", (int) state, imc->name);
410         res = imc->NotifyConnectionChange(imc->imcID, imc->connectionID,
411                                           state);
412         wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
413                    (unsigned long) res);
414
415         return res == TNC_RESULT_SUCCESS ? 0 : -1;
416 }
417
418
419 static int tncc_imc_begin_handshake(struct tnc_if_imc *imc)
420 {
421         TNC_Result res;
422
423         wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_BeginHandshake for IMC "
424                    "'%s'", imc->name);
425         res = imc->BeginHandshake(imc->imcID, imc->connectionID);
426         wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_BeginHandshake: %lu",
427                    (unsigned long) res);
428
429         return res == TNC_RESULT_SUCCESS ? 0 : -1;
430 }
431
432
433 static int tncc_load_imc(struct tnc_if_imc *imc)
434 {
435         if (imc->path == NULL) {
436                 wpa_printf(MSG_DEBUG, "TNC: No IMC configured");
437                 return -1;
438         }
439
440         wpa_printf(MSG_DEBUG, "TNC: Opening IMC: %s (%s)",
441                    imc->name, imc->path);
442 #ifdef CONFIG_NATIVE_WINDOWS
443 #ifdef UNICODE
444         {
445                 TCHAR *lib = wpa_strdup_tchar(imc->path);
446                 if (lib == NULL)
447                         return -1;
448                 imc->dlhandle = LoadLibrary(lib);
449                 os_free(lib);
450         }
451 #else /* UNICODE */
452         imc->dlhandle = LoadLibrary(imc->path);
453 #endif /* UNICODE */
454         if (imc->dlhandle == NULL) {
455                 wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %d",
456                            imc->name, imc->path, (int) GetLastError());
457                 return -1;
458         }
459 #else /* CONFIG_NATIVE_WINDOWS */
460         imc->dlhandle = dlopen(imc->path, RTLD_LAZY);
461         if (imc->dlhandle == NULL) {
462                 wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %s",
463                            imc->name, imc->path, dlerror());
464                 return -1;
465         }
466 #endif /* CONFIG_NATIVE_WINDOWS */
467
468         if (tncc_imc_resolve_funcs(imc) < 0) {
469                 wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMC functions");
470                 return -1;
471         }
472
473         if (tncc_imc_initialize(imc) < 0 ||
474             tncc_imc_provide_bind_function(imc) < 0) {
475                 wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMC");
476                 return -1;
477         }
478
479         return 0;
480 }
481
482
483 static void tncc_unload_imc(struct tnc_if_imc *imc)
484 {
485         tncc_imc_terminate(imc);
486         tnc_imc[imc->imcID] = NULL;
487
488         if (imc->dlhandle) {
489 #ifdef CONFIG_NATIVE_WINDOWS
490                 FreeLibrary(imc->dlhandle);
491 #else /* CONFIG_NATIVE_WINDOWS */
492                 dlclose(imc->dlhandle);
493 #endif /* CONFIG_NATIVE_WINDOWS */
494         }
495         os_free(imc->name);
496         os_free(imc->path);
497         os_free(imc->supported_types);
498         os_free(imc->imc_send);
499 }
500
501
502 static int tncc_supported_type(struct tnc_if_imc *imc, unsigned int type)
503 {
504         size_t i;
505         unsigned int vendor, subtype;
506
507         if (imc == NULL || imc->supported_types == NULL)
508                 return 0;
509
510         vendor = type >> 8;
511         subtype = type & 0xff;
512
513         for (i = 0; i < imc->num_supported_types; i++) {
514                 unsigned int svendor, ssubtype;
515                 svendor = imc->supported_types[i] >> 8;
516                 ssubtype = imc->supported_types[i] & 0xff;
517                 if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
518                     (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
519                         return 1;
520         }
521
522         return 0;
523 }
524
525
526 static void tncc_send_to_imcs(struct tncc_data *tncc, unsigned int type,
527                               const u8 *msg, size_t len)
528 {
529         struct tnc_if_imc *imc;
530         TNC_Result res;
531
532         wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMC(s)", msg, len);
533
534         for (imc = tncc->imc; imc; imc = imc->next) {
535                 if (imc->ReceiveMessage == NULL ||
536                     !tncc_supported_type(imc, type))
537                         continue;
538
539                 wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMC '%s'",
540                            imc->name);
541                 res = imc->ReceiveMessage(imc->imcID, imc->connectionID,
542                                           (TNC_BufferReference) msg, len,
543                                           type);
544                 wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
545                            (unsigned long) res);
546         }
547 }
548
549
550 void tncc_init_connection(struct tncc_data *tncc)
551 {
552         struct tnc_if_imc *imc;
553
554         for (imc = tncc->imc; imc; imc = imc->next) {
555                 tncc_imc_notify_connection_change(
556                         imc, TNC_CONNECTION_STATE_CREATE);
557                 tncc_imc_notify_connection_change(
558                         imc, TNC_CONNECTION_STATE_HANDSHAKE);
559
560                 os_free(imc->imc_send);
561                 imc->imc_send = NULL;
562                 imc->imc_send_len = 0;
563
564                 tncc_imc_begin_handshake(imc);
565         }
566 }
567
568
569 size_t tncc_total_send_len(struct tncc_data *tncc)
570 {
571         struct tnc_if_imc *imc;
572
573         size_t len = 0;
574         for (imc = tncc->imc; imc; imc = imc->next)
575                 len += imc->imc_send_len;
576         return len;
577 }
578
579
580 u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos)
581 {
582         struct tnc_if_imc *imc;
583
584         for (imc = tncc->imc; imc; imc = imc->next) {
585                 if (imc->imc_send == NULL)
586                         continue;
587
588                 os_memcpy(pos, imc->imc_send, imc->imc_send_len);
589                 pos += imc->imc_send_len;
590                 os_free(imc->imc_send);
591                 imc->imc_send = NULL;
592                 imc->imc_send_len = 0;
593         }
594
595         return pos;
596 }
597
598
599 char * tncc_if_tnccs_start(struct tncc_data *tncc)
600 {
601         char *buf = os_malloc(1000);
602         if (buf == NULL)
603                 return NULL;
604         tncc->last_batchid++;
605         os_snprintf(buf, 1000, IF_TNCCS_START, tncc->last_batchid);
606         return buf;
607 }
608
609
610 char * tncc_if_tnccs_end(void)
611 {
612         char *buf = os_malloc(100);
613         if (buf == NULL)
614                 return NULL;
615         os_snprintf(buf, 100, IF_TNCCS_END);
616         return buf;
617 }
618
619
620 static void tncc_notify_recommendation(struct tncc_data *tncc,
621                                        enum tncc_process_res res)
622 {
623         TNC_ConnectionState state;
624         struct tnc_if_imc *imc;
625
626         switch (res) {
627         case TNCCS_RECOMMENDATION_ALLOW:
628                 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
629                 break;
630         case TNCCS_RECOMMENDATION_NONE:
631                 state = TNC_CONNECTION_STATE_ACCESS_NONE;
632                 break;
633         case TNCCS_RECOMMENDATION_ISOLATE:
634                 state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
635                 break;
636         default:
637                 state = TNC_CONNECTION_STATE_ACCESS_NONE;
638                 break;
639         }
640
641         for (imc = tncc->imc; imc; imc = imc->next)
642                 tncc_imc_notify_connection_change(imc, state);
643 }
644
645
646 static int tncc_get_type(char *start, unsigned int *type)
647 {
648         char *pos = os_strstr(start, "<Type>");
649         if (pos == NULL)
650                 return -1;
651         pos += 6;
652         *type = strtoul(pos, NULL, 16);
653         return 0;
654 }
655
656
657 static unsigned char * tncc_get_base64(char *start, size_t *decoded_len)
658 {
659         char *pos, *pos2;
660         unsigned char *decoded;
661
662         pos = os_strstr(start, "<Base64>");
663         if (pos == NULL)
664                 return NULL;
665
666         pos += 8;
667         pos2 = os_strstr(pos, "</Base64>");
668         if (pos2 == NULL)
669                 return NULL;
670         *pos2 = '\0';
671
672         decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
673                                 decoded_len);
674         *pos2 = '<';
675         if (decoded == NULL) {
676                 wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
677         }
678
679         return decoded;
680 }
681
682
683 static enum tncc_process_res tncc_get_recommendation(char *start)
684 {
685         char *pos, *pos2, saved;
686         int recom;
687
688         pos = os_strstr(start, "<TNCCS-Recommendation ");
689         if (pos == NULL)
690                 return TNCCS_RECOMMENDATION_ERROR;
691
692         pos += 21;
693         pos = os_strstr(pos, " type=");
694         if (pos == NULL)
695                 return TNCCS_RECOMMENDATION_ERROR;
696         pos += 6;
697
698         if (*pos == '"')
699                 pos++;
700
701         pos2 = pos;
702         while (*pos2 != '\0' && *pos2 != '"' && *pos2 != '>')
703                 pos2++;
704
705         if (*pos2 == '\0')
706                 return TNCCS_RECOMMENDATION_ERROR;
707
708         saved = *pos2;
709         *pos2 = '\0';
710         wpa_printf(MSG_DEBUG, "TNC: TNCCS-Recommendation: '%s'", pos);
711
712         recom = TNCCS_RECOMMENDATION_ERROR;
713         if (os_strcmp(pos, "allow") == 0)
714                 recom = TNCCS_RECOMMENDATION_ALLOW;
715         else if (os_strcmp(pos, "none") == 0)
716                 recom = TNCCS_RECOMMENDATION_NONE;
717         else if (os_strcmp(pos, "isolate") == 0)
718                 recom = TNCCS_RECOMMENDATION_ISOLATE;
719
720         *pos2 = saved;
721
722         return recom;
723 }
724
725
726 enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc,
727                                             const u8 *msg, size_t len)
728 {
729         char *buf, *start, *end, *pos, *pos2, *payload;
730         unsigned int batch_id;
731         unsigned char *decoded;
732         size_t decoded_len;
733         enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION;
734         int recommendation_msg = 0;
735
736         buf = os_malloc(len + 1);
737         if (buf == NULL)
738                 return TNCCS_PROCESS_ERROR;
739
740         os_memcpy(buf, msg, len);
741         buf[len] = '\0';
742         start = os_strstr(buf, "<TNCCS-Batch ");
743         end = os_strstr(buf, "</TNCCS-Batch>");
744         if (start == NULL || end == NULL || start > end) {
745                 os_free(buf);
746                 return TNCCS_PROCESS_ERROR;
747         }
748
749         start += 13;
750         while (*start == ' ')
751                 start++;
752         *end = '\0';
753
754         pos = os_strstr(start, "BatchId=");
755         if (pos == NULL) {
756                 os_free(buf);
757                 return TNCCS_PROCESS_ERROR;
758         }
759
760         pos += 8;
761         if (*pos == '"')
762                 pos++;
763         batch_id = atoi(pos);
764         wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
765                    batch_id);
766         if (batch_id != tncc->last_batchid + 1) {
767                 wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
768                            "%u (expected %u)",
769                            batch_id, tncc->last_batchid + 1);
770                 os_free(buf);
771                 return TNCCS_PROCESS_ERROR;
772         }
773         tncc->last_batchid = batch_id;
774
775         while (*pos != '\0' && *pos != '>')
776                 pos++;
777         if (*pos == '\0') {
778                 os_free(buf);
779                 return TNCCS_PROCESS_ERROR;
780         }
781         pos++;
782         payload = start;
783
784         /*
785          * <IMC-IMV-Message>
786          * <Type>01234567</Type>
787          * <Base64>foo==</Base64>
788          * </IMC-IMV-Message>
789          */
790
791         while (*start) {
792                 char *endpos;
793                 unsigned int type;
794
795                 pos = os_strstr(start, "<IMC-IMV-Message>");
796                 if (pos == NULL)
797                         break;
798                 start = pos + 17;
799                 end = os_strstr(start, "</IMC-IMV-Message>");
800                 if (end == NULL)
801                         break;
802                 *end = '\0';
803                 endpos = end;
804                 end += 18;
805
806                 if (tncc_get_type(start, &type) < 0) {
807                         *endpos = '<';
808                         start = end;
809                         continue;
810                 }
811                 wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
812
813                 decoded = tncc_get_base64(start, &decoded_len);
814                 if (decoded == NULL) {
815                         *endpos = '<';
816                         start = end;
817                         continue;
818                 }
819
820                 tncc_send_to_imcs(tncc, type, decoded, decoded_len);
821
822                 os_free(decoded);
823
824                 start = end;
825         }
826
827         /*
828          * <TNCC-TNCS-Message>
829          * <Type>01234567</Type>
830          * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
831          * <Base64>foo==</Base64>
832          * </TNCC-TNCS-Message>
833          */
834
835         start = payload;
836         while (*start) {
837                 unsigned int type;
838                 char *xml, *xmlend, *endpos;
839
840                 pos = os_strstr(start, "<TNCC-TNCS-Message>");
841                 if (pos == NULL)
842                         break;
843                 start = pos + 19;
844                 end = os_strstr(start, "</TNCC-TNCS-Message>");
845                 if (end == NULL)
846                         break;
847                 *end = '\0';
848                 endpos = end;
849                 end += 20;
850
851                 if (tncc_get_type(start, &type) < 0) {
852                         *endpos = '<';
853                         start = end;
854                         continue;
855                 }
856                 wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
857                            type);
858
859                 /* Base64 OR XML */
860                 decoded = NULL;
861                 xml = NULL;
862                 xmlend = NULL;
863                 pos = os_strstr(start, "<XML>");
864                 if (pos) {
865                         pos += 5;
866                         pos2 = os_strstr(pos, "</XML>");
867                         if (pos2 == NULL) {
868                                 *endpos = '<';
869                                 start = end;
870                                 continue;
871                         }
872                         xmlend = pos2;
873                         xml = pos;
874                 } else {
875                         decoded = tncc_get_base64(start, &decoded_len);
876                         if (decoded == NULL) {
877                                 *endpos = '<';
878                                 start = end;
879                                 continue;
880                         }
881                 }
882
883                 if (decoded) {
884                         wpa_hexdump_ascii(MSG_MSGDUMP,
885                                           "TNC: TNCC-TNCS-Message Base64",
886                                           decoded, decoded_len);
887                         os_free(decoded);
888                 }
889
890                 if (xml) {
891                         wpa_hexdump_ascii(MSG_MSGDUMP,
892                                           "TNC: TNCC-TNCS-Message XML",
893                                           (unsigned char *) xml,
894                                           xmlend - xml);
895                 }
896
897                 if (type == TNC_TNCCS_RECOMMENDATION && xml) {
898                         /*
899                          * <TNCCS-Recommendation type="allow">
900                          * </TNCCS-Recommendation>
901                          */
902                         *xmlend = '\0';
903                         res = tncc_get_recommendation(xml);
904                         *xmlend = '<';
905                         recommendation_msg = 1;
906                 }
907
908                 start = end;
909         }
910
911         os_free(buf);
912
913         if (recommendation_msg)
914                 tncc_notify_recommendation(tncc, res);
915
916         return res;
917 }
918
919
920 #ifdef CONFIG_NATIVE_WINDOWS
921 static int tncc_read_config_reg(struct tncc_data *tncc, HKEY hive)
922 {
923         HKEY hk, hk2;
924         LONG ret;
925         DWORD i;
926         struct tnc_if_imc *imc, *last;
927         int j;
928
929         last = tncc->imc;
930         while (last && last->next)
931                 last = last->next;
932
933         ret = RegOpenKeyEx(hive, TNC_WINREG_PATH, 0, KEY_ENUMERATE_SUB_KEYS,
934                            &hk);
935         if (ret != ERROR_SUCCESS)
936                 return 0;
937
938         for (i = 0; ; i++) {
939                 TCHAR name[255], *val;
940                 DWORD namelen, buflen;
941
942                 namelen = 255;
943                 ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL,
944                                    NULL);
945
946                 if (ret == ERROR_NO_MORE_ITEMS)
947                         break;
948
949                 if (ret != ERROR_SUCCESS) {
950                         wpa_printf(MSG_DEBUG, "TNC: RegEnumKeyEx failed: 0x%x",
951                                    (unsigned int) ret);
952                         break;
953                 }
954
955                 if (namelen >= 255)
956                         namelen = 255 - 1;
957                 name[namelen] = '\0';
958
959                 wpa_printf(MSG_DEBUG, "TNC: IMC '" TSTR "'", name);
960
961                 ret = RegOpenKeyEx(hk, name, 0, KEY_QUERY_VALUE, &hk2);
962                 if (ret != ERROR_SUCCESS) {
963                         wpa_printf(MSG_DEBUG, "Could not open IMC key '" TSTR
964                                    "'", name);
965                         continue;
966                 }
967
968                 ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, NULL,
969                                       &buflen);
970                 if (ret != ERROR_SUCCESS) {
971                         wpa_printf(MSG_DEBUG, "TNC: Could not read Path from "
972                                    "IMC key '" TSTR "'", name);
973                         RegCloseKey(hk2);
974                         continue;
975                 }
976
977                 val = os_malloc(buflen);
978                 if (val == NULL) {
979                         RegCloseKey(hk2);
980                         continue;
981                 }
982
983                 ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL,
984                                       (LPBYTE) val, &buflen);
985                 if (ret != ERROR_SUCCESS) {
986                         os_free(val);
987                         RegCloseKey(hk2);
988                         continue;
989                 }
990
991                 RegCloseKey(hk2);
992
993                 wpa_unicode2ascii_inplace(val);
994                 wpa_printf(MSG_DEBUG, "TNC: IMC Path '%s'", (char *) val);
995
996                 for (j = 0; j < TNC_MAX_IMC_ID; j++) {
997                         if (tnc_imc[j] == NULL)
998                                 break;
999                 }
1000                 if (j >= TNC_MAX_IMC_ID) {
1001                         wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
1002                         os_free(val);
1003                         continue;
1004                 }
1005
1006                 imc = os_zalloc(sizeof(*imc));
1007                 if (imc == NULL) {
1008                         os_free(val);
1009                         break;
1010                 }
1011
1012                 imc->imcID = j;
1013
1014                 wpa_unicode2ascii_inplace(name);
1015                 imc->name = os_strdup((char *) name);
1016                 imc->path = os_strdup((char *) val);
1017
1018                 os_free(val);
1019
1020                 if (last == NULL)
1021                         tncc->imc = imc;
1022                 else
1023                         last->next = imc;
1024                 last = imc;
1025
1026                 tnc_imc[imc->imcID] = imc;
1027         }
1028
1029         RegCloseKey(hk);
1030
1031         return 0;
1032 }
1033
1034
1035 static int tncc_read_config(struct tncc_data *tncc)
1036 {
1037         if (tncc_read_config_reg(tncc, HKEY_LOCAL_MACHINE) < 0 ||
1038             tncc_read_config_reg(tncc, HKEY_CURRENT_USER) < 0)
1039                 return -1;
1040         return 0;
1041 }
1042
1043 #else /* CONFIG_NATIVE_WINDOWS */
1044
1045 static struct tnc_if_imc * tncc_parse_imc(char *start, char *end, int *error)
1046 {
1047         struct tnc_if_imc *imc;
1048         char *pos, *pos2;
1049         int i;
1050
1051         for (i = 0; i < TNC_MAX_IMC_ID; i++) {
1052                 if (tnc_imc[i] == NULL)
1053                         break;
1054         }
1055         if (i >= TNC_MAX_IMC_ID) {
1056                 wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
1057                 return NULL;
1058         }
1059
1060         imc = os_zalloc(sizeof(*imc));
1061         if (imc == NULL) {
1062                 *error = 1;
1063                 return NULL;
1064         }
1065
1066         imc->imcID = i;
1067
1068         pos = start;
1069         wpa_printf(MSG_DEBUG, "TNC: Configured IMC: %s", pos);
1070         if (pos + 1 >= end || *pos != '"') {
1071                 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
1072                            "(no starting quotation mark)", start);
1073                 os_free(imc);
1074                 return NULL;
1075         }
1076
1077         pos++;
1078         pos2 = pos;
1079         while (pos2 < end && *pos2 != '"')
1080                 pos2++;
1081         if (pos2 >= end) {
1082                 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
1083                            "(no ending quotation mark)", start);
1084                 os_free(imc);
1085                 return NULL;
1086         }
1087         *pos2 = '\0';
1088         wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
1089         imc->name = os_strdup(pos);
1090
1091         pos = pos2 + 1;
1092         if (pos >= end || *pos != ' ') {
1093                 wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
1094                            "(no space after name)", start);
1095                 os_free(imc);
1096                 return NULL;
1097         }
1098
1099         pos++;
1100         wpa_printf(MSG_DEBUG, "TNC: IMC file: '%s'", pos);
1101         imc->path = os_strdup(pos);
1102         tnc_imc[imc->imcID] = imc;
1103
1104         return imc;
1105 }
1106
1107
1108 static int tncc_read_config(struct tncc_data *tncc)
1109 {
1110         char *config, *end, *pos, *line_end;
1111         size_t config_len;
1112         struct tnc_if_imc *imc, *last;
1113
1114         last = NULL;
1115
1116         config = os_readfile(TNC_CONFIG_FILE, &config_len);
1117         if (config == NULL) {
1118                 wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
1119                            "file '%s'", TNC_CONFIG_FILE);
1120                 return -1;
1121         }
1122
1123         end = config + config_len;
1124         for (pos = config; pos < end; pos = line_end + 1) {
1125                 line_end = pos;
1126                 while (*line_end != '\n' && *line_end != '\r' &&
1127                        line_end < end)
1128                         line_end++;
1129                 *line_end = '\0';
1130
1131                 if (os_strncmp(pos, "IMC ", 4) == 0) {
1132                         int error = 0;
1133
1134                         imc = tncc_parse_imc(pos + 4, line_end, &error);
1135                         if (error)
1136                                 return -1;
1137                         if (imc) {
1138                                 if (last == NULL)
1139                                         tncc->imc = imc;
1140                                 else
1141                                         last->next = imc;
1142                                 last = imc;
1143                         }
1144                 }
1145         }
1146
1147         os_free(config);
1148
1149         return 0;
1150 }
1151
1152 #endif /* CONFIG_NATIVE_WINDOWS */
1153
1154
1155 struct tncc_data * tncc_init(void)
1156 {
1157         struct tncc_data *tncc;
1158         struct tnc_if_imc *imc;
1159
1160         tncc = os_zalloc(sizeof(*tncc));
1161         if (tncc == NULL)
1162                 return NULL;
1163
1164         /* TODO:
1165          * move loading and Initialize() to a location that is not
1166          *    re-initialized for every EAP-TNC session (?)
1167          */
1168
1169         if (tncc_read_config(tncc) < 0) {
1170                 wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
1171                 goto failed;
1172         }
1173
1174         for (imc = tncc->imc; imc; imc = imc->next) {
1175                 if (tncc_load_imc(imc)) {
1176                         wpa_printf(MSG_ERROR, "TNC: Failed to load IMC '%s'",
1177                                    imc->name);
1178                         goto failed;
1179                 }
1180         }
1181
1182         return tncc;
1183
1184 failed:
1185         tncc_deinit(tncc);
1186         return NULL;
1187 }
1188
1189
1190 void tncc_deinit(struct tncc_data *tncc)
1191 {
1192         struct tnc_if_imc *imc, *prev;
1193
1194         imc = tncc->imc;
1195         while (imc) {
1196                 tncc_unload_imc(imc);
1197
1198                 prev = imc;
1199                 imc = imc->next;
1200                 os_free(prev);
1201         }
1202
1203         os_free(tncc);
1204 }