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