Merge branch 'vendor/NCURSES'
[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 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->name);
1110                 os_free(imc);
1111                 return NULL;
1112         }
1113
1114         pos++;
1115         wpa_printf(MSG_DEBUG, "TNC: IMC file: '%s'", pos);
1116         imc->path = os_strdup(pos);
1117         tnc_imc[imc->imcID] = imc;
1118
1119         return imc;
1120 }
1121
1122
1123 static int tncc_read_config(struct tncc_data *tncc)
1124 {
1125         char *config, *end, *pos, *line_end;
1126         size_t config_len;
1127         struct tnc_if_imc *imc, *last;
1128
1129         last = NULL;
1130
1131         config = os_readfile(TNC_CONFIG_FILE, &config_len);
1132         if (config == NULL) {
1133                 wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
1134                            "file '%s'", TNC_CONFIG_FILE);
1135                 return -1;
1136         }
1137
1138         end = config + config_len;
1139         for (pos = config; pos < end; pos = line_end + 1) {
1140                 line_end = pos;
1141                 while (*line_end != '\n' && *line_end != '\r' &&
1142                        line_end < end)
1143                         line_end++;
1144                 *line_end = '\0';
1145
1146                 if (os_strncmp(pos, "IMC ", 4) == 0) {
1147                         int error = 0;
1148
1149                         imc = tncc_parse_imc(pos + 4, line_end, &error);
1150                         if (error)
1151                                 return -1;
1152                         if (imc) {
1153                                 if (last == NULL)
1154                                         tncc->imc = imc;
1155                                 else
1156                                         last->next = imc;
1157                                 last = imc;
1158                         }
1159                 }
1160         }
1161
1162         os_free(config);
1163
1164         return 0;
1165 }
1166
1167 #endif /* CONFIG_NATIVE_WINDOWS */
1168
1169
1170 struct tncc_data * tncc_init(void)
1171 {
1172         struct tncc_data *tncc;
1173         struct tnc_if_imc *imc;
1174
1175         tncc = os_zalloc(sizeof(*tncc));
1176         if (tncc == NULL)
1177                 return NULL;
1178
1179         /* TODO:
1180          * move loading and Initialize() to a location that is not
1181          *    re-initialized for every EAP-TNC session (?)
1182          */
1183
1184         if (tncc_read_config(tncc) < 0) {
1185                 wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
1186                 goto failed;
1187         }
1188
1189         for (imc = tncc->imc; imc; imc = imc->next) {
1190                 if (tncc_load_imc(imc)) {
1191                         wpa_printf(MSG_ERROR, "TNC: Failed to load IMC '%s'",
1192                                    imc->name);
1193                         goto failed;
1194                 }
1195         }
1196
1197         return tncc;
1198
1199 failed:
1200         tncc_deinit(tncc);
1201         return NULL;
1202 }
1203
1204
1205 void tncc_deinit(struct tncc_data *tncc)
1206 {
1207         struct tnc_if_imc *imc, *prev;
1208
1209         imc = tncc->imc;
1210         while (imc) {
1211                 tncc_unload_imc(imc);
1212
1213                 prev = imc;
1214                 imc = imc->next;
1215                 os_free(prev);
1216         }
1217
1218         os_free(tncc);
1219 }
1220
1221
1222 static struct wpabuf * tncc_build_soh(int ver)
1223 {
1224         struct wpabuf *buf;
1225         u8 *tlv_len, *tlv_len2, *outer_len, *inner_len, *ssoh_len, *end;
1226         u8 correlation_id[24];
1227         /* TODO: get correct name */
1228         char *machinename = "wpa_supplicant@w1.fi";
1229
1230         if (os_get_random(correlation_id, sizeof(correlation_id)))
1231                 return NULL;
1232         wpa_hexdump(MSG_DEBUG, "TNC: SoH Correlation ID",
1233                     correlation_id, sizeof(correlation_id));
1234
1235         buf = wpabuf_alloc(200);
1236         if (buf == NULL)
1237                 return NULL;
1238
1239         /* Vendor-Specific TLV (Microsoft) - SoH */
1240         wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
1241         tlv_len = wpabuf_put(buf, 2); /* Length */
1242         wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
1243         wpabuf_put_be16(buf, 0x01); /* TLV Type - SoH TLV */
1244         tlv_len2 = wpabuf_put(buf, 2); /* Length */
1245
1246         /* SoH Header */
1247         wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* Outer Type */
1248         outer_len = wpabuf_put(buf, 2);
1249         wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
1250         wpabuf_put_be16(buf, ver); /* Inner Type */
1251         inner_len = wpabuf_put(buf, 2);
1252
1253         if (ver == 2) {
1254                 /* SoH Mode Sub-Header */
1255                 /* Outer Type */
1256                 wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV);
1257                 wpabuf_put_be16(buf, 4 + 24 + 1 + 1); /* Length */
1258                 wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
1259                 /* Value: */
1260                 wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
1261                 wpabuf_put_u8(buf, 0x01); /* Intent Flag - Request */
1262                 wpabuf_put_u8(buf, 0x00); /* Content-Type Flag */
1263         }
1264
1265         /* SSoH TLV */
1266         /* System-Health-Id */
1267         wpabuf_put_be16(buf, 0x0002); /* Type */
1268         wpabuf_put_be16(buf, 4); /* Length */
1269         wpabuf_put_be32(buf, 79616);
1270         /* Vendor-Specific Attribute */
1271         wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV);
1272         ssoh_len = wpabuf_put(buf, 2);
1273         wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
1274
1275         /* MS-Packet-Info */
1276         wpabuf_put_u8(buf, SSOH_MS_PACKET_INFO);
1277         /* Note: IF-TNCCS-SOH v1.0 r8 claims this field to be:
1278          * Reserved(4 bits) r(1 bit) Vers(3 bits), but Windows XP
1279          * SP3 seems to be sending 0x11 for SSoH, i.e., r(request/response) bit
1280          * would not be in the specified location.
1281          * [MS-SOH] 4.0.2: Reserved(3 bits) r(1 bit) Vers(4 bits)
1282          */
1283         wpabuf_put_u8(buf, 0x11); /* r=request, vers=1 */
1284
1285         /* MS-Machine-Inventory */
1286         /* TODO: get correct values; 0 = not applicable for OS */
1287         wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY);
1288         wpabuf_put_be32(buf, 0); /* osVersionMajor */
1289         wpabuf_put_be32(buf, 0); /* osVersionMinor */
1290         wpabuf_put_be32(buf, 0); /* osVersionBuild */
1291         wpabuf_put_be16(buf, 0); /* spVersionMajor */
1292         wpabuf_put_be16(buf, 0); /* spVersionMinor */
1293         wpabuf_put_be16(buf, 0); /* procArch */
1294
1295         /* MS-MachineName */
1296         wpabuf_put_u8(buf, SSOH_MS_MACHINENAME);
1297         wpabuf_put_be16(buf, os_strlen(machinename) + 1);
1298         wpabuf_put_data(buf, machinename, os_strlen(machinename) + 1);
1299
1300         /* MS-CorrelationId */
1301         wpabuf_put_u8(buf, SSOH_MS_CORRELATIONID);
1302         wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
1303
1304         /* MS-Quarantine-State */
1305         wpabuf_put_u8(buf, SSOH_MS_QUARANTINE_STATE);
1306         wpabuf_put_be16(buf, 1); /* Flags: ExtState=0, f=0, qState=1 */
1307         wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (hi) */
1308         wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (lo) */
1309         wpabuf_put_be16(buf, 1); /* urlLenInBytes */
1310         wpabuf_put_u8(buf, 0); /* null termination for the url */
1311
1312         /* MS-Machine-Inventory-Ex */
1313         wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY_EX);
1314         wpabuf_put_be32(buf, 0); /* Reserved
1315                                   * (note: Windows XP SP3 uses 0xdecafbad) */
1316         wpabuf_put_u8(buf, 1); /* ProductType: Client */
1317
1318         /* Update SSoH Length */
1319         end = wpabuf_put(buf, 0);
1320         WPA_PUT_BE16(ssoh_len, end - ssoh_len - 2);
1321
1322         /* TODO: SoHReportEntry TLV (zero or more) */
1323
1324         /* Update length fields */
1325         end = wpabuf_put(buf, 0);
1326         WPA_PUT_BE16(tlv_len, end - tlv_len - 2);
1327         WPA_PUT_BE16(tlv_len2, end - tlv_len2 - 2);
1328         WPA_PUT_BE16(outer_len, end - outer_len - 2);
1329         WPA_PUT_BE16(inner_len, end - inner_len - 2);
1330
1331         return buf;
1332 }
1333
1334
1335 struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len)
1336 {
1337         const u8 *pos;
1338
1339         wpa_hexdump(MSG_DEBUG, "TNC: SoH Request", data, len);
1340
1341         if (len < 12)
1342                 return NULL;
1343
1344         /* SoH Request */
1345         pos = data;
1346
1347         /* TLV Type */
1348         if (WPA_GET_BE16(pos) != EAP_TLV_VENDOR_SPECIFIC_TLV)
1349                 return NULL;
1350         pos += 2;
1351
1352         /* Length */
1353         if (WPA_GET_BE16(pos) < 8)
1354                 return NULL;
1355         pos += 2;
1356
1357         /* Vendor_Id */
1358         if (WPA_GET_BE32(pos) != EAP_VENDOR_MICROSOFT)
1359                 return NULL;
1360         pos += 4;
1361
1362         /* TLV Type */
1363         if (WPA_GET_BE16(pos) != 0x02 /* SoH request TLV */)
1364                 return NULL;
1365
1366         wpa_printf(MSG_DEBUG, "TNC: SoH Request TLV received");
1367
1368         return tncc_build_soh(2);
1369 }