6759c92a3a3a1767a614f0f54ee256c564d42e92
[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/ietf/pb_pa_msg.h"
21 #include "messages/ietf/pb_error_msg.h"
22 #include "messages/ietf/pb_assessment_result_msg.h"
23 #include "messages/ietf/pb_access_recommendation_msg.h"
24 #include "messages/ietf/pb_remediation_parameters_msg.h"
25 #include "messages/ietf/pb_reason_string_msg.h"
26 #include "messages/ietf/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 * Callback function to communicate recommendation (TNC Server only)
136 */
137 tnccs_cb_t callback;
138
139 /**
140 * Data to pass to callback function (TNC Server only)
141 */
142 void *cb_data;
143
144 };
145
146 /**
147 * If the batch type changes then delete all accumulated PB-TNC messages
148 */
149 void change_batch_type(private_tnccs_20_t *this, pb_tnc_batch_type_t batch_type)
150 {
151 pb_tnc_msg_t *msg;
152
153 if (batch_type != this->batch_type)
154 {
155 if (this->batch_type != PB_BATCH_NONE)
156 {
157 DBG1(DBG_TNC, "cancelling PB-TNC %N batch",
158 pb_tnc_batch_type_names, this->batch_type);
159
160 while (this->messages->remove_last(this->messages,
161 (void**)&msg) == SUCCESS)
162 {
163 msg->destroy(msg);
164 }
165 }
166 this->batch_type = batch_type;
167 }
168 }
169
170 METHOD(tnccs_t, send_msg, TNC_Result,
171 private_tnccs_20_t* this, TNC_IMCID imc_id, TNC_IMVID imv_id,
172 TNC_UInt32 msg_flags,
173 TNC_BufferReference msg,
174 TNC_UInt32 msg_len,
175 TNC_VendorID msg_vid,
176 TNC_MessageSubtype msg_subtype)
177 {
178 pb_tnc_msg_t *pb_tnc_msg;
179 pb_tnc_batch_type_t batch_type;
180 enum_name_t *pa_subtype_names;
181 bool excl;
182
183 if (!this->send_msg)
184 {
185 DBG1(DBG_TNC, "%s %u not allowed to call SendMessage()",
186 this->is_server ? "IMV" : "IMC",
187 this->is_server ? imv_id : imc_id);
188 return TNC_RESULT_ILLEGAL_OPERATION;
189 }
190 excl = (msg_flags & TNC_MESSAGE_FLAGS_EXCLUSIVE) != 0;
191
192 pb_tnc_msg = pb_pa_msg_create(msg_vid, msg_subtype, imc_id, imv_id,
193 excl, chunk_create(msg, msg_len));
194
195 pa_subtype_names = get_pa_subtype_names(msg_vid);
196 if (pa_subtype_names)
197 {
198 DBG2(DBG_TNC, "creating PB-PA message type '%N/%N' 0x%06x/0x%08x",
199 pen_names, msg_vid, pa_subtype_names, msg_subtype,
200 msg_vid, msg_subtype);
201 }
202 else
203 {
204 DBG2(DBG_TNC, "creating PB-PA message type '%N' 0x%06x/0x%08x",
205 pen_names, msg_vid, msg_vid, msg_subtype);
206 }
207
208 /* adding PA message to SDATA or CDATA batch only */
209 batch_type = this->is_server ? PB_BATCH_SDATA : PB_BATCH_CDATA;
210 this->mutex->lock(this->mutex);
211 if (this->batch_type == PB_BATCH_NONE)
212 {
213 this->batch_type = batch_type;
214 }
215 if (this->batch_type == batch_type)
216 {
217 this->messages->insert_last(this->messages, pb_tnc_msg);
218 }
219 else
220 {
221 pb_tnc_msg->destroy(pb_tnc_msg);
222 }
223 this->mutex->unlock(this->mutex);
224 return TNC_RESULT_SUCCESS;
225 }
226
227 /**
228 * Handle a single PB-TNC IETF standard message according to its type
229 */
230 static void handle_ietf_message(private_tnccs_20_t *this, pb_tnc_msg_t *msg)
231 {
232 pen_type_t msg_type = msg->get_type(msg);
233
234 switch (msg_type.type)
235 {
236 case PB_MSG_EXPERIMENTAL:
237 /* nothing to do */
238 break;
239 case PB_MSG_PA:
240 {
241 pb_pa_msg_t *pa_msg;
242 pen_type_t msg_subtype;
243 u_int16_t imc_id, imv_id;
244 chunk_t msg_body;
245 bool excl;
246 enum_name_t *pa_subtype_names;
247
248 pa_msg = (pb_pa_msg_t*)msg;
249 msg_subtype = pa_msg->get_subtype(pa_msg);
250 msg_body = pa_msg->get_body(pa_msg);
251 imc_id = pa_msg->get_collector_id(pa_msg);
252 imv_id = pa_msg->get_validator_id(pa_msg);
253 excl = pa_msg->get_exclusive_flag(pa_msg);
254
255 pa_subtype_names = get_pa_subtype_names(msg_subtype.vendor_id);
256 if (pa_subtype_names)
257 {
258 DBG2(DBG_TNC, "handling PB-PA message type '%N/%N' 0x%06x/0x%08x",
259 pen_names, msg_subtype.vendor_id, pa_subtype_names,
260 msg_subtype.type, msg_subtype.vendor_id, msg_subtype.type);
261 }
262 else
263 {
264 DBG2(DBG_TNC, "handling PB-PA message type '%N' 0x%06x/0x%08x",
265 pen_names, msg_subtype.vendor_id, msg_subtype.vendor_id,
266 msg_subtype.type);
267 }
268
269 this->send_msg = TRUE;
270 if (this->is_server)
271 {
272 tnc->imvs->receive_message(tnc->imvs, this->connection_id,
273 excl, msg_body.ptr, msg_body.len,
274 msg_subtype.vendor_id,
275 msg_subtype.type, imc_id, imv_id);
276 }
277 else
278 {
279 tnc->imcs->receive_message(tnc->imcs, this->connection_id,
280 excl, msg_body.ptr, msg_body.len,
281 msg_subtype.vendor_id,
282 msg_subtype.type, imv_id, imc_id);
283 }
284 this->send_msg = FALSE;
285 break;
286 }
287 case PB_MSG_ASSESSMENT_RESULT:
288 {
289 pb_assessment_result_msg_t *assess_msg;
290 u_int32_t result;
291
292 assess_msg = (pb_assessment_result_msg_t*)msg;
293 result = assess_msg->get_assessment_result(assess_msg);
294 DBG1(DBG_TNC, "PB-TNC assessment result is '%N'",
295 TNC_IMV_Evaluation_Result_names, result);
296 break;
297 }
298 case PB_MSG_ACCESS_RECOMMENDATION:
299 {
300 pb_access_recommendation_msg_t *rec_msg;
301 pb_access_recommendation_code_t rec;
302 TNC_ConnectionState state = TNC_CONNECTION_STATE_ACCESS_NONE;
303
304 rec_msg = (pb_access_recommendation_msg_t*)msg;
305 rec = rec_msg->get_access_recommendation(rec_msg);
306 DBG1(DBG_TNC, "PB-TNC access recommendation is '%N'",
307 pb_access_recommendation_code_names, rec);
308 switch (rec)
309 {
310 case PB_REC_ACCESS_ALLOWED:
311 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
312 break;
313 case PB_REC_ACCESS_DENIED:
314 state = TNC_CONNECTION_STATE_ACCESS_NONE;
315 break;
316 case PB_REC_QUARANTINED:
317 state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
318 }
319 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
320 state);
321 break;
322 }
323 case PB_MSG_REMEDIATION_PARAMETERS:
324 {
325 pb_remediation_parameters_msg_t *rem_msg;
326 pen_type_t parameters_type;
327 chunk_t parameters, string, lang_code;
328
329 rem_msg = (pb_remediation_parameters_msg_t*)msg;
330 parameters_type = rem_msg->get_parameters_type(rem_msg);
331 parameters = rem_msg->get_parameters(rem_msg);
332
333 if (parameters_type.vendor_id == PEN_IETF)
334 {
335 switch (parameters_type.type)
336 {
337 case PB_REMEDIATION_URI:
338 DBG1(DBG_TNC, "remediation uri: %.*s",
339 parameters.len, parameters.ptr);
340 break;
341 case PB_REMEDIATION_STRING:
342 string = rem_msg->get_string(rem_msg, &lang_code);
343 DBG1(DBG_TNC, "remediation string: [%.*s]\n%.*s",
344 lang_code.len, lang_code.ptr,
345 string.len, string.ptr);
346 break;
347 default:
348 DBG1(DBG_TNC, "remediation parameters: %B", &parameters);
349 }
350 }
351 else
352 {
353 DBG1(DBG_TNC, "remediation parameters: %B", &parameters);
354 }
355 break;
356 }
357 case PB_MSG_ERROR:
358 {
359 pb_error_msg_t *err_msg;
360 bool fatal;
361 u_int32_t vendor_id;
362 u_int16_t error_code;
363
364 err_msg = (pb_error_msg_t*)msg;
365 fatal = err_msg->get_fatal_flag(err_msg);
366 vendor_id = err_msg->get_vendor_id(err_msg);
367 error_code = err_msg->get_error_code(err_msg);
368
369 if (fatal)
370 {
371 this->fatal_error = TRUE;
372 }
373
374 if (vendor_id == PEN_IETF)
375 {
376 switch (error_code)
377 {
378 case PB_ERROR_INVALID_PARAMETER:
379 case PB_ERROR_UNSUPPORTED_MANDATORY_MSG:
380 DBG1(DBG_TNC, "received %s PB-TNC error '%N' "
381 "(offset %u bytes)",
382 fatal ? "fatal" : "non-fatal",
383 pb_tnc_error_code_names, error_code,
384 err_msg->get_offset(err_msg));
385 break;
386 case PB_ERROR_VERSION_NOT_SUPPORTED:
387 DBG1(DBG_TNC, "received %s PB-TNC error '%N' "
388 "caused by bad version 0x%02x",
389 fatal ? "fatal" : "non-fatal",
390 pb_tnc_error_code_names, error_code,
391 err_msg->get_bad_version(err_msg));
392 break;
393 case PB_ERROR_UNEXPECTED_BATCH_TYPE:
394 case PB_ERROR_LOCAL_ERROR:
395 default:
396 DBG1(DBG_TNC, "received %s PB-TNC error '%N'",
397 fatal ? "fatal" : "non-fatal",
398 pb_tnc_error_code_names, error_code);
399 break;
400 }
401 }
402 else
403 {
404 DBG1(DBG_TNC, "received %s PB-TNC error (%u) "
405 "with Vendor ID 0x%06x",
406 fatal ? "fatal" : "non-fatal",
407 error_code, vendor_id);
408 }
409 break;
410 }
411 case PB_MSG_LANGUAGE_PREFERENCE:
412 {
413 pb_language_preference_msg_t *lang_msg;
414 chunk_t lang;
415
416 lang_msg = (pb_language_preference_msg_t*)msg;
417 lang = lang_msg->get_language_preference(lang_msg);
418
419 if (this->recs)
420 {
421 DBG2(DBG_TNC, "setting language preference to '%.*s'",
422 (int)lang.len, lang.ptr);
423 this->recs->set_preferred_language(this->recs, lang);
424 }
425 break;
426 }
427 case PB_MSG_REASON_STRING:
428 {
429 pb_reason_string_msg_t *reason_msg;
430 chunk_t reason_string, language_code;
431
432 reason_msg = (pb_reason_string_msg_t*)msg;
433 reason_string = reason_msg->get_reason_string(reason_msg);
434 language_code = reason_msg->get_language_code(reason_msg);
435 DBG1(DBG_TNC, "reason string is '%.*s' [%.*s]",
436 (int)reason_string.len, reason_string.ptr,
437 (int)language_code.len, language_code.ptr);
438 break;
439 }
440 default:
441 break;
442 }
443 }
444
445 /**
446 * Handle a single PB-TNC TCG standard message according to its type
447 */
448 static void handle_tcg_message(private_tnccs_20_t *this, pb_tnc_msg_t *msg)
449 {
450 pen_type_t msg_type = msg->get_type(msg);
451
452 switch (msg_type.type)
453 {
454 case PB_TCG_MSG_PDP_REFERRAL:
455 /* TODO handle PDP Referral */
456 break;
457 default:
458 break;
459 }
460 }
461
462 /**
463 * Handle a single PB-TNC message according to its type
464 */
465 static void handle_message(private_tnccs_20_t *this, pb_tnc_msg_t *msg)
466 {
467 pen_type_t msg_type = msg->get_type(msg);
468
469 switch (msg_type.vendor_id)
470 {
471 case PEN_IETF:
472 handle_ietf_message(this, msg);
473 break;
474 case PEN_TCG:
475 handle_tcg_message(this, msg);
476 break;
477 default:
478 break;
479 }
480 }
481
482 /**
483 * Build a CRETRY or SRETRY batch
484 */
485 static void build_retry_batch(private_tnccs_20_t *this)
486 {
487 pb_tnc_batch_type_t batch_retry_type;
488
489 batch_retry_type = this->is_server ? PB_BATCH_SRETRY : PB_BATCH_CRETRY;
490 if (this->batch_type == batch_retry_type)
491 {
492 /* retry batch has already been selected */
493 return;
494 }
495
496 change_batch_type(this, batch_retry_type);
497
498 if (this->is_server)
499 {
500 this->recs->clear_recommendation(this->recs);
501 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
502 TNC_CONNECTION_STATE_HANDSHAKE);
503 }
504 }
505
506 METHOD(tls_t, process, status_t,
507 private_tnccs_20_t *this, void *buf, size_t buflen)
508 {
509 chunk_t data;
510 pb_tnc_batch_t *batch;
511 pb_tnc_msg_t *msg;
512 enumerator_t *enumerator;
513 status_t status;
514
515 if (this->is_server && !this->connection_id)
516 {
517 this->connection_id = tnc->tnccs->create_connection(tnc->tnccs,
518 TNCCS_2_0, (tnccs_t*)this, _send_msg,
519 &this->request_handshake_retry,
520 this->max_msg_len, &this->recs);
521 if (!this->connection_id)
522 {
523 return FAILED;
524 }
525 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
526 TNC_CONNECTION_STATE_CREATE);
527 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
528 TNC_CONNECTION_STATE_HANDSHAKE);
529 }
530
531 data = chunk_create(buf, buflen);
532 DBG1(DBG_TNC, "received TNCCS batch (%u bytes) for Connection ID %u",
533 data.len, this->connection_id);
534 DBG3(DBG_TNC, "%B", &data);
535 batch = pb_tnc_batch_create_from_data(this->is_server, data);
536 status = batch->process(batch, this->state_machine);
537
538 if (status != FAILED)
539 {
540 enumerator_t *enumerator;
541 pb_tnc_msg_t *msg;
542 pb_tnc_batch_type_t batch_type;
543 bool empty = TRUE;
544
545 batch_type = batch->get_type(batch);
546
547 if (batch_type == PB_BATCH_CRETRY)
548 {
549 /* Send an SRETRY batch in response */
550 this->mutex->lock(this->mutex);
551 build_retry_batch(this);
552 this->mutex->unlock(this->mutex);
553 }
554 else if (batch_type == PB_BATCH_SRETRY)
555 {
556 /* Restart the measurements */
557 tnc->imcs->notify_connection_change(tnc->imcs,
558 this->connection_id, TNC_CONNECTION_STATE_HANDSHAKE);
559 this->send_msg = TRUE;
560 tnc->imcs->begin_handshake(tnc->imcs, this->connection_id);
561 this->send_msg = FALSE;
562 }
563
564 enumerator = batch->create_msg_enumerator(batch);
565 while (enumerator->enumerate(enumerator, &msg))
566 {
567 handle_message(this, msg);
568 empty = FALSE;
569 }
570 enumerator->destroy(enumerator);
571
572 /* received an empty CLOSE batch from PB-TNC client */
573 if (this->is_server && batch_type == PB_BATCH_CLOSE && empty)
574 {
575 batch->destroy(batch);
576 if (this->fatal_error)
577 {
578 DBG1(DBG_TNC, "a fatal PB-TNC error occurred, "
579 "terminating connection");
580 return FAILED;
581 }
582 else
583 {
584 return SUCCESS;
585 }
586 }
587
588 this->send_msg = TRUE;
589 if (this->is_server)
590 {
591 tnc->imvs->batch_ending(tnc->imvs, this->connection_id);
592 }
593 else
594 {
595 tnc->imcs->batch_ending(tnc->imcs, this->connection_id);
596 }
597 this->send_msg = FALSE;
598 }
599
600 switch (status)
601 {
602 case FAILED:
603 this->fatal_error = TRUE;
604 this->mutex->lock(this->mutex);
605 change_batch_type(this, PB_BATCH_CLOSE);
606 this->mutex->unlock(this->mutex);
607 /* fall through to add error messages to outbound batch */
608 case VERIFY_ERROR:
609 enumerator = batch->create_error_enumerator(batch);
610 while (enumerator->enumerate(enumerator, &msg))
611 {
612 this->mutex->lock(this->mutex);
613 this->messages->insert_last(this->messages, msg->get_ref(msg));
614 this->mutex->unlock(this->mutex);
615 }
616 enumerator->destroy(enumerator);
617 break;
618 case SUCCESS:
619 default:
620 break;
621 }
622 batch->destroy(batch);
623
624 return NEED_MORE;
625 }
626
627 /**
628 * Build a RESULT batch if a final recommendation is available
629 */
630 static void check_and_build_recommendation(private_tnccs_20_t *this)
631 {
632 TNC_IMV_Action_Recommendation rec;
633 TNC_IMV_Evaluation_Result eval;
634 TNC_ConnectionState state;
635 TNC_IMVID id;
636 chunk_t reason, language;
637 enumerator_t *enumerator;
638 pb_tnc_msg_t *msg;
639 pb_access_recommendation_code_t pb_rec;
640
641 if (!this->recs->have_recommendation(this->recs, &rec, &eval))
642 {
643 tnc->imvs->solicit_recommendation(tnc->imvs, this->connection_id);
644 }
645 if (this->recs->have_recommendation(this->recs, &rec, &eval))
646 {
647 this->batch_type = PB_BATCH_RESULT;
648
649 msg = pb_assessment_result_msg_create(eval);
650 this->messages->insert_last(this->messages, msg);
651
652 /**
653 * Map IMV Action Recommendation codes to PB Access Recommendation codes
654 * and communicate Access Recommendation to IMVs
655 */
656 switch (rec)
657 {
658 case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
659 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
660 pb_rec = PB_REC_ACCESS_ALLOWED;
661 break;
662 case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
663 state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
664 pb_rec = PB_REC_QUARANTINED;
665 break;
666 case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
667 case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
668 default:
669 state = TNC_CONNECTION_STATE_ACCESS_NONE;
670 pb_rec = PB_REC_ACCESS_DENIED;
671 }
672 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
673 state);
674
675 msg = pb_access_recommendation_msg_create(pb_rec);
676 this->messages->insert_last(this->messages, msg);
677
678 enumerator = this->recs->create_reason_enumerator(this->recs);
679 while (enumerator->enumerate(enumerator, &id, &reason, &language))
680 {
681 msg = pb_reason_string_msg_create(reason, language);
682 this->messages->insert_last(this->messages, msg);
683 }
684 enumerator->destroy(enumerator);
685 }
686 }
687
688 METHOD(tls_t, build, status_t,
689 private_tnccs_20_t *this, void *buf, size_t *buflen, size_t *msglen)
690 {
691 status_t status;
692 pb_tnc_state_t state;
693
694 /* Initialize the connection */
695 if (!this->is_server && !this->connection_id)
696 {
697 pb_tnc_msg_t *msg;
698 char *pref_lang;
699
700 this->connection_id = tnc->tnccs->create_connection(tnc->tnccs,
701 TNCCS_2_0, (tnccs_t*)this, _send_msg,
702 &this->request_handshake_retry,
703 this->max_msg_len, NULL);
704 if (!this->connection_id)
705 {
706 return FAILED;
707 }
708
709 /* Create PB-TNC Language Preference message */
710 pref_lang = tnc->imcs->get_preferred_language(tnc->imcs);
711 msg = pb_language_preference_msg_create(chunk_create(pref_lang,
712 strlen(pref_lang)));
713 this->mutex->lock(this->mutex);
714 this->batch_type = PB_BATCH_CDATA;
715 this->messages->insert_last(this->messages, msg);
716 this->mutex->unlock(this->mutex);
717
718 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
719 TNC_CONNECTION_STATE_CREATE);
720 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
721 TNC_CONNECTION_STATE_HANDSHAKE);
722 this->send_msg = TRUE;
723 tnc->imcs->begin_handshake(tnc->imcs, this->connection_id);
724 this->send_msg = FALSE;
725 }
726
727 state = this->state_machine->get_state(this->state_machine);
728
729 if (this->fatal_error && state == PB_STATE_END)
730 {
731 DBG1(DBG_TNC, "a fatal PB-TNC error occurred, terminating connection");
732 return FAILED;
733 }
734
735 /* Do not allow any asynchronous IMCs or IMVs to add additional messages */
736 this->mutex->lock(this->mutex);
737
738 if (this->request_handshake_retry)
739 {
740 if (state != PB_STATE_INIT)
741 {
742 build_retry_batch(this);
743 }
744
745 /* Reset the flag for the next handshake retry request */
746 this->request_handshake_retry = FALSE;
747 }
748
749 if (this->is_server && state == PB_STATE_SERVER_WORKING &&
750 this->recs->have_recommendation(this->recs, NULL, NULL))
751 {
752 check_and_build_recommendation(this);
753 }
754
755 if (this->batch_type == PB_BATCH_NONE)
756 {
757 if (this->is_server)
758 {
759 if (state == PB_STATE_SERVER_WORKING)
760 {
761 if (this->state_machine->get_empty_cdata(this->state_machine))
762 {
763 check_and_build_recommendation(this);
764 }
765 else
766 {
767 DBG2(DBG_TNC, "no recommendation available yet, "
768 "sending empty PB-TNC SDATA batch");
769 this->batch_type = PB_BATCH_SDATA;
770 }
771 }
772 }
773 else
774 {
775 switch (state)
776 {
777 case PB_STATE_CLIENT_WORKING:
778 DBG2(DBG_TNC, "no client data to send, "
779 "sending empty PB-TNC CDATA batch");
780 this->batch_type = PB_BATCH_CDATA;
781 break;
782 case PB_STATE_DECIDED:
783 /**
784 * In the DECIDED state and if no CRETRY is under way,
785 * a PB-TNC client replies with an empty CLOSE batch.
786 */
787 this->batch_type = PB_BATCH_CLOSE;
788 break;
789 default:
790 break;
791 }
792 }
793 }
794
795 if (this->batch_type != PB_BATCH_NONE)
796 {
797 pb_tnc_batch_t *batch;
798 pb_tnc_msg_t *msg;
799 chunk_t data;
800 int msg_count;
801 enumerator_t *enumerator;
802
803 if (this->state_machine->send_batch(this->state_machine, this->batch_type))
804 {
805 batch = pb_tnc_batch_create(this->is_server, this->batch_type,
806 min(this->max_batch_len, *buflen));
807
808 enumerator = this->messages->create_enumerator(this->messages);
809 while (enumerator->enumerate(enumerator, &msg))
810 {
811 if (batch->add_msg(batch, msg))
812 {
813 this->messages->remove_at(this->messages, enumerator);
814 }
815 else
816 {
817 break;
818 }
819 }
820 enumerator->destroy(enumerator);
821
822 batch->build(batch);
823 data = batch->get_encoding(batch);
824 DBG1(DBG_TNC, "sending PB-TNC %N batch (%d bytes) for Connection ID %u",
825 pb_tnc_batch_type_names, this->batch_type, data.len,
826 this->connection_id);
827 DBG3(DBG_TNC, "%B", &data);
828
829 *buflen = data.len;
830 *msglen = 0;
831 memcpy(buf, data.ptr, *buflen);
832 batch->destroy(batch);
833
834 msg_count = this->messages->get_count(this->messages);
835 if (msg_count)
836 {
837 DBG2(DBG_TNC, "queued %d PB-TNC message%s for next %N batch",
838 msg_count, (msg_count == 1) ? "" : "s",
839 pb_tnc_batch_type_names, this->batch_type);
840 }
841 else
842 {
843 this->batch_type = PB_BATCH_NONE;
844 }
845
846 status = ALREADY_DONE;
847 }
848 else
849 {
850 change_batch_type(this, PB_BATCH_NONE);
851 status = INVALID_STATE;
852 }
853 }
854 else
855 {
856 DBG1(DBG_TNC, "no PB-TNC batch to send");
857 status = INVALID_STATE;
858 }
859 this->mutex->unlock(this->mutex);
860
861 return status;
862 }
863
864 METHOD(tls_t, is_server, bool,
865 private_tnccs_20_t *this)
866 {
867 return this->is_server;
868 }
869
870 METHOD(tls_t, get_server_id, identification_t*,
871 private_tnccs_20_t *this)
872 {
873 return this->server;
874 }
875
876 METHOD(tls_t, set_peer_id, void,
877 private_tnccs_20_t *this, identification_t *id)
878 {
879 DESTROY_IF(this->peer);
880 this->peer = id->clone(id);
881 }
882
883 METHOD(tls_t, get_peer_id, identification_t*,
884 private_tnccs_20_t *this)
885 {
886 return this->peer;
887 }
888
889 METHOD(tls_t, get_purpose, tls_purpose_t,
890 private_tnccs_20_t *this)
891 {
892 return TLS_PURPOSE_EAP_TNC;
893 }
894
895 METHOD(tls_t, is_complete, bool,
896 private_tnccs_20_t *this)
897 {
898 TNC_IMV_Action_Recommendation rec;
899 TNC_IMV_Evaluation_Result eval;
900
901 if (this->recs && this->recs->have_recommendation(this->recs, &rec, &eval))
902 {
903 return this->callback ? this->callback(rec, eval) : TRUE;
904 }
905 else
906 {
907 return FALSE;
908 }
909 }
910
911 METHOD(tls_t, get_eap_msk, chunk_t,
912 private_tnccs_20_t *this)
913 {
914 return chunk_empty;
915 }
916
917 METHOD(tls_t, destroy, void,
918 private_tnccs_20_t *this)
919 {
920 tnc->tnccs->remove_connection(tnc->tnccs, this->connection_id,
921 this->is_server);
922 this->server->destroy(this->server);
923 this->peer->destroy(this->peer);
924 this->state_machine->destroy(this->state_machine);
925 this->mutex->destroy(this->mutex);
926 this->messages->destroy_offset(this->messages,
927 offsetof(pb_tnc_msg_t, destroy));
928 free(this);
929 }
930
931 METHOD(tnccs_t, get_transport, tnc_ift_type_t,
932 private_tnccs_20_t *this)
933 {
934 return this->transport;
935 }
936
937 METHOD(tnccs_t, set_transport, void,
938 private_tnccs_20_t *this, tnc_ift_type_t transport)
939 {
940 this->transport = transport;
941 }
942
943 METHOD(tnccs_t, get_auth_type, u_int32_t,
944 private_tnccs_20_t *this)
945 {
946 return this->auth_type;
947 }
948
949 METHOD(tnccs_t, set_auth_type, void,
950 private_tnccs_20_t *this, u_int32_t auth_type)
951 {
952 this->auth_type = auth_type;
953 }
954
955 /**
956 * See header
957 */
958 tnccs_t* tnccs_20_create(bool is_server,
959 identification_t *server, identification_t *peer,
960 tnc_ift_type_t transport, tnccs_cb_t cb)
961 {
962 private_tnccs_20_t *this;
963
964 INIT(this,
965 .public = {
966 .tls = {
967 .process = _process,
968 .build = _build,
969 .is_server = _is_server,
970 .get_server_id = _get_server_id,
971 .set_peer_id = _set_peer_id,
972 .get_peer_id = _get_peer_id,
973 .get_purpose = _get_purpose,
974 .is_complete = _is_complete,
975 .get_eap_msk = _get_eap_msk,
976 .destroy = _destroy,
977 },
978 .get_transport = _get_transport,
979 .set_transport = _set_transport,
980 .get_auth_type = _get_auth_type,
981 .set_auth_type = _set_auth_type,
982 },
983 .is_server = is_server,
984 .server = server->clone(server),
985 .peer = peer->clone(peer),
986 .transport = transport,
987 .callback = cb,
988 .state_machine = pb_tnc_state_machine_create(is_server),
989 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
990 .messages = linked_list_create(),
991 .max_batch_len = lib->settings->get_int(lib->settings,
992 "libtnccs.plugins.tnccs-20.max_batch_size", 65522),
993 .max_msg_len = lib->settings->get_int(lib->settings,
994 "libtnccs.plugins.tnccs-20.max_message_size", 65490),
995 );
996
997 return &this->public;
998 }