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