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