2 * Copyright (C) 2015 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 #include "tnccs_20_server.h"
17 #include "messages/pb_tnc_msg.h"
18 #include "messages/ietf/pb_pa_msg.h"
19 #include "messages/ietf/pb_error_msg.h"
20 #include "messages/ietf/pb_assessment_result_msg.h"
21 #include "messages/ietf/pb_access_recommendation_msg.h"
22 #include "messages/ietf/pb_remediation_parameters_msg.h"
23 #include "messages/ietf/pb_reason_string_msg.h"
24 #include "messages/ietf/pb_language_preference_msg.h"
25 #include "messages/ita/pb_mutual_capability_msg.h"
26 #include "messages/ita/pb_noskip_test_msg.h"
27 #include "messages/tcg/pb_pdp_referral_msg.h"
28 #include "state_machine/pb_tnc_state_machine.h"
30 #include <tncif_names.h>
31 #include <tncif_pa_subtypes.h>
34 #include <tnc/tnccs/tnccs_manager.h>
35 #include <tnc/imv/imv_manager.h>
37 #include <threading/mutex.h>
38 #include <utils/debug.h>
39 #include <collections/linked_list.h>
42 typedef struct private_tnccs_20_server_t private_tnccs_20_server_t
;
45 * Private data of a tnccs_20_server_t object.
47 struct private_tnccs_20_server_t
{
50 * Public tnccs_20_server_t interface.
52 tnccs_20_server_t
public;
55 * PB-TNC State Machine
57 pb_tnc_state_machine_t
*state_machine
;
60 * Connection ID assigned to this TNCCS connection
62 TNC_ConnectionID connection_id
;
65 * PB-TNC messages to be sent
67 linked_list_t
*messages
;
70 * Type of PB-TNC batch being constructed
72 pb_tnc_batch_type_t batch_type
;
75 * Maximum PB-TNC batch size
80 * Mutex locking the batch in construction
85 * Flag set while processing
90 * Flag set by IMC/IMV RequestHandshakeRetry() function
92 bool request_handshake_retry
;
95 * Flag set after sending SRETRY batch
100 * SendMessage() by IMV only allowed if flag is set
105 * Set of IMV recommendations
107 recommendations_t
*recs
;
110 * TNC IF-T transport protocol for EAP methods
115 * Mutual PB-TNC protocol enabled
120 * Mutual Capability message sent
122 bool sent_mutual_capability
;
127 * The following two functions are shared with the tnccs_20_server class
129 extern void tnccs_20_handle_ietf_error_msg(pb_tnc_msg_t
*msg
,
131 extern bool tnccs_20_handle_ita_mutual_capability_msg(pb_tnc_msg_t
*msg
);
134 * If the batch type changes then delete all accumulated PB-TNC messages
136 static void change_batch_type(private_tnccs_20_server_t
*this,
137 pb_tnc_batch_type_t batch_type
)
141 if (batch_type
!= this->batch_type
)
143 if (this->batch_type
!= PB_BATCH_NONE
)
145 DBG1(DBG_TNC
, "cancelling PB-TNC %N batch",
146 pb_tnc_batch_type_names
, this->batch_type
);
148 while (this->messages
->remove_last(this->messages
,
149 (void**)&msg
) == SUCCESS
)
154 this->batch_type
= batch_type
;
159 * Handle a single PB-TNC IETF standard message according to its type
161 static void handle_ietf_message(private_tnccs_20_server_t
*this, pb_tnc_msg_t
*msg
)
163 pen_type_t msg_type
= msg
->get_type(msg
);
165 switch (msg_type
.type
)
167 case PB_MSG_EXPERIMENTAL
:
173 pen_type_t msg_subtype
;
174 uint16_t imc_id
, imv_id
;
177 enum_name_t
*pa_subtype_names
;
179 pa_msg
= (pb_pa_msg_t
*)msg
;
180 msg_subtype
= pa_msg
->get_subtype(pa_msg
);
181 msg_body
= pa_msg
->get_body(pa_msg
);
182 imc_id
= pa_msg
->get_collector_id(pa_msg
);
183 imv_id
= pa_msg
->get_validator_id(pa_msg
);
184 excl
= pa_msg
->get_exclusive_flag(pa_msg
);
186 pa_subtype_names
= get_pa_subtype_names(msg_subtype
.vendor_id
);
187 if (pa_subtype_names
)
189 DBG2(DBG_TNC
, "handling PB-PA message type '%N/%N' 0x%06x/0x%08x",
190 pen_names
, msg_subtype
.vendor_id
, pa_subtype_names
,
191 msg_subtype
.type
, msg_subtype
.vendor_id
, msg_subtype
.type
);
195 DBG2(DBG_TNC
, "handling PB-PA message type '%N' 0x%06x/0x%08x",
196 pen_names
, msg_subtype
.vendor_id
, msg_subtype
.vendor_id
,
199 this->send_msg
= TRUE
;
200 tnc
->imvs
->receive_message(tnc
->imvs
, this->connection_id
,
201 excl
, msg_body
.ptr
, msg_body
.len
,
202 msg_subtype
.vendor_id
,
203 msg_subtype
.type
, imc_id
, imv_id
);
204 this->send_msg
= FALSE
;
208 tnccs_20_handle_ietf_error_msg(msg
, &this->fatal_error
);
210 case PB_MSG_LANGUAGE_PREFERENCE
:
212 pb_language_preference_msg_t
*lang_msg
;
215 lang_msg
= (pb_language_preference_msg_t
*)msg
;
216 lang
= lang_msg
->get_language_preference(lang_msg
);
217 DBG2(DBG_TNC
, "setting language preference to '%.*s'",
218 (int)lang
.len
, lang
.ptr
);
219 this->recs
->set_preferred_language(this->recs
, lang
);
228 * Handle a single PB-TNC ITA standard message according to its type
230 static void handle_ita_message(private_tnccs_20_server_t
*this, pb_tnc_msg_t
*msg
)
232 pen_type_t msg_type
= msg
->get_type(msg
);
234 switch (msg_type
.type
)
236 case PB_ITA_MSG_MUTUAL_CAPABILITY
:
237 this->mutual
= tnccs_20_handle_ita_mutual_capability_msg(msg
);
239 /* Respond with PB-TNC Mutual Capability message if necessary */
240 if (this->mutual
&& !this->sent_mutual_capability
)
242 msg
= pb_mutual_capability_msg_create(PB_MUTUAL_HALF_DUPLEX
);
243 this->mutex
->lock(this->mutex
);
244 this->messages
->insert_last(this->messages
, msg
);
245 this->mutex
->unlock(this->mutex
);
246 this->sent_mutual_capability
= TRUE
;
255 * Handle a single PB-TNC message according to its type
257 static void handle_message(private_tnccs_20_server_t
*this, pb_tnc_msg_t
*msg
)
259 pen_type_t msg_type
= msg
->get_type(msg
);
261 switch (msg_type
.vendor_id
)
264 handle_ietf_message(this, msg
);
267 handle_ita_message(this, msg
);
275 * Build an SRETRY batch
277 static void build_retry_batch(private_tnccs_20_server_t
*this)
279 if (this->batch_type
== PB_BATCH_SRETRY
)
281 /* retry batch has already been selected */
284 change_batch_type(this, PB_BATCH_SRETRY
);
286 this->recs
->clear_recommendation(this->recs
);
288 /* Handshake will be retried with next incoming CDATA batch */
289 this->retry_handshake
= TRUE
;
292 METHOD(tnccs_20_handler_t
, process
, status_t
,
293 private_tnccs_20_server_t
*this, pb_tnc_batch_t
*batch
)
295 pb_tnc_batch_type_t batch_type
;
298 batch_type
= batch
->get_type(batch
);
300 DBG1(DBG_TNC
, "processing PB-TNC %N batch for Connection ID %d",
301 pb_tnc_batch_type_names
, batch_type
, this->connection_id
);
302 status
= batch
->process(batch
, this->state_machine
);
304 if (status
!= FAILED
)
306 enumerator_t
*enumerator
;
310 if (batch_type
== PB_BATCH_CDATA
)
312 /* retry handshake after a previous SRETRY batch */
313 if (this->retry_handshake
)
315 tnc
->imvs
->notify_connection_change(tnc
->imvs
,
316 this->connection_id
, TNC_CONNECTION_STATE_HANDSHAKE
);
317 this->retry_handshake
= FALSE
;
320 else if (batch_type
== PB_BATCH_CRETRY
)
322 /* Send an SRETRY batch in response */
323 this->mutex
->lock(this->mutex
);
324 build_retry_batch(this);
325 this->mutex
->unlock(this->mutex
);
328 enumerator
= batch
->create_msg_enumerator(batch
);
329 while (enumerator
->enumerate(enumerator
, &msg
))
331 handle_message(this, msg
);
334 enumerator
->destroy(enumerator
);
336 /* received a CLOSE batch from PB-TNC client */
337 if (batch_type
== PB_BATCH_CLOSE
)
339 return empty
? SUCCESS
: FAILED
;
342 this->send_msg
= TRUE
;
343 tnc
->imvs
->batch_ending(tnc
->imvs
, this->connection_id
);
344 this->send_msg
= FALSE
;
350 this->fatal_error
= TRUE
;
351 status
= VERIFY_ERROR
;
365 * Build a RESULT batch if a final recommendation is available
367 static void check_and_build_recommendation(private_tnccs_20_server_t
*this)
369 TNC_IMV_Action_Recommendation rec
;
370 TNC_IMV_Evaluation_Result eval
;
371 TNC_ConnectionState state
;
373 chunk_t reason
, language
;
374 enumerator_t
*enumerator
;
376 pb_access_recommendation_code_t pb_rec
;
378 if (!this->recs
->have_recommendation(this->recs
, &rec
, &eval
))
380 tnc
->imvs
->solicit_recommendation(tnc
->imvs
, this->connection_id
);
382 if (this->recs
->have_recommendation(this->recs
, &rec
, &eval
))
384 this->batch_type
= PB_BATCH_RESULT
;
386 msg
= pb_assessment_result_msg_create(eval
);
387 this->messages
->insert_last(this->messages
, msg
);
390 * Map IMV Action Recommendation codes to PB Access Recommendation codes
391 * and communicate Access Recommendation to IMVs
395 case TNC_IMV_ACTION_RECOMMENDATION_ALLOW
:
396 state
= TNC_CONNECTION_STATE_ACCESS_ALLOWED
;
397 pb_rec
= PB_REC_ACCESS_ALLOWED
;
399 case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE
:
400 state
= TNC_CONNECTION_STATE_ACCESS_ISOLATED
;
401 pb_rec
= PB_REC_QUARANTINED
;
403 case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS
:
404 case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
:
406 state
= TNC_CONNECTION_STATE_ACCESS_NONE
;
407 pb_rec
= PB_REC_ACCESS_DENIED
;
409 tnc
->imvs
->notify_connection_change(tnc
->imvs
, this->connection_id
,
412 msg
= pb_access_recommendation_msg_create(pb_rec
);
413 this->messages
->insert_last(this->messages
, msg
);
415 enumerator
= this->recs
->create_reason_enumerator(this->recs
);
416 while (enumerator
->enumerate(enumerator
, &id
, &reason
, &language
))
418 msg
= pb_reason_string_msg_create(reason
, language
);
419 this->messages
->insert_last(this->messages
, msg
);
421 enumerator
->destroy(enumerator
);
425 METHOD(tnccs_20_handler_t
, build
, status_t
,
426 private_tnccs_20_server_t
*this, void *buf
, size_t *buflen
, size_t *msglen
)
429 pb_tnc_state_t state
;
431 state
= this->state_machine
->get_state(this->state_machine
);
433 if (this->fatal_error
&& state
== PB_STATE_END
)
435 DBG1(DBG_TNC
, "a fatal PB-TNC error occurred, terminating connection");
439 /* Do not allow any asynchronous IMVs to add additional messages */
440 this->mutex
->lock(this->mutex
);
442 if (this->request_handshake_retry
)
444 if (state
!= PB_STATE_INIT
)
446 build_retry_batch(this);
449 /* Reset the flag for the next handshake retry request */
450 this->request_handshake_retry
= FALSE
;
453 if (state
== PB_STATE_SERVER_WORKING
&&
454 this->recs
->have_recommendation(this->recs
, NULL
, NULL
))
456 check_and_build_recommendation(this);
459 if (this->batch_type
== PB_BATCH_NONE
)
461 if (state
== PB_STATE_SERVER_WORKING
)
463 if (this->state_machine
->get_empty_cdata(this->state_machine
))
465 check_and_build_recommendation(this);
469 DBG2(DBG_TNC
, "no recommendation available yet, "
470 "sending empty PB-TNC SDATA batch");
471 this->batch_type
= PB_BATCH_SDATA
;
476 if (this->batch_type
!= PB_BATCH_NONE
)
478 pb_tnc_batch_t
*batch
;
482 enumerator_t
*enumerator
;
484 if (this->state_machine
->send_batch(this->state_machine
, this->batch_type
))
486 batch
= pb_tnc_batch_create(TRUE
, this->batch_type
,
487 min(this->max_batch_len
, *buflen
));
489 enumerator
= this->messages
->create_enumerator(this->messages
);
490 while (enumerator
->enumerate(enumerator
, &msg
))
492 if (batch
->add_msg(batch
, msg
))
494 this->messages
->remove_at(this->messages
, enumerator
);
501 enumerator
->destroy(enumerator
);
504 data
= batch
->get_encoding(batch
);
505 DBG1(DBG_TNC
, "sending PB-TNC %N batch (%d bytes) for Connection ID %u",
506 pb_tnc_batch_type_names
, this->batch_type
, data
.len
,
507 this->connection_id
);
508 DBG3(DBG_TNC
, "%B", &data
);
512 memcpy(buf
, data
.ptr
, *buflen
);
513 batch
->destroy(batch
);
515 msg_count
= this->messages
->get_count(this->messages
);
518 DBG2(DBG_TNC
, "queued %d PB-TNC message%s for next %N batch",
519 msg_count
, (msg_count
== 1) ? "" : "s",
520 pb_tnc_batch_type_names
, this->batch_type
);
524 this->batch_type
= PB_BATCH_NONE
;
527 status
= ALREADY_DONE
;
531 change_batch_type(this, PB_BATCH_NONE
);
532 status
= INVALID_STATE
;
537 DBG1(DBG_TNC
, "no PB-TNC batch to send");
538 status
= INVALID_STATE
;
540 this->mutex
->unlock(this->mutex
);
545 METHOD(tnccs_20_handler_t
, begin_handshake
, void,
546 private_tnccs_20_server_t
*this, bool mutual
)
549 identification_t
*pdp_server
;
552 tnc
->imvs
->notify_connection_change(tnc
->imvs
, this->connection_id
,
553 TNC_CONNECTION_STATE_HANDSHAKE
);
555 /* Send a PB-TNC TCG PDP Referral message if PDP is known */
556 pdp_server
= (identification_t
*)lib
->get(lib
, "pt-tls-server");
557 pdp_port
= (uint16_t*)lib
->get(lib
, "pt-tls-port");
559 if (this->eap_transport
&& pdp_server
&& pdp_port
)
561 msg
= pb_pdp_referral_msg_create_from_fqdn(
562 pdp_server
->get_encoding(pdp_server
), *pdp_port
);
563 this->mutex
->lock(this->mutex
);
564 this->messages
->insert_last(this->messages
, msg
);
565 this->mutex
->unlock(this->mutex
);
568 /* Send a PB-Noskip-Test message for testing purposes */
569 if (lib
->settings
->get_bool(lib
->settings
,
570 "%s.plugins.tnccs-20.tests.pb_tnc_noskip", FALSE
, lib
->ns
))
572 msg
= pb_noskip_test_msg_create();
573 this->mutex
->lock(this->mutex
);
574 this->messages
->insert_last(this->messages
, msg
);
575 this->mutex
->unlock(this->mutex
);
579 METHOD(tnccs_20_handler_t
, get_send_flag
, bool,
580 private_tnccs_20_server_t
*this)
582 return this->send_msg
;
585 METHOD(tnccs_20_handler_t
, get_mutual
, bool,
586 private_tnccs_20_server_t
*this)
591 METHOD(tnccs_20_handler_t
, get_state
, pb_tnc_state_t
,
592 private_tnccs_20_server_t
*this)
594 return this->state_machine
->get_state(this->state_machine
);
597 METHOD(tnccs_20_handler_t
, add_msg
, void,
598 private_tnccs_20_server_t
*this, pb_tnc_msg_t
*msg
)
600 /* adding PA message to SDATA batch only */
601 this->mutex
->lock(this->mutex
);
602 if (this->batch_type
== PB_BATCH_NONE
)
604 this->batch_type
= PB_BATCH_SDATA
;
606 if (this->batch_type
== PB_BATCH_SDATA
)
608 this->messages
->insert_last(this->messages
, msg
);
614 this->mutex
->unlock(this->mutex
);
617 METHOD(tnccs_20_handler_t
, handle_errors
, void,
618 private_tnccs_20_server_t
*this, pb_tnc_batch_t
*batch
,
619 bool fatal_header_error
)
622 enumerator_t
*enumerator
;
624 if (fatal_header_error
|| this->fatal_error
)
626 this->mutex
->lock(this->mutex
);
627 change_batch_type(this, PB_BATCH_CLOSE
);
628 this->mutex
->unlock(this->mutex
);
631 enumerator
= batch
->create_error_enumerator(batch
);
632 while (enumerator
->enumerate(enumerator
, &msg
))
634 this->mutex
->lock(this->mutex
);
635 this->messages
->insert_last(this->messages
, msg
->get_ref(msg
));
636 this->mutex
->unlock(this->mutex
);
638 enumerator
->destroy(enumerator
);
641 METHOD(tnccs_20_handler_t
, destroy
, void,
642 private_tnccs_20_server_t
*this)
644 if (this->connection_id
)
646 tnc
->tnccs
->remove_connection(tnc
->tnccs
, this->connection_id
, TRUE
);
648 this->state_machine
->destroy(this->state_machine
);
649 this->mutex
->destroy(this->mutex
);
650 this->messages
->destroy_offset(this->messages
,
651 offsetof(pb_tnc_msg_t
, destroy
));
655 METHOD(tnccs_20_server_t
, have_recommendation
, bool,
656 private_tnccs_20_server_t
*this, TNC_IMV_Action_Recommendation
*rec
,
657 TNC_IMV_Evaluation_Result
*eval
)
659 return this->recs
->have_recommendation(this->recs
, rec
, eval
);
665 tnccs_20_handler_t
* tnccs_20_server_create(tnccs_t
*tnccs
,
666 tnccs_send_message_t send_msg
,
667 size_t max_batch_len
,
671 private_tnccs_20_server_t
*this;
678 .begin_handshake
= _begin_handshake
,
679 .get_send_flag
= _get_send_flag
,
680 .get_mutual
= _get_mutual
,
681 .get_state
= _get_state
,
683 .handle_errors
= _handle_errors
,
686 .have_recommendation
= _have_recommendation
,
688 .state_machine
= pb_tnc_state_machine_create(TRUE
),
689 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
690 .messages
= linked_list_create(),
691 .batch_type
= PB_BATCH_SDATA
,
692 .max_batch_len
= max_batch_len
,
693 .eap_transport
= eap_transport
,
696 this->connection_id
= tnc
->tnccs
->create_connection(tnc
->tnccs
,
697 TNCCS_2_0
, tnccs
, send_msg
,
698 &this->request_handshake_retry
,
699 max_msg_len
, &this->recs
);
700 if (!this->connection_id
)
705 tnc
->imvs
->notify_connection_change(tnc
->imvs
, this->connection_id
,
706 TNC_CONNECTION_STATE_CREATE
);
708 return &this->public.handler
;