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