09900ade966d246f7b2e81ffa929b75cbb3e2ed7
[strongswan.git] / src / libtnccs / plugins / tnccs_20 / tnccs_20.c
1 /*
2 * Copyright (C) 2010 Sansar Choinyanbuu
3 * Copyright (C) 2010-2013 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "tnccs_20.h"
18 #include "batch/pb_tnc_batch.h"
19 #include "messages/pb_tnc_msg.h"
20 #include "messages/pb_pa_msg.h"
21 #include "messages/pb_error_msg.h"
22 #include "messages/pb_assessment_result_msg.h"
23 #include "messages/pb_access_recommendation_msg.h"
24 #include "messages/pb_remediation_parameters_msg.h"
25 #include "messages/pb_reason_string_msg.h"
26 #include "messages/pb_language_preference_msg.h"
27 #include "state_machine/pb_tnc_state_machine.h"
28
29 #include <tncif_names.h>
30 #include <tncif_pa_subtypes.h>
31
32 #include <tnc/tnc.h>
33 #include <tnc/tnccs/tnccs_manager.h>
34 #include <tnc/imc/imc_manager.h>
35 #include <tnc/imv/imv_manager.h>
36
37 #include <threading/mutex.h>
38 #include <utils/debug.h>
39 #include <collections/linked_list.h>
40 #include <pen/pen.h>
41
42 typedef struct private_tnccs_20_t private_tnccs_20_t;
43
44 /**
45 * Private data of a tnccs_20_t object.
46 */
47 struct private_tnccs_20_t {
48
49 /**
50 * Public tnccs_t interface.
51 */
52 tnccs_t public;
53
54 /**
55 * TNCC if TRUE, TNCS if FALSE
56 */
57 bool is_server;
58
59 /**
60 * Server identity
61 */
62 identification_t *server;
63
64 /**
65 * Client identity
66 */
67 identification_t *peer;
68
69 /**
70 * Underlying TNC IF-T transport protocol
71 */
72 tnc_ift_type_t transport;
73
74 /**
75 * Type of TNC client authentication
76 */
77 u_int32_t auth_type;
78
79 /**
80 * PB-TNC State Machine
81 */
82 pb_tnc_state_machine_t *state_machine;
83
84 /**
85 * Connection ID assigned to this TNCCS connection
86 */
87 TNC_ConnectionID connection_id;
88
89 /**
90 * PB-TNC messages to be sent
91 */
92 linked_list_t *messages;
93
94 /**
95 * Type of PB-TNC batch being constructed
96 */
97 pb_tnc_batch_type_t batch_type;
98
99 /**
100 * Maximum PB-TNC batch size
101 */
102 size_t max_batch_len;
103
104 /**
105 * Maximum PA-TNC message size
106 */
107 size_t max_msg_len;
108
109 /**
110 * Mutex locking the batch in construction
111 */
112 mutex_t *mutex;
113
114 /**
115 * Flag set while processing
116 */
117 bool fatal_error;
118
119 /**
120 * Flag set by IMC/IMV RequestHandshakeRetry() function
121 */
122 bool request_handshake_retry;
123
124 /**
125 * SendMessage() by IMC/IMV only allowed if flag is set
126 */
127 bool send_msg;
128
129 /**
130 * Set of IMV recommendations (TNC Server only)
131 */
132 recommendations_t *recs;
133
134 };
135
136 /**
137 * If the batch type changes then delete all accumulated PB-TNC messages
138 */
139 void change_batch_type(private_tnccs_20_t *this, pb_tnc_batch_type_t batch_type)
140 {
141 pb_tnc_msg_t *msg;
142
143 if (batch_type != this->batch_type)
144 {
145 if (this->batch_type != PB_BATCH_NONE)
146 {
147 DBG1(DBG_TNC, "cancelling PB-TNC %N batch",
148 pb_tnc_batch_type_names, this->batch_type);
149
150 while (this->messages->remove_last(this->messages,
151 (void**)&msg) == SUCCESS)
152 {
153 msg->destroy(msg);
154 }
155 }
156 this->batch_type = batch_type;
157 }
158 }
159
160 METHOD(tnccs_t, send_msg, TNC_Result,
161 private_tnccs_20_t* this, TNC_IMCID imc_id, TNC_IMVID imv_id,
162 TNC_UInt32 msg_flags,
163 TNC_BufferReference msg,
164 TNC_UInt32 msg_len,
165 TNC_VendorID msg_vid,
166 TNC_MessageSubtype msg_subtype)
167 {
168 pb_tnc_msg_t *pb_tnc_msg;
169 pb_tnc_batch_type_t batch_type;
170 enum_name_t *pa_subtype_names;
171 bool excl;
172
173 if (!this->send_msg)
174 {
175 DBG1(DBG_TNC, "%s %u not allowed to call SendMessage()",
176 this->is_server ? "IMV" : "IMC",
177 this->is_server ? imv_id : imc_id);
178 return TNC_RESULT_ILLEGAL_OPERATION;
179 }
180 excl = (msg_flags & TNC_MESSAGE_FLAGS_EXCLUSIVE) != 0;
181
182 pb_tnc_msg = pb_pa_msg_create(msg_vid, msg_subtype, imc_id, imv_id,
183 excl, chunk_create(msg, msg_len));
184
185 pa_subtype_names = get_pa_subtype_names(msg_vid);
186 if (pa_subtype_names)
187 {
188 DBG2(DBG_TNC, "creating PB-PA message type '%N/%N' 0x%06x/0x%08x",
189 pen_names, msg_vid, pa_subtype_names, msg_subtype,
190 msg_vid, msg_subtype);
191 }
192 else
193 {
194 DBG2(DBG_TNC, "creating PB-PA message type '%N' 0x%06x/0x%08x",
195 pen_names, msg_vid, msg_vid, msg_subtype);
196 }
197
198 /* adding PA message to SDATA or CDATA batch only */
199 batch_type = this->is_server ? PB_BATCH_SDATA : PB_BATCH_CDATA;
200 this->mutex->lock(this->mutex);
201 if (this->batch_type == PB_BATCH_NONE)
202 {
203 this->batch_type = batch_type;
204 }
205 if (this->batch_type == batch_type)
206 {
207 this->messages->insert_last(this->messages, pb_tnc_msg);
208 }
209 else
210 {
211 pb_tnc_msg->destroy(pb_tnc_msg);
212 }
213 this->mutex->unlock(this->mutex);
214 return TNC_RESULT_SUCCESS;
215 }
216
217 /**
218 * Handle a single PB-TNC message according to its type
219 */
220 static void handle_message(private_tnccs_20_t *this, pb_tnc_msg_t *msg)
221 {
222 switch (msg->get_type(msg))
223 {
224 case PB_MSG_EXPERIMENTAL:
225 /* nothing to do */
226 break;
227 case PB_MSG_PA:
228 {
229 pb_pa_msg_t *pa_msg;
230 pen_type_t msg_subtype;
231 u_int16_t imc_id, imv_id;
232 chunk_t msg_body;
233 bool excl;
234 enum_name_t *pa_subtype_names;
235
236 pa_msg = (pb_pa_msg_t*)msg;
237 msg_subtype = pa_msg->get_subtype(pa_msg);
238 msg_body = pa_msg->get_body(pa_msg);
239 imc_id = pa_msg->get_collector_id(pa_msg);
240 imv_id = pa_msg->get_validator_id(pa_msg);
241 excl = pa_msg->get_exclusive_flag(pa_msg);
242
243 pa_subtype_names = get_pa_subtype_names(msg_subtype.vendor_id);
244 if (pa_subtype_names)
245 {
246 DBG2(DBG_TNC, "handling PB-PA message type '%N/%N' 0x%06x/0x%08x",
247 pen_names, msg_subtype.vendor_id, pa_subtype_names,
248 msg_subtype.type, msg_subtype.vendor_id, msg_subtype.type);
249 }
250 else
251 {
252 DBG2(DBG_TNC, "handling PB-PA message type '%N' 0x%06x/0x%08x",
253 pen_names, msg_subtype.vendor_id, msg_subtype.vendor_id,
254 msg_subtype.type);
255 }
256
257 this->send_msg = TRUE;
258 if (this->is_server)
259 {
260 tnc->imvs->receive_message(tnc->imvs, this->connection_id,
261 excl, msg_body.ptr, msg_body.len,
262 msg_subtype.vendor_id,
263 msg_subtype.type, imc_id, imv_id);
264 }
265 else
266 {
267 tnc->imcs->receive_message(tnc->imcs, this->connection_id,
268 excl, msg_body.ptr, msg_body.len,
269 msg_subtype.vendor_id,
270 msg_subtype.type, imv_id, imc_id);
271 }
272 this->send_msg = FALSE;
273 break;
274 }
275 case PB_MSG_ASSESSMENT_RESULT:
276 {
277 pb_assessment_result_msg_t *assess_msg;
278 u_int32_t result;
279
280 assess_msg = (pb_assessment_result_msg_t*)msg;
281 result = assess_msg->get_assessment_result(assess_msg);
282 DBG1(DBG_TNC, "PB-TNC assessment result is '%N'",
283 TNC_IMV_Evaluation_Result_names, result);
284 break;
285 }
286 case PB_MSG_ACCESS_RECOMMENDATION:
287 {
288 pb_access_recommendation_msg_t *rec_msg;
289 pb_access_recommendation_code_t rec;
290 TNC_ConnectionState state = TNC_CONNECTION_STATE_ACCESS_NONE;
291
292 rec_msg = (pb_access_recommendation_msg_t*)msg;
293 rec = rec_msg->get_access_recommendation(rec_msg);
294 DBG1(DBG_TNC, "PB-TNC access recommendation is '%N'",
295 pb_access_recommendation_code_names, rec);
296 switch (rec)
297 {
298 case PB_REC_ACCESS_ALLOWED:
299 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
300 break;
301 case PB_REC_ACCESS_DENIED:
302 state = TNC_CONNECTION_STATE_ACCESS_NONE;
303 break;
304 case PB_REC_QUARANTINED:
305 state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
306 }
307 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
308 state);
309 break;
310 }
311 case PB_MSG_REMEDIATION_PARAMETERS:
312 {
313 pb_remediation_parameters_msg_t *rem_msg;
314 pen_type_t parameters_type;
315 chunk_t parameters, string, lang_code;
316
317 rem_msg = (pb_remediation_parameters_msg_t*)msg;
318 parameters_type = rem_msg->get_parameters_type(rem_msg);
319 parameters = rem_msg->get_parameters(rem_msg);
320
321 if (parameters_type.vendor_id == PEN_IETF)
322 {
323 switch (parameters_type.type)
324 {
325 case PB_REMEDIATION_URI:
326 DBG1(DBG_TNC, "remediation uri: %.*s",
327 parameters.len, parameters.ptr);
328 break;
329 case PB_REMEDIATION_STRING:
330 string = rem_msg->get_string(rem_msg, &lang_code);
331 DBG1(DBG_TNC, "remediation string: [%.*s]\n%.*s",
332 lang_code.len, lang_code.ptr,
333 string.len, string.ptr);
334 break;
335 default:
336 DBG1(DBG_TNC, "remediation parameters: %B", &parameters);
337 }
338 }
339 else
340 {
341 DBG1(DBG_TNC, "remediation parameters: %B", &parameters);
342 }
343 break;
344 }
345 case PB_MSG_ERROR:
346 {
347 pb_error_msg_t *err_msg;
348 bool fatal;
349 u_int32_t vendor_id;
350 u_int16_t error_code;
351
352 err_msg = (pb_error_msg_t*)msg;
353 fatal = err_msg->get_fatal_flag(err_msg);
354 vendor_id = err_msg->get_vendor_id(err_msg);
355 error_code = err_msg->get_error_code(err_msg);
356
357 if (fatal)
358 {
359 this->fatal_error = TRUE;
360 }
361
362 if (vendor_id == PEN_IETF)
363 {
364 switch (error_code)
365 {
366 case PB_ERROR_INVALID_PARAMETER:
367 case PB_ERROR_UNSUPPORTED_MANDATORY_MSG:
368 DBG1(DBG_TNC, "received %s PB-TNC error '%N' "
369 "(offset %u bytes)",
370 fatal ? "fatal" : "non-fatal",
371 pb_tnc_error_code_names, error_code,
372 err_msg->get_offset(err_msg));
373 break;
374 case PB_ERROR_VERSION_NOT_SUPPORTED:
375 DBG1(DBG_TNC, "received %s PB-TNC error '%N' "
376 "caused by bad version 0x%02x",
377 fatal ? "fatal" : "non-fatal",
378 pb_tnc_error_code_names, error_code,
379 err_msg->get_bad_version(err_msg));
380 break;
381 case PB_ERROR_UNEXPECTED_BATCH_TYPE:
382 case PB_ERROR_LOCAL_ERROR:
383 default:
384 DBG1(DBG_TNC, "received %s PB-TNC error '%N'",
385 fatal ? "fatal" : "non-fatal",
386 pb_tnc_error_code_names, error_code);
387 break;
388 }
389 }
390 else
391 {
392 DBG1(DBG_TNC, "received %s PB-TNC error (%u) "
393 "with Vendor ID 0x%06x",
394 fatal ? "fatal" : "non-fatal",
395 error_code, vendor_id);
396 }
397 break;
398 }
399 case PB_MSG_LANGUAGE_PREFERENCE:
400 {
401 pb_language_preference_msg_t *lang_msg;
402 chunk_t lang;
403
404 lang_msg = (pb_language_preference_msg_t*)msg;
405 lang = lang_msg->get_language_preference(lang_msg);
406
407 if (this->recs)
408 {
409 DBG2(DBG_TNC, "setting language preference to '%.*s'",
410 (int)lang.len, lang.ptr);
411 this->recs->set_preferred_language(this->recs, lang);
412 }
413 break;
414 }
415 case PB_MSG_REASON_STRING:
416 {
417 pb_reason_string_msg_t *reason_msg;
418 chunk_t reason_string, language_code;
419
420 reason_msg = (pb_reason_string_msg_t*)msg;
421 reason_string = reason_msg->get_reason_string(reason_msg);
422 language_code = reason_msg->get_language_code(reason_msg);
423 DBG1(DBG_TNC, "reason string is '%.*s' [%.*s]",
424 (int)reason_string.len, reason_string.ptr,
425 (int)language_code.len, language_code.ptr);
426 break;
427 }
428 default:
429 break;
430 }
431 }
432
433 /**
434 * Build a CRETRY or SRETRY batch
435 */
436 static void build_retry_batch(private_tnccs_20_t *this)
437 {
438 pb_tnc_batch_type_t batch_retry_type;
439
440 batch_retry_type = this->is_server ? PB_BATCH_SRETRY : PB_BATCH_CRETRY;
441 if (this->batch_type == batch_retry_type)
442 {
443 /* retry batch has already been selected */
444 return;
445 }
446
447 change_batch_type(this, batch_retry_type);
448
449 if (this->is_server)
450 {
451 this->recs->clear_recommendation(this->recs);
452 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
453 TNC_CONNECTION_STATE_HANDSHAKE);
454 }
455 }
456
457 METHOD(tls_t, process, status_t,
458 private_tnccs_20_t *this, void *buf, size_t buflen)
459 {
460 chunk_t data;
461 pb_tnc_batch_t *batch;
462 pb_tnc_msg_t *msg;
463 enumerator_t *enumerator;
464 status_t status;
465
466 if (this->is_server && !this->connection_id)
467 {
468 this->connection_id = tnc->tnccs->create_connection(tnc->tnccs,
469 TNCCS_2_0, (tnccs_t*)this, _send_msg,
470 &this->request_handshake_retry,
471 this->max_msg_len, &this->recs);
472 if (!this->connection_id)
473 {
474 return FAILED;
475 }
476 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
477 TNC_CONNECTION_STATE_CREATE);
478 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
479 TNC_CONNECTION_STATE_HANDSHAKE);
480 }
481
482 data = chunk_create(buf, buflen);
483 DBG1(DBG_TNC, "received TNCCS batch (%u bytes) for Connection ID %u",
484 data.len, this->connection_id);
485 DBG3(DBG_TNC, "%B", &data);
486 batch = pb_tnc_batch_create_from_data(this->is_server, data);
487 status = batch->process(batch, this->state_machine);
488
489 if (status != FAILED)
490 {
491 enumerator_t *enumerator;
492 pb_tnc_msg_t *msg;
493 pb_tnc_batch_type_t batch_type;
494 bool empty = TRUE;
495
496 batch_type = batch->get_type(batch);
497
498 if (batch_type == PB_BATCH_CRETRY)
499 {
500 /* Send an SRETRY batch in response */
501 this->mutex->lock(this->mutex);
502 build_retry_batch(this);
503 this->mutex->unlock(this->mutex);
504 }
505 else if (batch_type == PB_BATCH_SRETRY)
506 {
507 /* Restart the measurements */
508 tnc->imcs->notify_connection_change(tnc->imcs,
509 this->connection_id, TNC_CONNECTION_STATE_HANDSHAKE);
510 this->send_msg = TRUE;
511 tnc->imcs->begin_handshake(tnc->imcs, this->connection_id);
512 this->send_msg = FALSE;
513 }
514
515 enumerator = batch->create_msg_enumerator(batch);
516 while (enumerator->enumerate(enumerator, &msg))
517 {
518 handle_message(this, msg);
519 empty = FALSE;
520 }
521 enumerator->destroy(enumerator);
522
523 /* received an empty CLOSE batch from PB-TNC client */
524 if (this->is_server && batch_type == PB_BATCH_CLOSE && empty)
525 {
526 batch->destroy(batch);
527 if (this->fatal_error)
528 {
529 DBG1(DBG_TNC, "a fatal PB-TNC error occurred, "
530 "terminating connection");
531 return FAILED;
532 }
533 else
534 {
535 return SUCCESS;
536 }
537 }
538
539 this->send_msg = TRUE;
540 if (this->is_server)
541 {
542 tnc->imvs->batch_ending(tnc->imvs, this->connection_id);
543 }
544 else
545 {
546 tnc->imcs->batch_ending(tnc->imcs, this->connection_id);
547 }
548 this->send_msg = FALSE;
549 }
550
551 switch (status)
552 {
553 case FAILED:
554 this->fatal_error = TRUE;
555 this->mutex->lock(this->mutex);
556 change_batch_type(this, PB_BATCH_CLOSE);
557 this->mutex->unlock(this->mutex);
558 /* fall through to add error messages to outbound batch */
559 case VERIFY_ERROR:
560 enumerator = batch->create_error_enumerator(batch);
561 while (enumerator->enumerate(enumerator, &msg))
562 {
563 this->mutex->lock(this->mutex);
564 this->messages->insert_last(this->messages, msg->get_ref(msg));
565 this->mutex->unlock(this->mutex);
566 }
567 enumerator->destroy(enumerator);
568 break;
569 case SUCCESS:
570 default:
571 break;
572 }
573 batch->destroy(batch);
574
575 return NEED_MORE;
576 }
577
578 /**
579 * Build a RESULT batch if a final recommendation is available
580 */
581 static void check_and_build_recommendation(private_tnccs_20_t *this)
582 {
583 TNC_IMV_Action_Recommendation rec;
584 TNC_IMV_Evaluation_Result eval;
585 TNC_ConnectionState state;
586 TNC_IMVID id;
587 chunk_t reason, language;
588 enumerator_t *enumerator;
589 pb_tnc_msg_t *msg;
590 pb_access_recommendation_code_t pb_rec;
591
592 if (!this->recs->have_recommendation(this->recs, &rec, &eval))
593 {
594 tnc->imvs->solicit_recommendation(tnc->imvs, this->connection_id);
595 }
596 if (this->recs->have_recommendation(this->recs, &rec, &eval))
597 {
598 this->batch_type = PB_BATCH_RESULT;
599
600 msg = pb_assessment_result_msg_create(eval);
601 this->messages->insert_last(this->messages, msg);
602
603 /**
604 * Map IMV Action Recommendation codes to PB Access Recommendation codes
605 * and communicate Access Recommendation to IMVs
606 */
607 switch (rec)
608 {
609 case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
610 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
611 pb_rec = PB_REC_ACCESS_ALLOWED;
612 break;
613 case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
614 state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
615 pb_rec = PB_REC_QUARANTINED;
616 break;
617 case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
618 case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
619 default:
620 state = TNC_CONNECTION_STATE_ACCESS_NONE;
621 pb_rec = PB_REC_ACCESS_DENIED;
622 }
623 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
624 state);
625
626 msg = pb_access_recommendation_msg_create(pb_rec);
627 this->messages->insert_last(this->messages, msg);
628
629 enumerator = this->recs->create_reason_enumerator(this->recs);
630 while (enumerator->enumerate(enumerator, &id, &reason, &language))
631 {
632 msg = pb_reason_string_msg_create(reason, language);
633 this->messages->insert_last(this->messages, msg);
634 }
635 enumerator->destroy(enumerator);
636 }
637 }
638
639 METHOD(tls_t, build, status_t,
640 private_tnccs_20_t *this, void *buf, size_t *buflen, size_t *msglen)
641 {
642 status_t status;
643 pb_tnc_state_t state;
644
645 /* Initialize the connection */
646 if (!this->is_server && !this->connection_id)
647 {
648 pb_tnc_msg_t *msg;
649 char *pref_lang;
650
651 this->connection_id = tnc->tnccs->create_connection(tnc->tnccs,
652 TNCCS_2_0, (tnccs_t*)this, _send_msg,
653 &this->request_handshake_retry,
654 this->max_msg_len, NULL);
655 if (!this->connection_id)
656 {
657 return FAILED;
658 }
659
660 /* Create PB-TNC Language Preference message */
661 pref_lang = tnc->imcs->get_preferred_language(tnc->imcs);
662 msg = pb_language_preference_msg_create(chunk_create(pref_lang,
663 strlen(pref_lang)));
664 this->mutex->lock(this->mutex);
665 this->batch_type = PB_BATCH_CDATA;
666 this->messages->insert_last(this->messages, msg);
667 this->mutex->unlock(this->mutex);
668
669 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
670 TNC_CONNECTION_STATE_CREATE);
671 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
672 TNC_CONNECTION_STATE_HANDSHAKE);
673 this->send_msg = TRUE;
674 tnc->imcs->begin_handshake(tnc->imcs, this->connection_id);
675 this->send_msg = FALSE;
676 }
677
678 state = this->state_machine->get_state(this->state_machine);
679
680 if (this->fatal_error && state == PB_STATE_END)
681 {
682 DBG1(DBG_TNC, "a fatal PB-TNC error occurred, terminating connection");
683 return FAILED;
684 }
685
686 /* Do not allow any asynchronous IMCs or IMVs to add additional messages */
687 this->mutex->lock(this->mutex);
688
689 if (this->request_handshake_retry)
690 {
691 if (state != PB_STATE_INIT)
692 {
693 build_retry_batch(this);
694 }
695
696 /* Reset the flag for the next handshake retry request */
697 this->request_handshake_retry = FALSE;
698 }
699
700 if (this->is_server && state == PB_STATE_SERVER_WORKING &&
701 this->recs->have_recommendation(this->recs, NULL, NULL))
702 {
703 check_and_build_recommendation(this);
704 }
705
706 if (this->batch_type == PB_BATCH_NONE)
707 {
708 if (this->is_server)
709 {
710 if (state == PB_STATE_SERVER_WORKING)
711 {
712 if (this->state_machine->get_empty_cdata(this->state_machine))
713 {
714 check_and_build_recommendation(this);
715 }
716 else
717 {
718 DBG2(DBG_TNC, "no recommendation available yet, "
719 "sending empty PB-TNC SDATA batch");
720 this->batch_type = PB_BATCH_SDATA;
721 }
722 }
723 }
724 else
725 {
726 switch (state)
727 {
728 case PB_STATE_CLIENT_WORKING:
729 DBG2(DBG_TNC, "no client data to send, "
730 "sending empty PB-TNC CDATA batch");
731 this->batch_type = PB_BATCH_CDATA;
732 break;
733 case PB_STATE_DECIDED:
734 /**
735 * In the DECIDED state and if no CRETRY is under way,
736 * a PB-TNC client replies with an empty CLOSE batch.
737 */
738 this->batch_type = PB_BATCH_CLOSE;
739 break;
740 default:
741 break;
742 }
743 }
744 }
745
746 if (this->batch_type != PB_BATCH_NONE)
747 {
748 pb_tnc_batch_t *batch;
749 pb_tnc_msg_t *msg;
750 chunk_t data;
751 int msg_count;
752 enumerator_t *enumerator;
753
754 if (this->state_machine->send_batch(this->state_machine, this->batch_type))
755 {
756 batch = pb_tnc_batch_create(this->is_server, this->batch_type,
757 min(this->max_batch_len, *buflen));
758
759 enumerator = this->messages->create_enumerator(this->messages);
760 while (enumerator->enumerate(enumerator, &msg))
761 {
762 if (batch->add_msg(batch, msg))
763 {
764 this->messages->remove_at(this->messages, enumerator);
765 }
766 else
767 {
768 break;
769 }
770 }
771 enumerator->destroy(enumerator);
772
773 batch->build(batch);
774 data = batch->get_encoding(batch);
775 DBG1(DBG_TNC, "sending PB-TNC %N batch (%d bytes) for Connection ID %u",
776 pb_tnc_batch_type_names, this->batch_type, data.len,
777 this->connection_id);
778 DBG3(DBG_TNC, "%B", &data);
779
780 *buflen = data.len;
781 *msglen = 0;
782 memcpy(buf, data.ptr, *buflen);
783 batch->destroy(batch);
784
785 msg_count = this->messages->get_count(this->messages);
786 if (msg_count)
787 {
788 DBG2(DBG_TNC, "queued %d PB-TNC message%s for next %N batch",
789 msg_count, (msg_count == 1) ? "" : "s",
790 pb_tnc_batch_type_names, this->batch_type);
791 }
792 else
793 {
794 this->batch_type = PB_BATCH_NONE;
795 }
796
797 status = ALREADY_DONE;
798 }
799 else
800 {
801 change_batch_type(this, PB_BATCH_NONE);
802 status = INVALID_STATE;
803 }
804 }
805 else
806 {
807 DBG1(DBG_TNC, "no PB-TNC batch to send");
808 status = INVALID_STATE;
809 }
810 this->mutex->unlock(this->mutex);
811
812 return status;
813 }
814
815 METHOD(tls_t, is_server, bool,
816 private_tnccs_20_t *this)
817 {
818 return this->is_server;
819 }
820
821 METHOD(tls_t, get_server_id, identification_t*,
822 private_tnccs_20_t *this)
823 {
824 return this->server;
825 }
826
827 METHOD(tls_t, get_peer_id, identification_t*,
828 private_tnccs_20_t *this)
829 {
830 return this->peer;
831 }
832
833 METHOD(tls_t, get_purpose, tls_purpose_t,
834 private_tnccs_20_t *this)
835 {
836 return TLS_PURPOSE_EAP_TNC;
837 }
838
839 METHOD(tls_t, is_complete, bool,
840 private_tnccs_20_t *this)
841 {
842 TNC_IMV_Action_Recommendation rec;
843 TNC_IMV_Evaluation_Result eval;
844
845 if (this->recs && this->recs->have_recommendation(this->recs, &rec, &eval))
846 {
847 return tnc->imvs->enforce_recommendation(tnc->imvs, rec, eval);
848 }
849 else
850 {
851 return FALSE;
852 }
853 }
854
855 METHOD(tls_t, get_eap_msk, chunk_t,
856 private_tnccs_20_t *this)
857 {
858 return chunk_empty;
859 }
860
861 METHOD(tls_t, destroy, void,
862 private_tnccs_20_t *this)
863 {
864 tnc->tnccs->remove_connection(tnc->tnccs, this->connection_id,
865 this->is_server);
866 this->server->destroy(this->server);
867 this->peer->destroy(this->peer);
868 this->state_machine->destroy(this->state_machine);
869 this->mutex->destroy(this->mutex);
870 this->messages->destroy_offset(this->messages,
871 offsetof(pb_tnc_msg_t, destroy));
872 free(this);
873 }
874
875 METHOD(tnccs_t, get_transport, tnc_ift_type_t,
876 private_tnccs_20_t *this)
877 {
878 return this->transport;
879 }
880
881 METHOD(tnccs_t, set_transport, void,
882 private_tnccs_20_t *this, tnc_ift_type_t transport)
883 {
884 this->transport = transport;
885 }
886
887 METHOD(tnccs_t, get_auth_type, u_int32_t,
888 private_tnccs_20_t *this)
889 {
890 return this->auth_type;
891 }
892
893 METHOD(tnccs_t, set_auth_type, void,
894 private_tnccs_20_t *this, u_int32_t auth_type)
895 {
896 this->auth_type = auth_type;
897 }
898
899 /**
900 * See header
901 */
902 tnccs_t* tnccs_20_create(bool is_server,
903 identification_t *server,
904 identification_t *peer,
905 tnc_ift_type_t transport)
906 {
907 private_tnccs_20_t *this;
908
909 INIT(this,
910 .public = {
911 .tls = {
912 .process = _process,
913 .build = _build,
914 .is_server = _is_server,
915 .get_server_id = _get_server_id,
916 .get_peer_id = _get_peer_id,
917 .get_purpose = _get_purpose,
918 .is_complete = _is_complete,
919 .get_eap_msk = _get_eap_msk,
920 .destroy = _destroy,
921 },
922 .get_transport = _get_transport,
923 .set_transport = _set_transport,
924 .get_auth_type = _get_auth_type,
925 .set_auth_type = _set_auth_type,
926 },
927 .is_server = is_server,
928 .server = server->clone(server),
929 .peer = peer->clone(peer),
930 .transport = transport,
931 .state_machine = pb_tnc_state_machine_create(is_server),
932 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
933 .messages = linked_list_create(),
934 .max_batch_len = lib->settings->get_int(lib->settings,
935 "libtnccs.plugins.tnccs-20.max_batch_size", 65522),
936 .max_msg_len = lib->settings->get_int(lib->settings,
937 "libtnccs.plugins.tnccs-20.max_message_size", 65490),
938 );
939
940 return &this->public;
941 }