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