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