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